]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/mutator/gamemode_ctf.qc
Merge branch 'master' into martin-t/dmgtext
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / mutator / gamemode_ctf.qc
index 4c88d5e45f76d5abf32348cbbe0e0339525d34d6..b34e3f59f51fe58ac3f5cccb5828b68fae45f242 100644 (file)
@@ -1,43 +1,8 @@
 #include "gamemode_ctf.qh"
 
-#ifndef CSQC
 #include <common/effects/all.qh>
-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, autocvar_timelimit_override, -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
 #include <common/vehicles/all.qh>
 #include <server/teamplay.qh>
-#endif
 
 #include <lib/warpzone/common.qh>
 
@@ -278,10 +243,10 @@ bool ctf_CaptureShield_CheckStatus(entity p)
        if(ctf_captureshield_max_ratio <= 0)
                return false;
 
-       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);
+       s  = GameRules_scoring_add(p, CTF_CAPS, 0);
+       s2 = GameRules_scoring_add(p, CTF_PICKUPS, 0);
+       s3 = GameRules_scoring_add(p, CTF_RETURNS, 0);
+       s4 = GameRules_scoring_add(p, CTF_FCKILLS, 0);
 
        sr = ((s - s2) + (s3 + s4));
 
@@ -292,10 +257,10 @@ bool ctf_CaptureShield_CheckStatus(entity p)
        FOREACH_CLIENT(IS_PLAYER(it), {
                if(DIFF_TEAM(it, p))
                        continue;
-               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);
+               se  = GameRules_scoring_add(it, CTF_CAPS, 0);
+               se2 = GameRules_scoring_add(it, CTF_PICKUPS, 0);
+               se3 = GameRules_scoring_add(it, CTF_RETURNS, 0);
+               se4 = GameRules_scoring_add(it, CTF_FCKILLS, 0);
 
                ser = ((se - se2) + (se3 + se4));
 
@@ -339,7 +304,7 @@ void ctf_CaptureShield_Touch(entity this, entity toucher)
        vector mymid = (this.absmin + this.absmax) * 0.5;
        vector theirmid = (toucher.absmin + toucher.absmax) * 0.5;
 
-       Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(theirmid - mymid) * ctf_captureshield_force);
+       Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, mymid, normalize(theirmid - mymid) * ctf_captureshield_force);
        if(IS_REAL_CLIENT(toucher)) { Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); }
 }
 
@@ -387,8 +352,8 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype)
        ctf_EventLog("dropped", player.team, player);
 
        // scoring
-       PlayerTeamScore_AddScore(player, -((flag.score_drop) ? flag.score_drop : autocvar_g_ctf_score_penalty_drop));
-       PlayerScore_Add(player, SP_CTF_DROPS, 1);
+       GameRules_scoring_add_team(player, SCORE, -((flag.score_drop) ? flag.score_drop : autocvar_g_ctf_score_penalty_drop));
+       GameRules_scoring_add(player, CTF_DROPS, 1);
 
        // waypoints
        if(autocvar_g_ctf_flag_dropped_waypoint) {
@@ -419,6 +384,7 @@ void ctf_Handle_Retrieve(entity flag, entity player)
        // transfer flag to player
        flag.owner = player;
        flag.owner.flagcarried = flag;
+       GameRules_scoring_vip(player, true);
 
        // reset flag
        if(player.vehicle)
@@ -476,6 +442,7 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype)
        setattachment(flag, NULL, "");
        setorigin(flag, player.origin + FLAG_DROP_OFFSET);
        flag.owner.flagcarried = NULL;
+       GameRules_scoring_vip(flag.owner, false);
        flag.owner = NULL;
        flag.solid = SOLID_TRIGGER;
        flag.ctf_dropper = player;
@@ -573,6 +540,9 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype)
        if(CTF_DIFFTEAM(player, flag)) { return; }
        if((flag.cnt || enemy_flag.cnt) && flag.cnt != enemy_flag.cnt) { return; } // this should catch some edge cases (capturing grouped flag at ungrouped flag disallowed etc)
 
