Send feedback to feedback@q3f.com
Visit #q3f on IRC: irc.be.quakenet.org, east.gamesnet.net or irc.enterthegame.com


Q3F News Sites
PlanetQ3F

PlanetFortress Germany

Q3F.SE

Q3F.Fr

AusFortress

Custom Levels
Leakspot.net

Q3F Server Sites
RailBait

CannonFodder

Tutorial 04: Advanced Capture and Hold
This tutorial will cover:

- Advanced commandpoints
- HUD status flags
- Capture rewards and map control

This tutorial is a bit harder to follow as it delves significantly further into the bowels of the entity system than the previous tuts. Make sure you have read tutorials 1-3 and have a good understanding of them before proceeding.

Advanced Commandpoints
----------------------

In our basic C&H tutorial we set about creating a nice 2 team C&H map with 4 commandpoints. The implementation we used was bulky however - 6 triggers, 2 goalinfo's and a func_commandpoint per commandpoint. Its clear that if more flags, or more teams, were added to the mix this solution would soon become unusable. We need a method to cut down on the number of entities.

The solution comes in the form of cascade triggers. The principal of cascade triggers is simple enough to understand:

Player triggers entity A -> entity A triggers entity(s) B -> entity(s) B trigger entity(s) C etc.

One of the things that makes this so useful is you can put conditions on entity C triggering, such as "holding" "redflag", and it checks against Player to see if the criteria match. The only exception to this rule is when the entity is forced, in which case it doesn't check any criteria anyway.

Lets define what we want to do then I'll show you how to use cascade triggers to make it work:

1) player walks into commandpoint
2) if player is holding a flag they gain control of the commandpoint and the correct flag is returned.
3) display correct flag (red/blue) above the commandpoint

1)
--
Setup your map as in basic C&H but this time add 'flag' to the 'groupname' field in your flags:

{
"classname" "func_goalitem"
"groupname" "red_flag_1,flag"    // all the flags should be marked as belonging to the 'flag' group
"allowteams" "red"     // note not blue this time
"model" "models/flags/r_flag.md3"
"flags" "showcarry,revealagent"
"inactive_all_message" "The ^1red^* flag returned to base!"
"inactive_flaginfo" "The ^1Red^* flag is at base."
"carried_flaginfo" "%N is carrying the ^1Red^* flag!"
"wait" "0"
"_color" "1.000000 0.392157 0.392157"
"light" "150"
}

2)
--
Instead of using one trigger for each flag we will use one func_commandpoint that activates if the player is holding 'flag'.

{
"classname" "func_commandpoint"    // its a commandpoint ;)
"holding" "flag"    // only trigger if the player has a flag
"groupname" "cp1"    // its CP 1
"initialstate" "inactive"    // no one owns it to begin with
"active_all_message" "~%N has claimed CP1 for the %T!"
// let everyone know about your glorious capture
"pulseteamscore" "2"    // 2 points every...
"wait" "60"    // ...seconds
}

This assigns the commandpoint to the correct team and starts the score pulse. Next we need to return the carried flag and display the 'status' flag above the commandpoint. Add this line to the func_commandpoint:

"activetarget" "flag_returner,cp1statusflag"

This will trigger all ents in the flag_returner group:

{
"classname" "trigger_multiple"     // its going to be used a lot...
"groupname" "flag_returner"    // this ent is just to return flags
"holding" "red_flag_1"     // note because the ents cascade this only triggers if Player is holding red_flag_1
"activetarget" "red_flag_1=~inactive"     // send red_flag_1 home
"wait" "0.001"     // set it to this so it can be triggered more than once per frame
}

You will need one of these for each flag - ie 6. You may be wondering how this is better than the previous method - don't worry I'll reveal all in a moment :)

3)
--
The logic of setting the status flags needs some thinking about before we plow in. We want to trigger all the status goalinfos much like we did with the flag_returners, and use conditions to only activate the one we need. The problem here is that we can't go from invisible->active directly, the only way is with a force (~) which ignores conditions. If we use a force both status flags will display which is clearly wrong. The solution is to use an intermediate entity. If the condition is met (in this case 'allowteams' 'red') then the red flag is displayed and the blue one hidden. If the condition isn't met it uses the failtarget field to display the blue flag and hide the red one.

The intermediate ent:

Now the flags themselves:


Ok now we've gone through and set it all up a brief discussion of WHY its better is in order. Lets look back at our old C&H map with the help of Bob the Recon :)


This is how it used to be - 6 triggers, 2 goalinfo's and a commandpoint.
Now look at how it is now:

Erm ok, a commandpoint, an info_notnull, 2 goalinfo's and 6 triggers. One more entity.... BUT - the neat thing is, unlike with the old C&H, we don't need to clone all these entities for each commandpoint:

As you can see because we get to reuse our flag returners we serious slash the number of entities needed - only having to add 4 for each new commandpoint as compared to 9 the old way.

HUD status flags
----------------

C&H maps tend to be larger and more architecturally complex than CTF maps. This can lead to confusion if the mapper isn't careful so extra attention must be paid to making things as intuitive as possible. One of the niftier tools at the mappers disposal is the Q3F HUD. We've seen this already with our flags:

We can use this with out commandpoints aswell to give a constant display on the hud showing who holds which commandpoint. And model can be used for this - at time of writing the C&H hud models are still being made so I'll use a half size flag of the correct color.

Slots are arranged in two vertical columns of 5:

1 6
2 7
3 8
4 9
5 0

Normally 1-5 are used for map objects (flags,keys,torches,batteries etc) and 6-0 are used for commandpoints and similar. This isn't set in stone but following conventions helps the newbies. As we have 4 commandpoints we will use slots 6-9.

We remove the 'holding' field from the entity - it now displays to everyone on all teams. The scale field is on a range of 0.0 to 1.0, ie small to normal. We use 0.5 to further distinguish it from carried flags.

We need to change the HUD models at the same time as the CP flags so we add to the info_notnull:

"activetarget" "red_cp1_flag=~active,blue_cp1_flag=~invisible,cp1_hud_red=~active,cp1_hud_blue=~invisible"
"failtarget" "blue_cp1_flag=~active,red_cp1_flag=~invisible,cp1_hud_blue=~active,cp1_hud_red=~invisible"

Capture Rewards and Map control
-------------------------------

So far our commandpoints have been a little boring - giving the holding player x teamscore every y seconds is all well and good, but about as exciting as watching the proverbial paint dry. Ideally we'd want to be able to control the way the map flows depending on who holds what commandpoint, and guess what - we can :-) The little gem that comes to our rescue is the teamset and nonteamset fields. A quick delve into the Q3F Ent Docs tells us:

If 'teamset' is set, all named entities will have their 'allowteams' criteria key set to the activator's team.
If 'nonteamset' is set, all named entities will have their 'allowteams' criteria key set to all teams but the activator's team.

This is fairly straight forward and doesn't need extended explanation. As an example:

"teamset" "cp1"

would set all entities with the groupname 'cp1' to the activators team. This could be resupplies, doors, lifts, jump pads, infact pretty much anything you want. A word of warning though - you don't want to seriously alter the game balance with each capture - if you confer an attacking advantage through use of teamdoors u might also want to open up defense holes. Ideally your CP should give the holding team an edge but not something that will swing the balance of the game.

- by Steve 'MrChumble' Batham


Site design ©1999, 2000, 2001, 2002 RR2DO2.
Q3F ©1999, 2000, 2001, 2002 Q3F. For legal info click here.