|
1 | 1 | #include <sourcemod>
|
| 2 | +#include <sdkhooks> |
2 | 3 | #include <sdktools>
|
3 | 4 | #include <cstrike>
|
4 |
| -#include <retakes> |
5 | 5 |
|
6 |
| -#pragma newdecls required |
7 | 6 | #pragma semicolon 1
|
| 7 | +#pragma newdecls required |
8 | 8 |
|
9 |
| -Bombsite activeBombsite; |
10 |
| -bool hasBombBeenDeleted; |
11 |
| - |
12 |
| -ConVar isPluginEnabled; |
13 |
| -ConVar freezeTime; |
14 |
| - |
15 |
| -float bombPosition[3]; |
16 |
| - |
17 |
| -Handle bombTimer; |
18 |
| - |
19 |
| -int bombTicking; |
20 |
| - |
21 |
| -public Plugin myinfo = |
22 |
| -{ |
23 |
| - name = "[Retakes] Autoplant", |
24 |
| - author = "b3none", |
25 |
| - description = "Autoplant the bomb for CS:GO Retakes.", |
| 9 | +#define MESSAGE_PREFIX "[\x02InstaDefuse\x01]" |
| 10 | + |
| 11 | +Handle hOnlyTooLate = null; |
| 12 | +Handle hcv_InfernoDuration = null; |
| 13 | +Handle hTimer_MolotovThreatEnd = null; |
| 14 | + |
| 15 | +public Plugin myinfo = { |
| 16 | + name = "[Retakes] Instant Defuse", |
| 17 | + author = "B3none, Eyal282", |
| 18 | + description = "Allows a CT to instantly defuse the bomb when all Ts are dead and nothing can prevemt the defusal.", |
26 | 19 | version = "1.0.0",
|
27 | 20 | url = "https://github.com/b3none"
|
28 |
| -}; |
| 21 | +} |
29 | 22 |
|
30 | 23 | public void OnPluginStart()
|
31 | 24 | {
|
32 |
| - isPluginEnabled = CreateConVar("sm_autoplant_enabled", "1", "Should the autoplant plugin be enabled", _, true, 0.0, true, 1.0); |
33 |
| - |
34 |
| - freezeTime = FindConVar("mp_freezetime"); |
35 |
| - |
36 |
| - bombTicking = FindSendPropInfo("CPlantedC4", "m_bBombTicking"); |
37 |
| - |
38 |
| - HookEvent("round_start", OnRoundStart, EventHookMode_PostNoCopy); |
39 |
| - HookEvent("round_end", OnRoundEnd, EventHookMode_PostNoCopy); |
| 25 | + HookEvent("bomb_begindefuse", Event_BombBeginDefuse, EventHookMode_Post); |
| 26 | + HookEvent("molotov_detonate", Event_MolotovDetonate); |
| 27 | + HookEvent("hegrenade_detonate", Event_AttemptInstantDefuse, EventHookMode_Post); |
| 28 | + |
| 29 | + HookEvent("player_death", Event_AttemptInstantDefuse, EventHookMode_PostNoCopy); |
| 30 | + HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); |
| 31 | + |
| 32 | + hcv_InfernoDuration = CreateConVar("instant_defuse_inferno_duration", "7.0", "If Valve ever changed the duration of molotov, this cvar should change with it."); |
| 33 | + hOnlyTooLate = CreateConVar("instant_defuse_only_too_late", "1.0", "If Valve ever changed the duration of molotov, this cvar should change with it.", _, true, 0.0, true, 1.0); |
40 | 34 | }
|
41 | 35 |
|
42 |
| -public Action OnRoundStart(Event eEvent, const char[] sName, bool bDontBroadcast) |
| 36 | +public void OnMapStart() |
43 | 37 | {
|
44 |
| - hasBombBeenDeleted = false; |
| 38 | + hTimer_MolotovThreatEnd = null; |
| 39 | +} |
45 | 40 |
|
46 |
| - if (isPluginEnabled.BoolValue) { |
47 |
| - for (int client = 1; client <= MaxClients; client++) { |
48 |
| - if (IsClientInGame(client) && IsPlayerAlive(client) && GetPlayerWeaponSlot(client, 4) > 0) { |
49 |
| - int bomb = GetPlayerWeaponSlot(client, 4); |
50 |
| - |
51 |
| - hasBombBeenDeleted = SafeRemoveWeapon(client, bomb); |
52 |
| - |
53 |
| - GetClientAbsOrigin(client, bombPosition); |
54 |
| - |
55 |
| - delete bombTimer; |
56 |
| - |
57 |
| - bombTimer = CreateTimer(freezeTime.FloatValue, PlantBomb, client); |
58 |
| - } |
59 |
| - } |
| 41 | +public Action Event_RoundStart(Handle event, const char[] name, bool dontBroadcast) |
| 42 | +{ |
| 43 | + if(hTimer_MolotovThreatEnd != null) |
| 44 | + { |
| 45 | + CloseHandle(hTimer_MolotovThreatEnd); |
| 46 | + hTimer_MolotovThreatEnd = null; |
60 | 47 | }
|
| 48 | +} |
61 | 49 |
|
| 50 | +public Action Event_BombBeginDefuse(Handle event, const char[] name, bool dontBroadcast) |
| 51 | +{ |
| 52 | + RequestFrame(Event_BombBeginDefusePlusFrame, GetEventInt(event, "userid")); |
| 53 | + |
62 | 54 | return Plugin_Continue;
|
63 | 55 | }
|
64 | 56 |
|
65 |
| -public void OnRoundEnd(Event event, const char[] sName, bool bDontBroadcast) |
| 57 | +public void Event_BombBeginDefusePlusFrame(int userId) |
66 | 58 | {
|
67 |
| - delete bombTimer; |
68 |
| - |
69 |
| - GameRules_SetProp("m_bBombPlanted", 0); |
| 59 | + int client = GetClientOfUserId(userId); |
| 60 | + |
| 61 | + if(IsValidClient(client)) |
| 62 | + { |
| 63 | + AttemptInstantDefuse(client); |
| 64 | + } |
70 | 65 | }
|
71 | 66 |
|
72 |
| -public Action PlantBomb(Handle timer, int client) |
| 67 | +void AttemptInstantDefuse(int client, int exemptNade = 0) |
73 | 68 | {
|
74 |
| - bombTimer = INVALID_HANDLE; |
75 |
| - |
76 |
| - if (IsValidClient(client) || !hasBombBeenDeleted) { |
77 |
| - if (hasBombBeenDeleted) { |
78 |
| - int bombEntity = CreateEntityByName("planted_c4"); |
79 |
| - |
80 |
| - GameRules_SetProp("m_bBombPlanted", 1); |
81 |
| - SetEntData(bombEntity, bombTicking, 1, 1, true); |
82 |
| - SendBombPlanted(client); |
83 |
| - |
84 |
| - if (DispatchSpawn(bombEntity)) { |
85 |
| - ActivateEntity(bombEntity); |
86 |
| - TeleportEntity(bombEntity, bombPosition, NULL_VECTOR, NULL_VECTOR); |
87 |
| - |
88 |
| - if (!(GetEntityFlags(bombEntity) & FL_ONGROUND)) { |
89 |
| - float direction[3]; |
90 |
| - float floor[3]; |
91 |
| - |
92 |
| - Handle trace; |
93 |
| - |
94 |
| - direction[0] = 89.0; |
95 |
| - |
96 |
| - TR_TraceRay(bombPosition, direction, MASK_PLAYERSOLID_BRUSHONLY, RayType_Infinite); |
97 |
| - |
98 |
| - if (TR_DidHit(trace)) { |
99 |
| - TR_GetEndPosition(floor, trace); |
100 |
| - TeleportEntity(bombEntity, floor, NULL_VECTOR, NULL_VECTOR); |
101 |
| - } |
102 |
| - } |
103 |
| - } |
| 69 | + if(!GetEntProp(client, Prop_Send, "m_bIsDefusing")) |
| 70 | + { |
| 71 | + return; |
| 72 | + } |
| 73 | + |
| 74 | + int StartEnt = MaxClients + 1; |
| 75 | + |
| 76 | + int c4 = FindEntityByClassname(StartEnt, "planted_c4"); |
| 77 | + |
| 78 | + if(c4 == -1 || FindAlivePlayer(CS_TEAM_T) != 0) |
| 79 | + { |
| 80 | + return; |
| 81 | + } |
| 82 | + else if(GetConVarInt(hOnlyTooLate) == 1 && GetEntPropFloat(c4, Prop_Send, "m_flC4Blow") < GetEntPropFloat(c4, Prop_Send, "m_flDefuseCountDown")) |
| 83 | + { |
| 84 | + // Force Terrorist win because they do not have enough time to defuse the bomb. |
| 85 | + CS_TerminateRound(1.0, CSRoundEnd_TargetBombed); |
| 86 | + return; |
| 87 | + } |
| 88 | + else if(GetEntityFlags(client) && !FL_ONGROUND) |
| 89 | + { |
| 90 | + return; |
| 91 | + } |
| 92 | + |
| 93 | + int ent; |
| 94 | + if((ent = FindEntityByClassname(StartEnt, "hegrenade_projectile")) != -1 || (ent = FindEntityByClassname(StartEnt, "molotov_projectile")) != -1) |
| 95 | + { |
| 96 | + if(ent != exemptNade) |
| 97 | + { |
| 98 | + PrintToChatAll("%s There is a live nade somewhere, Good luck defusing!", MESSAGE_PREFIX); |
| 99 | + return; |
104 | 100 | }
|
105 |
| - } else { |
106 |
| - CS_TerminateRound(1.0, CSRoundEnd_Draw); |
| 101 | + } |
| 102 | + else if(hTimer_MolotovThreatEnd != null) |
| 103 | + { |
| 104 | + PrintToChatAll("%s Molotov too close to bomb, Good luck defusing!", MESSAGE_PREFIX); |
| 105 | + return; |
107 | 106 | }
|
| 107 | + |
| 108 | + SetEntPropFloat(c4, Prop_Send, "m_flDefuseCountDown", 0.0); |
| 109 | + SetEntPropFloat(c4, Prop_Send, "m_flDefuseLength", 0.0); |
| 110 | + SetEntProp(client, Prop_Send, "m_iProgressBarDuration", 0); |
108 | 111 | }
|
109 |
| - |
110 |
| -public void SendBombPlanted(int client) |
| 112 | + |
| 113 | +public Action Event_AttemptInstantDefuse(Handle event, const char[] name, bool dontBroadcast) |
111 | 114 | {
|
112 |
| - Event event = CreateEvent("bomb_planted"); |
113 |
| - |
114 |
| - if (event != null) { |
115 |
| - event.SetInt("userid", GetClientUserId(client)); |
116 |
| - event.SetInt("site", view_as<int>(activeBombsite)); |
117 |
| - event.Fire(); |
| 115 | + int defuser = FindDefusingPlayer(); |
| 116 | + |
| 117 | + int ent = 0; |
| 118 | + |
| 119 | + if(StrContains(name, "detonate") != -1) |
| 120 | + { |
| 121 | + ent = GetEventInt(event, "entityid"); |
118 | 122 | }
|
| 123 | + |
| 124 | + if(defuser != 0) |
| 125 | + { |
| 126 | + AttemptInstantDefuse(defuser, ent); |
| 127 | + } |
119 | 128 | }
|
120 | 129 |
|
121 |
| -public void Retakes_OnSitePicked(Bombsite& selectedBombsite) |
| 130 | +public Action Event_MolotovDetonate(Handle event, const char[] name, bool dontBroadcast) |
122 | 131 | {
|
123 |
| - activeBombsite = selectedBombsite; |
| 132 | + float Origin[3]; |
| 133 | + Origin[0] = GetEventFloat(event, "x"); |
| 134 | + Origin[1] = GetEventFloat(event, "y"); |
| 135 | + Origin[2] = GetEventFloat(event, "z"); |
| 136 | + |
| 137 | + int c4 = FindEntityByClassname(MaxClients + 1, "planted_c4"); |
| 138 | + |
| 139 | + if(c4 == -1) |
| 140 | + { |
| 141 | + return; |
| 142 | + } |
| 143 | + |
| 144 | + float C4Origin[3]; |
| 145 | + GetEntPropVector(c4, Prop_Data, "m_vecOrigin", C4Origin); |
| 146 | + |
| 147 | + if(GetVectorDistance(Origin, C4Origin, false) > 150) |
| 148 | + { |
| 149 | + return; |
| 150 | + } |
| 151 | + |
| 152 | + if(hTimer_MolotovThreatEnd != null) |
| 153 | + { |
| 154 | + CloseHandle(hTimer_MolotovThreatEnd); |
| 155 | + hTimer_MolotovThreatEnd = null; |
| 156 | + } |
| 157 | + |
| 158 | + hTimer_MolotovThreatEnd = CreateTimer(GetConVarFloat(hcv_InfernoDuration), Timer_MolotovThreatEnd, _, TIMER_FLAG_NO_MAPCHANGE); |
124 | 159 | }
|
125 |
| - |
126 |
| -stock bool SafeRemoveWeapon(int client, int weapon) |
| 160 | + |
| 161 | +public Action Timer_MolotovThreatEnd(Handle timer) |
127 | 162 | {
|
128 |
| - if (!IsValidEntity(weapon) || !IsValidEdict(weapon) || !HasEntProp(weapon, Prop_Send, "m_hOwnerEntity")) { |
129 |
| - return false; |
| 163 | + hTimer_MolotovThreatEnd = null; |
| 164 | + |
| 165 | + int defuser = FindDefusingPlayer(); |
| 166 | + |
| 167 | + if(defuser != 0) |
| 168 | + { |
| 169 | + AttemptInstantDefuse(defuser); |
130 | 170 | }
|
131 |
| - |
132 |
| - int ownerEntity = GetEntPropEnt(weapon, Prop_Send, "m_hOwnerEntity"); |
133 |
| - |
134 |
| - if (ownerEntity != client) { |
135 |
| - SetEntPropEnt(weapon, Prop_Send, "m_hOwnerEntity", client); |
| 171 | +} |
| 172 | + |
| 173 | +stock int FindDefusingPlayer() |
| 174 | +{ |
| 175 | + for(int i = 1; i <= MaxClients; i++) |
| 176 | + { |
| 177 | + if(IsValidClient(i) && IsPlayerAlive(i) && GetEntProp(i, Prop_Send, "m_bIsDefusing")) |
| 178 | + { |
| 179 | + return i; |
| 180 | + } |
136 | 181 | }
|
137 |
| - |
138 |
| - CS_DropWeapon(client, weapon, false); |
139 |
| - |
140 |
| - if (HasEntProp(weapon, Prop_Send, "m_hWeaponWorldModel")) { |
141 |
| - int worldModel = GetEntPropEnt(weapon, Prop_Send, "m_hWeaponWorldModel"); |
142 |
| - |
143 |
| - if (IsValidEdict(worldModel) && IsValidEntity(worldModel)) { |
144 |
| - if (!AcceptEntityInput(worldModel, "Kill")) { |
145 |
| - return false; |
146 |
| - } |
| 182 | + |
| 183 | + return 0; |
| 184 | +} |
| 185 | + |
| 186 | +stock int FindAlivePlayer(int team) |
| 187 | +{ |
| 188 | + for(int i = 1; i <= MaxClients; i++) |
| 189 | + { |
| 190 | + if(IsValidClient(i) && IsPlayerAlive(i) && GetClientTeam(i) == team) |
| 191 | + { |
| 192 | + return i; |
147 | 193 | }
|
148 | 194 | }
|
149 |
| - |
150 |
| - return AcceptEntityInput(weapon, "Kill"); |
| 195 | + |
| 196 | + return 0; |
151 | 197 | }
|
152 | 198 |
|
153 | 199 | stock bool IsValidClient(int client)
|
154 | 200 | {
|
155 |
| - return client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client); |
| 201 | + return IsClientInGame(client) && client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientAuthorized(client) && !IsFakeClient(client); |
156 | 202 | }
|
0 commit comments