+       if (toucher.goalentity == flag.bot_basewaypoint)
+               toucher.goalentity_lock_timeout = 0;
+
        if(ctf_oneflag)
        for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
        if(SAME_TEAM(tmp_entity, player))
@@ -602,16 +572,16 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype)
        float pscore = 0;
        if(enemy_flag.score_capture || flag.score_capture)
                pscore = floor((max(1, enemy_flag.score_capture) + max(1, flag.score_capture)) * 0.5);
-       PlayerTeamScore_AddScore(player, ((pscore) ? pscore : autocvar_g_ctf_score_capture));
+       GameRules_scoring_add_team(player, SCORE, ((pscore) ? pscore : autocvar_g_ctf_score_capture));
        float capscore = 0;
        if(enemy_flag.score_team_capture || flag.score_team_capture)
                capscore = floor((max(1, enemy_flag.score_team_capture) + max(1, flag.score_team_capture)) * 0.5);
-       PlayerTeamScore_Add(player, SP_CTF_CAPS, ST_CTF_CAPS, ((capscore) ? capscore : 1));
+       GameRules_scoring_add_team(player, CTF_CAPS, ((capscore) ? capscore : 1));
 
-       old_time = PlayerScore_Add(player, SP_CTF_CAPTIME, 0);
+       old_time = GameRules_scoring_add(player, CTF_CAPTIME, 0);
        new_time = TIME_ENCODE(time - enemy_flag.ctf_pickuptime);
        if(!old_time || new_time < old_time)
-               PlayerScore_Add(player, SP_CTF_CAPTIME, new_time - old_time);
+               GameRules_scoring_add(player, CTF_CAPTIME, new_time - old_time);
 
        // effects
        Send_Effect_(flag.capeffect, flag.origin, '0 0 0', 1);
@@ -624,9 +594,11 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype)
                if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); }
 
                if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper))
-                       { PlayerTeamScore_AddScore(enemy_flag.ctf_dropper, ((enemy_flag.score_assist) ? enemy_flag.score_assist : autocvar_g_ctf_score_capture_assist)); }
+                       { GameRules_scoring_add_team(enemy_flag.ctf_dropper, SCORE, ((enemy_flag.score_assist) ? enemy_flag.score_assist : autocvar_g_ctf_score_capture_assist)); }
        }
 
+       flag.enemy = toucher;
+
        // reset the flag
        player.next_take_time = time + autocvar_g_ctf_flag_collect_delay;
        ctf_RespawnFlag(enemy_flag);
@@ -650,8 +622,8 @@ void ctf_Handle_Return(entity flag, entity player)
        // scoring
        if(IS_PLAYER(player))
        {
-               PlayerTeamScore_AddScore(player, ((flag.score_return) ? flag.score_return : autocvar_g_ctf_score_return)); // reward for return
-               PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
+               GameRules_scoring_add_team(player, SCORE, ((flag.score_return) ? flag.score_return : autocvar_g_ctf_score_return)); // reward for return
+               GameRules_scoring_add(player, CTF_RETURNS, 1); // add to count of returns
 
                nades_GiveBonus(player,autocvar_g_nades_bonus_score_medium);
        }
