|
4 | 4 | #include <sourcemod> |
5 | 5 | #include <dhooks> |
6 | 6 |
|
7 | | -#define Z_JOCKEY 5 |
8 | | -#define TEAM_SURVIVOR 2 |
9 | | -#define TEAM_INFECTED 3 |
| 7 | +#define Z_JOCKEY 5 |
10 | 8 |
|
11 | | -#define GAMEDATA "l4d2_si_ability" |
| 9 | +#define TEAM_SURVIVOR 2 |
| 10 | +#define TEAM_INFECTED 3 |
12 | 11 |
|
13 | | -Handle hCLeap_OnTouch; |
| 12 | +#define GAMEDATA "l4d2_si_ability" |
| 13 | +#define L4D2_MAXPLAYERS 32 |
14 | 14 |
|
15 | | -bool blockJumpCap[MAXPLAYERS + 1]; |
| 15 | +enum struct eShovedInfo |
| 16 | +{ |
| 17 | + int eiUserId; |
| 18 | + float efBlockUntil; |
| 19 | +} |
| 20 | + |
| 21 | +ConVar g_hCvarJumpCapBlockTime = null; |
| 22 | + |
| 23 | +Handle g_hCLeap_OnTouch = null; |
| 24 | + |
| 25 | +eShovedInfo g_esShovedInfo[L4D2_MAXPLAYERS + 1]; |
16 | 26 |
|
17 | 27 | public Plugin myinfo = |
18 | 28 | { |
19 | 29 | name = "L4D2 Jockey Jump-Cap Patch", |
20 | 30 | author = "Visor, A1m`", |
21 | 31 | description = "Prevent Jockeys from being able to land caps with non-ability jumps in unfair situations", |
22 | | - version = "1.4", |
| 32 | + version = "1.6", |
23 | 33 | url = "https://github.com/SirPlease/L4D2-Competitive-Rework" |
24 | 34 | }; |
25 | 35 |
|
26 | 36 | public void OnPluginStart() |
| 37 | +{ |
| 38 | + InitGameData(); |
| 39 | + |
| 40 | + g_hCvarJumpCapBlockTime = CreateConVar( |
| 41 | + "l4d2_jumpcap_block_time", |
| 42 | + "3.0", |
| 43 | + "Sets the block duration for jockey jumpcaps (in seconds)", |
| 44 | + _, true, 1.0, true, 10.0 |
| 45 | + ); |
| 46 | + |
| 47 | + HookEvent("round_start", Event_Reset, EventHookMode_PostNoCopy); |
| 48 | + HookEvent("round_end", Event_Reset, EventHookMode_PostNoCopy); |
| 49 | + HookEvent("player_shoved", Event_PlayerShoved); |
| 50 | +} |
| 51 | + |
| 52 | +void InitGameData() |
27 | 53 | { |
28 | 54 | Handle hGamedata = LoadGameConfigFile(GAMEDATA); |
29 | 55 |
|
30 | 56 | if (!hGamedata) { |
31 | 57 | SetFailState("Gamedata '%s.txt' missing or corrupt.", GAMEDATA); |
32 | 58 | } |
33 | | - |
34 | | - int iCleapOnTouch = GameConfGetOffset(hGamedata, "CBaseAbility::OnTouch"); |
35 | | - if (iCleapOnTouch == -1) { |
| 59 | + |
| 60 | + int iCleapOnTouchOffset = GameConfGetOffset(hGamedata, "CBaseAbility::OnTouch"); |
| 61 | + if (iCleapOnTouchOffset == -1) { |
36 | 62 | SetFailState("Failed to get offset 'CBaseAbility::OnTouch'."); |
37 | 63 | } |
38 | 64 |
|
39 | | - hCLeap_OnTouch = DHookCreate(iCleapOnTouch, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, CLeap_OnTouch); |
40 | | - DHookAddParam(hCLeap_OnTouch, HookParamType_CBaseEntity); |
41 | | - |
42 | | - HookEvent("round_start", ResetEvent, EventHookMode_PostNoCopy); |
43 | | - HookEvent("round_end", ResetEvent, EventHookMode_PostNoCopy); |
44 | | - HookEvent("player_shoved", OnPlayerShoved); |
45 | | - |
| 65 | + g_hCLeap_OnTouch = DHookCreate(iCleapOnTouchOffset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity, CLeap_OnTouch); |
| 66 | + DHookAddParam(g_hCLeap_OnTouch, HookParamType_CBaseEntity); |
| 67 | + |
46 | 68 | delete hGamedata; |
47 | 69 | } |
48 | 70 |
|
49 | | -void ResetEvent(Event hEvent, const char[] name, bool dontBroadcast) |
| 71 | +void Event_Reset(Event hEvent, const char[] sEventName, bool bDontBroadcast) |
50 | 72 | { |
51 | | - for (int i = 0; i <= MAXPLAYERS; i++) { |
52 | | - blockJumpCap[i] = false; |
| 73 | + for (int i = 0; i <= L4D2_MAXPLAYERS; i++) { |
| 74 | + g_esShovedInfo[i].eiUserId = 0; |
| 75 | + g_esShovedInfo[i].efBlockUntil = 0.0; |
53 | 76 | } |
54 | 77 | } |
55 | 78 |
|
56 | | -public void OnEntityCreated(int entity, const char[] classname) |
| 79 | +public void OnEntityCreated(int iEntity, const char[] sClassName) |
57 | 80 | { |
58 | | - if (strcmp(classname, "ability_leap") == 0) { |
59 | | - DHookEntity(hCLeap_OnTouch, false, entity); |
| 81 | + if (strcmp(sClassName, "ability_leap") == 0) { |
| 82 | + DHookEntity(g_hCLeap_OnTouch, false, iEntity); |
60 | 83 | } |
61 | 84 | } |
62 | 85 |
|
63 | | -void OnPlayerShoved(Event hEvent, const char[] name, bool dontBroadcast) |
| 86 | +void Event_PlayerShoved(Event hEvent, const char[] sEventName, bool bDontBroadcast) |
64 | 87 | { |
65 | | - int shovee = GetClientOfUserId(hEvent.GetInt("userid")); |
66 | | - int shover = GetClientOfUserId(hEvent.GetInt("attacker")); |
67 | | - |
68 | | - if (IsSurvivor(shover) && IsJockey(shovee)) { |
69 | | - blockJumpCap[shovee] = true; |
70 | | - CreateTimer(3.0, ResetJumpcapState, shovee, TIMER_FLAG_NO_MAPCHANGE); |
| 88 | + int iShover = GetClientOfUserId(hEvent.GetInt("attacker")); |
| 89 | + if (!IsSurvivor(iShover)) { |
| 90 | + return; |
71 | 91 | } |
72 | | -} |
73 | 92 |
|
74 | | -Action ResetJumpcapState(Handle hTimer, any jockey) |
75 | | -{ |
76 | | - blockJumpCap[jockey] = false; |
77 | | - return Plugin_Handled; |
| 93 | + int iShoveeUserId = hEvent.GetInt("userid"); |
| 94 | + int iShovee = GetClientOfUserId(iShoveeUserId); |
| 95 | + if (IsJockey(iShovee)) { |
| 96 | + g_esShovedInfo[iShovee].eiUserId = iShoveeUserId; |
| 97 | + g_esShovedInfo[iShovee].efBlockUntil = GetGameTime() + g_hCvarJumpCapBlockTime.FloatValue; |
| 98 | + } |
78 | 99 | } |
79 | 100 |
|
80 | | -MRESReturn CLeap_OnTouch(int ability, Handle hParams) |
| 101 | +MRESReturn CLeap_OnTouch(int iAbility, DHookParam hParams) |
81 | 102 | { |
82 | | - int jockey = GetEntPropEnt(ability, Prop_Send, "m_owner"); |
83 | | - if (IsJockey(jockey) && !IsFakeClient(jockey)) { |
84 | | - int survivor = DHookGetParam(hParams, 1); |
85 | | - if (IsSurvivor(survivor)) { |
86 | | - if (!IsAbilityActive(ability) && blockJumpCap[jockey]) { |
87 | | - return MRES_Supercede; |
88 | | - } |
89 | | - } |
| 103 | + int iJockey = GetEntPropEnt(iAbility, Prop_Send, "m_owner"); |
| 104 | + if (!IsJockey(iJockey) || IsFakeClient(iJockey)) { |
| 105 | + return MRES_Ignored; |
| 106 | + } |
| 107 | + |
| 108 | + int iSurvivor = hParams.Get(1); |
| 109 | + if (!IsSurvivor(iSurvivor)) { |
| 110 | + return MRES_Ignored; |
| 111 | + } |
| 112 | + |
| 113 | + if (!IsAbilityActive(iAbility) && IsJumpBlocked(iJockey)) { |
| 114 | + return MRES_Supercede; |
90 | 115 | } |
| 116 | + |
91 | 117 | return MRES_Ignored; |
92 | 118 | } |
93 | 119 |
|
94 | | -bool IsAbilityActive(int ability) |
| 120 | +bool IsJumpBlocked(int iClient) |
| 121 | +{ |
| 122 | + return (g_esShovedInfo[iClient].eiUserId == GetClientUserId(iClient) |
| 123 | + && g_esShovedInfo[iClient].efBlockUntil >= GetGameTime()); |
| 124 | +} |
| 125 | + |
| 126 | +bool IsAbilityActive(int iAbility) |
95 | 127 | { |
96 | | - return view_as<bool>(GetEntProp(ability, Prop_Send, "m_isLeaping", 1)); |
| 128 | + return (GetEntProp(iAbility, Prop_Send, "m_isLeaping", 1) > 0); |
97 | 129 | } |
98 | 130 |
|
99 | | -bool IsJockey(int client) |
| 131 | +bool IsJockey(int iClient) |
100 | 132 | { |
101 | | - return (client > 0 |
102 | | - && client <= MaxClients |
103 | | - && IsClientInGame(client) |
104 | | - && GetClientTeam(client) == TEAM_INFECTED |
105 | | - && GetEntProp(client, Prop_Send, "m_zombieClass") == Z_JOCKEY); |
| 133 | + return (iClient > 0 |
| 134 | + && iClient <= MaxClients |
| 135 | + && IsClientInGame(iClient) |
| 136 | + && GetClientTeam(iClient) == TEAM_INFECTED |
| 137 | + && GetEntProp(iClient, Prop_Send, "m_zombieClass") == Z_JOCKEY); |
106 | 138 | } |
107 | 139 |
|
108 | | -bool IsSurvivor(int client) |
| 140 | +bool IsSurvivor(int iClient) |
109 | 141 | { |
110 | | - return (client > 0 |
111 | | - && client <= MaxClients |
112 | | - && IsClientInGame(client) |
113 | | - && GetClientTeam(client) == TEAM_SURVIVOR); |
| 142 | + return (iClient > 0 |
| 143 | + && iClient <= MaxClients |
| 144 | + && IsClientInGame(iClient) |
| 145 | + && GetClientTeam(iClient) == TEAM_SURVIVOR); |
114 | 146 | } |
0 commit comments