]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_ctf.qc
Clean up a bunch of gamemode specific code
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_ctf.qc
index 108b8dda6f369b10163cdc376d968e90e6d65a49..016f9bd47a034ab31f651a40d9706afab771e8b5 100644 (file)
@@ -1,13 +1,82 @@
 #include "gamemode_ctf.qh"
-#include "../_all.qh"
 
 #include "gamemode.qh"
 
 #ifdef SVQC
 #include "../../common/vehicles/all.qh"
+#include "../teamplay.qh"
 #endif
 
-#include "../../warpzonelib/common.qh"
+#include "../../lib/warpzone/common.qh"
+
+bool autocvar_g_ctf_allow_vehicle_carry;
+bool autocvar_g_ctf_allow_vehicle_touch;
+bool autocvar_g_ctf_allow_monster_touch;
+bool autocvar_g_ctf_throw;
+float autocvar_g_ctf_throw_angle_max;
+float autocvar_g_ctf_throw_angle_min;
+int autocvar_g_ctf_throw_punish_count;
+float autocvar_g_ctf_throw_punish_delay;
+float autocvar_g_ctf_throw_punish_time;
+float autocvar_g_ctf_throw_strengthmultiplier;
+float autocvar_g_ctf_throw_velocity_forward;
+float autocvar_g_ctf_throw_velocity_up;
+float autocvar_g_ctf_drop_velocity_up;
+float autocvar_g_ctf_drop_velocity_side;
+bool autocvar_g_ctf_oneflag_reverse;
+bool autocvar_g_ctf_portalteleport;
+bool autocvar_g_ctf_pass;
+float autocvar_g_ctf_pass_arc;
+float autocvar_g_ctf_pass_arc_max;
+float autocvar_g_ctf_pass_directional_max;
+float autocvar_g_ctf_pass_directional_min;
+float autocvar_g_ctf_pass_radius;
+float autocvar_g_ctf_pass_wait;
+bool autocvar_g_ctf_pass_request;
+float autocvar_g_ctf_pass_turnrate;
+float autocvar_g_ctf_pass_timelimit;
+float autocvar_g_ctf_pass_velocity;
+bool autocvar_g_ctf_dynamiclights;
+float autocvar_g_ctf_flag_collect_delay;
+float autocvar_g_ctf_flag_damageforcescale;
+bool autocvar_g_ctf_flag_dropped_waypoint;
+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;
+float autocvar_g_ctf_flag_return_carried_radius;
+float autocvar_g_ctf_flag_return_time;
+bool autocvar_g_ctf_flag_return_when_unreachable;
+float autocvar_g_ctf_flag_return_damage;
+float autocvar_g_ctf_flag_return_damage_delay;
+float autocvar_g_ctf_flag_return_dropped;
+float autocvar_g_ctf_flagcarrier_auto_helpme_damage;
+float autocvar_g_ctf_flagcarrier_auto_helpme_time;
+float autocvar_g_ctf_flagcarrier_selfdamagefactor;
+float autocvar_g_ctf_flagcarrier_selfforcefactor;
+float autocvar_g_ctf_flagcarrier_damagefactor;
+float autocvar_g_ctf_flagcarrier_forcefactor;
+//float autocvar_g_ctf_flagcarrier_waypointforenemy_spotting;
+bool autocvar_g_ctf_fullbrightflags;
+bool autocvar_g_ctf_ignore_frags;
+int autocvar_g_ctf_score_capture;
+int autocvar_g_ctf_score_capture_assist;
+int autocvar_g_ctf_score_kill;
+int autocvar_g_ctf_score_penalty_drop;
+int autocvar_g_ctf_score_penalty_returned;
+int autocvar_g_ctf_score_pickup_base;
+int autocvar_g_ctf_score_pickup_dropped_early;
+int autocvar_g_ctf_score_pickup_dropped_late;
+int autocvar_g_ctf_score_return;
+float autocvar_g_ctf_shield_force;
+float autocvar_g_ctf_shield_max_ratio;
+int autocvar_g_ctf_shield_min_negscore;
+bool autocvar_g_ctf_stalemate;
+int autocvar_g_ctf_stalemate_endcondition;
+float autocvar_g_ctf_stalemate_time;
+bool autocvar_g_ctf_reverse;
+float autocvar_g_ctf_dropped_capture_delay;
+float autocvar_g_ctf_dropped_capture_radius;
 
 void ctf_FakeTimeLimit(entity e, float t)
 {
@@ -53,8 +122,8 @@ void ctf_CaptureRecord(entity flag, entity player)
 void ctf_FlagcarrierWaypoints(entity player)
 {
        WaypointSprite_Spawn(WP_FlagCarrier, 0, 0, player, FLAG_WAYPOINT_OFFSET, world, player.team, player, wps_flagcarrier, true, RADARICON_FLAG);
-       WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON) * 2);
-       WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
+       WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2);
+       WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id));
        WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team));
 }
 
