]> git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/ctf.qc
Lastly, show portraits when they need to be shown, and sent the right player. Current...
[voretournament/voretournament.git] / data / qcsrc / server / ctf.qc
1 #define FLAG_MIN (PL_MIN + '0 0 -13')\r
2 #define FLAG_MAX (PL_MAX + '0 0 -13')\r
3 \r
4 .entity basewaypoint;\r
5 .entity sprite;\r
6 entity ctf_worldflaglist; // CTF flags in the map\r
7 .entity ctf_worldflagnext;\r
8 .float dropperid;\r
9 .float ctf_droptime;\r
10 \r
11 .float next_take_time;                  // the next time a player can pick up a flag (time + blah)\r
12                                                                 /// I used this, in part, to fix the looping score bug. - avirox\r
13 //float FLAGSCORE_PICKUP        =  1;\r
14 //float FLAGSCORE_RETURN        =  5; // returned by owner team\r
15 //float FLAGSCORE_RETURNROGUE   = 10; // returned by rogue team\r
16 //float FLAGSCORE_CAPTURE       =  5;\r
17 \r
18 #define FLAG_CARRY_POS '-15 0 7'\r
19 \r
20 .float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture\r
21 \r
22 float captureshield_min_negscore; // punish at -20 points\r
23 float captureshield_max_ratio; // punish at most 30% of each team\r
24 float captureshield_force; // push force of the shield\r
25 \r
26 float ctf_captureshield_shielded(entity p)\r
27 {\r
28         float s, se;\r
29         entity e;\r
30         float players_worseeq, players_total;\r
31 \r
32         if(captureshield_max_ratio <= 0)\r
33                 return FALSE;\r
34 \r
35         s = PlayerScore_Add(p, SP_SCORE, 0);\r
36         if(s >= -captureshield_min_negscore)\r
37                 return FALSE;\r
38 \r
39         players_total = players_worseeq = 0;\r
40         FOR_EACH_PLAYER(e)\r
41         {\r
42                 if(e.team != p.team)\r
43                         continue;\r
44                 se = PlayerScore_Add(e, SP_SCORE, 0);\r
45                 if(se <= s)\r
46                         ++players_worseeq;\r
47                 ++players_total;\r
48         }\r
49 \r
50         // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse\r
51         // use this rule here\r
52         \r
53         if(players_worseeq >= players_total * captureshield_max_ratio)\r
54                 return FALSE;\r
55 \r
56         return TRUE;\r
57 }\r
58 \r
59 void ctf_captureshield_update(entity p, float dir)\r
60 {\r
61         float should;\r
62         if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only\r
63         {\r
64                 should = ctf_captureshield_shielded(p);\r
65                 if(should != dir)\r
66                 {\r
67                         if(should)\r
68                         {\r
69                                 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.");\r
70                                 // TODO csqc notifier for this\r
71                         }\r
72                         else\r
73                         {\r
74                                 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.");\r
75                                 // TODO csqc notifier for this\r
76                         }\r
77                         p.ctf_captureshielded = should;\r
78                 }\r
79         }\r
80 }\r
81 \r
82 float ctf_captureshield_customize()\r
83 {\r
84         if not(other.ctf_captureshielded)\r
85                 return FALSE;\r
86         if(self.team == other.team)\r
87                 return FALSE;\r
88         return TRUE;\r
89 }\r
90 \r
91 void ctf_captureshield_touch()\r
92 {\r
93         if not(other.ctf_captureshielded)\r
94                 return;\r
95         if(self.team == other.team)\r
96                 return;\r
97         vector mymid;\r
98         vector othermid;\r
99         mymid = (self.absmin + self.absmax) * 0.5;\r
100         othermid = (other.absmin + other.absmax) * 0.5;\r
101         Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force);\r
102         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.");\r
103 }\r
104 \r
105 void ctf_flag_spawnstuff()\r
106 {\r
107         entity e;\r
108         e = spawn();\r
109         e.enemy = self;\r
110         e.team = self.team;\r
111         e.touch = ctf_captureshield_touch;\r
112         e.customizeentityforclient = ctf_captureshield_customize;\r
113         e.classname = "ctf_captureshield";\r
114         e.effects = EF_ADDITIVE;\r
115         e.movetype = MOVETYPE_NOCLIP;\r
116         e.solid = SOLID_TRIGGER;\r
117         e.avelocity = '7 0 11';\r
118         setorigin(e, self.origin);\r
119         setmodel(e, "models/ctf/shield.md3");\r
120         e.scale = 0.5;\r
121         setsize(e, e.scale * e.mins, e.scale * e.maxs);\r
122 \r
123         waypoint_spawnforitem_force(self, self.origin);\r
124         self.nearestwaypointtimeout = 0; // activate waypointing again\r
125         self.basewaypoint = self.nearestwaypoint;\r
126 \r
127         if(self.team == COLOR_TEAM1)\r
128         {\r
129                 WaypointSprite_SpawnFixed("redbase", self.origin + '0 0 61', self, sprite);\r
130                 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM1 - 1, FALSE));\r
131         }\r
132         else\r
133         {\r
134                 WaypointSprite_SpawnFixed("bluebase", self.origin + '0 0 61', self, sprite);\r
135                 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM2 - 1, FALSE));\r
136         }\r
137 }\r
138 \r
139 float ctf_score_value(string parameter)\r
140 {\r
141         if(g_ctf_win_mode != 2)\r
142                 return cvar(strcat("g_ctf_personal", parameter));\r
143         else\r
144                 return cvar(strcat("g_ctf_flag", parameter));\r
145 }\r
146 \r
147 void FakeTimeLimit(entity e, float t)\r
148 {\r
149         msg_entity = e;\r
150         WriteByte(MSG_ONE, 3); // svc_updatestat\r
151         WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT\r
152         if(t < 0)\r
153                 WriteCoord(MSG_ONE, cvar("timelimit"));\r
154         else\r
155                 WriteCoord(MSG_ONE, (t + 1) / 60);\r
156 }\r
157 \r
158 float   flagcaptimerecord;\r
159 .float  flagpickuptime;\r
160 //.float  iscommander;\r
161 //.float  ctf_state;\r
162 \r
163 void() FlagThink;\r
164 void() FlagTouch;\r
165 \r
166 void place_flag()\r
167 {\r
168         if(self.classname != "item_flag_team")\r
169         {\r
170                 backtrace("PlaceFlag a non-flag");\r
171                 return;\r
172         }\r
173 \r
174         if(!self.t_width)\r
175                 self.t_width = 0.1; // frame animation rate\r
176         if(!self.t_length)\r
177                 self.t_length = 58; // maximum frame\r
178 \r
179         setattachment(self, world, "");\r
180         self.mdl = self.model;\r
181         self.flags = FL_ITEM;\r
182         self.solid = SOLID_TRIGGER;\r
183         self.movetype = MOVETYPE_NONE;\r
184         self.velocity = '0 0 0';\r
185         self.origin_z = self.origin_z + 6;\r
186         self.think = FlagThink;\r
187         self.touch = FlagTouch;\r
188         self.nextthink = time + 0.1;\r
189         self.cnt = FLAG_BASE;\r
190         self.mangle = self.angles;\r
191         self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;\r
192         //self.effects = self.effects | EF_DIMLIGHT;\r
193         if(self.noalign)\r
194         {\r
195                 self.dropped_origin = self.origin;\r
196         }\r
197         else\r
198         {\r
199                 droptofloor();\r
200                 self.movetype = MOVETYPE_TOSS;\r
201         }\r
202 \r
203         InitializeEntity(self, ctf_flag_spawnstuff, INITPRIO_SETLOCATION);\r
204 };\r
205 \r
206 void LogCTF(string mode, float flagteam, entity actor)\r
207 {\r
208         string s;\r
209         if(!cvar("sv_eventlog"))\r
210                 return;\r
211         s = strcat(":ctf:", mode);\r
212         s = strcat(s, ":", ftos(flagteam));\r
213         if(actor != world)\r
214                 s = strcat(s, ":", ftos(actor.playerid));\r
215         GameLogEcho(s);\r
216 }\r
217 \r
218 void RegenFlag(entity e)\r
219 {\r
220         if(e.classname != "item_flag_team")\r
221         {\r
222                 backtrace("RegenFlag a non-flag");\r
223                 return;\r
224         }\r
225 \r
226         if(e.waypointsprite_attachedforcarrier)\r
227                 WaypointSprite_DetachCarrier(e);\r
228 \r
229         setattachment(e, world, "");\r
230         e.damageforcescale = 0;\r
231         e.takedamage = DAMAGE_NO;\r
232         e.movetype = MOVETYPE_NONE;\r
233         if(!e.noalign)\r
234                 e.movetype = MOVETYPE_TOSS;\r
235         e.velocity = '0 0 0';\r
236         e.solid = SOLID_TRIGGER;\r
237         // TODO: play a sound here\r
238         setorigin(e, e.dropped_origin);\r
239         e.angles = e.mangle;\r
240         e.cnt = FLAG_BASE;\r
241         e.owner = world;\r
242         e.flags = FL_ITEM; // clear FL_ONGROUND and any other junk\r
243 };\r
244 \r
245 void ReturnFlag(entity e)\r
246 {\r
247         if(e.classname != "item_flag_team")\r
248         {\r
249                 backtrace("ReturnFlag a non-flag");\r
250                 return;\r
251         }\r
252 \r
253         if (e.owner)\r
254         if (e.owner.flagcarried == e)\r
255         {\r
256                 WaypointSprite_DetachCarrier(e.owner);\r
257                 e.owner.flagcarried = world;\r
258 \r
259                 if(e.speedrunning)\r
260                         FakeTimeLimit(e.owner, -1);\r
261         }\r
262         e.owner = world;\r
263         RegenFlag(e);\r
264 };\r
265 \r
266 void DropFlag(entity e, entity penalty_receiver, entity attacker)\r
267 {\r
268         local entity p;\r
269 \r
270         if(e.classname != "item_flag_team")\r
271         {\r
272                 backtrace("DropFlag a non-flag");\r
273                 return;\r
274         }\r
275 \r
276         if(e.speedrunning)\r
277         {\r
278                 ReturnFlag(e);\r
279                 return;\r
280         }\r
281 \r
282         if (!e.owner)\r
283         {\r
284                 dprint("FLAG: drop - no owner?!?!\n");\r
285                 return;\r
286         }\r
287         p = e.owner;\r
288         if (p.flagcarried != e)\r
289         {\r
290                 dprint("FLAG: drop - owner is not carrying this flag??\n");\r
291                 return;\r
292         }\r
293         bprint(p.netname, "^7 lost the ", e.netname, "\n");\r
294 \r
295         if(penalty_receiver)\r
296                 UpdateFrags(penalty_receiver, -ctf_score_value("penalty_suicidedrop"));\r
297         else\r
298                 UpdateFrags(p, -ctf_score_value("penalty_drop"));\r
299         PlayerScore_Add(p, SP_CTF_DROPS, +1);\r
300         ctf_captureshield_update(p, 0); // shield only\r
301         e.playerid = attacker.playerid;\r
302         e.ctf_droptime = time;\r
303         WaypointSprite_Spawn("flagdropped", 0, 0, e, '0 0 1' * 61, world, COLOR_TEAM1 + COLOR_TEAM2 - e.team, e, waypointsprite_attachedforcarrier, FALSE);\r
304         \r
305         if(p.waypointsprite_attachedforcarrier)\r
306         {\r
307                 WaypointSprite_Ping(p.waypointsprite_attachedforcarrier);\r
308                 WaypointSprite_DetachCarrier(p);\r
309         }\r
310         else\r
311         {\r
312                 bprint("\{1}^1Flag carrier had no flag sprite?!?\n");\r
313                 backtrace("Flag carrier had no flag sprite?!?");\r
314         }\r
315         LogCTF("dropped", p.team, p);\r
316         sound (self, CHAN_TRIGGER, self.noise4, VOL_BASE, ATTN_NONE);\r
317 \r
318         setattachment(e, world, "");\r
319         e.damageforcescale = cvar("g_balance_ctf_damageforcescale");\r
320         e.takedamage = DAMAGE_YES;\r
321 \r
322         if (p.flagcarried == e)\r
323                 p.flagcarried = world;\r
324         e.owner = world;\r
325 \r
326         e.flags = FL_ITEM; // clear FL_ONGROUND and any other junk\r
327         e.solid = SOLID_TRIGGER;\r
328         e.movetype = MOVETYPE_TOSS;\r
329         // setsize(e, '-16 -16 0', '16 16 74');\r
330         setorigin(e, p.origin - '0 0 24' + '0 0 37');\r
331         e.cnt = FLAG_DROPPED;\r
332         e.velocity = '0 0 300';\r
333         e.pain_finished = time + cvar("g_ctf_flag_returntime");//30;\r
334 \r
335         trace_startsolid = FALSE;\r
336         tracebox(e.origin, e.mins, e.maxs, e.origin, TRUE, e);\r
337         if(trace_startsolid)\r
338                 dprint("FLAG FALLTHROUGH will happen SOON\n");\r
339 };\r
340 \r
341 void AnimateFlag()\r
342 {\r
343         if(self.delay > time)\r
344                 return;\r
345         self.delay = time + self.t_width;\r
346         if(self.nextthink > self.delay)\r
347                 self.nextthink = self.delay;\r
348 \r
349         self.frame = self.frame + 1;\r
350         if(self.frame > self.t_length)\r
351                 self.frame = 0;\r
352 }\r
353 \r
354 void FlagThink()\r
355 {\r
356         local entity e;\r
357 \r
358         self.nextthink = time + 0.1;\r
359 \r
360         // sorry, we have to reset the flag size if it got squished by something\r
361         if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX)\r
362         {\r
363                 // if we can grow back, grow back\r
364                 tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);\r
365                 if(!trace_startsolid)\r
366                         setsize(self, FLAG_MIN, FLAG_MAX);\r
367         }\r
368 \r
369         if(self == ctf_worldflaglist) // only for the first flag\r
370         {\r
371                 FOR_EACH_CLIENT(e)\r
372                         ctf_captureshield_update(e, 1); // release shield only\r
373         }\r
374 \r
375         AnimateFlag();\r
376 \r
377         if(self.speedrunning)\r
378         if(self.cnt == FLAG_CARRY)\r
379         {\r
380                 if(self.owner)\r
381                 if(flagcaptimerecord)\r
382                 if(time >= self.flagpickuptime + flagcaptimerecord)\r
383                 {\r
384                         bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n");\r
385 \r
386                         sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);\r
387                         self.owner.impulse = 141; // returning!\r
388 \r
389                         e = self;\r
390                         self = self.owner;\r
391                         ReturnFlag(e);\r
392                         ImpulseCommands();\r
393                         self = e;\r
394                         return;\r
395                 }\r
396         }\r
397 \r
398         if (self.cnt == FLAG_BASE)\r
399                 return;\r
400 \r
401         if (self.cnt == FLAG_DROPPED)\r
402         {\r
403                 // flag fallthrough? FIXME remove this if bug is really fixed now\r
404                 if(self.origin_z < -131072)\r
405                 {\r
406                         dprint("FLAG FALLTHROUGH just happened\n");\r
407                         self.pain_finished = 0;\r
408                 }\r
409                 setattachment(self, world, "");\r
410                 if (time > self.pain_finished)\r
411                 {\r
412                         bprint("The ", self.netname, " has returned to base\n");\r
413                         sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);\r
414                         LogCTF("returned", self.team, world);\r
415                         ReturnFlag(self);\r
416                 }\r
417                 return;\r
418         }\r
419 \r
420         e = self.owner;\r
421         if (e.classname != "player" || (e.deadflag) || (e.flagcarried != self))\r
422         {\r
423                 dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n");\r
424                 DropFlag(self, world, world);\r
425                 return;\r
426         }\r
427 \r
428         if(cvar("g_ctf_allow_drop"))\r
429         if(e.BUTTON_USE)\r
430                 DropFlag(self, e, world);\r
431 };\r
432 \r
433 void flag_cap_ring_spawn(vector org)\r
434 {\r
435         shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1);\r
436 };\r
437 \r
438 void FlagTouch()\r
439 {\r
440         if(gameover) return;\r
441 \r
442         local float t;\r
443         local entity player;\r
444         local string s, s0, h0, h1;\r
445         if (other.classname != "player")\r
446                 return;\r
447         if (other.health < 1) // ignore dead players\r
448                 return;\r
449 \r
450         if (self.cnt == FLAG_CARRY)\r
451                 return;\r
452 \r
453         if (self.cnt == FLAG_BASE)\r
454         if (other.team == self.team)\r
455         if (other.flagcarried) // he's got a flag\r
456         if (other.flagcarried.team != self.team) // capture\r
457         {\r
458                 if (other.flagcarried == world)\r
459                 {\r
460                         return;\r
461                 }\r
462                 if(cvar("g_ctf_captimerecord_always") || player_count - currentbots <= 1) // at most one human\r
463                 {\r
464                         t = time - other.flagcarried.flagpickuptime;\r
465                         s = ftos_decimals(t, 2);\r
466                         s0 = ftos_decimals(flagcaptimerecord, 2);\r
467                         h0 = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));\r
468                         h1 = other.netname;\r
469                         if(h0 == h1)\r
470                                 h0 = "their";\r
471                         else\r
472                                 h0 = strcat(h0, "^7's"); // h0: display text for previous netname\r
473                         if (flagcaptimerecord == 0)\r
474                         {\r
475                                 bprint(other.netname, "^7 captured the ", other.flagcarried.netname, " in ", s, " seconds\n");\r
476                                 flagcaptimerecord = t;\r
477                                 db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t));\r
478                                 db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1);\r
479                                 write_recordmarker(other, time - t, t);\r
480                         }\r
481                         else if (t < flagcaptimerecord)\r
482                         {\r
483                                 bprint(other.netname, "^7 captured the ", other.flagcarried.netname, " in ", s, ", breaking ", strcat(h0, " previous record of ", s0, " seconds\n"));\r
484                                 flagcaptimerecord = t;\r
485                                 db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t));\r
486                                 db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1);\r
487                                 write_recordmarker(other, time - t, t);\r
488                         }\r
489                         else\r
490                         {\r
491                                 bprint(other.netname, "^7 captured the ", other.flagcarried.netname, " in ", s, ", failing to break ", strcat(h0, " record of ", s0, " seconds\n"));\r
492                         }\r
493                 }\r
494                 else\r
495                         bprint(other.netname, "^7 captured the ", other.flagcarried.netname, "\n");\r
496 \r
497                 PlayerTeamScore_Add(other, SP_CTF_CAPS, ST_CTF_CAPS, 1);\r
498                 LogCTF("capture", other.flagcarried.team, other);\r
499                 // give credit to the individual player\r
500                 UpdateFrags(other, ctf_score_value("score_capture"));\r
501 \r
502                 if (cvar("g_ctf_flag_capture_effects")) {\r
503                         if (other.team == COLOR_TEAM1) { // red team scores effect\r
504                                 pointparticles(particleeffectnum("red_ground_quake"), self.origin, '0 0 0', 1);\r
505                                 flag_cap_ring_spawn(self.origin);\r
506                         }\r
507                         if (other.team == COLOR_TEAM2) { // blue team scores effect\r
508                                 pointparticles(particleeffectnum("blue_ground_quake"), self.origin, '0 0 0', 1);\r
509                                 flag_cap_ring_spawn(self.origin);\r
510                         }\r
511                 }\r
512 \r
513                 sound (other, CHAN_AUTO, self.noise2, VOL_BASE, ATTN_NONE);\r
514                 WaypointSprite_DetachCarrier(other);\r
515                 if(self.speedrunning)\r
516                         FakeTimeLimit(other, -1);\r
517                 RegenFlag (other.flagcarried);\r
518                 other.flagcarried = world;\r
519                 other.next_take_time = time + 1;\r
520         }\r
521         if (self.cnt == FLAG_BASE)\r
522         if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2) // only red and blue team can steal flags\r
523         if (other.team != self.team)\r
524         if (!other.flagcarried)\r
525         if (!other.ctf_captureshielded)\r
526         {\r
527                 if (other.next_take_time > time)\r
528                         return;\r
529                         \r
530                 if (cvar("g_ctf_flag_pickup_effects")) // pickup effect\r
531                         pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1);\r
532                         \r
533                 // pick up\r
534                 self.flagpickuptime = time; // used for timing runs\r
535                 self.speedrunning = other.speedrunning; // if speedrunning, flag will self-return and teleport the owner back after the record\r
536                 if(other.speedrunning)\r
537                 if(flagcaptimerecord)\r
538                         FakeTimeLimit(other, time + flagcaptimerecord);\r
539                 self.solid = SOLID_NOT;\r
540                 setorigin(self, self.origin); // relink\r
541                 self.owner = other;\r
542                 other.flagcarried = self;\r
543                 self.cnt = FLAG_CARRY;\r
544                 self.angles = '0 0 0';\r
545                 bprint(other.netname, "^7 got the ", self.netname, "\n");\r
546                 UpdateFrags(other, ctf_score_value("score_pickup_base"));\r
547                 self.dropperid = other.playerid;\r
548                 PlayerScore_Add(other, SP_CTF_PICKUPS, 1);\r
549                 LogCTF("steal", self.team, other);\r
550                 sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NONE);\r
551 \r
552                 FOR_EACH_PLAYER(player)\r
553                         if(player.team == self.team)\r
554                                 centerprint(player, "The enemy got your flag! Retrieve it!");\r
555 \r
556                 self.movetype = MOVETYPE_NONE;\r
557                 setorigin(self, FLAG_CARRY_POS);\r
558                 setattachment(self, other, "");\r
559                 WaypointSprite_AttachCarrier("flagcarrier", other);\r
560                 WaypointSprite_UpdateTeamRadar(other.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '1 1 0');\r
561                 WaypointSprite_Ping(self.sprite);\r
562 \r
563                 return;\r
564         }\r
565 \r
566         if (self.cnt == FLAG_DROPPED)\r
567         {\r
568                 self.flags = FL_ITEM; // clear FL_ONGROUND and any other junk\r
569                 if (other.team == self.team || (other.team != COLOR_TEAM1 && other.team != COLOR_TEAM2))\r
570                 {\r
571                         // return flag\r
572                         bprint(other.netname, "^7 returned the ", self.netname, "\n");\r
573 \r
574                         // punish the player who last had it\r
575                         FOR_EACH_PLAYER(player)\r
576                                 if(player.playerid == self.dropperid)\r
577                                 {\r
578                                         PlayerScore_Add(player, SP_SCORE, -ctf_score_value("penalty_returned"));\r
579                                         ctf_captureshield_update(player, 0); // shield only\r
580                                 }\r
581 \r
582                         // punish the team who was last carrying it\r
583                         if(self.team == COLOR_TEAM1)\r
584                                 TeamScore_AddToTeam(COLOR_TEAM2, ST_SCORE, -ctf_score_value("penalty_returned"));\r
585                         else\r
586                                 TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, -ctf_score_value("penalty_returned"));\r
587 \r
588                         // reward the player who returned it\r
589                         if(other.playerid == self.playerid) // is this the guy who killed the FC last?\r
590                         {\r
591                                 if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)\r
592                                         UpdateFrags(other, ctf_score_value("score_return_by_killer"));\r
593                                 else\r
594                                         UpdateFrags(other, ctf_score_value("score_return_rogue_by_killer"));\r
595                         }\r
596                         else\r
597                         {\r
598                                 if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)\r
599                                         UpdateFrags(other, ctf_score_value("score_return"));\r
600                                 else\r
601                                         UpdateFrags(other, ctf_score_value("score_return_rogue"));\r
602                         }\r
603                         PlayerScore_Add(other, SP_CTF_RETURNS, 1);\r
604                         LogCTF("return", self.team, other);\r
605                         sound (other, CHAN_AUTO, self.noise1, VOL_BASE, ATTN_NONE);\r
606                         ReturnFlag(self);\r
607                 }\r
608                 else if (!other.flagcarried && (other.playerid != self.dropperid || time > self.ctf_droptime + cvar("g_balance_ctf_delay_collect")))\r
609                 {\r
610                         if(self.waypointsprite_attachedforcarrier)\r
611                                 WaypointSprite_DetachCarrier(self);\r
612 \r
613                         if (cvar("g_ctf_flag_pickup_effects")) // field pickup effect\r
614                                 pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1);\r
615                         \r
616                         // pick up\r
617                         self.solid = SOLID_NOT;\r
618                         setorigin(self, self.origin); // relink\r
619                         self.owner = other;\r
620                         other.flagcarried = self;\r
621                         self.cnt = FLAG_CARRY;\r
622                         bprint(other.netname, "^7 picked up the ", self.netname, "\n");\r
623 \r
624                         float f;\r
625                         f = bound(0, (self.pain_finished - time) / cvar("g_ctf_flag_returntime"), 1);\r
626                         //print("factor is ", ftos(f), "\n");\r
627                         f = ctf_score_value("score_pickup_dropped_late") * (1-f)\r
628                           + ctf_score_value("score_pickup_dropped_early") * f;\r
629                         f = floor(f + 0.5);\r
630                         self.dropperid = other.playerid;\r
631                         //print("score is ", ftos(f), "\n");\r
632 \r
633                         UpdateFrags(other, f);\r
634                         PlayerScore_Add(other, SP_CTF_PICKUPS, 1);\r
635                         LogCTF("pickup", self.team, other);\r
636                         sound (other, CHAN_AUTO, self.noise, VOL_BASE, ATTN_NONE);\r
637 \r
638                         FOR_EACH_PLAYER(player)\r
639                                 if(player.team == self.team)\r
640                                         centerprint(player, "The enemy got your flag! Retrieve it!");\r
641 \r
642                         self.movetype = MOVETYPE_NONE;  // flag must have MOVETYPE_NONE here, otherwise it will drop through the floor...\r
643                         setorigin(self, FLAG_CARRY_POS);\r
644                         setattachment(self, other, "");\r
645                         self.damageforcescale = 0;\r
646                         self.takedamage = DAMAGE_NO;\r
647                         WaypointSprite_AttachCarrier("flagcarrier", other);\r
648                         WaypointSprite_UpdateTeamRadar(other.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '1 1 0');\r
649                 }\r
650         }\r
651 };\r
652 \r
653 /*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)\r
654 CTF Starting point for a player\r
655 in team one (Red).\r
656 \r
657 Keys:\r
658 "angle"\r
659  viewing angle when spawning\r
660 */\r
661 void spawnfunc_info_player_team1()\r
662 {\r
663         if(g_assault)\r
664         {\r
665                 remove(self);\r
666                 return;\r
667         }\r
668         self.team = COLOR_TEAM1; // red\r
669         spawnfunc_info_player_deathmatch();\r
670 };\r
671 //self.team = 4;self.classname = "info_player_start";spawnfunc_info_player_start();};\r
672 \r
673 /*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)\r
674 CTF Starting point for a player in\r
675 team two (Blue).\r
676 \r
677 Keys:\r
678 "angle"\r
679  viewing angle when spawning\r
680 */\r
681 void spawnfunc_info_player_team2()\r
682 {\r
683         if(g_assault)\r
684         {\r
685                 remove(self);\r
686                 return;\r
687         }\r
688         self.team = COLOR_TEAM2; // blue\r
689         spawnfunc_info_player_deathmatch();\r
690 };\r
691 //self.team = 13;self.classname = "info_player_start";spawnfunc_info_player_start();};\r
692 \r
693 /*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)\r
694 CTF Starting point for a player in\r
695 team three (Yellow).\r
696 \r
697 Keys:\r
698 "angle"\r
699  viewing angle when spawning\r
700 */\r
701 void spawnfunc_info_player_team3()\r
702 {\r
703         if(g_assault)\r
704         {\r
705                 remove(self);\r
706                 return;\r
707         }\r
708         self.team = COLOR_TEAM3; // yellow\r
709         spawnfunc_info_player_deathmatch();\r
710 };\r
711 \r
712 \r
713 /*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)\r
714 CTF Starting point for a player in\r
715 team four (Magenta).\r
716 \r
717 Keys:\r
718 "angle"\r
719  viewing angle when spawning\r
720 */\r
721 void spawnfunc_info_player_team4()\r
722 {\r
723         if(g_assault)\r
724         {\r
725                 remove(self);\r
726                 return;\r
727         }\r
728         self.team = COLOR_TEAM4; // purple\r
729         spawnfunc_info_player_deathmatch();\r
730 };\r
731 \r
732 void item_flag_reset()\r
733 {\r
734         DropFlag(self, world, world);\r
735         if(self.waypointsprite_attachedforcarrier)\r
736                 WaypointSprite_DetachCarrier(self);\r
737         ReturnFlag(self);\r
738 }\r
739 \r
740 void item_flag_postspawn()\r
741 { // Check CTF Item Flag Post Spawn\r
742 \r
743         // Flag Glow Trail Support\r
744         if(cvar("g_ctf_flag_glowtrails"))\r
745         { // Provide Flag Glow Trail\r
746                 if(self.team == COLOR_TEAM1)\r
747                         // Red\r
748                         self.glow_color = 251;\r
749                 else\r
750                 if(self.team == COLOR_TEAM2)\r
751                         // Blue\r
752                         self.glow_color = 210;\r
753                         \r
754                 self.glow_size = 25;\r
755                 self.glow_trail = 1;\r
756         }\r
757 };\r
758 \r
759 /*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)\r
760 CTF flag for team one (Red).\r
761 Multiple are allowed.\r
762 \r
763 Keys:\r
764 "angle"\r
765  Angle the flag will point\r
766 (minus 90 degrees)\r
767 "model"\r
768  model to use, note this needs red and blue as skins 0 and 1\r
769  (default models/ctf/flag.md3)\r
770 "noise"\r
771  sound played when flag is picked up\r
772  (default ctf/take.wav)\r
773 "noise1"\r
774  sound played when flag is returned by a teammate\r
775  (default ctf/return.wav)\r
776 "noise2"\r
777  sound played when flag is captured\r
778  (default ctf/redcapture.wav)\r
779 "noise3"\r
780  sound played when flag is lost in the field and respawns itself\r
781  (default ctf/respawn.wav)\r
782 */\r
783 \r
784 void spawnfunc_item_flag_team1()\r
785 {\r
786         if (!g_ctf)\r
787         {\r
788                 remove(self);\r
789                 return;\r
790         }\r
791 \r
792         //if(!cvar("teamplay"))\r
793         //      cvar_set("teamplay", "3");\r
794 \r
795         // link flag into ctf_worldflaglist\r
796         self.ctf_worldflagnext = ctf_worldflaglist;\r
797         ctf_worldflaglist = self;\r
798 \r
799         self.classname = "item_flag_team";\r
800         if(g_ctf_reverse)\r
801         {\r
802                 self.team = COLOR_TEAM2; // color 13 team (blue)\r
803                 self.items = IT_KEY1; // silver key (bluish enough)\r
804         }\r
805         else\r
806         {\r
807                 self.team = COLOR_TEAM1; // color 4 team (red)\r
808                 self.items = IT_KEY2; // gold key (redish enough)\r
809         }\r
810         self.netname = "^1RED^7 flag";\r
811         self.target = "###item###";\r
812         self.skin = cvar("g_ctf_flag_red_skin");\r
813         if(self.spawnflags & 1)\r
814                 self.noalign = 1;\r
815         if (!self.model)\r
816                 self.model = cvar_string("g_ctf_flag_red_model");\r
817         if (!self.noise)\r
818                 self.noise = "ctf/red_taken.wav";\r
819         if (!self.noise1)\r
820                 self.noise1 = "ctf/red_returned.wav";\r
821         if (!self.noise2)\r
822                 self.noise2 = "ctf/red_capture.wav"; // blue team scores by capturing the red flag\r
823         if (!self.noise3)\r
824                 self.noise3 = "ctf/flag_respawn.wav";\r
825         if (!self.noise4)\r
826                 self.noise4 = "ctf/red_dropped.wav";\r
827         precache_model (self.model);\r
828         setmodel (self, self.model); // precision set below\r
829         precache_sound (self.noise);\r
830         precache_sound (self.noise1);\r
831         precache_sound (self.noise2);\r
832         precache_sound (self.noise3);\r
833         precache_sound (self.noise4);\r
834         //setsize(self, '-16 -16 -37', '16 16 37');\r
835         setsize(self, FLAG_MIN, FLAG_MAX);\r
836         setorigin(self, self.origin + '0 0 37');\r
837         self.nextthink = time + 0.2; // start after doors etc\r
838         self.think = place_flag;\r
839 \r
840         if(!self.scale)\r
841                 self.scale = 0.6;\r
842         //if(!self.glow_size)\r
843         //      self.glow_size = 50;\r
844 \r
845         self.effects = self.effects | EF_LOWPRECISION;\r
846         if(cvar("g_ctf_fullbrightflags"))\r
847                 self.effects |= EF_FULLBRIGHT;\r
848         if(cvar("g_ctf_dynamiclights"))\r
849                 self.effects |= EF_RED;\r
850 \r
851         // From Spidflisk\r
852         item_flag_postspawn();\r
853 \r
854         precache_model("models/ctf/shield.md3");\r
855         precache_model("models/ctf/shockwavetransring.md3");\r
856 \r
857         self.reset = item_flag_reset;\r
858 };\r
859 \r
860 /*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -24) (48 48 64)\r
861 CTF flag for team two (Blue).\r
862 Multiple are allowed.\r
863 \r
864 Keys:\r
865 "angle"\r
866  Angle the flag will point\r
867 (minus 90 degrees)\r
868 "model"\r
869  model to use, note this needs red and blue as skins 0 and 1\r
870  (default models/ctf/flag.md3)\r
871 "noise"\r
872  sound played when flag is picked up\r
873  (default ctf/take.wav)\r
874 "noise1"\r
875  sound played when flag is returned by a teammate\r
876  (default ctf/return.wav)\r
877 "noise2"\r
878  sound played when flag is captured\r
879  (default ctf/bluecapture.wav)\r
880 "noise3"\r
881  sound played when flag is lost in the field and respawns itself\r
882  (default ctf/respawn.wav)\r
883 */\r
884 \r
885 void spawnfunc_item_flag_team2()\r
886 {\r
887         if (!g_ctf)\r
888         {\r
889                 remove(self);\r
890                 return;\r
891         }\r
892         //if(!cvar("teamplay"))\r
893         //      cvar_set("teamplay", "3");\r
894 \r
895         // link flag into ctf_worldflaglist\r
896         self.ctf_worldflagnext = ctf_worldflaglist;\r
897         ctf_worldflaglist = self;\r
898 \r
899         self.classname = "item_flag_team";\r
900         if(g_ctf_reverse)\r
901         {\r
902                 self.team = COLOR_TEAM1; // color 4 team (red)\r
903                 self.items = IT_KEY2; // gold key (redish enough)\r
904         }\r
905         else\r
906         {\r
907                 self.team = COLOR_TEAM2; // color 13 team (blue)\r
908                 self.items = IT_KEY1; // silver key (bluish enough)\r
909         }\r
910         self.netname = "^4BLUE^7 flag";\r
911         self.target = "###item###";\r
912         self.skin = cvar("g_ctf_flag_blue_skin");\r
913         if(self.spawnflags & 1)\r
914                 self.noalign = 1;\r
915         if (!self.model)\r
916                 self.model = cvar_string("g_ctf_flag_blue_model");\r
917         if (!self.noise)\r
918                 self.noise = "ctf/blue_taken.wav";\r
919         if (!self.noise1)\r
920                 self.noise1 = "ctf/blue_returned.wav";\r
921         if (!self.noise2)\r
922                 self.noise2 = "ctf/blue_capture.wav"; // blue team scores by capturing the red flag\r
923         if (!self.noise3)\r
924                 self.noise3 = "ctf/flag_respawn.wav";\r
925         if (!self.noise4)\r
926                 self.noise4 = "ctf/blue_dropped.wav";\r
927         precache_model (self.model);\r
928         setmodel (self, self.model); // precision set below\r
929         precache_sound (self.noise);\r
930         precache_sound (self.noise1);\r
931         precache_sound (self.noise2);\r
932         precache_sound (self.noise3);\r
933         precache_sound (self.noise4);\r
934         //setsize(self, '-16 -16 -37', '16 16 37');\r
935         setsize(self, FLAG_MIN, FLAG_MAX);\r
936         setorigin(self, self.origin + '0 0 37');\r
937         self.nextthink = time + 0.2; // start after doors etc\r
938         self.think = place_flag;\r
939 \r
940         if(!self.scale)\r
941                 self.scale = 0.6;\r
942         //if(!self.glow_size)\r
943         //      self.glow_size = 50;\r
944 \r
945         self.effects = self.effects | EF_LOWPRECISION;\r
946         if(cvar("g_ctf_fullbrightflags"))\r
947                 self.effects |= EF_FULLBRIGHT;\r
948         if(cvar("g_ctf_dynamiclights"))\r
949                 self.effects |= EF_BLUE;\r
950 \r
951         // From Spidflisk\r
952         item_flag_postspawn();\r
953 \r
954         precache_model("models/ctf/shield.md3");\r
955         precache_model("models/ctf/shockwavetransring.md3");\r
956 \r
957         self.reset = item_flag_reset;\r
958 };\r
959 \r
960 \r
961 /*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)\r
962 Team declaration for CTF gameplay, this allows you to decide what team\r
963 names and control point models are used in your map.\r
964 \r
965 Note: If you use spawnfunc_ctf_team entities you must define at least 2!  However, unlike\r
966 domination, you don't need to make a blank one too.\r
967 \r
968 Keys:\r
969 "netname"\r
970  Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)\r
971 "cnt"\r
972  Scoreboard color of the team (for example 4 is red and 13 is blue)\r
973 \r
974 */\r
975 \r
976 void spawnfunc_ctf_team()\r
977 {\r
978         if (!g_ctf)\r
979         {\r
980                 remove(self);\r
981                 return;\r
982         }\r
983         self.classname = "ctf_team";\r
984         self.team = self.cnt + 1;\r
985 };\r
986 \r
987 // code from here on is just to support maps that don't have control point and team entities\r
988 void ctf_spawnteam (string teamname, float teamcolor)\r
989 {\r
990         local entity oldself;\r
991         oldself = self;\r
992         self = spawn();\r
993         self.classname = "ctf_team";\r
994         self.netname = teamname;\r
995         self.cnt = teamcolor;\r
996 \r
997         spawnfunc_ctf_team();\r
998 \r
999         self = oldself;\r
1000 };\r
1001 \r
1002 // spawn some default teams if the map is not set up for ctf\r
1003 void ctf_spawnteams()\r
1004 {\r
1005         float numteams;\r
1006 \r
1007         numteams = 2;//cvar("g_ctf_default_teams");\r
1008 \r
1009         ctf_spawnteam("Red", COLOR_TEAM1 - 1);\r
1010         ctf_spawnteam("Blue", COLOR_TEAM2 - 1);\r
1011 };\r
1012 \r
1013 void ctf_delayedinit()\r
1014 {\r
1015         // if no teams are found, spawn defaults\r
1016         if (find(world, classname, "ctf_team") == world)\r
1017                 ctf_spawnteams();\r
1018 \r
1019         ScoreRules_ctf();\r
1020 };\r
1021 \r
1022 void ctf_init()\r
1023 {\r
1024         InitializeEntity(world, ctf_delayedinit, INITPRIO_GAMETYPE);\r
1025         flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));\r
1026 \r
1027         captureshield_min_negscore = cvar("g_ctf_shield_min_negscore");\r
1028         captureshield_max_ratio = cvar("g_ctf_shield_max_ratio");\r
1029         captureshield_force = cvar("g_ctf_shield_force");\r
1030 };\r
1031 \r
1032 void ctf_setstatus2(entity flag, float shift)\r
1033 {\r
1034         if (flag.cnt == FLAG_CARRY)\r
1035                 if (flag.owner == self)\r
1036                         self.items |= shift * 3;\r
1037                 else\r
1038                         self.items |= shift * 1;\r
1039         else if (flag.cnt == FLAG_DROPPED)\r
1040                 self.items |= shift * 2;\r
1041         else\r
1042         {\r
1043                 // no status bits\r
1044         }\r
1045 };\r
1046 \r
1047 void ctf_setstatus()\r
1048 {\r
1049         self.items &~= IT_RED_FLAG_TAKEN;\r
1050         self.items &~= IT_RED_FLAG_LOST;\r
1051         self.items &~= IT_BLUE_FLAG_TAKEN;\r
1052         self.items &~= IT_BLUE_FLAG_LOST;\r
1053         self.items &~= IT_CTF_SHIELDED;\r
1054 \r
1055         if (g_ctf) {\r
1056                 local entity flag;\r
1057                 float redflags, blueflags;\r
1058 \r
1059                 if(self.ctf_captureshielded)\r
1060                         self.items |= IT_CTF_SHIELDED;\r
1061 \r
1062                 redflags = 0;\r
1063                 blueflags = 0;\r
1064 \r
1065                 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)\r
1066                 {\r
1067                         if(flag.items & IT_KEY2) // blue\r
1068                                 ++redflags;\r
1069                         else if(flag.items & IT_KEY1) // red\r
1070                                 ++blueflags;\r
1071                 }\r
1072 \r
1073                 // blinking magic: if there is more than one flag, show one of these in a clever way\r
1074                 if(redflags)\r
1075                         redflags = mod(floor(time * redflags * 0.75), redflags);\r
1076                 if(blueflags)\r
1077                         blueflags = mod(floor(time * blueflags * 0.75), blueflags);\r
1078 \r
1079                 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)\r
1080                 {\r
1081                         if(flag.items & IT_KEY2) // blue\r
1082                         {\r
1083                                 if(--redflags == -1) // happens exactly once (redflags is in 0..count-1, and will --'ed count times)\r
1084                                         ctf_setstatus2(flag, IT_RED_FLAG_TAKEN);\r
1085                         }\r
1086                         else if(flag.items & IT_KEY1) // red\r
1087                         {\r
1088                                 if(--blueflags == -1) // happens exactly once\r
1089                                         ctf_setstatus2(flag, IT_BLUE_FLAG_TAKEN);\r
1090                         }\r
1091                 }\r
1092         }\r
1093 };\r
1094 /*\r
1095 entity(float cteam) ctf_team_has_commander =\r
1096 {\r
1097         entity pl;\r
1098         if(cteam != COLOR_TEAM1 || cteam != COLOR_TEAM2)\r
1099                 return world;\r
1100         \r
1101         FOR_EACH_REALPLAYER(pl) {\r
1102                 if(pl.team == cteam && pl.iscommander) {\r
1103                         return pl;\r
1104                 }\r
1105         }\r
1106         return world;\r
1107 };\r
1108 \r
1109 void(entity e, float st) ctf_setstate =\r
1110 {\r
1111         e.ctf_state = st;\r
1112         ++e.version;\r
1113 };\r
1114 \r
1115 void(float cteam) ctf_new_commander =\r
1116 {\r
1117         entity pl, plmax;\r
1118         \r
1119         plmax = world;\r
1120         FOR_EACH_REALPLAYER(pl) {\r
1121                 if(pl.team == cteam) {\r
1122                         if(pl.iscommander) { // don't reassign if alreay there\r
1123                                 return;\r
1124                         }\r
1125                         if(plmax == world || plmax.frags < pl.frags) <<<<<<<<<<<<<<<<< BROKEN in new scoring system\r
1126                                 plmax = pl;\r
1127                 }\r
1128         }\r
1129         if(plmax == world) {\r
1130                 bprint(strcat(ColoredTeamName(cteam), " Team has no Commander!\n"));\r
1131                 return;\r
1132         }\r
1133 \r
1134         plmax.iscommander = TRUE;\r
1135         ctf_setstate(plmax, 3);\r
1136         sprint(plmax, "^3You're the commander now!\n");\r
1137         centerprint(plmax, "^3You're the commander now!\n");\r
1138 };\r
1139 \r
1140 void() ctf_clientconnect =\r
1141 {\r
1142         self.iscommander = FALSE;\r
1143         \r
1144         if(!self.team || self.classname != "player") {\r
1145                 ctf_setstate(self, -1);\r
1146         } else\r
1147                 ctf_setstate(self, 0);\r
1148 \r
1149         self.team_saved = self.team;\r
1150         \r
1151         if(self.team != 0 && self.classname == "player" && !ctf_team_has_commander(self.team)) {\r
1152                 ctf_new_commander(self.team);\r
1153         }\r
1154 };\r
1155 \r
1156 void() ctf_playerchanged =\r
1157 {\r
1158         if(!self.team || self.classname != "player") {\r
1159                 ctf_setstate(self, -1);\r
1160         } else if(self.ctf_state < 0 && self.classname == "player") {\r
1161                 ctf_setstate(self, 0);\r
1162         }\r
1163 \r
1164         if(self.iscommander &&\r
1165            (self.classname != "player" || self.team != self.team_saved)\r
1166                 )\r
1167         {\r
1168                 self.iscommander = FALSE;\r
1169                 if(self.classname == "player")\r
1170                         ctf_setstate(self, 0);\r
1171                 else\r
1172                         ctf_setstate(self, -1);\r
1173                 ctf_new_commander(self.team_saved);\r
1174         }\r
1175         \r
1176         self.team_saved = self.team;\r
1177         \r
1178         ctf_new_commander(self.team);\r
1179 };\r
1180 \r
1181 void() ctf_clientdisconnect =\r
1182 {\r
1183         if(self.iscommander)\r
1184         {\r
1185                 ctf_new_commander(self.team);\r
1186         }\r
1187 };\r
1188 \r
1189 entity GetPlayer(string);\r
1190 float() ctf_clientcommand =\r
1191 {\r
1192         entity e;\r
1193         if(argv(0) == "order") {\r
1194                 if(!g_ctf) {\r
1195                         sprint(self, "This command is not supported in this gamemode.\n");\r
1196                         return TRUE;\r
1197                 }\r
1198                 if(!self.iscommander) {\r
1199                         sprint(self, "^1You are not the commander!\n");\r
1200                         return TRUE;\r
1201                 }\r
1202                 if(argv(2) == "") {\r
1203                         sprint(self, "Usage: order #player status   - (playernumber as in status)\n");\r
1204                         return TRUE;\r
1205                 }\r
1206                 e = GetPlayer(argv(1));\r
1207                 if(e == world) {\r
1208                         sprint(self, "Invalid player.\nUsage: order #player status   - (playernumber as in status)\n");\r
1209                         return TRUE;\r
1210                 }\r
1211                 if(e.team != self.team) {\r
1212                         sprint(self, "^3You can only give orders to your own team!\n");\r
1213                         return TRUE;\r
1214                 }\r
1215                 if(argv(2) == "attack") {\r
1216                         sprint(self, strcat("Ordering ", e.netname, " to attack!\n"));\r
1217                         sprint(e, "^1Attack!\n");\r
1218                         centerprint(e, "^7You've been ordered to^9\n^1Attack!\n");\r
1219                         ctf_setstate(e, 1);\r
1220                 } else if(argv(2) == "defend") {\r
1221                         sprint(self, strcat("Ordering ", e.netname, " to defend!\n"));\r
1222                         sprint(e, "^Defend!\n");\r
1223                         centerprint(e, "^7You've been ordered to^9\n^2Defend!\n");\r
1224                         ctf_setstate(e, 2);\r
1225                 } else {\r
1226                         sprint(self, "^7Invalid command, use ^3attack^7, or ^3defend^7.\n");\r
1227                 }\r
1228                 return TRUE;\r
1229         }\r
1230         return FALSE;\r
1231 };\r
1232 */\r