Skip to content

Commit c406449

Browse files
committed
Update l4d2_car_alarm_hittable_fix ( #541 )
- Added functionality to trigger car alarms when Survivors simply touch the car. - Added convars to control whether we care regarding AI and/or Survivors are capped when touching the car. - These changes do not mess with normal game logic, they simply add on to it.
1 parent ed94f28 commit c406449

3 files changed

Lines changed: 222 additions & 28 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"Games"
2+
{
3+
"left4dead2"
4+
{
5+
"Addresses"
6+
{
7+
"CCarProp::InputSurvivorStandingOnCar"
8+
{
9+
"windows"
10+
{
11+
"signature" "CCarProp::InputSurvivorStandingOnCar"
12+
}
13+
"linux"
14+
{
15+
"signature" "CCarProp::InputSurvivorStandingOnCar"
16+
}
17+
}
18+
}
19+
20+
"Offsets"
21+
{
22+
"InputSurvivorStandingOnCar_Offset"
23+
{
24+
"windows" "227" // 0xE3
25+
"linux" "260" // 0x104
26+
}
27+
"InputSurvivorStandingOnCar_Byte"
28+
{
29+
"windows" "116" // 0x74
30+
"linux" "15" // 0x0F
31+
}
32+
"InputSurvivorStandingOnCar_Count"
33+
{
34+
"windows" "1"
35+
"linux" "6"
36+
}
37+
}
38+
39+
"Signatures"
40+
{
41+
"CCarProp::InputSurvivorStandingOnCar"
42+
{
43+
"library" "server"
44+
"linux" "@_ZN8CCarProp26InputSurvivorStandingOnCarER11inputdata_t"
45+
"windows" "\x55\x8B\x2A\x83\x2A\x2A\x56\x8B\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x0F\x85\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x0F\x85\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x0F\x85"
46+
/* 55 8B ? 83 ? ? 56 8B ? ? ? ? ? ? ? ? 0F 85 ? ? ? ? ? ? ? ? ? ? ? 0F 85 ? ? ? ? ? ? ? ? ? ? ? 0F 85 */
47+
/* Search: "triggered_car_alarm" */
48+
}
49+
}
50+
}
51+
}
1.21 KB
Binary file not shown.
Lines changed: 171 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,155 @@
11
#pragma semicolon 1
22
#pragma newdecls required
33

4-
#include <sourcemod>
5-
#include <sdkhooks>
6-
#include <sdktools>
4+
#include <sourcemod>
5+
#include <sdktools>
6+
#include <sdkhooks>
77

8+
#define GAMEDATA "l4d2_car_alarm_bots"
9+
#define MAX_BYTES 33
10+
int g_ByteCount, g_ByteMatch;
11+
int g_ByteSaved[MAX_BYTES];
12+
Address g_Address;
13+
14+
ConVar
15+
g_hCarAlarmSettings,
16+
g_hCarTouchCapped,
17+
g_hCarAI;
18+
19+
int FLAGS[3] = {
20+
1 << 0, // Trigger Car Alarm on Survivor Touch
21+
1 << 1, // Trigger Car Alarm disabled when hit by another Hittable.
22+
};
23+
24+
int iFlags;
25+
bool bCarTouchCapped;
26+
bool bAI;
27+
28+
29+
// ====================================================================================================
30+
// PLUGIN INFO / START / END
31+
// ====================================================================================================
832
public Plugin myinfo =
933
{
10-
name = "L4D2 Car Alarm Hittable Fix",
11-
author = "Sir",
12-
description = "Disables the Car Alarm when a Tank hittable hits the alarmed car.",
13-
version = "1.1",
14-
url = "nah"
34+
name = "L4D2 Car Alarm Fixes",
35+
author = "Sir & Silvers (Gamedata and general idea from l4d2_car_alarm_bots)",
36+
description = "Disables the Car Alarm when a Tank hittable hits the alarmed car and makes sure the Car Alarm triggers whenever a Survivor touches it",
37+
version = "1.2",
38+
url = "https://github.com/SirPlease/L4D2-Competitive-Rework"
1539
};
1640

17-
public void OnEntityCreated(int entity, const char[] classname)
41+
public void OnPluginStart()
1842
{
19-
// Hook Alarmed Cars.
20-
if (!StrEqual(classname, "prop_car_alarm")) return;
21-
SDKHook(entity, SDKHook_Touch, OnAlarmCarTouch);
43+
// ====================================================================================================
44+
// GAMEDATA
45+
// ====================================================================================================
46+
char sPath[PLATFORM_MAX_PATH];
47+
BuildPath(Path_SM, sPath, sizeof(sPath), "gamedata/%s.txt", GAMEDATA);
48+
if( FileExists(sPath) == false ) SetFailState("\n==========\nMissing required file: \"%s\".\nRead installation instructions again.\n==========", sPath);
49+
50+
Handle hGameData = LoadGameConfigFile(GAMEDATA);
51+
if( hGameData == null ) SetFailState("Failed to load \"%s.txt\" gamedata.", GAMEDATA);
52+
53+
g_Address = GameConfGetAddress(hGameData, "CCarProp::InputSurvivorStandingOnCar");
54+
if( !g_Address ) SetFailState("Failed to load \"CCarProp::InputSurvivorStandingOnCar\" address.");
55+
56+
int offset = GameConfGetOffset(hGameData, "InputSurvivorStandingOnCar_Offset");
57+
if( offset == -1 ) SetFailState("Failed to load \"InputSurvivorStandingOnCar_Offset\" offset.");
58+
59+
g_ByteMatch = GameConfGetOffset(hGameData, "InputSurvivorStandingOnCar_Byte");
60+
if( g_ByteMatch == -1 ) SetFailState("Failed to load \"InputSurvivorStandingOnCar_Byte\" byte.");
61+
62+
g_ByteCount = GameConfGetOffset(hGameData, "InputSurvivorStandingOnCar_Count");
63+
if( g_ByteCount == -1 ) SetFailState("Failed to load \"InputSurvivorStandingOnCar_Count\" count.");
64+
if( g_ByteCount > MAX_BYTES ) SetFailState("Error: byte count exceeds scripts defined value (%d/%d).", g_ByteCount, MAX_BYTES);
65+
66+
g_Address += view_as<Address>(offset);
67+
68+
for( int i = 0; i < g_ByteCount; i++ )
69+
{
70+
g_ByteSaved[i] = LoadFromAddress(g_Address + view_as<Address>(i), NumberType_Int8);
71+
}
72+
if( g_ByteSaved[0] != g_ByteMatch ) SetFailState("Failed to load, byte mis-match. %d (0x%02X != 0x%02X)", offset, g_ByteSaved[0], g_ByteMatch);
73+
74+
delete hGameData;
75+
76+
// =================================================================================================
77+
// CONVARS
78+
// =================================================================================================
79+
80+
g_hCarAlarmSettings = CreateConVar("l4d2_car_alarm_settings", "3", "Bitmask: 1-Trigger Alarm on Survivor Touch/ 2-Disable Alarm when a Hittable hits the Alarm Car", FCVAR_NOTIFY);
81+
g_hCarTouchCapped = CreateConVar("l4d2_car_alarm_touch_capped", "1", "Only add the additional car alarm trigger when the Survivor is capped by an Infected when touching the car? (Requires bitmask settings)", FCVAR_NOTIFY, true, 0.0, true, 1.0);
82+
g_hCarAI = CreateConVar("l4d2_car_alarm_touch_ai", "0", "Care about AI Survivors touching the car? (Default vanilla = 0) Requires bitmask settings", FCVAR_NOTIFY, true, 0.0, true, 1.0);
83+
84+
iFlags = g_hCarAlarmSettings.IntValue;
85+
bCarTouchCapped = g_hCarTouchCapped.BoolValue;
86+
bAI = g_hCarAI.BoolValue;
87+
g_hCarAlarmSettings.AddChangeHook(ChangedConVars);
88+
g_hCarTouchCapped.AddChangeHook(ChangedConVars);
89+
g_hCarAI.AddChangeHook(ChangedConVars);
2290
}
2391

24-
public Action OnAlarmCarTouch(int car, int entity)
25-
{
26-
// Speaks for itself
27-
if (IsTankHittable(entity))
92+
public void OnPluginEnd()
93+
{
94+
PatchAddress(false);
95+
}
96+
97+
// ====================================================================================================
98+
// PATCH / HOOK
99+
// ====================================================================================================
100+
void PatchAddress(int patch)
101+
{
102+
static bool patched;
103+
104+
if( !patched && patch )
105+
{
106+
patched = true;
107+
for( int i = 0; i < g_ByteCount; i++ )
108+
StoreToAddress(g_Address + view_as<Address>(i), g_ByteMatch == 0x0F ? 0x90 : 0xEB, NumberType_Int8);
109+
}
110+
else if( patched && !patch )
111+
{
112+
patched = false;
113+
for( int i = 0; i < g_ByteCount; i++ )
114+
StoreToAddress(g_Address + view_as<Address>(i), g_ByteSaved[i], NumberType_Int8);
115+
}
116+
}
117+
118+
// ====================================================================================================
119+
// EVENTS
120+
// ====================================================================================================
121+
public void OnEntityCreated(int entity, const char[] classname)
122+
{
123+
if(strcmp(classname, "prop_car_alarm") == 0)
124+
SDKHook(entity, SDKHook_Touch, OnTouch);
125+
}
126+
127+
public void OnTouch(int car, int other)
128+
{
129+
// Is the other entity a Survivor?
130+
if ((iFlags & FLAGS[0]) && other >= 1 && other <= MaxClients && GetClientTeam(other) == 2)
131+
{
132+
// We don't want the AI to trigger the car alarm.
133+
if (!bAI && IsFakeClient(other))
134+
return;
135+
136+
// We only care about capped players touching the car.
137+
if (bCarTouchCapped && !IsPlayerCapped(other))
138+
return;
139+
140+
PatchAddress(true);
141+
AcceptEntityInput(car, "SurvivorStandingOnCar", other, other);
142+
PatchAddress(false);
143+
144+
// Unhook car, we don't need it anymore.
145+
SDKUnhook(car, SDKHook_Touch, OnTouch);
146+
}
147+
148+
// Is the other entity a Hittable car?
149+
else if ((iFlags & FLAGS[1]) && IsTankHittable(other))
28150
{
29151
// This returns 1 on every hittable at all times.
30-
if (GetEntProp(entity, Prop_Send, "m_hasTankGlow") > 0)
152+
if (GetEntProp(other, Prop_Send, "m_hasTankGlow") > 0)
31153
{
32154
// Disable the Car Alarm
33155
AcceptEntityInput(car, "Disable");
@@ -36,11 +158,9 @@ public Action OnAlarmCarTouch(int car, int entity)
36158
CreateTimer(0.3, DisableAlarm, car);
37159

38160
// Unhook car, we don't need it anymore.
39-
SDKUnhook(car, SDKHook_Touch, OnAlarmCarTouch);
161+
SDKUnhook(car, SDKHook_Touch, OnTouch);
40162
}
41163
}
42-
43-
return Plugin_Continue;
44164
}
45165

46166
public Action DisableAlarm(Handle timer, any car)
@@ -56,32 +176,55 @@ public Action DisableAlarm(Handle timer, any car)
56176
}
57177
}
58178

