]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Add Mayhem score option functionalities for DM, TDM and Duel LegendaryGuard/mayhem_score
authorLegendaryGuard <rootuser999@gmail.com>
Sun, 3 Jul 2022 22:38:36 +0000 (00:38 +0200)
committerLegendaryGuard <rootuser999@gmail.com>
Sun, 3 Jul 2022 22:38:36 +0000 (00:38 +0200)
qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc
qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc
qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc
qcsrc/common/gamemodes/sv_rules.qc
qcsrc/common/gamemodes/sv_rules.qh
qcsrc/server/world.qc
xonotic-server.cfg

index e622a1942fbee46b6c792aae8abe47df0cad0c83..7e991d96538656bd9ee792aa63794e24787456d6 100644 (file)
@@ -5,3 +5,40 @@ MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining)
        // announce remaining frags
        return true;
 }
+
+MUTATOR_HOOKFUNCTION(dm, ReadLevelCvars)
+{
+       Mayhem_FragLimit_Override();
+}
+
+MUTATOR_HOOKFUNCTION(dm, PlayerDamage_SplitHealthArmor)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       //float dmg_taken = M_ARGV(4, float);
+       //float dmg_saved = M_ARGV(5, float);
+       float frag_deathtype = M_ARGV(6, float);
+       float frag_damage = M_ARGV(7, float);
+
+       Mayhem_Scoring_Method( 
+               frag_attacker, 
+               frag_target, 
+               M_ARGV(4, float), 
+               M_ARGV(5, float), 
+               frag_deathtype, 
+               frag_damage
+       );
+}
+
+MUTATOR_HOOKFUNCTION(dm, GiveFragsForKill, CBC_ORDER_FIRST)
+{
+       if (autocvar_g_mayhem)
+       {
+               entity frag_attacker = M_ARGV(0, entity);
+               M_ARGV(2, float) = 0; //score to give for the frag directly
+
+               if (IS_PLAYER(frag_attacker)) FFAMayhemCalculatePlayerScore(frag_attacker);
+
+               return true;
+       }
+}
index fc662e2a9fa826648cebfcb875b493ef1a4c40db..a2abfd3a1e2d5bbce58bf941004c0fb3b4c18c63 100644 (file)
@@ -20,3 +20,40 @@ MUTATOR_HOOKFUNCTION(duel, FilterItemDefinition)
                return !autocvar_g_duel_with_powerups;
        }
 }
+
+MUTATOR_HOOKFUNCTION(duel, ReadLevelCvars)
+{
+       Mayhem_FragLimit_Override();
+}
+
+MUTATOR_HOOKFUNCTION(duel, PlayerDamage_SplitHealthArmor)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       //float dmg_taken = M_ARGV(4, float);
+       //float dmg_saved = M_ARGV(5, float);
+       float frag_deathtype = M_ARGV(6, float);
+       float frag_damage = M_ARGV(7, float);
+
+       Mayhem_Scoring_Method( 
+               frag_attacker, 
+               frag_target, 
+               M_ARGV(4, float), 
+               M_ARGV(5, float), 
+               frag_deathtype, 
+               frag_damage
+       );
+}
+
+MUTATOR_HOOKFUNCTION(duel, GiveFragsForKill, CBC_ORDER_FIRST)
+{
+       if (autocvar_g_mayhem)
+       {
+               entity frag_attacker = M_ARGV(0, entity);
+               M_ARGV(2, float) = 0; //score to give for the frag directly
+
+               if (IS_PLAYER(frag_attacker)) FFAMayhemCalculatePlayerScore(frag_attacker);
+
+               return true;
+       }
+}
index 5e850889679fd1ebceb5992b86cbc6f605c044a4..c56c2c2c32b26fd21a828dca926e475eab86fe1f 100644 (file)
@@ -70,3 +70,40 @@ MUTATOR_HOOKFUNCTION(tdm, Scores_CountFragsRemaining)
        // announce remaining frags
        return true;
 }
