From: LegendaryGuard Date: Fri, 26 Mar 2021 23:28:47 +0000 (+0100) Subject: Changed to Murder in Megaerebus Manor, fixes and enhanced bots Civilians and Sleuths... X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=3c568f3a44776afa76dd4daf4540cc9774f37496;p=xonotic%2Fxonotic-data.pk3dir.git Changed to Murder in Megaerebus Manor, fixes and enhanced bots Civilians and Sleuths vs Murderers and improvements --- diff --git a/gamemodes-client.cfg b/gamemodes-client.cfg index 633b73fe2..aa8daa67e 100644 --- a/gamemodes-client.cfg +++ b/gamemodes-client.cfg @@ -32,7 +32,7 @@ alias cl_hook_gamestart_ka alias cl_hook_gamestart_ft alias cl_hook_gamestart_inv alias cl_hook_gamestart_duel -alias cl_hook_gamestart_ttt //LegendGuard adds ttt client hook for TTT 20-02-2021 +alias cl_hook_gamestart_mmm //LegendGuard adds mmm client hook for MMM 20-02-2021 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 6e058ec4e..f7bbfc4bd 100644 --- a/gamemodes-server.cfg +++ b/gamemodes-server.cfg @@ -29,7 +29,7 @@ alias sv_hook_gamestart_ka alias sv_hook_gamestart_ft alias sv_hook_gamestart_inv alias sv_hook_gamestart_duel -alias sv_hook_gamestart_ttt //LegendGuard adds ttt hook for TTT 20-02-2021 +alias sv_hook_gamestart_mmm //LegendGuard adds mmm hook for MMM 20-02-2021 // 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 @@ -59,7 +59,7 @@ 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_ttt //LegendGuard adds ttt hook for TTT 20-02-2021 +alias sv_vote_gametype_hook_mmm //LegendGuard adds mmm hook for MMM 20-02-2021 // 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. @@ -210,13 +210,13 @@ 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_ttt_respawn_delay_small 0 //LegendGuard adds ttt cvars for TTT 20-02-2021 -set g_ttt_respawn_delay_small_count 0 -set g_ttt_respawn_delay_large 0 -set g_ttt_respawn_delay_large_count 0 -set g_ttt_respawn_delay_max 0 -set g_ttt_respawn_waves 0 -set g_ttt_weapon_stay 0 +set g_mmm_respawn_delay_small 0 //LegendGuard adds mmm cvars for MMM 20-02-2021 +set g_mmm_respawn_delay_small_count 0 +set g_mmm_respawn_delay_large 0 +set g_mmm_respawn_delay_large_count 0 +set g_mmm_respawn_delay_max 0 +set g_mmm_respawn_waves 0 +set g_mmm_weapon_stay 0 // ========= @@ -566,16 +566,21 @@ set g_duel 0 "Duel: frag the opponent more in a one versus one arena battle" 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" -//LegendGuard adds ttt cvars for TTT 20-02-2021 +//LegendGuard adds mmm cvars for MMM 20-02-2021 // ========== -// trouble in terrorist town +// murder in megaerebus manor // ========== -set g_ttt 0 "Trouble in Terrorist Town: A group of space terrorists have traitors among them. Traitors must kill terrorists, while the terrorists have to try to find and kill the traitors" -set g_ttt_not_lms_maps 0 "when this is set, LMS maps will NOT be listed in ttt" -//set g_ttt_traitor_count 0.25 "number of players who will become traitors, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players" -set g_ttt_punish_teamkill 0 "kill the player when they kill an ally" -set g_ttt_reward_innocent 1 "give a point to all innocent players if the round timelimit is reached, in addition to the points given for kills" -set g_ttt_warmup 10 "how long the players will have time to run around the map before the round starts" -set g_ttt_round_timelimit 180 "round time limit in seconds" -set g_ttt_max_karma_points 1000 "limit of maximum number of karma points will have in the server" -set g_ttt_min_karma_points 400 "limit where number of karma points can be reached when are being decreased" \ No newline at end of file +set g_mmm 0 "Murder in Megaerebus Manor: A group of space civilians have murderers among them. Murderers must kill civilians, while the civilians have to try to find and kill the murderers" +set g_mmm_not_lms_maps 0 "when this is set, LMS maps will NOT be listed in mmm" +set g_mmm_civilian_count 0.625 "number of players who will become civilians, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players" +//set g_mmm_murderer_count 0.25 "number of players who will become murderers, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players" +set g_mmm_punish_teamkill 0 "kill the player when they kill an ally" +set g_mmm_reward_civilian 1 "give a point to all civilian players if the round timelimit is reached, in addition to the points given for kills" +set g_mmm_warmup 10 "how long the players will have time to run around the map before the round starts" +set g_mmm_round_timelimit 180 "round time limit in seconds" +set g_mmm_max_karma_points 1000 "limit of maximum number of karma points will have in the server" +set g_mmm_min_karma_points 400 "limit where number of karma points can be reached when are being decreased" +set g_mmm_karma_bankick_tool 0 "tool for strict rules when karma is low: '0' forces player to spec, '1' kicks player, '2' bans player" +set g_mmm_karma_bantime 1800 "number of seconds to ban someone with very low karma" +set g_mmm_karma_damageactive 1 "enable karma damage rule. If a player's karma is low, they will not do as much damage as a player who has high or full karma" +set g_mmm_reward_sleuth 1 "give a point to all sleuth players if investigated corpses" \ No newline at end of file diff --git a/gfx/menu/luma/gametype_mmm.tga b/gfx/menu/luma/gametype_mmm.tga new file mode 100644 index 000000000..481c62132 Binary files /dev/null and b/gfx/menu/luma/gametype_mmm.tga differ diff --git a/gfx/menu/luma/gametype_ttt.tga b/gfx/menu/luma/gametype_ttt.tga deleted file mode 100644 index d36db5ddd..000000000 Binary files a/gfx/menu/luma/gametype_ttt.tga and /dev/null differ diff --git a/gfx/menu/luminos/gametype_mmm.tga b/gfx/menu/luminos/gametype_mmm.tga new file mode 100644 index 000000000..8bacd80fd Binary files /dev/null and b/gfx/menu/luminos/gametype_mmm.tga differ diff --git a/gfx/menu/luminos/gametype_ttt.tga b/gfx/menu/luminos/gametype_ttt.tga deleted file mode 100644 index 7fd5a5341..000000000 Binary files a/gfx/menu/luminos/gametype_ttt.tga and /dev/null differ diff --git a/gfx/menu/wickedx/gametype_mmm.tga b/gfx/menu/wickedx/gametype_mmm.tga new file mode 100644 index 000000000..8bacd80fd Binary files /dev/null and b/gfx/menu/wickedx/gametype_mmm.tga differ diff --git a/gfx/menu/wickedx/gametype_ttt.tga b/gfx/menu/wickedx/gametype_ttt.tga deleted file mode 100644 index 7fd5a5341..000000000 Binary files a/gfx/menu/wickedx/gametype_ttt.tga and /dev/null differ diff --git a/gfx/menu/xaw/gametype_mmm.tga b/gfx/menu/xaw/gametype_mmm.tga new file mode 100644 index 000000000..938b529ef Binary files /dev/null and b/gfx/menu/xaw/gametype_mmm.tga differ diff --git a/gfx/menu/xaw/gametype_ttt.tga b/gfx/menu/xaw/gametype_ttt.tga deleted file mode 100644 index f0be4ad8e..000000000 Binary files a/gfx/menu/xaw/gametype_ttt.tga and /dev/null differ diff --git a/notifications.cfg b/notifications.cfg index cfcff6f7c..3028b11b9 100644 --- a/notifications.cfg +++ b/notifications.cfg @@ -286,9 +286,9 @@ seta notification_INFO_SUPERWEAPON_PICKUP "1" "0 = off, 1 = print to console, 2 seta notification_INFO_TEAMCHANGE_LARGERTEAM "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_TEAMCHANGE_NOTALLOWED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -//LegendGuard adds ttt notifications for TTT 20-02-2021 -seta notification_INFO_TTT_TRAITOR_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -seta notification_INFO_TTT_INNOCENT_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +//LegendGuard adds mmm notifications for MMM 20-02-2021 +seta notification_INFO_MMM_MURDERER_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_MMM_CIVILIAN_WIN "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_VERSION_BETA "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_VERSION_OLD "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -543,12 +543,12 @@ seta notification_CENTER_TEAMCHANGE_SPECTATE "1" "0 = off, 1 = centerprint" seta notification_CENTER_TEAMCHANGE_SUICIDE "1" "0 = off, 1 = centerprint" seta notification_CENTER_TIMEOUT_BEGINNING "1" "0 = off, 1 = centerprint" seta notification_CENTER_TIMEOUT_ENDING "1" "0 = off, 1 = centerprint" -//LegendGuard adds ttt notification for TTT 20-02-2021 -seta notification_CENTER_TTT_TRAITOR "1" "0 = off, 1 = centerprint" -seta notification_CENTER_TTT_TRAITOR_WIN "1" "0 = off, 1 = centerprint" -seta notification_CENTER_TTT_INNOCENT "1" "0 = off, 1 = centerprint" -seta notification_CENTER_TTT_INNOCENT_WIN "1" "0 = off, 1 = centerprint" -seta notification_CENTER_TTT_DETECTIVE "1" "0 = off, 1 = centerprint" +//LegendGuard adds mmm notification for MMM 20-02-2021 +seta notification_CENTER_MMM_MURDERER "1" "0 = off, 1 = centerprint" +seta notification_CENTER_MMM_MURDERER_WIN "1" "0 = off, 1 = centerprint" +seta notification_CENTER_MMM_CIVILIAN "1" "0 = off, 1 = centerprint" +seta notification_CENTER_MMM_CIVILIAN_WIN "1" "0 = off, 1 = centerprint" +seta notification_CENTER_MMM_SLEUTH "1" "0 = off, 1 = centerprint" seta notification_CENTER_VEHICLE_ENTER "1" "0 = off, 1 = centerprint" seta notification_CENTER_VEHICLE_ENTER_GUNNER "1" "0 = off, 1 = centerprint" diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc index 5f0d78897..713ed6f99 100644 --- a/qcsrc/common/ent_cs.qc +++ b/qcsrc/common/ent_cs.qc @@ -157,11 +157,11 @@ ENTCS_PROP(SOLID, true, sv_solid, solid, ENTCS_SET_NORMAL, { WriteByte(chan, ent.sv_solid); }, { ent.sv_solid = ReadByte(); }) -//LegendGuard adds ENTCS_PROP from TTT 20-02-2021 -// gamemode specific player ttt status (independent of score and frags) -ENTCS_PROP(TTT_STATUS, true, ttt_status, ttt_status, ENTCS_SET_NORMAL, - { WriteShort(chan, ent.ttt_status); }, - { ent.ttt_status = ReadShort(); }) +//LegendGuard adds ENTCS_PROP from MMM 20-02-2021 +// gamemode specific player mmm status (independent of score and frags) +ENTCS_PROP(MMM_STATUS, true, mmm_status, mmm_status, ENTCS_SET_NORMAL, + { WriteShort(chan, ent.mmm_status); }, + { ent.mmm_status = ReadShort(); }) #ifdef SVQC diff --git a/qcsrc/common/gamemodes/gamemode/_mod.inc b/qcsrc/common/gamemodes/gamemode/_mod.inc index 759f00def..aed8d748b 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/_mod.inc @@ -12,8 +12,8 @@ #include #include #include +#include //LegendGuard adds _mod.inc for Murder in Megaerebus Manor 20-02-2021 #include #include #include -#include //LegendGuard adds _mod.inc for Trouble In Terrorist Town 20-02-2021 #include \ No newline at end of file diff --git a/qcsrc/common/gamemodes/gamemode/_mod.qh b/qcsrc/common/gamemodes/gamemode/_mod.qh index ef725161b..3631a218b 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/_mod.qh @@ -12,8 +12,8 @@ #include #include #include +#include //LegendGuard adds _mod.qh for Murder in Megaerebus Manor 20-02-2021 #include #include #include -#include //LegendGuard adds _mod.qh for Trouble In Terrorist Town 20-02-2021 #include \ No newline at end of file diff --git a/qcsrc/common/gamemodes/gamemode/mmm/_mod.inc b/qcsrc/common/gamemodes/gamemode/mmm/_mod.inc new file mode 100644 index 000000000..32ad9f796 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/_mod.inc @@ -0,0 +1,8 @@ +// generated file; do not modify +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/mmm/_mod.qh b/qcsrc/common/gamemodes/gamemode/mmm/_mod.qh new file mode 100644 index 000000000..03ae61b36 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/_mod.qh @@ -0,0 +1,8 @@ +// generated file; do not modify +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/mmm/cl_mmm.qc b/qcsrc/common/gamemodes/gamemode/mmm/cl_mmm.qc new file mode 100644 index 000000000..888260048 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/cl_mmm.qc @@ -0,0 +1,103 @@ +#include "cl_mmm.qh" + +#include +#include + +void HUD_Mod_MMM(vector pos, vector mySize) +{ + mod_active = 1; // mmm should always show the mod HUD + + int mystatus = entcs_receiver(player_localnum).mmm_status; + string player_text = ""; + vector player_color = '1 1 1'; + //string player_icon = ""; + if(mystatus == MMM_STATUS_MURDERER) + { + player_text = _("Murderer"); + player_color = '1 0 0'; + //player_icon = "player_red"; + } + else if(mystatus == MMM_STATUS_CIVILIAN) + { + player_text = _("Civilian"); + player_color = '0 1 0'; + //player_icon = "player_neutral"; + } + else if(mystatus == MMM_STATUS_SLEUTH) + { + player_text = _("Sleuth"); + player_color = '0 0 1'; + //player_icon = "player_blue"; + } + else + { + // if the player has no valid status, don't draw anything + return; + } + + string time_text = string_null; + vector timer_color = '1 1 1'; + if(!STAT(GAME_STOPPED) && !warmup_stage && STAT(MMM_ROUNDTIMER) > 0) + { + float timeleft = max(0, STAT(MMM_ROUNDTIMER) - time); + timeleft = ceil(timeleft); + float minutesLeft = floor(timeleft / 60); + time_text = seconds_tostring(timeleft); + if(intermission_time || minutesLeft >= 5 || warmup_stage || STAT(MMM_ROUNDTIMER) == 0) + timer_color = '1 1 1'; //white + else if(minutesLeft >= 1) + timer_color = '1 1 0'; //yellow + else + timer_color = '1 0 0'; //red + } + + //drawpic_aspect_skin(pos, player_icon, vec2(0.5 * mySize.x, mySize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + if(!time_text) + drawstring_aspect(pos, player_text, vec2(mySize.x, mySize.y), player_color, panel_fg_alpha, DRAWFLAG_NORMAL); + else + { + drawstring_aspect(pos, player_text, vec2(0.5 * mySize.x, mySize.y), player_color, panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(pos + eX * (0.5 * mySize.x), time_text, vec2(0.5 * mySize.x, mySize.y), timer_color, panel_fg_alpha, DRAWFLAG_NORMAL); + } +} + +REGISTER_MUTATOR(cl_mmm, true); + +MUTATOR_HOOKFUNCTION(cl_mmm, ForcePlayercolors_Skip, CBC_ORDER_LAST) +{ + if(!ISGAMETYPE(MMM)) + return false; + + entity player = M_ARGV(0, entity); + entity e = entcs_receiver(player.entnum - 1); + int otherplayer_status = ((e) ? e.mmm_status : 0); + int mystatus = entcs_receiver(player_localnum).mmm_status; + + int plcolor = MMM_COLOR_CIVILIAN; // default to civilian + + if((mystatus == MMM_STATUS_MURDERER || intermission || STAT(GAME_STOPPED)) && otherplayer_status == MMM_STATUS_MURDERER) + { + plcolor = MMM_COLOR_MURDERER; + } + + //LegendGuard adds CIVILIAN part 21-02-2021 + if((mystatus == MMM_STATUS_CIVILIAN || intermission || STAT(GAME_STOPPED)) && otherplayer_status == MMM_STATUS_CIVILIAN) + { + plcolor = MMM_COLOR_CIVILIAN; + } + + //LegendGuard adds if sentence for Sleuth model color which will shown for everyone 21-02-2021 + if (otherplayer_status == MMM_STATUS_SLEUTH) + { + plcolor = MMM_COLOR_SLEUTH; + } + + player.colormap = 1024 + plcolor; + return true; +} + +MUTATOR_HOOKFUNCTION(cl_mmm, DrawScoreboard_Force) +{ + // show the scoreboard when the round ends, so players can see who the murderer was + return STAT(GAME_STOPPED); +} diff --git a/qcsrc/common/gamemodes/gamemode/mmm/cl_mmm.qh b/qcsrc/common/gamemodes/gamemode/mmm/cl_mmm.qh new file mode 100644 index 000000000..154a1274a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/cl_mmm.qh @@ -0,0 +1,3 @@ +#pragma once + +void HUD_Mod_MMM(vector pos, vector mySize); diff --git a/qcsrc/common/gamemodes/gamemode/mmm/mmm.qc b/qcsrc/common/gamemodes/gamemode/mmm/mmm.qc new file mode 100644 index 000000000..7228ebc29 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/mmm.qc @@ -0,0 +1 @@ +#include "mmm.qh" diff --git a/qcsrc/common/gamemodes/gamemode/mmm/mmm.qh b/qcsrc/common/gamemodes/gamemode/mmm/mmm.qh new file mode 100644 index 000000000..a53794974 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/mmm.qh @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#ifdef CSQC +void HUD_Mod_MMM(vector pos, vector mySize); +#endif +CLASS(MurderinMegaerebusManor, Gametype) + INIT(MurderinMegaerebusManor) + { + this.gametype_init(this, _("Murder in Megaerebus Manor"),"mmm","g_mmm",GAMETYPE_FLAG_USEPOINTS,"","timelimit=30 pointlimit=20",_("A group of space civilians have murderers among them. Murderers must kill civilians, while the civilians have to try to find and kill the murderers")); + } + METHOD(MurderinMegaerebusManor, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return true; + } + METHOD(MurderinMegaerebusManor, m_isForcedSupported, bool(Gametype this)) + { + if(!cvar("g_mmm_not_lms_maps")) + { + // if this is unset, all LMS maps support MurderinMegaerebusManor too + if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS.m_flags)) + return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported) + } + return false; + } +#ifdef CSQC + ATTRIB(MurderinMegaerebusManor, m_modicons, void(vector pos, vector mySize), HUD_Mod_MMM); +#endif +ENDCLASS(MurderinMegaerebusManor) +REGISTER_GAMETYPE(MMM, NEW(MurderinMegaerebusManor)); + +#ifdef GAMEQC +// shared state signalling the player's mmm status +.int mmm_status; +const int MMM_STATUS_CIVILIAN = 1; +const int MMM_STATUS_MURDERER = 2; +const int MMM_STATUS_SLEUTH = 3; + +// hardcoded player colors for mmm +const int MMM_COLOR_SLEUTH = 221; // blue +const int MMM_COLOR_CIVILIAN = 51; // green +const int MMM_COLOR_MURDERER = 68; // red +#endif diff --git a/qcsrc/common/gamemodes/gamemode/mmm/sv_mmm.qc b/qcsrc/common/gamemodes/gamemode/mmm/sv_mmm.qc new file mode 100644 index 000000000..14795bb2c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/sv_mmm.qc @@ -0,0 +1,935 @@ +#include "sv_mmm.qh" + +//set g_mmm_sleuth_count 0.125 "number of players who will become sleuths, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players" +//float autocvar_g_mmm_sleuth_count = 0.125; //I don't think that it won't be used... +float autocvar_g_mmm_civilian_count = 0.625; +//float autocvar_g_mmm_murderer_count = 0.25; +float autocvar_g_mmm_round_timelimit = 180; +float autocvar_g_mmm_warmup = 10; +bool autocvar_g_mmm_punish_teamkill = false; +bool autocvar_g_mmm_reward_civilian = true; +bool autocvar_g_mmm_reward_sleuth = true; //sleuth reward if investigated corpses +float autocvar_g_mmm_max_karma_points = 1000; //LegendGuard sets Karma points 21-02-2021 +float autocvar_g_mmm_min_karma_points = 400; +int autocvar_g_mmm_karma_bankick_tool = 0; //LegendGuard sets a ban tool for server admins 11-03-2021 +float autocvar_g_mmm_karma_bantime = 1800; //karma ban seconds +bool autocvar_g_mmm_karma_damageactive = true; //LegendGuard sets Karma damage setting if active 20-03-2021 + +// Sleuth is a created team, this team is added inside Civilians team + +void mmm_FakeTimeLimit(entity e, float t) +{ + if(!IS_REAL_CLIENT(e)) + return; +#if 0 + msg_entity = e; + WriteByte(MSG_ONE, 3); // svc_updatestat + WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT + if(t < 0) + WriteCoord(MSG_ONE, autocvar_timelimit); + else + WriteCoord(MSG_ONE, (t + 1) / 60); +#else + STAT(MMM_ROUNDTIMER, e) = t; +#endif +} + +void nades_Clear(entity player); + +void karma_Control(entity it) +{ + float masksize = autocvar_g_ban_default_masksize; + float bantime = autocvar_g_mmm_karma_bantime; + if(it.karmapoints >= autocvar_g_mmm_max_karma_points) + { + //Resets karmapoints to maintain the maximum + //PrintToChatAll("^3REWARD ^1MAXIMUM RESET"); + GameRules_scoring_add(it, MMM_KARMA, autocvar_g_mmm_max_karma_points - it.karmapoints); + it.karmapoints = autocvar_g_mmm_max_karma_points; + } + else if(it.karmapoints <= autocvar_g_mmm_min_karma_points) + { + switch (autocvar_g_mmm_karma_bankick_tool) + { + //force to spec + case 0: PutObserverInServer(it); return; + //kick + case 1: dropclient(it); return; + //ban and kick + case 2: Ban_KickBanClient(it, bantime, masksize, "Too low karma"); return; + //force to spec + default: PutObserverInServer(it); return; + } + } +} + +void karmaLoseDifference(entity attacker, entity target) +{ + //BASIC MATH THEORY: example: 1000 * 0.3 * (0.1 + 0.4) * 0.25 // karma points reduce when player attacked to other player + if (target.karmapoints < attacker.karmapoints) + { + float decreasekarma = - ( target.karmapoints * random() * ( 0.1 + random() ) * 0.25 ); + GameRules_scoring_add(attacker, MMM_KARMA, decreasekarma); + attacker.karmapoints = attacker.karmapoints + decreasekarma; + } + else if (target.karmapoints > attacker.karmapoints) + { + float decreasekarma = - ( target.karmapoints * random() * ( 0.1 + random() ) * 0.25 ); + GameRules_scoring_add(attacker, MMM_KARMA, decreasekarma); + attacker.karmapoints = attacker.karmapoints + decreasekarma; + } + else + { + float decreasekarma = - ( target.karmapoints * random() * ( 0.1 + random() ) * 0.25 ); + GameRules_scoring_add(attacker, MMM_KARMA, decreasekarma); + attacker.karmapoints = attacker.karmapoints + decreasekarma; + } +} + +void karmaWinDifference(entity it) +{ + GameRules_scoring_add(it, SCORE, 1); // reward civilians who make it to the end of the round time limit + float increasekarma = ( autocvar_g_mmm_min_karma_points * random() * ( 0.1 + random() ) * 0.12 ); + GameRules_scoring_add(it, MMM_KARMA, increasekarma); + it.karmapoints = it.karmapoints + increasekarma; +} + +void mmm_UpdateScores(bool timed_out) +{ + // give players their hard-earned kills now that the round is over + FOREACH_CLIENT(true, + { + it.totalfrags += it.mmm_validkills; + if(it.mmm_validkills) + { + GameRules_scoring_add(it, SCORE, it.mmm_validkills); + } + it.mmm_validkills = 0; + // player survived the round + if(IS_PLAYER(it) && !IS_DEAD(it)) // LegendGuard adds something for Karma 21-02-2021 + { + if((autocvar_g_mmm_reward_civilian && timed_out && it.mmm_status == MMM_STATUS_CIVILIAN) + || (autocvar_g_mmm_reward_civilian && !timed_out && it.mmm_status == MMM_STATUS_CIVILIAN)) + { + karmaWinDifference(it); + //PrintToChatAll(sprintf("^2REWARD ^7it.karmapoints: ^1%f", it.karmapoints)); + karma_Control(it); + } + + //Sleuth reward after investigated a corpse + if((autocvar_g_mmm_reward_sleuth && timed_out && it.mmm_status == MMM_STATUS_SLEUTH) + || (autocvar_g_mmm_reward_sleuth && !timed_out && it.mmm_status == MMM_STATUS_SLEUTH)) + { + if (it.investigated == true) + { + karmaWinDifference(it); + it.investigated = false; + } + karma_Control(it); + } + + if(it.mmm_status == MMM_STATUS_CIVILIAN) + { + karmaWinDifference(it); + //PrintToChatAll(sprintf("^2CIVILIAN ^7it.karmapoints: ^1%f", it.karmapoints)); + karma_Control(it); + } + else if(it.mmm_status == MMM_STATUS_MURDERER) + { + karmaWinDifference(it); + //PrintToChatAll(sprintf("^1MURDERER ^7it.karmapoints: ^1%f", it.karmapoints)); + karma_Control(it); + } + } + }); +} + +float mmm_CheckWinner() +{ + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + // if the match times out, civilians win too! + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MMM_CIVILIAN_WIN); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MMM_CIVILIAN_WIN); + FOREACH_CLIENT(true, + { + if(IS_PLAYER(it)) + nades_Clear(it); + mmm_FakeTimeLimit(it, -1); + }); + + mmm_UpdateScores(true); + + allowed_to_spawn = false; + game_stopped = true; + round_handler_Init(5, autocvar_g_mmm_warmup, autocvar_g_mmm_round_timelimit); + return 1; + } + + int civilian_count = 0, murderer_count = 0, sleuth_count = 0; + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), + { + if(it.mmm_status == MMM_STATUS_CIVILIAN) + civilian_count++; + else if(it.mmm_status == MMM_STATUS_MURDERER) + murderer_count++; + else if(it.mmm_status == MMM_STATUS_SLEUTH) //LegendGuard adds sleuth_count 20-02-2021 + sleuth_count++; + }); + if(civilian_count > 0 && murderer_count > 0) + { + return 0; + } + + if(murderer_count > 0) // murderers win + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MMM_MURDERER_WIN); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MMM_MURDERER_WIN); + } + else if(civilian_count > 0) // civilians win + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MMM_CIVILIAN_WIN); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MMM_CIVILIAN_WIN); + } + else if (sleuth_count > 0 && civilian_count > 0) // sleuths are same as civilians win + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MMM_CIVILIAN_WIN); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_MMM_CIVILIAN_WIN); + } + else + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); + } + + mmm_UpdateScores(false); + + allowed_to_spawn = false; + game_stopped = true; + round_handler_Init(5, autocvar_g_mmm_warmup, autocvar_g_mmm_round_timelimit); + + FOREACH_CLIENT(true, + { + if(IS_PLAYER(it)) + { + it.respawn_flags = RESPAWN_SILENT; //CSQC print output respawn lib.qh error fix + nades_Clear(it); + } + mmm_FakeTimeLimit(it, -1); + }); + + return 1; +} + +void mmm_RoundStart() +{ + allowed_to_spawn = boolean(warmup_stage); + int playercount = 0; + + FOREACH_CLIENT(true, + { + if(IS_PLAYER(it) && !IS_DEAD(it)) + { + ++playercount; + it.mmm_status = MMM_STATUS_CIVILIAN; + } + else + it.mmm_status = 0; // this is mostly a safety check; if a client manages to somehow maintain a mmm status, clear it before the round starts! + it.mmm_validkills = 0; + }); + + int civilian_count = bound(1, ((autocvar_g_mmm_civilian_count >= 1) ? autocvar_g_mmm_civilian_count : floor(playercount * autocvar_g_mmm_civilian_count)), playercount - 1); // 20%, but ensure at least 1 and less than total + int total_civilians = 0; + //int murderer_count = bound(1, ((autocvar_g_mmm_murderer_count >= 1) ? autocvar_g_mmm_murderer_count : floor(playercount * autocvar_g_mmm_murderer_count)), playercount - 1); // 20%, but ensure at least 1 and less than total + int total_murderers = 0; + //int sleuth_count = bound(1, ((autocvar_g_mmm_sleuth_count >= 1) ? autocvar_g_mmm_sleuth_count : floor(playercount * autocvar_g_mmm_sleuth_count)), playercount - 1); // 20%, but ensure at least 1 and less than total + int total_sleuths = 0; + + //civilians TOTAL + FOREACH_CLIENT_RANDOM(IS_PLAYER(it) && !IS_DEAD(it), + { + if(total_civilians >= civilian_count) + break; + //LegendGuard fixes the round start again 22-03-2021 + total_civilians++; + if (total_civilians <= 1) + { + if (total_murderers <= 1) + { + total_murderers++; + it.mmm_status = MMM_STATUS_MURDERER; + } + } + else if (total_civilians == 2) + { + if (total_sleuths >= 1) + break; + else + { + total_sleuths++; + it.mmm_status = MMM_STATUS_SLEUTH; + } + } + else if (total_civilians == 5) + { + if (total_sleuths >= 2) + break; + else + { + total_sleuths++; + it.mmm_status = MMM_STATUS_SLEUTH; + } + } + else if (total_civilians >= 7) + { + if (total_sleuths >= 3) + break; + else if (total_murderers == 3) + { + total_murderers++; + it.mmm_status = MMM_STATUS_MURDERER; + } + else + { + total_sleuths++; + it.mmm_status = MMM_STATUS_SLEUTH; + } + } + else + { + total_murderers++; + it.mmm_status = MMM_STATUS_MURDERER; + } + }); + + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), + { + float totalmeankarma = ((autocvar_g_mmm_max_karma_points + autocvar_g_mmm_min_karma_points + it.karmapoints) / 3); + karma_Control(it); + it.activekillerrole = false; + + if(it.mmm_status == MMM_STATUS_CIVILIAN) + { + SetResource(it, RES_SHELLS, 10); + SetResource(it, RES_BULLETS, 20); + SetResource(it, RES_ROCKETS, 10); + SetResource(it, RES_CELLS, 10); + if (it.karmapoints <= totalmeankarma) + { + centerprint(it, strcat(BOLD_OPERATOR, "^1KARMA WARNING!\n^3Here, have the Rifle!")); + GiveWeapon(it, WEP_RIFLE.m_id, OP_PLUS, 1); + } + //Gives Mine Layer weapon to the player + GiveWeapon(it, WEP_MINE_LAYER.m_id, OP_PLUS, 1); + Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_MMM_CIVILIAN); + Send_Notification(NOTIF_ONE_ONLY, it, MSG_INFO, INFO_MMM_CIVILIAN); + //PrintToChatAll(sprintf("^1DEBUG^7: %s is ^2Civilian^7!", it.netname)); + } + else if(it.mmm_status == MMM_STATUS_MURDERER) + { + SetResource(it, RES_SHELLS, 10); + SetResource(it, RES_BULLETS, 20); + SetResource(it, RES_ROCKETS, 10); + SetResource(it, RES_CELLS, 10); + if (it.karmapoints <= totalmeankarma) + { + centerprint(it, strcat(BOLD_OPERATOR, "^1KARMA WARNING!\n^3Here, have the Rifle!")); + GiveWeapon(it, WEP_RIFLE.m_id, OP_PLUS, 1); + } + //Gives Mine Layer weapon to the player + GiveWeapon(it, WEP_MINE_LAYER.m_id, OP_PLUS, 1); + Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_MMM_MURDERER); + Send_Notification(NOTIF_ONE_ONLY, it, MSG_INFO, INFO_MMM_MURDERER); + //PrintToChatAll(sprintf("^1DEBUG^7: %s is ^1Murderer^7!", it.netname)); + } + else if(it.mmm_status == MMM_STATUS_SLEUTH) + { + SetResource(it, RES_ROCKETS, 10); + if (it.karmapoints <= totalmeankarma) + { + centerprint(it, strcat(BOLD_OPERATOR, "^1KARMA WARNING!\n^3Here, have the Rifle!")); + GiveWeapon(it, WEP_RIFLE.m_id, OP_PLUS, 1); + } + //Gives Shockwave and Mine Layer weapon to the player + GiveWeapon(it, WEP_SHOCKWAVE.m_id, OP_PLUS, 1); + GiveWeapon(it, WEP_MINE_LAYER.m_id, OP_PLUS, 1); + Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_MMM_SLEUTH); + Send_Notification(NOTIF_ONE_ONLY, it, MSG_INFO, INFO_MMM_SLEUTH); + PrintToChatAll(sprintf("%s is ^4Sleuth^7!", it.netname)); + } + mmm_FakeTimeLimit(it, round_handler_GetEndTime()); + }); +} + +bool mmm_CheckPlayers() +{ + static int prev_missing_players; + allowed_to_spawn = true; + int playercount = 0; + + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), + { + //PrintToChatAll(sprintf("it.karmapoints ^5begin: ^3%f",it.karmapoints)); + //Karma points start + if (it.karmastarted != true) + { + GameRules_scoring_add(it, MMM_KARMA, autocvar_g_mmm_max_karma_points - it.karmapoints); + it.karmapoints = autocvar_g_mmm_max_karma_points; + it.karmastarted = true; + } + karma_Control(it); + ++playercount; + //PrintToChatAll(sprintf("it.karmapoints ^6end: ^3%f",it.karmapoints)); + }); + + if (playercount >= 2) + { + if(prev_missing_players > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS); + prev_missing_players = -1; + return true; + } + + if(playercount == 0) + { + if(prev_missing_players > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS); + prev_missing_players = -1; + return false; + } + + // if we get here, only 1 player is missing + if(prev_missing_players != 1) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_PLAYERS, 1); + prev_missing_players = 1; + } + return false; +} + +bool mmm_isEliminated(entity e) +{ + if(e.caplayer == 1 && (IS_DEAD(e) || e.frags == FRAGS_PLAYER_OUT_OF_GAME)) + return true; + if(e.caplayer == 0.5) + return true; + return false; +} + +void mmm_Initialize() // run at the start of a match, initiates game mode +{ + GameRules_scoring(0, SFL_SORT_PRIO_PRIMARY, 0, { + field(SP_MMM_KARMA, "karma", SFL_SORT_PRIO_SECONDARY); //LegendGuard adds Karma points in the scoreboard 22-02-2021 + }); + + allowed_to_spawn = true; + round_handler_Spawn(mmm_CheckPlayers, mmm_CheckWinner, mmm_RoundStart); + round_handler_Init(5, autocvar_g_mmm_warmup, autocvar_g_mmm_round_timelimit); + EliminatedPlayers_Init(mmm_isEliminated); +} + +void checkWeaponDeathtype(entity target, float deathtype) +{ + switch (deathtype) + { + case WEP_ARC.m_id: case 276: case 788: target.killedwithweapon = "Impacted by the Arc's electric shock"; return; + case WEP_BLASTER.m_id: case 513: target.killedwithweapon = "Blasted by the Blaster"; return; + case WEP_CRYLINK.m_id: case 263: case 519: target.killedwithweapon = "Shot by the Crylink"; return; + case WEP_DEVASTATOR.m_id: case 522: case 1546: target.killedwithweapon = "Bombarded by the Devastator"; return; + case WEP_ELECTRO.m_id: case 262: case 518: case 1542: target.killedwithweapon = "Electrocuted by the Electro"; return; + case WEP_FIREBALL.m_id: case 273: case 529: case 1297: target.killedwithweapon = "Burned by the Fireball"; return; + case WEP_HAGAR.m_id: case 265: target.killedwithweapon = "Gunned by the Hagar"; return; + case WEP_HLAC.m_id: case 270: case 526: target.killedwithweapon = "Cut down with the HLAC"; return; + case WEP_HOOK.m_id: case 1805: target.killedwithweapon = "Caught in Hook gravity bomb"; return; + case WEP_MACHINEGUN.m_id: target.activekillerrole = true; target.killedwithweapon = "Riddled full of holes by the Machine Gun"; return; + case WEP_MINE_LAYER.m_id: case 517: case 1541: target.killedwithweapon = "Exploited by the Mine Layer"; return; + case WEP_MORTAR.m_id: case 516: case 1284: target.killedwithweapon = "Blew up with the Mortar"; return; + case WEP_OVERKILL_NEX.m_id: target.killedwithweapon = "Sniped by the Overkill Nex"; return; + case WEP_RIFLE.m_id: case 272: target.activekillerrole = true; target.killedwithweapon = "Sniped by the Rifle"; return; + case WEP_SEEKER.m_id: case 274: case 786: target.killedwithweapon = "Blasted by the Seeker"; return; + case WEP_SHOCKWAVE.m_id: target.killedwithweapon = "Gunned down by the Shockwave"; return; + case 275: target.killedwithweapon = "Knocked by the Shockwave"; return; + case WEP_SHOTGUN.m_id: target.activekillerrole = true; target.killedwithweapon = "Shot by Shotgun"; return; + case 258: target.killedwithweapon = "Knocked by the Shotgun"; return; + case WEP_TUBA.m_id: target.killedwithweapon = "Ear pain by the @!#%'n Tuba"; return; + case WEP_VAPORIZER.m_id: case 257: case 769: target.killedwithweapon = "Sniped by the Vaporizer"; return; + case WEP_VORTEX.m_id: target.killedwithweapon = "Sniped by the Vortex"; return; + case DEATH_FALL.m_id: target.killedwithweapon = "Fall"; return; + case DEATH_FIRE.m_id: target.killedwithweapon = "Burned with the fire"; return; + case DEATH_LAVA.m_id: target.killedwithweapon = "Burned in lava"; return; + case DEATH_MIRRORDAMAGE.m_id: target.killedwithweapon = "Suicide"; return; + case DEATH_SLIME.m_id: target.killedwithweapon = "Melted in slime"; return; + case DEATH_TELEFRAG.m_id: target.killedwithweapon = "Telefragged"; return; + case DEATH_NADE.m_id: target.killedwithweapon = "Blown up by the nade"; return; + case DEATH_NADE_NAPALM.m_id: target.killedwithweapon = "Burned by the Napalm nade"; return; + case DEATH_NADE_ICE.m_id: target.killedwithweapon = "Frozen by the Ice nade"; return; + case DEATH_NADE_HEAL.m_id: target.killedwithweapon = "Sucked by the Heal nade"; return; + default: target.killedwithweapon = "Unknown"; return; + } +} + +void ReduceKarmaPointsandFrags(entity frag_attacker, entity frag_target, float frag_deathtype, entity wep_ent) +{ + karmaLoseDifference(frag_attacker, frag_target); + GiveFrags(frag_attacker, frag_target, ((autocvar_g_mmm_punish_teamkill) ? -1 : -2), frag_deathtype, wep_ent.weaponentity_fld); + karma_Control(frag_attacker); + frag_target.whokilled = frag_attacker.netname; +} + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(mmm, ClientObituary) +{ + // LegendGuard's IDEA: To adjust the grade of severity of karma, + // we could add if sentence per weapons and adjust each weapon attack + // its own grade. Instead doing random decrease grade 22-02-2021 + + // in mmm, announcing a frag would tell everyone who the murderer is + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(IS_PLAYER(frag_attacker) && frag_attacker != frag_target) + { + float frag_deathtype = M_ARGV(3, float); + entity wep_ent = M_ARGV(4, entity); + + //PrintToChatAll(strcat("deathtype var: ", ftos(frag_deathtype))); + checkWeaponDeathtype(frag_target, frag_deathtype); + // "team" kill, a point is awarded to the player by default so we must take it away plus an extra one + // unless the player is going to be punished for suicide, in which case just remove one + if(frag_attacker.mmm_status == frag_target.mmm_status) + { + //PrintToChatAll("^1DEBUG^7: A ^2PLAYER^7 has fragged a ^2PLAYER OF HIS OWN TEAM^7, TOO BAD!"); + ReduceKarmaPointsandFrags(frag_attacker, frag_target, frag_deathtype, wep_ent); + switch (frag_attacker.mmm_status) + { + case MMM_STATUS_CIVILIAN: frag_target.killerrole = "\n^3Killer role: ^2Civilian"; return; + case MMM_STATUS_MURDERER: frag_target.killerrole = "\n^3Killer role: ^1Murderer"; return; + case MMM_STATUS_SLEUTH: frag_target.killerrole = "\n^3Killer role: ^4Sleuth"; return; + default: return; + } + //PrintToChatAll(sprintf("frag_attacker.karmapoints: ^1%f", frag_attacker.karmapoints)); + } + + if(frag_attacker.mmm_status == MMM_STATUS_SLEUTH) + { + if (frag_target.mmm_status == MMM_STATUS_CIVILIAN || frag_target.mmm_status == MMM_STATUS_SLEUTH) + { + //PrintToChatAll("^1DEBUG^7: A ^4Sleuth^7 fragged an ^2Civilian^7/^4Sleuth^7, TOO BAD!"); + ReduceKarmaPointsandFrags(frag_attacker, frag_target, frag_deathtype, wep_ent); + frag_target.killerrole = "\n^3Killer role: ^4Sleuth"; + //PrintToChatAll(sprintf("frag_attacker.karmapoints: ^1%f", frag_attacker.karmapoints)); + } + else + { + frag_target.whokilled = frag_attacker.netname; + frag_target.killerrole = "\n^3Killer role: ^4Sleuth"; + } + } + + if (frag_attacker.mmm_status == MMM_STATUS_CIVILIAN) + { + if (frag_target.mmm_status == MMM_STATUS_SLEUTH) + { + //PrintToChatAll("^1DEBUG^7: An ^2Civilian^7 fragged a ^4Sleuth^7, TOO BAD!"); + ReduceKarmaPointsandFrags(frag_attacker, frag_target, frag_deathtype, wep_ent); + frag_target.killerrole = "\n^3Killer role: ^2Civilian"; + } + else + { + frag_target.whokilled = frag_attacker.netname; + frag_target.killerrole = "\n^3Killer role: ^2Civilian"; + } + } + + if (frag_attacker.mmm_status == MMM_STATUS_MURDERER) + { + if (frag_target.mmm_status == MMM_STATUS_CIVILIAN) + { + frag_target.whokilled = frag_attacker.netname; + frag_target.killerrole = "\n^3Killer role: ^1Murderer"; + } + else + { + frag_target.whokilled = frag_attacker.netname; + frag_target.killerrole = "\n^3Killer role: ^1Murderer"; + } + } + //if mmm_status is 1, means civilian, 2 means murderer, 3 means sleuth, TODO: the bots: frag_attacker(1) shouldn't attack to frag_target(3) + //PrintToChatAll(sprintf("^1DEBUG^7: frag_attacker.mmm_status is ^3%s^7",ftos(frag_attacker.mmm_status))); + //PrintToChatAll(sprintf("^1DEBUG^7: frag_target.mmm_status is ^3%s^7",ftos(frag_target.mmm_status))); + } + else + { + float frag_deathtype = M_ARGV(3, float); + checkWeaponDeathtype(frag_target, frag_deathtype); + } + + //TODO: try to do a "find out" if a sleuth can see who fragged to who if possible 21-02-2021 + M_ARGV(5, bool) = true; // anonymous attacker +} + +//karma weapon damage, halve the damage attack when player has low karma 20-03-2021 +MUTATOR_HOOKFUNCTION(mmm, Damage_Calculate) +{ + entity attacker = M_ARGV(1, entity); + entity target = M_ARGV(2, entity); + float deathtype = M_ARGV(3, float); + float damage = M_ARGV(4, float); + vector force = M_ARGV(6, vector); + string corpsemessagestrcat = ""; + + if (autocvar_g_mmm_karma_damageactive != false) + { + if (IS_PLAYER(attacker)) + { + if(target == attacker) // damage done to yourself + { + damage /= autocvar_g_weapondamagefactor / (attacker.karmapoints / autocvar_g_mmm_max_karma_points); + force /= autocvar_g_weaponforcefactor / (attacker.karmapoints / autocvar_g_mmm_max_karma_points); + } + else if (target != attacker) + { + damage /= autocvar_g_weapondamagefactor / (attacker.karmapoints / autocvar_g_mmm_max_karma_points); + force /= autocvar_g_weaponforcefactor / (attacker.karmapoints / autocvar_g_mmm_max_karma_points); + } + else + { + damage *= autocvar_g_weapondamagefactor; + force *= autocvar_g_weaponforcefactor; + } + } + } + + //SLEUTH CORPSE DETECTION SKILL 21-03-2021 + if (attacker.mmm_status == MMM_STATUS_SLEUTH) + { + if(IS_DEAD(target)) + { + //Shockwave weapon as radar gun to check the corpses 22-03-2021 + if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) + { + if (target.killedwithweapon == "") + target.killedwithweapon = "UNKNOWN CAUSE"; + + if (target.activekillerrole != true) + { + target.killerrole = ""; + target.activekillerrole = false; + } + + string killedbyphrase = strcat("\n^3Killed by:^7 ", target.whokilled, target.killerrole); + string wepkilledphrase = strcat("\n^3Cause:^7 ", target.killedwithweapon); + if (target.whokilled == "") + { + killedbyphrase = ""; + if (target.killedwithweapon == "") + wepkilledphrase = "\n^3Cause:^7 UNKNOWN CAUSE"; + } + + damage = 0; + force = '0 0 0'; + if (target.mmm_status == MMM_STATUS_CIVILIAN) + { + //try to add centerprint message for chat privately if possible + corpsemessagestrcat = strcat("\n^3Name:^7 ", target.netname, "\n^3Role: ^2Civilian", killedbyphrase, wepkilledphrase); + centerprint(attacker, strcat(BOLD_OPERATOR, corpsemessagestrcat));//("\n^6Name^3:^7 ", target.netname, "\n^5Role^3: ^2Civilian\n", "^1Killed by^3:^7 ", target.whokilled))); + } + else if (target.mmm_status == MMM_STATUS_MURDERER) + { + corpsemessagestrcat = strcat("\n^3Name:^7 ", target.netname, "\n^3Role: ^1Murderer", killedbyphrase, wepkilledphrase); + centerprint(attacker, strcat(BOLD_OPERATOR, corpsemessagestrcat));//("\n^6Name^3:^7 ", target.netname, "\n^5Role^3: ^1Murderer\n", "^1Killed by^3:^7 ", target.whokilled))); + } + else if (target.mmm_status == MMM_STATUS_SLEUTH) + { + corpsemessagestrcat = strcat("\n^3Name:^7 ", target.netname, "\n^3Role: ^4Sleuth", killedbyphrase, wepkilledphrase); + centerprint(attacker, strcat(BOLD_OPERATOR, corpsemessagestrcat));//("\n^6Name^3:^7 ", target.netname, "\n^5Role^3: ^4Sleuth\n", "^1Killed by^3:^7 ", target.whokilled))); + } + attacker.investigated = true; + } + } + } + + M_ARGV(4, float) = damage; + M_ARGV(6, vector) = force; +} + +MUTATOR_HOOKFUNCTION(mmm, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player) || player.caplayer) + { + // update the scoreboard colour display to out the real killer at the end of the round + // running this every frame to avoid cheats + int plcolor = MMM_COLOR_CIVILIAN; + if(player.mmm_status == MMM_STATUS_CIVILIAN && game_stopped) //Civilian status by default + plcolor = MMM_COLOR_CIVILIAN; + if(player.mmm_status == MMM_STATUS_MURDERER && game_stopped) + plcolor = MMM_COLOR_MURDERER; + //LegendGuard adds for Sleuth 21-02-2021 + if(player.mmm_status == MMM_STATUS_SLEUTH)// && game_stopped) + plcolor = MMM_COLOR_SLEUTH; + setcolor(player, plcolor); + } + + //CORPSE FEATURE 10-03-2021 + if (IS_DEAD(player)) + { + player.event_damage = func_null; + //player.health = 0; + player.solid = SOLID_CORPSE; + set_movetype(player, MOVETYPE_STEP); //test with MOVETYPE_TOSS or MOVETYPE_WALK (it's like sliding object) or MOVETYPE_BOUNCE (maybe not good) + } +} + +MUTATOR_HOOKFUNCTION(mmm, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.mmm_status = 0; + player.mmm_validkills = 0; + player.caplayer = 1; + if (!warmup_stage) + eliminatedPlayers.SendFlags |= 1; +} + +MUTATOR_HOOKFUNCTION(mmm, ForbidSpawn) +{ + entity player = M_ARGV(0, entity); + + // spectators / observers that weren't playing can join; they are + // immediately forced to observe in the PutClientInServer hook + // this way they are put in a team and can play in the next round + if (!allowed_to_spawn && player.caplayer) + return true; + return false; +} + +MUTATOR_HOOKFUNCTION(mmm, PutClientInServer) +{ + entity player = M_ARGV(0, entity); + + if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join + { + TRANSMUTE(Observer, player); + if (CS(player).jointime != time && !player.caplayer) // not when connecting + { + player.caplayer = 0.5; + Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE); + } + } +} + +MUTATOR_HOOKFUNCTION(mmm, reset_map_players) +{ + FOREACH_CLIENT(true, { + CS(it).killcount = 0; + it.mmm_status = 0; + mmm_FakeTimeLimit(it, -1); // restore original timelimit + if (!it.caplayer && IS_BOT_CLIENT(it)) + it.caplayer = 1; + if (it.caplayer) + { + TRANSMUTE(Player, it); + it.caplayer = 1; + it.respawn_flags = RESPAWN_SILENT; //CSQC print output respawn lib.qh error fix + PutClientInServer(it); + } + }); + bot_relinkplayerlist(); + return true; +} + +MUTATOR_HOOKFUNCTION(mmm, reset_map_global) +{ + allowed_to_spawn = true; + return true; +} + +entity mmm_LastPlayerForTeam(entity this) +{ + entity last_pl = NULL; + FOREACH_CLIENT(IS_PLAYER(it) && it != this, { + if (!IS_DEAD(it) && this.mmm_status == it.mmm_status) + { + if (!last_pl) + { + last_pl = it; + } + else + return NULL; + } + }); + return last_pl; +} + +void mmm_LastPlayerForTeam_Notify(entity this) +{ + if (!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) + { + entity pl = mmm_LastPlayerForTeam(this); + if (pl) + Send_Notification(NOTIF_ONE_ONLY, pl, MSG_CENTER, CENTER_ALONE); + } +} + +MUTATOR_HOOKFUNCTION(mmm, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + //float frag_deathtype = M_ARGV(3, float); + + mmm_LastPlayerForTeam_Notify(frag_target); + if (!allowed_to_spawn) + { + frag_target.respawn_flags = RESPAWN_DENY; + // prevent unwanted sudden rejoin as spectator and movement of spectator camera + frag_target.respawn_time = time + 2; + } + frag_target.respawn_flags |= RESPAWN_DENY; + frag_target.event_damage = func_null; + frag_target.health = 0; + + if (!warmup_stage) + { + eliminatedPlayers.SendFlags |= 1; + if (IS_BOT_CLIENT(frag_target)) + bot_clear(frag_target); + } + + //if(frag_attacker.mmm_status == frag_target.mmm_status) + // killed an ally! punishment is sentenced + if(frag_attacker.mmm_status == MMM_STATUS_SLEUTH) + { + if (frag_target.mmm_status == MMM_STATUS_CIVILIAN) + { + //PrintToChatAll("^1DEBUG^7: ^4SLEUTH ^1DAMAGE/DEAD^7 HAS TAKEN!"); + //30 damage points deal + Damage(frag_attacker, frag_attacker, frag_attacker, 30, DEATH_MIRRORDAMAGE.m_id, DMG_NOWEP, frag_attacker.origin, '0 0 0'); + } + } + if (frag_attacker.mmm_status == MMM_STATUS_CIVILIAN) + { + if (frag_target.mmm_status == MMM_STATUS_SLEUTH) + { + //PrintToChatAll("^1DEBUG^7: ^2CIVILIAN ^1DAMAGE/DEAD^7 HAS TAKEN!"); + //30 damage points deal + Damage(frag_attacker, frag_attacker, frag_attacker, 30, DEATH_MIRRORDAMAGE.m_id, DMG_NOWEP, frag_attacker.origin, '0 0 0'); + } + } + return true; +} + +MUTATOR_HOOKFUNCTION(mmm, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + if (IS_PLAYER(player) && !IS_DEAD(player)) + mmm_LastPlayerForTeam_Notify(player); + return true; +} + +MUTATOR_HOOKFUNCTION(mmm, MakePlayerObserver) +{ + // LegendGuard, here is where spectators shouldn't talk to any players to say the hints or who is who 21-10-2021 + entity player = M_ARGV(0, entity); + + if (IS_PLAYER(player) && !IS_DEAD(player)) + mmm_LastPlayerForTeam_Notify(player); + if (player.killindicator_teamchange == -2) // player wants to spectate + player.caplayer = 0; + if (player.caplayer) + player.frags = FRAGS_PLAYER_OUT_OF_GAME; + if (!warmup_stage) + eliminatedPlayers.SendFlags |= 1; + if (!player.caplayer) + { + player.mmm_validkills = 0; + player.mmm_status = 0; + mmm_FakeTimeLimit(player, -1); // restore original timelimit + return false; // allow team reset + } + return true; // prevent team reset +} + +MUTATOR_HOOKFUNCTION(mmm, Scores_CountFragsRemaining) +{ + // announce remaining frags? + return true; +} + +MUTATOR_HOOKFUNCTION(mmm, GiveFragsForKill, CBC_ORDER_FIRST) +{ + entity frag_attacker = M_ARGV(0, entity); + if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) + frag_attacker.mmm_validkills += M_ARGV(2, float); + M_ARGV(2, float) = 0; // score will be given to the winner when the round ends + return true; +} + +MUTATOR_HOOKFUNCTION(mmm, AddPlayerScore) +{ + // add scorefield for scoreboard here + entity scorefield = M_ARGV(0, entity); + if(scorefield == SP_KILLS || scorefield == SP_DEATHS || scorefield == SP_SUICIDES || scorefield == SP_DMG || scorefield == SP_DMGTAKEN) + M_ARGV(1, float) = 0; // don't report that the player has killed or been killed, that would out them as a murderer! +} + +MUTATOR_HOOKFUNCTION(mmm, CalculateRespawnTime) +{ + // no respawn calculations needed, player is forced to spectate anyway + return true; +} + +MUTATOR_HOOKFUNCTION(mmm, Bot_FixCount, CBC_ORDER_EXCLUSIVE) +{ + FOREACH_CLIENT(IS_REAL_CLIENT(it), { + if (IS_PLAYER(it) || it.caplayer == 1) + ++M_ARGV(0, int); + ++M_ARGV(1, int); + }); + return true; +} + +MUTATOR_HOOKFUNCTION(mmm, ClientCommand_Spectate) +{ + entity player = M_ARGV(0, entity); + + if (player.caplayer) + { + // they're going to spec, we can do other checks + if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player))) + Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE); + return MUT_SPECCMD_FORCE; + } + + return MUT_SPECCMD_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(mmm, GetPlayerStatus) +{ + entity player = M_ARGV(0, entity); + + return player.caplayer == 1; +} + +MUTATOR_HOOKFUNCTION(mmm, BotShouldAttack) +{ + entity bot = M_ARGV(0, entity); + entity targ = M_ARGV(1, entity); + + if(targ.mmm_status == bot.mmm_status) + { + return true; + } + + // LegendGuard fixed the problem of Sleuths and Civilians attacking each other 26-03-2021 + if(bot.mmm_status == MMM_STATUS_SLEUTH) + { + if(targ.mmm_status == MMM_STATUS_CIVILIAN) + return true; + } +} diff --git a/qcsrc/common/gamemodes/gamemode/mmm/sv_mmm.qh b/qcsrc/common/gamemodes/gamemode/mmm/sv_mmm.qh new file mode 100644 index 000000000..e5eca2115 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/mmm/sv_mmm.qh @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +void mmm_Initialize(); + +REGISTER_MUTATOR(mmm, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + mmm_Initialize(); + } + return false; +} + +.int mmm_validkills; // store the player's valid kills to be given at the end of the match (avoid exposing their score until then) +.float karmapoints; //LegendGuard adds karma points to store player status 22-02-2021 +.string whokilled; //LegendGuard sets a variable to know who killed who 22-03-2021 +.string killerrole; //LegendGuard sets a variable to identify killer role 25-03-2021 +.string killedwithweapon; //LegendGuard sets a variable to know what cause provoked to the victim 23-03-2021 +.bool investigated; //LegendGuard sets a bool to make sure if detective investigated already a corpse once 24-03-2021 +.bool karmastarted; //LegendGuard fixes with a bool when round start for karma points +.bool activekillerrole; //LegendGuard sets a variable to active killer role 25-03-2021 \ No newline at end of file diff --git a/qcsrc/common/gamemodes/gamemode/ttt/_mod.inc b/qcsrc/common/gamemodes/gamemode/ttt/_mod.inc deleted file mode 100644 index 5574f4aaf..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/_mod.inc +++ /dev/null @@ -1,8 +0,0 @@ -// generated file; do not modify -#include -#ifdef CSQC - #include -#endif -#ifdef SVQC - #include -#endif diff --git a/qcsrc/common/gamemodes/gamemode/ttt/_mod.qh b/qcsrc/common/gamemodes/gamemode/ttt/_mod.qh deleted file mode 100644 index 09983d725..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/_mod.qh +++ /dev/null @@ -1,8 +0,0 @@ -// generated file; do not modify -#include -#ifdef CSQC - #include -#endif -#ifdef SVQC - #include -#endif diff --git a/qcsrc/common/gamemodes/gamemode/ttt/cl_ttt.qc b/qcsrc/common/gamemodes/gamemode/ttt/cl_ttt.qc deleted file mode 100644 index 8820cf1e1..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/cl_ttt.qc +++ /dev/null @@ -1,103 +0,0 @@ -#include "cl_ttt.qh" - -#include -#include - -void HUD_Mod_TTT(vector pos, vector mySize) -{ - mod_active = 1; // ttt should always show the mod HUD - - int mystatus = entcs_receiver(player_localnum).ttt_status; - string player_text = ""; - vector player_color = '1 1 1'; - //string player_icon = ""; - if(mystatus == TTT_STATUS_TRAITOR) - { - player_text = _("Traitor"); - player_color = '1 0 0'; - //player_icon = "player_red"; - } - else if(mystatus == TTT_STATUS_INNOCENT) - { - player_text = _("Innocent"); - player_color = '0 1 0'; - //player_icon = "player_neutral"; - } - else if(mystatus == TTT_STATUS_DETECTIVE) - { - player_text = _("Detective"); - player_color = '0 0 1'; - //player_icon = "player_blue"; - } - else - { - // if the player has no valid status, don't draw anything - return; - } - - string time_text = string_null; - vector timer_color = '1 1 1'; - if(!STAT(GAME_STOPPED) && !warmup_stage && STAT(TTT_ROUNDTIMER) > 0) - { - float timeleft = max(0, STAT(TTT_ROUNDTIMER) - time); - timeleft = ceil(timeleft); - float minutesLeft = floor(timeleft / 60); - time_text = seconds_tostring(timeleft); - if(intermission_time || minutesLeft >= 5 || warmup_stage || STAT(TTT_ROUNDTIMER) == 0) - timer_color = '1 1 1'; //white - else if(minutesLeft >= 1) - timer_color = '1 1 0'; //yellow - else - timer_color = '1 0 0'; //red - } - - //drawpic_aspect_skin(pos, player_icon, vec2(0.5 * mySize.x, mySize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - if(!time_text) - drawstring_aspect(pos, player_text, vec2(mySize.x, mySize.y), player_color, panel_fg_alpha, DRAWFLAG_NORMAL); - else - { - drawstring_aspect(pos, player_text, vec2(0.5 * mySize.x, mySize.y), player_color, panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(pos + eX * (0.5 * mySize.x), time_text, vec2(0.5 * mySize.x, mySize.y), timer_color, panel_fg_alpha, DRAWFLAG_NORMAL); - } -} - -REGISTER_MUTATOR(cl_ttt, true); - -MUTATOR_HOOKFUNCTION(cl_ttt, ForcePlayercolors_Skip, CBC_ORDER_LAST) -{ - if(!ISGAMETYPE(TTT)) - return false; - - entity player = M_ARGV(0, entity); - entity e = entcs_receiver(player.entnum - 1); - int otherplayer_status = ((e) ? e.ttt_status : 0); - int mystatus = entcs_receiver(player_localnum).ttt_status; - - int plcolor = TTT_COLOR_INNOCENT; // default to innocent - - if((mystatus == TTT_STATUS_TRAITOR || intermission || STAT(GAME_STOPPED)) && otherplayer_status == TTT_STATUS_TRAITOR) - { - plcolor = TTT_COLOR_TRAITOR; - } - - //LegendGuard adds INNOCENT part 21-02-2021 - if((mystatus == TTT_STATUS_INNOCENT || intermission || STAT(GAME_STOPPED)) && otherplayer_status == TTT_STATUS_INNOCENT) - { - plcolor = TTT_COLOR_INNOCENT; - } - - //LegendGuard adds if sentence for Detective model color which will shown for everyone 21-02-2021 - if (otherplayer_status == TTT_STATUS_DETECTIVE) - { - plcolor = TTT_COLOR_DETECTIVE; - } - - player.colormap = 1024 + plcolor; - return true; -} - -MUTATOR_HOOKFUNCTION(cl_ttt, DrawScoreboard_Force) -{ - // show the scoreboard when the round ends, so players can see who the traitor was - return STAT(GAME_STOPPED); -} diff --git a/qcsrc/common/gamemodes/gamemode/ttt/cl_ttt.qh b/qcsrc/common/gamemodes/gamemode/ttt/cl_ttt.qh deleted file mode 100644 index 508ddf2ac..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/cl_ttt.qh +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void HUD_Mod_TTT(vector pos, vector mySize); diff --git a/qcsrc/common/gamemodes/gamemode/ttt/sv_ttt.qc b/qcsrc/common/gamemodes/gamemode/ttt/sv_ttt.qc deleted file mode 100644 index 63c8e66b3..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/sv_ttt.qc +++ /dev/null @@ -1,942 +0,0 @@ -#include "sv_ttt.qh" - -//set g_ttt_detective_count 0.125 "number of players who will become detectives, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players" -//float autocvar_g_ttt_detective_count = 0.125; //I don't think that it won't be used... -float autocvar_g_ttt_innocent_count = 0.625; -//float autocvar_g_ttt_traitor_count = 0.25; -float autocvar_g_ttt_round_timelimit = 180; -float autocvar_g_ttt_warmup = 10; -bool autocvar_g_ttt_punish_teamkill = false; -bool autocvar_g_ttt_reward_innocent = true; -bool autocvar_g_ttt_reward_detective = true; //detective reward if investigated corpses -float autocvar_g_ttt_max_karma_points = 1000; //LegendGuard sets Karma points 21-02-2021 -float autocvar_g_ttt_min_karma_points = 400; -int autocvar_g_ttt_karma_bankick_tool = 0; //LegendGuard sets a ban tool for server admins 11-03-2021 -float autocvar_g_ttt_karma_bantime = 1800; //karma ban seconds -bool autocvar_g_ttt_karma_damageactive = true; //LegendGuard sets Karma damage setting if active 20-03-2021 - -// 27-02-2021 -// Ideas: -// Add for the corpse a role of who killed 22-03-2021 - -// Detective is a created team, this team is added inside Innocents team - -//TODO: -// detective shouldn't be attacked by innocent bots - -void ttt_FakeTimeLimit(entity e, float t) -{ - if(!IS_REAL_CLIENT(e)) - return; -#if 0 - msg_entity = e; - WriteByte(MSG_ONE, 3); // svc_updatestat - WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT - if(t < 0) - WriteCoord(MSG_ONE, autocvar_timelimit); - else - WriteCoord(MSG_ONE, (t + 1) / 60); -#else - STAT(TTT_ROUNDTIMER, e) = t; -#endif -} - -void nades_Clear(entity player); - -void karma_Control(entity it) -{ - float masksize = autocvar_g_ban_default_masksize; - float bantime = autocvar_g_ttt_karma_bantime; - if(it.karmapoints >= autocvar_g_ttt_max_karma_points) - { - //Resets karmapoints to maintain the maximum - //PrintToChatAll("^3REWARD ^1MAXIMUM RESET"); - GameRules_scoring_add(it, TTT_KARMA, autocvar_g_ttt_max_karma_points - it.karmapoints); - it.karmapoints = autocvar_g_ttt_max_karma_points; - } - else if(it.karmapoints <= autocvar_g_ttt_min_karma_points) - { - switch (autocvar_g_ttt_karma_bankick_tool) - { - case 0: - { - //force to spec - PutObserverInServer(it); - return; - } - case 1: - { - //kick - dropclient(it); - return; - } - case 2: - { - //ban and kick - Ban_KickBanClient(it, bantime, masksize, "Too low karma"); - return; - } - default: - { - //force to spec - PutObserverInServer(it); - return; - } - } - } -} - -void karmaLoseDifference(entity attacker, entity target) -{ - //BASIC MATH THEORY: example: 1000 * 0.3 * (0.1 + 0.4) * 0.25 // karma points reduce when player attacked to other player - if (target.karmapoints < attacker.karmapoints) - { - float decreasekarma = - ( target.karmapoints * random() * ( 0.1 + random() ) * 0.25 ); - GameRules_scoring_add(attacker, TTT_KARMA, decreasekarma); - attacker.karmapoints = attacker.karmapoints + decreasekarma; - } - else if (target.karmapoints > attacker.karmapoints) - { - float decreasekarma = - ( target.karmapoints * random() * ( 0.1 + random() ) * 0.25 ); - GameRules_scoring_add(attacker, TTT_KARMA, decreasekarma); - attacker.karmapoints = attacker.karmapoints + decreasekarma; - } - else - { - float decreasekarma = - ( target.karmapoints * random() * ( 0.1 + random() ) * 0.25 ); - GameRules_scoring_add(attacker, TTT_KARMA, decreasekarma); - attacker.karmapoints = attacker.karmapoints + decreasekarma; - } -} - -void karmaWinDifference(entity it) -{ - GameRules_scoring_add(it, SCORE, 1); // reward innocents who make it to the end of the round time limit - float increasekarma = ( autocvar_g_ttt_min_karma_points * random() * ( 0.1 + random() ) * 0.12 ); - GameRules_scoring_add(it, TTT_KARMA, increasekarma); - it.karmapoints = it.karmapoints + increasekarma; -} - -void ttt_UpdateScores(bool timed_out) -{ - // give players their hard-earned kills now that the round is over - FOREACH_CLIENT(true, - { - it.totalfrags += it.ttt_validkills; - if(it.ttt_validkills) - { - GameRules_scoring_add(it, SCORE, it.ttt_validkills); - } - it.ttt_validkills = 0; - // player survived the round - if(IS_PLAYER(it) && !IS_DEAD(it)) // LegendGuard adds something for Karma 21-02-2021 - { - if((autocvar_g_ttt_reward_innocent && timed_out && it.ttt_status == TTT_STATUS_INNOCENT) - || (autocvar_g_ttt_reward_innocent && !timed_out && it.ttt_status == TTT_STATUS_INNOCENT)) - { - karmaWinDifference(it); - //PrintToChatAll(sprintf("^2REWARD ^7it.karmapoints: ^1%f", it.karmapoints)); - karma_Control(it); - } - - //Detective reward after investigated a corpse - if((autocvar_g_ttt_reward_detective && timed_out && it.ttt_status == TTT_STATUS_DETECTIVE) - || (autocvar_g_ttt_reward_detective && !timed_out && it.ttt_status == TTT_STATUS_DETECTIVE)) - { - if (it.investigated == true) - { - karmaWinDifference(it); - it.investigated = false; - } - karma_Control(it); - } - - if(it.ttt_status == TTT_STATUS_INNOCENT) - { - GameRules_scoring_add(it, TTT_RESISTS, 1); - karmaWinDifference(it); - //PrintToChatAll(sprintf("^2INNOCENT ^7it.karmapoints: ^1%f", it.karmapoints)); - karma_Control(it); - } - else if(it.ttt_status == TTT_STATUS_TRAITOR) - { - karmaWinDifference(it); - //PrintToChatAll(sprintf("^1TRAITOR ^7it.karmapoints: ^1%f", it.karmapoints)); - karma_Control(it); - } - } - }); -} - -float ttt_CheckWinner() -{ - if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) - { - // if the match times out, innocents win too! - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_TTT_INNOCENT_WIN); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TTT_INNOCENT_WIN); - FOREACH_CLIENT(true, - { - if(IS_PLAYER(it)) - nades_Clear(it); - ttt_FakeTimeLimit(it, -1); - }); - - ttt_UpdateScores(true); - - allowed_to_spawn = false; - game_stopped = true; - round_handler_Init(5, autocvar_g_ttt_warmup, autocvar_g_ttt_round_timelimit); - return 1; - } - - int innocent_count = 0, traitor_count = 0, detective_count = 0; - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), - { - if(it.ttt_status == TTT_STATUS_INNOCENT) - innocent_count++; - else if(it.ttt_status == TTT_STATUS_TRAITOR) - traitor_count++; - else if(it.ttt_status == TTT_STATUS_DETECTIVE) //LegendGuard adds detective_count 20-02-2021 - detective_count++; - }); - if(innocent_count > 0 && traitor_count > 0) - { - return 0; - } - - if(traitor_count > 0) // traitors win - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_TTT_TRAITOR_WIN); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TTT_TRAITOR_WIN); - } - else if(innocent_count > 0) // innocents win - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_TTT_INNOCENT_WIN); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TTT_INNOCENT_WIN); - } - else if (detective_count > 0 && innocent_count > 0) // detectives are same as innocents win - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_TTT_INNOCENT_WIN); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TTT_INNOCENT_WIN); - } - else - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); - } - - ttt_UpdateScores(false); - - allowed_to_spawn = false; - game_stopped = true; - round_handler_Init(5, autocvar_g_ttt_warmup, autocvar_g_ttt_round_timelimit); - - FOREACH_CLIENT(true, - { - if(IS_PLAYER(it)) - { - it.respawn_flags = RESPAWN_SILENT; //CSQC print output respawn lib.qh error fix - nades_Clear(it); - } - ttt_FakeTimeLimit(it, -1); - }); - - return 1; -} - -void ttt_RoundStart() -{ - allowed_to_spawn = boolean(warmup_stage); - int playercount = 0; - - FOREACH_CLIENT(true, - { - if(IS_PLAYER(it) && !IS_DEAD(it)) - { - ++playercount; - it.ttt_status = TTT_STATUS_INNOCENT; - } - else - it.ttt_status = 0; // this is mostly a safety check; if a client manages to somehow maintain a ttt status, clear it before the round starts! - it.ttt_validkills = 0; - }); - - int innocent_count = bound(1, ((autocvar_g_ttt_innocent_count >= 1) ? autocvar_g_ttt_innocent_count : floor(playercount * autocvar_g_ttt_innocent_count)), playercount - 1); // 20%, but ensure at least 1 and less than total - int total_innocents = 0; - //int traitor_count = bound(1, ((autocvar_g_ttt_traitor_count >= 1) ? autocvar_g_ttt_traitor_count : floor(playercount * autocvar_g_ttt_traitor_count)), playercount - 1); // 20%, but ensure at least 1 and less than total - int total_traitors = 0; - //int detective_count = bound(1, ((autocvar_g_ttt_detective_count >= 1) ? autocvar_g_ttt_detective_count : floor(playercount * autocvar_g_ttt_detective_count)), playercount - 1); // 20%, but ensure at least 1 and less than total - int total_detectives = 0; - - //innocents TOTAL - FOREACH_CLIENT_RANDOM(IS_PLAYER(it) && !IS_DEAD(it), - { - if(total_innocents >= innocent_count) - break; - //LegendGuard fixes the round start again 22-03-2021 - total_innocents++; - if (total_innocents <= 1) - { - if (total_traitors <= 1) - { - total_traitors++; - it.ttt_status = TTT_STATUS_TRAITOR; - } - } - else if (total_innocents == 2) - { - if (total_detectives >= 1) - break; - else - { - total_detectives++; - it.ttt_status = TTT_STATUS_DETECTIVE; - } - } - else if (total_innocents == 5) - { - if (total_detectives >= 2) - break; - else - { - total_detectives++; - it.ttt_status = TTT_STATUS_DETECTIVE; - } - } - else if (total_innocents >= 7) - { - if (total_detectives >= 3) - break; - else if (total_traitors == 3) - { - total_traitors++; - it.ttt_status = TTT_STATUS_TRAITOR; - } - else - { - total_detectives++; - it.ttt_status = TTT_STATUS_DETECTIVE; - } - } - else - it.ttt_status = TTT_STATUS_TRAITOR; - }); - - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), - { - float totalmeankarma = ((autocvar_g_ttt_max_karma_points + autocvar_g_ttt_min_karma_points + it.karmapoints) / 3); - karma_Control(it); - - if(it.ttt_status == TTT_STATUS_INNOCENT) - { - SetResource(it, RES_SHELLS, 50); - SetResource(it, RES_BULLETS, 70); - SetResource(it, RES_ROCKETS, 30); - SetResource(it, RES_CELLS, 60); - if (it.karmapoints <= totalmeankarma) - { - centerprint(it, strcat(BOLD_OPERATOR, "^1KARMA WARNING!\n^3Here, have the Rifle!")); - GiveWeapon(it, WEP_RIFLE.m_id, OP_PLUS, 1); - } - //Gives Mine Layer weapon to the player - GiveWeapon(it, WEP_MINE_LAYER.m_id, OP_PLUS, 1); - Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_TTT_INNOCENT); - Send_Notification(NOTIF_ONE_ONLY, it, MSG_INFO, INFO_TTT_INNOCENT); - //PrintToChatAll(sprintf("^1DEBUG^7: %s is ^2Innocent^7!", it.netname)); - } - else if(it.ttt_status == TTT_STATUS_TRAITOR) - { - SetResource(it, RES_SHELLS, 20); - SetResource(it, RES_BULLETS, 60); - SetResource(it, RES_ROCKETS, 20); - SetResource(it, RES_CELLS, 40); - if (it.karmapoints <= totalmeankarma) - { - centerprint(it, strcat(BOLD_OPERATOR, "^1KARMA WARNING!\n^3Here, have the Rifle!")); - GiveWeapon(it, WEP_RIFLE.m_id, OP_PLUS, 1); - } - //Gives Mine Layer weapon to the player - GiveWeapon(it, WEP_MINE_LAYER.m_id, OP_PLUS, 1); - Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_TTT_TRAITOR); - Send_Notification(NOTIF_ONE_ONLY, it, MSG_INFO, INFO_TTT_TRAITOR); - //PrintToChatAll(sprintf("^1DEBUG^7: %s is ^1Traitor^7!", it.netname)); - } - else if(it.ttt_status == TTT_STATUS_DETECTIVE) - { - SetResource(it, RES_ROCKETS, 20); - if (it.karmapoints <= totalmeankarma) - { - centerprint(it, strcat(BOLD_OPERATOR, "^1KARMA WARNING!\n^3Here, have the Rifle!")); - GiveWeapon(it, WEP_RIFLE.m_id, OP_PLUS, 1); - } - //Gives Shockwave and Mine Layer weapon to the player - GiveWeapon(it, WEP_SHOCKWAVE.m_id, OP_PLUS, 1); - GiveWeapon(it, WEP_MINE_LAYER.m_id, OP_PLUS, 1); - Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_TTT_DETECTIVE); - Send_Notification(NOTIF_ONE_ONLY, it, MSG_INFO, INFO_TTT_DETECTIVE); - PrintToChatAll(sprintf("%s is ^4Detective^7!", it.netname)); - } - ttt_FakeTimeLimit(it, round_handler_GetEndTime()); - }); -} - -bool ttt_CheckPlayers() -{ - static int prev_missing_players; - allowed_to_spawn = true; - int playercount = 0; - - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), - { - //PrintToChatAll(sprintf("it.karmapoints ^5begin: ^3%f",it.karmapoints)); - //Karma points start - if (it.karmastarted != true) - { - GameRules_scoring_add(it, TTT_KARMA, autocvar_g_ttt_max_karma_points - it.karmapoints); - it.karmapoints = autocvar_g_ttt_max_karma_points; - it.karmastarted = true; - } - karma_Control(it); - ++playercount; - //PrintToChatAll(sprintf("it.karmapoints ^6end: ^3%f",it.karmapoints)); - }); - - if (playercount >= 2) - { - if(prev_missing_players > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS); - prev_missing_players = -1; - return true; - } - - if(playercount == 0) - { - if(prev_missing_players > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS); - prev_missing_players = -1; - return false; - } - - // if we get here, only 1 player is missing - if(prev_missing_players != 1) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_PLAYERS, 1); - prev_missing_players = 1; - } - return false; -} - -bool ttt_isEliminated(entity e) -{ - if(e.caplayer == 1 && (IS_DEAD(e) || e.frags == FRAGS_PLAYER_OUT_OF_GAME)) - return true; - if(e.caplayer == 0.5) - return true; - return false; -} - -void ttt_Initialize() // run at the start of a match, initiates game mode -{ - GameRules_scoring(0, SFL_SORT_PRIO_PRIMARY, 0, { - field(SP_TTT_RESISTS, "resists", 0); - field(SP_TTT_KARMA, "karma", SFL_SORT_PRIO_SECONDARY); //LegendGuard adds Karma points in the scoreboard 22-02-2021 - }); - - allowed_to_spawn = true; - round_handler_Spawn(ttt_CheckPlayers, ttt_CheckWinner, ttt_RoundStart); - round_handler_Init(5, autocvar_g_ttt_warmup, autocvar_g_ttt_round_timelimit); - EliminatedPlayers_Init(ttt_isEliminated); -} - -void checkWeaponDeathtype(entity target, float deathtype) -{ - switch (deathtype) - { - case WEP_ARC.m_id: case 276: case 788: target.killedwithweapon = "Impacted by the Arc's electric shock"; return; - case WEP_BLASTER.m_id: case 513: target.killedwithweapon = "Blasted by the Blaster"; return; - case WEP_CRYLINK.m_id: case 263: case 519: target.killedwithweapon = "Shot by the Crylink"; return; - case WEP_DEVASTATOR.m_id: case 522: target.killedwithweapon = "Bombarded by the Devastator"; return; - case WEP_ELECTRO.m_id: case 262: case 518: case 1542: target.killedwithweapon = "Electrocuted by the Electro"; return; - case WEP_FIREBALL.m_id: case 273: case 529: case 1297: target.killedwithweapon = "Burned by the Fireball"; return; - case WEP_HAGAR.m_id: target.killedwithweapon = "Gunned by the Hagar"; return; - case WEP_HOOK.m_id: case 1805: target.killedwithweapon = "Caught in Hook gravity bomb"; return; - case WEP_MACHINEGUN.m_id: target.activekillerrole = true; target.killedwithweapon = "Riddled full of holes by the Machine Gun"; return; - case WEP_MINE_LAYER.m_id: target.killedwithweapon = "Exploited by the Mine Layer"; return; - case WEP_MORTAR.m_id: case 516: case 1284: target.killedwithweapon = "Blew up with the Mortar"; return; - case WEP_RIFLE.m_id: target.activekillerrole = true; target.killedwithweapon = "Sniped by the Rifle"; return; - case WEP_SEEKER.m_id: target.killedwithweapon = "Blasted by the Seeker"; return; - case WEP_SHOCKWAVE.m_id: target.killedwithweapon = "Gunned down by the Shockwave"; return; - case 275: target.killedwithweapon = "Knocked by the Shockwave"; return; - case WEP_SHOTGUN.m_id: target.activekillerrole = true; target.killedwithweapon = "Shot by Shotgun"; return; - case 258: target.killedwithweapon = "Knocked by the Shotgun"; return; - case WEP_TUBA.m_id: target.killedwithweapon = "Ear pain by the @!#%'n Tuba"; return; - case WEP_VAPORIZER.m_id: case 257: case 769: target.killedwithweapon = "Sniped by the Vaporizer"; return; - case WEP_VORTEX.m_id: target.killedwithweapon = "Sniped by the Vortex"; return; - case DEATH_FALL.m_id: target.killedwithweapon = "Fall"; return; - case DEATH_FIRE.m_id: target.killedwithweapon = "Burned with the fire"; return; - case DEATH_LAVA.m_id: target.killedwithweapon = "Burned in lava"; return; - case DEATH_MIRRORDAMAGE.m_id: target.killedwithweapon = "Suicide"; return; - case DEATH_SLIME.m_id: target.killedwithweapon = "Melted in slime"; return; - case DEATH_TELEFRAG.m_id: target.killedwithweapon = "Telefragged"; return; - default: target.killedwithweapon = "Unknown"; return; - } -} - -void ReduceKarmaPointsandFrags(entity frag_attacker, entity frag_target, float frag_deathtype, entity wep_ent) -{ - karmaLoseDifference(frag_attacker, frag_target); - GiveFrags(frag_attacker, frag_target, ((autocvar_g_ttt_punish_teamkill) ? -1 : -2), frag_deathtype, wep_ent.weaponentity_fld); - karma_Control(frag_attacker); - frag_target.whokilled = frag_attacker.netname; -} - -// ============== -// Hook Functions -// ============== - -MUTATOR_HOOKFUNCTION(ttt, ClientObituary) -{ - // LegendGuard's IDEA: To adjust the grade of severity of karma, - // we could add if sentence per weapons and adjust each weapon attack - // its own grade. Instead doing random decrease grade 22-02-2021 - - // in ttt, announcing a frag would tell everyone who the traitor is - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(IS_PLAYER(frag_attacker) && frag_attacker != frag_target) - { - float frag_deathtype = M_ARGV(3, float); - entity wep_ent = M_ARGV(4, entity); - - //PrintToChatAll(strcat("deathtype var: ", ftos(frag_deathtype))); - checkWeaponDeathtype(frag_target, frag_deathtype); - // "team" kill, a point is awarded to the player by default so we must take it away plus an extra one - // unless the player is going to be punished for suicide, in which case just remove one - if(frag_attacker.ttt_status == frag_target.ttt_status) - { - //PrintToChatAll("^1DEBUG^7: A ^2PLAYER^7 has fragged a ^2PLAYER OF HIS OWN TEAM^7, TOO BAD!"); - ReduceKarmaPointsandFrags(frag_attacker, frag_target, frag_deathtype, wep_ent); - switch (frag_attacker.ttt_status) - { - case TTT_STATUS_INNOCENT: frag_target.killerrole = "\n^3Killer role: ^2Innocent"; return; - case TTT_STATUS_TRAITOR: frag_target.killerrole = "\n^3Killer role: ^1Traitor"; return; - case TTT_STATUS_DETECTIVE: frag_target.killerrole = "\n^3Killer role: ^4Detective"; return; - default: return; - } - //PrintToChatAll(sprintf("frag_attacker.karmapoints: ^1%f", frag_attacker.karmapoints)); - } - - if(frag_attacker.ttt_status == TTT_STATUS_DETECTIVE) - { - if (frag_target.ttt_status == TTT_STATUS_INNOCENT || frag_target.ttt_status == TTT_STATUS_DETECTIVE) - { - //PrintToChatAll("^1DEBUG^7: A ^4Detective^7 fragged an ^2Innocent^7/^4Detective^7, TOO BAD!"); - ReduceKarmaPointsandFrags(frag_attacker, frag_target, frag_deathtype, wep_ent); - frag_target.killerrole = "\n^3Killer role: ^4Detective"; - //PrintToChatAll(sprintf("frag_attacker.karmapoints: ^1%f", frag_attacker.karmapoints)); - } - else - { - frag_target.whokilled = frag_attacker.netname; - frag_target.killerrole = "\n^3Killer role: ^4Detective"; - } - } - - if (frag_attacker.ttt_status == TTT_STATUS_INNOCENT) - { - if (frag_target.ttt_status == TTT_STATUS_DETECTIVE) - { - //PrintToChatAll("^1DEBUG^7: An ^2Innocent^7 fragged a ^4Detective^7, TOO BAD!"); - ReduceKarmaPointsandFrags(frag_attacker, frag_target, frag_deathtype, wep_ent); - frag_target.killerrole = "\n^3Killer role: ^2Innocent"; - } - else - { - frag_target.whokilled = frag_attacker.netname; - frag_target.killerrole = "\n^3Killer role: ^2Innocent"; - } - } - - if (frag_attacker.ttt_status == TTT_STATUS_TRAITOR) - { - if (frag_target.ttt_status == TTT_STATUS_INNOCENT) - { - frag_target.whokilled = frag_attacker.netname; - frag_target.killerrole = "\n^3Killer role: ^1Traitor"; - } - else - { - frag_target.whokilled = frag_attacker.netname; - frag_target.killerrole = "\n^3Killer role: ^1Traitor"; - } - } - //if ttt_status is 1, means innocent, 2 means traitor, 3 means detective, TODO: the bots: frag_attacker(1) shouldn't attack to frag_target(3) - //PrintToChatAll(sprintf("^1DEBUG^7: frag_attacker.ttt_status is ^3%s^7",ftos(frag_attacker.ttt_status))); - //PrintToChatAll(sprintf("^1DEBUG^7: frag_target.ttt_status is ^3%s^7",ftos(frag_target.ttt_status))); - } - - //TODO: try to do a "find out" if a detective can see who fragged to who if possible 21-02-2021 - M_ARGV(5, bool) = true; // anonymous attacker -} - -//karma weapon damage, halve the damage attack when player has low karma 20-03-2021 -MUTATOR_HOOKFUNCTION(ttt, Damage_Calculate) -{ - entity attacker = M_ARGV(1, entity); - entity target = M_ARGV(2, entity); - float deathtype = M_ARGV(3, float); - float damage = M_ARGV(4, float); - vector force = M_ARGV(6, vector); - string corpsemessagestrcat = ""; - - if (autocvar_g_ttt_karma_damageactive != false) - { - if (IS_PLAYER(attacker)) - { - if(target == attacker) // damage done to yourself - { - damage /= autocvar_g_weapondamagefactor / (attacker.karmapoints / autocvar_g_ttt_max_karma_points); - force /= autocvar_g_weaponforcefactor / (attacker.karmapoints / autocvar_g_ttt_max_karma_points); - } - else if (target != attacker) - { - damage /= autocvar_g_weapondamagefactor / (attacker.karmapoints / autocvar_g_ttt_max_karma_points); - force /= autocvar_g_weaponforcefactor / (attacker.karmapoints / autocvar_g_ttt_max_karma_points); - } - else - { - damage *= autocvar_g_weapondamagefactor; - force *= autocvar_g_weaponforcefactor; - } - } - } - - //DETECTIVE CORPSE DETECTION SKILL 21-03-2021 - if (attacker.ttt_status == TTT_STATUS_DETECTIVE) - { - if(IS_DEAD(target)) - { - //Shockwave weapon as radar gun to check the corpses 22-03-2021 - if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE)) - { - if (target.killedwithweapon == "") - target.killedwithweapon = "UNKNOWN CAUSE"; - - if (target.activekillerrole != true) - target.killerrole = ""; - - string killedbyphrase = strcat("\n^3Killed by:^7 ", target.whokilled, target.killerrole); - string wepkilledphrase = strcat("\n^3Cause:^7 ", target.killedwithweapon); - if (target.whokilled == "") - { - killedbyphrase = ""; - if (target.killedwithweapon == "") - wepkilledphrase = "\n^3Cause:^7 UNCLEAR"; - } - - damage = 0; - force = '0 0 0'; - if (target.ttt_status == TTT_STATUS_INNOCENT) - { - //try to add centerprint message for chat privately if possible - corpsemessagestrcat = strcat("\n^3Name:^7 ", target.netname, "\n^3Role: ^2Innocent", killedbyphrase, wepkilledphrase); - centerprint(attacker, strcat(BOLD_OPERATOR, corpsemessagestrcat));//("\n^6Name^3:^7 ", target.netname, "\n^5Role^3: ^2Innocent\n", "^1Killed by^3:^7 ", target.whokilled))); - } - else if (target.ttt_status == TTT_STATUS_TRAITOR) - { - corpsemessagestrcat = strcat("\n^3Name:^7 ", target.netname, "\n^3Role: ^1Traitor", killedbyphrase, wepkilledphrase); - centerprint(attacker, strcat(BOLD_OPERATOR, corpsemessagestrcat));//("\n^6Name^3:^7 ", target.netname, "\n^5Role^3: ^1Traitor\n", "^1Killed by^3:^7 ", target.whokilled))); - } - else if (target.ttt_status == TTT_STATUS_DETECTIVE) - { - corpsemessagestrcat = strcat("\n^3Name:^7 ", target.netname, "\n^3Role: ^4Detective", killedbyphrase, wepkilledphrase); - centerprint(attacker, strcat(BOLD_OPERATOR, corpsemessagestrcat));//("\n^6Name^3:^7 ", target.netname, "\n^5Role^3: ^4Detective\n", "^1Killed by^3:^7 ", target.whokilled))); - } - attacker.investigated = true; - } - } - } - - M_ARGV(4, float) = damage; - M_ARGV(6, vector) = force; -} - -MUTATOR_HOOKFUNCTION(ttt, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(IS_PLAYER(player) || player.caplayer) - { - // update the scoreboard colour display to out the real killer at the end of the round - // running this every frame to avoid cheats - int plcolor = TTT_COLOR_INNOCENT; - if(player.ttt_status == TTT_STATUS_INNOCENT && game_stopped) //Innocent status by default - plcolor = TTT_COLOR_INNOCENT; - if(player.ttt_status == TTT_STATUS_TRAITOR && game_stopped) - plcolor = TTT_COLOR_TRAITOR; - //LegendGuard adds for Detective 21-02-2021 - if(player.ttt_status == TTT_STATUS_DETECTIVE)// && game_stopped) - plcolor = TTT_COLOR_DETECTIVE; - setcolor(player, plcolor); - } - - //CORPSE FEATURE 10-03-2021 - if (IS_DEAD(player)) - { - player.event_damage = func_null; - //player.health = 0; - player.solid = SOLID_CORPSE; - set_movetype(player, MOVETYPE_STEP); //test with MOVETYPE_TOSS or MOVETYPE_WALK (it's like sliding object) or MOVETYPE_BOUNCE (maybe not good) - } -} - -MUTATOR_HOOKFUNCTION(ttt, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.ttt_status = 0; - player.ttt_validkills = 0; - player.caplayer = 1; - if (!warmup_stage) - eliminatedPlayers.SendFlags |= 1; -} - -MUTATOR_HOOKFUNCTION(ttt, ForbidSpawn) -{ - entity player = M_ARGV(0, entity); - - // spectators / observers that weren't playing can join; they are - // immediately forced to observe in the PutClientInServer hook - // this way they are put in a team and can play in the next round - if (!allowed_to_spawn && player.caplayer) - return true; - return false; -} - -MUTATOR_HOOKFUNCTION(ttt, PutClientInServer) -{ - entity player = M_ARGV(0, entity); - - if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join - { - TRANSMUTE(Observer, player); - if (CS(player).jointime != time && !player.caplayer) // not when connecting - { - player.caplayer = 0.5; - Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE); - } - } -} - -MUTATOR_HOOKFUNCTION(ttt, reset_map_players) -{ - FOREACH_CLIENT(true, { - CS(it).killcount = 0; - it.ttt_status = 0; - ttt_FakeTimeLimit(it, -1); // restore original timelimit - if (!it.caplayer && IS_BOT_CLIENT(it)) - it.caplayer = 1; - if (it.caplayer) - { - TRANSMUTE(Player, it); - it.caplayer = 1; - it.respawn_flags = RESPAWN_SILENT; //CSQC print output respawn lib.qh error fix - PutClientInServer(it); - } - }); - bot_relinkplayerlist(); - return true; -} - -MUTATOR_HOOKFUNCTION(ttt, reset_map_global) -{ - allowed_to_spawn = true; - return true; -} - -entity ttt_LastPlayerForTeam(entity this) -{ - entity last_pl = NULL; - FOREACH_CLIENT(IS_PLAYER(it) && it != this, { - if (!IS_DEAD(it) && this.ttt_status == it.ttt_status) - { - if (!last_pl) - { - last_pl = it; - } - else - return NULL; - } - }); - return last_pl; -} - -void ttt_LastPlayerForTeam_Notify(entity this) -{ - if (!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) - { - entity pl = ttt_LastPlayerForTeam(this); - if (pl) - Send_Notification(NOTIF_ONE_ONLY, pl, MSG_CENTER, CENTER_ALONE); - } -} - -MUTATOR_HOOKFUNCTION(ttt, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - //float frag_deathtype = M_ARGV(3, float); - - ttt_LastPlayerForTeam_Notify(frag_target); - if (!allowed_to_spawn) - { - frag_target.respawn_flags = RESPAWN_DENY; - // prevent unwanted sudden rejoin as spectator and movement of spectator camera - frag_target.respawn_time = time + 2; - } - frag_target.respawn_flags |= RESPAWN_DENY; - frag_target.event_damage = func_null; - frag_target.health = 0; - - if (!warmup_stage) - { - eliminatedPlayers.SendFlags |= 1; - if (IS_BOT_CLIENT(frag_target)) - bot_clear(frag_target); - } - - //if(frag_attacker.ttt_status == frag_target.ttt_status) - // killed an ally! punishment is sentenced - if(frag_attacker.ttt_status == TTT_STATUS_DETECTIVE) - { - if (frag_target.ttt_status == TTT_STATUS_INNOCENT) - { - //PrintToChatAll("^1DEBUG^7: ^4DETECTIVE ^1DAMAGE/DEAD^7 HAS TAKEN!"); - //30 damage points deal - Damage(frag_attacker, frag_attacker, frag_attacker, 30, DEATH_MIRRORDAMAGE.m_id, DMG_NOWEP, frag_attacker.origin, '0 0 0'); - } - } - if (frag_attacker.ttt_status == TTT_STATUS_INNOCENT) - { - if (frag_target.ttt_status == TTT_STATUS_DETECTIVE) - { - //PrintToChatAll("^1DEBUG^7: ^2INNOCENT ^1DAMAGE/DEAD^7 HAS TAKEN!"); - //30 damage points deal - Damage(frag_attacker, frag_attacker, frag_attacker, 30, DEATH_MIRRORDAMAGE.m_id, DMG_NOWEP, frag_attacker.origin, '0 0 0'); - } - } - return true; -} - -MUTATOR_HOOKFUNCTION(ttt, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - if (IS_PLAYER(player) && !IS_DEAD(player)) - ttt_LastPlayerForTeam_Notify(player); - return true; -} - -MUTATOR_HOOKFUNCTION(ttt, MakePlayerObserver) -{ - // LegendGuard, here is where spectators shouldn't talk to any players to say the hints or who is who 21-10-2021 - entity player = M_ARGV(0, entity); - - if (IS_PLAYER(player) && !IS_DEAD(player)) - ttt_LastPlayerForTeam_Notify(player); - if (player.killindicator_teamchange == -2) // player wants to spectate - player.caplayer = 0; - if (player.caplayer) - player.frags = FRAGS_PLAYER_OUT_OF_GAME; - if (!warmup_stage) - eliminatedPlayers.SendFlags |= 1; - if (!player.caplayer) - { - player.ttt_validkills = 0; - player.ttt_status = 0; - ttt_FakeTimeLimit(player, -1); // restore original timelimit - return false; // allow team reset - } - return true; // prevent team reset -} - -MUTATOR_HOOKFUNCTION(ttt, Scores_CountFragsRemaining) -{ - // announce remaining frags? - return true; -} - -MUTATOR_HOOKFUNCTION(ttt, GiveFragsForKill, CBC_ORDER_FIRST) -{ - entity frag_attacker = M_ARGV(0, entity); - if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) - frag_attacker.ttt_validkills += M_ARGV(2, float); - M_ARGV(2, float) = 0; // score will be given to the winner when the round ends - return true; -} - -MUTATOR_HOOKFUNCTION(ttt, AddPlayerScore) -{ - // add scorefield for scoreboard here - entity scorefield = M_ARGV(0, entity); - if(scorefield == SP_KILLS || scorefield == SP_DEATHS || scorefield == SP_SUICIDES || scorefield == SP_DMG || scorefield == SP_DMGTAKEN) - M_ARGV(1, float) = 0; // don't report that the player has killed or been killed, that would out them as a traitor! -} - -MUTATOR_HOOKFUNCTION(ttt, CalculateRespawnTime) -{ - // no respawn calculations needed, player is forced to spectate anyway - return true; -} - -MUTATOR_HOOKFUNCTION(ttt, Bot_FixCount, CBC_ORDER_EXCLUSIVE) -{ - FOREACH_CLIENT(IS_REAL_CLIENT(it), { - if (IS_PLAYER(it) || it.caplayer == 1) - ++M_ARGV(0, int); - ++M_ARGV(1, int); - }); - return true; -} - -MUTATOR_HOOKFUNCTION(ttt, ClientCommand_Spectate) -{ - entity player = M_ARGV(0, entity); - - if (player.caplayer) - { - // they're going to spec, we can do other checks - if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player))) - Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE); - return MUT_SPECCMD_FORCE; - } - - return MUT_SPECCMD_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(ttt, GetPlayerStatus) -{ - entity player = M_ARGV(0, entity); - - return player.caplayer == 1; -} - -MUTATOR_HOOKFUNCTION(ttt, BotShouldAttack) -{ - //TODO: LegendGuard, try bots attack to innocents vs traitors, detectives must be on innocents team 21-02-2021 - entity bot = M_ARGV(0, entity); - entity targ = M_ARGV(1, entity); - - if(targ.ttt_status == bot.ttt_status) - { - return true; - } - - if(targ.ttt_status == TTT_STATUS_DETECTIVE) - { - if(bot.ttt_status == TTT_STATUS_INNOCENT) - return false; - } -} diff --git a/qcsrc/common/gamemodes/gamemode/ttt/sv_ttt.qh b/qcsrc/common/gamemodes/gamemode/ttt/sv_ttt.qh deleted file mode 100644 index 5046924f6..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/sv_ttt.qh +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -void ttt_Initialize(); - -REGISTER_MUTATOR(ttt, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - ttt_Initialize(); - } - return false; -} - -.int ttt_validkills; // store the player's valid kills to be given at the end of the match (avoid exposing their score until then) -.float karmapoints; //LegendGuard adds karma points to store player status 22-02-2021 -.string whokilled; //LegendGuard sets a variable to know who killed who 22-03-2021 -.string killerrole; //LegendGuard sets a variable to identify killer role 25-03-2021 -.string killedwithweapon; //LegendGuard sets a variable to know what cause provoked to the victim 23-03-2021 -.bool investigated; //LegendGuard sets a bool to make sure if detective investigated already a corpse once 24-03-2021 -.bool karmastarted; //LegendGuard fixes with a bool when round start for karma points -.bool activekillerrole; //LegendGuard sets a variable to active killer role 25-03-2021 \ No newline at end of file diff --git a/qcsrc/common/gamemodes/gamemode/ttt/ttt.qc b/qcsrc/common/gamemodes/gamemode/ttt/ttt.qc deleted file mode 100644 index 0e43e0925..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/ttt.qc +++ /dev/null @@ -1 +0,0 @@ -#include "ttt.qh" diff --git a/qcsrc/common/gamemodes/gamemode/ttt/ttt.qh b/qcsrc/common/gamemodes/gamemode/ttt/ttt.qh deleted file mode 100644 index 08c483173..000000000 --- a/qcsrc/common/gamemodes/gamemode/ttt/ttt.qh +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include - -#ifdef CSQC -void HUD_Mod_TTT(vector pos, vector mySize); -#endif -CLASS(TroubleinTerroristTown, Gametype) - INIT(TroubleinTerroristTown) - { - this.gametype_init(this, _("Trouble in Terrorist Town"),"ttt","g_ttt",GAMETYPE_FLAG_USEPOINTS,"","timelimit=30 pointlimit=20",_("A group of space terrorists have traitors among them. Traitors must kill terrorists, while the terrorists have to try to find and kill the traitors")); - } - METHOD(TroubleinTerroristTown, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) - { - return true; - } - METHOD(TroubleinTerroristTown, m_isForcedSupported, bool(Gametype this)) - { - if(!cvar("g_ttt_not_lms_maps")) - { - // if this is unset, all LMS maps support TroubleinTerroristTown too - if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS.m_flags)) - return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported) - } - return false; - } -#ifdef CSQC - ATTRIB(TroubleinTerroristTown, m_modicons, void(vector pos, vector mySize), HUD_Mod_TTT); -#endif -ENDCLASS(TroubleinTerroristTown) -REGISTER_GAMETYPE(TTT, NEW(TroubleinTerroristTown)); - -#ifdef GAMEQC -// shared state signalling the player's ttt status -//TODO: add Detective STATUS and COLOR 20-02-2021 -.int ttt_status; -const int TTT_STATUS_INNOCENT = 1; -const int TTT_STATUS_TRAITOR = 2; -const int TTT_STATUS_DETECTIVE = 3; - -// hardcoded player colors for ttt -const int TTT_COLOR_DETECTIVE = 221; // blue -const int TTT_COLOR_INNOCENT = 51; // green -const int TTT_COLOR_TRAITOR = 68; // red -#endif diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index 7a43de5a9..f192e5f11 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -439,17 +439,17 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_INFO_NOTIF(TEAMCHANGE_LARGERTEAM, N_CONSOLE, 0, 0, "", "", "", _("^BGYou cannot change to a larger team"), "") MSG_INFO_NOTIF(TEAMCHANGE_NOTALLOWED, N_CONSOLE, 0, 0, "", "", "", _("^BGYou are not allowed to change teams"), "") - //LegendGuard adds MSG_INFO_NOTIF for TTT 20-02-2021 + //LegendGuard adds MSG_INFO_NOTIF for MMM 20-02-2021 //LegendGuard adds N_CHATCON option 20-03-2021 - MSG_INFO_NOTIF(TTT_TRAITOR, N_CHATCON, 0, 0, "", "", "", _("^BGYou are ^K1Traitor^BG!"), "") - MSG_INFO_NOTIF(TTT_TRAITOR_WIN, N_CONSOLE, 0, 0, "", "", "", _("^K1Traitors^BG win the round"), "") + MSG_INFO_NOTIF(MMM_MURDERER, N_CHATCON, 0, 0, "", "", "", _("^BGYou are ^K1Murderer^BG!"), "") + MSG_INFO_NOTIF(MMM_MURDERER_WIN, N_CONSOLE, 0, 0, "", "", "", _("^K1Murderers^BG win the round"), "") //LegendGuard adds N_CHATCON option 20-03-2021 - MSG_INFO_NOTIF(TTT_INNOCENT, N_CHATCON, 0, 0, "", "", "", _("^BGYou are ^F1Innocent^BG!"), "") - MSG_INFO_NOTIF(TTT_INNOCENT_WIN, N_CONSOLE, 0, 0, "", "", "", _("^F1Innocents^BG win the round"), "") + MSG_INFO_NOTIF(MMM_CIVILIAN, N_CHATCON, 0, 0, "", "", "", _("^BGYou are ^F1Civilian^BG!"), "") + MSG_INFO_NOTIF(MMM_CIVILIAN_WIN, N_CONSOLE, 0, 0, "", "", "", _("^F1Civilians^BG win the round"), "") //LegendGuard adds N_CHATCON option 20-03-2021 - MSG_INFO_NOTIF(TTT_DETECTIVE, N_CHATCON, 0, 0, "", "", "", _("^BGYou are ^4Detective^BG!"), "") + MSG_INFO_NOTIF(MMM_SLEUTH, N_CHATCON, 0, 0, "", "", "", _("^BGYou are ^4Sleuth^BG!"), "") MSG_INFO_NOTIF(VERSION_BETA, N_CONSOLE, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have ^F2Xonotic %s"), "") MSG_INFO_NOTIF(VERSION_OLD, N_CHATCON, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s"), "") @@ -781,14 +781,14 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input != MSG_CENTER_NOTIF(TIMEOUT_BEGINNING, N_ENABLE, 0, 1, "", CPID_TIMEOUT, "1 f1", _("^F4Timeout begins in ^COUNT"), "") MSG_CENTER_NOTIF(TIMEOUT_ENDING, N_ENABLE, 0, 1, "", CPID_TIMEIN, "1 f1", _("^F4Timeout ends in ^COUNT"), "") - //LegendGuard adds MSG_CENTER_NOTIF for TTT 20-02-2021 - MSG_CENTER_NOTIF(TTT_TRAITOR, N_ENABLE, 0, 0, "", CPID_TTT, "5 0", strcat(BOLD_OPERATOR, _("^BGYou are ^K1Traitor^BG! Kill all the innocents without raising suspicion!")), "") - MSG_CENTER_NOTIF(TTT_TRAITOR_WIN, N_ENABLE, 0, 0, "", CPID_ROUND, "0 0", _("^K1Traitors^BG win the round"), "") + //LegendGuard adds MSG_CENTER_NOTIF for MMM 20-02-2021 + MSG_CENTER_NOTIF(MMM_MURDERER, N_ENABLE, 0, 0, "", CPID_MMM, "5 0", strcat(BOLD_OPERATOR, _("^BGYou are ^K1Murderer^BG! Kill all the civilians without raising suspicion!")), "") + MSG_CENTER_NOTIF(MMM_MURDERER_WIN, N_ENABLE, 0, 0, "", CPID_ROUND, "0 0", _("^K1Murderers^BG win the round"), "") - MSG_CENTER_NOTIF(TTT_INNOCENT, N_ENABLE, 0, 0, "", CPID_TTT, "5 0", strcat(BOLD_OPERATOR, _("^BGYou are ^F1Innocent^BG! Try to find out who are traitors and survive until time is up!")), "") - MSG_CENTER_NOTIF(TTT_INNOCENT_WIN, N_ENABLE, 0, 0, "", CPID_ROUND, "0 0", _("^F1Innocents^BG win the round"), "") + MSG_CENTER_NOTIF(MMM_CIVILIAN, N_ENABLE, 0, 0, "", CPID_MMM, "5 0", strcat(BOLD_OPERATOR, _("^BGYou are ^F1Civilian^BG! Try to find out who are murderers and survive until time is up!")), "") + MSG_CENTER_NOTIF(MMM_CIVILIAN_WIN, N_ENABLE, 0, 0, "", CPID_ROUND, "0 0", _("^F1Civilians^BG win the round"), "") - MSG_CENTER_NOTIF(TTT_DETECTIVE, N_ENABLE, 0, 0, "", CPID_TTT, "5 0", strcat(BOLD_OPERATOR, _("^BGYou are ^4Detective^BG! Find out who are traitors and protect the innocents!")), "") + MSG_CENTER_NOTIF(MMM_SLEUTH, N_ENABLE, 0, 0, "", CPID_MMM, "5 0", strcat(BOLD_OPERATOR, _("^BGYou are ^4Sleuth^BG! Find out who are murderers and protect the civilians!")), "") MSG_CENTER_NOTIF(JOIN_PREVENT_MINIGAME, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^K1Cannot join given minigame session!"), "" ) diff --git a/qcsrc/common/notifications/all.qh b/qcsrc/common/notifications/all.qh index 8c921055a..64b11c7be 100644 --- a/qcsrc/common/notifications/all.qh +++ b/qcsrc/common/notifications/all.qh @@ -40,7 +40,7 @@ string Get_Notif_TypeName(MSG net_type) LOG_WARNF("Get_Notif_TypeName(%d): Improper net type!", ORDINAL(net_type)); return ""; } -//LegendGuard adds CASE(CPID, TTT) after TIMEIN for TTT 20-02-2021 +//LegendGuard adds CASE(CPID, MMM) after TIMEIN for MMM 20-02-2021 ENUMCLASS(CPID) CASE(CPID, ASSAULT_ROLE) CASE(CPID, ROUND) @@ -72,7 +72,7 @@ ENUMCLASS(CPID) CASE(CPID, TEAMCHANGE) CASE(CPID, TIMEOUT) CASE(CPID, TIMEIN) - CASE(CPID, TTT) + CASE(CPID, MMM) CASE(CPID, VEHICLES) CASE(CPID, VEHICLES_OTHER) /** always last */ diff --git a/qcsrc/common/scores.qh b/qcsrc/common/scores.qh index c64eeaf89..8e1d8e61c 100644 --- a/qcsrc/common/scores.qh +++ b/qcsrc/common/scores.qh @@ -86,8 +86,7 @@ REGISTER_SP(NEXBALL_FAULTS); REGISTER_SP(ONS_TAKES); REGISTER_SP(ONS_CAPS); -REGISTER_SP(TTT_RESISTS); //LegendGuard adds REGISTER_SP for TTT 20-02-2021 //Innocents as number of suvivals -REGISTER_SP(TTT_KARMA); //LegendGuard adds REGISTER_SP for TTT Karma points 21-02-2021 +REGISTER_SP(MMM_KARMA); //LegendGuard adds REGISTER_SP for MMM Karma points 21-02-2021 #endif diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index 880b378c8..2ce953cd5 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -435,4 +435,4 @@ REGISTER_STAT(GUNALIGN, int) SPECTATE_COPYFIELD(_STAT(GUNALIGN)) #endif -REGISTER_STAT(TTT_ROUNDTIMER, float) //LegendGuard adds TTT_ROUNDTIMER for TTT 20-02-2021 +REGISTER_STAT(MMM_ROUNDTIMER, float) //LegendGuard adds MMM_ROUNDTIMER for MMM 20-02-2021 diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index 5a98716bd..39a26956b 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -683,8 +683,8 @@ float updateCompression() GAMETYPE(MAPINFO_TYPE_ASSAULT) \ /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \ /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \ - GAMETYPE(MAPINFO_TYPE_TTT) \ - //LegendGuard adds GAMETYPE for menu for TTT 20-02-2021 + GAMETYPE(MAPINFO_TYPE_MMM) \ + //LegendGuard adds GAMETYPE for menu for MMM 20-02-2021 /**/ // hidden gametypes come last so indexing always works correctly diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index 91cb13810..7f8d1e990 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -301,8 +301,8 @@ void cvar_changes_init() BADCVAR("g_tdm"); BADCVAR("g_tdm_on_dm_maps"); BADCVAR("g_tdm_teams"); - BADCVAR("g_ttt"); - BADCVAR("g_ttt_not_dm_maps"); + BADCVAR("g_mmm"); + BADCVAR("g_mmm_not_dm_maps"); BADCVAR("g_vip"); BADCVAR("leadlimit"); BADCVAR("nextmap"); @@ -312,7 +312,7 @@ void cvar_changes_init() BADCVAR("g_mapinfo_ignore_warnings"); BADCVAR("g_maplist_ignore_sizes"); BADCVAR("g_maplist_sizes_count_bots"); - //LegendGuard adds BADCVAR(g_*) for TTT 20-02-2021 + //LegendGuard adds BADCVAR(g_*) for MMM 20-02-2021 // long BADCVAR("hostname");