From bbb36c99a0fb7a3913aa7ad6bac6099fd9fbc977 Mon Sep 17 00:00:00 2001 From: Forgetest <33988868+jensewe@users.noreply.github.com> Date: Sat, 26 Sep 2026 02:20:56 +0800 Subject: [PATCH 1/4] Archive old l4d2_static_shotgun_spread --- addons/sourcemod/{gamedata => scripting/archive}/code_patcher.txt | 0 .../scripting/{ => archive}/l4d2_static_shotgun_spread.sp | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename addons/sourcemod/{gamedata => scripting/archive}/code_patcher.txt (100%) rename addons/sourcemod/scripting/{ => archive}/l4d2_static_shotgun_spread.sp (100%) diff --git a/addons/sourcemod/gamedata/code_patcher.txt b/addons/sourcemod/scripting/archive/code_patcher.txt similarity index 100% rename from addons/sourcemod/gamedata/code_patcher.txt rename to addons/sourcemod/scripting/archive/code_patcher.txt diff --git a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp b/addons/sourcemod/scripting/archive/l4d2_static_shotgun_spread.sp similarity index 100% rename from addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp rename to addons/sourcemod/scripting/archive/l4d2_static_shotgun_spread.sp From bc91966658284e48b315bd5984b5c8626280e08f Mon Sep 17 00:00:00 2001 From: Forgetest <33988868+jensewe@users.noreply.github.com> Date: Sat, 26 Sep 2026 02:21:28 +0800 Subject: [PATCH 2/4] Add l4d_pellet_spread_customizer --- .../gamedata/l4d_pellet_spread_customizer.txt | 148 +++++++++++ .../include/l4d_pellet_spread_customizer.inc | 50 ++++ .../scripting/l4d_pellet_spread_customizer.sp | 239 ++++++++++++++++++ 3 files changed, 437 insertions(+) create mode 100644 addons/sourcemod/gamedata/l4d_pellet_spread_customizer.txt create mode 100644 addons/sourcemod/scripting/include/l4d_pellet_spread_customizer.inc create mode 100644 addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp diff --git a/addons/sourcemod/gamedata/l4d_pellet_spread_customizer.txt b/addons/sourcemod/gamedata/l4d_pellet_spread_customizer.txt new file mode 100644 index 0000000000..24bfa3d91f --- /dev/null +++ b/addons/sourcemod/gamedata/l4d_pellet_spread_customizer.txt @@ -0,0 +1,148 @@ +"Games" +{ + "#default" + { + "Functions" + { + "CTerrorGun::FireBullet" + { + "signature" "CTerrorGun::FireBullet" + "callconv" "thiscall" + "return" "void" + "this" "entity" + } + + "SharedRandomFloat" + { + "signature" "SharedRandomFloat" + "callconv" "cdecl" + "return" "float" + "arguments" + { + "name" + { + "type" "charptr" + } + "min" + { + "type" "float" + } + "max" + { + "type" "float" + } + "seed" + { + "type" "int" + } + } + } + } + } + + "left4dead" + { + "MemPatches" + { + "center_pellet" + { + "signature" "CTerrorPlayer::FireBullet" + "linux" + { + "offset" "44Ch" + "verify" "\xB8\x01" + "patch" "\xB8\x00" + } + "windows" + { + "offset" "262h" + "verify" "\xB1\x01" + "patch" "\xB1\x00" + } + } + } + + "Signatures" + { + "CTerrorGun::FireBullet" + { + "library" "server" + "linux" "@_ZN10CTerrorGun10FireBulletEv" + "windows" "\x83\xEC\x40\x53\x56\x8B\xF1\xE8" + /* 83 EC 40 53 56 8B F1 E8 */ + } + + /* @A1m`: + * How to find in windows: + * Can be found by string "CTerrorPlayer::FireBullet Spread" or "CTerrorPlayer::FireBullet SpreadDir" + */ + "CTerrorPlayer::FireBullet" + { + "library" "server" + "linux" "@_ZN13CTerrorPlayer10FireBulletE6VectorRK6QAngle10CSWeaponIDi" + "windows" "\x81\xEC\x90\x00\x00\x00\x8B\x84\x24\xA4\x00\x00\x00" + } + + "SharedRandomFloat" + { + "library" "server" + "linux" "@_Z17SharedRandomFloatPKcffi" + "windows" "\x83\xEC\x08\xA1\x2A\x2A\x2A\x2A\x53\x56\x57\x8B\x7C\x24\x24\x8D\x4C\x24\x24\x51\x89\x7C\x24\x14\x89\x44\x24\x10\xE8\x2A\x2A\x2A\x2A\x6A\x04\x8D\x54\x24\x14\x52\x8D\x44\x24\x30\x50\xE8\x2A\x2A\x2A\x2A\x6A\x04\x8D\x4C\x24\x24\x51\x8D\x54\x24\x3C\x52\xE8\x2A\x2A\x2A\x2A\x8B\x74\x24\x34\x56\xE8\x2A\x2A\x2A\x2A\x50\x8D\x44\x24\x48\x56\x50\xE8\x2A\x2A\x2A\x2A\x8D\x4C\x24\x50\x51\xE8\x2A\x2A\x2A\x2A\x8B\x15\x2A\x2A\x2A\x2A\x8B\x5C\x24\x54\x83\xC4\x30\x83\x7A\x30\x00\x74\x2A\x57\x53\x56\xB0\xFF\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x88\x44\x24\x38\x88\x44\x24\x39\x88\x44\x24\x3B\x8D\x44\x24\x38\x68\x2A\x2A\x2A\x2A\x50\xC6\x44\x24\x42\x00\xFF\x15\x2A\x2A\x2A\x2A\x83\xC4\x1C\x53\xB9\x2A\x2A\x2A\x2A\xFF\x15\x2A\x2A\x2A\x2A\xD9\x44\x24\x20" + // 83 EC 08 A1 ? ? ? ? 53 56 57 8B 7C 24 24 8D 4C 24 24 51 89 7C 24 14 89 44 24 10 E8 ? ? ? ? 6A 04 8D 54 24 14 52 8D 44 24 30 50 E8 ? ? ? ? 6A 04 8D 4C 24 24 51 8D 54 24 3C 52 E8 ? ? ? ? 8B 74 24 34 56 E8 ? ? ? ? 50 8D 44 24 48 56 50 E8 ? ? ? ? 8D 4C 24 50 51 E8 ? ? ? ? 8B 15 ? ? ? ? 8B 5C 24 54 83 C4 30 83 7A 30 00 74 ? 57 53 56 B0 FF 68 ? ? ? ? 68 ? ? ? ? 88 44 24 38 88 44 24 39 88 44 24 3B 8D 44 24 38 68 ? ? ? ? 50 C6 44 24 42 00 FF 15 ? ? ? ? 83 C4 1C 53 B9 ? ? ? ? FF 15 ? ? ? ? D9 44 24 20 + } + } + } + + "left4dead2" + { + "MemPatches" + { + "center_pellet" + { + "signature" "CTerrorPlayer::FireBullet" + "linux" + { + "offset" "3FAh" + "verify" "\xB8\x01" + "patch" "\xB8\x00" + } + "windows" + { + "offset" "2EBh" + "verify" "\xB1\x01" + "patch" "\xB1\x00" + } + } + } + + "Signatures" + { + "CTerrorGun::FireBullet" + { + "library" "server" + "linux" "@_ZN10CTerrorGun10FireBulletEv" + "windows" "\x55\x8B\xEC\x83\xEC\x34\x53\x56\x8B\xF1\xE8" + /* 55 8B EC 83 EC 34 53 56 8B F1 E8 */ + } + + /* @A1m`: + * How to find in windows: + * Can be found by string "CTerrorPlayer::FireBullet Spread" or "CTerrorPlayer::FireBullet SpreadDir" + */ + "CTerrorPlayer::FireBullet" + { + "library" "server" + "linux" "@_ZN13CTerrorPlayer10FireBulletE6VectorRK6QAngle10CSWeaponIDi" + "windows" "\x55\x8b\xec\x81\xec\x2a\x2a\x2a\x2a\x8b\x45\x18\x57\x8b\xf9" + } + + "SharedRandomFloat" + { + "library" "server" + "linux" "@_Z17SharedRandomFloatPKcffi" + "windows" "\x55\x8B\xEC\x83\xEC\x08\xA1\x2A\x2A\x2A\x2A\x53\x56\x57\x8B\x7D\x14\x8D\x4D\x14\x51\x89\x7D\xF8\x89\x45\xFC\xE8\x2A\x2A\x2A\x2A\x6A\x04\x8D\x55\xFC\x52\x8D\x45\x14\x50\xE8\x2A\x2A\x2A\x2A\x6A\x04\x8D\x4D\xF8\x51\x8D\x55\x14\x52\xE8\x2A\x2A\x2A\x2A\x8B\x75\x08\x56\xE8\x2A\x2A\x2A\x2A\x50\x8D\x45\x14\x56\x50\xE8\x2A\x2A\x2A\x2A\x8D\x4D\x14\x51\xE8\x2A\x2A\x2A\x2A\x8B\x15\x2A\x2A\x2A\x2A\x8B\x5D\x14\x83\xC4\x30\x83\x7A\x30\x00\x74\x2A\x57\x53\x56\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x8D\x45\x14\x68\x2A\x2A\x2A\x2A\x50\xC7\x45\x14\xFF\xFF\x00\xFF\xFF\x15\x2A\x2A\x2A\x2A\x83\xC4\x1C\x53\xB9\x2A\x2A\x2A\x2A\xFF\x15\x2A\x2A\x2A\x2A\xD9\x45\x10" + // 55 8B EC 83 EC 08 A1 ? ? ? ? 53 56 57 8B 7D 14 8D 4D 14 51 89 7D F8 89 45 FC E8 ? ? ? ? 6A 04 8D 55 FC 52 8D 45 14 50 E8 ? ? ? ? 6A 04 8D 4D F8 51 8D 55 14 52 E8 ? ? ? ? 8B 75 08 56 E8 ? ? ? ? 50 8D 45 14 56 50 E8 ? ? ? ? 8D 4D 14 51 E8 ? ? ? ? 8B 15 ? ? ? ? 8B 5D 14 83 C4 30 83 7A 30 00 74 ? 57 53 56 68 ? ? ? ? 68 ? ? ? ? 8D 45 14 68 ? ? ? ? 50 C7 45 14 FF FF 00 FF FF 15 ? ? ? ? 83 C4 1C 53 B9 ? ? ? ? FF 15 ? ? ? ? D9 45 10 + } + } + } +} \ No newline at end of file diff --git a/addons/sourcemod/scripting/include/l4d_pellet_spread_customizer.inc b/addons/sourcemod/scripting/include/l4d_pellet_spread_customizer.inc new file mode 100644 index 0000000000..9db2ca7281 --- /dev/null +++ b/addons/sourcemod/scripting/include/l4d_pellet_spread_customizer.inc @@ -0,0 +1,50 @@ +#if defined _l4d_pellet_spread_customizer_included_ + #endinput +#endif +#define _l4d_pellet_spread_customizer_included_ + +/** + * @brief Called whenever a weapon is about to fire bullets. + * @remarks Determine whether the first bullet should be affected by ScatterYaw/Pitch. + * @remarks Bullet that's affected by ScatterYaw/Pitch is called pellet hereinafter. + * @note Called in pre-detour of function "CTerrorGun::FireBullet". + * @note Has no effect if either ScatterYaw/Pitch is not configured for this weapon. + * + * @param weapon Weapon entity index + * + * @return Plugin_Handled to force the first bullet acting as a pellet, + * Plugin_Continue otherwise. + */ +forward Action L4D_OnPelletFirstBullet(int weapon); + +/** + * @brief Called whenever generating spread values for a pellet. + * @remarks Spread -> how far from the center, SpreadDir -> circular angle. + * @remarks By default the actual output for spread is ranged in [-x, x], for angle it is [0, 180]. + * @note Negative values are accepted, for the opposite direction. + * @note "nMaxPellets" adds 1 if "L4D_OnPelletFirstBullet" returns Plugin_Handled. + * @note Called in post-detour of function "SharedRandomFloat", the same frame later "L4D_OnPelletFirstBullet". + * + * @param weapon Weapon entity index + * @param spread/angle Spread value + * @param nPellet Current pellet number (starting from 1) + * @param nMaxPellets Max pellet number + * + * @return Plugin_Changed to change to the exact value, + * Plugin_Continue to leave it randomly generated. + */ +forward Action L4D_OnPelletSpread(int weapon, float &spread, int nPellet, int nMaxPellets); +forward Action L4D_OnPelletSpreadDir(int weapon, float &angle, int nPellet, int nMaxPellets); + + + +public SharedPlugin __pl_l4d_pellet_spread_customizer = +{ + name = "l4d_pellet_spread_customizer", + file = "l4d_pellet_spread_customizer.smx", +#if defined REQUIRE_PLUGIN + required = 1, +#else + required = 0, +#endif +}; diff --git a/addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp b/addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp new file mode 100644 index 0000000000..4354b48a09 --- /dev/null +++ b/addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp @@ -0,0 +1,239 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include +#include +#include +#include <@Forgetest/gamedatawrapper> + +#define DEBUG 0 +#define PLUGIN_VERSION "1.0" + +public Plugin myinfo = +{ + name = "[L4D & 2] Pellet Spread Customizer", + author = "Forgetest", + description = "Customize spread pattern for pellet-shooting guns.", + version = PLUGIN_VERSION, + url = "https://github.com/Target5150/MoYu_Server_Stupid_Plugins", +} + +MemoryPatch g_patch_CenterPellet; + +GlobalForward g_fwd_CenterPellet; +GlobalForward g_fwd_SpreadMod; +GlobalForward g_fwd_SpreadDIrMod; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + g_fwd_CenterPellet = new GlobalForward("L4D_OnPelletFirstBullet", ET_Event, Param_Cell); + g_fwd_SpreadMod = new GlobalForward("L4D_OnPelletSpread", ET_Event, Param_Cell, Param_FloatByRef, Param_Cell, Param_Cell); + g_fwd_SpreadDIrMod = new GlobalForward("L4D_OnPelletSpreadDir", ET_Event, Param_Cell, Param_FloatByRef, Param_Cell, Param_Cell); + + RegPluginLibrary("l4d_pellet_spread_customizer"); + return APLRes_Success; +} + +public void OnPluginStart() +{ + GameDataWrapper gd = new GameDataWrapper("l4d_pellet_spread_customizer"); + g_patch_CenterPellet = gd.CreatePatchOrFail("center_pellet", false); + delete gd.CreateDetourOrFail("CTerrorGun::FireBullet", DTR_CTerrorGun_FireBullet, DTR_CTerrorGun_FireBullet_Post); + delete gd.CreateDetourOrFail("SharedRandomFloat", _, DTR_SharedRandomFloat_Post); + delete gd; +} + +void ToggleCenterPelletPatch(bool enable) +{ + static bool status = false; + + if (status == enable) + return; + + status = enable; + if (status) + g_patch_CenterPellet.Enable(); + else + g_patch_CenterPellet.Disable(); +} + +bool ShouldUseCenterPellet(int weapon) +{ + if (g_fwd_CenterPellet.FunctionCount == 0) + return true; + + Action result = Plugin_Continue; + Call_StartForward(g_fwd_CenterPellet); + Call_PushCell(weapon); + Call_Finish(result); + + return result == Plugin_Continue; +} + +static int g_nMaxBullets; +static bool g_bCenterPellet; +static int g_iFireBulletWeapon = -1; +MRESReturn DTR_CTerrorGun_FireBullet(int weapon) +{ + g_nMaxBullets = GetWeaponMaxBullets(weapon); + g_iFireBulletWeapon = weapon; + + if (ShouldUseCenterPellet(weapon)) + { + g_bCenterPellet = true; + ToggleCenterPelletPatch(false); + } + else + { + g_bCenterPellet = false; + ToggleCenterPelletPatch(true); + } + + return MRES_Ignored; +} + +MRESReturn DTR_CTerrorGun_FireBullet_Post(int weapon) +{ + g_iFireBulletWeapon = -1; + ToggleCenterPelletPatch(false); + return MRES_Ignored; +} + +bool ApplyPelletSpreadMod(int weapon, float &spread, int nPellet, int nMaxPellets) +{ + if (g_fwd_SpreadMod.FunctionCount == 0) + return false; + + Action result = Plugin_Continue; + Call_StartForward(g_fwd_SpreadMod); + Call_PushCell(weapon); + Call_PushFloatRef(spread); + Call_PushCell(nPellet); + Call_PushCell(nMaxPellets); + Call_Finish(result); + + return result == Plugin_Changed; +} + +bool ApplyPelletSpreadDirMod(int weapon, float &angle, int nPellet, int nMaxPellets) +{ + if (g_fwd_SpreadDIrMod.FunctionCount == 0) + return false; + + Action result = Plugin_Continue; + Call_StartForward(g_fwd_SpreadDIrMod); + Call_PushCell(weapon); + Call_PushFloatRef(angle); + Call_PushCell(nPellet); + Call_PushCell(nMaxPellets); + Call_Finish(result); + + return result == Plugin_Changed; +} + +MRESReturn DTR_SharedRandomFloat_Post(DHookReturn hReturn, DHookParam hParams) +{ + if (g_iFireBulletWeapon == -1) + return MRES_Ignored; + + Assert(g_nMaxBullets > 0); + + char name[64]; + hParams.GetString(1, name, sizeof(name)); + + if (strncmp(name, "CTerrorPlayer::FireBullet Spread", 32)) + return MRES_Ignored; + + int nPellet = hParams.Get(4); + int nMaxPellets = g_nMaxBullets; + + if (g_bCenterPellet) + { + nPellet -= 1; + nMaxPellets -= 1; + } + + if (!strcmp(name[32], "Dir")) + { + // SpreadDir + float angle = hReturn.Value; + if (ApplyPelletSpreadDirMod(g_iFireBulletWeapon, angle, nPellet, nMaxPellets)) + { + DebugChatMsg("#%d angle %.1f", nPellet, angle); + hReturn.Value = angle; + return MRES_Override; + } + } + else + { + // Spread + float flSpread = hParams.Get(3); + if (ApplyPelletSpreadMod(g_iFireBulletWeapon, flSpread, nPellet, nMaxPellets)) + { + DebugChatMsg("#%d spread %.1f", nPellet, flSpread); + hReturn.Value = flSpread; + return MRES_Override; + } + } + + return MRES_Ignored; +} + +int GetWeaponMaxBullets(int weapon) +{ + char weaponname[64]; + GetEdictClassname(weapon, weaponname, sizeof(weaponname)); + return L4D2_GetIntWeaponAttribute(weaponname, L4D2IWA_Bullets); +} + +void DebugChatMsg(const char[] format, any ...) +{ +#if DEBUG + char buffer[512]; + VFormat(buffer, sizeof(buffer), format, 2); + PrintToChatAll("[DEBUG] %s", buffer); +#else + #pragma unused format +#endif +} + +void Assert(bool expr, const char[] msg = "") +{ +#if DEBUG + if (!expr) + { + if (msg[0]) + ThrowError("Assertion failed! (%s)", msg); + else + ThrowError("Assertion failed!"); + } +#else + #pragma unused expr + #pragma unused msg +#endif +} + +stock ConVar CreateConVarHook(const char[] name, + const char[] defaultValue, + const char[] description="", + int flags=0, + bool hasMin=false, float min=0.0, + bool hasMax=false, float max=0.0, + ConVarChanged callback) +{ + ConVar cv = CreateConVar(name, defaultValue, description, flags, hasMin, min, hasMax, max); + + char value[512]; + cv.GetString(value, sizeof(value)); + + Call_StartFunction(INVALID_HANDLE, callback); + Call_PushCell(cv); + Call_PushString(value); + Call_PushString(value); + Call_Finish(); + + cv.AddChangeHook(callback); + + return cv; +} From 8f73243ccbb0c8c05667b94879c011b68a7aeb10 Mon Sep 17 00:00:00 2001 From: Forgetest <33988868+jensewe@users.noreply.github.com> Date: Sat, 26 Sep 2026 02:21:38 +0800 Subject: [PATCH 3/4] Add new l4d2_static_shotgun_spread --- .../scripting/l4d2_static_shotgun_spread.sp | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp diff --git a/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp new file mode 100644 index 0000000000..10f813c101 --- /dev/null +++ b/addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp @@ -0,0 +1,65 @@ +#pragma semicolon 1 +#pragma newdecls required + +#include +#include "include/l4d_pellet_spread_customizer" + +// You can use the visualise_impacts.smx plugin to test the resulting spread. +// It will render small purple boxes where the server-side pellets land. + +ConVar g_hCvarRing1Bullets = null; +ConVar g_hCvarRing1Factor = null; +ConVar g_hCvarCenterPellet = null; + +public Plugin myinfo = +{ + name = "L4D2 Static Shotgun Spread", + author = "Jahze, Visor, A1m`, Rena, Forgetest", + version = "2.0", + description = "Apply circular pattern to shotgun spreads", + url = "https://github.com/Target5150/MoYu_Server_Stupid_Plugins" +}; + +public void OnPluginStart() +{ + g_hCvarRing1Bullets = CreateConVar("sgspread_ring1_bullets", "3", "Number of bullets for the first ring, the remaining bullets will be in the second ring."); + g_hCvarRing1Factor = CreateConVar("sgspread_ring1_factor", "2", "Determines how far or closer the bullets will be from the center for the first ring."); + g_hCvarCenterPellet = CreateConVar("sgspread_center_pellet", "1", "Center pellet: 0 - off, 1 - on.", _, true, 0.0, true, 1.0); +} + +public Action L4D_OnPelletFirstBullet(int weapon) +{ + return g_hCvarCenterPellet.BoolValue ? Plugin_Continue : Plugin_Handled; +} + +public Action L4D_OnPelletSpread(int weapon, float &spread, int nPellet, int nMaxPellets) +{ + // For bullets on the first ring, divides the spread value by the factor. + if (nPellet <= g_hCvarRing1Bullets.IntValue) + { + spread /= g_hCvarRing1Factor.FloatValue; + } + + // For bullets on the second ring, sticks to the max spread value. + + return Plugin_Changed; +} + +public Action L4D_OnPelletSpreadDir(int weapon, float &angle, int nPellet, int nMaxPellets) +{ + int numSlices = 0; + if (nPellet <= g_hCvarRing1Bullets.IntValue) + { + numSlices = g_hCvarRing1Bullets.IntValue; + } + else + { + numSlices = nMaxPellets - g_hCvarRing1Bullets.IntValue; + } + + // @Forgetest: + // Not sure where the first pellet of ring should be. Actually no full understanding of the original ASM codes. + // Keep it simple here, starting at 0. + angle = 180.0 / numSlices * ((nPellet - 1) % numSlices); // "% numSlices" should be unnecessary, whatever. + return Plugin_Changed; +} From af7a5b257a0e71742cca0c09f08138642823092b Mon Sep 17 00:00:00 2001 From: Forgetest <33988868+jensewe@users.noreply.github.com> Date: Sat, 26 Sep 2026 02:52:59 +0800 Subject: [PATCH 4/4] Fix compile --- .../scripting/l4d_pellet_spread_customizer.sp | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp b/addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp index 4354b48a09..646da55461 100644 --- a/addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp +++ b/addons/sourcemod/scripting/l4d_pellet_spread_customizer.sp @@ -5,7 +5,6 @@ #include #include #include -#include <@Forgetest/gamedatawrapper> #define DEBUG 0 #define PLUGIN_VERSION "1.0" @@ -19,6 +18,33 @@ public Plugin myinfo = url = "https://github.com/Target5150/MoYu_Server_Stupid_Plugins", } +methodmap GameDataWrapper < GameData { + public GameDataWrapper(const char[] file) { + GameData gd = new GameData(file); + if (!gd) SetFailState("Missing gamedata \"%s\"", file); + return view_as(gd); + } + public MemoryPatch CreatePatchOrFail(const char[] name, bool enable = false) { + MemoryPatch hPatch = MemoryPatch.CreateFromConf(this, name); + if (!(enable ? hPatch.Enable() : hPatch.Validate())) + SetFailState("Failed to patch \"%s\"", name); + return hPatch; + } + public DynamicDetour CreateDetourOrFail( + const char[] name, + DHookCallback preHook = INVALID_FUNCTION, + DHookCallback postHook = INVALID_FUNCTION) { + DynamicDetour hSetup = DynamicDetour.FromConf(this, name); + if (!hSetup) + SetFailState("Missing detour setup \"%s\"", name); + if (preHook != INVALID_FUNCTION && !hSetup.Enable(Hook_Pre, preHook)) + SetFailState("Failed to pre-detour \"%s\"", name); + if (postHook != INVALID_FUNCTION && !hSetup.Enable(Hook_Post, postHook)) + SetFailState("Failed to post-detour \"%s\"", name); + return hSetup; + } +} + MemoryPatch g_patch_CenterPellet; GlobalForward g_fwd_CenterPellet;