+
+MUTATOR_HOOKFUNCTION(tdm, ReadLevelCvars)
+{
+       Mayhem_FragLimit_Override();
+}
+
+MUTATOR_HOOKFUNCTION(tdm, PlayerDamage_SplitHealthArmor)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       //float dmg_taken = M_ARGV(4, float);
+       //float dmg_saved = M_ARGV(5, float);
+       float frag_deathtype = M_ARGV(6, float);
+       float frag_damage = M_ARGV(7, float);
+
+       Mayhem_Scoring_Method( 
+               frag_attacker, 
+               frag_target, 
+               M_ARGV(4, float), 
+               M_ARGV(5, float), 
+               frag_deathtype, 
+               frag_damage
+       );
+}
+
+MUTATOR_HOOKFUNCTION(tdm, GiveFragsForKill, CBC_ORDER_FIRST)
+{
+       if (autocvar_g_mayhem)
+       {
+               entity frag_attacker = M_ARGV(0, entity);
+               M_ARGV(2, float) = 0; //score to give for the frag directly
+
+               if (IS_PLAYER(frag_attacker)) FFAMayhemCalculatePlayerScore(frag_attacker);
+
+               return true;
+       }
+}
index 84d89909fe98274f47eb44e51999758f9371dbb6..a31d13ab2c84f1ea3a020bc74e47825fb38bb17f 100644 (file)
@@ -1,5 +1,6 @@
 #include "sv_rules.qh"
 
+#include <common/scores.qh>
 #include <server/spawnpoints.qh>
 #include <server/teamplay.qh>
 
@@ -131,3 +132,176 @@ float _GameRules_scoring_add_team(entity client, entity sp, int st, float value)
 {
     return PlayerTeamScore_Add(client, sp, st, value);
 }