@@ -660,7 +632,7 @@ void ctf_Handle_Return(entity flag, entity player)
 
        if(flag.ctf_dropper)
        {
-               PlayerScore_Add(flag.ctf_dropper, SP_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the player who dropped the flag
+               GameRules_scoring_add(flag.ctf_dropper, SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the player who dropped the flag
                ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag
                flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time
        }
@@ -669,6 +641,8 @@ void ctf_Handle_Return(entity flag, entity player)
        if(player.flagcarried == flag)
                WaypointSprite_Kill(player.wps_flagcarrier);
 
+       flag.enemy = player;
+
        // reset the flag
        ctf_RespawnFlag(flag);
 }
@@ -681,6 +655,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
        // attach the flag to the player
        flag.owner = player;
        player.flagcarried = flag;
+       GameRules_scoring_vip(player, true);
        if(player.vehicle)
        {
                setattachment(flag, player.vehicle, "");
@@ -735,13 +710,13 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
        _sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE);
 
        // scoring
-       PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
+       GameRules_scoring_add(player, CTF_PICKUPS, 1);
        nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor);
        switch(pickuptype)
        {
                case PICKUP_BASE:
                {
-                       PlayerTeamScore_AddScore(player, ((flag.score_pickup) ? flag.score_pickup : autocvar_g_ctf_score_pickup_base));
+                       GameRules_scoring_add_team(player, SCORE, ((flag.score_pickup) ? flag.score_pickup : autocvar_g_ctf_score_pickup_base));
                        ctf_EventLog("steal", flag.team, player);
                        break;
                }
@@ -751,7 +726,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype)
                        pickup_dropped_score = (autocvar_g_ctf_flag_return_time ? bound(0, ((flag.ctf_droptime + autocvar_g_ctf_flag_return_time) - time) / autocvar_g_ctf_flag_return_time, 1) : 1);
                        pickup_dropped_score = floor((autocvar_g_ctf_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_ctf_score_pickup_dropped_early * pickup_dropped_score) + 0.5);
                        LOG_TRACE("pickup_dropped_score is ", ftos(pickup_dropped_score));
-                       PlayerTeamScore_AddScore(player, pickup_dropped_score);
+                       GameRules_scoring_add_team(player, SCORE, pickup_dropped_score);
                        ctf_EventLog("pickup", flag.team, player);
                        break;
                }
@@ -805,6 +780,7 @@ void ctf_CheckFlagReturn(entity flag, int returntype)
                        }
                        _sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE);
                        ctf_EventLog("returned", flag.team, NULL);
+                       flag.enemy = NULL;
                        ctf_RespawnFlag(flag);
                }
        }
@@ -889,7 +865,7 @@ void ctf_CheckStalemate()
        }
 }
 
-void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
 {
        if(ITEM_DAMAGE_NEEDKILL(deathtype))
        {
@@ -1167,6 +1143,7 @@ void ctf_RespawnFlag(entity flag)
                WaypointSprite_Kill(flag.wps_flagcarrier);
 
                flag.owner.flagcarried = NULL;
+               GameRules_scoring_vip(flag.owner, false);
 
                if(flag.speedrunning)
                        ctf_FakeTimeLimit(flag.owner, -1);
@@ -1207,8 +1184,9 @@ void ctf_RespawnFlag(entity flag)
 void ctf_Reset(entity this)
 {
        if(this.owner && IS_PLAYER(this.owner))
-        ctf_Handle_Throw(this.owner, NULL, DROP_RESET);
+               ctf_Handle_Throw(this.owner, NULL, DROP_RESET);
 
+       this.enemy = NULL;
        ctf_RespawnFlag(this);
 }
 
@@ -1684,11 +1662,10 @@ void havocbot_role_ctf_carrier(entity this)
                return;
        }
 
-       if (this.bot_strategytime < time)
+       if (navigation_goalrating_timeout(this))
        {
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-
                navigation_goalrating_start(this);
+
                if(ctf_oneflag)
                        havocbot_goalrating_ctf_enemybase(this, 50000);
                else
@@ -1699,13 +1676,26 @@ void havocbot_role_ctf_carrier(entity this)
 
                navigation_goalrating_end(this);
 
+               navigation_goalrating_timeout_set(this);
+
+               entity head = ctf_worldflaglist;
+               while (head)
+               {
+                       if (this.goalentity == head.bot_basewaypoint)
+                       {
+                               this.goalentity_lock_timeout = time + 5;
+                               break;
+                       }
+                       head = head.ctf_worldflagnext;
+               }
+
                if (this.goalentity)
                        this.havocbot_cantfindflag = time + 10;
                else if (time > this.havocbot_cantfindflag)
                {
                        // Can't navigate to my own base, suicide!
                        // TODO: drop it and wander around
-                       Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0');
+                       Damage(this, this, this, 100000, DEATH_KILL.m_id, DMG_NOWEP, this.origin, '0 0 0');
                        return;
                }
        }
@@ -1760,14 +1750,17 @@ void havocbot_role_ctf_escort(entity this)
        }
 
        // Chase the flag carrier
