]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
ka, tka: refactor/fix ball carrier time scoring
authorbones_was_here <bones_was_here@xonotic.au>
Thu, 27 Feb 2025 16:50:57 +0000 (02:50 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Fri, 14 Mar 2025 18:18:36 +0000 (04:18 +1000)
ka: runs the ball carried think func every frame to facilitate other
features, which also allows the code to be simplified somewhat and makes
cvar g_keepaway_score_timeinterval redundant. See subsequent commits.

tka: as above, also implements fractional timepoints accumulation,
making g_tka_score_timepoints behaviour consistent with
g_keepaway_score_timepoints.

qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc
qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc
qcsrc/common/gamemodes/sv_rules.qc
qcsrc/common/gamemodes/sv_rules.qh

index 648d05c3f81270303d69e18f86f8ae2f6b1944f6..b07e4327376d00f97aedc65ad133d154c76b9e95 100644 (file)
@@ -79,30 +79,28 @@ void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated
        sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
 }
 
-.float timepoints_counter;
+/// accumulates points precisely while main points value remains an integer, shared with tka
+.float timepoints_accum;
 MUTATOR_HOOKFUNCTION(ka, reset_map_global)
 {
        FOREACH_CLIENT(true,
        {
-               it.timepoints_counter = 0;
+               it.timepoints_accum = 0;
        });
        return true;
 }
 
