X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=blobdiff_plain;f=qcsrc%2Fserver%2Fmutators%2Fgamemode_ctf.qc;fp=qcsrc%2Fserver%2Fmutators%2Fgamemode_ctf.qc;h=0e49615a158b36ee9c28bbdead67f9586dcfa69c;hp=967d8d656fdf72b43a54e7fdff077c3d111c4d50;hb=5b9d6b7e214e7c48d7de4f1c7d62006707463135;hpb=0b50c5b420fff105059d69ce49b5e2774c3170d9 diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc index 967d8d656f..0e49615a15 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@ -17,7 +17,8 @@ void ctf_FakeTimeLimit(entity e, float t) void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later { if(autocvar_sv_eventlog) - GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""))); + GameLogEcho(sprintf(":ctf:%s:%d:%d:%s", mode, flagteam, actor.team, ((actor != world) ? ftos(actor.playerid) : ""))); + //GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""))); } void ctf_CaptureRecord(entity flag, entity player) @@ -27,11 +28,13 @@ void ctf_CaptureRecord(entity flag, entity player) string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname")); // notify about shit - if(!ctf_captimerecord) { Send_Notification(NOTIF_ALL, world, MSG_CHOICE, APP_TEAM_ENT_2(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_2(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_2(flag, CHOICE_CTF_CAPTURE_UNBROKEN_), player.netname, refername, (cap_time * 100), (cap_record * 100)); } + 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)); } // write that shit in the database + if(!ctf_oneflag) // but not in 1-flag mode if((!ctf_captimerecord) || (cap_time < cap_record)) { ctf_captimerecord = cap_time; @@ -118,15 +121,21 @@ float ctf_CheckPassDirection(vector head_center, vector passer_center, vector pa float ctf_CaptureShield_CheckStatus(entity p) { - float s, se; + float s, s2, s3, s4, se, se2, se3, se4, sr, ser; entity e; float players_worseeq, players_total; if(ctf_captureshield_max_ratio <= 0) return FALSE; - s = PlayerScore_Add(p, SP_SCORE, 0); - if(s >= -ctf_captureshield_min_negscore) + s = PlayerScore_Add(p, SP_CTF_CAPS, 0); + s2 = PlayerScore_Add(p, SP_CTF_PICKUPS, 0); + s3 = PlayerScore_Add(p, SP_CTF_RETURNS, 0); + s4 = PlayerScore_Add(p, SP_CTF_FCKILLS, 0); + + sr = ((s - s2) + (s3 + s4)); + + if(sr >= -ctf_captureshield_min_negscore) return FALSE; players_total = players_worseeq = 0; @@ -134,8 +143,14 @@ float ctf_CaptureShield_CheckStatus(entity p) { if(DIFF_TEAM(e, p)) continue; - se = PlayerScore_Add(e, SP_SCORE, 0); - if(se <= s) + 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); + + ser = ((se - se2) + (se3 + se4)); + + if(ser <= sr) ++players_worseeq; ++players_total; } @@ -161,22 +176,34 @@ void ctf_CaptureShield_Update(entity player, float wanted_status) float ctf_CaptureShield_Customize() { + if(self.enemy.active != ACTIVE_ACTIVE) { return TRUE; } if(!other.ctf_captureshielded) { return FALSE; } - if(SAME_TEAM(self, other)) { return FALSE; } + if(CTF_SAMETEAM(self, other)) { return FALSE; } return TRUE; } void ctf_CaptureShield_Touch() { + if(self.enemy.active != ACTIVE_ACTIVE) + { + vector mymid = (self.absmin + self.absmax) * 0.5; + vector othermid = (other.absmin + other.absmax) * 0.5; + + Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force); + if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_INACTIVE); } + + return; + } + if(!other.ctf_captureshielded) { return; } - if(SAME_TEAM(self, other)) { return; } + if(CTF_SAMETEAM(self, other)) { return; } vector mymid = (self.absmin + self.absmax) * 0.5; vector othermid = (other.absmin + other.absmax) * 0.5; Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force); - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); + if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); } } void ctf_CaptureShield_Spawn(entity flag) @@ -219,7 +246,7 @@ void ctf_Handle_Drop(entity flag, entity player, float droptype) flag.ctf_status = FLAG_DROPPED; // messages and sounds - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_LOST_), player.netname); + Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(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); @@ -257,8 +284,17 @@ void ctf_Handle_Retrieve(entity flag, entity player) flag.owner.flagcarried = flag; // reset flag - setattachment(flag, player, ""); - setorigin(flag, FLAG_CARRY_OFFSET); + if(player.vehicle) + { + setattachment(flag, player.vehicle, ""); + setorigin(flag, VEHICLE_FLAG_OFFSET); + flag.scale = VEHICLE_FLAG_SCALE; + } + else + { + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + } flag.movetype = MOVETYPE_NONE; flag.takedamage = DAMAGE_NO; flag.solid = SOLID_NOT; @@ -272,11 +308,11 @@ void ctf_Handle_Retrieve(entity flag, entity player) FOR_EACH_REALPLAYER(tmp_player) { if(tmp_player == sender) - Send_Notification(NOTIF_ONE, tmp_player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_SENT_), player.netname); + 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, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_RECEIVED_), sender.netname); + 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, APP_TEAM_ENT_2(flag, CENTER_CTF_PASS_OTHER_), sender.netname, player.netname); + 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); } // create new waypoint @@ -384,16 +420,33 @@ void ctf_Handle_Capture(entity flag, entity toucher, float capturetype) { entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher); entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper); + entity player_team_flag = world, tmp_entity; float old_time, new_time; - if (!player) { return; } // without someone to give the reward to, we can't possibly cap + if(!player) { return; } // without someone to give the reward to, we can't possibly cap + if(ctf_oneflag) + { + if(CTF_SAMETEAM(player, flag)) { return; } + } + else if(CTF_DIFFTEAM(player, flag)) { return; } + + if(ctf_oneflag) + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + if(SAME_TEAM(tmp_entity, player)) + { + player_team_flag = tmp_entity; + break; + } + + player.throw_prevtime = time; + player.throw_count = 0; nades_GiveBonus(player, autocvar_g_nades_bonus_score_high ); // messages and sounds - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(enemy_flag, CENTER_CTF_CAPTURE_)); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT_4(enemy_flag, CENTER_CTF_CAPTURE_) : CENTER_CTF_CAPTURE_NEUTRAL)); ctf_CaptureRecord(enemy_flag, player); - sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTEN_NONE); + 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); switch(capturetype) { @@ -435,12 +488,12 @@ void ctf_Handle_Return(entity flag, entity player) // messages and sounds if(player.flags & FL_MONSTER) { - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name); + Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(flag, INFO_CTF_RETURN_MONSTER_), player.monster_name); } - else + else if(flag.team) { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_RETURN_)); - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_RETURN_), player.netname); + 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); } sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE); ctf_EventLog("return", flag.team, player); @@ -463,6 +516,10 @@ void ctf_Handle_Return(entity flag, entity player) flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time } + // other + if(player.flagcarried == flag) + WaypointSprite_Kill(player.wps_flagcarrier); + // reset the flag ctf_RespawnFlag(flag); } @@ -471,12 +528,22 @@ void ctf_Handle_Pickup(entity flag, entity player, float 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; player.flagcarried = flag; - setattachment(flag, player, ""); - setorigin(flag, FLAG_CARRY_OFFSET); + if(player.vehicle) + { + setattachment(flag, player.vehicle, ""); + setorigin(flag, VEHICLE_FLAG_OFFSET); + flag.scale = VEHICLE_FLAG_SCALE; + } + else + { + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + } // flag setup flag.movetype = MOVETYPE_NONE; @@ -493,12 +560,28 @@ void ctf_Handle_Pickup(entity flag, entity player, float pickuptype) } // messages and sounds - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_PICKUP_), player.netname); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(flag, CENTER_CTF_PICKUP_)); + Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(flag, INFO_CTF_PICKUP_) : INFO_CTF_PICKUP_NEUTRAL), player.netname); if(ctf_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); } - - Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, CHOICE_CTF_PICKUP_TEAM, Team_ColorCode(player.team), player.netname); - Send_Notification(NOTIF_TEAM, flag, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY, Team_ColorCode(player.team), player.netname); + 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 { 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); + + 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); + + 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); sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE); @@ -559,14 +642,14 @@ void ctf_CheckFlagReturn(entity flag, float returntype) { switch(returntype) { - case RETURN_DROPPED: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_DROPPED_)); break; - case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_DAMAGED_)); break; - case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_), ctf_captimerecord); break; - case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_NEEDKILL_)); break; + 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; default: case RETURN_TIMEOUT: - { Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(flag, INFO_CTF_FLAGRETURN_TIMEOUT_)); break; } + { Send_Notification(NOTIF_ALL, world, MSG_INFO, ((flag.team) ? APP_TEAM_ENT_4(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); @@ -575,10 +658,25 @@ void ctf_CheckFlagReturn(entity flag, float returntype) } } +float ctf_Stalemate_Customize() +{ + // make spectators see what the player would see + entity e, wp_owner; + e = WaypointSprite_getviewentity(other); + wp_owner = self.owner; + + // team waypoints + if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return FALSE; } + if(SAME_TEAM(wp_owner, e)) { return FALSE; } + if(!IS_PLAYER(e)) { return FALSE; } + + return TRUE; +} + void ctf_CheckStalemate(void) { // declarations - float stale_red_flags = 0, stale_blue_flags = 0; + float stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0; entity tmp_entity; entity ctf_staleflaglist = world; // reset the list, we need to build the list each time this function runs @@ -597,15 +695,25 @@ void ctf_CheckStalemate(void) { case NUM_TEAM_1: ++stale_red_flags; break; case NUM_TEAM_2: ++stale_blue_flags; break; + case NUM_TEAM_3: ++stale_yellow_flags; break; + case NUM_TEAM_4: ++stale_pink_flags; break; + default: ++stale_neutral_flags; break; } } } - if(stale_red_flags && stale_blue_flags) + if(ctf_oneflag) + stale_flags = (stale_neutral_flags >= 1); + else + stale_flags = (stale_red_flags >= 1) + (stale_blue_flags >= 1) + (stale_yellow_flags >= 1) + (stale_pink_flags >= 1); + + if(ctf_oneflag && stale_flags == 1) ctf_stalemate = TRUE; - else if((!stale_red_flags && !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 2) + else if(stale_flags == ctf_teams) + ctf_stalemate = TRUE; + else if(stale_flags == 0 && autocvar_g_ctf_stalemate_endcondition == 2) { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; } - else if((!stale_red_flags || !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 1) + else if(stale_flags < ctf_teams && autocvar_g_ctf_stalemate_endcondition == 1) { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; } // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary @@ -614,7 +722,10 @@ void ctf_CheckStalemate(void) for(tmp_entity = ctf_staleflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_staleflagnext) { if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier)) - WaypointSprite_Spawn("enemyflagcarrier", 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, tmp_entity.team, tmp_entity.owner, wps_enemyflagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team)); + { + WaypointSprite_Spawn(((ctf_oneflag) ? "flagcarrier" : "enemyflagcarrier"), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, 0, tmp_entity.owner, wps_enemyflagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team)); + tmp_entity.owner.wps_enemyflagcarrier.customizeentityforclient = ctf_Stalemate_Customize; + } } if (!wpforenemy_announced) @@ -631,9 +742,15 @@ void ctf_FlagDamage(entity inflictor, entity attacker, float damage, float death { if(ITEM_DAMAGE_NEEDKILL(deathtype)) { - // automatically kill the flag and return it - self.health = 0; - ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + if(autocvar_g_ctf_flag_return_damage_delay) + { + self.ctf_flagdamaged = TRUE; + } + else + { + self.health = 0; + ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + } return; } if(autocvar_g_ctf_flag_return_damage) @@ -716,7 +833,13 @@ void ctf_FlagThink() return; } } - if(autocvar_g_ctf_flag_return_time) + if(self.ctf_flagdamaged) + { + self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE); + ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + return; + } + else if(autocvar_g_ctf_flag_return_time) { self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE); ctf_CheckFlagReturn(self, RETURN_TIMEOUT); @@ -746,6 +869,13 @@ void ctf_FlagThink() wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check) } } + if(CTF_SAMETEAM(self, self.owner) && self.team) + { + if(autocvar_g_ctf_flag_return) // drop the flag if reverse status has changed + ctf_Handle_Throw(self.owner, world, DROP_THROW); + else if(vlen(self.owner.origin - self.ctf_spawnorigin) <= autocvar_g_ctf_flag_return_carried_radius) + ctf_Handle_Return(self, self.owner); + } return; } @@ -757,6 +887,7 @@ void ctf_FlagThink() if((self.pass_target == world) || (self.pass_target.deadflag != DEAD_NO) + || (self.pass_target.flagcarried) || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius) || ((trace_fraction < 1) && (trace_ent != self.pass_target)) || (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit)) @@ -783,18 +914,24 @@ void ctf_FlagThink() void ctf_FlagTouch() { if(gameover) { return; } + if(self.active != ACTIVE_ACTIVE) { return; } - entity toucher = other; - float is_not_monster = (!(toucher.flags & FL_MONSTER)); + entity toucher = other, tmp_entity; + float is_not_monster = (!(toucher.flags & FL_MONSTER)), num_perteam = 0; // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces if(ITEM_TOUCH_NEEDKILL()) { - self.health = 0; - ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + if(!autocvar_g_ctf_flag_return_damage_delay) + { + self.health = 0; + ctf_CheckFlagReturn(self, RETURN_NEEDKILL); + } return; } + FOR_EACH_PLAYER(tmp_entity) if(SAME_TEAM(toucher, tmp_entity)) { ++num_perteam; } + // special touch behaviors if(toucher.frozen) { return; } else if(toucher.vehicle_flags & VHF_ISVEHICLE) @@ -825,16 +962,23 @@ void ctf_FlagTouch() { case FLAG_BASE: { - if(SAME_TEAM(toucher, self) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, self) && is_not_monster) + if(ctf_oneflag) + { + if(CTF_DIFFTEAM(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 + } + 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(DIFF_TEAM(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) + 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 break; } case FLAG_DROPPED: { - if(SAME_TEAM(toucher, self)) + 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 @@ -873,9 +1017,7 @@ void ctf_RespawnFlag(entity flag) // reset the player (if there is one) if((flag.owner) && (flag.owner.flagcarried == flag)) { - if(flag.owner.wps_enemyflagcarrier) - WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier); - + WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier); WaypointSprite_Kill(flag.wps_flagcarrier); flag.owner.flagcarried = world; @@ -884,7 +1026,10 @@ void ctf_RespawnFlag(entity flag) ctf_FakeTimeLimit(flag.owner, -1); } - if((flag.ctf_status == FLAG_DROPPED) && (flag.wps_flagdropped)) + if((flag.owner) && (flag.owner.vehicle)) + flag.scale = FLAG_SCALE; + + if(flag.ctf_status == FLAG_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); } // reset the flag @@ -907,6 +1052,9 @@ void ctf_RespawnFlag(entity flag) flag.ctf_dropper = world; flag.ctf_pickuptime = 0; flag.ctf_droptime = 0; + flag.ctf_flagdamaged = 0; + + ctf_CheckStalemate(); } void ctf_Reset() @@ -918,6 +1066,40 @@ void ctf_Reset() ctf_RespawnFlag(self); } +void ctf_Use() +{ + if(self.ctf_status != FLAG_BASE) { return; } + + entity flag; + float flag_cnt = 0; + + for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) + { + if(flag != self) + if(SAME_TEAM(flag, self)) + if(flag.active == ACTIVE_ACTIVE) + ++flag_cnt; + } + + if(self.active == ACTIVE_ACTIVE) + if(flag_cnt < 1) + { + dprint("ctf_Use: Unable to deactivate flag (not enough active flags on the map)\n"); + return; + } + + self.active = ((self.active) ? ACTIVE_NOT : ACTIVE_ACTIVE); + + if(self.active == ACTIVE_ACTIVE) + WaypointSprite_Ping(self.wps_flagbase); +} + +float ctf_FlagWaypoint_Customize() +{ + if(self.owner.active != ACTIVE_ACTIVE) { return FALSE; } + return TRUE; +} + void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup() { // bot waypoints @@ -926,17 +1108,28 @@ void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf self.bot_basewaypoint = self.nearestwaypoint; // waypointsprites - WaypointSprite_SpawnFixed(((self.team == NUM_TEAM_1) ? "redbase" : "bluebase"), self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, FALSE)); - WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, FALSE)); + string basename = "base"; + + switch(self.team) + { + case NUM_TEAM_1: basename = "redbase"; break; + case NUM_TEAM_2: basename = "bluebase"; break; + case NUM_TEAM_3: basename = "yellowbase"; break; + case NUM_TEAM_4: basename = "pinkbase"; break; + default: basename = "neutralbase"; break; + } + + WaypointSprite_SpawnFixed(basename, self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, ((self.team) ? Team_ColorRGB(self.team) : '1 1 1')); + WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, ((self.team) ? colormapPaletteColor(self.team - 1, FALSE) : '1 1 1')); + self.wps_flagbase.customizeentityforclient = ctf_FlagWaypoint_Customize; // captureshield setup ctf_CaptureShield_Spawn(self); } -void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc +void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc { // declarations - teamnumber = fabs(teamnumber - bound(0, autocvar_g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1. self = flag; // for later usage with droptofloor() // main setup @@ -945,9 +1138,8 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag setattachment(flag, world, ""); - flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag"); // Primarily only used for debugging or when showing nearby item name - flag.team = ((teamnumber) ? NUM_TEAM_1 : NUM_TEAM_2); // NUM_TEAM_1: color 4 team (red) - NUM_TEAM_2: color 13 team (blue) - flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough) + flag.netname = sprintf("%s%s^7 flag", Team_ColorCode(teamnumber), Team_ColorName_Upper(teamnumber)); + flag.team = teamnumber; flag.classname = "item_flag_team"; flag.target = "###item###"; // wut? flag.flags = FL_ITEM | FL_NOTARGET; @@ -964,25 +1156,27 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag flag.velocity = '0 0 0'; flag.mangle = flag.angles; flag.reset = ctf_Reset; + flag.use = ctf_Use; flag.touch = ctf_FlagTouch; flag.think = ctf_FlagThink; flag.nextthink = time + FLAG_THINKRATE; flag.ctf_status = FLAG_BASE; // appearence - if(flag.model == "") { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); } + if(flag.model == "") { flag.model = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_model : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_model : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_model : ((teamnumber == NUM_TEAM_4) ? autocvar_g_ctf_flag_pink_model : autocvar_g_ctf_flag_neutral_model)))); } if(!flag.scale) { flag.scale = FLAG_SCALE; } - if(!flag.skin) { flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin); } - if(flag.toucheffect == "") { flag.toucheffect = ((teamnumber) ? "redflag_touch" : "blueflag_touch"); } - if(flag.passeffect == "") { flag.passeffect = ((teamnumber) ? "red_pass" : "blue_pass"); } - if(flag.capeffect == "") { flag.capeffect = ((teamnumber) ? "red_cap" : "blue_cap"); } + if(!flag.skin) { flag.skin = ((teamnumber == NUM_TEAM_1) ? autocvar_g_ctf_flag_red_skin : ((teamnumber == NUM_TEAM_2) ? autocvar_g_ctf_flag_blue_skin : ((teamnumber == NUM_TEAM_3) ? autocvar_g_ctf_flag_yellow_skin : ((teamnumber == NUM_TEAM_4) ? autocvar_g_ctf_flag_pink_skin : autocvar_g_ctf_flag_neutral_skin)))); } + if(flag.toucheffect == "") { flag.toucheffect = ((teamnumber == NUM_TEAM_1) ? "redflag_touch" : ((teamnumber == NUM_TEAM_2) ? "blueflag_touch" : ((teamnumber == NUM_TEAM_3) ? "yellowflag_touch" : ((teamnumber == NUM_TEAM_4) ? "pinkflag_touch" : "neutralflag_touch")))); } + if(flag.passeffect == "") { flag.passeffect = ((teamnumber == NUM_TEAM_1) ? "red_pass" : ((teamnumber == NUM_TEAM_2) ? "blue_pass" : ((teamnumber == NUM_TEAM_3) ? "yellow_pass" : ((teamnumber == NUM_TEAM_4) ? "pink_pass" : "neutral_pass")))); } + if(flag.capeffect == "") { flag.capeffect = ((teamnumber == NUM_TEAM_1) ? "red_cap" : ((teamnumber == NUM_TEAM_2) ? "blue_cap" : ((teamnumber == NUM_TEAM_3) ? "yellow_cap" : ((teamnumber == NUM_TEAM_4) ? "pink_cap" : "")))); } // neutral flag cant be capped itself + if(!flag.active) { flag.active = ACTIVE_ACTIVE; } // sound - if(flag.snd_flag_taken == "") { flag.snd_flag_taken = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); } - if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); } - if(flag.snd_flag_capture == "") { flag.snd_flag_capture = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag + if(flag.snd_flag_taken == "") { flag.snd_flag_taken = ((teamnumber == NUM_TEAM_1) ? "ctf/red_taken.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_taken.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_taken.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_taken.wav" : "ctf/neutral_taken.wav")))); } + if(flag.snd_flag_returned == "") { flag.snd_flag_returned = ((teamnumber == NUM_TEAM_1) ? "ctf/red_returned.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_returned.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_returned.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_returned.wav" : "")))); } // neutral flag can't be returned by players + if(flag.snd_flag_capture == "") { flag.snd_flag_capture = ((teamnumber == NUM_TEAM_1) ? "ctf/red_capture.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_capture.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_capture.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_capture.wav" : "")))); } // again can't be captured if(flag.snd_flag_respawn == "") { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match. - if(flag.snd_flag_dropped == "") { flag.snd_flag_dropped = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); } + if(flag.snd_flag_dropped == "") { flag.snd_flag_dropped = ((teamnumber == NUM_TEAM_1) ? "ctf/red_dropped.wav" : ((teamnumber == NUM_TEAM_2) ? "ctf/blue_dropped.wav" : ((teamnumber == NUM_TEAM_3) ? "ctf/yellow_dropped.wav" : ((teamnumber == NUM_TEAM_4) ? "ctf/pink_dropped.wav" : "ctf/neutral_dropped.wav")))); } if(flag.snd_flag_touch == "") { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound if(flag.snd_flag_pass == "") { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here @@ -1005,15 +1199,25 @@ void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag if(autocvar_g_ctf_flag_glowtrails) { - flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue + flag.glow_color = ((teamnumber == NUM_TEAM_1) ? 251 : ((teamnumber == NUM_TEAM_2) ? 210 : ((teamnumber == NUM_TEAM_3) ? 110 : ((teamnumber == NUM_TEAM_4) ? 145 : 254)))); flag.glow_size = 25; flag.glow_trail = 1; } flag.effects |= EF_LOWPRECISION; if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; } - if(autocvar_g_ctf_dynamiclights) { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); } - + if(autocvar_g_ctf_dynamiclights) + { + switch(teamnumber) + { + case NUM_TEAM_1: flag.effects |= EF_RED; break; + case NUM_TEAM_2: flag.effects |= EF_BLUE; break; + case NUM_TEAM_3: flag.effects |= EF_DIMLIGHT; break; + case NUM_TEAM_4: flag.effects |= EF_RED; break; + default: flag.effects |= EF_DIMLIGHT; break; + } + } + // flag placement if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location { @@ -1066,7 +1270,7 @@ entity havocbot_ctf_find_flag(entity bot) f = ctf_worldflaglist; while (f) { - if (bot.team == f.team) + if (CTF_SAMETEAM(bot, f)) return f; f = f.ctf_worldflagnext; } @@ -1079,7 +1283,20 @@ entity havocbot_ctf_find_enemy_flag(entity bot) f = ctf_worldflaglist; while (f) { - if (bot.team != f.team) + if(ctf_oneflag) + { + if(CTF_DIFFTEAM(bot, f)) + { + if(f.team) + { + if(bot.flagcarried) + return f; + } + else if(!bot.flagcarried) + return f; + } + } + else if (CTF_DIFFTEAM(bot, f)) return f; f = f.ctf_worldflagnext; } @@ -1096,7 +1313,7 @@ float havocbot_ctf_teamcount(entity bot, vector org, float tc_radius) FOR_EACH_PLAYER(head) { - if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot) + if(DIFF_TEAM(head, bot) || head.deadflag != DEAD_NO || head == bot) continue; if(vlen(head.origin - org) < tc_radius) @@ -1112,7 +1329,7 @@ void havocbot_goalrating_ctf_ourflag(float ratingscale) head = ctf_worldflaglist; while (head) { - if (self.team == head.team) + if (CTF_SAMETEAM(self, head)) break; head = head.ctf_worldflagnext; } @@ -1126,7 +1343,7 @@ void havocbot_goalrating_ctf_ourbase(float ratingscale) head = ctf_worldflaglist; while (head) { - if (self.team == head.team) + if (CTF_SAMETEAM(self, head)) break; head = head.ctf_worldflagnext; } @@ -1142,7 +1359,20 @@ void havocbot_goalrating_ctf_enemyflag(float ratingscale) head = ctf_worldflaglist; while (head) { - if (self.team != head.team) + if(ctf_oneflag) + { + if(CTF_DIFFTEAM(self, head)) + { + if(head.team) + { + if(self.flagcarried) + break; + } + else if(!self.flagcarried) + break; + } + } + else if(CTF_DIFFTEAM(self, head)) break; head = head.ctf_worldflagnext; } @@ -1264,7 +1494,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(head.team==bot.team) + if(SAME_TEAM(head, bot)) ++c; if(c==1) @@ -1310,7 +1540,10 @@ void havocbot_role_ctf_carrier() self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; navigation_goalrating_start(); - havocbot_goalrating_ctf_ourbase(50000); + if(ctf_oneflag) + havocbot_goalrating_ctf_enemybase(50000); + else + havocbot_goalrating_ctf_ourbase(50000); if(self.health<100) havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000); @@ -1630,7 +1863,7 @@ void havocbot_role_ctf_defense() } if(closestplayer) - if(closestplayer.team!=self.team) + if(DIFF_TEAM(closestplayer, self)) if(vlen(org - self.origin)>1000) if(checkpvs(self.origin,closestplayer)||random()<0.5) havocbot_goalrating_ctf_ourbase(30000); @@ -1698,27 +1931,39 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink) { entity flag; + float t = 0, t2 = 0, t3 = 0; + // initially clear items so they can be set as necessary later. - self.items &= ~(IT_RED_FLAG_CARRYING | IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST - | IT_BLUE_FLAG_CARRYING | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED); + self.ctf_flagstatus &= ~(CTF_RED_FLAG_CARRYING | CTF_RED_FLAG_TAKEN | CTF_RED_FLAG_LOST + | CTF_BLUE_FLAG_CARRYING | CTF_BLUE_FLAG_TAKEN | CTF_BLUE_FLAG_LOST + | CTF_YELLOW_FLAG_CARRYING | CTF_YELLOW_FLAG_TAKEN | CTF_YELLOW_FLAG_LOST + | CTF_PINK_FLAG_CARRYING | CTF_PINK_FLAG_TAKEN | CTF_PINK_FLAG_LOST + | CTF_NEUTRAL_FLAG_CARRYING | CTF_NEUTRAL_FLAG_TAKEN | CTF_NEUTRAL_FLAG_LOST + | CTF_FLAG_NEUTRAL | CTF_SHIELDED); // scan through all the flags and notify the client about them for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) { + if(flag.team == NUM_TEAM_1) { t = CTF_RED_FLAG_CARRYING; t2 = CTF_RED_FLAG_TAKEN; t3 = CTF_RED_FLAG_LOST; } + if(flag.team == NUM_TEAM_2) { t = CTF_BLUE_FLAG_CARRYING; t2 = CTF_BLUE_FLAG_TAKEN; t3 = CTF_BLUE_FLAG_LOST; } + if(flag.team == NUM_TEAM_3) { t = CTF_YELLOW_FLAG_CARRYING; t2 = CTF_YELLOW_FLAG_TAKEN; t3 = CTF_YELLOW_FLAG_LOST; } + if(flag.team == NUM_TEAM_4) { t = CTF_PINK_FLAG_CARRYING; t2 = CTF_PINK_FLAG_TAKEN; t3 = CTF_PINK_FLAG_LOST; } + if(flag.team == 0) { t = CTF_NEUTRAL_FLAG_CARRYING; t2 = CTF_NEUTRAL_FLAG_TAKEN; t3 = CTF_NEUTRAL_FLAG_LOST; self.ctf_flagstatus |= CTF_FLAG_NEUTRAL; } + switch(flag.ctf_status) { case FLAG_PASSING: case FLAG_CARRY: { if((flag.owner == self) || (flag.pass_sender == self)) - self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_CARRYING : IT_BLUE_FLAG_CARRYING); // carrying: self is currently carrying the flag - else - self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_TAKEN : IT_BLUE_FLAG_TAKEN); // taken: someone on self's team is carrying the flag + self.ctf_flagstatus |= t; // carrying: self is currently carrying the flag + else + self.ctf_flagstatus |= t2; // taken: someone else is carrying the flag break; } case FLAG_DROPPED: { - self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_LOST : IT_BLUE_FLAG_LOST); // lost: the flag is dropped somewhere on the map + self.ctf_flagstatus |= t3; // lost: the flag is dropped somewhere on the map break; } } @@ -1726,7 +1971,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink) // item for stopping players from capturing the flag too often if(self.ctf_captureshielded) - self.items |= IT_CTF_SHIELDED; + self.ctf_flagstatus |= CTF_SHIELDED; // update the health of the flag carrier waypointsprite if(self.wps_flagcarrier) @@ -1750,7 +1995,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values t frag_force *= autocvar_g_ctf_flagcarrier_forcefactor; } } - else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && DIFF_TEAM(frag_target, frag_attacker)) // if the target is a flagcarrier + else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && CTF_DIFFTEAM(frag_target, frag_attacker)) // if the target is a flagcarrier { if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON))) if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time) @@ -1772,7 +2017,11 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerDies) } if(frag_target.flagcarried) - { ctf_Handle_Throw(frag_target, world, DROP_NORMAL); } + { + entity tmp_entity = frag_target.flagcarried; + ctf_Handle_Throw(frag_target, world, DROP_NORMAL); + tmp_entity.ctf_dropper = world; + } return FALSE; } @@ -1957,7 +2206,7 @@ MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun) { if(self.flagcarried) { - Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_2(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_)); + 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)); ctf_RespawnFlag(self.flagcarried); return TRUE; } @@ -2005,6 +2254,19 @@ MUTATOR_HOOKFUNCTION(ctf_BotRoles) return TRUE; } +MUTATOR_HOOKFUNCTION(ctf_GetTeamCount) +{ + //ret_float = ctf_teams; + ret_string = "ctf_team"; + return TRUE; +} + +MUTATOR_HOOKFUNCTION(ctf_SpectateCopy) +{ + self.ctf_flagstatus = other.ctf_flagstatus; + return FALSE; +} + // ========== // Spawnfuncs @@ -2071,7 +2333,7 @@ void spawnfunc_item_flag_team1() { if(!g_ctf) { remove(self); return; } - ctf_FlagSetup(1, self); // 1 = red + ctf_FlagSetup(NUM_TEAM_1, self); } /*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37) @@ -2089,7 +2351,61 @@ void spawnfunc_item_flag_team2() { if(!g_ctf) { remove(self); return; } - ctf_FlagSetup(0, self); // the 0 is misleading, but -- 0 = blue. + ctf_FlagSetup(NUM_TEAM_2, self); +} + +/*QUAKED spawnfunc_item_flag_team3 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team three (Yellow). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +void spawnfunc_item_flag_team3() +{ + if(!g_ctf) { remove(self); return; } + + ctf_FlagSetup(NUM_TEAM_3, self); +} + +/*QUAKED spawnfunc_item_flag_team4 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team four (Pink). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +void spawnfunc_item_flag_team4() +{ + if(!g_ctf) { remove(self); return; } + + ctf_FlagSetup(NUM_TEAM_4, self); +} + +/*QUAKED spawnfunc_item_flag_neutral (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag (Neutral). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +void spawnfunc_item_flag_neutral() +{ + if(!g_ctf) { remove(self); return; } + + ctf_FlagSetup(0, self); } /*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32) @@ -2120,9 +2436,10 @@ void spawnfunc_team_CTF_bluespawn() { spawnfunc_info_player_team2(); } // ============== // scoreboard setup -void ctf_ScoreRules() +void ctf_ScoreRules(float teams) { - ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, TRUE); + CheckAllowedTeams(world); + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); ScoreInfo_SetLabel_TeamScore (ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); @@ -2150,15 +2467,31 @@ void ctf_SpawnTeam (string teamname, float teamcolor) void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to be set up. { + ctf_teams = 2; + + entity tmp_entity; + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + { + if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); } + if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); } + if(tmp_entity.team == 0) { ctf_oneflag = TRUE; } + } + + ctf_teams = bound(2, ctf_teams, 4); + // if no teams are found, spawn defaults if(find(world, classname, "ctf_team") == world) { print("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) + ctf_SpawnTeam("Yellow", NUM_TEAM_3 - 1); + if(ctf_teams >= 4) + ctf_SpawnTeam("Pink", NUM_TEAM_4 - 1); } - ctf_ScoreRules(); + ctf_ScoreRules(ctf_teams); } void ctf_Initialize() @@ -2168,6 +2501,8 @@ void ctf_Initialize() ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore; 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); } @@ -2189,6 +2524,8 @@ MUTATOR_DEFINITION(gamemode_ctf) MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY); MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY); MUTATOR_HOOK(HavocBot_ChooseRole, ctf_BotRoles, CBC_ORDER_ANY); + MUTATOR_HOOK(GetTeamCount, ctf_GetTeamCount, CBC_ORDER_ANY); + MUTATOR_HOOK(SpectateCopy, ctf_SpectateCopy, CBC_ORDER_ANY); MUTATOR_ONADD {