-       if (this.bot_strategytime < time)
+       if (navigation_goalrating_timeout(this))
        {
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
+
                havocbot_goalrating_ctf_enemyflag(this, 30000);
                havocbot_goalrating_ctf_ourstolenflag(this, 40000);
                havocbot_goalrating_items(this, 10000, this.origin, 10000);
+
                navigation_goalrating_end(this);
+
+               navigation_goalrating_timeout_set(this);
        }
 }
 
@@ -1840,15 +1833,18 @@ void havocbot_role_ctf_offense(entity this)
                return;
        }
 
-       if (this.bot_strategytime < time)
+       if (navigation_goalrating_timeout(this))
        {
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
+
                havocbot_goalrating_ctf_ourstolenflag(this, 50000);
                havocbot_goalrating_ctf_enemybase(this, 20000);
                havocbot_goalrating_items(this, 5000, this.origin, 1000);
                havocbot_goalrating_items(this, 1000, this.origin, 10000);
+
                navigation_goalrating_end(this);
+
+               navigation_goalrating_timeout_set(this);
        }
 }
 
@@ -1873,11 +1869,8 @@ void havocbot_role_ctf_retriever(entity this)
        mf = havocbot_ctf_find_flag(this);
        if(mf.ctf_status==FLAG_BASE)
        {
-               if(this.goalcurrent == mf)
-               {
-                       navigation_clearroute(this);
-                       this.bot_strategytime = 0;
-               }
+               if (mf.enemy == this) // did this bot return the flag?
+                       navigation_goalrating_timeout_force(this);
                havocbot_ctf_reset_role(this);
                return;
        }
@@ -1891,18 +1884,21 @@ void havocbot_role_ctf_retriever(entity this)
                return;
        }
 
-       if (this.bot_strategytime < time)
+       if (navigation_goalrating_timeout(this))
        {
                float rt_radius;
                rt_radius = 10000;
 
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
+
                havocbot_goalrating_ctf_ourstolenflag(this, 50000);
                havocbot_goalrating_ctf_droppedflags(this, 40000, this.origin, rt_radius);
                havocbot_goalrating_ctf_enemybase(this, 30000);
                havocbot_goalrating_items(this, 500, this.origin, rt_radius);
+
                navigation_goalrating_end(this);
+
+               navigation_goalrating_timeout_set(this);
        }
 }
 
@@ -1938,22 +1934,25 @@ void havocbot_role_ctf_middle(entity this)
                return;
        }
 
-       if (this.bot_strategytime < time)
+       if (navigation_goalrating_timeout(this))
        {
                vector org;
 
                org = havocbot_middlepoint;
                org.z = this.origin.z;
 
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
+
                havocbot_goalrating_ctf_ourstolenflag(this, 50000);
                havocbot_goalrating_ctf_droppedflags(this, 30000, this.origin, 10000);
                havocbot_goalrating_enemyplayers(this, 10000, org, havocbot_middlepoint_radius * 0.5);
                havocbot_goalrating_items(this, 5000, org, havocbot_middlepoint_radius * 0.5);
                havocbot_goalrating_items(this, 2500, this.origin, 10000);
                havocbot_goalrating_ctf_enemybase(this, 2500);
+
                navigation_goalrating_end(this);
+
+               navigation_goalrating_timeout_set(this);
        }
 }
 
@@ -1989,11 +1988,10 @@ void havocbot_role_ctf_defense(entity this)
                havocbot_ctf_reset_role(this);
                return;
        }