+/// runs (only) while a player is carrying the ball
 void ka_TimeScoring(entity this)
 {
-       if(this.owner.ballcarried)
-       { // add points for holding the ball after a certain amount of time
-               float timescore = 0;
-               if(autocvar_g_keepaway_score_timepoints)
-                       timescore = autocvar_g_keepaway_score_timepoints / max(0.001, autocvar_g_keepaway_score_timeinterval);
-
-               if (timescore)
-                       GameRules_scoring_add_float2int(this.owner, SCORE, timescore, timepoints_counter, 1);
-
-               GameRules_scoring_add(this.owner, KEEPAWAY_BCTIME, 1);
-               this.nextthink = time + 1;
+       if (autocvar_g_keepaway_score_timepoints && autocvar_g_keepaway_score_timeinterval)
+       {
+               float timescore = autocvar_g_keepaway_score_timepoints / autocvar_g_keepaway_score_timeinterval;
+               GameRules_scoring_add_float2int(this.owner, SCORE, timescore * frametime, timepoints_accum, 1);
        }
+       GameRules_scoring_add(this.owner, KEEPAWAY_BCTIME, frametime);
+
+       this.nextthink = time;
 }
 
 void ka_DamageEvent(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
@@ -147,7 +145,7 @@ void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball c
        this.effects |= EF_NODRAW;
        settouch(this, func_null);
        setthink(this, ka_TimeScoring);
-       this.nextthink = time + 1;
+       this.nextthink = time;
        this.takedamage = DAMAGE_NO;
        this.event_damage = func_null;
        this.damagedbycontents = false;
index f6835cee2bb8dea00feaeec9c368e3a145c62c2f..04f686ca7eb0be46c884a4471e8790a3e22bcc60 100644 (file)
@@ -76,16 +76,28 @@ void tka_RespawnBall(entity this) // runs whenever the ball needs to be relocate
        sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
 }
 
-void tka_TimeScoring(entity this)
+/// accumulates points precisely while main points value remains an integer, shared with ka
+.float timepoints_accum;
+MUTATOR_HOOKFUNCTION(tka, reset_map_global)
 {
-       if(this.owner.ballcarried)
-       { // add points for holding the ball after a certain amount of time
-               if(autocvar_g_tka_score_timepoints)
-                       GameRules_scoring_add_team(this.owner, SCORE, autocvar_g_tka_score_timepoints);
+       FOREACH_CLIENT(true,
+       {
+               it.timepoints_accum = 0;
+       });
+       return true;
+}
 
-               GameRules_scoring_add(this.owner, TKA_BCTIME, (autocvar_g_tka_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds"
-               this.nextthink = time + autocvar_g_tka_score_timeinterval;
+/// runs (only) while a player is carrying the ball
+void tka_TimeScoring(entity this)
+{
+       if (autocvar_g_tka_score_timepoints && autocvar_g_tka_score_timeinterval)
+       {
+               float timescore = autocvar_g_tka_score_timepoints / autocvar_g_tka_score_timeinterval;
+               GameRules_scoring_add_team_float2int(this.owner, SCORE, timescore * frametime, timepoints_accum, 1);
        }
+       GameRules_scoring_add(this.owner, TKA_BCTIME, frametime);
+
+       this.nextthink = time;
 }
 
 void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something
@@ -124,7 +136,7 @@ void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball
        this.effects |= EF_NODRAW;
        settouch(this, func_null);
        setthink(this, tka_TimeScoring);
-       this.nextthink = time + autocvar_g_tka_score_timeinterval;
+       this.nextthink = time;
        this.takedamage = DAMAGE_NO;
        navigation_dynamicgoal_unset(this);
 
index 03b6ce1a404030350b52171941454c0db2366fe7..f582f82cbef1289d2344613ffeddc95b7a6cd202 100644 (file)
@@ -107,11 +107,11 @@ bool GameRules_scoring_is_vip(entity player)
        return player.m_GameRules_scoring_vip;
 }
 
-// Uses client.float_field to accumulate and consume float score and adds score to the player as int (rounded)
-// only when at least one unit of score has been accumulated. It works with negative score too
-// Float scores can't be used as score because they aren't supported by the QC score networking system
-// and online server browsers (e.g. qstat)
-float _GameRules_scoring_add_float2int(entity client, entity sp, float value, .float float_field, float score_factor)
+/// Uses client.(float_field) to accumulate and consume float score and adds score to the player as int (rounded)
+/// only when at least one unit of score has been accumulated. It works with negative score too
+/// Float scores can't be used as score because they aren't supported by the QC score networking system
+/// and online server browsers (e.g. qstat)
+float _GameRules_scoring_add_float2int(entity client, entity sp, int st, float value, .float float_field, float score_factor, bool team)
 {
        client.(float_field) += value;
        float score_counter = client.(float_field) / score_factor;
@@ -120,10 +120,10 @@ float _GameRules_scoring_add_float2int(entity client, entity sp, float value, .f
 
        // NOTE: this code works for subtracting score too
        int points = floor(score_counter + 0.5);
-       client.(float_field) -= points * score_factor;
        if (!points)
                return 0;
-       return PlayerScore_Add(client, sp, points);
+       client.(float_field) -= points * score_factor;
+       return team ? PlayerTeamScore_Add(client, sp, st, points) : PlayerScore_Add(client, sp, points);
 }
 
 float _GameRules_scoring_add(entity client, entity sp, float value)
index 6078428b66f9f189c6b26ab616190be931096f54..1cb6a83906b61c5a21f196d0180f919401f640b4 100644 (file)
@@ -73,9 +73,11 @@ void GameRules_scoring_vip(entity player, bool value);
 bool GameRules_scoring_is_vip(entity player);
 
 #define GameRules_scoring_add_float2int(client, fld, value, float_field, score_factor) \
-       _GameRules_scoring_add_float2int(client, SP_##fld, value, float_field, score_factor)
-float _GameRules_scoring_add_float2int(entity client, entity sp, float value, .float field, float score_factor);
+       _GameRules_scoring_add_float2int(client, SP_##fld, 0, value, float_field, score_factor, false)
+float _GameRules_scoring_add_float2int(entity client, entity sp, int st, float value, .float float_field, float score_factor, bool team);
 #define GameRules_scoring_add(client, fld, value) _GameRules_scoring_add(client, SP_##fld, value)
 float _GameRules_scoring_add(entity client, entity sp, float value);
+#define GameRules_scoring_add_team_float2int(client, fld, value, float_field, score_factor) \
+       _GameRules_scoring_add_float2int(client, SP_##fld, ST_##fld, value, float_field, score_factor, true)
 #define GameRules_scoring_add_team(client, fld, value) _GameRules_scoring_add_team(client, SP_##fld, ST_##fld, value)
 float _GameRules_scoring_add_team(entity client, entity sp, int st, float value);