Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 122 additions & 99 deletions addons/sourcemod/scripting/l4d2_static_shotgun_spread.sp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
#include <sourcemod>
#include <sourcescramble>

#define GAMEDATA_FILE "code_patcher"
#define KEY_SGSPREAD "sgspread"
#define BULLET_MAX_SIZE 4
#define DEBUG 0
#define BULLET_MAX_SIZE 3
#define GAMEDATA_FILE "code_patcher"
#define KEY_SGSPREAD "sgspread"

// Original code & Notes: https://github.com/Jahze/l4d2_plugins/tree/master/spread_patch
// Static Shotgun Spread leverages code_patcher (code_patcher.txt gamedata)
Expand All @@ -22,153 +21,177 @@ enum
eWindows = 0,
eLinux,
/* eMac, joke:) */

ePlatform_Size
}
};

int
g_ePlatform;
g_ePlatform = eLinux;

static const int
g_BulletOffsets[ePlatform_Size][BULLET_MAX_SIZE] = {
{ 0xf, 0x21, 0x30, 0x3f }, // Windows
{ 0x11, 0x22, 0x2f, 0x43 } // Linux
g_iBulletOffsets[ePlatform_Size][BULLET_MAX_SIZE] = {
{ 0xf, 0x21, 0x30 }, // Windows
{ 0x11, 0x22, 0x2f } // Linux
},
g_FactorOffset[ePlatform_Size] = {
g_iDegreeOffsets[ePlatform_Size] = {
0x3f, // Windows
0x43 // Linux
},
g_iFactorOffset[ePlatform_Size] = {
0x36, // Windows
0x34 // Linux
},
g_CenterPelletOffset[ePlatform_Size] = {
g_iCenterPelletOffset[ePlatform_Size] = {
-0x36, // Windows
-0x1c // Linux
};

Address
g_pBullets[BULLET_MAX_SIZE] = {Address_Null, ...},
g_pDegree = Address_Null,
g_pFactor = Address_Null,
g_pCenterPellet = Address_Null;

MemoryPatch
g_hPatch_sgspread;
g_hPatchSgSpread = null;

ConVar
hRing1BulletsCvar,
hRing1FactorCvar,
hCenterPelletCvar;
g_hCvarRing1Bullets = null,
g_hCvarRing1Factor = null,
g_hCvarCenterPellet = null;

public Plugin myinfo =
public Plugin myinfo =
{
name = "L4D2 Static Shotgun Spread",
author = "Jahze, Visor, A1m`, Rena",
version = "1.6.1",
version = "1.6.5",
description = "Changes the values in the sgspread patch",
url = "https://github.com/SirPlease/L4D2-Competitive-Rework"
};

public void OnPluginStart()
{
Handle conf = LoadGameConfigFile(GAMEDATA_FILE);
if (conf == null) {
InitGameData();

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);

InitPlugin();
}

void InitGameData()
{
Handle hConf = LoadGameConfigFile(GAMEDATA_FILE);
if (hConf == null) {
SetFailState("Missing gamedata \"" ... GAMEDATA_FILE ... "\"");
}

g_hPatch_sgspread = MemoryPatch.CreateFromConf(conf, KEY_SGSPREAD);
if (g_hPatch_sgspread == null || !g_hPatch_sgspread.Validate()) {

g_ePlatform = GameConfGetOffset(hConf, "OS");
if (g_ePlatform == -1) {
SetFailState("Failed to retrieve offset \"OS\"");
}

g_hPatchSgSpread = MemoryPatch.CreateFromConf(hConf, KEY_SGSPREAD);
if (g_hPatchSgSpread == null || !g_hPatchSgSpread.Validate()) {
SetFailState("Failed to validate MemoryPatch \"" ... KEY_SGSPREAD ... "\"");
}

if (!g_hPatch_sgspread.Enable()) {
SetFailState("Failed to enable MemoryPatch \"" ... KEY_SGSPREAD ... "\"");

Address pFinalAddr = g_hPatchSgSpread.Address + view_as<Address>(g_iCenterPelletOffset[g_ePlatform]);
int iCurrentValue = LoadFromAddress(pFinalAddr, NumberType_Int8);
if (iCurrentValue != 1) {
SetFailState("Center pellet offset is uncorrect! CheckByte: %x", iCurrentValue);
}

g_ePlatform = GameConfGetOffset(conf, "OS");
if (g_ePlatform == -1) {
SetFailState("Failed to retrieve offset \"OS\"");

if (!g_hPatchSgSpread.Enable()) {
SetFailState("Failed to enable MemoryPatch \"" ... KEY_SGSPREAD ... "\"");
}

delete conf;

hRing1BulletsCvar = CreateConVar("sgspread_ring1_bullets", "3", "Number of bullets for the first ring, the remaining bullets will be in the second ring.");
hRing1FactorCvar = CreateConVar("sgspread_ring1_factor", "2", "Determines how far or closer the bullets will be from the center for the first ring.");
hCenterPelletCvar = CreateConVar("sgspread_center_pellet", "1", "Center pellet: 0 - off, 1 - on.", _, true, 0.0, true, 1.0);

hRing1BulletsCvar.AddChangeHook(OnRing1BulletsChange);
hRing1FactorCvar.AddChangeHook(OnRing1FactorChange);
hCenterPelletCvar.AddChangeHook(OnCenterPelletChange);

HotPatchBullets(hRing1BulletsCvar.IntValue);
HotPatchFactor(hRing1FactorCvar.IntValue);
HotPatchCenterPellet(hCenterPelletCvar.BoolValue);

delete hConf;
}

static void HotPatchCenterPellet(bool newValue)
void InitPlugin()
{
Address pAddr = g_hPatch_sgspread.Address;

int currentValue = LoadFromAddress(pAddr + view_as<Address>(g_CenterPelletOffset[g_ePlatform]), NumberType_Int8);

int bullets = hRing1BulletsCvar.IntValue;

#if DEBUG
static bool IsFirstPatch = false;
if (!IsFirstPatch) {
PrintToServer("Center pellet offset is %s! CheckByte: %x", (currentValue == 0x01) ? "correct ": "uncorrect", currentValue);
PrintToChatAll("Center pellet offset is %s! CheckByte: %x", (currentValue == 0x01) ? "correct ": "uncorrect", currentValue);
IsFirstPatch = true;
for (int i = 0; i < BULLET_MAX_SIZE; i++) {
g_pBullets[i] = g_hPatchSgSpread.Address + view_as<Address>(g_iBulletOffsets[g_ePlatform][i]);
}
#endif

if (!!currentValue == newValue) {
return;
}

StoreToAddress(pAddr + view_as<Address>(g_CenterPelletOffset[g_ePlatform]), view_as<int>(newValue), NumberType_Int8);

StoreToAddress(pAddr + view_as<Address>(g_BulletOffsets[g_ePlatform][0]), bullets + (1 - view_as<int>(!newValue)), NumberType_Int8);
StoreToAddress(pAddr + view_as<Address>(g_BulletOffsets[g_ePlatform][1]), bullets + (1 - view_as<int>(!newValue)), NumberType_Int8);
StoreToAddress(pAddr + view_as<Address>(g_BulletOffsets[g_ePlatform][2]), bullets + (2 - view_as<int>(!newValue)), NumberType_Int8);

g_pDegree = g_hPatchSgSpread.Address + view_as<Address>(g_iDegreeOffsets[g_ePlatform]);
g_pFactor = g_hPatchSgSpread.Address + view_as<Address>(g_iFactorOffset[g_ePlatform]);
g_pCenterPellet = g_hPatchSgSpread.Address + view_as<Address>(g_iCenterPelletOffset[g_ePlatform]);

HotPatchBullets(g_hCvarRing1Bullets.IntValue);
HotPatchFactor(g_hCvarRing1Factor.IntValue);
HotPatchCenterPellet(g_hCvarCenterPellet.BoolValue);

g_hCvarRing1Bullets.AddChangeHook(OnRing1BulletsChange);
g_hCvarRing1Factor.AddChangeHook(OnRing1FactorChange);
g_hCvarCenterPellet.AddChangeHook(OnCenterPelletChange);
}

static void HotPatchBullets(int nBullets)
public void OnPluginEnd()
{
bool centerpellet = !hCenterPelletCvar.BoolValue;
float degree = 0.0;

if (g_ePlatform == eWindows) {
degree = 360.0 / float(nBullets);
} else {
degree = 360.0 / (2.0 * float(nBullets));
if (g_pCenterPellet != Address_Null) {
int iCurrentValue = LoadFromAddress(g_pCenterPellet, NumberType_Int8);

if (iCurrentValue != 1) {
StoreToAddress(g_pCenterPellet, 1, NumberType_Int8);
}
}

Address pAddr = g_hPatch_sgspread.Address;

StoreToAddress(pAddr + view_as<Address>(g_BulletOffsets[g_ePlatform][0]), nBullets + (1 - view_as<int>(centerpellet)), NumberType_Int8);
StoreToAddress(pAddr + view_as<Address>(g_BulletOffsets[g_ePlatform][1]), nBullets + (1 - view_as<int>(centerpellet)), NumberType_Int8);
StoreToAddress(pAddr + view_as<Address>(g_BulletOffsets[g_ePlatform][2]), nBullets + (2 - view_as<int>(centerpellet)), NumberType_Int8);

StoreToAddress(pAddr + view_as<Address>(g_BulletOffsets[g_ePlatform][3]), view_as<int>(degree), NumberType_Int32);
}

static void HotPatchFactor(int factor)
void OnRing1BulletsChange(ConVar hConVar, const char[] sOldValue, const char[] sNewValue)
{
Address pAddr = g_hPatch_sgspread.Address;
HotPatchBullets(hConVar.IntValue);
}

if (g_ePlatform == eWindows) {
StoreToAddress(pAddr + view_as<Address>(g_FactorOffset[eWindows]), view_as<int>(float(factor)), NumberType_Int32); //On windows need the float type !!!
return;
void OnRing1FactorChange(ConVar hConVar, const char[] sOldValue, const char[] sNewValue)
{
HotPatchFactor(hConVar.IntValue);
}

void OnCenterPelletChange(ConVar hConVar, const char[] sOldValue, const char[] sNewValue)
{
HotPatchCenterPellet(hConVar.BoolValue);
}

void HotPatchBullets(int iBullets)
{
PatchBullets(iBullets, g_hCvarCenterPellet.BoolValue);

float fBullets = float(iBullets);
if (g_ePlatform == eLinux) {
fBullets *= 2.0;
}
StoreToAddress(pAddr + view_as<Address>(g_FactorOffset[eLinux]), factor, NumberType_Int32);

StoreToAddress(g_pDegree, view_as<int>(360.0 / fBullets), NumberType_Int32);
}

void OnRing1BulletsChange(ConVar hCvar, const char[] oldVal, const char[] newVal)
void HotPatchFactor(int iFactor)
{
int nBullets = StringToInt(newVal);
HotPatchBullets(nBullets);
// Asm patch on windows need the float type !
int iSetFactor = (g_ePlatform == eWindows) ? view_as<int>(float(iFactor)) : iFactor;
StoreToAddress(g_pFactor, iSetFactor, NumberType_Int32);
}

void OnRing1FactorChange(ConVar hCvar, const char[] oldVal, const char[] newVal)
void HotPatchCenterPellet(bool bNewValue)
{
int factor = StringToInt(newVal);
HotPatchFactor(factor);
bool iCurrentValue = LoadFromAddress(g_pCenterPellet, NumberType_Int8);
if (iCurrentValue == bNewValue) {
return;
}

StoreToAddress(g_pCenterPellet, view_as<int>(bNewValue), NumberType_Int8);

PatchBullets(g_hCvarRing1Bullets.IntValue, bNewValue);
}

void OnCenterPelletChange(ConVar hCvar, const char[] oldVal, const char[] newVal)
void PatchBullets(int iBullets, bool bCenterPellet)
{
bool value = !!StringToInt(newVal);
HotPatchCenterPellet(value);
int iOffset1 = iBullets + (bCenterPellet ? 1 : 0);
int iOffset2 = iBullets + (bCenterPellet ? 2 : 1);

StoreToAddress(g_pBullets[0], iOffset1, NumberType_Int8);
StoreToAddress(g_pBullets[1], iOffset1, NumberType_Int8);
StoreToAddress(g_pBullets[2], iOffset2, NumberType_Int8);
}