-       if (this.bot_strategytime < time)
+       if (navigation_goalrating_timeout(this))
        {
                vector org = mf.dropped_origin;
 
-               this.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
                navigation_goalrating_start(this);
 
                // if enemies are closer to our base, go there
@@ -2019,7 +2017,10 @@ void havocbot_role_ctf_defense(entity this)
                havocbot_goalrating_enemyplayers(this, 15000, org, havocbot_middlepoint_radius);
                havocbot_goalrating_items(this, 10000, org, havocbot_middlepoint_radius);
                havocbot_goalrating_items(this, 5000, this.origin, 10000);
+
                navigation_goalrating_end(this);
+
+               navigation_goalrating_timeout_set(this);
        }
 }
 
@@ -2033,7 +2034,8 @@ void havocbot_role_ctf_setrole(entity bot, int role)
                        bot.havocbot_role = havocbot_role_ctf_carrier;
                        bot.havocbot_role_timeout = 0;
                        bot.havocbot_cantfindflag = time + 10;
-                       bot.bot_strategytime = 0;
+                       if (bot.havocbot_previous_role != bot.havocbot_role)
+                               navigation_goalrating_timeout_force(bot);
                        break;
                case HAVOCBOT_CTF_ROLE_DEFENSE:
                        s = "defense";
@@ -2055,14 +2057,16 @@ void havocbot_role_ctf_setrole(entity bot, int role)
                        bot.havocbot_previous_role = bot.havocbot_role;
                        bot.havocbot_role = havocbot_role_ctf_retriever;
                        bot.havocbot_role_timeout = time + 10;
-                       bot.bot_strategytime = 0;
+                       if (bot.havocbot_previous_role != bot.havocbot_role)
+                               navigation_goalrating_timeout_expire(bot, 2);
                        break;
                case HAVOCBOT_CTF_ROLE_ESCORT:
                        s = "escort";
                        bot.havocbot_previous_role = bot.havocbot_role;
                        bot.havocbot_role = havocbot_role_ctf_escort;
                        bot.havocbot_role_timeout = time + 30;
-                       bot.bot_strategytime = 0;
+                       if (bot.havocbot_previous_role != bot.havocbot_role)
+                               navigation_goalrating_timeout_expire(bot, 2);
                        break;
        }
        LOG_TRACE(bot.netname, " switched to ", s);
@@ -2081,7 +2085,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink)
        bool b1 = false, b2 = false, b3 = false, b4 = false, b5 = false; // TODO: kill this, we WANT to show the other flags, somehow! (note: also means you don't see if you're FC)
 
        // initially clear items so they can be set as necessary later.
