Skip to content

Commit 454e59d

Browse files
committed
feat(ratingpro): 持久化TankRaw并调整评分曲线
1 parent 5d40206 commit 454e59d

3 files changed

Lines changed: 101 additions & 9 deletions

File tree

5.2 KB
Binary file not shown.

‎addons/sourcemod/scripting/l4d2_round_ratingpro.sp‎

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313

1414
#define ZC_TANK 8
1515

16-
#define RATING_SLOPE 0.7
17-
#define RATING_INTERCEPT 2.5
16+
#define RATING_EXPONENT 0.75
1817
#define TANK_NO_DATA_SCORE 4.6
1918

2019
public Plugin myinfo =
@@ -29,6 +28,7 @@ public Plugin myinfo =
2928
ConVar g_hEnabled;
3029
ConVar g_hDelay;
3130
ConVar g_hMinRaw;
31+
StringMap g_hLastTankRaw;
3232

3333
bool g_bRoundEnded;
3434
bool g_bSeen[MAXPLAYERS + 1];
@@ -63,6 +63,8 @@ int g_iTankDeath[MAXPLAYERS + 1];
6363

6464
public void OnPluginStart()
6565
{
66+
g_hLastTankRaw = new StringMap();
67+
6668
g_hEnabled = CreateConVar("sm_round_ratingpro_enabled", "1", "Enable round-end RatingPro estimate announcement.", FCVAR_NONE, true, 0.0, true, 1.0);
6769
g_hDelay = CreateConVar("sm_round_ratingpro_delay", "3.0", "Delay after round_end before announcing the best round RatingPro client.", FCVAR_NONE, true, 0.0, true, 15.0);
6870
g_hMinRaw = CreateConVar("sm_round_ratingpro_min_raw", "1.0", "Minimum weighted raw score required before announcing a winner.", FCVAR_NONE, true, 0.0, true, 10.0);
@@ -341,6 +343,8 @@ public void OnTankRockEaten(int tank, int survivor)
341343

342344
public Action Timer_PrintBestRating(Handle timer)
343345
{
346+
SaveLatestTankRaw();
347+
344348
float outputMin;
345349
float outputMax;
346350
float defenseMin;
@@ -375,9 +379,9 @@ public Action Timer_PrintBestRating(Handle timer)
375379
float defense = NormalizeRaw(GetDefenseRaw(client), defenseMin, defenseMax);
376380
float focus = NormalizeRaw(GetFocusRaw(client), focusMin, focusMax);
377381
float infector = NormalizeRaw(GetInfectorRaw(client), infectorMin, infectorMax);
378-
float tank = NormalizeTankRaw(GetTankRaw(client), tankMin, tankMax);
382+
float tank = NormalizeTankRaw(GetEffectiveTankRaw(client), tankMin, tankMax);
379383
float raw = GetWeightedRaw(output, defense, focus, infector, tank);
380-
float rating = raw * RATING_SLOPE + RATING_INTERCEPT;
384+
float rating = Pow(raw / 10.0, RATING_EXPONENT) * 10.0;
381385

382386
if (bestClient == 0 || rating > bestRating)
383387
{
@@ -437,9 +441,9 @@ void PrintAllClientRatingDetails(float outputMin, float outputMax, float defense
437441
float defense = NormalizeRaw(GetDefenseRaw(client), defenseMin, defenseMax);
438442
float focus = NormalizeRaw(GetFocusRaw(client), focusMin, focusMax);
439443
float infector = NormalizeRaw(GetInfectorRaw(client), infectorMin, infectorMax);
440-
float tank = NormalizeTankRaw(GetTankRaw(client), tankMin, tankMax);
444+
float tank = NormalizeTankRaw(GetEffectiveTankRaw(client), tankMin, tankMax);
441445
float raw = GetWeightedRaw(output, defense, focus, infector, tank);
442-
float rating = raw * RATING_SLOPE + RATING_INTERCEPT;
446+
float rating = Pow(raw / 10.0, RATING_EXPONENT) * 10.0;
443447

444448
CPrintToChat(client, "{blue}[{green}RatingPro{blue}]{default} 你的评分 {green}%.1f{default} 综合 {olive}%.1f{default} | 输出 %.1f 防守 %.1f 关键操作 %.1f 特感进攻 %.1f Tank表现 %.1f",
445449
rating, raw, output, defense, focus, infector, tank);
@@ -487,14 +491,14 @@ void PrintAllClientRatingDetailsToConsole(float outputMin, float outputMax, floa
487491
float defenseRaw = GetDefenseRaw(client);
488492
float focusRaw = GetFocusRaw(client);
489493
float infectorRaw = GetInfectorRaw(client);
490-
float tankRaw = GetTankRaw(client);
494+
float tankRaw = GetEffectiveTankRaw(client);
491495
float output = NormalizeRaw(outputRaw, outputMin, outputMax);
492496
float defense = NormalizeRaw(defenseRaw, defenseMin, defenseMax);
493497
float focus = NormalizeRaw(focusRaw, focusMin, focusMax);
494498
float infector = NormalizeRaw(infectorRaw, infectorMin, infectorMax);
495499
float tank = NormalizeTankRaw(tankRaw, tankMin, tankMax);
496500
float raw = GetWeightedRaw(output, defense, focus, infector, tank);
497-
float rating = raw * RATING_SLOPE + RATING_INTERCEPT;
501+
float rating = Pow(raw / 10.0, RATING_EXPONENT) * 10.0;
498502

499503
PrintToServer("[RatingPro] #%d %s team=%d rating=%.1f raw=%.1f normalized(output=%.1f defense=%.1f focus=%.1f infector=%.1f tank=%.1f)",
500504
client, name, GetClientTeam(client), rating, raw, output, defense, focus, infector, tank);
@@ -662,6 +666,57 @@ float GetTankRaw(int client)
662666
return float(g_iTankDmgUpright[client] + g_iTankPunch[client] * 10 + g_iTankRock[client] * 20 + g_iTankHittable[client] * 35 + g_iTankIncap[client] * 45 + g_iTankDeath[client] * 60);
663667
}
664668

669+
float GetEffectiveTankRaw(int client)
670+
{
671+
float currentRaw = GetTankRaw(client);
672+
if (currentRaw > 0.0)
673+
{
674+
return currentRaw;
675+
}
676+
677+
if (!IsHumanClient(client) || g_hLastTankRaw == null)
678+
{
679+
return 0.0;
680+
}
681+
682+
char auth[64];
683+
char rawText[32];
684+
if (!GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth), true)
685+
|| !g_hLastTankRaw.GetString(auth, rawText, sizeof(rawText)))
686+
{
687+
return 0.0;
688+
}
689+
690+
return StringToFloat(rawText);
691+
}
692+
693+
void SaveLatestTankRaw()
694+
{
695+
if (g_hLastTankRaw == null)
696+
{
697+
return;
698+
}
699+
700+
for (int client = 1; client <= MaxClients; client++)
701+
{
702+
float tankRaw = GetTankRaw(client);
703+
if (tankRaw <= 0.0 || !IsHumanClient(client))
704+
{
705+
continue;
706+
}
707+
708+
char auth[64];
709+
if (!GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth), true))
710+
{
711+
continue;
712+
}
713+
714+
char rawText[32];
715+
FloatToString(tankRaw, rawText, sizeof(rawText));
716+
g_hLastTankRaw.SetString(auth, rawText);
717+
}
718+
}
719+
665720
float GetWeightedRaw(float output, float defense, float focus, float infector, float tank)
666721
{
667722
return output * 0.30 + defense * 0.10 + focus * 0.10 + infector * 0.25 + tank * 0.25;
@@ -703,7 +758,7 @@ bool BuildRawRanges(float &outputMin, float &outputMax, float &defenseMin, float
703758
float defense = GetDefenseRaw(client);
704759
float focus = GetFocusRaw(client);
705760
float infector = GetInfectorRaw(client);
706-
float tank = GetTankRaw(client);
761+
float tank = GetEffectiveTankRaw(client);
707762

708763
if (!found)
709764
{
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from pathlib import Path
2+
import unittest
3+
4+
5+
SOURCE = (
6+
Path(__file__).resolve().parents[1]
7+
/ "addons"
8+
/ "sourcemod"
9+
/ "scripting"
10+
/ "l4d2_round_ratingpro.sp"
11+
)
12+
13+
14+
class TankRatingPersistenceContractTests(unittest.TestCase):
15+
def test_final_rating_uses_the_selected_power_curve(self):
16+
source = SOURCE.read_text(encoding="utf-8-sig")
17+
18+
self.assertIn("#define RATING_EXPONENT 0.75", source)
19+
self.assertIn("Pow(raw / 10.0, RATING_EXPONENT) * 10.0", source)
20+
21+
def test_tank_range_uses_persisted_raw_when_round_has_no_tank_event(self):
22+
source = SOURCE.read_text(encoding="utf-8-sig")
23+
24+
self.assertIn("StringMap g_hLastTankRaw", source)
25+
self.assertIn("GetClientAuthId(client, AuthId_Steam2", source)
26+
self.assertIn("GetEffectiveTankRaw", source)
27+
self.assertIn("float tank = GetEffectiveTankRaw(client);", source)
28+
29+
def test_round_end_updates_latest_tank_raw_before_rating_is_recomputed(self):
30+
source = SOURCE.read_text(encoding="utf-8-sig")
31+
32+
self.assertIn("SaveLatestTankRaw();", source)
33+
self.assertIn("SaveLatestTankRaw();\n\n\tfloat outputMin;", source)
34+
35+
36+
if __name__ == "__main__":
37+
unittest.main()

0 commit comments

Comments
 (0)