From: LegendaryGuard Date: Sun, 3 Jul 2022 22:38:36 +0000 (+0200) Subject: Add Mayhem score option functionalities for DM, TDM and Duel X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=9024787155f9041473ad7c0c9c72ecc618148163;p=xonotic%2Fxonotic-data.pk3dir.git Add Mayhem score option functionalities for DM, TDM and Duel --- diff --git a/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc b/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc index e622a1942..7e991d965 100644 --- a/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc +++ b/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc @@ -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; + } +} diff --git a/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc index fc662e2a9..a2abfd3a1 100644 --- a/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc +++ b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc @@ -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; + } +} diff --git a/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc b/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc index 5e8508896..c56c2c2c3 100644 --- a/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc +++ b/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc @@ -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; + } +} diff --git a/qcsrc/common/gamemodes/sv_rules.qc b/qcsrc/common/gamemodes/sv_rules.qc index 84d89909f..a31d13ab2 100644 --- a/qcsrc/common/gamemodes/sv_rules.qc +++ b/qcsrc/common/gamemodes/sv_rules.qc @@ -1,5 +1,6 @@ #include "sv_rules.qh" +#include #include #include @@ -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); +} diff --git a/qcsrc/common/gamemodes/sv_rules.qh b/qcsrc/common/gamemodes/sv_rules.qh index 2efcf1973..49935c931 100644 --- a/qcsrc/common/gamemodes/sv_rules.qh +++ b/qcsrc/common/gamemodes/sv_rules.qh @@ -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 diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index 41c9705da..e9934cf1c 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -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"); diff --git a/xonotic-server.cfg b/xonotic-server.cfg index 859314980..e81d35abb 100644 --- a/xonotic-server.cfg +++ b/xonotic-server.cfg @@ -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"