View Full Version : [Support] How would you make CTF?
dandylion
04-19-2011, 04:20 PM
This is more for other coders than anything. I've been working on an alternate custom game for my heroes/map, and decided to do a CTF mode.
I had more or less psuedocoded the idea beforehand, but thus far have had little success in implementation.
Flag is a unit. Unit has an "aura" ability, which triggers when a unit is nearby. When the aura "triggers", the unit is "killed" and a state is applied to the triggering unit (State_IHAZFLAG). If the state "expires" i.e. on the death of the unit that has the state, a new unit is "spawned". If an ally is the first to come in contact with it, the flag teleports to the original spawn location, otherwise if the enemy is the first to come in contact with it, they have the state applied to them.
Alternatively, the flag could be an item box that is picked up, and the rest would work when the unit is in inventory etc.
The state applied to the target that has the flag is silenced and perplexed (rely on allies to keep you alive or fend off foes, you can attack, but no spells or items).
In addition, there's a "device" that checks if your own team's "flag" is present. If it is, you can score by coming within an aura range of it, otherwise the aura is turned off, and you can't score until the flag has teleported home.
Thus far I've been attempting to use a unit as the flag (neutral, for whatever reason gadgets weren't spawning correctly or being placed correctly, but units seemed to work fine). The unit has two aura abilities, one for allies and one for enemies. The rest is done from there. My block right now: The "aura" never seems to apply a state and kill the "flag", so right now it's just sitting in the base.
Would there be a better way to go about this that you can think of? Any ideas?
tojooko
04-19-2011, 06:02 PM
Well, I would make flag a powerup, becouse it has <ontouch> event, where is state applied. There would be 2 types of flag (Red and Blue), every with defined team. I think that carrier is silenced and perplexed is good idea, but I would add additional skill: Drop_flag which could replace Taunt
pukkBOOBies
04-19-2011, 07:49 PM
first pick doctor gg
CrashLemon
04-19-2011, 09:25 PM
Look at empath's ult.
Koiuy
04-19-2011, 10:51 PM
^ Above, lol'ed hard.
Not much into coding, so forgive me if my idea is crap or language w/e.
Why not make it into an item like you suggested, perplex, silence etc - whoever bears the item in inventory.
And when capturing the flag, could you make a somewhat 'stash' kind of object with one slot only allowing the flag item, and when current object is placed within the stash, make up some score tally??
In terms of death, make it the same with Doombringer?
Tafelpoot
04-21-2011, 06:00 AM
I think you need some modifiers on the flag, Home, Carried and Dropped.
And a disabled state for the carrier.
When Home, it has an ontouch for enemy heroes, binding itself to them (unbindondeath="true"), apply state Disabled to target and setmodifier Carried to self. Also ismobile="false" in declaration.
When Carried, it has an onrelease, that sets modifier Dropped and expires the Disabled state. Also, onframe an areaofeffect targeting other flags, testing hasmodifier Home, and then rewarding the player/team.
Otherwise you can make a Flag Socket gadget entity with an aura (modifier keys are good here as well). The aura is off when the ally flag is stolen.
When Dropped, it has the ontouch for ALL heroes, comparing the team, binding itself to enemy heroes (like in Home) in case of enemy, and teleporting to base in case of ally. Then set the corresponding modifierkey.
All this shizz in one gadget (or unit), instead of a unit with 2 abilities with each there states.
Additionally I recommend setting the carrier as proxy of the flag for easy access.
Dropping the flag with tauntkey is a good idea, this is an ability that does an unbind, sets modifierkey to Dropped on the flag and expires Disabled state on self.
Furthermore you can give the heroes an onkill event, with an areaofeffect target flag to give extra gold for "defending the flag" assist or "capturing flag" assist.
Edit: typos
dandylion
04-21-2011, 10:10 AM
Well, this is how I've approached it so far... and things are... not working as intended, and I can't find where my issue lies.
I'm using a neutral for my flag, it has an ability that on frame spawns an affector, the affector then does one of two things; applies a state to an enemy, or teleports the flag back to the starting position. I'll work on the logistics for determining whether the flag is in the base or not later, right now I just want to get the actions set up.
The neutral as the flag works fine. Spawns and has the ability. For whatever reason it appears the "affector" part isn't working.
Ability to find targets and spawn affector
<?xml version="1.0" encoding="UTF-8"?>
<ability
name="Ability_Flag"
icon="icon.tga"
maxlevel="1"
requiredlevel="1"
actiontype="passive"
>
<onframe>
<spawnaffector name="Affector_Target_Finder" param="source_position" />
<printdebuginfo />
</onframe>
</ability>
Affector, which applies the state or resets the flag
<?xml version="1.0" encoding="UTF-8"?>
<affector
name="Affector_Target_Finder"
radius="100"
targetselection="closest"
targetscheme="all_heroes"
effecttype=""
>
<onimpact>
<cantarget targetscheme="enemy_heroes">
<applystate name="State_Red_Flag" target="target_entity" continuous="true"/>
</cantarget>
<cantarget targetscheme="ally_heroes">
<spawnunit name="Neutral_AntloreHealer" count="1" target="param" />
</cantarget>
<printdebuginfo />
<kill target="source_entity" />
</onimpact>
</affector>
State, that when expired spawns a flag at the location of the carrier.
<?xml version="1.0" encoding="UTF-8"?>
<state
name="State_Red_Flag"
icon="icon.tga"
passiveeffect="/items/powerups/damage/effects/state.effect"
effecttype="StatusBuff"
>
<onexpired>
<condition test="not_hasstate State_Scoring_Aura">
<spawnunit name="Powerup_Damage" count="1" target="source_position" />
</condition>
<else>
<!-- do point stuff here -->
</else>
</onexpired>
</state>
See any problems or have any suggestions? It's not complete, I know, but I just wanted to start with the basics and move up from there.
tojooko
04-21-2011, 12:34 PM
I really can't get your point of view. You spawn affector every frame to check if there is any hero in 100 units radius? And later u 'kill' flag applying state? That u must walk into flag instead of touching it is strange for me.
All in all, becouse ur flag is neutral you can give him aura ( or 2 ) which applies on heroes insted of spawning affector.
PS. Add "revealed" into Carrier_state, taking flag during stealth time from invisible rune could be op
PPS. I suggest also to add
isselectable="false"
mapicon="/shared/icons/minimap_tower.tga"
mapiconsize="0.03"
in flag ( not sure that mapicon works)
EDIT:
In carrier state add aura, which affects only Well ( or Flag_spawner) to get easy trigger (Add your point stuff here)
Tafelpoot
04-21-2011, 12:49 PM
I don't know if you can pass an entity/position as your param. Use proxy to pass entities (and with it their position). And why do you spawn an Neutral_AntloreKeeper ?? Just make a new entity CTF_Gadget_Flag or so.
Also, as tojooko said, it's quite resource have to spawn an affector every frame, an aura applying a state with onimpact event is more then enough.
But, as tojooko said, you should give your flag an ontouch, (look at the powerup entities !).
I feel challenged to try it out :) I'll look into it now :D
Oloko
04-21-2011, 01:05 PM
I personally prefer to use aura rather than affectors if you want to do a "proximity trigger". If you want to use an instant affector (no delay and no multiple impact) I would recommend you to use an <areaofeffect /> action.
I don't really have time to look at all of your script since I'm at work, but I think that changing the affector for a aura or a areaofeffect could really help.
Tafelpoot
04-21-2011, 06:43 PM
I made this quickly, it's still a bit messy but works pretty well.
flag.entity
<?xml version="1.0" encoding="UTF-8"?>
<powerup
name="Powerup_Flag"
icon="/items/powerups/speed/icon.tga"
portrait="/items/powerups/speed/icon.tga"
model="/items/powerups/speed/model.mdf"
skin=""
passiveeffect="/items/powerups/speed/effects/passive.effect"
spawneffect="/items/powerups/speed/effects/spawn.effect"
respawneffect=""
selectsound=""
orderconfirmedsound=""
preglobalscale="1.7"
modelscale="1"
effectscale="0.9"
boundsheight="32"
boundsradius="16"
selectionradius="32"
targetoffset="0 0 16"
immunity="GadgetImmunity"
attacktype="none"
combattype="Ranged"
corpsetime="300"
corpsefadetime="0"
deathtime="0"
touchtargetscheme="all_heroes"
touchsound=""
>
<ontouched>
<compare a="team" b="target_team" op="eq">
<!-- if you have the flag, you score -->
<condition test="hasstate State_HasFlag" target="target_entity">
<print text="score!"/>
<expirestate name="State_HasFlag" target="target_entity"/>
<!-- now reset the enemy flag -->
<compare a="team" b="1" op="eq">
<setent0 name="hellbourneBase"/>
<spawnunit name="Powerup_Flag" target="ent0" offset="0 200" team="2"/>
</compare>
<else>
<setent0 name="legionBase"/>
<spawnunit name="Powerup_Flag" target="ent0" offset="0 200" team="1"/>
</else>
</condition>
<!-- otherwise, go get the enemy flag! -->
<else>
<print text="go get them tiger!"/>
</else>
<!-- this is a bit annoying ... you have to spawn the flag again -->
<spawnunit name="Powerup_Flag" target="this_position" fixedposition="true"/>
</compare>
<else>
<!-- pick up the enemy flag -->
<print text="enemy flag picked up, return it to base!"/>
<applystate name="State_HasFlag" duration="-1" />
</else>
</ontouched>
<modifier key="dropped" >
<ontouched>
<compare a="team" b="target_team" op="eq">
<!-- teleport your ally flag back to your base -->
<compare a="team" b="1" op="eq">
<setent0 name="legionBase"/>
<spawnunit name="Powerup_Flag" target="ent0" offset="200 0"/>
</compare>
<else>
<setent0 name="hellbourneBase"/>
<spawnunit name="Powerup_Flag" target="ent0" offset="200 0"/>
</else>
</compare>
<else>
<!-- pick up the enemy flag -->
<print text="enemy flag picked up, return it to base!"/>
<applystate name="State_HasFlag" duration="-1" />
</else>
</ontouched>
</modifier>
</powerup>
state_hazflag.entity
<?xml version="1.0" encoding="UTF-8"?>
<state
name="State_HasFlag"
icon="/items/powerups/speed/icon.tga"
passiveeffect="/items/powerups/speed/effects/state.effect"
movespeedmultiplier="1.1"
silenced="true"
disarmed="true"
perplexed="true"
effecttype="StatusBuff"
>
<ondeath>
<printvalue value="team"/>
<printvalue value="target_team"/>
<printvalue value="source_team"/>
<spawnunit name="Powerup_Flag" target="this_owner_entity" team="source_team" pushentity="true"/>
<activatemodifierkey entity="stack_entity" name="dropped" />
</ondeath>
</state>
You should use a flag socket entity, I just used legion and hellbourneBase now. I made a map with both bases close to eachother, tested this with a legion hero and a hellbourne hero. Works fine, read console output as prove. I hope you can use it !
dandylion
04-22-2011, 11:26 AM
Your code is very similar to what I had initially tried. My first attempt was with powerups, but I could never get it to work correctly, which is why I tried the method I had initially posted here. I had very limited success with it, but was overly complicated.
I went back and used your code, almost exactly as you have it, and it still won't work.
A few questions for clarification... do you have those entities already spawned into your map? And if so, did you simply add them manually into your entitylist? As far as I know, there's no way to load your custom entities into the map editor.
I have tried both methods (modifying existing entities i.e. damage/speed powerups, and manually adding custom entities via entitylist) and neither seem to work. The powerups are there, but whenever "grabbed" by either faction's units, they simply consume and don't apply a state at all (and never respawn).
Tafelpoot
04-22-2011, 12:04 PM
I use practice mode, and spawn them with the test menu (cg_dev = 1).
Make sure you put the right team on each of the flags, if you use my exact code.
dandylion
04-22-2011, 12:39 PM
Okay. So spawning them in practice mode for the most part works, but the problem still remains: to have them actually work in a normal game (without manual spawning), they would need to work and be placed when the game starts. Everything that I've tried in that regard has not worked.
I know in order to get my BfC:Domination game to work properly, the units would have to have abilities that did the same thing as what would (at least for now) be in the bottom portion of the powerup entity.
Tafelpoot
04-22-2011, 01:37 PM
Yeh i just made a small map to test it, you cannot spawn them at start (in entitylist).
Also there are some small mechanics mistakes, setting the correct modifier and team, in case of a dropped flag. But it's friday evening now, and I've got other plans...
Maybe I'll find a fix tomorrow :)
ultrahiangle
04-22-2011, 04:15 PM
have to slow down flag carrier for obviouse reasons along with disabling movement skills
X`plicit
04-22-2011, 09:57 PM
I havent read any of the above but wouldnt people just get a team full of disablers and a tp stone and tp to base and get capture?
X`plicit
04-22-2011, 09:58 PM
srry for double post but read some of the main posts and thats it.
dandylion
04-22-2011, 10:40 PM
In my game, I have 16 unique heroes (as in, no nymphora, no TP stones, etc). The person carrying the flag is intended to have a debuff on them that basically prevents anything from causing them to "teleport" or "blink" any substantial distance.
Tafelpoot
04-23-2011, 06:14 PM
Alright, I got it to work :)
I made a small map so you can test it easily.
Some lessons I learned in this map :
- when you put the flags in the entitylist, they won't work. The proper solution is to make a flagspawner, with the ability "spawn_flags". I was lazy and set "spawn_flags" as the ulti of your hero. This is similar to your conquest map where the buildings didn't react.
- if you push an entity to stack in a compare event, it's not remembered outside of it.
- use <deactivatemodifierkey name="dropped"/> instead of <activatemodifierkey name=""/>. Probably you can fix this with the modpriority or exclusive="true"
How to test this : load the map (twice, so that everything is loaded well)
Use panda to pick up the hellbourne flag, and bring it back to base. You should get 100 gold on panda. Also, CTRL+F8 and read the console logs.
Use behemoth to pick up the legion flag, and bring it back to hb base. You get 100 gold on behe.
Use behe to pick up the legion flag. Kill him (with panda or test button). Return the flag with panda. Or with behemoth.
The movespeed buff when carrying the flag is just to make testing faster and for pretty graphics.
Edit : extra note on spawning them at start : you should make an invisible spawner entity (you can define it in entitylist), with the "spawn_creeps" ability. In game_info.entity put after the foreachplayer part :
<setent0 name="your_spawner_gadget"/>
<useability slot="0" source="ent0"/>
Also, the scriptthread "spawn_runes" in my game_info.entity does NOT work. I forgot to delete that part. I would appreciate though if someone could tell me why it doesn't work.
dandylion
04-24-2011, 12:01 PM
So, got it working for the most part here (even with them spawning at the beginning of the match without player interaction), but there's still one glitch: the very first time each "flag" is grabbed, it spawns 2. Circumstances don't matter, both enemy and ally grabs spawn a second flag.
I'll post the code I have later, to see if someone can figure out why it's going on.
Serret
04-24-2011, 02:58 PM
when it comes to CTF what i'd do is rather than having a player "pick up a flag", I'd make a flag a monkey like criter or simply limit the speed of the carrier, and if he uses any escape mechanism the flag would just drop.
Tafelpoot
04-25-2011, 04:44 AM
I'll post the code I have later, to see if someone can figure out why it's going on.I'm looking forward :)
Good luck with it!
dandylion
04-25-2011, 04:26 PM
So... still combating my "double spawn" issue. I tried making an affector that killed any other flags, but it was essentially killing ALL the flags, even with using ignore and conditional tests... frustrating to say the least... but for the most part, the rest of it works well.
I use a neutral_goat prespawned into the map for initial spawning of the flag (he's perched on a cliff over the flag area for constant vision of your flag), and then use that as the "predefined location" to spawn additional flags after scoring and whatnot.
goat spawn ability:
<?xml version="1.0" encoding="UTF-8"?>
<ability
name="Ability_SpawnFlags"
actiontype="self_position"
icon="/items/powerups/speed/icon.tga"
>
<onspawn>
<compare a="team" b="source_team" op="eq">
<compare a="team" b="1" op="eq">
<setent0 name="legionBase"/>
<spawnunit name="Powerup_Damage" target="ent0" offset="0 250" pushentity="true" />
</compare>
<else>
<setent1 name="hellbourneBase"/>
<spawnunit name="Powerup_MoveSpeed" target="ent1" offset="0 250" pushentity="true" />
</else>
</compare>
</onspawn>
</ability>
flag entity (as a powerup, there's a different unit for each team, but you can imagine that they correspond to each other, but opposite)
<?xml version="1.0" encoding="UTF-8"?>
<powerup
name="Powerup_Damage"
icon="icon.tga"
portrait="icon.tga"
model="/items/powerups/damage/model.mdf"
skin=""
passiveeffect="/items/powerups/damage/effects/passive.effect"
spawneffect="/items/powerups/damage/effects/spawn.effect"
respawneffect=""
selectsound=""
orderconfirmedsound=""
preglobalscale="1.7"
modelscale="1"
effectscale="0.9"
boundsheight="32"
boundsradius="16"
selectionradius="32"
targetoffset="0 0 16"
immunity="GadgetImmunity"
attacktype="none"
combattype="Ranged"
corpsetime="300"
corpsefadetime="0"
deathtime="0"
touchtargetscheme="all_heroes"
touchsound=""
>
<ontouched>
<compare a="team" b="target_team" op="eq">
<!-- if you have the flag, you score -->
<condition test="hasstate State_HasFlagHell" target="target_entity">
<expirestate name="State_HasFlagHell" target="target_entity"/>
<!-- now reset the enemy flag -->
<compare a="team" b="1" op="eq">
<setent0 name="hellbourneBase"/>
<spawnunit name="Powerup_MoveSpeed" target="ent0" offset="0 -250" team="2"/>
</compare>
<else>
<setent1 name="legionBase"/>
<spawnunit name="Powerup_Damage" target="ent1" offset="0 250" team="1"/>
</else>
</condition>
<!-- spawn the flag again -->
<spawnunit name="Powerup_Damage" target="this_position" fixedposition="true"/>
</compare>
<else>
<!-- pick up the enemy flag -->
<applystate name="State_HasFlagLeg" duration="-1" />
</else>
</ontouched>
<modifier key="dropped" >
<ontouched>
<compare a="team" b="target_team" op="eq">
<!-- teleport your ally flag back to your base -->
<compare a="team" b="1" op="eq">
<setent0 name="legionBase"/>
<spawnunit name="Powerup_Damage" target="ent0" offset="0 250" pushentity="true" />
<deactivatemodifierkey entity="stack_entity" name="dropped" />
</compare>
</compare>
<else>
<!-- pick up the enemy flag -->
<applystate name="State_HasFlagLeg" duration="-1" />
</else>
</ontouched>
</modifier>
</powerup>
states applied to flag carriers
<?xml version="1.0" encoding="UTF-8"?>
<state
name="State_HasFlagLeg"
icon="/items/powerups/damage/icon.tga"
passiveeffect="/items/powerups/damage/effects/state.effect"
silenced="true"
perplexed="true"
revealed="true"
effecttype=""
>
<oninflict>
<printdebuginfo />
</oninflict>
<ondeath>
<!-- to calculate the opposite team, do 3-1=2 or 3-2=1 -->
<setvar0 a="3" b="team" op="sub"/>
<spawnunit name="Powerup_Damage" target="source_position" team="var0" pushentity="true"/>
<activatemodifierkey name="dropped" entity="stack_entity"/>
</ondeath>
</state>
Still not picking up why a second flag spawns on the first <ontouch> activation of each flag. Thinking of adding an initial charge to it, where after the first, it loses a charge and then spawns a new flag, hopefully to fix the double spawn on first contact issue, but if anyone has any other ideas or explanations as to why it happens, feel free to give me your input.
Tafelpoot
04-26-2011, 09:20 AM
I think that the <onspawn> of your spawn goat (makes my day :D) triggers multiple frames if you spawn it at the very start of the game. I suggest you put a <wait duration="1000"/> after spawning the heroes in game_info.entity and then spawn your goat (dormant="1"). This should fix the double flags.
Also I suggest you give your own names to these entities, instead of editing existing ones. Flag_Legion and Flag_Hellbourne for instance instead of Powerup_Damage and Powerup_Speed.
My implementation made no difference between legion and hellbourne (Powerup_Flag), but you use 2 different files (Powerup_Speed and Powerup_Damage).
The logic part works different here (it's a little simpler, but you've got to write/load it twice ... ). I wrote it in pseudo code, but it should be very clear.
Flag_Legion (your Powerup_Damage)
Ontouch
compare the target_team with 1 (= same team).
condition hasstate HasFlag on target
score points or whatever
expire HasFlag
setent0 hellbournbase
spawnunit Flag_Hellbourne next to ent0
/condition
spawnunit Flag_Legion on this_position (because it disappeared ontouch)
/compare
else (= enemy)
applystate HasFlag to target
/else
/ontouch
modifier dropped
ontouch
compare target_team eq 1 (you return your ally flag)
setent0 LegionBase
spawnunit Flag_Legion next to ent0 and push it
deactivatemodifier dropped on stack_entity
/compare
else (you pick up the enemy flag that was dropped)
applystate HasFlag to target
/else
/ontouch
Flag_Hellbourne (your Powerup_Speed). Just compare with team 2 and adjust the bases.
Ontouch
compare the target_team with 2 (= same team).
condition hasstate HasFlag on target
score points or whatever
expire HasFlag
setent0 hellbournbase
spawnunit Flag_Legion next to ent0
/condition
spawnunit Flag_Hellbourne on this_position (because it disappeared ontouch)
/compare
else (= enemy)
applystate HasFlag to target
/else
/ontouch
modifier dropped
ontouch
compare target_team eq 2 (you return your ally flag)
setent0 LegionBase
spawnunit Flag_Hellbourne next to ent0 and push it
deactivatemodifier dropped on stack_entity
/compare
else (you pick up the enemy flag that was dropped)
applystate HasFlag to target
/else
/ontouch
Edit :
Also, you have to adjust the HasFlag state, start with a compare team eq 1, then spawn a Flag_Hellbourne at this_owner_position with team="2" and activate dropped modifier. In else put the opposite.
When debugging the double flag spawning, I suggest you put some different <print text="something"/> after each <spawnunit/>... you can find the source of your problem easier that way.
This will solve your double spawning. Let me know if you have any other questions !