+
+// Mayhem score functionality
+void Mayhem_FragLimit_Override()
+{
+       if (!autocvar_g_mayhem) return;
+
+       if (autocvar_fraglimit_override != 0)
+               GameRules_limit_score(autocvar_g_mayhem_visual_score_limit);
+       else
+               GameRules_limit_score(9999);
+}
+
+void FFAMayhemCalculatePlayerScore(entity scorer)
+{
+       if (!autocvar_g_mayhem) return;
+
+       if (autocvar_fraglimit_override > 0) autocvar_g_mayhem_fraglimit = autocvar_fraglimit_override;
+       
+       switch (autocvar_g_mayhem_scoringmethod)
+       {
+               default:
+               case 1:
+               {
+                       //calculate how much score the player should have based on their damage dealt and frags gotten and then add the missing score
+
+                       //give a different weight for suicides if scoring method 1 doesn't have selfdamage2score enabled to harshly punish for suicides to avoid exploiting
+                       float suicide_weight = 1 + (autocvar_g_mayhem_scoringmethod_1_disable_selfdamage2score * (1/autocvar_g_mayhem_scoringmethod_1_frag_weight));
+
+                       //total damage divided by player start health&armor to get how many lives worth of damage they've dealt, then how much that is out of the fraglimit, then calculate new value affected by weight
+                       float playerdamagescore = (((((scorer.total_damage_dealt/(start_health + start_armorvalue)) * 100)/autocvar_g_mayhem_fraglimit) * autocvar_g_mayhem_scoringmethod_1_damage_weight) );
+                                                                                                                                                                                                       //  * 100 to avoid float inaccuracy at that decimal level
+
+                       //playerdamagescore rounded
+                       float roundedplayerdamagescore = ((rint(playerdamagescore*10))/10);
+
+                       //kills minus suicides, total out of fraglimit, calculate weight
+                       float playerkillscore = ((((PlayerScore_Get(scorer, SP_KILLS) - (PlayerScore_Get(scorer, SP_SUICIDES) * suicide_weight)) * 100) / autocvar_g_mayhem_fraglimit) * autocvar_g_mayhem_scoringmethod_1_frag_weight);
+                                                                                                                                                                                                                                                               //   * 100 to avoid float inaccuracy at that decimal level
+
+                       //only used for debug print, add killscore and damagescore together
+                       float playerscore = (roundedplayerdamagescore + playerkillscore);
+
+                       //add killscore and damagescore together to get total score and then adjust it to be total out of the visual score limit
+                       float playerscorevisual = ((roundedplayerdamagescore + playerkillscore) * autocvar_g_mayhem_visual_score_limit);
+                                                                                                                                                                                                                                               
+
+                       //calculated how much score the player has and now calculate total of how much they are supposed to have
+                       float scoretoadd = (playerscorevisual - (PlayerScore_Get(scorer, SP_SCORE) * 100));
+                                                                                                                                                                  //  * 100 to avoid float inaccuracy at that decimal level
+
+                       //adjust total score to be what the player is supposed to have
+                       GameRules_scoring_add_team(scorer, SCORE, floor(scoretoadd / 100));
+                                                                                                                                       // / 100 to move back to the decimal level
+
+                       if(0){
+                               //debug printing
+                               if(!IS_BOT_CLIENT(scorer)){
+                                       print(sprintf("%f", scorer.total_damage_dealt),                 " scorer.total_damage_dealt \n");
+                                       print(sprintf("%f", playerdamagescore),                                 " playerdamagescore \n");
+                                       print(sprintf("%f", roundedplayerdamagescore),                  " rounded playerdamagescore \n");
+                                       print(sprintf("%f", playerkillscore),                                   " playerkillscore \n");
+                                       print(sprintf("%f", PlayerScore_Get(scorer, SP_KILLS)), " PlayerScore_Get(scorer, SP_KILLS) \n");
+                                       print(sprintf("%f", playerscore),                                               " playerscore \n");
+                                       print(sprintf("%f", playerscorevisual),                                 " visual playerscore \n");
+                                       print(sprintf("%f", scoretoadd),                                                " scoretoadd \n");
+                                       print(sprintf("%f", PlayerScore_Get(scorer, SP_SCORE)), " PlayerScore_Get(scorer, SP_SCORE) \n \n");
+                               }
+                       }
+               return;
+               }
+
+               case 2:
+               {
+                       //calculate how much score the player should have based on their frags gotten and then add the missing score
+                       float playerkillscore = (((PlayerScore_Get(scorer, SP_KILLS) - PlayerScore_Get(scorer, SP_SUICIDES)) * 100)/ autocvar_g_mayhem_fraglimit);
+                       float playerscorevisual = (playerkillscore * autocvar_g_mayhem_visual_score_limit) / 100;
+                       float scoretoadd = (playerscorevisual - PlayerScore_Get(scorer, SP_SCORE));
+                       GameRules_scoring_add_team(scorer, SCORE, floor(scoretoadd));
+
+                       if(0){
+                               //debug printing
+                               if(!IS_BOT_CLIENT(scorer)){
+                                       print(sprintf("%f", playerkillscore),                                   " playerkillscore \n");
+                                       print(sprintf("%f", PlayerScore_Get(scorer, SP_KILLS)), " PlayerScore_Get(scorer, SP_KILLS) \n");
+                                       print(sprintf("%f", playerscorevisual),                                 " visual playerscore \n");
+                                       print(sprintf("%f", scoretoadd),                                                " scoretoadd \n");
+                                       print(sprintf("%f", PlayerScore_Get(scorer, SP_SCORE)), " PlayerScore_Get(scorer, SP_SCORE) \n \n");
+                               }
+                       }
+               return;
+               }
+
+               case 3:
+               {
+                       //calculate how much score the player should have based on their damage dealt and then add the missing score
+                       float playerdamagescore = (((scorer.total_damage_dealt/(start_health + start_armorvalue)) * 100)/autocvar_g_mayhem_fraglimit);
+                       float roundedplayerdamagescore = ((rint(playerdamagescore*10))/10);
+                       float playerscorevisual = (roundedplayerdamagescore * autocvar_g_mayhem_visual_score_limit);
+                       float scoretoadd = (playerscorevisual - (PlayerScore_Get(scorer, SP_SCORE) * 100));
+                       GameRules_scoring_add_team(scorer, SCORE, floor(scoretoadd / 100));
+
+                       if(0){
+                               //debug printing
+                               if(!IS_BOT_CLIENT(scorer)){
+                                       print(sprintf("%f", scorer.total_damage_dealt),                 " scorer.total_damage_dealt \n");
+                                       print(sprintf("%f", playerdamagescore),                                 " playerdamagescore \n");
+                                       print(sprintf("%f", roundedplayerdamagescore),                  " rounded playerdamagescore \n");
+                                       print(sprintf("%f", playerscorevisual),                                 " visual playerscore \n");
+                                       print(sprintf("%f", scoretoadd),                                                " scoretoadd \n");
+                                       print(sprintf("%f", PlayerScore_Get(scorer, SP_SCORE)), " PlayerScore_Get(scorer, SP_SCORE) \n \n");
+                               }
+                       }
+               return;
+               }
+       }
+}
+
+void Mayhem_Scoring_Method(entity frag_attacker, entity frag_target, float dmg_taken, float dmg_saved, float frag_deathtype, float frag_damage)
+{
+       if (!autocvar_g_mayhem) return;
+
+       if (autocvar_g_mayhem_scoringmethod == 2) return;
+
+       if (StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) 
+               && autocvar_g_spawnshield_blockdamage >= 1) 
+               return;
+
+       float damage_take = bound(0, dmg_taken, GetResource(frag_target, RES_HEALTH));
+       float damage_save = bound(0, dmg_saved, GetResource(frag_target, RES_ARMOR));
+       float excess = max(0, frag_damage - damage_take - damage_save);
+       float total = frag_damage - excess;
+
+       if (total == 0) return;
+
+       if (StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) && autocvar_g_spawnshield_blockdamage < 1)
+               total *= 1 - bound(0, autocvar_g_spawnshield_blockdamage, 1);
+
+       entity scorer = NULL; //entity which needs their score to be updated
+
+       if (IS_PLAYER(frag_attacker))
+       {
+               //non-friendly fire
+               if (frag_target != frag_attacker)
+                       frag_attacker.total_damage_dealt += total;
+
+               //friendly fire aka self damage
+               if (frag_target == frag_attacker && !autocvar_g_mayhem_scoringmethod_1_disable_selfdamage2score)
+                       frag_attacker.total_damage_dealt -= total;
+
+               scorer = frag_attacker;
+       }
+       else
+       {
+               //handle (environmental hazard) suiciding, check first if player has a registered attacker who most likely pushed them there to avoid punishing pushed players as pushers are already rewarded
+               //deathtypes:
+               //kill = suicide, drown = drown in water/liquid, hurttrigger = out of the map void or hurt triggers inside maps like electric sparks
+               //camp = campcheck, lava = lava, slime = slime
+               //team change / rebalance suicides are currently not included
+               if (!autocvar_g_mayhem_scoringmethod_1_disable_selfdamage2score && (
+                       frag_deathtype == DEATH_KILL.m_id ||
+                       frag_deathtype == DEATH_DROWN.m_id ||
+                       frag_deathtype == DEATH_HURTTRIGGER.m_id ||
+                       frag_deathtype == DEATH_CAMP.m_id ||
+                       frag_deathtype == DEATH_LAVA.m_id ||
+                       frag_deathtype == DEATH_SLIME.m_id ||
+                       frag_deathtype == DEATH_SWAMP.m_id))
+                               frag_target.total_damage_dealt -= total;
+
+               scorer = frag_target;
+       }
+
+       FFAMayhemCalculatePlayerScore(scorer);
+}
index 2efcf19738aa34ee1b4d2aee1e22a3c74db1274f..49935c9310e2187c2e852d8c2f222f9ad3467ddd 100644 (file)
@@ -4,10 +4,19 @@
 int autocvar_leadlimit_and_fraglimit;
 int autocvar_leadlimit_override;
 
