X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fmutators%2Fmutator%2Fgamemode_ctf.qc;h=a6d34c0d4af675506a764d79d2284486e88bdacf;hb=9f70bdba9a6fb2c06324be13504341da967f7028;hp=e2eb898effb6d5fa6bd80504fd71ac343545350b;hpb=a0b626ee78db909c214cc0fc7e829f8a8379704f;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/mutators/mutator/gamemode_ctf.qc b/qcsrc/server/mutators/mutator/gamemode_ctf.qc index e2eb898ef..a6d34c0d4 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ctf.qc +++ b/qcsrc/server/mutators/mutator/gamemode_ctf.qc @@ -1,6 +1,39 @@ #ifndef GAMEMODE_CTF_H #define GAMEMODE_CTF_H +#ifndef CSQC +void ctf_Initialize(); + +REGISTER_MUTATOR(ctf, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + ctf_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_capturelimit_override, autocvar_captureleadlimit_override, -1, -1); + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back ctf_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} +#endif + #ifdef SVQC // used in cheats.qc void ctf_RespawnFlag(entity flag); @@ -14,9 +47,14 @@ const int SP_CTF_DROPS = 7; const int SP_CTF_FCKILLS = 8; const int SP_CTF_RETURNS = 9; +CLASS(Flag, Pickup) + ATTRIB(Flag, m_mins, vector, PL_MIN_CONST + '0 0 -13') + ATTRIB(Flag, m_maxs, vector, PL_MAX_CONST + '0 0 -13') +ENDCLASS(Flag) +Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); } +void ctf_FlagTouch() { SELFPARAM(); ITEM_HANDLE(Pickup, CTF_FLAG, this, other); } + // flag constants // for most of these, there is just one question to be asked: WHYYYYY? -#define FLAG_MIN (PL_MIN_CONST + '0 0 -13') -#define FLAG_MAX (PL_MAX_CONST + '0 0 -13') const float FLAG_SCALE = 0.6; @@ -92,6 +130,8 @@ const int RETURN_DAMAGE = 3; const int RETURN_SPEEDRUN = 4; const int RETURN_NEEDKILL = 5; +void ctf_Handle_Throw(entity player, entity receiver, float droptype); + // flag properties #define ctf_spawnorigin dropped_origin bool ctf_stalemate; // indicates that a stalemate is active @@ -143,7 +183,7 @@ void havocbot_role_ctf_setrole(entity bot, int role); #define CTF_DIFFTEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? SAME_TEAM(a,b) : DIFF_TEAM(a,b)) // networked flag statuses -.int ctf_flagstatus; +.int ctf_flagstatus = _STAT(CTF_FLAGSTATUS); #endif const int CTF_RED_FLAG_TAKEN = 1; @@ -168,11 +208,11 @@ const int CTF_SHIELDED = 4096; #ifdef IMPLEMENTATION #ifdef SVQC -#include "../../../common/vehicles/all.qh" -#include "../../teamplay.qh" +#include +#include #endif -#include "../../../lib/warpzone/common.qh" +#include bool autocvar_g_ctf_allow_vehicle_carry; bool autocvar_g_ctf_allow_vehicle_touch; @@ -209,6 +249,7 @@ bool autocvar_g_ctf_flag_dropped_floatinwater; bool autocvar_g_ctf_flag_glowtrails; int autocvar_g_ctf_flag_health; bool autocvar_g_ctf_flag_return; +bool autocvar_g_ctf_flag_return_carrying; float autocvar_g_ctf_flag_return_carried_radius; float autocvar_g_ctf_flag_return_time; bool autocvar_g_ctf_flag_return_when_unreachable; @@ -269,9 +310,9 @@ void ctf_CaptureRecord(entity flag, entity player) // notify about shit if(ctf_oneflag) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname); } - else if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_TIME_), player.netname, (cap_time * 100)); } - else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_BROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); } - else { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_CAPTURE_UNBROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); } + else if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_TIME), player.netname, (cap_time * 100)); } + else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); } + else { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); } // write that shit in the database if(!ctf_oneflag) // but not in 1-flag mode @@ -360,7 +401,6 @@ bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector pas bool ctf_CaptureShield_CheckStatus(entity p) { int s, s2, s3, s4, se, se2, se3, se4, sr, ser; - entity e; int players_worseeq, players_total; if(ctf_captureshield_max_ratio <= 0) @@ -377,21 +417,20 @@ bool ctf_CaptureShield_CheckStatus(entity p) return false; players_total = players_worseeq = 0; - FOR_EACH_PLAYER(e) - { - if(DIFF_TEAM(e, p)) + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + if(DIFF_TEAM(it, p)) continue; - se = PlayerScore_Add(e, SP_CTF_CAPS, 0); - se2 = PlayerScore_Add(e, SP_CTF_PICKUPS, 0); - se3 = PlayerScore_Add(e, SP_CTF_RETURNS, 0); - se4 = PlayerScore_Add(e, SP_CTF_FCKILLS, 0); + se = PlayerScore_Add(it, SP_CTF_CAPS, 0); + se2 = PlayerScore_Add(it, SP_CTF_PICKUPS, 0); + se3 = PlayerScore_Add(it, SP_CTF_RETURNS, 0); + se4 = PlayerScore_Add(it, SP_CTF_FCKILLS, 0); ser = ((se - se2) + (se3 + se4)); if(ser <= sr) ++players_worseeq; ++players_total; - } + )); // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse // use this rule here @@ -434,13 +473,12 @@ void ctf_CaptureShield_Touch() void ctf_CaptureShield_Spawn(entity flag) {SELFPARAM(); - entity shield = spawn(); + entity shield = new(ctf_captureshield); shield.enemy = self; shield.team = self.team; shield.touch = ctf_CaptureShield_Touch; shield.customizeentityforclient = ctf_CaptureShield_Customize; - shield.classname = "ctf_captureshield"; shield.effects = EF_ADDITIVE; shield.movetype = MOVETYPE_NOCLIP; shield.solid = SOLID_TRIGGER; @@ -472,7 +510,7 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype) flag.ctf_status = FLAG_DROPPED; // messages and sounds - Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_LOST_) : INFO_CTF_LOST_NEUTRAL), player.netname); + Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_LOST) : INFO_CTF_LOST_NEUTRAL), player.netname); _sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE); ctf_EventLog("dropped", player.team, player); @@ -504,7 +542,6 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype) void ctf_Handle_Retrieve(entity flag, entity player) { - entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players entity sender = flag.pass_sender; // transfer flag to player @@ -533,15 +570,14 @@ void ctf_Handle_Retrieve(entity flag, entity player) _sound(player, CH_TRIGGER, flag.snd_flag_pass, VOL_BASE, ATTEN_NORM); ctf_EventLog("receive", flag.team, player); - FOR_EACH_REALPLAYER(tmp_player) - { - if(tmp_player == sender) - Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_SENT_) : CENTER_CTF_PASS_SENT_NEUTRAL), player.netname); - else if(tmp_player == player) - Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_RECEIVED_) : CENTER_CTF_PASS_RECEIVED_NEUTRAL), sender.netname); - else if(SAME_TEAM(tmp_player, sender)) - Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT_4(flag, CENTER_CTF_PASS_OTHER_) : CENTER_CTF_PASS_OTHER_NEUTRAL), sender.netname, player.netname); - } + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA( + if(it == sender) + Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_SENT) : CENTER_CTF_PASS_SENT_NEUTRAL), player.netname); + else if(it == player) + Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_RECEIVED) : CENTER_CTF_PASS_RECEIVED_NEUTRAL), sender.netname); + else if(SAME_TEAM(it, sender)) + Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_OTHER) : CENTER_CTF_PASS_OTHER_NEUTRAL), sender.netname, player.netname); + )); // create new waypoint ctf_FlagcarrierWaypoints(player); @@ -639,6 +675,10 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype) ctf_CaptureShield_Update(player, 0); // shield player from picking up flag } +void shockwave_spawn(string m, vector org, float sz, float t1, float t2) +{ + return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2); +} // ============== // Event Handlers @@ -668,7 +708,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype) player.throw_count = 0; // messages and sounds - Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT_4(enemy_flag, CENTER_CTF_CAPTURE_) : CENTER_CTF_CAPTURE_NEUTRAL)); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT(enemy_flag, CENTER_CTF_CAPTURE) : CENTER_CTF_CAPTURE_NEUTRAL)); ctf_CaptureRecord(enemy_flag, player); _sound(player, CH_TRIGGER, ((ctf_oneflag) ? player_team_flag.snd_flag_capture : ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture)), VOL_BASE, ATTEN_NONE); @@ -712,12 +752,12 @@ void ctf_Handle_Return(entity flag, entity player) // messages and sounds if(IS_MONSTER(player)) { - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN_MONSTER), player.monster_name); } else if(flag.team) { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(flag, CENTER_CTF_RETURN_)); - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_), player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT(flag, CENTER_CTF_RETURN)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN), player.netname); } _sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE); ctf_EventLog("return", flag.team, player); @@ -752,7 +792,6 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) { // declarations float pickup_dropped_score; // used to calculate dropped pickup score - entity tmp_entity; // temporary entity // attach the flag to the player flag.owner = player; @@ -784,28 +823,25 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) } // messages and sounds - Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_PICKUP_) : INFO_CTF_PICKUP_NEUTRAL), player.netname); + Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_PICKUP) : INFO_CTF_PICKUP_NEUTRAL), player.netname); if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); } if(!flag.team) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL); } - else if(CTF_DIFFTEAM(player, flag)) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_4(flag, CENTER_CTF_PICKUP_)); } + else if(CTF_DIFFTEAM(player, flag)) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT(flag, CENTER_CTF_PICKUP)); } else { Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team)); } - Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, ((flag.team) ? APP_TEAM_ENT_4(flag, CHOICE_CTF_PICKUP_TEAM_) : CHOICE_CTF_PICKUP_TEAM_NEUTRAL), Team_ColorCode(player.team), player.netname); + Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, ((flag.team) ? APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM) : CHOICE_CTF_PICKUP_TEAM_NEUTRAL), Team_ColorCode(player.team), player.netname); if(!flag.team) - FOR_EACH_PLAYER(tmp_entity) - if(tmp_entity != player) - if(DIFF_TEAM(player, tmp_entity)) - Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname); + FOREACH_CLIENT(IS_PLAYER(it) && it != player && DIFF_TEAM(it, player), LAMBDA(Send_Notification(NOTIF_ONE, it, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname))); if(flag.team) - FOR_EACH_PLAYER(tmp_entity) - if(tmp_entity != player) - if(CTF_SAMETEAM(flag, tmp_entity)) - if(SAME_TEAM(player, tmp_entity)) - Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, APP_TEAM_ENT_4(flag, CHOICE_CTF_PICKUP_TEAM_), Team_ColorCode(player.team), player.netname); - else - Send_Notification(NOTIF_ONE, tmp_entity, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname); + FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA( + if(CTF_SAMETEAM(flag, it)) + if(SAME_TEAM(player, it)) + Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname); + else + Send_Notification(NOTIF_ONE, it, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname); + )); _sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE); @@ -866,14 +902,14 @@ void ctf_CheckFlagReturn(entity flag, int returntype) { switch(returntype) { - case RETURN_DROPPED: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DROPPED_) : INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL)); break; - case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_DAMAGED_) : INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL)); break; - case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_) : INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL), ctf_captimerecord); break; - case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_NEEDKILL_) : INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL)); break; + case RETURN_DROPPED: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DROPPED) : INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL)); break; + case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DAMAGED) : INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL)); break; + case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_SPEEDRUN) : INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL), ctf_captimerecord); break; + case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_NEEDKILL) : INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL)); break; default: case RETURN_TIMEOUT: - { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_FLAGRETURN_TIMEOUT_) : INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL)); break; } + { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_TIMEOUT) : INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL)); break; } } _sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE); ctf_EventLog("returned", flag.team, world); @@ -897,7 +933,7 @@ bool ctf_Stalemate_Customize() return true; } -void ctf_CheckStalemate(void) +void ctf_CheckStalemate() { // declarations int stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0; @@ -955,34 +991,33 @@ void ctf_CheckStalemate(void) if (!wpforenemy_announced) { - FOR_EACH_REALPLAYER(tmp_entity) - Send_Notification(NOTIF_ONE, tmp_entity, MSG_CENTER, ((tmp_entity.flagcarried) ? CENTER_CTF_STALEMATE_CARRIER : CENTER_CTF_STALEMATE_OTHER)); + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA(Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((it.flagcarried) ? CENTER_CTF_STALEMATE_CARRIER : CENTER_CTF_STALEMATE_OTHER)))); wpforenemy_announced = true; } } } -void ctf_FlagDamage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); +void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ if(ITEM_DAMAGE_NEEDKILL(deathtype)) { if(autocvar_g_ctf_flag_return_damage_delay) { - self.ctf_flagdamaged = true; + this.ctf_flagdamaged = true; } else { - self.health = 0; - ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + this.health = 0; + ctf_CheckFlagReturn(this, RETURN_NEEDKILL); } return; } if(autocvar_g_ctf_flag_return_damage) { // reduce health and check if it should be returned - self.health = self.health - damage; - ctf_CheckFlagReturn(self, RETURN_DAMAGE); + this.health = this.health - damage; + ctf_CheckFlagReturn(this, RETURN_DAMAGE); return; } } @@ -996,15 +1031,14 @@ void ctf_FlagThink() // captureshield if(self == ctf_worldflaglist) // only for the first flag - FOR_EACH_CLIENT(tmp_entity) - ctf_CaptureShield_Update(tmp_entity, 1); // release shield only + FOREACH_CLIENT(true, LAMBDA(ctf_CaptureShield_Update(it, 1))); // release shield only // sanity checks - if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) { // reset the flag boundaries in case it got squished + if(self.mins != CTF_FLAG.m_mins || self.maxs != CTF_FLAG.m_maxs) { // reset the flag boundaries in case it got squished LOG_TRACE("wtf the flag got squashed?\n"); - tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self); + tracebox(self.origin, CTF_FLAG.m_mins, CTF_FLAG.m_maxs, self.origin, MOVE_NOMONSTERS, self); if(!trace_startsolid || self.noalign) // can we resize it without getting stuck? - setsize(self, FLAG_MIN, FLAG_MAX); } + setsize(self, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); } switch(self.ctf_status) // reset flag angles in case warpzones adjust it { @@ -1081,8 +1115,8 @@ void ctf_FlagThink() ctf_CheckFlagReturn(self, RETURN_SPEEDRUN); setself(self.owner); - self.impulse = CHIMPULSE_SPEEDRUN; // move the player back to the waypoint they set - ImpulseCommands(); + self.impulse = CHIMPULSE_SPEEDRUN.impulse; // move the player back to the waypoint they set + ImpulseCommands(self); setself(this); } if(autocvar_g_ctf_stalemate) @@ -1110,7 +1144,7 @@ void ctf_FlagThink() WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self); if((self.pass_target == world) - || (self.pass_target.deadflag != DEAD_NO) + || (IS_DEAD(self.pass_target)) || (self.pass_target.flagcarried) || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius) || ((trace_fraction < 1) && (trace_ent != self.pass_target)) @@ -1135,29 +1169,30 @@ void ctf_FlagThink() } } -void ctf_FlagTouch() -{SELFPARAM(); +METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) +{ + return = false; if(gameover) { return; } if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; } - entity toucher = other, tmp_entity; - bool is_not_monster = (!IS_MONSTER(toucher)), num_perteam = 0; + bool is_not_monster = (!IS_MONSTER(toucher)); // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces if(ITEM_TOUCH_NEEDKILL()) { if(!autocvar_g_ctf_flag_return_damage_delay) { - self.health = 0; - ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + flag.health = 0; + ctf_CheckFlagReturn(flag, RETURN_NEEDKILL); } - if(!self.ctf_flagdamaged) { return; } + if(!flag.ctf_flagdamaged) { return; } } - FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; } + int num_perteam = 0; + FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(toucher, it), LAMBDA(++num_perteam)); // special touch behaviors - if(toucher.frozen) { return; } + if(STAT(FROZEN, toucher)) { return; } else if(IS_VEHICLE(toucher)) { if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner) @@ -1172,40 +1207,45 @@ void ctf_FlagTouch() } else if (!IS_PLAYER(toucher)) // The flag just touched an object, most likely the world { - if(time > self.wait) // if we haven't in a while, play a sound/effect + if(time > flag.wait) // if we haven't in a while, play a sound/effect { - Send_Effect_(self.toucheffect, self.origin, '0 0 0', 1); - _sound(self, CH_TRIGGER, self.snd_flag_touch, VOL_BASE, ATTEN_NORM); - self.wait = time + FLAG_TOUCHRATE; + Send_Effect_(flag.toucheffect, flag.origin, '0 0 0', 1); + _sound(flag, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM); + flag.wait = time + FLAG_TOUCHRATE; } return; } - else if(toucher.deadflag != DEAD_NO) { return; } + else if(IS_DEAD(toucher)) { return; } - switch(self.ctf_status) + switch(flag.ctf_status) { case FLAG_BASE: { if(ctf_oneflag) { - if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster) - ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base - else if(!self.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) - ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the neutral flag + if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster) + ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base + else if(!flag.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the neutral flag } - else if(CTF_SAMETEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster) - ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base - else if(CTF_DIFFTEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) - ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag + else if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, flag) && is_not_monster) + ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base + else if(CTF_DIFFTEAM(toucher, flag) && (toucher.flagcarried) && CTF_SAMETEAM(toucher.flagcarried, toucher) && (!toucher.ctf_captureshielded) && autocvar_g_ctf_flag_return_carrying && (time > toucher.next_take_time) && is_not_monster) + { + ctf_Handle_Return(toucher.flagcarried, toucher); // return their current flag + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // now pickup the flag + } + else if(CTF_DIFFTEAM(toucher, flag) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the enemies flag break; } case FLAG_DROPPED: { - if(CTF_SAMETEAM(toucher, self) && (autocvar_g_ctf_flag_return || num_perteam <= 1) && self.team) // automatically return if there's only 1 player on the team - ctf_Handle_Return(self, toucher); // toucher just returned his own flag - else if(is_not_monster && (!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) - ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag + if(CTF_SAMETEAM(toucher, flag) && (autocvar_g_ctf_flag_return || num_perteam <= 1 || (autocvar_g_ctf_flag_return_carrying && toucher.flagcarried)) && flag.team) // automatically return if there's only 1 player on the team + ctf_Handle_Return(flag, toucher); // toucher just returned his own flag + else if(is_not_monster && (!toucher.flagcarried) && ((toucher != flag.ctf_dropper) || (time > flag.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) + ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag break; } @@ -1217,12 +1257,12 @@ void ctf_FlagTouch() case FLAG_PASSING: { - if((IS_PLAYER(toucher)) && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender)) + if((IS_PLAYER(toucher)) && !IS_DEAD(toucher) && (toucher != flag.pass_sender)) { - if(DIFF_TEAM(toucher, self.pass_sender)) - ctf_Handle_Return(self, toucher); + if(DIFF_TEAM(toucher, flag.pass_sender)) + ctf_Handle_Return(flag, toucher); else - ctf_Handle_Retrieve(self, toucher); + ctf_Handle_Retrieve(flag, toucher); } break; } @@ -1281,16 +1321,15 @@ void ctf_RespawnFlag(entity flag) ctf_CheckStalemate(); } -void ctf_Reset() -{SELFPARAM(); - if(self.owner) - if(IS_PLAYER(self.owner)) - ctf_Handle_Throw(self.owner, world, DROP_RESET); +void ctf_Reset(entity this) +{ + if(this.owner && IS_PLAYER(this.owner)) + ctf_Handle_Throw(this.owner, world, DROP_RESET); - ctf_RespawnFlag(self); + ctf_RespawnFlag(this); } -void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup() +void ctf_DelayedFlagSetup() // called after a flag is placed on a map by ctf_FlagSetup() {SELFPARAM(); // bot waypoints waypoint_spawnforitem_force(self, self.origin); @@ -1316,12 +1355,6 @@ void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf ctf_CaptureShield_Spawn(self); } -void set_flag_string(entity flag, .string field, string value, string teamname) -{ - if(flag.(field) == "") - flag.(field) = strzone(sprintf(value,teamname)); -} - void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc {SELFPARAM(); // declarations @@ -1346,7 +1379,7 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e flag.event_damage = ctf_FlagDamage; flag.pushable = true; flag.teleportable = TELEPORT_NORMAL; - flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP; flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable; flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable; flag.velocity = '0 0 0'; @@ -1362,20 +1395,20 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e if(!flag.scale) { flag.scale = FLAG_SCALE; } if(flag.skin == 0) { flag.skin = cvar(sprintf("g_ctf_flag_%s_skin", teamname)); } if(flag.model == "") { flag.model = cvar_string(sprintf("g_ctf_flag_%s_model", teamname)); } - set_flag_string(flag, toucheffect, "%sflag_touch", teamname); - set_flag_string(flag, passeffect, "%s_pass", teamname); - set_flag_string(flag, capeffect, "%s_cap", teamname); + if (flag.toucheffect == "") { flag.toucheffect = EFFECT_FLAG_TOUCH(teamnumber).eent_eff_name; } + if (flag.passeffect == "") { flag.passeffect = EFFECT_PASS(teamnumber).eent_eff_name; } + if (flag.capeffect == "") { flag.capeffect = EFFECT_CAP(teamnumber).eent_eff_name; } // sounds - flag.snd_flag_taken = SND(CTF_TAKEN(teamnumber)); - flag.snd_flag_returned = SND(CTF_RETURNED(teamnumber)); - flag.snd_flag_capture = SND(CTF_CAPTURE(teamnumber)); - flag.snd_flag_dropped = SND(CTF_DROPPED(teamnumber)); - if (flag.snd_flag_respawn == "") flag.snd_flag_respawn = SND(CTF_RESPAWN); // if there is ever a team-based sound for this, update the code to match. + flag.snd_flag_taken = strzone(SND(CTF_TAKEN(teamnumber))); + flag.snd_flag_returned = strzone(SND(CTF_RETURNED(teamnumber))); + flag.snd_flag_capture = strzone(SND(CTF_CAPTURE(teamnumber))); + flag.snd_flag_dropped = strzone(SND(CTF_DROPPED(teamnumber))); + if (flag.snd_flag_respawn == "") flag.snd_flag_respawn = strzone(SND(CTF_RESPAWN)); // if there is ever a team-based sound for this, update the code to match. precache_sound(flag.snd_flag_respawn); - if (flag.snd_flag_touch == "") flag.snd_flag_touch = SND(CTF_TOUCH); // again has no team-based sound + if (flag.snd_flag_touch == "") flag.snd_flag_touch = strzone(SND(CTF_TOUCH)); // again has no team-based sound precache_sound(flag.snd_flag_touch); - if (flag.snd_flag_pass == "") flag.snd_flag_pass = SND(CTF_PASS); // same story here + if (flag.snd_flag_pass == "") flag.snd_flag_pass = strzone(SND(CTF_PASS)); // same story here precache_sound(flag.snd_flag_pass); // precache @@ -1383,7 +1416,7 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e // appearence _setmodel(flag, flag.model); // precision set below - setsize(flag, FLAG_MIN, FLAG_MAX); + setsize(flag, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET)); if(autocvar_g_ctf_flag_glowtrails) @@ -1505,16 +1538,14 @@ int havocbot_ctf_teamcount(entity bot, vector org, float tc_radius) return 0; int c = 0; - entity head; - FOR_EACH_PLAYER(head) - { - if(DIFF_TEAM(head, bot) || head.deadflag != DEAD_NO || head == bot) + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + if(DIFF_TEAM(it, bot) || IS_DEAD(it) || it == bot) continue; - if(vlen(head.origin - org) < tc_radius) + if(vlen(it.origin - org) < tc_radius) ++c; - } + )); return c; } @@ -1654,10 +1685,10 @@ void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float s void havocbot_ctf_reset_role(entity bot) { float cdefense, cmiddle, coffense; - entity mf, ef, head; + entity mf, ef; float c; - if(bot.deadflag != DEAD_NO) + if(IS_DEAD(bot)) return; if(vlen(havocbot_ctf_middlepoint)==0) @@ -1689,9 +1720,7 @@ void havocbot_ctf_reset_role(entity bot) // if there is only me on the team switch to offense c = 0; - FOR_EACH_PLAYER(head) - if(SAME_TEAM(head, bot)) - ++c; + FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(it, bot), LAMBDA(++c)); if(c==1) { @@ -1719,7 +1748,7 @@ void havocbot_ctf_reset_role(entity bot) void havocbot_role_ctf_carrier() {SELFPARAM(); - if(self.deadflag != DEAD_NO) + if(IS_DEAD(self)) { havocbot_ctf_reset_role(self); return; @@ -1762,7 +1791,7 @@ void havocbot_role_ctf_escort() {SELFPARAM(); entity mf, ef; - if(self.deadflag != DEAD_NO) + if(IS_DEAD(self)) { havocbot_ctf_reset_role(self); return; @@ -1823,7 +1852,7 @@ void havocbot_role_ctf_offense() entity mf, ef; vector pos; - if(self.deadflag != DEAD_NO) + if(IS_DEAD(self)) { havocbot_ctf_reset_role(self); return; @@ -1904,7 +1933,7 @@ void havocbot_role_ctf_retriever() {SELFPARAM(); entity mf; - if(self.deadflag != DEAD_NO) + if(IS_DEAD(self)) { havocbot_ctf_reset_role(self); return; @@ -1952,7 +1981,7 @@ void havocbot_role_ctf_middle() {SELFPARAM(); entity mf; - if(self.deadflag != DEAD_NO) + if(IS_DEAD(self)) { havocbot_ctf_reset_role(self); return; @@ -2003,7 +2032,7 @@ void havocbot_role_ctf_defense() {SELFPARAM(); entity mf; - if(self.deadflag != DEAD_NO) + if(IS_DEAD(self)) { havocbot_ctf_reset_role(self); return; @@ -2043,20 +2072,16 @@ void havocbot_role_ctf_defense() navigation_goalrating_start(); // if enemies are closer to our base, go there - entity head, closestplayer = world; + entity closestplayer = world; float distance, bestdistance = 10000; - FOR_EACH_PLAYER(head) - { - if(head.deadflag!=DEAD_NO) - continue; - - distance = vlen(org - head.origin); + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), LAMBDA( + distance = vlen(org - it.origin); if(distance ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id))) if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time) @@ -2207,7 +2232,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerDies) { if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker)) && (frag_target.flagcarried)) { - PlayerTeamScore_AddScore(frag_attacker, autocvar_g_ctf_score_kill); + PlayerTeamScore_AddScore(frag_attacker, ((SAME_TEAM(frag_attacker, frag_target)) ? -autocvar_g_ctf_score_kill : autocvar_g_ctf_score_kill)); PlayerScore_Add(frag_attacker, SP_CTF_FCKILLS, 1); } @@ -2267,7 +2292,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerUseKey) entity player = self; - if((time > player.throw_antispam) && (player.deadflag == DEAD_NO) && !player.speedrunning && (!player.vehicle || autocvar_g_ctf_allow_vehicle_touch)) + if((time > player.throw_antispam) && !IS_DEAD(player) && !player.speedrunning && (!player.vehicle || autocvar_g_ctf_allow_vehicle_touch)) { // pass the flag to a team mate if(autocvar_g_ctf_pass) @@ -2277,7 +2302,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerUseKey) while(head) // find the closest acceptable target to pass to { - if(IS_PLAYER(head) && head.deadflag == DEAD_NO) + if(IS_PLAYER(head) && !IS_DEAD(head)) if(head != player && SAME_TEAM(head, player)) if(!head.speedrunning && !head.vehicle) { @@ -2412,7 +2437,7 @@ MUTATOR_HOOKFUNCTION(ctf, AbortSpeedrun) {SELFPARAM(); if(self.flagcarried) { - Send_Notification(NOTIF_ALL, world, MSG_INFO, ((self.flagcarried.team) ? APP_TEAM_ENT_4(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, ((self.flagcarried.team) ? APP_TEAM_ENT(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL)); ctf_RespawnFlag(self.flagcarried); return true; } @@ -2504,7 +2529,6 @@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand) if(!g_ctf) return true; - entity _player; int _team = 0; bool found = false; @@ -2519,16 +2543,15 @@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand) } } - FOR_EACH_PLAYER(_player) - { - if(_player.flagcarried && (_player.team == _team || _team == 0)) + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + if(it.flagcarried && (it.team == _team || _team == 0)) { found = true; - if(_team == 0 && IS_SPEC(self) && self.enemy == _player) - continue; // already spectating a fc, try to find the other fc - return superspec_Spectate(_player); + if(_team == 0 && IS_SPEC(self) && self.enemy == it) + continue; // already spectating this fc, try another + return superspec_Spectate(it); } - } + )); if(!found) superspec_msg("", "", self, "No active flag carrier\n", 1); @@ -2692,7 +2715,7 @@ void ctf_ScoreRules(int teams) // code from here on is just to support maps that don't have flag and team entities void ctf_SpawnTeam (string teamname, int teamcolor) { - entity this = new(ctf_team); + entity this = new_pure(ctf_team); this.netname = teamname; this.cnt = teamcolor; this.spawnfunc_checked = true; @@ -2716,7 +2739,7 @@ void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to // if no teams are found, spawn defaults if(find(world, classname, "ctf_team") == world) { - LOG_INFO("No ""ctf_team"" entities found on this map, creating them anyway.\n"); + LOG_TRACE("No \"ctf_team\" entities found on this map, creating them anyway.\n"); ctf_SpawnTeam("Red", NUM_TEAM_1 - 1); ctf_SpawnTeam("Blue", NUM_TEAM_2 - 1); if(ctf_teams >= 3) @@ -2736,37 +2759,7 @@ void ctf_Initialize() ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio; ctf_captureshield_force = autocvar_g_ctf_shield_force; - addstat(STAT_CTF_FLAGSTATUS, AS_INT, ctf_flagstatus); - InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE); } -REGISTER_MUTATOR(ctf, g_ctf) -{ - ActivateTeamplay(); - SetLimits(autocvar_capturelimit_override, -1, autocvar_captureleadlimit_override, -1); - have_team_spawns = -1; // request team spawns - - MUTATOR_ONADD - { - if(time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - ctf_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back ctf_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} #endif