-       player.ctf_flagstatus &= ~(CTF_RED_FLAG_CARRYING                | CTF_RED_FLAG_TAKEN            | CTF_RED_FLAG_LOST
+       STAT(CTF_FLAGSTATUS, player) &= ~(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
@@ -2095,7 +2099,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink)
                if(flag.team == NUM_TEAM_2 && !b2) { b2 = true; t = CTF_BLUE_FLAG_CARRYING;             t2 = CTF_BLUE_FLAG_TAKEN;               t3 = CTF_BLUE_FLAG_LOST; }
                if(flag.team == NUM_TEAM_3 && !b3) { b3 = true; t = CTF_YELLOW_FLAG_CARRYING;   t2 = CTF_YELLOW_FLAG_TAKEN;             t3 = CTF_YELLOW_FLAG_LOST; }
                if(flag.team == NUM_TEAM_4 && !b4) { b4 = true; t = CTF_PINK_FLAG_CARRYING;             t2 = CTF_PINK_FLAG_TAKEN;               t3 = CTF_PINK_FLAG_LOST; }
-               if(flag.team == 0 && !b5)                  { b5 = true; t = CTF_NEUTRAL_FLAG_CARRYING;  t2 = CTF_NEUTRAL_FLAG_TAKEN;    t3 = CTF_NEUTRAL_FLAG_LOST; player.ctf_flagstatus |= CTF_FLAG_NEUTRAL; }
+               if(flag.team == 0 && !b5)                  { b5 = true; t = CTF_NEUTRAL_FLAG_CARRYING;  t2 = CTF_NEUTRAL_FLAG_TAKEN;    t3 = CTF_NEUTRAL_FLAG_LOST; STAT(CTF_FLAGSTATUS, player) |= CTF_FLAG_NEUTRAL; }
 
                switch(flag.ctf_status)
                {
@@ -2103,14 +2107,14 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink)
                        case FLAG_CARRY:
                        {
                                if((flag.owner == player) || (flag.pass_sender == player))
-                                       player.ctf_flagstatus |= t; // carrying: player is currently carrying the flag
+                                       STAT(CTF_FLAGSTATUS, player) |= t; // carrying: player is currently carrying the flag
                                else
-                                       player.ctf_flagstatus |= t2; // taken: someone else is carrying the flag
+                                       STAT(CTF_FLAGSTATUS, player) |= t2; // taken: someone else is carrying the flag
                                break;
                        }
                        case FLAG_DROPPED:
                        {
-                               player.ctf_flagstatus |= t3; // lost: the flag is dropped somewhere on the map
+                               STAT(CTF_FLAGSTATUS, player) |= t3; // lost: the flag is dropped somewhere on the map
                                break;
                        }
                }
@@ -2118,10 +2122,10 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink)
 
        // item for stopping players from capturing the flag too often
        if(player.ctf_captureshielded)
-               player.ctf_flagstatus |= CTF_SHIELDED;
+               STAT(CTF_FLAGSTATUS, player) |= CTF_SHIELDED;
 
        if(ctf_stalemate)
-               player.ctf_flagstatus |= CTF_STALEMATE;
+               STAT(CTF_FLAGSTATUS, player) |= CTF_STALEMATE;
 
        // update the health of the flag carrier waypointsprite
        if(player.wps_flagcarrier)
@@ -2170,8 +2174,8 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerDies)
 
        if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker)) && (frag_target.flagcarried))
        {
-               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);
+               GameRules_scoring_add_team(frag_attacker, SCORE, ((SAME_TEAM(frag_attacker, frag_target)) ? -autocvar_g_ctf_score_kill : autocvar_g_ctf_score_kill));
+               GameRules_scoring_add(frag_attacker, CTF_FCKILLS, 1);
        }
 
        if(frag_target.flagcarried)
@@ -2471,7 +2475,7 @@ MUTATOR_HOOKFUNCTION(ctf, SpectateCopy)
        entity spectatee = M_ARGV(0, entity);
        entity client = M_ARGV(1, entity);
 
-       client.ctf_flagstatus = spectatee.ctf_flagstatus;
+       STAT(CTF_FLAGSTATUS, client) = STAT(CTF_FLAGSTATUS, spectatee);
 }
 
 MUTATOR_HOOKFUNCTION(ctf, GetRecords)
@@ -2690,15 +2694,15 @@ spawnfunc(team_CTL_bluelolly)  { spawnfunc_item_flag_team2(this);    }
 void ctf_ScoreRules(int teams)
 {
        CheckAllowedTeams(NULL);
-       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);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS, "pickups",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS, "fckills",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS, "returns",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS,   "drops",     SFL_LOWER_IS_BETTER);
-       ScoreRules_basics_end();
+       GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, 0, {
+        field_team(ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY);
+        field(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY);
+        field(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME);
+        field(SP_CTF_PICKUPS, "pickups", 0);
+        field(SP_CTF_FCKILLS, "fckills", 0);
+        field(SP_CTF_RETURNS, "returns", 0);
+        field(SP_CTF_DROPS, "drops", SFL_LOWER_IS_BETTER);
+       });
 }
 
 // code from here on is just to support maps that don't have flag and team entities