1 // ================================================================
2 // Official capture the flag game mode coding, reworked by Samual
3 // Last updated: March 27th, 2011
4 // ================================================================
7 #define FLAG_MIN (PL_MIN + '0 0 -13')
8 #define FLAG_MAX (PL_MAX + '0 0 -13')
9 #define FLAG_CARRY_POS '-15 0 7'
11 .entity bot_basewaypoint; // Flag waypointsprite
13 .entity wps_flagcarrier;
14 .entity wps_flagdropped;
16 entity ctf_worldflaglist; // CTF flags in the map
17 .entity ctf_worldflagnext;
19 .float ctf_dropperid; // Don't allow spam of dropping the flag
21 .float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
23 // Delay between when the person can pick up a flag // replace with .wait?
24 .float next_take_time;
26 // Record time for capturing the flag
27 float flagcaptimerecord;
28 .float flagpickuptime;
30 // CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag.
31 .float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
32 float captureshield_min_negscore; // punish at -20 points
33 float captureshield_max_ratio; // punish at most 30% of each team
34 float captureshield_force; // push force of the shield
37 // declare functions so they can be used in any order in the file
38 void ctf_FlagTouch(void);
39 void ctf_FlagThink(void);
40 void ctf_SetupFlag(float, entity);
41 void ctf_RespawnFlag(entity);
42 float ctf_CaptureShield_CheckStatus(entity);
43 void ctf_CaptureShield_Update(entity, float);
44 float ctf_CaptureShield_Customize(void);
45 void ctf_CaptureShield_Touch(void);
46 void ctf_CaptureShield_Spawn(entity);
53 float ctf_ReadScore(string parameter) // make this obsolete
55 if(g_ctf_win_mode != 2)
56 return cvar(strcat("g_ctf_personal", parameter));
58 return cvar(strcat("g_ctf_flag", parameter));
61 void ctf_FakeTimeLimit(entity e, float t)
64 WriteByte(MSG_ONE, 3); // svc_updatestat
65 WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
67 WriteCoord(MSG_ONE, autocvar_timelimit);
69 WriteCoord(MSG_ONE, (t + 1) / 60);
72 void ctf_EventLog(string mode, float flagteam, entity actor)
75 if(!autocvar_sv_eventlog)
77 s = strcat(":ctf:", mode);
78 s = strcat(s, ":", ftos(flagteam));
80 s = strcat(s, ":", ftos(actor.playerid));
84 void ctf_CaptureShockwave(vector org)
86 shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1);
89 void ctf_CreateBaseWaypoints(entity flag, float teamnumber)
92 waypoint_spawnforitem_force(flag, flag.origin);
93 flag.nearestwaypointtimeout = 0; // activate waypointing again
94 flag.bot_basewaypoint = flag.nearestwaypoint;
97 WaypointSprite_SpawnFixed(((teamnumber) ? "redbase" : "bluebase"), flag.origin + '0 0 61', flag, wps_flagbase);
98 WaypointSprite_UpdateTeamRadar(flag.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2) - 1, FALSE));
101 void ctf_SetStatus_ForType(entity flag, float type)
103 if(flag.cnt == FLAG_CARRY)
105 if(flag.owner == self)
106 self.items |= type * 3; // carrying: self is currently carrying the flag
108 self.items |= type * 1; // taken: someone on self's team is carrying the flag
110 else if(flag.cnt == FLAG_DROPPED)
111 self.items |= type * 2; // lost: the flag is dropped somewhere on the map
114 void ctf_SetStatus() // re-write this in some less shitty way
117 float redflags, blueflags;
120 // initially clear items so they can be set as necessary later.
121 self.items &~= (IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED);
123 // item for stopping players from capturing the flag too often
124 if(self.ctf_captureshielded)
125 self.items |= IT_CTF_SHIELDED;
127 // figure out what flags we already own
128 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
130 if(flag.items & IT_KEY2) // blue
132 else if(flag.items & IT_KEY1) // red
136 // blinking magic: if there is more than one flag, show one of these in a clever way // wtf?
138 redflags = mod(floor(time * redflags * 0.75), redflags);
141 blueflags = mod(floor(time * blueflags * 0.75), blueflags);
143 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
145 if(flag.items & IT_KEY2) // blue
147 if(--redflags == -1) // happens exactly once (redflags is in 0..count-1, and will --'ed count times) // WHAT THE FUCK DOES THIS MEAN? whoever wrote this is shitty at explaining things.
148 ctf_SetStatus_ForType(flag, IT_RED_FLAG_TAKEN);
150 else if(flag.items & IT_KEY1) // red
152 if(--blueflags == -1) // happens exactly once
153 ctf_SetStatus_ForType(flag, IT_BLUE_FLAG_TAKEN);
160 ctf_Handle_Drop(self);
161 ctf_RespawnFlag(self);
169 void ctf_Handle_Drop(entity player) // make sure this works
171 entity flag = player.flagcarried;
173 if(!flag) { return; }
174 if(flag.speedrunning) { ctf_RespawnFlag(flag); return; }
177 setattachment(flag, world, "");
178 flag.owner.flagcarried = world;
180 flag.ctf_status = FLAG_DROPPED;
181 flag.movetype = MOVETYPE_TOSS;
182 flag.solid = SOLID_TRIGGER;
183 flag.takedamage = DAMAGE_YES;
184 flag.flags = FL_ITEM; // does this need set? same as above.
185 setorigin(flag, player.origin - '0 0 24' + '0 0 37'); // eh wtf is with these weird values?
186 flag.velocity = ('0 0 200' + ('0 100 0' * crandom()) + ('100 0 0' * crandom()));
187 flag.pain_finished = time + autocvar_g_ctf_flag_returntime;
189 flag.ctf_droptime = time;
190 flag.ctf_dropperid = player.playerid;
192 // messages and sounds
193 Send_KillNotification(player.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO);
194 sound(flag, CHAN_TRIGGER, flag.noise4, VOL_BASE, ATTN_NONE);
195 ctf_EventLog("dropped", player.team, player);
198 PlayerScore_Add(player, SP_CTF_DROPS, 1);
199 UpdateFrags(player, -ctf_ReadScore("penalty_drop"));
202 WaypointSprite_Spawn("flagdropped", 0, 0, flag, '0 0 64', world, (COLOR_TEAM1 + COLOR_TEAM2 - flag.team), flag, wps_flagcarrier, FALSE);
203 WaypointSprite_Ping(player.wps_flagcarrier);
204 WaypointSprite_Kill(player.wps_flagcarrier);
206 ctf_CaptureShield_Update(player, 0); // shield only
209 trace_startsolid = FALSE;
210 tracebox(flag.origin, flag.mins, flag.maxs, flag.origin, TRUE, flag);
212 dprint("FLAG FALLTHROUGH will happen SOON\n");
217 void ctf_Handle_Capture(entity flag, entity player)
222 void ctf_Handle_Return(entity flag, entity player)
227 void ctf_Handle_Pickup_Base(entity flag, entity player)
232 void ctf_Handle_Pickup_Dropped(entity flag, entity player)
238 // ===================
239 // Main Flag Functions
240 // ===================
242 void ctf_SetupFlag(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
245 teamnumber = fabs(teamnumber - bound(0, g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1.
248 flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist // todo: find out if this can be simplified
249 ctf_worldflaglist = flag;
251 setattachment(flag, world, "");
253 flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
254 flag.team = ((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2); // COLOR_TEAM1: color 4 team (red) - COLOR_TEAM2: color 13 team (blue)
255 flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
256 flag.classname = "item_flag_team";
257 flag.target = "###item###"; // wut?
258 flag.flags = FL_ITEM;
259 flag.solid = SOLID_TRIGGER;
260 flag.velocity = '0 0 0';
261 flag.ctf_status = FLAG_BASE;
262 flag.mangle = flag.angles;
263 flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
265 if(flag.spawnflags & 1) // I don't understand what all this is about.
268 flag.dropped_origin = flag.origin;
269 flag.movetype = MOVETYPE_NONE;
273 flag.noalign = FALSE;
275 flag.movetype = MOVETYPE_TOSS;
278 flag.reset = ctf_Reset;
279 flag.touch = ctf_FlagTouch;
280 flag.think = ctf_RespawnFlag;
281 flag.nextthink = time + 0.2; // start after doors etc // Samual: 0.2 though? Why?
284 if(!flag.model) { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); }
285 setmodel (flag, flag.model); // precision set below
286 setsize(flag, FLAG_MIN, FLAG_MAX);
287 setorigin(flag, flag.origin + '0 0 37');
288 flag.origin_z = flag.origin_z + 6; // why 6?
289 if(!flag.scale) { flag.scale = 0.6; }
291 flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin);
293 if(autocvar_g_ctf_flag_glowtrails)
295 flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
300 flag.effects |= EF_LOWPRECISION;
301 if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; }
302 if(autocvar_g_ctf_dynamiclights) { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); }
305 if(!flag.noise) { flag.noise = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); }
306 if(!flag.noise1) { flag.noise1 = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); }
307 if(!flag.noise2) { flag.noise2 = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag
308 if(!flag.noise3) { flag.noise3 = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
309 if(!flag.noise4) { flag.noise4 = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); }
312 precache_sound(flag.noise);
313 precache_sound(flag.noise1);
314 precache_sound(flag.noise2);
315 precache_sound(flag.noise3);
316 precache_sound(flag.noise4);
317 precache_model(flag.model);
318 precache_model("models/ctf/shield.md3");
319 precache_model("models/ctf/shockwavetransring.md3");
321 // other initialization stuff
322 ctf_CreateBaseWaypoints(flag, teamnumber);
323 ctf_CaptureShield_Spawn(flag);
324 //InitializeEntity(self, ctf_CaptureShield_Spawn, INITPRIO_SETLOCATION);
327 void ctf_RespawnFlag(entity flag) // re-write this
329 if(flag.classname != "item_flag_team") { backtrace("ctf_RespawnFlag was called incorrectly."); return; }
332 if(flag.owner.flagcarried == flag)
334 WaypointSprite_DetachCarrier(flag.owner);
335 flag.owner.flagcarried = world;
337 if(flag.speedrunning)
338 ctf_FakeTimeLimit(flag.owner, -1);
342 if(flag.waypointsprite_attachedforcarrier)
343 WaypointSprite_DetachCarrier(flag);
345 setattachment(flag, world, "");
346 flag.damageforcescale = 0;
347 flag.takedamage = DAMAGE_NO;
348 flag.movetype = MOVETYPE_NONE;
350 flag.movetype = MOVETYPE_TOSS;
351 flag.velocity = '0 0 0';
352 flag.solid = SOLID_TRIGGER;
353 // TODO: play a sound here
354 setorigin(flag, flag.dropped_origin);
355 flag.angles = flag.mangle;
356 flag.ctf_status = FLAG_BASE;
358 flag.flags = FL_ITEM; // clear FL_ONGROUND and any other junk // there shouldn't be any "junk" set on this... look into it and make sure it's kept clean.
361 void ctf_FlagThink() // re-write this
365 self.nextthink = time + 0.1;
367 // sorry, we have to reset the flag size if it got squished by something
368 if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX)
370 // if we can grow back, grow back
371 tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
372 if(!trace_startsolid)
373 setsize(self, FLAG_MIN, FLAG_MAX);
376 if(self == ctf_worldflaglist) // only for the first flag
379 ctf_CaptureShield_Update(e, 1); // release shield only
382 if(self.speedrunning)
383 if(self.cnt == FLAG_CARRY)
386 if(flagcaptimerecord)
387 if(time >= self.flagpickuptime + flagcaptimerecord)
389 bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n");
391 sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
392 self.owner.impulse = 141; // returning!
403 if(self.cnt == FLAG_BASE)
406 if(self.cnt == FLAG_DROPPED)
408 // flag fallthrough? FIXME remove this if bug is really fixed now
409 if(self.origin_z < -131072)
411 dprint("FLAG FALLTHROUGH just happened\n");
412 self.pain_finished = 0;
414 setattachment(self, world, "");
415 if(time > self.pain_finished)
417 bprint("The ", self.netname, " has returned to base\n");
418 sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
419 ctf_EventLog("returned", self.team, world);
420 ctf_RespawnFlag(self);
426 if(e.classname != "player" || (e.deadflag) || (e.flagcarried != self))
428 dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n");
433 if(autocvar_g_ctf_allow_drop)
435 ctf_Handle_Drop(self);
440 if(gameover) { return; }
441 if(!self) { return; }
442 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
443 { // The flag fell off the map, respawn it since players can't get to it
444 ctf_RespawnFlag(self);
447 if(other.deadflag != DEAD_NO) { return; }
448 if(other.classname != "player")
449 { // The flag just touched an object, most likely the world
450 pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
451 sound(self, CHAN_AUTO, "keepaway/touch.wav", VOL_BASE, ATTN_NORM);
454 else if(self.wait > time) { return; }
456 switch(self.ctf_status)
459 if((other.team == self.team) && (other.flagcarried) && (other.flagcarried.team != self.team))
460 ctf_Handle_Capture(self, other); // other just captured the enemies flag to his base
461 else if((other.team != self.team) && (!other.flagcarried) && (!other.ctf_captureshielded))
462 ctf_Handle_Pickup_Base(self, other); // other just stole the enemies flag
466 if(other.team == self.team)
467 ctf_Handle_Return(self, other); // other just returned his own flag
468 else if((!other.flagcarried) && ((other.playerid != self.ctf_dropperid) || (time > self.ctf_droptime + autocvar_g_balance_ctf_delay_collect)))
469 ctf_Handle_Pickup_Dropped(self, other); // other just picked up a dropped enemy flag
474 dprint("Someone touched a flag even though it was being carried? wtf?\n");
475 break; // this should never happen
480 // =======================
481 // CaptureShield Functions
482 // =======================
484 float ctf_CaptureShield_CheckStatus(entity p) // check to see
488 float players_worseeq, players_total;
490 if(captureshield_max_ratio <= 0)
493 s = PlayerScore_Add(p, SP_SCORE, 0);
494 if(s >= -captureshield_min_negscore)
497 players_total = players_worseeq = 0;
502 se = PlayerScore_Add(e, SP_SCORE, 0);
508 // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
509 // use this rule here
511 if(players_worseeq >= players_total * captureshield_max_ratio)
517 void ctf_CaptureShield_Update(entity p, float dir)
520 if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only
522 should = ctf_CaptureShield_CheckStatus(p);
525 if(should) // TODO csqc notifier for this
526 centerprint_atprio(p, CENTERPRIO_SHIELDING, "^3You are now ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Make some defensive scores before trying again.");
528 centerprint_atprio(p, CENTERPRIO_SHIELDING, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.");
530 p.ctf_captureshielded = should;
535 float ctf_CaptureShield_Customize()
537 if not(other.ctf_captureshielded)
539 if(self.team == other.team)
544 void ctf_CaptureShield_Touch()
546 if not(other.ctf_captureshielded)
548 if(self.team == other.team)
552 mymid = (self.absmin + self.absmax) * 0.5;
553 othermid = (other.absmin + other.absmax) * 0.5;
554 Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force);
555 centerprint_atprio(other, CENTERPRIO_SHIELDING, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.");
558 void ctf_CaptureShield_Spawn(entity flag)
564 e.touch = ctf_CaptureShield_Touch;
565 e.customizeentityforclient = ctf_CaptureShield_Customize;
566 e.classname = "ctf_captureshield";
567 e.effects = EF_ADDITIVE;
568 e.movetype = MOVETYPE_NOCLIP;
569 e.solid = SOLID_TRIGGER;
570 e.avelocity = '7 0 11';
571 setorigin(e, self.origin);
572 setmodel(e, "models/ctf/shield.md3");
574 setsize(e, e.scale * e.mins, e.scale * e.maxs);
582 MUTATOR_HOOKFUNCTION(ctf_RemovePlayer)
584 if(self.flagcarried) { ctf_Handle_Drop(self); } // figure this out
594 /*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
595 CTF Starting point for a player in team one (Red).
596 Keys: "angle" viewing angle when spawning. */
597 void spawnfunc_info_player_team1()
599 if(g_assault) { remove(self); return; }
601 self.team = COLOR_TEAM1; // red
602 spawnfunc_info_player_deathmatch();
606 /*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)
607 CTF Starting point for a player in team two (Blue).
608 Keys: "angle" viewing angle when spawning. */
609 void spawnfunc_info_player_team2()
611 if(g_assault) { remove(self); return; }
613 self.team = COLOR_TEAM2; // blue
614 spawnfunc_info_player_deathmatch();
617 /*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)
618 CTF Starting point for a player in team three (Yellow).
619 Keys: "angle" viewing angle when spawning. */
620 void spawnfunc_info_player_team3()
622 if(g_assault) { remove(self); return; }
624 self.team = COLOR_TEAM3; // yellow
625 spawnfunc_info_player_deathmatch();
629 /*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)
630 CTF Starting point for a player in team four (Purple).
631 Keys: "angle" viewing angle when spawning. */
632 void spawnfunc_info_player_team4()
634 if(g_assault) { remove(self); return; }
636 self.team = COLOR_TEAM4; // purple
637 spawnfunc_info_player_deathmatch();
640 /*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
641 CTF flag for team one (Red). Multiple flags are allowed.
643 "angle" Angle the flag will point (minus 90 degrees)...
644 "model" model to use, note this needs red and blue as skins 0 and 1 (default models/ctf/flag.md3)...
645 "noise" sound played when flag is picked up (default ctf/take.wav)...
646 "noise1" sound played when flag is returned by a teammate (default ctf/return.wav)...
647 "noise2" sound played when flag is captured (default ctf/redcapture.wav)...
648 "noise3" sound played when flag is lost in the field and respawns itself (default ctf/respawn.wav)... */
649 void spawnfunc_item_flag_team1()
651 if(!g_ctf) { remove(self); return; }
653 ctf_SetupFlag(1, self);
656 /*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
657 CTF flag for team two (Blue). Multiple flags are allowed.
659 "angle" Angle the flag will point (minus 90 degrees)...
660 "model" model to use, note this needs red and blue as skins 0 and 1 (default models/ctf/flag.md3)...
661 "noise" sound played when flag is picked up (default ctf/take.wav)...
662 "noise1" sound played when flag is returned by a teammate (default ctf/return.wav)...
663 "noise2" sound played when flag is captured (default ctf/redcapture.wav)...
664 "noise3" sound played when flag is lost in the field and respawns itself (default ctf/respawn.wav)... */
665 void spawnfunc_item_flag_team2()
667 if(!g_ctf) { remove(self); return; }
669 ctf_SetupFlag(0, self);
672 /*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
673 Team declaration for CTF gameplay, this allows you to decide what team names and control point models are used in your map.
674 Note: If you use spawnfunc_ctf_team entities you must define at least 2! However, unlike domination, you don't need to make a blank one too.
676 "netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)...
677 "cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */
678 void spawnfunc_ctf_team()
680 if(!g_ctf) { remove(self); return; }
682 self.classname = "ctf_team";
683 self.team = self.cnt + 1;
691 // code from here on is just to support maps that don't have flag and team entities
692 void ctf_SpawnTeam (string teamname, float teamcolor)
694 local entity oldself;
697 self.classname = "ctf_team";
698 self.netname = teamname;
699 self.cnt = teamcolor;
701 spawnfunc_ctf_team();
706 void ctf_DelayedInit()
708 // if no teams are found, spawn defaults
709 if(find(world, classname, "ctf_team") == world)
711 ctf_SpawnTeam("Red", COLOR_TEAM1 - 1);
712 ctf_SpawnTeam("Blue", COLOR_TEAM2 - 1);
716 void ctf_Initialize()
718 flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
720 captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
721 captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
722 captureshield_force = autocvar_g_ctf_shield_force;
724 g_ctf_win_mode = cvar("g_ctf_win_mode");
728 InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
732 MUTATOR_DEFINITION(gamemode_ctf)
734 MUTATOR_HOOK(MakePlayerObserver, ctf_RemovePlayer, CBC_ORDER_ANY);
735 MUTATOR_HOOK(ClientDisconnect, ctf_RemovePlayer, CBC_ORDER_ANY);
736 MUTATOR_HOOK(PlayerDies, ctf_RemovePlayer, CBC_ORDER_ANY);
737 //MUTATOR_HOOK(GiveFragsForKill, ctf_GiveFragsForKill, CBC_ORDER_ANY);
738 //MUTATOR_HOOK(PlayerPreThink, ctf_PlayerPreThink, CBC_ORDER_ANY);
739 //MUTATOR_HOOK(PlayerDamage_Calculate, ctf_PlayerDamage, CBC_ORDER_ANY);
740 //MUTATOR_HOOK(PlayerPowerups, ctf_PlayerPowerups, CBC_ORDER_ANY);
744 if(time > 1) // game loads at time 1
745 error("This is a game type and it cannot be added at runtime.");
753 error("This is a game type and it cannot be removed at runtime.");