From d913e5e7ce05eb705516b6a4c9cb7e8109c041d4 Mon Sep 17 00:00:00 2001 From: Mikusch Date: Tue, 28 Mar 2023 00:40:35 +0200 Subject: [PATCH] Rework stun mechanics from upgrades (#31) * Reduce various stunning upgrade effects * Revert "Fix console warning spam from func_upgradestation" This reverts commit 66cc8a65648666ebbbbd7168f604bfe637915a89. * Some optimizations * Don't change initiator * Fix backstabs against RED players not working * Bump version --- addons/sourcemod/gamedata/mannvsmann.txt | 80 +++++++ addons/sourcemod/scripting/mannvsmann.sp | 24 +-- .../sourcemod/scripting/mannvsmann/convars.sp | 1 + .../sourcemod/scripting/mannvsmann/dhooks.sp | 196 ++++++++++++++++-- 4 files changed, 260 insertions(+), 41 deletions(-) diff --git a/addons/sourcemod/gamedata/mannvsmann.txt b/addons/sourcemod/gamedata/mannvsmann.txt index 19deea9..ccfb5b0 100644 --- a/addons/sourcemod/gamedata/mannvsmann.txt +++ b/addons/sourcemod/gamedata/mannvsmann.txt @@ -144,6 +144,21 @@ "linux" "@_ZN16CTFPowerupBottle12AllowedToUseEv" "windows" "\xA1\x2A\x2A\x2A\x2A\x57\x8B\xF9\x85\xC0\x74\x2A\x8B\x80\x88\x03\x00\x00" } + "CTFKnife::CanPerformBackstabAgainstTarget" + { + "linux" "@_ZN8CTFKnife31CanPerformBackstabAgainstTargetEP9CTFPlayer" + "windows" "\x55\x8B\xEC\x51\x56\x8B\x75\x08\x57\x8B\xF9\x85\xF6\x75\x2A\x5F" + } + "CTFBaseRocket::CheckForStunOnImpact" + { + "linux" "@_ZN13CTFBaseRocket20CheckForStunOnImpactEP9CTFPlayer" + "windows" "\x55\x8B\xEC\x83\xEC\x30\x53\x56\x8B\xF1\x57\x80\xBE\x01\x05\x00\x00\x00" + } + "CTFSniperRifle::ExplosiveHeadShot" + { + "linux" "@_ZN14CTFSniperRifle17ExplosiveHeadShotEP9CTFPlayerS1_" + "windows" "\x55\x8B\xEC\x81\xEC\xEC\x00\x00\x00\x53\x8B\x5D\x08" + } "UTIL_RemoveImmediate" { "linux" "@_Z20UTIL_RemoveImmediateP11CBaseEntity" @@ -172,6 +187,11 @@ "linux" "481" "windows" "474" } + "CTFStunBall::ApplyBallImpactEffectOnVictim" + { + "linux" "267" + "windows" "266" + } "CTFGameRules::SetWinningTeam" { "linux" "165" @@ -421,6 +441,52 @@ "return" "bool" "this" "entity" } + "CTFKnife::CanPerformBackstabAgainstTarget" + { + "signature" "CTFKnife::CanPerformBackstabAgainstTarget" + "callconv" "thiscall" + "return" "bool" + "this" "entity" + "arguments" + { + "pTarget" + { + "type" "cbaseentity" + } + } + } + "CTFBaseRocket::CheckForStunOnImpact" + { + "signature" "CTFBaseRocket::CheckForStunOnImpact" + "callconv" "thiscall" + "return" "void" + "this" "entity" + "arguments" + { + "pTarget" + { + "type" "cbaseentity" + } + } + } + "CTFSniperRifle::ExplosiveHeadShot" + { + "signature" "CTFSniperRifle::ExplosiveHeadShot" + "callconv" "thiscall" + "return" "void" + "this" "entity" + "arguments" + { + "pAttacker" + { + "type" "cbaseentity" + } + "pVictim" + { + "type" "cbaseentity" + } + } + } "CCurrencyPack::MyTouch" { "offset" "CCurrencyPack::MyTouch" @@ -478,6 +544,20 @@ } } } + "CTFStunBall::ApplyBallImpactEffectOnVictim" + { + "offset" "CTFStunBall::ApplyBallImpactEffectOnVictim" + "hooktype" "entity" + "return" "void" + "this" "entity" + "arguments" + { + "pOther" + { + "type" "cbaseentity" + } + } + } "CTFGameRules::SetWinningTeam" { "offset" "CTFGameRules::SetWinningTeam" diff --git a/addons/sourcemod/scripting/mannvsmann.sp b/addons/sourcemod/scripting/mannvsmann.sp index 6ff8504..04d000f 100644 --- a/addons/sourcemod/scripting/mannvsmann.sp +++ b/addons/sourcemod/scripting/mannvsmann.sp @@ -27,7 +27,7 @@ #pragma semicolon 1 #pragma newdecls required -#define PLUGIN_VERSION "1.13.0" +#define PLUGIN_VERSION "1.14.0" #define DEFAULT_UPGRADES_FILE "scripts/items/mvm_upgrades.txt" @@ -150,19 +150,6 @@ enum NUM_OBSERVER_MODES, }; -// edict->solid values -enum SolidType_t -{ - SOLID_NONE = 0, // no solid model - SOLID_BSP = 1, // a BSP tree - SOLID_BBOX = 2, // an AABB - SOLID_OBB = 3, // an OBB (not implemented yet) - SOLID_OBB_YAW = 4, // an OBB, constrained so that it can only yaw - SOLID_CUSTOM = 5, // Always call into the entity for tests - SOLID_VPHYSICS = 6, // solid vphysics object, get vcollide from the model and collide with that - SOLID_LAST, -}; - enum { RESET_MODE_TEAM_SWITCH = 0, @@ -202,6 +189,7 @@ ConVar sm_mvm_upgrades_reset_mode; ConVar sm_mvm_showhealth; ConVar sm_mvm_spawn_protection; ConVar sm_mvm_music_enabled; +ConVar sm_mvm_players_are_minibosses; ConVar sm_mvm_gas_explode_damage_modifier; ConVar sm_mvm_explosive_sniper_shot_damage_modifier; ConVar sm_mvm_medigun_shield_damage_modifier; @@ -588,13 +576,9 @@ void SetupOnMapStart() MvMTeam(team).Reset(); } - // Some upgrades require a valid populator + // Create a populator and an upgrade station, which enable some MvM features CreateEntityByName("info_populator"); - - // Set solid type to SOLID_NONE to suppress warnings - int upgradestation = CreateEntityByName("func_upgradestation"); - SetEntProp(upgradestation, Prop_Send, "m_nSolidType", SOLID_NONE); - DispatchSpawn(upgradestation); + DispatchSpawn(CreateEntityByName("func_upgradestation")); } void TogglePlugin(bool enable) diff --git a/addons/sourcemod/scripting/mannvsmann/convars.sp b/addons/sourcemod/scripting/mannvsmann/convars.sp index 133925c..5026dec 100644 --- a/addons/sourcemod/scripting/mannvsmann/convars.sp +++ b/addons/sourcemod/scripting/mannvsmann/convars.sp @@ -35,6 +35,7 @@ void ConVars_Init() sm_mvm_showhealth = CreateConVar("sm_mvm_showhealth", "0", "When set to 1, shows a floating health icon over enemy players."); sm_mvm_spawn_protection = CreateConVar("sm_mvm_spawn_protection", "1", "When set to 1, players are granted ubercharge while they leave their spawn."); sm_mvm_music_enabled = CreateConVar("sm_mvm_music_enabled", "1", "When set to 1, Mann vs. Machine music will play at the start and end of a round."); + sm_mvm_players_are_minibosses = CreateConVar("sm_mvm_players_are_minibosses", "1", "When set to 1, all upgrades will function as if players are MvM giants."); sm_mvm_gas_explode_damage_modifier = CreateConVar("sm_mvm_gas_explode_damage_modifier", "0.5", "Multiplier to damage of the explosion created by the 'Explode On Ignite' upgrade."); sm_mvm_explosive_sniper_shot_damage_modifier = CreateConVar("sm_mvm_explosive_sniper_shot_damage_modifier", "1.0", "Multiplier to damage of the explosion created by the 'Explosive Headshot' upgrade."); sm_mvm_medigun_shield_damage_modifier = CreateConVar("sm_mvm_medigun_shield_damage_modifier", "0", "Multiplier to damage of the shield created by the Medi Gun's 'Projectile Shield' upgrade."); diff --git a/addons/sourcemod/scripting/mannvsmann/dhooks.sp b/addons/sourcemod/scripting/mannvsmann/dhooks.sp index 491714a..42fe0d9 100644 --- a/addons/sourcemod/scripting/mannvsmann/dhooks.sp +++ b/addons/sourcemod/scripting/mannvsmann/dhooks.sp @@ -34,6 +34,7 @@ static DynamicHook g_DHookMyTouch; static DynamicHook g_DHookComeToRest; static DynamicHook g_DHookValidTouch; static DynamicHook g_DHookGetMeleeDamage; +static DynamicHook g_DHookApplyBallImpactEffectOnVictim; static DynamicHook g_DHookSetWinningTeam; static DynamicHook g_DHookShouldRespawnQuickly; static DynamicHook g_DHookRoundRespawn; @@ -65,12 +66,16 @@ void DHooks_Init(GameData gamedata) DHooks_AddDynamicDetour(gamedata, "CObjectSapper::ApplyRoboSapperEffects", DHookCallback_ApplyRoboSapperEffects_Pre, DHookCallback_ApplyRoboSapperEffects_Post); DHooks_AddDynamicDetour(gamedata, "CRegenerateZone::Regenerate", DHookCallback_Regenerate_Pre, _); DHooks_AddDynamicDetour(gamedata, "CTFPowerupBottle::AllowedToUse", DHookCallback_AllowedToUse_Pre, DHookCallback_AllowedToUse_Post); + DHooks_AddDynamicDetour(gamedata, "CTFKnife::CanPerformBackstabAgainstTarget", DHookCallback_CanPerformBackstabAgainstTarget_Pre, DHookCallback_CanPerformBackstabAgainstTarget_Post); + DHooks_AddDynamicDetour(gamedata, "CTFBaseRocket::CheckForStunOnImpact", DHookCallback_CheckForStunOnImpact_Pre, DHookCallback_CheckForStunOnImpact_Post); + DHooks_AddDynamicDetour(gamedata, "CTFSniperRifle::ExplosiveHeadShot", DHookCallback_ExplosiveHeadShot_Pre, DHookCallback_ExplosiveHeadShot_Post); // Create virtual hooks g_DHookMyTouch = DHooks_AddDynamicHook(gamedata, "CCurrencyPack::MyTouch"); g_DHookComeToRest = DHooks_AddDynamicHook(gamedata, "CCurrencyPack::ComeToRest"); g_DHookValidTouch = DHooks_AddDynamicHook(gamedata, "CTFPowerup::ValidTouch"); g_DHookGetMeleeDamage = DHooks_AddDynamicHook(gamedata, "CTFWeaponBaseMelee::GetMeleeDamage"); + g_DHookApplyBallImpactEffectOnVictim = DHooks_AddDynamicHook(gamedata, "CTFStunBall::ApplyBallImpactEffectOnVictim"); g_DHookSetWinningTeam = DHooks_AddDynamicHook(gamedata, "CTFGameRules::SetWinningTeam"); g_DHookShouldRespawnQuickly = DHooks_AddDynamicHook(gamedata, "CTFGameRules::ShouldRespawnQuickly"); g_DHookRoundRespawn = DHooks_AddDynamicHook(gamedata, "CTFGameRules::RoundRespawn"); @@ -178,6 +183,15 @@ void DHooks_OnEntityCreated(int entity, const char[] classname) DHooks_HookEntity(g_DHookGetMeleeDamage, Hook_Post, entity, DHookCallback_GetMeleeDamage_Post); } } + + if (!strcmp(classname, "tf_projectile_stun_ball") || !strcmp(classname, "tf_projectile_ball_ornament")) + { + if (g_DHookApplyBallImpactEffectOnVictim) + { + DHooks_HookEntity(g_DHookApplyBallImpactEffectOnVictim, Hook_Pre, entity, DHookCallback_ApplyBallImpactEffectOnVictim_Pre); + DHooks_HookEntity(g_DHookApplyBallImpactEffectOnVictim, Hook_Post, entity, DHookCallback_ApplyBallImpactEffectOnVictim_Post); + } + } } static void DHooks_AddDynamicDetour(GameData gamedata, const char[] name, DHookCallback callbackPre = INVALID_FUNCTION, DHookCallback callbackPost = INVALID_FUNCTION) @@ -441,10 +455,10 @@ static MRESReturn DHookCallback_RadiusSpyScan_Pre(Address pShared) static MRESReturn DHookCallback_RadiusSpyScan_Post(Address pShared) { - int player = TF2Util_GetPlayerFromSharedAddress(pShared); - if (!sm_mvm_radius_spy_scan.BoolValue) { + int player = TF2Util_GetPlayerFromSharedAddress(pShared); + MvMPlayer(player).ResetTeam(); return MRES_Ignored; } @@ -462,12 +476,17 @@ static MRESReturn DHookCallback_RadiusSpyScan_Post(Address pShared) static MRESReturn DHookCallback_ApplyRocketPackStun_Pre(Address pShared, DHookParam params) { - // Minibosses in MvM get slowed down instead of fully stunned - for (int client = 1; client <= MaxClients; client++) + if (sm_mvm_players_are_minibosses.BoolValue) { - if (IsClientInGame(client)) + int player = TF2Util_GetPlayerFromSharedAddress(pShared); + + // Minibosses get slowed down instead of fully stunned + for (int client = 1; client <= MaxClients; client++) { - MvMPlayer(client).SetIsMiniBoss(true); + if (client != player && IsClientInGame(client)) + { + MvMPlayer(client).SetIsMiniBoss(true); + } } } @@ -476,11 +495,16 @@ static MRESReturn DHookCallback_ApplyRocketPackStun_Pre(Address pShared, DHookPa static MRESReturn DHookCallback_ApplyRocketPackStun_Post(Address pShared, DHookParam params) { - for (int client = 1; client <= MaxClients; client++) + if (sm_mvm_players_are_minibosses.BoolValue) { - if (IsClientInGame(client)) + int player = TF2Util_GetPlayerFromSharedAddress(pShared); + + for (int client = 1; client <= MaxClients; client++) { - MvMPlayer(client).ResetIsMiniBoss(); + if (client != player && IsClientInGame(client)) + { + MvMPlayer(client).ResetIsMiniBoss(); + } } } @@ -529,7 +553,7 @@ static MRESReturn DHookCallback_FindSnapToBuildPos_Pre(int obj, DHookReturn ret, // The robot sapper only works on bots, give every player the fake client flag for (int client = 1; client <= MaxClients; client++) { - if (IsClientInGame(client) && client != builder) + if (client != builder && IsClientInGame(client)) { MvMPlayer(client).AddFlags(FL_FAKECLIENT); } @@ -549,7 +573,7 @@ static MRESReturn DHookCallback_FindSnapToBuildPos_Post(int obj, DHookReturn ret for (int client = 1; client <= MaxClients; client++) { - if (IsClientInGame(client) && client != builder) + if (client != builder && IsClientInGame(client)) { MvMPlayer(client).ResetFlags(); } @@ -581,19 +605,25 @@ static MRESReturn DHookCallback_ShouldQuickBuild_Post(int obj, DHookReturn ret) static MRESReturn DHookCallback_ApplyRoboSapperEffects_Pre(int sapper, DHookReturn ret, DHookParam params) { - int target = params.Get(1); - - // Minibosses in MvM get slowed down instead of fully stunned - MvMPlayer(target).SetIsMiniBoss(true); + if (sm_mvm_players_are_minibosses.BoolValue) + { + int target = params.Get(1); + + // Minibosses get slowed down instead of fully stunned + MvMPlayer(target).SetIsMiniBoss(true); + } return MRES_Ignored; } static MRESReturn DHookCallback_ApplyRoboSapperEffects_Post(int sapper, DHookReturn ret, DHookParam params) { - int target = params.Get(1); - - MvMPlayer(target).ResetIsMiniBoss(); + if (sm_mvm_players_are_minibosses.BoolValue) + { + int target = params.Get(1); + + MvMPlayer(target).ResetIsMiniBoss(); + } return MRES_Ignored; } @@ -636,6 +666,101 @@ static MRESReturn DHookCallback_AllowedToUse_Post(int bottle, DHookReturn ret) return MRES_Ignored; } +static MRESReturn DHookCallback_CanPerformBackstabAgainstTarget_Pre(int knife, DHookReturn ret, DHookParam params) +{ + int target = params.Get(1); + + SetMannVsMachineMode(true); + + MvMPlayer(target).SetTeam(TFTeam_Blue); + + if (sm_mvm_players_are_minibosses.BoolValue) + { + // Minibosses cannot be backstabbed from all sides while sapped + MvMPlayer(target).SetIsMiniBoss(true); + } + + return MRES_Ignored; +} + +static MRESReturn DHookCallback_CanPerformBackstabAgainstTarget_Post(int knife, DHookReturn ret, DHookParam params) +{ + int target = params.Get(1); + + ResetMannVsMachineMode(); + + MvMPlayer(target).ResetTeam(); + + if (sm_mvm_players_are_minibosses.BoolValue) + { + MvMPlayer(target).ResetIsMiniBoss(); + } + + return MRES_Ignored; +} + +static MRESReturn DHookCallback_CheckForStunOnImpact_Pre(int rocket, DHookParam params) +{ + if (sm_mvm_players_are_minibosses.BoolValue) + { + int target = params.Get(1); + + // Minibosses receive a weaker rocket specialist stun + MvMPlayer(target).SetIsMiniBoss(true); + } + + return MRES_Ignored; +} + +static MRESReturn DHookCallback_CheckForStunOnImpact_Post(int rocket, DHookParam params) +{ + if (sm_mvm_players_are_minibosses.BoolValue) + { + int target = params.Get(1); + + MvMPlayer(target).ResetIsMiniBoss(); + } + + return MRES_Ignored; +} + +static MRESReturn DHookCallback_ExplosiveHeadShot_Pre(int sniperrifle, DHookParam params) +{ + if (sm_mvm_players_are_minibosses.BoolValue) + { + int attacker = params.Get(1); + + for (int client = 1; client <= MaxClients; client++) + { + if (client != attacker && IsClientInGame(client)) + { + // Minibosses receive a weaker explosive headshot stun + MvMPlayer(client).SetIsMiniBoss(true); + } + } + } + + return MRES_Ignored; +} + +static MRESReturn DHookCallback_ExplosiveHeadShot_Post(int sniperrifle, DHookParam params) +{ + if (sm_mvm_players_are_minibosses.BoolValue) + { + int attacker = params.Get(1); + + for (int client = 1; client <= MaxClients; client++) + { + if (client != attacker && IsClientInGame(client)) + { + MvMPlayer(client).ResetIsMiniBoss(); + } + } + } + + return MRES_Ignored; +} + static MRESReturn DHookCallback_MyTouch_Pre(int currencypack, DHookReturn ret, DHookParam params) { // This virtual hook cannot be substituted with an SDKHook because the Touch function for CItem is actually CItem::ItemTouch, not CItem::MyTouch. @@ -703,12 +828,12 @@ static MRESReturn DHookCallback_ValidTouch_Post(int currencypack, DHookReturn re static MRESReturn DHookCallback_GetMeleeDamage_Pre(int melee, DHookReturn ret, DHookParam params) { - if (sm_mvm_backstab_armor_piercing.BoolValue) + if (sm_mvm_players_are_minibosses.BoolValue && sm_mvm_backstab_armor_piercing.BoolValue) { int entity = params.Get(1); if (IsValidClient(entity)) { - // Minibosses in MvM cannot get killed instantly by backstabs + // Minibosses cannot get killed instantly by backstabs MvMPlayer(entity).SetIsMiniBoss(true); } } @@ -718,7 +843,7 @@ static MRESReturn DHookCallback_GetMeleeDamage_Pre(int melee, DHookReturn ret, D static MRESReturn DHookCallback_GetMeleeDamage_Post(int melee, DHookReturn ret, DHookParam params) { - if (sm_mvm_backstab_armor_piercing.BoolValue) + if (sm_mvm_players_are_minibosses.BoolValue && sm_mvm_backstab_armor_piercing.BoolValue) { int entity = params.Get(1); if (IsValidClient(entity)) @@ -730,6 +855,35 @@ static MRESReturn DHookCallback_GetMeleeDamage_Post(int melee, DHookReturn ret, return MRES_Ignored; } +static MRESReturn DHookCallback_ApplyBallImpactEffectOnVictim_Pre(int ball, DHookParam params) +{ + SetMannVsMachineMode(true); + + if (sm_mvm_players_are_minibosses.BoolValue) + { + int player = params.Get(1); + + // Minibosses cannot get fully stunned by sandman balls + MvMPlayer(player).SetIsMiniBoss(true); + } + + return MRES_Ignored; +} + +static MRESReturn DHookCallback_ApplyBallImpactEffectOnVictim_Post(int ball, DHookParam params) +{ + ResetMannVsMachineMode(); + + if (sm_mvm_players_are_minibosses.BoolValue) + { + int player = params.Get(1); + + MvMPlayer(player).ResetIsMiniBoss(); + } + + return MRES_Ignored; +} + static MRESReturn DHookCallback_SetWinningTeam_Post(DHookParam params) { // This logic can not be moved to a teamplay_round_win event hook.