@@ -194,7 +263,7 @@ void ctf_CaptureShield_Touch()
        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);
+       Damage(other, self, self, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
        if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); }
 }
 
@@ -1518,7 +1587,7 @@ void havocbot_role_ctf_carrier()
                {
                        // Can't navigate to my own base, suicide!
                        // TODO: drop it and wander around
-                       Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
+                       Damage(self, self, self, 100000, DEATH_KILL.m_id, self.origin, '0 0 0');
                        return;
                }
        }
@@ -1889,7 +1958,7 @@ void havocbot_role_ctf_setrole(entity bot, int role)
 // Hook Functions
 // ==============
 
-MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
+MUTATOR_HOOKFUNCTION(ctfPlayerPreThink)
 {SELFPARAM();
        entity flag;
        int t = 0, t2 = 0, t3 = 0;
@@ -1936,12 +2005,12 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
 
        // update the health of the flag carrier waypointsprite
        if(self.wps_flagcarrier)
-               WaypointSprite_UpdateHealth(self.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON));
+               WaypointSprite_UpdateHealth(self.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id));
 
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
+MUTATOR_HOOKFUNCTION(ctf, PlayerDamage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc
 {
        if(frag_attacker.flagcarried) // if the attacker is a flagcarrier
        {
@@ -1958,7 +2027,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values t
        }
        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(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.m_id)))
                if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time)
                {
                        frag_target.wps_helpme_time = time;
@@ -1969,7 +2038,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values t
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_PlayerDies)
+MUTATOR_HOOKFUNCTION(ctfPlayerDies)
 {
        if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker)) && (frag_target.flagcarried))
        {
@@ -1987,30 +2056,38 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerDies)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKill)
+MUTATOR_HOOKFUNCTION(ctfGiveFragsForKill)
 {
        frag_score = 0;
        return (autocvar_g_ctf_ignore_frags); // no frags counted in ctf if this is true
 }
 
