1 #define FLAG_MIN (PL_MIN + '0 0 -13')
\r
2 #define FLAG_MAX (PL_MAX + '0 0 -13')
\r
4 .entity basewaypoint;
\r
6 entity ctf_worldflaglist; // CTF flags in the map
\r
7 .entity ctf_worldflagnext;
\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
18 #define FLAG_CARRY_POS '-15 0 7'
\r
20 .float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
\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
26 float ctf_captureshield_shielded(entity p)
\r
30 float players_worseeq, players_total;
\r
32 if(captureshield_max_ratio <= 0)
\r
35 s = PlayerScore_Add(p, SP_SCORE, 0);
\r
36 if(s >= -captureshield_min_negscore)
\r
39 players_total = players_worseeq = 0;
\r
42 if(e.team != p.team)
\r
44 se = PlayerScore_Add(e, SP_SCORE, 0);
\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
53 if(players_worseeq >= players_total * captureshield_max_ratio)
\r
59 void ctf_captureshield_update(entity p, float dir)
\r
62 if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only
\r
64 should = ctf_captureshield_shielded(p);
\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
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
77 p.ctf_captureshielded = should;
\r
82 float ctf_captureshield_customize()
\r
84 if not(other.ctf_captureshielded)
\r
86 if(self.team == other.team)
\r
91 void ctf_captureshield_touch()
\r
93 if not(other.ctf_captureshielded)
\r
95 if(self.team == other.team)
\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
105 void ctf_flag_spawnstuff()
\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
121 setsize(e, e.scale * e.mins, e.scale * e.maxs);
\r
123 waypoint_spawnforitem_force(self, self.origin);
\r
124 self.nearestwaypointtimeout = 0; // activate waypointing again
\r
125 self.basewaypoint = self.nearestwaypoint;
\r
127 if(self.team == COLOR_TEAM1)
\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
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
139 float ctf_score_value(string parameter)
\r
141 if(g_ctf_win_mode != 2)
\r
142 return cvar(strcat("g_ctf_personal", parameter));
\r
144 return cvar(strcat("g_ctf_flag", parameter));
\r
147 void FakeTimeLimit(entity e, float t)
\r
150 WriteByte(MSG_ONE, 3); // svc_updatestat
\r
151 WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
\r
153 WriteCoord(MSG_ONE, cvar("timelimit"));
\r
155 WriteCoord(MSG_ONE, (t + 1) / 60);
\r
158 float flagcaptimerecord;
\r
159 .float flagpickuptime;
\r
160 //.float iscommander;
\r
161 //.float ctf_state;
\r
168 if(self.classname != "item_flag_team")
\r
170 backtrace("PlaceFlag a non-flag");
\r
175 self.t_width = 0.1; // frame animation rate
\r
177 self.t_length = 58; // maximum frame
\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
195 self.dropped_origin = self.origin;
\r
200 self.movetype = MOVETYPE_TOSS;
\r
203 InitializeEntity(self, ctf_flag_spawnstuff, INITPRIO_SETLOCATION);
\r
206 void LogCTF(string mode, float flagteam, entity actor)
\r
209 if(!cvar("sv_eventlog"))
\r
211 s = strcat(":ctf:", mode);
\r
212 s = strcat(s, ":", ftos(flagteam));
\r
214 s = strcat(s, ":", ftos(actor.playerid));
\r
218 void RegenFlag(entity e)
\r
220 if(e.classname != "item_flag_team")
\r
222 backtrace("RegenFlag a non-flag");
\r
226 if(e.waypointsprite_attachedforcarrier)
\r
227 WaypointSprite_DetachCarrier(e);
\r
229 setattachment(e, world, "");
\r
230 e.damageforcescale = 0;
\r
231 e.takedamage = DAMAGE_NO;
\r
232 e.movetype = MOVETYPE_NONE;
\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
242 e.flags = FL_ITEM; // clear FL_ONGROUND and any other junk
\r
245 void ReturnFlag(entity e)
\r
247 if(e.classname != "item_flag_team")
\r
249 backtrace("ReturnFlag a non-flag");
\r
254 if (e.owner.flagcarried == e)
\r
256 WaypointSprite_DetachCarrier(e.owner);
\r
257 e.owner.flagcarried = world;
\r
260 FakeTimeLimit(e.owner, -1);
\r
266 void DropFlag(entity e, entity penalty_receiver, entity attacker)
\r
270 if(e.classname != "item_flag_team")
\r
272 backtrace("DropFlag a non-flag");
\r
284 dprint("FLAG: drop - no owner?!?!\n");
\r
288 if (p.flagcarried != e)
\r
290 dprint("FLAG: drop - owner is not carrying this flag??\n");
\r
293 bprint(p.netname, "^7 lost the ", e.netname, "\n");
\r
295 if(penalty_receiver)
\r
296 UpdateFrags(penalty_receiver, -ctf_score_value("penalty_suicidedrop"));
\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
305 if(p.waypointsprite_attachedforcarrier)
\r
307 WaypointSprite_Ping(p.waypointsprite_attachedforcarrier);
\r
308 WaypointSprite_DetachCarrier(p);
\r
312 bprint("\{1}^1Flag carrier had no flag sprite?!?\n");
\r
313 backtrace("Flag carrier had no flag sprite?!?");
\r
315 LogCTF("dropped", p.team, p);
\r
316 sound (self, CHAN_TRIGGER, self.noise4, VOL_BASE, ATTN_NONE);
\r
318 setattachment(e, world, "");
\r
319 e.damageforcescale = cvar("g_balance_ctf_damageforcescale");
\r
320 e.takedamage = DAMAGE_YES;
\r
322 if (p.flagcarried == e)
\r
323 p.flagcarried = world;
\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
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
343 if(self.delay > time)
\r
345 self.delay = time + self.t_width;
\r
346 if(self.nextthink > self.delay)
\r
347 self.nextthink = self.delay;
\r
349 self.frame = self.frame + 1;
\r
350 if(self.frame > self.t_length)
\r
358 self.nextthink = time + 0.1;
\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
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
369 if(self == ctf_worldflaglist) // only for the first flag
\r
372 ctf_captureshield_update(e, 1); // release shield only
\r
377 if(self.speedrunning)
\r
378 if(self.cnt == FLAG_CARRY)
\r
381 if(flagcaptimerecord)
\r
382 if(time >= self.flagpickuptime + flagcaptimerecord)
\r
384 bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n");
\r
386 sound (self, CHAN_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
\r
387 self.owner.impulse = 141; // returning!
\r
398 if (self.cnt == FLAG_BASE)
\r
401 if (self.cnt == FLAG_DROPPED)
\r
403 // flag fallthrough? FIXME remove this if bug is really fixed now
\r
404 if(self.origin_z < -131072)
\r
406 dprint("FLAG FALLTHROUGH just happened\n");
\r
407 self.pain_finished = 0;
\r
409 setattachment(self, world, "");
\r
410 if (time > self.pain_finished)
\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
421 if (e.classname != "player" || (e.deadflag) || (e.flagcarried != self))
\r
423 dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n");
\r
424 DropFlag(self, world, world);
\r
428 if(cvar("g_ctf_allow_drop"))
\r
430 DropFlag(self, e, world);
\r
433 void flag_cap_ring_spawn(vector org)
\r
435 shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1);
\r
440 if(gameover) return;
\r
443 local entity player;
\r
444 local string s, s0, h0, h1;
\r
445 if (other.classname != "player")
\r
447 if (other.health < 1) // ignore dead players
\r
450 if (self.cnt == FLAG_CARRY)
\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
458 if (other.flagcarried == world)
\r
462 if(cvar("g_ctf_captimerecord_always") || player_count - currentbots <= 1) // at most one human
\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
472 h0 = strcat(h0, "^7's"); // h0: display text for previous netname
\r
473 if (flagcaptimerecord == 0)
\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
481 else if (t < flagcaptimerecord)
\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
491 bprint(other.netname, "^7 captured the ", other.flagcarried.netname, " in ", s, ", failing to break ", strcat(h0, " record of ", s0, " seconds\n"));
\r
495 bprint(other.netname, "^7 captured the ", other.flagcarried.netname, "\n");
\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
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
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
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
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
527 if (other.next_take_time > time)
\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
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
552 FOR_EACH_PLAYER(player)
\r
553 if(player.team == self.team)
\r
554 centerprint(player, "The enemy got your flag! Retrieve it!");
\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
566 if (self.cnt == FLAG_DROPPED)
\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
572 bprint(other.netname, "^7 returned the ", self.netname, "\n");
\r
574 // punish the player who last had it
\r
575 FOR_EACH_PLAYER(player)
\r
576 if(player.playerid == self.dropperid)
\r
578 PlayerScore_Add(player, SP_SCORE, -ctf_score_value("penalty_returned"));
\r
579 ctf_captureshield_update(player, 0); // shield only
\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
586 TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, -ctf_score_value("penalty_returned"));
\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
591 if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
\r
592 UpdateFrags(other, ctf_score_value("score_return_by_killer"));
\r
594 UpdateFrags(other, ctf_score_value("score_return_rogue_by_killer"));
\r
598 if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
\r
599 UpdateFrags(other, ctf_score_value("score_return"));
\r
601 UpdateFrags(other, ctf_score_value("score_return_rogue"));
\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
608 else if (!other.flagcarried && (other.playerid != self.dropperid || time > self.ctf_droptime + cvar("g_balance_ctf_delay_collect")))
\r
610 if(self.waypointsprite_attachedforcarrier)
\r
611 WaypointSprite_DetachCarrier(self);
\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
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
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
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
638 FOR_EACH_PLAYER(player)
\r
639 if(player.team == self.team)
\r
640 centerprint(player, "The enemy got your flag! Retrieve it!");
\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
653 /*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
\r
654 CTF Starting point for a player
\r
659 viewing angle when spawning
\r
661 void spawnfunc_info_player_team1()
\r
668 self.team = COLOR_TEAM1; // red
\r
669 spawnfunc_info_player_deathmatch();
\r
671 //self.team = 4;self.classname = "info_player_start";spawnfunc_info_player_start();};
\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
679 viewing angle when spawning
\r
681 void spawnfunc_info_player_team2()
\r
688 self.team = COLOR_TEAM2; // blue
\r
689 spawnfunc_info_player_deathmatch();
\r
691 //self.team = 13;self.classname = "info_player_start";spawnfunc_info_player_start();};
\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
699 viewing angle when spawning
\r
701 void spawnfunc_info_player_team3()
\r
708 self.team = COLOR_TEAM3; // yellow
\r
709 spawnfunc_info_player_deathmatch();
\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
719 viewing angle when spawning
\r
721 void spawnfunc_info_player_team4()
\r
728 self.team = COLOR_TEAM4; // purple
\r
729 spawnfunc_info_player_deathmatch();
\r
732 void item_flag_reset()
\r
734 DropFlag(self, world, world);
\r
735 if(self.waypointsprite_attachedforcarrier)
\r
736 WaypointSprite_DetachCarrier(self);
\r
740 void item_flag_postspawn()
\r
741 { // Check CTF Item Flag Post Spawn
\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
748 self.glow_color = 251;
\r
750 if(self.team == COLOR_TEAM2)
\r
752 self.glow_color = 210;
\r
754 self.glow_size = 25;
\r
755 self.glow_trail = 1;
\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
765 Angle the flag will point
\r
768 model to use, note this needs red and blue as skins 0 and 1
\r
769 (default models/ctf/flag.md3)
\r
771 sound played when flag is picked up
\r
772 (default ctf/take.wav)
\r
774 sound played when flag is returned by a teammate
\r
775 (default ctf/return.wav)
\r
777 sound played when flag is captured
\r
778 (default ctf/redcapture.wav)
\r
780 sound played when flag is lost in the field and respawns itself
\r
781 (default ctf/respawn.wav)
\r
784 void spawnfunc_item_flag_team1()
\r
792 //if(!cvar("teamplay"))
\r
793 // cvar_set("teamplay", "3");
\r
795 // link flag into ctf_worldflaglist
\r
796 self.ctf_worldflagnext = ctf_worldflaglist;
\r
797 ctf_worldflaglist = self;
\r
799 self.classname = "item_flag_team";
\r
802 self.team = COLOR_TEAM2; // color 13 team (blue)
\r
803 self.items = IT_KEY1; // silver key (bluish enough)
\r
807 self.team = COLOR_TEAM1; // color 4 team (red)
\r
808 self.items = IT_KEY2; // gold key (redish enough)
\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
816 self.model = cvar_string("g_ctf_flag_red_model");
\r
818 self.noise = "ctf/red_taken.wav";
\r
820 self.noise1 = "ctf/red_returned.wav";
\r
822 self.noise2 = "ctf/red_capture.wav"; // blue team scores by capturing the red flag
\r
824 self.noise3 = "ctf/flag_respawn.wav";
\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
842 //if(!self.glow_size)
\r
843 // self.glow_size = 50;
\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
852 item_flag_postspawn();
\r
854 precache_model("models/ctf/shield.md3");
\r
855 precache_model("models/ctf/shockwavetransring.md3");
\r
857 self.reset = item_flag_reset;
\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
866 Angle the flag will point
\r
869 model to use, note this needs red and blue as skins 0 and 1
\r
870 (default models/ctf/flag.md3)
\r
872 sound played when flag is picked up
\r
873 (default ctf/take.wav)
\r
875 sound played when flag is returned by a teammate
\r
876 (default ctf/return.wav)
\r
878 sound played when flag is captured
\r
879 (default ctf/bluecapture.wav)
\r
881 sound played when flag is lost in the field and respawns itself
\r
882 (default ctf/respawn.wav)
\r
885 void spawnfunc_item_flag_team2()
\r
892 //if(!cvar("teamplay"))
\r
893 // cvar_set("teamplay", "3");
\r
895 // link flag into ctf_worldflaglist
\r
896 self.ctf_worldflagnext = ctf_worldflaglist;
\r
897 ctf_worldflaglist = self;
\r
899 self.classname = "item_flag_team";
\r
902 self.team = COLOR_TEAM1; // color 4 team (red)
\r
903 self.items = IT_KEY2; // gold key (redish enough)
\r
907 self.team = COLOR_TEAM2; // color 13 team (blue)
\r
908 self.items = IT_KEY1; // silver key (bluish enough)
\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
916 self.model = cvar_string("g_ctf_flag_blue_model");
\r
918 self.noise = "ctf/blue_taken.wav";
\r
920 self.noise1 = "ctf/blue_returned.wav";
\r
922 self.noise2 = "ctf/blue_capture.wav"; // blue team scores by capturing the red flag
\r
924 self.noise3 = "ctf/flag_respawn.wav";
\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
942 //if(!self.glow_size)
\r
943 // self.glow_size = 50;
\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
952 item_flag_postspawn();
\r
954 precache_model("models/ctf/shield.md3");
\r
955 precache_model("models/ctf/shockwavetransring.md3");
\r
957 self.reset = item_flag_reset;
\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
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
970 Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)
\r
972 Scoreboard color of the team (for example 4 is red and 13 is blue)
\r
976 void spawnfunc_ctf_team()
\r
983 self.classname = "ctf_team";
\r
984 self.team = self.cnt + 1;
\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
990 local entity oldself;
\r
993 self.classname = "ctf_team";
\r
994 self.netname = teamname;
\r
995 self.cnt = teamcolor;
\r
997 spawnfunc_ctf_team();
\r
1002 // spawn some default teams if the map is not set up for ctf
\r
1003 void ctf_spawnteams()
\r
1007 numteams = 2;//cvar("g_ctf_default_teams");
\r
1009 ctf_spawnteam("Red", COLOR_TEAM1 - 1);
\r
1010 ctf_spawnteam("Blue", COLOR_TEAM2 - 1);
\r
1013 void ctf_delayedinit()
\r
1015 // if no teams are found, spawn defaults
\r
1016 if (find(world, classname, "ctf_team") == world)
\r
1024 InitializeEntity(world, ctf_delayedinit, INITPRIO_GAMETYPE);
\r
1025 flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
\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
1032 void ctf_setstatus2(entity flag, float shift)
\r
1034 if (flag.cnt == FLAG_CARRY)
\r
1035 if (flag.owner == self)
\r
1036 self.items |= shift * 3;
\r
1038 self.items |= shift * 1;
\r
1039 else if (flag.cnt == FLAG_DROPPED)
\r
1040 self.items |= shift * 2;
\r
1047 void ctf_setstatus()
\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
1056 local entity flag;
\r
1057 float redflags, blueflags;
\r
1059 if(self.ctf_captureshielded)
\r
1060 self.items |= IT_CTF_SHIELDED;
\r
1065 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
\r
1067 if(flag.items & IT_KEY2) // blue
\r
1069 else if(flag.items & IT_KEY1) // red
\r
1073 // blinking magic: if there is more than one flag, show one of these in a clever way
\r
1075 redflags = mod(floor(time * redflags * 0.75), redflags);
\r
1077 blueflags = mod(floor(time * blueflags * 0.75), blueflags);
\r
1079 for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
\r
1081 if(flag.items & IT_KEY2) // blue
\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
1086 else if(flag.items & IT_KEY1) // red
\r
1088 if(--blueflags == -1) // happens exactly once
\r
1089 ctf_setstatus2(flag, IT_BLUE_FLAG_TAKEN);
\r
1095 entity(float cteam) ctf_team_has_commander =
\r
1098 if(cteam != COLOR_TEAM1 || cteam != COLOR_TEAM2)
\r
1101 FOR_EACH_REALPLAYER(pl) {
\r
1102 if(pl.team == cteam && pl.iscommander) {
\r
1109 void(entity e, float st) ctf_setstate =
\r
1115 void(float cteam) ctf_new_commander =
\r
1120 FOR_EACH_REALPLAYER(pl) {
\r
1121 if(pl.team == cteam) {
\r
1122 if(pl.iscommander) { // don't reassign if alreay there
\r
1125 if(plmax == world || plmax.frags < pl.frags) <<<<<<<<<<<<<<<<< BROKEN in new scoring system
\r
1129 if(plmax == world) {
\r
1130 bprint(strcat(ColoredTeamName(cteam), " Team has no Commander!\n"));
\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
1140 void() ctf_clientconnect =
\r
1142 self.iscommander = FALSE;
\r
1144 if(!self.team || self.classname != "player") {
\r
1145 ctf_setstate(self, -1);
\r
1147 ctf_setstate(self, 0);
\r
1149 self.team_saved = self.team;
\r
1151 if(self.team != 0 && self.classname == "player" && !ctf_team_has_commander(self.team)) {
\r
1152 ctf_new_commander(self.team);
\r
1156 void() ctf_playerchanged =
\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
1164 if(self.iscommander &&
\r
1165 (self.classname != "player" || self.team != self.team_saved)
\r
1168 self.iscommander = FALSE;
\r
1169 if(self.classname == "player")
\r
1170 ctf_setstate(self, 0);
\r
1172 ctf_setstate(self, -1);
\r
1173 ctf_new_commander(self.team_saved);
\r
1176 self.team_saved = self.team;
\r
1178 ctf_new_commander(self.team);
\r
1181 void() ctf_clientdisconnect =
\r
1183 if(self.iscommander)
\r
1185 ctf_new_commander(self.team);
\r
1189 entity GetPlayer(string);
\r
1190 float() ctf_clientcommand =
\r
1193 if(argv(0) == "order") {
\r
1195 sprint(self, "This command is not supported in this gamemode.\n");
\r
1198 if(!self.iscommander) {
\r
1199 sprint(self, "^1You are not the commander!\n");
\r
1202 if(argv(2) == "") {
\r
1203 sprint(self, "Usage: order #player status - (playernumber as in status)\n");
\r
1206 e = GetPlayer(argv(1));
\r
1208 sprint(self, "Invalid player.\nUsage: order #player status - (playernumber as in status)\n");
\r
1211 if(e.team != self.team) {
\r
1212 sprint(self, "^3You can only give orders to your own team!\n");
\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
1226 sprint(self, "^7Invalid command, use ^3attack^7, or ^3defend^7.\n");
\r