diff --git a/README.md b/README.md index fb81dd9..690f2d9 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,5 @@ In April 2021, [Mikusch](https://github.com/Mikusch) decided to remake the plugi - SourceMod 1.11+ - [TF2Attributes](https://github.com/nosoop/tf2attributes) -- [MemoryPatch](https://github.com/Kenzzer/MemoryPatch) (compile only) +- [TF2 Utils](https://github.com/nosoop/SM-TFUtils) +- [MemoryPatch](https://github.com/Kenzzer/MemoryPatch) (compile only) \ No newline at end of file diff --git a/addons/sourcemod/gamedata/mannvsmann.txt b/addons/sourcemod/gamedata/mannvsmann.txt index 869cdd8..af22296 100644 --- a/addons/sourcemod/gamedata/mannvsmann.txt +++ b/addons/sourcemod/gamedata/mannvsmann.txt @@ -19,6 +19,24 @@ { "tf" { + "Keys" + { + "CTFPlayer_BaseOffset" + { + "linux" "m_bIsMiniBoss" + "windows" "m_bIsMiniBoss" + } + "CCurrencyPack_BaseOffset" + { + "linux" "m_bDistributed" + "windows" "m_bDistributed" + } + "CPopulationManager_BaseOffset" + { + "linux" "m_vecOrigin" + "windows" "m_vecOrigin" + } + } "Signatures" { "CUpgrades::ApplyUpgradeToItem" @@ -164,11 +182,6 @@ } "Offsets" { - "CBaseEntity::GetBaseEntity" - { - "linux" "6" - "windows" "5" - } "CCurrencyPack::MyTouch" { "linux" "226" @@ -219,25 +232,20 @@ "linux" "229" "windows" "227" } - "CTFPlayerShared::m_pOuter" - { - "linux" "400" - "windows" "400" - } "CTFPlayer::m_hReviveMarker" { - "linux" "9164" - "windows" "9160" + "linux" "316" + "windows" "316" } "CCurrencyPack::m_nAmount" { - "linux" "1276" - "windows" "1256" + "linux" "-26" + "windows" "-26" } "CPopulationManager::m_isRestoringCheckpoint" { - "linux" "1512" - "windows" "1492" + "linux" "700" + "windows" "700" } } "Functions" diff --git a/addons/sourcemod/scripting/mannvsmann.sp b/addons/sourcemod/scripting/mannvsmann.sp index 96a65f3..b55e132 100644 --- a/addons/sourcemod/scripting/mannvsmann.sp +++ b/addons/sourcemod/scripting/mannvsmann.sp @@ -21,12 +21,13 @@ #include #include #include +#include #include #pragma semicolon 1 #pragma newdecls required -#define PLUGIN_VERSION "1.9.1" +#define PLUGIN_VERSION "1.10.0" #define DEFAULT_UPGRADES_FILE "scripts/items/mvm_upgrades.txt" @@ -202,12 +203,6 @@ ConVar mvm_defender_team; // DHooks TFTeam g_CurrencyPackTeam = TFTeam_Invalid; -// Offsets -int g_OffsetPlayerSharedOuter; -int g_OffsetPlayerReviveMarker; -int g_OffsetCurrencyPackAmount; -int g_OffsetRestoringCheckpoint; - // Other globals Handle g_CurrencyHudSync; Handle g_BuybackHudSync; @@ -222,6 +217,7 @@ bool g_ForceMapReset; #include "mannvsmann/dhooks.sp" #include "mannvsmann/events.sp" #include "mannvsmann/helpers.sp" +#include "mannvsmann/offsets.sp" #include "mannvsmann/patches.sp" #include "mannvsmann/sdkhooks.sp" #include "mannvsmann/sdkcalls.sp" @@ -243,21 +239,17 @@ public void OnPluginStart() g_CurrencyHudSync = CreateHudSynchronizer(); g_BuybackHudSync = CreateHudSynchronizer(); - Commands_Initialize(); - ConVars_Initialize(); - Events_Initialize(); + Commands_Init(); + ConVars_Init(); + Events_Init(); GameData gamedata = new GameData("mannvsmann"); if (gamedata) { - DHooks_Initialize(gamedata); - Patches_Initialize(gamedata); - SDKCalls_Initialize(gamedata); - - g_OffsetPlayerSharedOuter = gamedata.GetOffset("CTFPlayerShared::m_pOuter"); - g_OffsetPlayerReviveMarker = gamedata.GetOffset("CTFPlayer::m_hReviveMarker"); - g_OffsetCurrencyPackAmount = gamedata.GetOffset("CCurrencyPack::m_nAmount"); - g_OffsetRestoringCheckpoint = gamedata.GetOffset("CPopulationManager::m_isRestoringCheckpoint"); + DHooks_Init(gamedata); + Patches_Init(gamedata); + Offsets_Init(gamedata); + SDKCalls_Init(gamedata); delete gamedata; } @@ -373,7 +365,7 @@ public void OnEntityDestroyed(int entity) // Remove the currency value from the world money if (!GetEntProp(entity, Prop_Send, "m_bDistributed")) { - int amount = GetEntData(entity, g_OffsetCurrencyPackAmount); + int amount = GetEntData(entity, GetOffset("CCurrencyPack", "m_nAmount")); AddWorldMoney(TF2_GetTeam(entity), -amount); } } @@ -593,7 +585,7 @@ void SetupOnMapStart() // Set custom upgrades file and add it to downloads char path[PLATFORM_MAX_PATH]; mvm_custom_upgrades_file.GetString(path, sizeof(path)); - if (path[0] != EOS) + if (path[0]) { SetCustomUpgradesFile(path); } diff --git a/addons/sourcemod/scripting/mannvsmann/commands.sp b/addons/sourcemod/scripting/mannvsmann/commands.sp index d9c15a5..8ff991b 100644 --- a/addons/sourcemod/scripting/mannvsmann/commands.sp +++ b/addons/sourcemod/scripting/mannvsmann/commands.sp @@ -18,7 +18,7 @@ #pragma semicolon 1 #pragma newdecls required -void Commands_Initialize() +void Commands_Init() { RegAdminCmd("sm_currency_give", ConCmd_GiveCurrency, ADMFLAG_CHEATS, "Have some in-game money."); } diff --git a/addons/sourcemod/scripting/mannvsmann/convars.sp b/addons/sourcemod/scripting/mannvsmann/convars.sp index 8479774..bba2c47 100644 --- a/addons/sourcemod/scripting/mannvsmann/convars.sp +++ b/addons/sourcemod/scripting/mannvsmann/convars.sp @@ -18,7 +18,7 @@ #pragma semicolon 1 #pragma newdecls required -void ConVars_Initialize() +void ConVars_Init() { CreateConVar("mvm_version", PLUGIN_VERSION, "Mann vs. Mann plugin version", FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_DONTRECORD); mvm_enable = CreateConVar("mvm_enable", "1", "When set, the plugin will be enabled."); @@ -94,7 +94,7 @@ static void ConVarChanged_ShowHealth(ConVar convar, const char[] oldValue, const static void ConVarChanged_CustomUpgradesFile(ConVar convar, const char[] oldValue, const char[] newValue) { - if (newValue[0] != EOS) + if (newValue[0]) { SetCustomUpgradesFile(newValue); } diff --git a/addons/sourcemod/scripting/mannvsmann/dhooks.sp b/addons/sourcemod/scripting/mannvsmann/dhooks.sp index 59b7951..e41dd46 100644 --- a/addons/sourcemod/scripting/mannvsmann/dhooks.sp +++ b/addons/sourcemod/scripting/mannvsmann/dhooks.sp @@ -42,7 +42,7 @@ static DynamicHook g_DHookCheckRespawnWaves; static RoundState g_PreHookRoundState; static TFTeam g_PreHookTeam; // For clients, use the MvMPlayer methodmap -void DHooks_Initialize(GameData gamedata) +void DHooks_Init(GameData gamedata) { g_DynamicDetours = new ArrayList(sizeof(DetourData)); g_DynamicHookIds = new ArrayList(); @@ -420,7 +420,7 @@ static MRESReturn DHookCallback_DistributeCurrencyAmount_Post(DHookReturn ret, D return MRES_Ignored; } -static MRESReturn DHookCallback_ConditionGameRulesThink_Pre(Address playerShared) +static MRESReturn DHookCallback_ConditionGameRulesThink_Pre(Address pShared) { // Enables radius currency collection, radius spy scan and increased rage gain during setup SetMannVsMachineMode(true); @@ -428,14 +428,14 @@ static MRESReturn DHookCallback_ConditionGameRulesThink_Pre(Address playerShared return MRES_Ignored; } -static MRESReturn DHookCallback_ConditionGameRulesThink_Post(Address playerShared) +static MRESReturn DHookCallback_ConditionGameRulesThink_Post(Address pShared) { ResetMannVsMachineMode(); return MRES_Ignored; } -static MRESReturn DHookCallback_CanRecieveMedigunChargeEffect_Pre(Address playerShared, DHookReturn ret, DHookParam params) +static MRESReturn DHookCallback_CanRecieveMedigunChargeEffect_Pre(Address pShared, DHookReturn ret, DHookParam params) { // MvM allows flag carriers to be ubered (enabled from CTFPlayerShared::ConditionGameRulesThink), but we don't want this for balance reasons SetMannVsMachineMode(false); @@ -443,22 +443,22 @@ static MRESReturn DHookCallback_CanRecieveMedigunChargeEffect_Pre(Address player return MRES_Ignored; } -static MRESReturn DHookCallback_CanRecieveMedigunChargeEffect_Post(Address playerShared, DHookReturn ret, DHookParam params) +static MRESReturn DHookCallback_CanRecieveMedigunChargeEffect_Post(Address pShared, DHookReturn ret, DHookParam params) { ResetMannVsMachineMode(); return MRES_Ignored; } -static MRESReturn DHookCallback_RadiusSpyScan_Pre(Address playerShared) +static MRESReturn DHookCallback_RadiusSpyScan_Pre(Address pShared) { - int outer = GetPlayerSharedOuter(playerShared); - TFTeam team = TF2_GetClientTeam(outer); + int player = TF2Util_GetPlayerFromSharedAddress(pShared); + TFTeam team = TF2_GetClientTeam(player); // This MvM feature may confuse players, so we allow servers to toggle it if (!mvm_radius_spy_scan.BoolValue) { - MvMPlayer(outer).SetTeam(TFTeam_Spectator); + MvMPlayer(player).SetTeam(TFTeam_Spectator); return MRES_Ignored; } @@ -467,7 +467,7 @@ static MRESReturn DHookCallback_RadiusSpyScan_Pre(Address playerShared) { if (IsClientInGame(client)) { - if (client == outer) + if (client == player) { MvMPlayer(client).SetTeam(TFTeam_Red); } @@ -488,13 +488,13 @@ static MRESReturn DHookCallback_RadiusSpyScan_Pre(Address playerShared) return MRES_Ignored; } -static MRESReturn DHookCallback_RadiusSpyScan_Post(Address playerShared) +static MRESReturn DHookCallback_RadiusSpyScan_Post(Address pShared) { - int outer = GetPlayerSharedOuter(playerShared); + int player = TF2Util_GetPlayerFromSharedAddress(pShared); if (!mvm_radius_spy_scan.BoolValue) { - MvMPlayer(outer).ResetTeam(); + MvMPlayer(player).ResetTeam(); return MRES_Ignored; } @@ -509,7 +509,7 @@ static MRESReturn DHookCallback_RadiusSpyScan_Post(Address playerShared) return MRES_Ignored; } -static MRESReturn DHookCallback_ApplyRocketPackStun_Pre(Address playerShared, DHookParam params) +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++) @@ -523,7 +523,7 @@ static MRESReturn DHookCallback_ApplyRocketPackStun_Pre(Address playerShared, DH return MRES_Ignored; } -static MRESReturn DHookCallback_ApplyRocketPackStun_Post(Address playerShared, DHookParam params) +static MRESReturn DHookCallback_ApplyRocketPackStun_Post(Address pShared, DHookParam params) { for (int client = 1; client <= MaxClients; client++) { @@ -843,7 +843,7 @@ static MRESReturn DHookCallback_RoundRespawn_Pre() else { // Retain player upgrades (forces a call to CTFPlayer::ReapplyPlayerUpgrades) - SetEntData(populator, g_OffsetRestoringCheckpoint, true); + SetEntData(populator, GetOffset("CPopulationManager", "m_isRestoringCheckpoint"), true, 1); } } @@ -855,7 +855,7 @@ static MRESReturn DHookCallback_RoundRespawn_Post() int populator = FindEntityByClassname(-1, "info_populator"); if (populator != -1) { - SetEntData(populator, g_OffsetRestoringCheckpoint, false); + SetEntData(populator, GetOffset("CPopulationManager", "m_isRestoringCheckpoint"), false, 1); } return MRES_Ignored; diff --git a/addons/sourcemod/scripting/mannvsmann/events.sp b/addons/sourcemod/scripting/mannvsmann/events.sp index 07e9f67..acf0ea1 100644 --- a/addons/sourcemod/scripting/mannvsmann/events.sp +++ b/addons/sourcemod/scripting/mannvsmann/events.sp @@ -29,7 +29,7 @@ enum struct EventData static ArrayList g_Events; -void Events_Initialize() +void Events_Init() { g_Events = new ArrayList(sizeof(EventData)); @@ -280,10 +280,10 @@ static void EventHook_PlayerDeath(Event event, const char[] name, bool dontBroad { if (!(death_flags & TF_DEATHFLAG_DEADRINGER) && !silent_kill) { - if (GetEntDataEnt2(victim, g_OffsetPlayerReviveMarker) == -1) + if (GetEntDataEnt2(victim, GetOffset("CTFPlayer", "m_hReviveMarker")) == -1) { // Create revive marker - SetEntDataEnt2(victim, g_OffsetPlayerReviveMarker, SDKCall_ReviveMarkerCreate(victim)); + SetEntDataEnt2(victim, GetOffset("CTFPlayer", "m_hReviveMarker"), SDKCall_ReviveMarkerCreate(victim)); } } } diff --git a/addons/sourcemod/scripting/mannvsmann/helpers.sp b/addons/sourcemod/scripting/mannvsmann/helpers.sp index 468b836..ea7fba9 100644 --- a/addons/sourcemod/scripting/mannvsmann/helpers.sp +++ b/addons/sourcemod/scripting/mannvsmann/helpers.sp @@ -91,12 +91,6 @@ Address GetPlayerShared(int client) return GetEntityAddress(client) + offset; } -int GetPlayerSharedOuter(Address playerShared) -{ - Address outer = view_as
(LoadFromAddress(playerShared + view_as
(g_OffsetPlayerSharedOuter), NumberType_Int32)); - return SDKCall_GetBaseEntity(outer); -} - void SetCustomUpgradesFile(const char[] path) { if (FileExists(path, true, "MOD")) @@ -149,7 +143,7 @@ void ClearCustomUpgradesFile() bool IsMannVsMachineMode() { - return view_as(GameRules_GetProp("m_bPlayingMannVsMachine")); + return GameRules_GetProp("m_bPlayingMannVsMachine") != 0; } void SetMannVsMachineMode(bool value) diff --git a/addons/sourcemod/scripting/mannvsmann/offsets.sp b/addons/sourcemod/scripting/mannvsmann/offsets.sp new file mode 100644 index 0000000..60dc9aa --- /dev/null +++ b/addons/sourcemod/scripting/mannvsmann/offsets.sp @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2022 Mikusch + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma semicolon 1 +#pragma newdecls required + +static StringMap g_Offsets; + +void Offsets_Init(GameData gamedata) +{ + g_Offsets = new StringMap(); + + SetOffset(gamedata, "CTFPlayer", "m_hReviveMarker"); + SetOffset(gamedata, "CCurrencyPack", "m_nAmount"); + SetOffset(gamedata, "CPopulationManager", "m_isRestoringCheckpoint"); +} + +any GetOffset(const char[] cls, const char[] prop) +{ + char key[64]; + Format(key, sizeof(key), "%s::%s", cls, prop); + + int offset; + if (!g_Offsets.GetValue(key, offset)) + { + ThrowError("Offset '%s' not present in map", key); + } + + return offset; +} + +static void SetOffset(GameData gamedata, const char[] cls, const char[] prop) +{ + char key[64], base_key[64], base_prop[64]; + Format(key, sizeof(key), "%s::%s", cls, prop); + Format(base_key, sizeof(base_key), "%s_BaseOffset", cls); + + // Get the actual offset, calculated using a base offset if present + if (gamedata.GetKeyValue(base_key, base_prop, sizeof(base_prop))) + { + int base_offset = FindSendPropInfo(cls, base_prop); + if (base_offset == -1) + { + // If we found nothing, search on CBaseEntity instead + base_offset = FindSendPropInfo("CBaseEntity", base_prop); + if (base_offset == -1) + { + ThrowError("Base offset '%s::%s' could not be found", cls, base_prop); + } + } + + int offset = base_offset + gamedata.GetOffset(key); + g_Offsets.SetValue(key, offset); + } + else + { + int offset = gamedata.GetOffset(key); + if (offset == -1) + { + ThrowError("Offset '%s' could not be found", key); + } + + g_Offsets.SetValue(key, offset); + } +} diff --git a/addons/sourcemod/scripting/mannvsmann/patches.sp b/addons/sourcemod/scripting/mannvsmann/patches.sp index 250b99a..e30cabd 100644 --- a/addons/sourcemod/scripting/mannvsmann/patches.sp +++ b/addons/sourcemod/scripting/mannvsmann/patches.sp @@ -20,7 +20,7 @@ static ArrayList g_MemoryPatches; -void Patches_Initialize(GameData gamedata) +void Patches_Init(GameData gamedata) { g_MemoryPatches = new ArrayList(); diff --git a/addons/sourcemod/scripting/mannvsmann/sdkcalls.sp b/addons/sourcemod/scripting/mannvsmann/sdkcalls.sp index 4047140..dac4de8 100644 --- a/addons/sourcemod/scripting/mannvsmann/sdkcalls.sp +++ b/addons/sourcemod/scripting/mannvsmann/sdkcalls.sp @@ -27,12 +27,11 @@ static Handle g_SDKCallCanRecieveMedigunChargeEffect; static Handle g_SDKCallReviveMarkerCreate; static Handle g_SDKCallRemoveImmediate; static Handle g_SDKCallDistributeCurrencyAmount; -static Handle g_SDKCallGetBaseEntity; static Handle g_SDKCallShouldSwitchTeams; static Handle g_SDKCallShouldScrambleTeams; static Handle g_SDKCallGetNextRespawnWave; -void SDKCalls_Initialize(GameData gamedata) +void SDKCalls_Init(GameData gamedata) { g_SDKCallResetMap = PrepSDKCall_ResetMap(gamedata); g_SDKCallGetPlayerCurrencySpent = PrepSDKCall_GetPlayerCurrencySpent(gamedata); @@ -43,7 +42,6 @@ void SDKCalls_Initialize(GameData gamedata) g_SDKCallReviveMarkerCreate = PrepSDKCall_ReviveMarkerCreate(gamedata); g_SDKCallRemoveImmediate = PrepSDKCall_RemoveImmediate(gamedata); g_SDKCallDistributeCurrencyAmount = PrepSDKCall_DistributeCurrencyAmount(gamedata); - g_SDKCallGetBaseEntity = PrepSDKCall_GetBaseEntity(gamedata); g_SDKCallShouldSwitchTeams = PrepSDKCall_ShouldSwitchTeams(gamedata); g_SDKCallShouldScrambleTeams = PrepSDKCall_ShouldScrambleTeams(gamedata); g_SDKCallGetNextRespawnWave = PrepSDKCall_GetNextRespawnWave(gamedata); @@ -196,21 +194,6 @@ static Handle PrepSDKCall_DistributeCurrencyAmount(GameData gamedata) return call; } -static Handle PrepSDKCall_GetBaseEntity(GameData gamedata) -{ - StartPrepSDKCall(SDKCall_Raw); - PrepSDKCall_SetFromConf(gamedata, SDKConf_Virtual, "CBaseEntity::GetBaseEntity"); - PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity, SDKPass_Pointer); - - Handle call = EndPrepSDKCall(); - if (!call) - { - LogMessage("Failed to create SDKCall: CBaseEntity::GetBaseEntity"); - } - - return call; -} - static Handle PrepSDKCall_ShouldSwitchTeams(GameData gamedata) { StartPrepSDKCall(SDKCall_GameRules); @@ -300,11 +283,11 @@ int SDKCall_GetEquippedWearableForLoadoutSlot(int player, LoadoutPosition loadou return -1; } -bool SDKCall_CanRecieveMedigunChargeEffect(Address playerShared, MedigunChargeType type) +bool SDKCall_CanRecieveMedigunChargeEffect(Address pShared, MedigunChargeType type) { if (g_SDKCallCanRecieveMedigunChargeEffect) { - return SDKCall(g_SDKCallCanRecieveMedigunChargeEffect, playerShared, type); + return SDKCall(g_SDKCallCanRecieveMedigunChargeEffect, pShared, type); } return false; @@ -338,16 +321,6 @@ int SDKCall_DistributeCurrencyAmount(int amount, int player = -1, bool shared = return 0; } -int SDKCall_GetBaseEntity(Address address) -{ - if (g_SDKCallGetBaseEntity) - { - return SDKCall(g_SDKCallGetBaseEntity, address); - } - - return -1; -} - bool SDKCall_ShouldSwitchTeams() { if (g_SDKCallShouldSwitchTeams) diff --git a/addons/sourcemod/scripting/mannvsmann/sdkhooks.sp b/addons/sourcemod/scripting/mannvsmann/sdkhooks.sp index 6c0308b..8bbef56 100644 --- a/addons/sourcemod/scripting/mannvsmann/sdkhooks.sp +++ b/addons/sourcemod/scripting/mannvsmann/sdkhooks.sp @@ -149,7 +149,7 @@ static void SDKHookCB_CurrencyPack_SpawnPost(int currencypack) // Add the currency value to the world money if (!GetEntProp(currencypack, Prop_Send, "m_bDistributed")) { - int amount = GetEntData(currencypack, g_OffsetCurrencyPackAmount); + int amount = GetEntData(currencypack, GetOffset("CCurrencyPack", "m_nAmount")); AddWorldMoney(TF2_GetTeam(currencypack), amount); }