+bool autocvar_g_mayhem;
+float autocvar_g_mayhem_fraglimit;
+float autocvar_g_mayhem_visual_score_limit;
+int autocvar_g_mayhem_scoringmethod;
+float autocvar_g_mayhem_scoringmethod_1_damage_weight;
+float autocvar_g_mayhem_scoringmethod_1_frag_weight;
+bool autocvar_g_mayhem_scoringmethod_1_disable_selfdamage2score;
+
 // TODO: find a better location for these?
 int total_players;
 
 .int ingame;
+.float total_damage_dealt;
 #define INGAME_STATUS_NONE 0
 #define INGAME_STATUS_JOINING 0.5
 #define INGAME_STATUS_JOINED 1
@@ -64,6 +73,10 @@ void _GameRules_scoring_field(entity i, string label, int scoreflags);
 void _GameRules_scoring_field_team(float i, string label, int scoreflags);
 void _GameRules_scoring_end();
 
+void Mayhem_FragLimit_Override();
+void FFAMayhemCalculatePlayerScore(entity scorer);
+void Mayhem_Scoring_Method(entity frag_attacker, entity frag_target, float dmg_taken, float dmg_saved, float frag_deathtype, float frag_damage);
+
 /**
  * Mark a player as being 'important' (flag carrier, ball carrier, etc)
  * @param player the entity to mark
index 41c9705da06f510026176c49d70f787505ad95fa..e9934cf1c671a26f92e2fa7fc442e9635d8342df 100644 (file)
@@ -284,6 +284,7 @@ void cvar_changes_init()
                BADCVAR("g_keyhunt");
                BADCVAR("g_keyhunt_teams");
                BADCVAR("g_lms");
+               BADCVAR("g_mayhem");
                BADCVAR("g_nexball");
                BADCVAR("g_onslaught");
                BADCVAR("g_race");
@@ -364,6 +365,7 @@ void cvar_changes_init()
                BADCVAR("g_keepaway_ballcarrier_effects");
                BADCVAR("g_keepawayball_effects");
                BADCVAR("g_keyhunt_point_leadlimit");
+               BADPREFIX("g_mayhem_");
                BADCVAR("g_nexball_goalleadlimit");
                BADCVAR("g_new_toys_autoreplace");
                BADCVAR("g_new_toys_use_pickupsound");
index 8593149805835749a9dc9848804a7113836a6f5b..e81d35abb3ab23d34294a07dd6a60810c83d13c9 100644 (file)
@@ -444,6 +444,15 @@ set g_ban_sync_trusted_servers_verify 0 "when set to 1, additional bans sent by
 
 set g_showweaponspawns 1 "1: display waypoints for weapon spawns found on the map when a weapon key is pressed and the weapon is not owned; 2: for dropped weapons too; 3: for all the weapons sharing the same impulse"
 
+// Mayhem score
+set g_mayhem 0 "Mayhem score, allows DM, TDM and Duel gamemodes change the way scoring based on damage"
+set g_mayhem_scoringmethod 1 "1: By default 25% of the score is based on kills and 75% of it is based on damage. 2: 100% frags. 3: 100% damage."
+set g_mayhem_scoringmethod_1_damage_weight 0.75 "for the first scoring method how much is damage equal to player's spawning health worth in score"
+set g_mayhem_scoringmethod_1_disable_selfdamage2score 0 "disable reducing score with self damage at the cost of full penalty for suicides regardless of how much health was lost suiciding"
+set g_mayhem_scoringmethod_1_frag_weight 0.25 "for the first scoring method how much is a frag worth in score"
+set g_mayhem_fraglimit 30 "Mayhem basis for how many frags until the match ends, edit this over point_limit preferably"
+set g_mayhem_visual_score_limit 1000 "Mayhem visual score limit overriding the mapinfo specified one"
+
 set g_ballistics_mindistance 2 "when shooting through walls thinner than this, treat them as this thick (useful because patches (curved surfaces) have no thickness)"
 set g_ballistics_density_player 0.50 "how hard players are to shoot through compared to walls"
 set g_ballistics_density_corpse 0.10 "how hard corpses are to shoot through compared to walls"