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
14 entity ctf_worldflaglist; // CTF flags in the map
15 .entity ctf_worldflagnext;
17 .float ctf_dropperid; // Don't allow spam of dropping the flag
19 .float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
21 // Delay between when the person can pick up a flag // replace with .wait?
22 .float next_take_time;
24 // Record time for capturing the flag
25 float flagcaptimerecord;
26 .float flagpickuptime;
28 // CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag.
29 .float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
30 float captureshield_min_negscore; // punish at -20 points
31 float captureshield_max_ratio; // punish at most 30% of each team
32 float captureshield_force; // push force of the shield
35 // declare functions so they can be used in any order in the file
36 void ctf_TouchEvent(void);
37 void ctf_FlagThink(void);
38 void ctf_SetupFlag(float, entity);
45 float ctf_ReadScore(string parameter)
47 if(g_ctf_win_mode != 2)
48 return cvar(strcat("g_ctf_personal", parameter));
50 return cvar(strcat("g_ctf_flag", parameter));
53 void ctf_FakeTimeLimit(entity e, float t)
56 WriteByte(MSG_ONE, 3); // svc_updatestat
57 WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
59 WriteCoord(MSG_ONE, autocvar_timelimit);
61 WriteCoord(MSG_ONE, (t + 1) / 60);
64 void ctf_EventLog(string mode, float flagteam, entity actor)
67 if(!autocvar_sv_eventlog)
69 s = strcat(":ctf:", mode);
70 s = strcat(s, ":", ftos(flagteam));
72 s = strcat(s, ":", ftos(actor.playerid));
76 void ctf_CaptureShockwave(vector org)
78 shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1);
81 void ctf_CreateBaseWaypoints(entity flag, float teamnumber)
84 waypoint_spawnforitem_force(flag, flag.origin);
85 flag.nearestwaypointtimeout = 0; // activate waypointing again
86 flag.bot_basewaypoint = flag.nearestwaypoint;
89 WaypointSprite_SpawnFixed(((teamnumber) ? "redbase" : "bluebase"), flag.origin + '0 0 61', flag, wps_flagbase);
90 WaypointSprite_UpdateTeamRadar(flag.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2) - 1, FALSE));
93 void ctf_SetStatus_ForType(entity flag, float type)
95 if(flag.cnt == FLAG_CARRY)
97 if(flag.owner == self)
98 self.items |= type * 3; // carrying: self is currently carrying the flag
100 self.items |= type * 1; // taken: someone on self's team is carrying the flag
102 else if(flag.cnt == FLAG_DROPPED)
103 self.items |= type * 2; // lost: the flag is dropped somewhere on the map
109 float redflags, blueflags;
112 // initially clear items so they can be set as necessary later.
113 self.items &~= (IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED);
115 // item for stopping players from capturing the flag too often
116 if(self.ctf_captureshielded)
117 self.items |= IT_CTF_SHIELDED;
119 // figure out what flags we already own
120 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
122 if(flag.items & IT_KEY2) // blue
124 else if(flag.items & IT_KEY1) // red
128 // blinking magic: if there is more than one flag, show one of these in a clever way // wtf?
130 redflags = mod(floor(time * redflags * 0.75), redflags);
133 blueflags = mod(floor(time * blueflags * 0.75), blueflags);
135 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
137 if(flag.items & IT_KEY2) // blue
139 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.
140 ctf_SetStatus_ForType(flag, IT_RED_FLAG_TAKEN);
142 else if(flag.items & IT_KEY1) // red
144 if(--blueflags == -1) // happens exactly once
145 ctf_SetStatus_ForType(flag, IT_BLUE_FLAG_TAKEN);
152 DropFlag(self, world, world);
158 // ===================
159 // Main Flag Functions
160 // ===================
162 void ctf_SetupFlag(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc
165 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.
166 string flag_team_by_name;
169 flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist // todo: find out if this can be simplified
170 ctf_worldflaglist = flag;
172 setattachment(flag, world, "");
174 flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
175 flag.team = ((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2); // COLOR_TEAM1: color 4 team (red) - COLOR_TEAM2: color 13 team (blue)
176 flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
177 flag.classname = "item_flag_team";
178 flag.target = "###item###"; // wut?
179 flag.flags = FL_ITEM;
180 flag.solid = SOLID_TRIGGER;
181 flag.velocity = '0 0 0';
182 flag.ctf_status = FLAG_BASE;
183 flag.mangle = flag.angles;
184 flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
186 if((flag.spawnflags & 1)
189 flag.dropped_origin = flag.origin;
190 flag.movetype = MOVETYPE_NONE;
194 flag.noalign = FALSE;
196 flag.movetype = MOVETYPE_TOSS;
199 flag.reset = ctf_Reset;
200 flag.touch = ctf_TouchEvent;
201 flag.think = ctf_RespawnFlag;
202 flag.nextthink = time + 0.2; // start after doors etc // Samual: 0.2 though? Why?
205 if(!flag.model) { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); }
206 setmodel (flag, flag.model); // precision set below
207 setsize(flag, FLAG_MIN, FLAG_MAX);
208 setorigin(flag, flag.origin + '0 0 37');
209 flag.origin_z = flag.origin_z + 6; // why 6?
210 if(!flag.scale) { flag.scale = 0.6; }
212 flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin);
214 if(autocvar_g_ctf_flag_glowtrails)
216 flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
221 flag.effects |= EF_LOWPRECISION;
222 if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; }
223 if(autocvar_g_ctf_dynamiclights) { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); }
226 if(!flag.noise) { flag.noise = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); }
227 if(!flag.noise1) { flag.noise1 = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); }
228 if(!flag.noise2) { flag.noise2 = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag
229 if(!flag.noise3) { flag.noise3 = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
230 if(!flag.noise4) { flag.noise4 = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); }
233 precache_sound(flag.noise);
234 precache_sound(flag.noise1);
235 precache_sound(flag.noise2);
236 precache_sound(flag.noise3);
237 precache_sound(flag.noise4);
238 precache_model(flag.model);
239 precache_model("models/ctf/shield.md3");
240 precache_model("models/ctf/shockwavetransring.md3");
242 // other initialization stuff
243 ctf_CreateBaseWaypoints(flag, teamnumber);
244 ctf_CaptureShield_Spawn(flag, teamnumber);
245 //InitializeEntity(self, ctf_CaptureShield_Spawn, INITPRIO_SETLOCATION);
248 void ctf_RespawnFlag(entity flag)
252 void ctf_RegenFlag(entity e)
254 if(self.classname != "item_flag_team") { backtrace("ctf_RegenFlag was called incorrectly."); return; }
256 if(e.waypointsprite_attachedforcarrier)
257 WaypointSprite_DetachCarrier(e);
259 setattachment(e, world, "");
260 e.damageforcescale = 0;
261 e.takedamage = DAMAGE_NO;
262 e.movetype = MOVETYPE_NONE;
264 e.movetype = MOVETYPE_TOSS;
265 e.velocity = '0 0 0';
266 e.solid = SOLID_TRIGGER;
267 // TODO: play a sound here
268 setorigin(e, e.dropped_origin);
272 e.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.
275 void ctf_ReturnFlag(entity e)
277 if(e.classname != "item_flag_team") { backtrace("ctf_ReturnFlag was called incorrectly."); return; }
280 if(e.owner.flagcarried == e)
282 WaypointSprite_DetachCarrier(e.owner);
283 e.owner.flagcarried = world;
286 ctf_FakeTimeLimit(e.owner, -1);
292 void ctf_Handle_Drop(entity player)
294 entity flag = player.flagcarried;
296 if(!flag) { return; }
297 if(flag.speedrunning) { ReturnFlag(flag); return; }
300 setattachment(flag, world, "");
301 flag.owner.flagcarried = world;
303 flag.ctf_status = FLAG_DROPPED;
304 flag.movetype = MOVETYPE_TOSS;
305 flag.solid = SOLID_TRIGGER;
306 flag.damageforcescale = autocvar_g_balance_ctf_damageforcescale; // should this really be set here? I don't think so
307 flag.takedamage = DAMAGE_YES;
308 flag.flags = FL_ITEM; // does this need set?
309 setorigin(flag, p.origin - '0 0 24' + '0 0 37');
310 flag.ctf_status = FLAG_DROPPED;
311 flag.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
312 flag.pain_finished = time + autocvar_g_ctf_flag_returntime;//30;
314 flag.ctf_droptime = time;
319 // messages and sounds
320 Send_KillNotification(carrier.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO);
321 sound(flag, CHAN_TRIGGER, flag.noise4, VOL_BASE, ATTN_NONE);
322 ctf_EventLog("dropped", carrier.team, carrier);
325 PlayerScore_Add(carrier, SP_CTF_DROPS, 1);
327 UpdateFrags(penalty_receiver, -ctf_score_value("penalty_suicidedrop"));
329 UpdateFrags(carrier, -ctf_score_value("penalty_drop"));
332 WaypointSprite_Spawn("flagdropped", 0, 0, flag, '0 0 64', world, (COLOR_TEAM1 + COLOR_TEAM2 - flag.team), flag, waypointsprite_attachedforcarrier, FALSE);
333 WaypointSprite_Ping(carrier.waypointsprite_attachedforcarrier);
334 WaypointSprite_DetachCarrier(carrier);
336 ctf_captureshield_update(carrier, 0); // shield only
339 trace_startsolid = FALSE;
340 tracebox(flag.origin, flag.mins, flag.maxs, flag.origin, TRUE, flag);
342 dprint("FLAG FALLTHROUGH will happen SOON\n");
349 self.nextthink = time + 0.1;
351 // sorry, we have to reset the flag size if it got squished by something
352 if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX)
354 // if we can grow back, grow back
355 tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
356 if(!trace_startsolid)
357 setsize(self, FLAG_MIN, FLAG_MAX);
360 if(self == ctf_worldflaglist) // only for the first flag
363 ctf_captureshield_update(e, 1); // release shield only
366 if(self.speedrunning)
367 if(self.cnt == FLAG_CARRY)
370 if(flagcaptimerecord)
371 if(time >= self.flagpickuptime + flagcaptimerecord)
373 bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n");
375 sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
376 self.owner.impulse = 141; // returning!
387 if(self.cnt == FLAG_BASE)
390 if(self.cnt == FLAG_DROPPED)
392 // flag fallthrough? FIXME remove this if bug is really fixed now
393 if(self.origin_z < -131072)
395 dprint("FLAG FALLTHROUGH just happened\n");
396 self.pain_finished = 0;
398 setattachment(self, world, "");
399 if(time > self.pain_finished)
401 bprint("The ", self.netname, " has returned to base\n");
402 sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
403 ctf_EventLog("returned", self.team, world);
410 if(e.classname != "player" || (e.deadflag) || (e.flagcarried != self))
412 dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n");
413 DropFlag(self, world, world);
417 if(autocvar_g_ctf_allow_drop)
419 DropFlag(self, e, world);
422 void ctf_TouchEvent()
424 if(gameover) { return; }
425 if(!self) { return; }
426 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
427 { // The ball fell off the map, respawn it since players can't get to it
431 if(other.deadflag != DEAD_NO) { return; }
432 if(other.classname != "player")
433 { // the flag just touched an object, most likely the world
434 pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
435 sound(self, CHAN_AUTO, "keepaway/touch.wav", VOL_BASE, ATTN_NORM);
438 else if(self.wait > time) { return; }
440 switch(self.ctf_status)
443 if((other.team == self.team) && (other.flagcarried) && (other.flagcarried.team != self.team))
444 ctf_Handle_Capture(self, other); // other just captured the enemies flag to his base
445 else if((other.team != self.team) && (!other.flagcarried) && (!other.ctf_captureshielded))
446 ctf_Handle_Pickup_Base(self, other); // other just stole the enemies flag
450 if(other.team == self.team)
451 ctf_Handle_Return(self, other); // other just returned his own flag
452 else if((!other.flagcarried) && ((other.playerid != self.ctf_dropperid) || (time > self.ctf_droptime + autocvar_g_balance_ctf_delay_collect)))
453 ctf_Handle_Pickup_Dropped(self, other); // other just picked up a dropped enemy flag
458 dprint("Someone touched a flag even though it was being carried? wtf?\n");
459 break; // this should never happen
464 // =======================
465 // CaptureShield Functions
466 // =======================
468 float ctf_CaptureShield_CheckStatus(entity p) // check to see
472 float players_worseeq, players_total;
474 if(captureshield_max_ratio <= 0)
477 s = PlayerScore_Add(p, SP_SCORE, 0);
478 if(s >= -captureshield_min_negscore)
481 players_total = players_worseeq = 0;
486 se = PlayerScore_Add(e, SP_SCORE, 0);
492 // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
493 // use this rule here
495 if(players_worseeq >= players_total * captureshield_max_ratio)
501 void ctf_CaptureShield_Update(entity p, float dir)
504 if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only
506 should = ctf_CaptureShield_CheckStatus(p);
509 if(should) // TODO csqc notifier for this
510 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.");
512 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.");
514 p.ctf_captureshielded = should;
519 float ctf_CaptureShield_Customize()
521 if not(other.ctf_captureshielded)
523 if(self.team == other.team)
528 void ctf_CaptureShield_Touch()
530 if not(other.ctf_captureshielded)
532 if(self.team == other.team)
536 mymid = (self.absmin + self.absmax) * 0.5;
537 othermid = (other.absmin + other.absmax) * 0.5;
538 Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force);
539 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.");
542 void ctf_CaptureShield_Spawn()
548 e.touch = ctf_CaptureShield_Touch;
549 e.customizeentityforclient = ctf_CaptureShield_Customize;
550 e.classname = "ctf_captureshield";
551 e.effects = EF_ADDITIVE;
552 e.movetype = MOVETYPE_NOCLIP;
553 e.solid = SOLID_TRIGGER;
554 e.avelocity = '7 0 11';
555 setorigin(e, self.origin);
556 setmodel(e, "models/ctf/shield.md3");
558 setsize(e, e.scale * e.mins, e.scale * e.maxs);
566 MUTATOR_HOOKFUNCTION(ctf_RemovePlayer)
568 if(self.flagcarried) { ctf_DropEvent(self); } // figure this out
578 /*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
579 CTF Starting point for a player in team one (Red).
580 Keys: "angle" viewing angle when spawning. */
581 void spawnfunc_info_player_team1()
583 if(g_assault) { remove(self); return; }
585 self.team = COLOR_TEAM1; // red
586 spawnfunc_info_player_deathmatch();
590 /*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)
591 CTF Starting point for a player in team two (Blue).
592 Keys: "angle" viewing angle when spawning. */
593 void spawnfunc_info_player_team2()
595 if(g_assault) { remove(self); return; }
597 self.team = COLOR_TEAM2; // blue
598 spawnfunc_info_player_deathmatch();
601 /*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)
602 CTF Starting point for a player in team three (Yellow).
603 Keys: "angle" viewing angle when spawning. */
604 void spawnfunc_info_player_team3()
606 if(g_assault) { remove(self); return; }
608 self.team = COLOR_TEAM3; // yellow
609 spawnfunc_info_player_deathmatch();
613 /*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)
614 CTF Starting point for a player in team four (Purple).
615 Keys: "angle" viewing angle when spawning. */
616 void spawnfunc_info_player_team4()
618 if(g_assault) { remove(self); return; }
620 self.team = COLOR_TEAM4; // purple
621 spawnfunc_info_player_deathmatch();
624 /*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
625 CTF flag for team one (Red). Multiple flags are allowed.
627 "angle" Angle the flag will point (minus 90 degrees)...
628 "model" model to use, note this needs red and blue as skins 0 and 1 (default models/ctf/flag.md3)...
629 "noise" sound played when flag is picked up (default ctf/take.wav)...
630 "noise1" sound played when flag is returned by a teammate (default ctf/return.wav)...
631 "noise2" sound played when flag is captured (default ctf/redcapture.wav)...
632 "noise3" sound played when flag is lost in the field and respawns itself (default ctf/respawn.wav)... */
633 void spawnfunc_item_flag_team1()
635 if(!g_ctf) { remove(self); return; }
637 ctf_SetupFlag(1, self);
640 /*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
641 CTF flag for team two (Blue). 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_team2()
651 if(!g_ctf) { remove(self); return; }
653 ctf_SetupFlag(0, self);
656 /*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
657 Team declaration for CTF gameplay, this allows you to decide what team names and control point models are used in your map.
658 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.
660 "netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)...
661 "cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */
662 void spawnfunc_ctf_team()
664 if(!g_ctf) { remove(self); return; }
666 self.classname = "ctf_team";
667 self.team = self.cnt + 1;
675 // code from here on is just to support maps that don't have flag and team entities
676 void ctf_SpawnTeam (string teamname, float teamcolor)
678 local entity oldself;
681 self.classname = "ctf_team";
682 self.netname = teamname;
683 self.cnt = teamcolor;
685 spawnfunc_ctf_team();
690 void ctf_DelayedInit()
692 // if no teams are found, spawn defaults
693 if(find(world, classname, "ctf_team") == world)
695 ctf_SpawnTeam("Red", COLOR_TEAM1 - 1);
696 ctf_SpawnTeam("Blue", COLOR_TEAM2 - 1);
700 void ctf_Initialize()
702 flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
704 captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
705 captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
706 captureshield_force = autocvar_g_ctf_shield_force;
708 g_ctf_win_mode = cvar("g_ctf_win_mode");
712 InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
716 MUTATOR_DEFINITION(gamemode_ctf)
718 MUTATOR_HOOK(MakePlayerObserver, ctf_RemovePlayer, CBC_ORDER_ANY);
719 MUTATOR_HOOK(ClientDisconnect, ctf_RemovePlayer, CBC_ORDER_ANY);
720 MUTATOR_HOOK(PlayerDies, ctf_RemovePlayer, CBC_ORDER_ANY);
721 //MUTATOR_HOOK(GiveFragsForKill, ctf_GiveFragsForKill, CBC_ORDER_ANY);
722 //MUTATOR_HOOK(PlayerPreThink, ctf_PlayerPreThink, CBC_ORDER_ANY);
723 //MUTATOR_HOOK(PlayerDamage_Calculate, ctf_PlayerDamage, CBC_ORDER_ANY);
724 //MUTATOR_HOOK(PlayerPowerups, ctf_PlayerPowerups, CBC_ORDER_ANY);
728 if(time > 1) // game loads at time 1
729 error("This is a game type and it cannot be added at runtime.");
737 error("This is a game type and it cannot be removed at runtime.");