59-
if (Tank != -1) SDKHooks_TakeDamage(car, Tank, Tank, 0.0);
179+
if (Tank != -1)
180+
SDKHooks_TakeDamage(car, Tank, Tank, 0.0);
60181

61182
return Plugin_Stop;
62183
}
63184

185+
186+
// ====================================================================================================
187+
// STOCKS
188+
// ====================================================================================================
64189
stock bool IsValidTank(int client)
65190
{
66-
if (client <= 0 || client > MaxClients || !IsClientConnected(client)) return false;
67-
return (IsClientInGame(client) && GetClientTeam(client) == 3 && GetEntProp(client, Prop_Send, "m_zombieClass") == 8);
191+
if (client <= 0 || client > MaxClients || !IsClientConnected(client)) return false;
192+
return (IsClientInGame(client) && GetClientTeam(client) == 3 && GetEntProp(client, Prop_Send, "m_zombieClass") == 8);
68193
}
69194

70195
stock bool IsTankHittable(int iEntity)
71196
{
72197
if (!IsValidEntity(iEntity))
73-
{
74-
return false;
75-
}
198+
return false;
76199

77200
char className[64];
78201

79202
GetEdictClassname(iEntity, className, sizeof(className));
203+
80204
if (StrEqual(className, "prop_physics"))
81205
{
82-
if (GetEntProp(iEntity, Prop_Send, "m_hasTankGlow", 1)) return true;
206+
if (GetEntProp(iEntity, Prop_Send, "m_hasTankGlow", 1))
207+
return true;
83208
}
84-
else if (StrEqual(className, "prop_car_alarm")) return true;
209+
else if (StrEqual(className, "prop_car_alarm"))
210+
return true;
85211

86212
return false;
213+
}
214+
215+
stock bool IsPlayerCapped(int client)
216+
{
217+
if (GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0 ||
218+
GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0 ||
219+
GetEntPropEnt(client, Prop_Send, "m_carryAttacker") > 0)
220+
return true;
221+
222+
return false;
223+
}
224+
225+
void ChangedConVars(ConVar convar, const char[] oldValue, const char[] newValue)
226+
{
227+
iFlags = g_hCarAlarmSettings.IntValue;
228+
bCarTouchCapped = g_hCarTouchCapped.BoolValue;
229+
bAI = g_hCarAI.BoolValue;
87230
}

0 commit comments

Comments
 (0)