-MUTATOR_HOOKFUNCTION(ctf_RemovePlayer)
-{SELFPARAM();
-       entity flag; // temporary entity for the search method
-
-       if(self.flagcarried)
-               { ctf_Handle_Throw(self, world, DROP_NORMAL); }
+void ctf_RemovePlayer(entity player)
+{
+       if(player.flagcarried)
+               { ctf_Handle_Throw(player, world, DROP_NORMAL); }
 
-       for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
+       for(entity flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
        {
-               if(flag.pass_sender == self) { flag.pass_sender = world; }
-               if(flag.pass_target == self) { flag.pass_target = world; }
-               if(flag.ctf_dropper == self) { flag.ctf_dropper = world; }
+               if(flag.pass_sender == player) { flag.pass_sender = world; }
+               if(flag.pass_target == player) { flag.pass_target = world; }
+               if(flag.ctf_dropper == player) { flag.ctf_dropper = world; }
        }
+}
 
+MUTATOR_HOOKFUNCTION(ctf, MakePlayerObserver)
+{SELFPARAM();
+       ctf_RemovePlayer(self);
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_PortalTeleport)
+MUTATOR_HOOKFUNCTION(ctf, ClientDisconnect)
+{SELFPARAM();
+       ctf_RemovePlayer(self);
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ctf, PortalTeleport)
 {SELFPARAM();
        if(self.flagcarried)
        if(!autocvar_g_ctf_portalteleport)
@@ -2019,7 +2096,7 @@ MUTATOR_HOOKFUNCTION(ctf_PortalTeleport)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
+MUTATOR_HOOKFUNCTION(ctfPlayerUseKey)
 {SELFPARAM();
        if(MUTATOR_RETURNVALUE || gameover) { return false; }
 
@@ -2112,7 +2189,7 @@ MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_HelpMePing)
+MUTATOR_HOOKFUNCTION(ctfHelpMePing)
 {SELFPARAM();
        if(self.wps_flagcarrier) // update the flagcarrier waypointsprite with "NEEDING HELP" notification
        {
@@ -2128,7 +2205,7 @@ MUTATOR_HOOKFUNCTION(ctf_HelpMePing)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_VehicleEnter)
+MUTATOR_HOOKFUNCTION(ctfVehicleEnter)
 {
        if(vh_player.flagcarried)
        {
@@ -2151,7 +2228,7 @@ MUTATOR_HOOKFUNCTION(ctf_VehicleEnter)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_VehicleExit)
+MUTATOR_HOOKFUNCTION(ctfVehicleExit)
 {
        if(vh_player.flagcarried)
        {
@@ -2166,7 +2243,7 @@ MUTATOR_HOOKFUNCTION(ctf_VehicleExit)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun)
+MUTATOR_HOOKFUNCTION(ctfAbortSpeedrun)
 {SELFPARAM();
        if(self.flagcarried)
        {
@@ -2178,7 +2255,7 @@ MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_MatchEnd)
+MUTATOR_HOOKFUNCTION(ctfMatchEnd)
 {
        entity flag; // temporary entity for the search method
 
@@ -2212,25 +2289,98 @@ MUTATOR_HOOKFUNCTION(ctf_MatchEnd)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_BotRoles)
+MUTATOR_HOOKFUNCTION(ctf, HavocBot_ChooseRole)
 {SELFPARAM();
        havocbot_ctf_reset_role(self);
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_GetTeamCount)
+MUTATOR_HOOKFUNCTION(ctfGetTeamCount)
 {
        //ret_float = ctf_teams;
        ret_string = "ctf_team";
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ctf_SpectateCopy)
+MUTATOR_HOOKFUNCTION(ctfSpectateCopy)
 {SELFPARAM();
        self.ctf_flagstatus = other.ctf_flagstatus;
        return false;
 }
 
+MUTATOR_HOOKFUNCTION(ctf, GetRecords)
+{
+       for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i)
+       {
+               if (MapInfo_Get_ByID(i))
+               {
+                       float r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
+
+                       if(!r)
+                               continue;
+
+                       // TODO: uid2name
+                       string h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
+                       ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
+               }
+       }
+
+       return false;
+}
+
+bool superspec_Spectate(entity _player); // TODO
+void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel); // TODO
+MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand)
+{
+       if(IS_PLAYER(self) || MUTATOR_RETURNVALUE || !cvar("g_superspectate")) { return false; }
+
+       if(cmd_name == "followfc")
+       {
+               if(!g_ctf)
+                       return true;
+
+               entity _player;
+               int _team = 0;
+               bool found = false;
+
+               if(cmd_argc == 2)
+               {
+                       switch(argv(1))
+                       {
+                               case "red": _team = NUM_TEAM_1; break;
+                               case "blue": _team = NUM_TEAM_2; break;
+                               case "yellow": if(ctf_teams >= 3) _team = NUM_TEAM_3; break;
+                               case "pink": if(ctf_teams >= 4) _team = NUM_TEAM_4; break;
+                       }
+               }
+
+               FOR_EACH_PLAYER(_player)
+               {
+                       if(_player.flagcarried && (_player.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(!found)
+                       superspec_msg("", "", self, "No active flag carrier\n", 1);
+               return true;
+       }
+
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ctf, DropSpecialItems)
+{
+       if(frag_target.flagcarried)
+               ctf_Handle_Throw(frag_target, world, DROP_THROW);
+
+       return false;
+}
+
 
 // ==========
 // Spawnfuncs
@@ -2344,9 +2494,11 @@ spawnfunc(ctf_team)
 // compatibility for quake maps
 spawnfunc(team_CTF_redflag)    { spawnfunc_item_flag_team1(this);    }
 spawnfunc(team_CTF_blueflag)   { spawnfunc_item_flag_team2(this);    }
+spawnfunc(info_player_team1);
 spawnfunc(team_CTF_redplayer)  { spawnfunc_info_player_team1(this);  }
-spawnfunc(team_CTF_blueplayer) { spawnfunc_info_player_team2(this);  }
 spawnfunc(team_CTF_redspawn)   { spawnfunc_info_player_team1(this);  }
+spawnfunc(info_player_team2);
+spawnfunc(team_CTF_blueplayer) { spawnfunc_info_player_team2(this);  }
 spawnfunc(team_CTF_bluespawn)  { spawnfunc_info_player_team2(this);  }
 
 void team_CTF_neutralflag()                     { SELFPARAM(); spawnfunc_item_flag_neutral(self);  }
@@ -2424,25 +2576,11 @@ void ctf_Initialize()
        InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
 }
 
-
-MUTATOR_DEFINITION(gamemode_ctf)
+REGISTER_MUTATOR(ctf, g_ctf)
 {
-       MUTATOR_HOOK(MakePlayerObserver, ctf_RemovePlayer, CBC_ORDER_ANY);
-       MUTATOR_HOOK(ClientDisconnect, ctf_RemovePlayer, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerDies, ctf_PlayerDies, CBC_ORDER_ANY);
-       MUTATOR_HOOK(MatchEnd, ctf_MatchEnd, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PortalTeleport, ctf_PortalTeleport, CBC_ORDER_ANY);
-       MUTATOR_HOOK(GiveFragsForKill, ctf_GiveFragsForKill, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerPreThink, ctf_PlayerPreThink, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerDamage_Calculate, ctf_PlayerDamage, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerUseKey, ctf_PlayerUseKey, CBC_ORDER_ANY);
-       MUTATOR_HOOK(HelpMePing, ctf_HelpMePing, CBC_ORDER_ANY);
-       MUTATOR_HOOK(VehicleEnter, ctf_VehicleEnter, CBC_ORDER_ANY);
-       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);
+       ActivateTeamplay();
+       SetLimits(autocvar_capturelimit_override, -1, autocvar_captureleadlimit_override, -1);
+       have_team_spawns = -1; // request team spawns
 
        MUTATOR_ONADD
        {