From: drjaska Date: Tue, 18 Jan 2022 21:41:27 +0000 (+0200) Subject: Merge branch 'master' into drjaska/mayhem X-Git-Url: http://git.xonotic.org/?a=commitdiff_plain;h=9f220235a58a2d1659c90ee508b230f996156f2e;hp=6d997e7ed11d0f4a3449e8dcab751b06bb305743;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into drjaska/mayhem --- diff --git a/balance-mario.cfg b/balance-mario.cfg index 42e3a6696..8b9f37ac1 100644 --- a/balance-mario.cfg +++ b/balance-mario.cfg @@ -49,6 +49,22 @@ set g_lms_start_ammo_rockets 160 set g_lms_start_ammo_cells 180 set g_lms_start_ammo_plasma 180 set g_lms_start_ammo_fuel 0 +set g_mayhem_start_health 200 +set g_mayhem_start_armor 200 +set g_mayhem_start_ammo_shells 60 +set g_mayhem_start_ammo_nails 320 +set g_mayhem_start_ammo_rockets 160 +set g_mayhem_start_ammo_cells 180 +set g_mayhem_start_ammo_plasma 180 +set g_mayhem_start_ammo_fuel 0 +set g_tmayhem_start_health 200 +set g_tmayhem_start_armor 200 +set g_tmayhem_start_ammo_shells 60 +set g_tmayhem_start_ammo_nails 320 +set g_tmayhem_start_ammo_rockets 160 +set g_tmayhem_start_ammo_cells 180 +set g_tmayhem_start_ammo_plasma 180 +set g_tmayhem_start_ammo_fuel 0 set g_balance_nix_roundtime 25 set g_balance_nix_incrtime 1.6 set g_balance_nix_ammo_shells 60 diff --git a/balance-nexuiz25.cfg b/balance-nexuiz25.cfg index 6cdc29dcc..0b39de222 100644 --- a/balance-nexuiz25.cfg +++ b/balance-nexuiz25.cfg @@ -49,6 +49,22 @@ set g_lms_start_ammo_rockets 50 set g_lms_start_ammo_cells 50 set g_lms_start_ammo_plasma 50 set g_lms_start_ammo_fuel 0 +set g_mayhem_start_health 250 +set g_mayhem_start_armor 100 +set g_mayhem_start_ammo_shells 50 +set g_mayhem_start_ammo_nails 150 +set g_mayhem_start_ammo_rockets 50 +set g_mayhem_start_ammo_cells 50 +set g_mayhem_start_ammo_plasma 50 +set g_mayhem_start_ammo_fuel 0 +set g_tmayhem_start_health 250 +set g_tmayhem_start_armor 100 +set g_tmayhem_start_ammo_shells 50 +set g_tmayhem_start_ammo_nails 150 +set g_tmayhem_start_ammo_rockets 50 +set g_tmayhem_start_ammo_cells 50 +set g_tmayhem_start_ammo_plasma 50 +set g_tmayhem_start_ammo_fuel 0 set g_balance_nix_roundtime 25 set g_balance_nix_incrtime 1.6 set g_balance_nix_ammo_shells 15 diff --git a/balance-overkill.cfg b/balance-overkill.cfg index e33ee827f..3944d7d3b 100644 --- a/balance-overkill.cfg +++ b/balance-overkill.cfg @@ -49,6 +49,22 @@ set g_lms_start_ammo_rockets 160 set g_lms_start_ammo_cells 180 set g_lms_start_ammo_plasma 180 set g_lms_start_ammo_fuel 0 +set g_mayhem_start_health 200 +set g_mayhem_start_armor 100 +set g_mayhem_start_ammo_shells 60 +set g_mayhem_start_ammo_nails 320 +set g_mayhem_start_ammo_rockets 160 +set g_mayhem_start_ammo_cells 180 +set g_mayhem_start_ammo_plasma 180 +set g_mayhem_start_ammo_fuel 0 +set g_tmayhem_start_health 200 +set g_tmayhem_start_armor 100 +set g_tmayhem_start_ammo_shells 60 +set g_tmayhem_start_ammo_nails 320 +set g_tmayhem_start_ammo_rockets 160 +set g_tmayhem_start_ammo_cells 180 +set g_tmayhem_start_ammo_plasma 180 +set g_tmayhem_start_ammo_fuel 0 set g_balance_nix_roundtime 25 set g_balance_nix_incrtime 1.6 set g_balance_nix_ammo_shells 60 diff --git a/balance-samual.cfg b/balance-samual.cfg index 7850aaba8..5686c58d6 100644 --- a/balance-samual.cfg +++ b/balance-samual.cfg @@ -49,6 +49,22 @@ set g_lms_start_ammo_rockets 160 set g_lms_start_ammo_cells 180 set g_lms_start_ammo_plasma 180 set g_lms_start_ammo_fuel 0 +set g_mayhem_start_health 200 +set g_mayhem_start_armor 200 +set g_mayhem_start_ammo_shells 60 +set g_mayhem_start_ammo_nails 320 +set g_mayhem_start_ammo_rockets 160 +set g_mayhem_start_ammo_cells 180 +set g_mayhem_start_ammo_plasma 180 +set g_mayhem_start_ammo_fuel 0 +set g_tmayhem_start_health 200 +set g_tmayhem_start_armor 200 +set g_tmayhem_start_ammo_shells 60 +set g_tmayhem_start_ammo_nails 320 +set g_tmayhem_start_ammo_rockets 160 +set g_tmayhem_start_ammo_cells 180 +set g_tmayhem_start_ammo_plasma 180 +set g_tmayhem_start_ammo_fuel 0 set g_balance_nix_roundtime 25 set g_balance_nix_incrtime 1.6 set g_balance_nix_ammo_shells 60 diff --git a/balance-xdf.cfg b/balance-xdf.cfg index 94e98a646..f248b7ac8 100644 --- a/balance-xdf.cfg +++ b/balance-xdf.cfg @@ -49,6 +49,22 @@ set g_lms_start_ammo_rockets 160 set g_lms_start_ammo_cells 180 set g_lms_start_ammo_plasma 180 set g_lms_start_ammo_fuel 0 +set g_mayhem_start_health 200 +set g_mayhem_start_armor 200 +set g_mayhem_start_ammo_shells 60 +set g_mayhem_start_ammo_nails 320 +set g_mayhem_start_ammo_rockets 160 +set g_mayhem_start_ammo_cells 180 +set g_mayhem_start_ammo_plasma 180 +set g_mayhem_start_ammo_fuel 0 +set g_tmayhem_start_health 200 +set g_tmayhem_start_armor 200 +set g_tmayhem_start_ammo_shells 60 +set g_tmayhem_start_ammo_nails 320 +set g_tmayhem_start_ammo_rockets 160 +set g_tmayhem_start_ammo_cells 180 +set g_tmayhem_start_ammo_plasma 180 +set g_tmayhem_start_ammo_fuel 0 set g_balance_nix_roundtime 25 set g_balance_nix_incrtime 1.6 set g_balance_nix_ammo_shells 60 diff --git a/balance-xonotic.cfg b/balance-xonotic.cfg index 784e447f4..2cf8c9eb4 100644 --- a/balance-xonotic.cfg +++ b/balance-xonotic.cfg @@ -49,6 +49,22 @@ set g_lms_start_ammo_rockets 160 set g_lms_start_ammo_cells 180 set g_lms_start_ammo_plasma 180 set g_lms_start_ammo_fuel 0 +set g_mayhem_start_health 200 +set g_mayhem_start_armor 200 +set g_mayhem_start_ammo_shells 60 +set g_mayhem_start_ammo_nails 320 +set g_mayhem_start_ammo_rockets 160 +set g_mayhem_start_ammo_cells 180 +set g_mayhem_start_ammo_plasma 180 +set g_mayhem_start_ammo_fuel 0 +set g_tmayhem_start_health 200 +set g_tmayhem_start_armor 200 +set g_tmayhem_start_ammo_shells 60 +set g_tmayhem_start_ammo_nails 320 +set g_tmayhem_start_ammo_rockets 160 +set g_tmayhem_start_ammo_cells 180 +set g_tmayhem_start_ammo_plasma 180 +set g_tmayhem_start_ammo_fuel 0 set g_balance_nix_roundtime 25 set g_balance_nix_incrtime 1.6 set g_balance_nix_ammo_shells 60 diff --git a/balance-xpm.cfg b/balance-xpm.cfg index 189be9c71..87b781a55 100644 --- a/balance-xpm.cfg +++ b/balance-xpm.cfg @@ -49,6 +49,22 @@ set g_lms_start_ammo_rockets 160 set g_lms_start_ammo_cells 180 set g_lms_start_ammo_plasma 180 set g_lms_start_ammo_fuel 0 +set g_mayhem_start_health 200 +set g_mayhem_start_armor 200 +set g_mayhem_start_ammo_shells 60 +set g_mayhem_start_ammo_nails 320 +set g_mayhem_start_ammo_rockets 160 +set g_mayhem_start_ammo_cells 180 +set g_mayhem_start_ammo_plasma 180 +set g_mayhem_start_ammo_fuel 0 +set g_tmayhem_start_health 200 +set g_tmayhem_start_armor 200 +set g_tmayhem_start_ammo_shells 60 +set g_tmayhem_start_ammo_nails 320 +set g_tmayhem_start_ammo_rockets 160 +set g_tmayhem_start_ammo_cells 180 +set g_tmayhem_start_ammo_plasma 180 +set g_tmayhem_start_ammo_fuel 0 set g_balance_nix_roundtime 25 set g_balance_nix_incrtime 1.6 set g_balance_nix_ammo_shells 60 diff --git a/gamemodes-client.cfg b/gamemodes-client.cfg index c43b9d1d3..c3dd70912 100644 --- a/gamemodes-client.cfg +++ b/gamemodes-client.cfg @@ -32,6 +32,8 @@ alias cl_hook_gamestart_ka alias cl_hook_gamestart_ft alias cl_hook_gamestart_inv alias cl_hook_gamestart_duel +alias cl_hook_gamestart_mayhem +alias cl_hook_gamestart_tmayhem alias cl_hook_gameend "rpn /cl_matchcount dup load 1 + =" // increase match count every time a game ends alias cl_hook_shutdown alias cl_hook_activeweapon diff --git a/gamemodes-server.cfg b/gamemodes-server.cfg index 62570e523..d86eb141d 100644 --- a/gamemodes-server.cfg +++ b/gamemodes-server.cfg @@ -29,6 +29,8 @@ alias sv_hook_gamestart_ka alias sv_hook_gamestart_ft alias sv_hook_gamestart_inv alias sv_hook_gamestart_duel +alias sv_hook_gamestart_mayhem +alias sv_hook_gamestart_tmayhem // there is currently no hook for when the match is restarted // see sv_hook_readyrestart for previous uses of this hook //alias sv_hook_gamerestart @@ -58,6 +60,8 @@ alias sv_vote_gametype_hook_ons alias sv_vote_gametype_hook_rc alias sv_vote_gametype_hook_tdm alias sv_vote_gametype_hook_duel +alias sv_vote_gametype_hook_mayhem +alias sv_vote_gametype_hook_tmayhem // Example preset to allow 1v1ctf to be used for the gametype voting screen. // Aliases can have max 31 chars so the gametype can have max 9 chars. @@ -208,6 +212,20 @@ set g_duel_respawn_delay_large_count 0 set g_duel_respawn_delay_max 0 set g_duel_respawn_waves 0 set g_duel_weapon_stay 0 +set g_mayhem_respawn_delay_small 0 +set g_mayhem_respawn_delay_small_count 0 +set g_mayhem_respawn_delay_large 0 +set g_mayhem_respawn_delay_large_count 0 +set g_mayhem_respawn_delay_max 0 +set g_mayhem_respawn_waves 0 +set g_mayhem_weapon_stay 0 +set g_tmayhem_respawn_delay_small 0 +set g_tmayhem_respawn_delay_small_count 0 +set g_tmayhem_respawn_delay_large 0 +set g_tmayhem_respawn_delay_large_count 0 +set g_tmayhem_respawn_delay_max 0 +set g_tmayhem_respawn_waves 0 +set g_tmayhem_weapon_stay 0 // ========= @@ -560,3 +578,46 @@ set g_duel 0 "Duel: frag the opponent more in a one versus one arena battle" //set g_duel_warmup 180 "Have a short warmup period before beginning the actual duel" set g_duel_with_powerups 0 "Enable powerups to spawn in the duel gamemode" set g_duel_not_dm_maps 0 "when this is set, DM maps will NOT be listed in duel" + +// ============================== +// free for all and team mayhem +// ============================== +set g_mayhem 0 "Mayhem: Compete for the most damage dealt and frags in this chaotic mayhem!" +set g_tmayhem 0 "Team Mayhem: Compete with your team for the most damage dealt and frags in this chaotic mayhem!" + +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_tmayhem_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_tmayhem_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_tmayhem_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_tmayhem_scoringmethod_1_frag_weight 0.25 "for the first scoring method how much is a frag worth in score" + +set g_mayhem_fraglimit 30 "Team Mayhem basis for how many frags until the match ends, edit this over point_limit preferably" +set g_tmayhem_fraglimit 50 "Team 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_tmayhem_visual_score_limit 1000 "Team Mayhem visual score limit overriding the mapinfo specified one" + +set g_tmayhem_score_leadlimit -1 "Team Mayhem score lead limit(based on tmayhem_visual_score_limit, not tmayhem_fraglimit) overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" + +set g_mayhem_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena" +set g_tmayhem_weaponarena "most_available" "starting weapons - takes the same options as g_weaponarena" + +set g_mayhem_powerups 1 "Allow powerups in mayhem. Only checked if g_powerups is -1 therefore this will be overridden by g_powerups 1 or 0" +set g_tmayhem_powerups 1 "Allow powerups in team mayhem. Only checked if g_powerups is -1 therefore this will be overridden by g_powerups 1 or 0" +set g_mayhem_pickup_items 0 "spawn pickup items in mayhem" +set g_tmayhem_pickup_items 0 "spawn pickup items in team mayhem" +set g_mayhem_pickup_items_remove_weapons_and_ammo 1 "when pickup items are enabled in mayhem still remove weapons and ammo pickups" +set g_tmayhem_pickup_items_remove_weapons_and_ammo 1 "when pickup items are enabled in team mayhem still remove weapons and ammo pickups" + +set g_mayhem_selfdamage 0 "0 = disable selfdamage in mayhem, 1 = enable selfdamage in mayhem" +set g_tmayhem_selfdamage 0 "0 = disable selfdamage in tmayhem, 1 = enable selfdamage in tmayhem" + +set g_mayhem_regenerate 0 "allow players to regenerate hp. rates controlled by hp regeneration and rotting cvars" +set g_tmayhem_regenerate 0 "allow players to regenerate hp. rates controlled by hp regeneration and rotting cvars" + +set g_tmayhem_teams 2 "how many teams are in team mayhem (set by mapinfo)" +set g_tmayhem_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any" +set g_tmayhem_teams_override 0 "how many teams are in team mayhem" diff --git a/gfx/menu/luma/gametype_mayhem.tga b/gfx/menu/luma/gametype_mayhem.tga new file mode 100644 index 000000000..bcd8bb5b4 Binary files /dev/null and b/gfx/menu/luma/gametype_mayhem.tga differ diff --git a/gfx/menu/luma/gametype_tmayhem.tga b/gfx/menu/luma/gametype_tmayhem.tga new file mode 100644 index 000000000..1b4f70d43 Binary files /dev/null and b/gfx/menu/luma/gametype_tmayhem.tga differ diff --git a/gfx/menu/luminos/gametype_mayhem.tga b/gfx/menu/luminos/gametype_mayhem.tga new file mode 100644 index 000000000..cd7e57dfa Binary files /dev/null and b/gfx/menu/luminos/gametype_mayhem.tga differ diff --git a/gfx/menu/luminos/gametype_tmayhem.tga b/gfx/menu/luminos/gametype_tmayhem.tga new file mode 100644 index 000000000..8def1e5d0 Binary files /dev/null and b/gfx/menu/luminos/gametype_tmayhem.tga differ diff --git a/gfx/menu/wickedx/gametype_mayhem.tga b/gfx/menu/wickedx/gametype_mayhem.tga new file mode 100644 index 000000000..cd7e57dfa Binary files /dev/null and b/gfx/menu/wickedx/gametype_mayhem.tga differ diff --git a/gfx/menu/wickedx/gametype_tmayhem.tga b/gfx/menu/wickedx/gametype_tmayhem.tga new file mode 100644 index 000000000..8def1e5d0 Binary files /dev/null and b/gfx/menu/wickedx/gametype_tmayhem.tga differ diff --git a/gfx/menu/xaw/gametype_mayhem.tga b/gfx/menu/xaw/gametype_mayhem.tga new file mode 100644 index 000000000..b392973ff Binary files /dev/null and b/gfx/menu/xaw/gametype_mayhem.tga differ diff --git a/gfx/menu/xaw/gametype_tmayhem.tga b/gfx/menu/xaw/gametype_tmayhem.tga new file mode 100644 index 000000000..08e8a97aa Binary files /dev/null and b/gfx/menu/xaw/gametype_tmayhem.tga differ diff --git a/qcsrc/common/gamemodes/gamemode/_mod.inc b/qcsrc/common/gamemodes/gamemode/_mod.inc index a33ec87a0..b106ec9ac 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/_mod.inc @@ -12,7 +12,9 @@ #include #include #include +#include #include #include #include #include +#include \ No newline at end of file diff --git a/qcsrc/common/gamemodes/gamemode/_mod.qh b/qcsrc/common/gamemodes/gamemode/_mod.qh index ffd71d59d..15b6ecaac 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/_mod.qh @@ -12,7 +12,9 @@ #include #include #include +#include #include #include #include #include +#include \ No newline at end of file diff --git a/qcsrc/common/gamemodes/gamemode/mayhem/_mod.inc b/qcsrc/common/gamemodes/gamemode/mayhem/_mod.inc new file mode 100644 index 000000000..37daedbee --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mayhem/_mod.inc @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/mayhem/_mod.qh b/qcsrc/common/gamemodes/gamemode/mayhem/_mod.qh new file mode 100644 index 000000000..abe19802e --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mayhem/_mod.qh @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/mayhem/mayhem.qc b/qcsrc/common/gamemodes/gamemode/mayhem/mayhem.qc new file mode 100644 index 000000000..6af742888 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mayhem/mayhem.qc @@ -0,0 +1 @@ +#include "mayhem.qh" diff --git a/qcsrc/common/gamemodes/gamemode/mayhem/mayhem.qh b/qcsrc/common/gamemodes/gamemode/mayhem/mayhem.qh new file mode 100644 index 000000000..e853d3553 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mayhem/mayhem.qh @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +CLASS(mayhem, Gametype) + INIT(mayhem) + { + this.gametype_init(this, _("Mayhem"),"mayhem","g_mayhem",GAMETYPE_FLAG_USEPOINTS | GAMETYPE_FLAG_PREFERRED,"","timelimit=15 pointlimit=30 leadlimit=0",_("Compete for the most damage dealt and frags in this chaotic mayhem!")); + } + METHOD(mayhem, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return true; + } + METHOD(mayhem, m_isForcedSupported, bool(Gametype this)) + { + if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags)){ + return true; + } + if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH.m_flags)){ + return true; + } + return false; + } + METHOD(mayhem, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "g_mayhem_fraglimit", string_null, _("How many frags worth of score is needed before the match will end")); + } + ATTRIB(mayhem, m_legacydefaults, string, "30 20 0"); +ENDCLASS(mayhem) +REGISTER_GAMETYPE(MAYHEM, NEW(mayhem)); diff --git a/qcsrc/common/gamemodes/gamemode/mayhem/sv_mayhem.qc b/qcsrc/common/gamemodes/gamemode/mayhem/sv_mayhem.qc new file mode 100644 index 000000000..7a8ae68e3 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mayhem/sv_mayhem.qc @@ -0,0 +1,299 @@ +#include "sv_mayhem.qh" +#include + +float autocvar_g_mayhem_fraglimit; +float autocvar_g_mayhem_visual_score_limit; +float mayhempointmultiplier; + +bool autocvar_g_mayhem_regenerate; +string autocvar_g_mayhem_weaponarena; +bool autocvar_g_mayhem_powerups; +bool autocvar_g_mayhem_selfdamage; +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; +bool autocvar_g_mayhem_pickup_items; +bool autocvar_g_mayhem_pickup_items_remove_weapons_and_ammo; +bool autocvar_g_mayhem_unlimited_ammo; + +float autocvar_g_mayhem_start_health = 200; +float autocvar_g_mayhem_start_armor = 200; +float autocvar_g_mayhem_start_ammo_shells = 60; +float autocvar_g_mayhem_start_ammo_nails = 320; +float autocvar_g_mayhem_start_ammo_rockets = 160; +float autocvar_g_mayhem_start_ammo_cells = 180; +float autocvar_g_mayhem_start_ammo_plasma = 180; +float autocvar_g_mayhem_start_ammo_fuel = 0; + +.float total_damage_dealt; + +void mayhem_DelayedInit(entity this) +{ + return; +} + +void mayhem_Initialize() +{ + if(autocvar_g_mayhem_visual_score_limit > 0 && autocvar_g_mayhem_fraglimit > 0) + mayhempointmultiplier = autocvar_g_mayhem_visual_score_limit / autocvar_g_mayhem_fraglimit; + + GameRules_limit_score(autocvar_g_mayhem_visual_score_limit); + + InitializeEntity(NULL, mayhem_DelayedInit, INITPRIO_GAMETYPE); +} + +MUTATOR_HOOKFUNCTION(mayhem, Scores_CountFragsRemaining) +{ + // announce remaining frags + return true; +} + +MUTATOR_HOOKFUNCTION(mayhem, SetStartItems) +{ + start_items &= ~(IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS); + if(!cvar("g_use_ammunition") || autocvar_g_mayhem_unlimited_ammo) + start_items |= IT_UNLIMITED_AMMO; + + start_health = warmup_start_health = autocvar_g_mayhem_start_health; + start_armorvalue = warmup_start_armorvalue = autocvar_g_mayhem_start_armor; + start_ammo_shells = warmup_start_ammo_shells = autocvar_g_mayhem_start_ammo_shells; + start_ammo_nails = warmup_start_ammo_nails = autocvar_g_mayhem_start_ammo_nails; + start_ammo_rockets = warmup_start_ammo_rockets = autocvar_g_mayhem_start_ammo_rockets; + start_ammo_cells = warmup_start_ammo_cells = autocvar_g_mayhem_start_ammo_cells; + start_ammo_plasma = warmup_start_ammo_plasma = autocvar_g_mayhem_start_ammo_plasma; + start_ammo_fuel = warmup_start_ammo_fuel = autocvar_g_mayhem_start_ammo_fuel; +} + +//this hook also enables rotting, as players spawn with more hp and armor than what default rot limits are set to this is a bad idea as of now +MUTATOR_HOOKFUNCTION(mayhem, PlayerRegen) +{ + if(autocvar_g_mayhem_regenerate) + return false; + return true; +} + +MUTATOR_HOOKFUNCTION(mayhem, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(mayhem, SetWeaponArena) +{ + if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") + M_ARGV(0, string) = autocvar_g_mayhem_weaponarena; +} + +MUTATOR_HOOKFUNCTION(mayhem, FilterItem) +{ + entity item = M_ARGV(0, entity); + + //enable powerups if forced globally or global accepts gamemodes to have powerups according to their own settings + if (autocvar_g_powerups == 1 || (autocvar_g_powerups == -1 && autocvar_g_mayhem_powerups == 1)){ + if (item.itemdef.instanceOfPowerup){ + return false; + } + } + //disabled powerups if forced off globally or in this gamemode + if (autocvar_g_powerups == 0 || autocvar_g_mayhem_powerups == 0){ + if (item.itemdef.instanceOfPowerup){ + return true; + } + } + //remove all items if items are forced off globally + if (autocvar_g_pickup_items == 0){ + return true; + } + //if items are switched on in this gamemode allow the removal of weapons and ammo still + if ((autocvar_g_mayhem_pickup_items == 1 && autocvar_g_mayhem_pickup_items_remove_weapons_and_ammo == 1) && autocvar_g_pickup_items <= 0){ + if (item.itemdef.instanceOfAmmo || item.itemdef.instanceOfWeaponPickup){ + return true; + } + } + //remove items if not globally set to follow mode's settings and locally set off + if (autocvar_g_pickup_items == -1 && autocvar_g_mayhem_pickup_items == 0){ + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(mayhem, Damage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + + if ((autocvar_g_mayhem_selfdamage == 0 && frag_target == frag_attacker) || frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; + + M_ARGV(4, float) = frag_damage; +} + +void FFAMayhemCalculatePlayerScore(entity scorer){ + 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; + } + } +} + +MUTATOR_HOOKFUNCTION(mayhem, PlayerDamage_SplitHealthArmor) +{ + if (autocvar_g_mayhem_scoringmethod==2) return; + + entity frag_target = M_ARGV(2, entity); + + if (StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) && autocvar_g_spawnshield_blockdamage >= 1)return; + + entity frag_attacker = M_ARGV(1, entity); + float frag_deathtype = M_ARGV(6, float); + float frag_damage = M_ARGV(7, float); + float damage_take = bound(0, M_ARGV(4, float), GetResource(frag_target, RES_HEALTH)); + float damage_save = bound(0, M_ARGV(5, float), 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); +} + +MUTATOR_HOOKFUNCTION(mayhem, GiveFragsForKill, CBC_ORDER_FIRST) +{ + 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/mayhem/sv_mayhem.qh b/qcsrc/common/gamemodes/gamemode/mayhem/sv_mayhem.qh new file mode 100644 index 000000000..69b2878df --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mayhem/sv_mayhem.qh @@ -0,0 +1,15 @@ +#pragma once + +#include + +void mayhem_Initialize(); + +REGISTER_MUTATOR(mayhem, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + mayhem_Initialize(); + } + return 0; +} diff --git a/qcsrc/common/gamemodes/gamemode/tmayhem/_mod.inc b/qcsrc/common/gamemodes/gamemode/tmayhem/_mod.inc new file mode 100644 index 000000000..e78eb59b1 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tmayhem/_mod.inc @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/tmayhem/_mod.qh b/qcsrc/common/gamemodes/gamemode/tmayhem/_mod.qh new file mode 100644 index 000000000..261392346 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tmayhem/_mod.qh @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/tmayhem/sv_tmayhem.qc b/qcsrc/common/gamemodes/gamemode/tmayhem/sv_tmayhem.qc new file mode 100644 index 000000000..4c9e7f85d --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tmayhem/sv_tmayhem.qc @@ -0,0 +1,347 @@ +#include "sv_tmayhem.qh" + +float autocvar_g_tmayhem_fraglimit; +float autocvar_g_tmayhem_visual_score_limit; +float autocvar_g_tmayhem_score_leadlimit; +bool autocvar_g_tmayhem_team_spawns; +float tmayhempointmultiplier; + +// TODO? rename to teammayhem? requires checking alias and other string lengths +int autocvar_g_tmayhem_teams; +int autocvar_g_tmayhem_teams_override; + +bool autocvar_g_tmayhem_regenerate; +string autocvar_g_tmayhem_weaponarena; +bool autocvar_g_tmayhem_powerups; +bool autocvar_g_tmayhem_selfdamage; +int autocvar_g_tmayhem_scoringmethod; +float autocvar_g_tmayhem_scoringmethod_1_damage_weight; +float autocvar_g_tmayhem_scoringmethod_1_frag_weight; +bool autocvar_g_tmayhem_scoringmethod_1_disable_selfdamage2score; +bool autocvar_g_tmayhem_pickup_items; +bool autocvar_g_tmayhem_pickup_items_remove_weapons_and_ammo; +bool autocvar_g_tmayhem_unlimited_ammo; + +float autocvar_g_tmayhem_start_health = 200; +float autocvar_g_tmayhem_start_armor = 200; +float autocvar_g_tmayhem_start_ammo_shells = 60; +float autocvar_g_tmayhem_start_ammo_nails = 320; +float autocvar_g_tmayhem_start_ammo_rockets = 160; +float autocvar_g_tmayhem_start_ammo_cells = 180; +float autocvar_g_tmayhem_start_ammo_plasma = 180; +float autocvar_g_tmayhem_start_ammo_fuel = 0; + +.float total_damage_dealt; + +// code from here on is just to support maps that don't have team entities +void tmayhem_SpawnTeam (string teamname, int teamcolor) +{ + entity this = new_pure(tmayhem_team); + this.netname = teamname; + this.cnt = teamcolor - 1; + this.team = teamcolor; + this.spawnfunc_checked = true; + //spawnfunc_tmayhem_team(this); +} + +void tmayhem_DelayedInit(entity this) +{ + // if no teams are found, spawn defaults + if(find(NULL, classname, "tmayhem_team") == NULL) + { + LOG_TRACE("No \"tmayhem_team\" entities found on this map, creating them anyway."); + + int numteams = autocvar_g_tmayhem_teams_override; + if(numteams < 2) { numteams = autocvar_g_tmayhem_teams; } + + int teams = BITS(bound(2, numteams, 4)); + if(teams & BIT(0)) + tmayhem_SpawnTeam("Red", NUM_TEAM_1); + if(teams & BIT(1)) + tmayhem_SpawnTeam("Blue", NUM_TEAM_2); + if(teams & BIT(2)) + tmayhem_SpawnTeam("Yellow", NUM_TEAM_3); + if(teams & BIT(3)) + tmayhem_SpawnTeam("Pink", NUM_TEAM_4); + } +} + +void tmayhem_Initialize() +{ + if(autocvar_g_tmayhem_visual_score_limit != 0 && autocvar_g_tmayhem_fraglimit != 0) + tmayhempointmultiplier = autocvar_g_tmayhem_visual_score_limit / autocvar_g_tmayhem_fraglimit; + + GameRules_teams(true); + GameRules_spawning_teams(autocvar_g_tmayhem_team_spawns); + GameRules_limit_score(autocvar_g_tmayhem_visual_score_limit); + GameRules_limit_lead(autocvar_g_tmayhem_score_leadlimit); + + InitializeEntity(NULL, tmayhem_DelayedInit, INITPRIO_GAMETYPE); +} +// code up to here is just to support maps that don't have team entities + +MUTATOR_HOOKFUNCTION(tmayhem, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) +{ + M_ARGV(1, string) = "tmayhem_team"; +} + +MUTATOR_HOOKFUNCTION(tmayhem, Scores_CountFragsRemaining) +{ + // announce remaining frags + return true; +} + +MUTATOR_HOOKFUNCTION(tmayhem, SetStartItems) +{ + start_items &= ~(IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS); + if(!cvar("g_use_ammunition") || autocvar_g_tmayhem_unlimited_ammo) + start_items |= IT_UNLIMITED_AMMO; + + start_health = warmup_start_health = autocvar_g_tmayhem_start_health; + start_armorvalue = warmup_start_armorvalue = autocvar_g_tmayhem_start_armor; + start_ammo_shells = warmup_start_ammo_shells = autocvar_g_tmayhem_start_ammo_shells; + start_ammo_nails = warmup_start_ammo_nails = autocvar_g_tmayhem_start_ammo_nails; + start_ammo_rockets = warmup_start_ammo_rockets = autocvar_g_tmayhem_start_ammo_rockets; + start_ammo_cells = warmup_start_ammo_cells = autocvar_g_tmayhem_start_ammo_cells; + start_ammo_plasma = warmup_start_ammo_plasma = autocvar_g_tmayhem_start_ammo_plasma; + start_ammo_fuel = warmup_start_ammo_fuel = autocvar_g_tmayhem_start_ammo_fuel; +} + +//this hook also enables rotting, as players spawn with more hp and armor than what default rot limits are set to this is a bad idea as of now until PlayerRegen is changed +MUTATOR_HOOKFUNCTION(tmayhem, PlayerRegen) +{ + if(autocvar_g_tmayhem_regenerate) + return false; + return true; +} + +MUTATOR_HOOKFUNCTION(tmayhem, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(tmayhem, SetWeaponArena) +{ + if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") + M_ARGV(0, string) = autocvar_g_tmayhem_weaponarena; +} + +MUTATOR_HOOKFUNCTION(tmayhem, FilterItem) +{ + entity item = M_ARGV(0, entity); + + //enable powerups if forced globally or global accepts gamemodes to have powerups according to their own settings + if (autocvar_g_powerups == 1 || (autocvar_g_powerups == -1 && autocvar_g_tmayhem_powerups == 1)){ + if (item.itemdef.instanceOfPowerup){ + return false; + } + } + //disabled powerups if forced off globally or in this gamemode + if (autocvar_g_powerups == 0 || autocvar_g_tmayhem_powerups == 0){ + if (item.itemdef.instanceOfPowerup){ + return true; + } + } + //remove all items if items are forced off globally + if (autocvar_g_pickup_items == 0){ + return true; + } + //if items are switched on in this gamemode allow the removal of weapons and ammo still + if ((autocvar_g_tmayhem_pickup_items == 1 && autocvar_g_tmayhem_pickup_items_remove_weapons_and_ammo == 1) && autocvar_g_pickup_items <= 0){ + if (item.itemdef.instanceOfAmmo || item.itemdef.instanceOfWeaponPickup){ + return true; + } + } + //remove items if not globally set to follow mode's settings and locally set off + if (autocvar_g_pickup_items == -1 && autocvar_g_tmayhem_pickup_items == 0){ + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(tmayhem, Damage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + float frag_mirrordamage = M_ARGV(5, float); + + if (IS_PLAYER(frag_target)) //don't ever zero damage to non-players + if (!IS_DEAD(frag_target)) //enable anyone to gib corpses + if ((autocvar_g_tmayhem_selfdamage == 0 && frag_target == frag_attacker) || frag_deathtype == DEATH_FALL.m_id) //nullify self-damage if self-damage is disabled and always nullify splat + frag_damage = 0; + + frag_mirrordamage = 0; //no mirror damaging + + M_ARGV(4, float) = frag_damage; + M_ARGV(5, float) = frag_mirrordamage; +} + +void TeamMayhemCalculatePlayerScore(entity scorer){ + switch (autocvar_g_tmayhem_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_tmayhem_scoringmethod_1_disable_selfdamage2score * (1/autocvar_g_tmayhem_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_tmayhem_fraglimit) * autocvar_g_tmayhem_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_tmayhem_fraglimit) * autocvar_g_tmayhem_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_tmayhem_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_tmayhem_fraglimit); + float playerscorevisual = (playerkillscore * autocvar_g_tmayhem_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_tmayhem_fraglimit); + float roundedplayerdamagescore = ((rint(playerdamagescore*10))/10); + float playerscorevisual = (roundedplayerdamagescore * autocvar_g_tmayhem_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; + } + } +} + +MUTATOR_HOOKFUNCTION(tmayhem, PlayerDamage_SplitHealthArmor) +{ + if (autocvar_g_tmayhem_scoringmethod==2) return; + + entity frag_target = M_ARGV(2, entity); + + if (StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) && autocvar_g_spawnshield_blockdamage >= 1)return; + + entity frag_attacker = M_ARGV(1, entity); + float frag_deathtype = M_ARGV(6, float); + float frag_damage = M_ARGV(7, float); + float damage_take = bound(0, M_ARGV(4, float), GetResource(frag_target, RES_HEALTH)); + float damage_save = bound(0, M_ARGV(5, float), 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 (!SAME_TEAM(frag_target, frag_attacker)) + frag_attacker.total_damage_dealt += total; + + //friendly fire aka self damage + if (SAME_TEAM(frag_target, frag_attacker) || (frag_target == frag_attacker && !autocvar_g_tmayhem_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_tmayhem_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; + } + + TeamMayhemCalculatePlayerScore(scorer); +} + +MUTATOR_HOOKFUNCTION(tmayhem, GiveFragsForKill, CBC_ORDER_FIRST) +{ + entity frag_attacker = M_ARGV(0, entity); + M_ARGV(2, float) = 0; //score to give for the frag directly + + if (IS_PLAYER(frag_attacker)) TeamMayhemCalculatePlayerScore(frag_attacker); + + return true; +} diff --git a/qcsrc/common/gamemodes/gamemode/tmayhem/sv_tmayhem.qh b/qcsrc/common/gamemodes/gamemode/tmayhem/sv_tmayhem.qh new file mode 100644 index 000000000..c31967fad --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tmayhem/sv_tmayhem.qh @@ -0,0 +1,15 @@ +#pragma once + +#include + +void tmayhem_Initialize(); + +REGISTER_MUTATOR(tmayhem, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + tmayhem_Initialize(); + } + return 0; +} diff --git a/qcsrc/common/gamemodes/gamemode/tmayhem/tmayhem.qc b/qcsrc/common/gamemodes/gamemode/tmayhem/tmayhem.qc new file mode 100644 index 000000000..b05860a8c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tmayhem/tmayhem.qc @@ -0,0 +1 @@ +#include "tmayhem.qh" diff --git a/qcsrc/common/gamemodes/gamemode/tmayhem/tmayhem.qh b/qcsrc/common/gamemodes/gamemode/tmayhem/tmayhem.qh new file mode 100644 index 000000000..0e18d05d2 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tmayhem/tmayhem.qh @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +CLASS(tmayhem, Gametype) + INIT(tmayhem) + { + this.gametype_init(this, _("Team Mayhem"),"tmayhem","g_tmayhem",GAMETYPE_FLAG_TEAMPLAY | GAMETYPE_FLAG_USEPOINTS | GAMETYPE_FLAG_PRIORITY,"","timelimit=15 pointlimit=50 teams=2 leadlimit=0",_("Compete with your team for the most damage dealt and frags in this chaotic mayhem!")); + } + METHOD(tmayhem, m_parse_mapinfo, bool(string k, string v)) + { + if (!k) { + cvar_set("g_tmayhem_teams", cvar_defstring("g_tmayhem_teams")); + return true; + } + switch (k) { + case "teams": + cvar_set("g_tmayhem_teams", v); + return true; + } + return false; + } + METHOD(tmayhem, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return true; + } + METHOD(tmayhem, m_isForcedSupported, bool(Gametype this)) + { + if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags)){ + return true; + } + if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH.m_flags)){ + return true; + } + return false; + } + METHOD(tmayhem, m_setTeams, void(string sa)) + { + cvar_set("g_tmayhem_teams", sa); + } + METHOD(tmayhem, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "g_tmayhem_fraglimit", "g_tmayhem_teams_override", _("How many frags worth of score is needed before the match will end")); + } + ATTRIB(tmayhem, m_legacydefaults, string, "50 20 2 0"); +ENDCLASS(tmayhem) +REGISTER_GAMETYPE(TEAM_MAYHEM, NEW(tmayhem)); +#define g_tmayhem IS_GAMETYPE(TEAM_MAYHEM) diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index e77049d20..90bc3a1bd 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -674,6 +674,8 @@ float updateCompression() GAMETYPE(MAPINFO_TYPE_CTF) \ GAMETYPE(MAPINFO_TYPE_CA) \ GAMETYPE(MAPINFO_TYPE_FREEZETAG) \ + GAMETYPE(MAPINFO_TYPE_TEAM_MAYHEM) \ + GAMETYPE(MAPINFO_TYPE_MAYHEM) \ GAMETYPE(MAPINFO_TYPE_KEEPAWAY) \ GAMETYPE(MAPINFO_TYPE_KEYHUNT) \ GAMETYPE(MAPINFO_TYPE_LMS) \ diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index e2a44d2c8..80583f157 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -287,6 +287,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"); @@ -301,6 +302,8 @@ void cvar_changes_init() BADCVAR("g_tdm"); BADCVAR("g_tdm_on_dm_maps"); BADCVAR("g_tdm_teams"); + BADCVAR("g_tmayhem"); + BADCVAR("g_tmayhem_teams"); BADCVAR("g_vip"); BADCVAR("leadlimit"); BADCVAR("nextmap"); @@ -377,8 +380,19 @@ void cvar_changes_init() BADCVAR("g_spawn_alloweffects"); BADCVAR("g_tdm_point_leadlimit"); BADCVAR("g_tdm_point_limit"); + BADCVAR("g_mayhem_fraglimit"); + BADCVAR("g_tmayhem_fraglimit"); + BADCVAR("g_mayhem_visual_score_limit"); + BADCVAR("g_tmayhem_visual_score_limit"); + BADCVAR("g_tmayhem_score_leadlimit"); BADCVAR("leadlimit_and_fraglimit"); BADCVAR("leadlimit_override"); + BADCVAR("g_mayhem_scoringmethod"); + BADCVAR("g_mayhem_scoringmethod_damage_weight"); + BADCVAR("g_mayhem_scoringmethod_frag_weight"); + BADCVAR("g_tmayhem_scoringmethod"); + BADCVAR("g_tmayhem_scoringmethod_damage_weight"); + BADCVAR("g_tmayhem_scoringmethod_frag_weight"); BADCVAR("pausable"); BADCVAR("sv_announcer"); BADCVAR("sv_checkforpacketsduringsleep"); @@ -453,6 +467,7 @@ void cvar_changes_init() BADCVAR("g_keyhunt_point_limit"); BADCVAR("g_keyhunt_teams_override"); BADCVAR("g_lms_lives_override"); + BADCVAR("g_mayhem_powerups"); BADCVAR("g_maplist"); BADCVAR("g_maxplayers"); BADCVAR("g_mirrordamage"); @@ -469,6 +484,8 @@ void cvar_changes_init() BADCVAR("g_start_delay"); BADCVAR("g_superspectate"); BADCVAR("g_tdm_teams_override"); + BADCVAR("g_tmayhem_teams_override"); + BADCVAR("g_tmayhem_powerups"); BADCVAR("g_warmup"); BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay"); BADCVAR("hostname"); @@ -524,6 +541,8 @@ void cvar_changes_init() BADCVAR("g_ca_weaponarena"); BADCVAR("g_freezetag_weaponarena"); BADCVAR("g_lms_weaponarena"); + BADCVAR("g_mayhem_weaponarena"); + BADCVAR("g_tmayhem_weaponarena"); BADCVAR("g_ctf_stalemate_time"); #undef BADPRESUFFIX