1 |
/*******************************************************************************
|
2 |
*
|
3 |
* This program is free software; you can redistribute it and/or
|
4 |
* modify it under the terms of the GNU General Public License
|
5 |
* as published by the Free Software Foundation; either version 2
|
6 |
* of the License, or version 3.
|
7 |
*
|
8 |
* This program is distributed in the hope that it will be useful,
|
9 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11 |
* GNU General Public License for more details.
|
12 |
*
|
13 |
* You should have received a copy of the GNU General Public License
|
14 |
* along with this program; if not, write to the Free Software
|
15 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
16 |
*
|
17 |
* In addition, as a special exception, the author gives permission to
|
18 |
* link the code of this program with the Half-Life Game Engine ("HL
|
19 |
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
|
20 |
* L.L.C ("Valve"). You must obey the GNU General Public License in all
|
21 |
* respects for all of the code used other than the HL Engine and MODs
|
22 |
* from Valve. If you modify this file, you must extend this exception
|
23 |
* to your version of the file.
|
24 |
*
|
25 |
********************************************************************************
|
26 |
|
27 |
|
28 |
Molotov Cocktail
|
29 |
Version 3.30
|
30 |
Maintained by: DynamicBits (Andy)
|
31 |
|
32 |
* Commands:
|
33 |
- say molotov - Buy a Molotov
|
34 |
- say /molotov - Buy a Molotov
|
35 |
- molotov_give <player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give Molotov(s) to a player, a team, or everyone
|
36 |
- molotov_cocktail [0|1] - Enable/disable the plugin (If no arguments, show the status)
|
37 |
- molotov_override [0|1] - Enable/disable the standard grenade override (If no arguments, show the status)
|
38 |
|
39 |
* Cvars
|
40 |
- molotov_enabled <0|1> - (Default: 1) Enable(1)/disable(0) the plugin
|
41 |
- molotov_price <N> - (Default: 300) Set the Molotov price (Counter-Strike only)
|
42 |
- molotov_damage <N> - (Default: 50.0) Set the damage done by initial Molotov explosion
|
43 |
- molotov_radius <N> - (Default: 150.0) Set the radius of Molotov damage
|
44 |
- molotov_firetime <N> - (Default: 6) Duration (in seconds) of fire effects, sounds, etc.
|
45 |
- molotov_firedamage <N> - (Default: 3) Amount of damage done by fire effects (every 0.2 secs)
|
46 |
- molotov_ff <0|1|-1|-2> - (Default: 1) Set Molotov friendly fire status (Was molotov_tk)
|
47 |
* 0 - Disable friendly fire for Molotovs (regardless of mp_friendlyfire)
|
48 |
* 1 - Enable friendly fire for Molotovs (regardless of mp_friendlyfire)
|
49 |
* -1 - Use mp_friendlyfire value
|
50 |
* -2 - Check bit 5 (decimal: 16) of mp_teamplay (DOD and TFC only)
|
51 |
- molotov_override_he <0|1> - (Default: 0) Override the mod's standard grenade automatically with Molotov (Was molotov_tempoverride)
|
52 |
- molotov_max <N> - (Default: 1) Limit carried Molotovs to this amount
|
53 |
* (Recommended: CSTRIKE: ≤ 10; DOD: ≤ 9; TFC: ≤ 4;)
|
54 |
- molotov_buyzone <0|1> - (Default: 1) Limit Molotov buying to buyzone (Counter-Strike only)
|
55 |
- molotov_menu <0|1> - (Default: 0) Enable menu at beginning of each round (Was amx_molotovmenu)
|
56 |
|
57 |
* Required Modules:
|
58 |
- Fakemeta
|
59 |
- Cstrike (Counter-Strike only)
|
60 |
- Csx (Counter-Strike only)
|
61 |
- Dodfun (Day of Defeat only)
|
62 |
- Dodx (Day of Defeat only)
|
63 |
- Tfcx (Team Fortress Classic only)
|
64 |
- Engine (TFC with debugging only)
|
65 |
|
66 |
* Changelog/Credit:
|
67 |
- DynamicBits
|
68 |
* Version 3.30 (2014-04-13)
|
69 |
- (Beta) Day of Defeat support was added
|
70 |
- (Beta) Team Fortress Classic support was added
|
71 |
- (Untested) Stats logging support was added
|
72 |
- New values for molotov_ff were added
|
73 |
- Friction/velocity after explosion was adjusted for realism
|
74 |
- Bottle breaking sound was added
|
75 |
- Molotov suicides no longer reward extra score points
|
76 |
- Frag count calculations were fixed
|
77 |
- Money calculations were fixed
|
78 |
- Override no longer sets a negative number of Molotovs
|
79 |
- Default price was changed to match standard grenades
|
80 |
- Console text now goes to correct console
|
81 |
- Text (non-VGUI) buy menu support was removed
|
82 |
- Converted fun functions to fakemeta_util functions (removed fun include)
|
83 |
- Various optimizations
|
84 |
- A few typos were fixed
|
85 |
* Version 3.20 (2008-11-20)
|
86 |
- My first public release
|
87 |
- Finally tracked down and fixed the intermittent crashing problem (I hope!)
|
88 |
- Modified default damage values
|
89 |
- molotov_cocktail/molotov_override commands now change settings *or* display status
|
90 |
- Broken Molotov model stays closer to the explosion (looks more realistic)
|
91 |
- Task IDs are now guaranteed to be unique
|
92 |
- Modified anti-lag calculations to be more accurate (less likely to lag)
|
93 |
- Changed amx_molotovmenu CVAR to molotov_menu
|
94 |
- Changed molotov_tk CVAR to molotov_ff
|
95 |
- Changed molotov_tempoverride CVAR to molotov_override_he
|
96 |
- Preparation for support of mods other than Counter-Strike
|
97 |
- Fixed lots of coding mistakes
|
98 |
- Optimized several sections of code
|
99 |
- Corrected grammar/typos
|
100 |
- Clean up code (Removed unused code/unhelpful comments, fixed formatting, and semicolons!)
|
101 |
- Raffe (CantShoot)
|
102 |
* (Unversioned release)
|
103 |
- Originally fixed plugin to run on Linux servers
|
104 |
- Added optional menu to purchase Molotov cocktails each round
|
105 |
- Moved models and sounds into proper molotov/ subdirectories
|
106 |
- Fixed Molotovs not being reset upon player disconnect
|
107 |
- (Almost) fixed Molotovs not being removed for new round
|
108 |
- Added @all/@ct/@t arguments to molotov_give command
|
109 |
- Changed some models/sound
|
110 |
- [ --<-@ ] Black Rose
|
111 |
* Version 3.0-3.1c ?
|
112 |
- Unknown changes
|
113 |
- SAMURAI
|
114 |
* Original plugin author
|
115 |
|
116 |
|
117 |
*/
|
118 |
|
119 |
#pragma semicolon 1
|
120 |
|
121 |
// Uncomment only the define that applies to your mod
|
122 |
#define CSTRIKE
|
123 |
//#define DOD
|
124 |
//#define TFC
|
125 |
|
126 |
// Uncomment the following line to enable debug logging.
|
127 |
//#define MOLOTOV_DEBUG
|
128 |
|
129 |
|
130 |
#include <amxmodx>
|
131 |
#include <amxmisc>
|
132 |
//#include <fakemeta> // (runtime only)
|
133 |
#include <fakemeta_util>
|
134 |
#if defined CSTRIKE
|
135 |
#include <cstrike>
|
136 |
#include <csx> // Used only for custom_weapon_* functions
|
137 |
#endif
|
138 |
#if defined DOD
|
139 |
#include <dodfun>
|
140 |
#include <dodx> // Used only for custom_weapon_* functions
|
141 |
#endif
|
142 |
#if defined TFC
|
143 |
#include <tfcx>
|
144 |
#include <engine> // Used only with debugging enabled
|
145 |
#endif
|
146 |
|
147 |
// If you really want to same some memory and know you won't have 32 players, you can change this.
|
148 |
#define MAX_PLAYERS 32
|
149 |
#define ADMIN_ACCESS ADMIN_KICK
|
150 |
|
151 |
#define ANTI_LAGG 7 // Defines max calculations before a flame is spawned without check if on ground
|
152 |
// This is to prevent lag at really narrow ents where you could end up with 400 calculations per flame. Suggested: <= 10
|
153 |
|
154 |
#define MOLOTOV_HARD_LIMIT 10 // Maximum Molotov cocktails this code is capable of handling without bugs (per player)
|
155 |
|
156 |
#define ID_TO_INDEX(%0) %0 - 1 // Use this macro rather than dim the arrays with 33
|
157 |
|
158 |
#define MOLOTOV_MENU_KEYS MENU_KEY_0|MENU_KEY_1|MENU_KEY_2 // Choices to look for with optional menu
|
159 |
|
160 |
// Task IDs
|
161 |
#define MOLOTOV_TASKID_RESET 1000 // Set g_bReset to false after a short delay (task is TFC only)
|
162 |
#define MOLOTOV_TASKID_OFFSET MOLOTOV_HARD_LIMIT
|
163 |
// These task IDs are dynamically set per-Molotov
|
164 |
#define MOLOTOV_TASKID_BASE1 2000 // By default, with 32 players, task ids
|
165 |
#define MOLOTOV_TASKID_BASE2 MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // from 2000 to 2959 can
|
166 |
#define MOLOTOV_TASKID_BASE3 MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // potentially be used used.
|
167 |
|
168 |
#define TEAM_UNASSIGNED 0
|
169 |
#define TEAM_ONE 1
|
170 |
#define TEAM_TWO 2
|
171 |
#define TEAM_THREE 3
|
172 |
#define TEAM_FOUR 4
|
173 |
#define MC_TFC_PC_CIVILIAN 11 // Temporary workaround for AMXX bug 6042
|
174 |
|
175 |
|
176 |
new const g_PLUGIN[] = "Molotov Cocktail";
|
177 |
new const g_AUTHORS[] = "DynamicBits";
|
178 |
new const g_VERSION[] = "3.30";
|
179 |
|
180 |
new pEnabled; // Pointer to molotov_enabled
|
181 |
new pMlDamage; // Pointer to molotov_damage
|
182 |
new pMlRadius; // Pointer to molotov_radius
|
183 |
new pFireTime; // Pointer to molotov_firetime
|
184 |
new pOverride; // Pointer to molotov_override_he
|
185 |
new pMFF; // Pointer to molotov_ff
|
186 |
new pFriendlyFire; // Pointer to mp_friendlyfire
|
187 |
new pFireDmg; // Pointer to molotov_firedamage
|
188 |
new pMaxMolotovs; // Pointer to molotov_max
|
189 |
#if defined DOD || defined TFC
|
190 |
new pTeamPlay; // Pointer to mp_teamplay
|
191 |
#endif
|
192 |
#if defined CSTRIKE
|
193 |
new pBuyZone; // Pointer to molotov_buyzone
|
194 |
new pMolotovMenu; // Pointer to molotov_menu
|
195 |
new pPrice; // Pointer to molotov_price
|
196 |
|
197 |
new g_msgScoreInfo; // ScoreInfo message ID
|
198 |
#endif
|
199 |
new g_msgDeathMsg; // DeathMsg message ID
|
200 |
|
201 |
new g_NumMolotov[MAX_PLAYERS]; // How many Molotovs each player has
|
202 |
#if defined CSTRIKE
|
203 |
new bool:g_bRestarted; // Reset Molotovs after first round restart
|
204 |
#endif
|
205 |
new g_MaxPlayers; // Max players (calculated at runtime to make loops more efficient)
|
206 |
new g_wpnMolotov; // Custom weapon ID
|
207 |
new bool:g_bReset; // Reset and stop explosions after round ends; Stop reset_tasks() from getting called once per player
|
208 |
|
209 |
new g_iFireSprite, g_iSmokeSprite[2]; // Handles to the precached sprites
|
210 |
new g_iMolotovOffset[MAX_PLAYERS]; // Offset used for a player's task ID
|
211 |
|
212 |
// The Pawn compiler does not optimize the DATA section. Any string that appears multiple times should be optimized with a global constant.
|
213 |
// *Unused* constants do not affect the compiled size, however they create compiler warnings. (#pragma unused is a quick fix for the warnings.)
|
214 |
new const EVENT_ROUND_END[] = "event_round_end";
|
215 |
#if defined CSTRIKE
|
216 |
new const BUY_MOLOTOV[] = "buy_molotov";
|
217 |
new const WEAPON_HEGRENADE[] = "weapon_hegrenade";
|
218 |
#endif
|
219 |
#if defined DOD
|
220 |
new const WEAPON_HANDGRENADE[] = "weapon_handgrenade";
|
221 |
new const WEAPON_STICKGRENADE[] = "weapon_stickgrenade";
|
222 |
#endif
|
223 |
#if defined DOD || defined TFC
|
224 |
new const HUDTEXT[] = "HudText";
|
225 |
#endif
|
226 |
#if defined CSTRIKE || defined TFC
|
227 |
new const TEXTMSG[] = "TextMsg";
|
228 |
#endif
|
229 |
|
230 |
// Check for outdated tfcconst.inc file (and likely outdated AMX Mod X core/modules).
|
231 |
// My patch for AMXX bug 6042 was accepted, but I think I'll wait for a new final release of AMXX to enable this check.
|
232 |
// In the meantime, I created the MC_TFC_PC_CIVILIAN define.
|
233 |
//#if defined TFC && TFC_PC_CIVILIAN != 11 // TFC_PC_CIVILIAN was (incorrectly) 10 in older versions
|
234 |
// #error TFC_PC_CIVILIAN != 11. Update your tfcconst.inc include file. Get the latest AMX Mod X snapshots at www.amxmodx.org/snapshots.php
|
235 |
//#endif
|
236 |
|
237 |
|
238 |
// Initialize the plugin
|
239 |
public plugin_init() {
|
240 |
|
241 |
register_plugin(g_PLUGIN, g_VERSION, g_AUTHORS);
|
242 |
server_print("[MC] ---- Molotov Cocktail %s loaded ----", g_VERSION);
|
243 |
register_cvar("MolotovCocktail", g_VERSION, FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
244 |
|
245 |
#if defined CSTRIKE
|
246 |
register_menucmd(register_menuid("Buy Molotov Cocktail"), MOLOTOV_MENU_KEYS, "giveMolotov");
|
247 |
|
248 |
#if defined MOLOTOV_DEBUG
|
249 |
register_clcmd("molotov_menutest", "show_molotov_menu");
|
250 |
#endif
|
251 |
|
252 |
register_clcmd("say /molotov", BUY_MOLOTOV);
|
253 |
register_clcmd("say molotov", BUY_MOLOTOV);
|
254 |
#endif
|
255 |
register_concmd("molotov_give", "molotov_give", ADMIN_ACCESS, "<player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give free Molotov cocktails");
|
256 |
register_concmd("molotov_override", "molotov_override", ADMIN_ACCESS, "[0|1] - Enable/disable the standard grenade override (If no arguments, show the status)");
|
257 |
register_concmd("molotov_cocktail", "molotov_cocktail", ADMIN_ACCESS, "[0|1] - Enable/disable the plugin (If no arguments, show the status)");
|
258 |
|
259 |
pEnabled = register_cvar("molotov_enabled", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
260 |
pOverride = register_cvar("molotov_override_he", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
261 |
pMlDamage = register_cvar("molotov_damage", "50.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
262 |
pMlRadius = register_cvar("molotov_radius", "150.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
263 |
pFireTime = register_cvar("molotov_firetime", "6", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
264 |
pFireDmg = register_cvar("molotov_firedamage", "3", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
265 |
pMFF = register_cvar("molotov_ff", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
266 |
pMaxMolotovs = register_cvar("molotov_max", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
267 |
pFriendlyFire = register_cvar("mp_friendlyfire", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
268 |
#if defined CSTRIKE
|
269 |
pBuyZone = register_cvar("molotov_buyzone", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
270 |
pMolotovMenu = register_cvar("molotov_menu", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
271 |
pPrice = register_cvar("molotov_price", "300", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
272 |
#endif
|
273 |
#if defined DOD
|
274 |
pTeamPlay = register_cvar("mp_teamplay", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
275 |
#endif
|
276 |
#if defined TFC
|
277 |
pTeamPlay = register_cvar("mp_teamplay", "21", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
|
278 |
#endif
|
279 |
|
280 |
register_event("DeathMsg", "event_deathmsg", "a", "2>0"); // For some reason, arg2 (Victim) is sometimes -1 (at least in TFC on Windows HLDS with FoxBot).
|
281 |
#if defined CSTRIKE || defined DOD
|
282 |
register_event("CurWeapon", "event_curweapon", "be", "1=1");
|
283 |
register_event("HLTV", "event_new_round", "a", "1=0", "2=0"); // cstrike/dod new round; So far, I haven't found a TFC equivalent
|
284 |
#endif
|
285 |
#if defined CSTRIKE
|
286 |
register_event(TEXTMSG, "event_gamerestart", "a", "2=#Game_Commencing", "2=#Game_will_restart_in");
|
287 |
#endif
|
288 |
#if defined DOD
|
289 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&VICTORY"); // Sent once per player on round end
|
290 |
#endif
|
291 |
#if defined TFC
|
292 |
// Since TFC doesn't have any generic end of round event/message, specific messages need to be caught for certain maps.
|
293 |
// Maps that don't have traditional rounds (2fort, badlands, casbah, crossover2, cz2, ravelin, skate2, well, etc.) don't apply here.
|
294 |
// All of the default maps are accounted for. If there is demand for specific custom maps, I will add the appropriate message(s).
|
295 |
new sCurrentMap[32];
|
296 |
get_mapname(sCurrentMap, charsmax(sCurrentMap));
|
297 |
if (!strcmp(sCurrentMap, "avanti")) {
|
298 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#italy_endround_win"); // Was Avanti originally called Italy?
|
299 |
} else if ((!strcmp(sCurrentMap, "dustbowl")) || (!strcmp(sCurrentMap, "castleargh")) || (!strcmp(sCurrentMap, "castleargh2"))) {
|
300 |
register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_one"); // Technically these are "stages," not "rounds"
|
301 |
register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_two");
|
302 |
//register_event(TEXTMSG, "event_round_end", "b", "2=#dustbowl_blue_caps"); // The map ends after this cap
|
303 |
} else if (!strcmp(sCurrentMap, "epicenter")) {
|
304 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#dblmint_you_capped_flag"); // dblmint?!
|
305 |
} else if (!strcmp(sCurrentMap, "flagrun")) {
|
306 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&you won this round!");
|
307 |
} else if (!strcmp(sCurrentMap, "hunted")) {
|
308 |
register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#hunted_target_killed");
|
309 |
} else if (!strcmp(sCurrentMap, "push")) {
|
310 |
register_event(TEXTMSG, EVENT_ROUND_END, "b", "2&_netname_scores");
|
311 |
} else if (!strcmp(sCurrentMap, "rock2")) {
|
312 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=1 . . .^n");
|
313 |
} else if (!strcmp(sCurrentMap, "warpath")) {
|
314 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_red_wins");
|
315 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_blue_wins");
|
316 |
// ---------- Custom Maps ----------
|
317 |
} else if (!strcmp(sCurrentMap, "castleargh3")) {
|
318 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&You have secured"); // This works for all four stages
|
319 |
} else if (!strcmp(sCurrentMap, "hwguyz2")) {
|
320 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#2fort_you_capped_flag");
|
321 |
register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&Time Ran Out");
|
322 |
}
|
323 |
|
324 |
//#if defined MOLOTOV_DEBUG
|
325 |
//register_event(TEXTMSG, "event_textmsg_a", "a");
|
326 |
//register_event(TEXTMSG, "event_textmsg_b", "b");
|
327 |
//register_event(HUDTEXT, "event_hudtext_a", "a");
|
328 |
//register_event(HUDTEXT, "event_hudtext_b", "b");
|
329 |
//#endif
|
330 |
#endif
|
331 |
|
332 |
#if defined CSTRIKE
|
333 |
register_logevent(EVENT_ROUND_END, 2, "1=Round_End");
|
334 |
#endif
|
335 |
|
336 |
register_forward(FM_EmitSound, "fw_emitsound");
|
337 |
#if defined TFC
|
338 |
register_forward(FM_SetModel, "fw_setmodel_post", 1);
|
339 |
#endif
|
340 |
|
341 |
g_MaxPlayers = get_maxplayers();
|
342 |
|
343 |
#if defined CSTRIKE
|
344 |
g_msgScoreInfo = get_user_msgid("ScoreInfo");
|
345 |
#endif
|
346 |
g_msgDeathMsg = get_user_msgid("DeathMsg");
|
347 |
|
348 |
g_wpnMolotov = custom_weapon_add("molotov", 0, "molotov"); // I can hardly find any documentation or sample code for this. I have no
|
349 |
// idea if I'm using it correctly or not. I'm not even sure what it affects.
|
350 |
}
|
351 |
|
352 |
// These are primarily for catching messages in TFC to add custom round end triggers.
|
353 |
/*
|
354 |
#if defined MOLOTOV_DEBUG
|
355 |
public event_textmsg_a() {
|
356 |
new sArg2[64];
|
357 |
read_data(2, sArg2, charsmax(sArg2));
|
358 |
|
359 |
client_print(0, print_chat, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
|
360 |
console_print(0, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
|
361 |
}
|
362 |
|
363 |
public event_textmsg_b() {
|
364 |
new sArg2[64];
|
365 |
read_data(2, sArg2, charsmax(sArg2));
|
366 |
|
367 |
client_print(0, print_chat, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
|
368 |
console_print(0, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
|
369 |
}
|
370 |
|
371 |
public event_hudtext_a() {
|
372 |
new sArg1[64];
|
373 |
read_data(1, sArg1, charsmax(sArg1));
|
374 |
|
375 |
client_print(0, print_chat, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
|
376 |
console_print(0, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
|
377 |
}
|
378 |
|
379 |
public event_hudtext_b() {
|
380 |
new sArg1[64];
|
381 |
read_data(1, sArg1, charsmax(sArg1));
|
382 |
|
383 |
client_print(0, print_chat, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
|
384 |
console_print(0, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
|
385 |
}
|
386 |
#endif
|
387 |
*/
|
388 |
|
389 |
// Precache models and sound(s)
|
390 |
public plugin_precache() {
|
391 |
|
392 |
g_iFireSprite = precache_model("sprites/flame.spr");
|
393 |
|
394 |
g_iSmokeSprite[0] = precache_model("sprites/black_smoke3.spr");
|
395 |
#if defined DOD
|
396 |
g_iSmokeSprite[1] = g_iSmokeSprite[0]; // steam1.spr shows a black background in dod
|
397 |
#else
|
398 |
g_iSmokeSprite[1] = precache_model("sprites/steam1.spr");
|
399 |
#endif
|
400 |
|
401 |
#if defined CSTRIKE || defined DOD
|
402 |
precache_model("models/molotov/p_molotov.mdl");
|
403 |
precache_model("models/molotov/v_molotov.mdl");
|
404 |
#endif
|
405 |
precache_model("models/molotov/w_molotov.mdl");
|
406 |
precache_model("models/molotov/w_broke_molotov.mdl");
|
407 |
|
408 |
precache_sound("molotov/molotov_fire.wav");
|
409 |
|
410 |
}
|
411 |
|
412 |
// Reset Molotovs so that a new player doesn't have any
|
413 |
public client_disconnect(id) {
|
414 |
g_NumMolotov[ID_TO_INDEX(id)] = 0;
|
415 |
}
|
416 |
|
417 |
// Catch the first impact of the Molotov and start the sound/explosion
|
418 |
// A Molotov cocktail should "explode" on impact, not after a set time.
|
419 |
public fw_emitsound(ent, channel, sample[]) {
|
420 |
#if defined CSTRIKE
|
421 |
if (equal(sample[8], "he_bounce", 9)) {
|
422 |
#else
|
423 |
#if defined DOD || defined TFC
|
424 |
// DOD: debris/bustglass2.wav and debris/bustglass1.wav are played for breaking glass, but ent is not the grenade, so Molotovs "disappear" (This is a bug in this plugin)
|
425 |
// A fix would be to use FM_Touch or Ham_Touch or register_touch instead of FM_EmitSound
|
426 |
if (equal(sample[8], "grenade_hit", 11)) {
|
427 |
#endif
|
428 |
#endif
|
429 |
|
430 |
new sModel[32];
|
431 |
pev(ent, pev_model, sModel, charsmax(sModel));
|
432 |
|
433 |
// Depending on where the Molotov lands, the EmitSound forward may get called 50+ times.
|
434 |
// After the first hit, the model is changed to w_broke_molotov, so this code is skipped on successive calls
|
435 |
if (equal(sModel[15], "w_molotov.mdl")) {
|
436 |
#if defined TFC
|
437 |
set_pev(ent, pev_nextthink, 99999.0); // For TFC, this is about the only way I can stop the explosion.
|
438 |
#endif
|
439 |
// The glass breaking sound has a low range (ATTN_STATIC) so as not to be overpowering
|
440 |
emit_sound(ent, CHAN_AUTO, "debris/glass2.wav", VOL_NORM, ATTN_STATIC, 0, PITCH_LOW);
|
441 |
|
442 |
new Float:fFriction, Float:fVelocity[3];
|
443 |
pev(ent, pev_friction, fFriction);
|
444 |
fFriction *= 1.15; // Increase friction to make it look more realistic
|
445 |
set_pev(ent, pev_friction, fFriction);
|
446 |
|
447 |
pev(ent, pev_velocity, fVelocity);
|
448 |
fVelocity[0] *= 0.3; // Decrease velocity because friction doesn't do it all
|
449 |
fVelocity[1] *= 0.3;
|
450 |
fVelocity[2] *= 0.3;
|
451 |
set_pev(ent, pev_velocity, fVelocity);
|
452 |
|
453 |
molotov_explode(ent); // Replacement for normal grenade explosion
|
454 |
|
455 |
return FMRES_SUPERCEDE;
|
456 |
} else if (equal(sModel[15], "w_broke_molotov.")) { // "mdl" is truncated because of the array size, which is OK
|
457 |
return FMRES_SUPERCEDE; // Don't play any sounds for bounces.
|
458 |
}
|
459 |
}
|
460 |
|
461 |
return FMRES_IGNORED;
|
462 |
}
|
463 |
|
464 |
// Since TFC handles grenades differently, this is roughly equivalant to event_curweapon() used by cstrike and dod.
|
465 |
#if defined TFC
|
466 |
public fw_setmodel_post(ent, const model[]) {
|
467 |
if (!pev_valid(ent)) { // Check if it's a valid entity to prevent errors
|
468 |
return FMRES_IGNORED;
|
469 |
}
|
470 |
|
471 |
new sClassname[32];
|
472 |
pev(ent, pev_classname, sClassname, charsmax(sClassname));
|
473 |
|
474 |
if (!get_pcvar_num(pEnabled) || !equal(sClassname, "normalgrenade")) {
|
475 |
return FMRES_IGNORED;
|
476 |
}
|
477 |
|
478 |
new iOwner = pev(ent, pev_owner);
|
479 |
if (!g_NumMolotov[ID_TO_INDEX(iOwner)] && !get_pcvar_num(pOverride)) { // If no Molotovs and override is disabled, return
|
480 |
return FMRES_IGNORED;
|
481 |
}
|
482 |
|
483 |
if (g_NumMolotov[ID_TO_INDEX(iOwner)] > 0) { // Prevent negative values
|
484 |
g_NumMolotov[ID_TO_INDEX(iOwner)]--;
|
485 |
}
|
486 |
|
487 |
set_pev(ent, pev_team, get_user_team(iOwner));
|
488 |
custom_weapon_shot(g_wpnMolotov, iOwner);
|
489 |
|
490 |
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
|
491 |
|
492 |
return FMRES_HANDLED;
|
493 |
}
|
494 |
#endif
|
495 |
|
496 |
// When the player changes weapons to the Molotov, update the model
|
497 |
#if defined CSTRIKE || defined DOD
|
498 |
public event_curweapon(id) {
|
499 |
|
500 |
if (!get_pcvar_num(pEnabled) || !is_user_alive(id)) {
|
501 |
return PLUGIN_CONTINUE;
|
502 |
}
|
503 |
|
504 |
if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) { // If no Molotovs and override is disabled, return
|
505 |
return PLUGIN_CONTINUE;
|
506 |
}
|
507 |
|
508 |
new iWeaponID = get_user_weapon(id, _, _);
|
509 |
#if defined CSTRIKE
|
510 |
if (iWeaponID != CSW_HEGRENADE) {
|
511 |
#else // elseif *should* work, but there is a bug in the compiler
|
512 |
#if defined DOD
|
513 |
// current weapon is never set to DODW_MILLS_BOMB in this event; only DODW_HANDGRENADE/DODW_STICKGRENADE
|
514 |
if ((iWeaponID != DODW_HANDGRENADE) && (iWeaponID != DODW_STICKGRENADE)) {
|
515 |
#endif
|
516 |
#endif
|
517 |
return PLUGIN_CONTINUE;
|
518 |
}
|
519 |
|
520 |
set_pev(id, pev_viewmodel2, "models/molotov/v_molotov.mdl"); // View model (First person) *model2 doesn't require allocating the string
|
521 |
set_pev(id, pev_weaponmodel2, "models/molotov/p_molotov.mdl"); // Player model (Third person)
|
522 |
|
523 |
#if defined DOD
|
524 |
// I think 3 is correct, but it looks strange..
|
525 |
set_pev(id, pev_weaponanim, 3); // 0: "idle"; 1: "pullpin"; 2: "throw"; 3: "deploy"
|
526 |
#endif
|
527 |
|
528 |
return PLUGIN_CONTINUE;
|
529 |
}
|
530 |
#endif
|
531 |
|
532 |
// Reset Molotovs on death
|
533 |
public event_deathmsg() {
|
534 |
#if defined MOLOTOV_DEBUG
|
535 |
log_amx("[MC] ========== DeathMsg ========== K(%d) V(%d)", read_data(1), read_data(2));
|
536 |
#endif
|
537 |
g_NumMolotov[ID_TO_INDEX(read_data(2))] = 0;
|
538 |
}
|
539 |
|
540 |
// cstrike only
|
541 |
#if defined CSTRIKE
|
542 |
public event_gamerestart() {
|
543 |
#if defined MOLOTOV_DEBUG
|
544 |
log_amx("[MC] ========== Game Restart ==========");
|
545 |
#endif
|
546 |
g_bRestarted = true;
|
547 |
}
|
548 |
#endif
|
549 |
|
550 |
// cstrike, dod, and tfc will all call this once per player on round end
|
551 |
public event_round_end() {
|
552 |
#if defined MOLOTOV_DEBUG
|
553 |
log_amx("[MC] ========== Round End ==========");
|
554 |
#endif
|
555 |
|
556 |
if (g_bReset == false) {
|
557 |
reset_tasks();
|
558 |
g_bReset = true;
|
559 |
#if defined TFC
|
560 |
set_task(2.0, "cancel_reset", MOLOTOV_TASKID_RESET); // TFC won't call event_new_round, so do that stuff here instead
|
561 |
#endif
|
562 |
}
|
563 |
}
|
564 |
|
565 |
// cstrike and dod will call this once per round, but TFC won't.
|
566 |
#if defined CSTRIKE || defined DOD
|
567 |
public event_new_round(id) {
|
568 |
#if defined MOLOTOV_DEBUG
|
569 |
log_amx("[MC] ========== New Round ==========");
|
570 |
#endif
|
571 |
g_bReset = false; // Stop blocking
|
572 |
|
573 |
if (!get_pcvar_num(pEnabled)) {
|
574 |
return PLUGIN_CONTINUE;
|
575 |
}
|
576 |
|
577 |
reset_tasks(); // This probably isn't needed anymore, but it shouldn't hurt anything
|
578 |
|
579 |
#if defined CSTRIKE
|
580 |
if (get_pcvar_num(pMolotovMenu)) {
|
581 |
if (get_pcvar_num(pOverride)) {
|
582 |
client_print(id, print_center, "Molotov cocktails will replace purchased HE grenades");
|
583 |
} else {
|
584 |
show_molotov_menu(id);
|
585 |
}
|
586 |
}
|
587 |
|
588 |
// For cstrike only, make sure the player didn't quickly purchase a Molotov before the first actual round
|
589 |
if (g_bRestarted) {
|
590 |
arrayset(g_NumMolotov, 0, sizeof(g_NumMolotov)); // Reset everyone to zero Molotovs
|
591 |
g_bRestarted = false;
|
592 |
}
|
593 |
#endif
|
594 |
|
595 |
if (get_pcvar_num(pOverride)) {
|
596 |
set_molotovs();
|
597 |
} else {
|
598 |
reset_molotovs();
|
599 |
}
|
600 |
|
601 |
return PLUGIN_CONTINUE;
|
602 |
}
|
603 |
#endif
|
604 |
|
605 |
// Enable/Disable/Get status of override
|
606 |
public molotov_override(id, level, cid) {
|
607 |
|
608 |
if (!cmd_access(id, level, cid, 1)) { // First argument (passed to molotov_override) is optional
|
609 |
return PLUGIN_HANDLED;
|
610 |
}
|
611 |
|
612 |
if (!get_pcvar_num(pEnabled)) {
|
613 |
return PLUGIN_HANDLED;
|
614 |
}
|
615 |
|
616 |
if (read_argc() == 1) { // No arguments; Display status
|
617 |
console_print(id, "Override is currently %s.", get_pcvar_num(pOverride) ? "enabled" : "disabled");
|
618 |
return PLUGIN_HANDLED;
|
619 |
}
|
620 |
|
621 |
new sArg[2];
|
622 |
read_argv(1, sArg, charsmax(sArg));
|
623 |
|
624 |
new iArg = str_to_num(sArg);
|
625 |
|
626 |
if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) { // If less than 0 or greater than 1 or not a digit
|
627 |
console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
|
628 |
return PLUGIN_HANDLED;
|
629 |
}
|
630 |
|
631 |
if (iArg == get_pcvar_num(pOverride)) {
|
632 |
console_print(id, "Override is already %s.", iArg ? "enabled" : "disabled");
|
633 |
return PLUGIN_HANDLED;
|
634 |
}
|
635 |
|
636 |
set_pcvar_num(pOverride, iArg);
|
637 |
console_print(id, "Override was %s.", iArg ? "enabled" : "disabled");
|
638 |
|
639 |
#if defined CSTRIKE || defined DOD
|
640 |
if (iArg) { // If plugin is enabled (checked above) and override is enabled, set models to Molotov
|
641 |
set_molotovs();
|
642 |
} else {
|
643 |
reset_molotovs();
|
644 |
}
|
645 |
#endif
|
646 |
|
647 |
return PLUGIN_HANDLED;
|
648 |
}
|
649 |
|
650 |
// Enable/Disable/Get status of plugin
|
651 |
public molotov_cocktail(id, level, cid) {
|
652 |
if (!cmd_access(id, level, cid, 1)) { // First argument (passed to molotov_cocktail) is optional
|
653 |
return PLUGIN_HANDLED;
|
654 |
}
|
655 |
|
656 |
if (read_argc() == 1) { // No arguments; Display status
|
657 |
console_print(id, "Plugin is currently %s. (Override:%d; MFF:%d)", get_pcvar_num(pEnabled) ? "enabled" : "disabled", get_pcvar_num(pOverride), get_pcvar_num(pMFF));
|
658 |
return PLUGIN_HANDLED;
|
659 |
}
|
660 |
|
661 |
new sArg[2];
|
662 |
read_argv(1, sArg, charsmax(sArg));
|
663 |
|
664 |
new iArg = str_to_num(sArg);
|
665 |
|
666 |
if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) { // If less than 0 or greater than 1 or not a digit
|
667 |
console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
|
668 |
return PLUGIN_HANDLED;
|
669 |
}
|
670 |
|
671 |
if (iArg == get_pcvar_num(pEnabled)) {
|
672 |
console_print(id, "Plugin is already %s.", iArg ? "enabled" : "disabled");
|
673 |
return PLUGIN_HANDLED;
|
674 |
}
|
675 |
|
676 |
set_pcvar_num(pEnabled, iArg);
|
677 |
console_print(id, "Plugin was %s.", iArg ? "enabled" : "disabled");
|
678 |
|
679 |
#if defined CSTRIKE || defined DOD
|
680 |
if (iArg && get_pcvar_num(pOverride)) { // If the plugin was enabled and override is enabled, set models to Molotov
|
681 |
set_molotovs();
|
682 |
} else {
|
683 |
reset_molotovs();
|
684 |
}
|
685 |
#endif
|
686 |
|
687 |
return PLUGIN_HANDLED;
|
688 |
}
|
689 |
|
690 |
// Handle molotov_give console command
|
691 |
public molotov_give(id, level, cid) {
|
692 |
if (!cmd_access(id, level, cid, 2)) {
|
693 |
return PLUGIN_HANDLED;
|
694 |
}
|
695 |
|
696 |
new sArg1[16], iTarget;
|
697 |
read_argv(1, sArg1, charsmax(sArg1));
|
698 |
#if defined MOLOTOV_DEBUG
|
699 |
log_amx("[MC] molotov_give sArg1[0](%s)", sArg1[0]);
|
700 |
#endif
|
701 |
new sAdmin[32];
|
702 |
get_user_name(id, sAdmin, charsmax(sAdmin));
|
703 |
new iGiveAmount = (get_pcvar_num(pMaxMolotovs) < MOLOTOV_HARD_LIMIT ? get_pcvar_num(pMaxMolotovs) : MOLOTOV_HARD_LIMIT);
|
704 |
|
705 |
if (sArg1[0] == '@') {
|
706 |
|
707 |
new iTargetTeam, sTeamName[32];
|
708 |
new Players[MAX_PLAYERS], iNum;
|
709 |
|
710 |
if (equali(sArg1[1], "all")) {
|
711 |
iTargetTeam = 0;
|
712 |
} else if (equali(sArg1[1], "t") || equali(sArg1[1], "al") || equali(sArg1[1], "br") || equali(sArg1[1], "b")) { // CS_TEAM_T or ALLIES/British or Blue
|
713 |
iTargetTeam = TEAM_ONE;
|
714 |
} else if (equali(sArg1[1], "ct") || equali(sArg1[1], "ax") || equali(sArg1[1], "r")) { // CS_TEAM_CT or AXIS or Red
|
715 |
iTargetTeam = TEAM_TWO;
|
716 |
#if defined TFC
|
717 |
} else if (equali(sArg1[1], "y")) { // Yellow
|
718 |
iTargetTeam = TEAM_THREE;
|
719 |
} else if (equali(sArg1[1], "g")) { // Green
|
720 |
iTargetTeam = TEAM_FOUR;
|
721 |
#endif
|
722 |
}
|
723 |
|
724 |
get_players(Players, iNum, "ach"); // alive, no bots, no HLTV
|
725 |
|
726 |
for (new i = 0; i < iNum; ++i) {
|
727 |
iTarget = Players[i];
|
728 |
|
729 |
if ((iTargetTeam == 0) || (get_user_team(iTarget) == iTargetTeam)) {
|
730 |
g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;
|
731 |
|
732 |
#if defined CSTRIKE
|
733 |
fm_give_item(iTarget, WEAPON_HEGRENADE);
|
734 |
cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
|
735 |
#endif
|
736 |
#if defined DOD
|
737 |
// TODO - This sets the count, but it is not immediately updated on the HUD
|
738 |
switch(get_user_team(iTarget)) {
|
739 |
case ALLIES: { // (or British)
|
740 |
fm_give_item(iTarget, WEAPON_HANDGRENADE);
|
741 |
dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
|
742 |
}
|
743 |
case AXIS: {
|
744 |
fm_give_item(iTarget, WEAPON_STICKGRENADE);
|
745 |
dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
|
746 |
}
|
747 |
}
|
748 |
#endif
|
749 |
#if defined TFC
|
750 |
new iClass = pev(iTarget, pev_playerclass);
|
751 |
if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) { // No unselected/spectator, scout, or civilian
|
752 |
tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount); // Requires 1.8.3-dev-hg185 or newer
|
753 |
}
|
754 |
#endif
|
755 |
#if defined CSTRIKE
|
756 |
emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
|
757 |
#endif
|
758 |
#if defined DOD
|
759 |
emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); // "items/weaponpickup.wav" could work too, I suppose
|
760 |
#endif
|
761 |
#if defined TFC
|
762 |
// Shotgun pumping sound for picking up grenades... That's how TFC does it!
|
763 |
emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
|
764 |
#endif
|
765 |
}
|
766 |
}
|
767 |
|
768 |
switch(iTargetTeam) {
|
769 |
case 0: {
|
770 |
sTeamName = "everyone";
|
771 |
}
|
772 |
case TEAM_ONE: {
|
773 |
#if defined CSTRIKE
|
774 |
sTeamName = "all terrorists";
|
775 |
#endif
|
776 |
#if defined DOD
|
777 |
sTeamName = "all allies"; // TODO - Allies or British
|
778 |
#endif
|
779 |
#if defined TFC
|
780 |
sTeamName = "all blue"; // I *could* pull the team1_name value from the info_tfdetect entity (but only for some maps?)
|
781 |
#endif
|
782 |
}
|
783 |
case TEAM_TWO: {
|
784 |
#if defined CSTRIKE
|
785 |
sTeamName = "all ct's";
|
786 |
#endif
|
787 |
#if defined DOD
|
788 |
sTeamName = "all axis";
|
789 |
#endif
|
790 |
#if defined TFC
|
791 |
sTeamName = "all red";
|
792 |
}
|
793 |
case TEAM_THREE: {
|
794 |
sTeamName = "all yellow";
|
795 |
}
|
796 |
case TEAM_FOUR: {
|
797 |
sTeamName = "all green";
|
798 |
#endif
|
799 |
}
|
800 |
}
|
801 |
client_print(0, print_chat, "ADMIN %s has given %s %d Molotov cocktails!", sAdmin, sTeamName, iGiveAmount);
|
802 |
|
803 |
} else {
|
804 |
|
805 |
iTarget = cmd_target(id, sArg1, 6);
|
806 |
|
807 |
if (!is_user_connected(iTarget) || !is_user_alive(iTarget)) {
|
808 |
return PLUGIN_HANDLED;
|
809 |
}
|
810 |
|
811 |
g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;
|
812 |
|
813 |
#if defined CSTRIKE
|
814 |
fm_give_item(iTarget, WEAPON_HEGRENADE);
|
815 |
cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
|
816 |
#endif
|
817 |
#if defined DOD
|
818 |
switch(get_user_team(iTarget)) {
|
819 |
case ALLIES: { // (or British)
|
820 |
fm_give_item(iTarget, WEAPON_HANDGRENADE);
|
821 |
dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
|
822 |
}
|
823 |
case AXIS: {
|
824 |
fm_give_item(iTarget, WEAPON_STICKGRENADE);
|
825 |
dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
|
826 |
}
|
827 |
}
|
828 |
#endif
|
829 |
#if defined TFC
|
830 |
new iClass = pev(iTarget, pev_playerclass);
|
831 |
if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) { // No unselected/spectator, scout, or civilian
|
832 |
tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount); // Requires 1.8.3-dev-hg185 or newer
|
833 |
}
|
834 |
#endif
|
835 |
#if defined CSTRIKE
|
836 |
emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
|
837 |
#endif
|
838 |
#if defined DOD
|
839 |
emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM); // "items/weaponpickup.wav" could work too, I suppose
|
840 |
#endif
|
841 |
#if defined TFC
|
842 |
// Shotgun pumping sound for picking up grenades... That's how TFC does it!
|
843 |
emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
|
844 |
#endif
|
845 |
|
846 |
client_print(iTarget, print_chat, "ADMIN %s has given you %d Molotov cocktails!", sAdmin, iGiveAmount);
|
847 |
|
848 |
}
|
849 |
return PLUGIN_HANDLED;
|
850 |
}
|
851 |
|
852 |
// Handle the /molotov command and molotov menu
|
853 |
#if defined CSTRIKE
|
854 |
public buy_molotov(id) {
|
855 |
|
856 |
if (!get_pcvar_num(pEnabled)) {
|
857 |
return PLUGIN_HANDLED;
|
858 |
}
|
859 |
|
860 |
//if (get_pcvar_num(pOverride)) {
|
861 |
// client_print(id, print_center, "Just buy a HE grenade and get Molotov automatically!");
|
862 |
// return PLUGIN_HANDLED;
|
863 |
//}
|
864 |
|
865 |
if (!is_user_alive(id)) {
|
866 |
client_print(id, print_center, "You can't buy Molotov cocktails because you are dead.");
|
867 |
return PLUGIN_HANDLED;
|
868 |
}
|
869 |
|
870 |
if (!cs_get_user_buyzone(id) && get_pcvar_num(pBuyZone)) {
|
871 |
client_print(id, print_center, "You are not in a buyzone.");
|
872 |
return PLUGIN_HANDLED;
|
873 |
}
|
874 |
|
875 |
new iMoney = cs_get_user_money(id);
|
876 |
|
877 |
if (iMoney < get_pcvar_num(pPrice)) {
|
878 |
client_print(id, print_center, "You don't have enough $ to buy a Molotov cocktail.");
|
879 |
return PLUGIN_HANDLED;
|
880 |
}
|
881 |
|
882 |
if (!g_NumMolotov[ID_TO_INDEX(id)] && user_has_weapon(id, CSW_HEGRENADE)) {
|
883 |
if (get_pcvar_num(pOverride)) {
|
884 |
g_NumMolotov[ID_TO_INDEX(id)] = cs_get_user_bpammo(id, CSW_HEGRENADE); // If the user buys one from the VGUI menu with the override enabled, this updates g_NumMolotov
|
885 |
} else {
|
886 |
client_print(id, print_center, "You already have an HE Grenade.");
|
887 |
return PLUGIN_HANDLED;
|
888 |
}
|
889 |
}
|
890 |
|
891 |
if (g_NumMolotov[ID_TO_INDEX(id)] == get_pcvar_num(pMaxMolotovs)) {
|
892 |
if (g_NumMolotov[ID_TO_INDEX(id)] == 1) {
|
893 |
client_print(id, print_center, "You already have a Molotov cocktail.");
|
894 |
} else {
|
895 |
client_print(id, print_center, "You already have %d Molotov cocktails.", g_NumMolotov[ID_TO_INDEX(id)]);
|
896 |
}
|
897 |
return PLUGIN_HANDLED;
|
898 |
}
|
899 |
|
900 |
cs_set_user_money(id, iMoney - get_pcvar_num(pPrice));
|
901 |
fm_give_item(id, WEAPON_HEGRENADE);
|
902 |
cs_set_user_bpammo(id, CSW_HEGRENADE, ++g_NumMolotov[ID_TO_INDEX(id)]);
|
903 |
|
904 |
|
905 |
client_print(id, print_chat, "You got a Molotov cocktail!");
|
906 |
|
907 |
return PLUGIN_HANDLED;
|
908 |
}
|
909 |
#endif
|
910 |
|
911 |
// Just before the grenade is thrown, change the model
|
912 |
#if defined CSTRIKE || defined DOD
|
913 |
public grenade_throw(id, ent, wid) {
|
914 |
#if defined CSTRIKE
|
915 |
if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || wid != CSW_HEGRENADE) {
|
916 |
#else
|
917 |
#if defined DOD
|
918 |
// current weapon can be DODW_MILLS_BOMB in this forward, but not in CurWeapon
|
919 |
if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || ((wid != DODW_HANDGRENADE) && (wid != DODW_STICKGRENADE) && (wid != DODW_MILLS_BOMB))) {
|
920 |
#endif
|
921 |
#endif
|
922 |
return PLUGIN_CONTINUE;
|
923 |
}
|
924 |
|
925 |
if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) { // If no Molotovs and override is disabled, return
|
926 |
return PLUGIN_CONTINUE;
|
927 |
}
|
928 |
|
929 |
if (g_NumMolotov[ID_TO_INDEX(id)] > 0) { // Prevent negative values
|
930 |
g_NumMolotov[ID_TO_INDEX(id)]--;
|
931 |
}
|
932 |
|
933 |
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
|
934 |
set_pev(ent, pev_nextthink, 99999.0);
|
935 |
|
936 |
custom_weapon_shot(g_wpnMolotov, id);
|
937 |
|
938 |
#if defined CSTRIKE // dod sets the team, cstrike doesn't, TFC sets this in fw_setmodel_post()
|
939 |
set_pev(ent, pev_team, get_user_team(id));
|
940 |
#endif
|
941 |
|
942 |
#if defined DOD
|
943 |
//set_pev(id, pev_weaponanim, 0); // 0:"idle"; 1:"pullpin"; 2:"throw"; 3:"deploy"
|
944 |
#endif
|
945 |
|
946 |
return PLUGIN_HANDLED;
|
947 |
}
|
948 |
#endif
|
949 |
|
950 |
// Set up the explosion, sound, damage, etc.
|
951 |
molotov_explode(ent) {
|
952 |
|
953 |
new param[7], iOrigin[3];
|
954 |
new Float:fOrigin[3];
|
955 |
new iOwner = pev(ent, pev_owner);
|
956 |
// The broken bottle may continue to travel, but the fire will be centered around the explosion site, marked by this temporary info_target entity.
|
957 |
new ent2 = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"));
|
958 |
|
959 |
pev(ent, pev_origin, fOrigin);
|
960 |
|
961 |
#if defined MOLOTOV_DEBUG
|
962 |
log_amx("[MC] molotov_explode ent(%d) owner(%d) ent2(%d) -----", ent, iOwner, ent2);
|
963 |
#endif
|
964 |
|
965 |
param[0] = ent;
|
966 |
param[1] = ent2;
|
967 |
param[2] = iOwner;
|
968 |
param[3] = pev(ent, pev_team);
|
969 |
param[4] = iOrigin[0] = floatround(fOrigin[0]);
|
970 |
param[5] = iOrigin[1] = floatround(fOrigin[1]);
|
971 |
param[6] = iOrigin[2] = floatround(fOrigin[2]);
|
972 |
|
973 |
engfunc(EngFunc_SetModel, ent, "models/molotov/w_broke_molotov.mdl");
|
974 |
|
975 |
random_fire(iOrigin, ent2);
|
976 |
radius_damage2(iOwner, param[3], fOrigin, get_pcvar_float(pMlDamage), get_pcvar_float(pMlRadius), DMG_BLAST, true);
|
977 |
|
978 |
// If the round ends because of damage inflicted by the initial blast (in the previous line of code), skip any further Molotov effects.
|
979 |
// g_bReset may already be set, so it is safe to check it at this point.
|
980 |
if (g_bReset == true) {
|
981 |
set_pev(ent, pev_flags, pev(ent, pev_flags) | FL_KILLME); // Remove the Molotov and later cancel the explosion
|
982 |
return PLUGIN_HANDLED;
|
983 |
}
|
984 |
|
985 |
new Float:FireTime = get_pcvar_float(pFireTime);
|
986 |
|
987 |
if (++g_iMolotovOffset[ID_TO_INDEX(iOwner)] == MOLOTOV_HARD_LIMIT) {
|
988 |
g_iMolotovOffset[ID_TO_INDEX(iOwner)] = 0;
|
989 |
}
|
990 |
|
991 |
set_task(0.2, "fire_damage", MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7, "a", floatround(FireTime / 0.2, floatround_floor));
|
992 |
set_task(1.0, "fire_sound", MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7, "a", floatround(FireTime) - 1);
|
993 |
// This task removes the broken Molotov and "info_target" entity once molotov_firetime has expired
|
994 |
set_task(FireTime, "fire_stop", MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7);
|
995 |
|
996 |
return PLUGIN_CONTINUE;
|
997 |
}
|
998 |
|
999 |
// Since there isn't a reliable new round trigger in TFC, a task is created at round end that calls this function after a delay
|
1000 |
#if defined TFC
|
1001 |
public cancel_reset() {
|
1002 |
g_bReset = false;
|
1003 |
}
|
1004 |
#endif
|
1005 |
|
1006 |
// Make fire sounds
|
1007 |
public fire_sound(param[]) {
|
1008 |
emit_sound(param[1], CHAN_AUTO, "molotov/molotov_fire.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
|
1009 |
}
|
1010 |
|
1011 |
// Remove Molotov entities
|
1012 |
public fire_stop(param[]) {
|
1013 |
if (pev_valid(param[0])) { set_pev(param[0], pev_flags, pev(param[0], pev_flags) | FL_KILLME); } // Molotov entity
|
1014 |
if (pev_valid(param[1])) { set_pev(param[1], pev_flags, pev(param[1], pev_flags) | FL_KILLME); } // info_target entity
|
1015 |
}
|
1016 |
|
1017 |
// Call visual effect and damage functions
|
1018 |
public fire_damage(param[]) {
|
1019 |
|
1020 |
new iOrigin[3], Float:fOrigin[3];
|
1021 |
iOrigin[0] = param[4];
|
1022 |
iOrigin[1] = param[5];
|
1023 |
iOrigin[2] = param[6];
|
1024 |
|
1025 |
random_fire(iOrigin, param[1]); // Visual effect
|
1026 |
|
1027 |
IVecFVec(iOrigin, fOrigin);
|
1028 |
radius_damage2(param[2], param[3], fOrigin, get_pcvar_float(pFireDmg), get_pcvar_float(pMlRadius), DMG_BURN, false); // Actual damage
|
1029 |
}
|
1030 |
|
1031 |
// There is a radius_damage() in engine, so this was renamed.
|
1032 |
stock radius_damage2(iAttacker, iAttackerTeam, Float:fOrigin[3], Float:fDamage, Float:fRange, iDmgType, bool:bCalc = true) {
|
1033 |
|
1034 |
new Float:pOrigin[3], Float:fDist, Float:fTmpDmg;
|
1035 |
new i, iFF = get_pcvar_num(pMFF);
|
1036 |
|
1037 |
if (iFF == -1) { // Obey mp_friendlyfire
|
1038 |
iFF = get_pcvar_num(pFriendlyFire);
|
1039 |
#if defined DOD || defined TFC
|
1040 |
} else if (iFF == -2) { // Obey mp_teamplay (bit 5)
|
1041 |
new iTeamPlay = get_pcvar_num(pTeamPlay);
|
1042 |
if (iTeamPlay & (1 << 4)) { // bit 5 (16 = teammates take no damage from explosive weaponfire)
|
1043 |
iFF = 0;
|
1044 |
}
|
1045 |
#endif
|
1046 |
} // else, leave it at 0 or 1
|
1047 |
|
1048 |
while (i++ < g_MaxPlayers) {
|
1049 |
if (!is_user_alive(i)) {
|
1050 |
continue;
|
1051 |
}
|
1052 |
|
1053 |
#if defined TFC
|
1054 |
if ((iFF == 0) && ((iAttackerTeam == get_user_team(i)) || (tfc_is_team_ally(iAttackerTeam, get_user_team(i))))) { // TODO: tfc_is_team_ally is broken in AMX Mod X
|
1055 |
#else
|
1056 |
if ((iFF == 0) && (iAttackerTeam == get_user_team(i))) {
|
1057 |
#endif
|
1058 |
continue;
|
1059 |
}
|
1060 |
|
1061 |
pev(i, pev_origin, pOrigin);
|
1062 |
fDist = get_distance_f(fOrigin, pOrigin);
|
1063 |
|
1064 |
if (fDist > fRange) {
|
1065 |
continue;
|
1066 |
}
|
1067 |
|
1068 |
if (bCalc) {
|
1069 |
fTmpDmg = fDamage - (fDamage / fRange) * fDist;
|
1070 |
} else {
|
1071 |
fTmpDmg = fDamage;
|
1072 |
}
|
1073 |
|
1074 |
if (floatround(fTmpDmg) > 0) { // This eliminated the "[CSX] Invalid damage 0" error
|
1075 |
custom_weapon_dmg(g_wpnMolotov, iAttacker, i, floatround(fTmpDmg), 0);
|
1076 |
}
|
1077 |
|
1078 |
if (pev(i, pev_health) <= fTmpDmg) {
|
1079 |
kill(iAttacker, i, iAttackerTeam);
|
1080 |
} else {
|
1081 |
fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
|
1082 |
}
|
1083 |
}
|
1084 |
|
1085 |
// At this point, i is one higher than the highest possible player ID, so this loop only affects non-player entities
|
1086 |
while ((i = engfunc(EngFunc_FindEntityInSphere, i, fOrigin, fRange))) { // Extra parentheses fix warning 211: possibly unintended assignment
|
1087 |
if (pev(i, pev_takedamage)) {
|
1088 |
if (bCalc) {
|
1089 |
pev(i, pev_origin, pOrigin);
|
1090 |
fTmpDmg = fDamage - (fDamage / fRange) * get_distance_f(fOrigin, pOrigin);
|
1091 |
} else {
|
1092 |
fTmpDmg = fDamage;
|
1093 |
}
|
1094 |
fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
|
1095 |
}
|
1096 |
}
|
1097 |
}
|
1098 |
|
1099 |
// This stock only creates the visual effect. It does not handle any damage.
|
1100 |
// I tried using TE_FIREFIELD, but I can't make it look good in dod.
|
1101 |
stock random_fire(Origin[3], ent) {
|
1102 |
|
1103 |
static iRange, iOrigin[3], g_g, i;
|
1104 |
|
1105 |
iRange = get_pcvar_num(pMlRadius);
|
1106 |
|
1107 |
for (i = 1; i <= 5; i++) {
|
1108 |
|
1109 |
g_g = 1;
|
1110 |
|
1111 |
iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
|
1112 |
iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
|
1113 |
iOrigin[2] = Origin[2];
|
1114 |
iOrigin[2] = ground_z(iOrigin, ent);
|
1115 |
|
1116 |
while (get_distance(iOrigin, Origin) > iRange) { // If iOrigin is too far away, recalculate its position
|
1117 |
|
1118 |
iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
|
1119 |
iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
|
1120 |
iOrigin[2] = Origin[2];
|
1121 |
|
1122 |
if (++g_g >= ANTI_LAGG) {
|
1123 |
iOrigin[2] = ground_z(iOrigin, ent, 1);
|
1124 |
} else {
|
1125 |
iOrigin[2] = ground_z(iOrigin, ent);
|
1126 |
}
|
1127 |
}
|
1128 |
|
1129 |
new rand = random_num(5, 15);
|
1130 |
|
1131 |
message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
|
1132 |
write_byte(TE_SPRITE);
|
1133 |
write_coord(iOrigin[0]); // Position
|
1134 |
write_coord(iOrigin[1]);
|
1135 |
write_coord(iOrigin[2] + rand * 5);
|
1136 |
write_short(g_iFireSprite); // Sprite index
|
1137 |
write_byte(rand); // Scale
|
1138 |
write_byte(100); // Brightness
|
1139 |
message_end();
|
1140 |
}
|
1141 |
|
1142 |
// One smoke puff for each call to random_fire, regardless of number of flames
|
1143 |
message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
|
1144 |
write_byte(TE_SMOKE);
|
1145 |
write_coord(iOrigin[0]); // Position
|
1146 |
write_coord(iOrigin[1]);
|
1147 |
write_coord(iOrigin[2] + 120);
|
1148 |
write_short(g_iSmokeSprite[random_num(0, 1)]); // Sprite index
|
1149 |
write_byte(random_num(10, 30)); // Scale
|
1150 |
write_byte(random_num(10, 20)); // Framerate
|
1151 |
message_end();
|
1152 |
|
1153 |
}
|
1154 |
|
1155 |
// Stop all visual effect/physical damage tasks
|
1156 |
stock reset_tasks() {
|
1157 |
#if defined MOLOTOV_DEBUG
|
1158 |
new tmpdbgid;
|
1159 |
#endif
|
1160 |
for (new i; i < g_MaxPlayers; i++) { // for 0..31
|
1161 |
for (new o; o < MOLOTOV_TASKID_OFFSET; o++) {
|
1162 |
if (task_exists(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
|
1163 |
remove_task(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o);
|
1164 |
#if defined MOLOTOV_DEBUG
|
1165 |
tmpdbgid = MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o;
|
1166 |
log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
|
1167 |
#endif
|
1168 |
}
|
1169 |
|
1170 |
if (task_exists(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
|
1171 |
remove_task(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o);
|
1172 |
#if defined MOLOTOV_DEBUG
|
1173 |
tmpdbgid = MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o;
|
1174 |
log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
|
1175 |
#endif
|
1176 |
}
|
1177 |
// The third task for each Molotov is not stopped so it can remove the Molotov/info_target entities.
|
1178 |
}
|
1179 |
}
|
1180 |
}
|
1181 |
|
1182 |
// This function handles the killing and scoring.
|
1183 |
// iKillerTeam is stored because the killer can disconnect before the Molotov kills someone, leading to inaccurate scoring
|
1184 |
stock kill(iKiller, iVictim, iKillerTeam) {
|
1185 |
|
1186 |
//TFC: DeathMsg, ScoreInfo, ScoreInfo // One ScoreInfo for killer and one for victim (order varies)
|
1187 |
//DOD: DeathMsg, ScoreShort, Frags // ScoreShort=victim, Frags=killer
|
1188 |
// CS: DeathMsg, Money, ScoreInfo, ScoreInfo
|
1189 |
|
1190 |
// Scoreboard
|
1191 |
// CSTRIKE: Score Deaths
|
1192 |
// DMC: Frags Deaths
|
1193 |
// DOD: Score Kills Deaths
|
1194 |
// HL: Score Deaths
|
1195 |
// HLOF: Kills Deaths
|
1196 |
// TFC: Score Deaths
|
1197 |
// Ricochet: Points
|
1198 |
/* ----- Attacker ----- ------ Victim ------
|
1199 |
Score Deaths Kills Score Death Kills
|
1200 |
CS kill +1 - N/A - +1 N/A
|
1201 |
CS team kill -1 - N/A - +1 N/A
|
1202 |
CS suicide -1 +1 N/A ---------------------
|
1203 |
CS detonate/defuse +3 - N/A ---------------------
|
1204 |
DOD kill - - +1 - +1 -
|
1205 |
DOD team kill - - - - +1 - (mp_tkpenalty handles punishment)
|
1206 |
DOD suicide - +1 - ---------------------
|
1207 |
DOD cap +1 - - ---------------------
|
1208 |
TFC kill +1 - N/A - +1 N/A
|
1209 |
TFC team kill -1 - N/A - +1 N/A
|
1210 |
TFC suicide -1 +1 N/A ---------------------
|
1211 |
TFC cap/control varies - N/A ---------------------
|
1212 |
*/
|
1213 |
|
1214 |
|
1215 |
// ------------------------------------------------------------------------------------------------- DeathMsg (CS, DOD, TFC)
|
1216 |
// DeathMsg - Triggers HUD message and player console message
|
1217 |
// DOD and CSTRIKE have different formats for DeathMsg; most other mods should be default
|
1218 |
message_begin(MSG_ALL, g_msgDeathMsg, {0,0,0}, 0);
|
1219 |
write_byte(iKiller); // Killer ID
|
1220 |
write_byte(iVictim); // Victim ID
|
1221 |
#if defined CSTRIKE
|
1222 |
write_byte(0); // Is Headshot?
|
1223 |
#endif
|
1224 |
#if defined DOD
|
1225 |
write_byte(0); // Weapon ID - These don't match the DODW_* constants, and a custom weapon ID does not work, so we use "world"
|
1226 |
#else
|
1227 |
write_string("molotov"); // Truncated Weapon Name
|
1228 |
#endif
|
1229 |
message_end();
|
1230 |
// ------------------------------------------------------------------------------------------------- /DeathMsg (CS, DOD, TFC)
|
1231 |
|
1232 |
// This block of code actually kills the user (silently - DeathMsg was already created)
|
1233 |
new iVictimTeam = get_user_team(iVictim);
|
1234 |
new iMsgBlock = get_msg_block(g_msgDeathMsg); // Store original block value
|
1235 |
set_msg_block(g_msgDeathMsg, BLOCK_ONCE); // Start blocking DeathMsg
|
1236 |
|
1237 |
#if defined CSTRIKE
|
1238 |
|
1239 |
new iKillerFrags = get_user_frags(iKiller);
|
1240 |
new iVictimFrags = get_user_frags(iVictim);
|
1241 |
|
1242 |
// TFC and CS scoring are mostly the same. See TFC comment. (I did most of my testing with TFC first)
|
1243 |
if (iKiller != iVictim) {
|
1244 |
fm_set_user_frags(iVictim, iVictimFrags + 1); // Add frag that user_kill() will remove
|
1245 |
}
|
1246 |
|
1247 |
if (iKillerTeam != iVictimTeam) {
|
1248 |
iKillerFrags++; // Killer's Score = Score + 1
|
1249 |
} else {
|
1250 |
iKillerFrags--; // Killer's Score = Score - 1
|
1251 |
}
|
1252 |
fm_set_user_frags(iKiller, iKillerFrags);
|
1253 |
|
1254 |
// CSTRIKE Results --------------------------------------------------------------------------------------------------
|
1255 |
// DeathMsg ScoreInfoMsg KScore KDeath VScore VDeath Internally
|
1256 |
//user_kill(iVictim, 0); // Yes Yes (Victim) 0 0 -1 +1 Everything matches
|
1257 |
//user_kill(iVictim, 1); // Yes Yes (Victim) 0 0 -1 +1 VScore is different internally (unchanged)
|
1258 |
//user_silentkill(iVictim); // No Yes (Victim) 0 0 -1 +1 VScore is different internally (unchanged)
|
1259 |
|
1260 |
//dllfunc(DLLFunc_ClientKill, iVictim); // Yes Yes (Victim) 0 0 -1 +1 Everything matches
|
1261 |
//fm_user_kill(iVictim, 0); // Yes Yes (Victim) 0 0 -1 +1 Everything matches
|
1262 |
//fm_user_kill(iVictim, 1); // Yes Yes (Victim) 0 0 0 +1 Everything matches (fm_user_kill adds 1 to VScore)
|
1263 |
|
1264 |
// user_silentkill() blocks the DeathMsg, but it doesn't update the scoreboard properly
|
1265 |
// fm_user_kill() updates the scoreboard properly, but generates a DeathMsg with Victim=Killer, so we have to block that.
|
1266 |
#endif
|
1267 |
|
1268 |
#if defined TFC
|
1269 |
new iVictimFrags = tfc_get_user_frags(iVictim);
|
1270 |
|
1271 |
// TFC treats every type of kill in code as a suicide and takes away 1 frag from the victim.
|
1272 |
// After *extensive* testing, I found that the easiest solution is to increment the victim frags
|
1273 |
// (stored in pdata) beforehand and let the game decrement it and send out ScoreInfo messages.
|
1274 |
// This applies to *victims* of team kills as well as normal kills. Suicides still lose a frag.
|
1275 |
if (iKiller != iVictim) {
|
1276 |
tfc_set_user_frags(iVictim, iVictimFrags + 1);
|
1277 |
}
|
1278 |
|
1279 |
if ((iKillerTeam == iVictimTeam) || tfc_is_team_ally(iKillerTeam, iVictimTeam)) { // TODO: tfc_is_team_ally() is broken in AMX Mod X
|
1280 |
tfc_set_user_frags(iKiller, get_user_frags(iKiller) - 1); // Killer's Score = Score - 1
|
1281 |
} else {
|
1282 |
tfc_set_user_frags(iKiller, get_user_frags(iKiller) + 1); // Killer's Score = Score + 1
|
1283 |
}
|
1284 |
|
1285 |
// TFC Results --------------------------------------------------------------------------
|
1286 |
// DeathMsg ScoreInfoMsg VFrag Internally
|
1287 |
//user_kill(iVictim, 0); // Yes Yes -1 Everything matches
|
1288 |
//user_kill(iVictim, 1); // Yes Yes 0 VFrags doesn't match
|
1289 |
//user_silentkill(iVictim); // No Yes 0 VFrags doesn't match
|
1290 |
|
1291 |
//dllfunc(DLLFunc_ClientKill, iVictim); // Yes Yes -1 Everything matches
|
1292 |
//fm_user_kill(iVictim, 0); // Yes Yes -1 Everything matches
|
1293 |
//fm_user_kill(iVictim, 1); // Yes Yes -1 Everything matches
|
1294 |
|
1295 |
#endif
|
1296 |
|
1297 |
// DOD just works..
|
1298 |
// DOD Results --------------------------------------------------------------------------------------------------
|
1299 |
// DeathMsg ScoreShortMsg FragMsg KFrag VDeath VFrag Internally
|
1300 |
//user_kill(iVictim, 0); // Yes Yes N 0 +1 0 Everything matches
|
1301 |
//user_kill(iVictim, 1); // Yes Yes N 0 +1 0 Everything matches
|
1302 |
//user_silentkill(iVictim); // No Yes N 0 +1 0 Everything matches
|
1303 |
|
1304 |
//dllfunc(DLLFunc_ClientKill, iVictim); // Yes Yes N 0 +1 0 Everything matches
|
1305 |
//fm_user_kill(iVictim, 0); // Yes Yes N 0 +1 0 Everything matches
|
1306 |
//fm_user_kill(iVictim, 1); // Yes Yes N 0 +1 +1 OK, but VFrags shouldn't be changed
|
1307 |
|
1308 |
user_kill(iVictim, 0);
|
1309 |
set_msg_block(g_msgDeathMsg, iMsgBlock); // Stop blocking DeathMsg
|
1310 |
|
1311 |
//CSTRIKE client console messages:
|
1312 |
//Kill "Player1 killed [P0D]M0rbid Desire (2) with molotov"
|
1313 |
//TK "Player1 killed [POD]Kate_Winslet (2) with molotov"
|
1314 |
//Self "Player1 killed self with molotov"
|
1315 |
|
1316 |
//DOD client console messages: (DOD uses indexes instead of strings and a custom weapon name can't be sent to the player console)
|
1317 |
//Kill "Player1 killed Sgt.Moving_Target with world"
|
1318 |
//TK "Player1 killed his teammate Sgt.dontSHOOTiJUSTwannaTALK with world"
|
1319 |
//Self "Player1 killed self"
|
1320 |
|
1321 |
//TFC client console messages:
|
1322 |
//Kill "Player1 killed [FoX]JesseJames with molotov"
|
1323 |
//TK "Player1 killed [FoX]Barry with molotov"
|
1324 |
//Self "Player1 killed self with molotov"
|
1325 |
|
1326 |
|
1327 |
// I'm not really sure if this does anything, but it seems to match the Valve wiki: https://developer.valvesoftware.com/wiki/HL_Log_Standard
|
1328 |
new sVictim[32], sVictimAuth[35], sVictimTeam[32];
|
1329 |
get_user_name(iVictim, sVictim, charsmax(sVictim));
|
1330 |
get_user_authid(iVictim, sVictimAuth, charsmax(sVictimAuth));
|
1331 |
get_user_team(iVictim, sVictimTeam, charsmax(sVictimTeam)); // TERRORIST, CT, Allies, Axis, #Dustbowl_team1 (Attackers/Blue), #Dustbowl_team2 (Defenders/Red)
|
1332 |
if (iKiller == iVictim) {
|
1333 |
log_message("^"%s<%d><%s><%s>^" committed suicide with ^"molotov^"", sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam);
|
1334 |
} else if (is_user_connected(iKiller)) {
|
1335 |
new sKiller[32], sKillerAuth[35], sKillerTeam[32];
|
1336 |
get_user_name(iKiller, sKiller, charsmax(sKiller));
|
1337 |
get_user_authid(iKiller, sKillerAuth, charsmax(sKillerAuth));
|
1338 |
get_user_team(iKiller, sKillerTeam, charsmax(sKillerTeam));
|
1339 |
log_message("^"%s<%d><%s><%s>^" killed ^"%s<%d><%s><%s>^" with ^"molotov^"", sKiller, get_user_userid(iKiller), sKillerAuth, sKillerTeam, sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam);
|
1340 |
}
|
1341 |
// TODO: There currently isn't a log message for a kill by a disconnected player. The wiki doesn't show the expected format.
|
1342 |
|
1343 |
// ------------------------------------------------------------------------------------------------- Money (CS)
|
1344 |
#if defined CSTRIKE
|
1345 |
new iMoney;
|
1346 |
if (iKillerTeam == iVictimTeam) {
|
1347 |
iMoney = cs_get_user_money(iKiller) - 3300; // TODO - $1500 hostage kill penalty
|
1348 |
cs_set_user_money(iKiller, iMoney < 0 ? 0 : iMoney);
|
1349 |
} else {
|
1350 |
iMoney = cs_get_user_money(iKiller) + 300;
|
1351 |
cs_set_user_money(iKiller, iMoney > 16000 ? 16000 : iMoney);
|
1352 |
}
|
1353 |
#endif
|
1354 |
// ------------------------------------------------------------------------------------------------- /Money (CS)
|
1355 |
// ------------------------------------------------------------------------------------------------- ScoreInfo (CS and TFC)
|
1356 |
// ScoreInfo - Updates scoreboard on clients (actual values are changed elsewhere)
|
1357 |
// TFC - ScoreInfo messages are sent automatically after killing a player.
|
1358 |
// CS - ScoreInfo is sent automatically for the victim, but not killer.
|
1359 |
#if defined CSTRIKE
|
1360 |
message_begin(MSG_ALL, g_msgScoreInfo); // Killer ScoreInfo
|
1361 |
write_byte(iKiller);
|
1362 |
write_short(iKillerFrags);
|
1363 |
write_short(get_user_deaths(iKiller));
|
1364 |
write_short(0);
|
1365 |
write_short(iKillerTeam);
|
1366 |
message_end();
|
1367 |
#endif
|
1368 |
// ------------------------------------------------------------------------------------------------- /ScoreInfo (CS and TFC)
|
1369 |
|
1370 |
#if defined DOD
|
1371 |
// ------------------------------------------------------------------------------------------------- ScoreShort (DOD)
|
1372 |
// ScoreShort is sent by user_kill()
|
1373 |
// ------------------------------------------------------------------------------------------------- /ScoreShort (DOD)
|
1374 |
// ------------------------------------------------------------------------------------------------- Frags (DOD)
|
1375 |
if (iKillerTeam != iVictimTeam) { // Only give a frag if the player killed was an enemy (not suicide or TK)
|
1376 |
dod_set_user_kills(iKiller, dod_get_user_kills(iKiller) + 1, 1); // These natives seem to work properly.
|
1377 |
}
|
1378 |
// ------------------------------------------------------------------------------------------------- /Frags (DOD)
|
1379 |
#endif
|
1380 |
}
|
1381 |
|
1382 |
// Attempt to drop the passed coordinates to ground level
|
1383 |
stock ground_z(iOrigin[3], ent, skip = 0, iRecursion = 0) {
|
1384 |
|
1385 |
iOrigin[2] += random_num(5, 80);
|
1386 |
|
1387 |
if (!pev_valid(ent)) {
|
1388 |
return iOrigin[2];
|
1389 |
}
|
1390 |
|
1391 |
new Float:fOrigin[3];
|
1392 |
IVecFVec(iOrigin, fOrigin);
|
1393 |
set_pev(ent, pev_origin, fOrigin);
|
1394 |
engfunc(EngFunc_DropToFloor, ent);
|
1395 |
|
1396 |
if (!skip && !engfunc(EngFunc_EntIsOnFloor, ent)) {
|
1397 |
if (iRecursion >= ANTI_LAGG) {
|
1398 |
skip = 1;
|
1399 |
}
|
1400 |
|
1401 |
return ground_z(iOrigin, ent, skip, ++iRecursion);
|
1402 |
}
|
1403 |
|
1404 |
pev(ent, pev_origin, fOrigin);
|
1405 |
|
1406 |
return floatround(fOrigin[2]);
|
1407 |
}
|
1408 |
|
1409 |
// If plugin or override is disabled, reset Molotov models to original models
|
1410 |
#if defined CSTRIKE || defined DOD
|
1411 |
stock reset_molotovs() {
|
1412 |
new ent = g_MaxPlayers;
|
1413 |
#if defined CSTRIKE
|
1414 |
// TODO - My limited testing showed this code is pointless.
|
1415 |
// It has no negative effect, so I'm leaving it for the 3.30 release.
|
1416 |
// (I don't think "model" is a valid parameter.)
|
1417 |
new iOwner;
|
1418 |
while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
|
1419 |
iOwner = pev(ent, pev_owner);
|
1420 |
#if defined MOLOTOV_DEBUG
|
1421 |
client_print(0, print_chat, "reset_molotovs - found one Molotov! Owner(%d)", iOwner);
|
1422 |
#endif
|
1423 |
// If plugin is disabled or player owns no molotovs, reset their model
|
1424 |
if (!get_pcvar_num(pEnabled) || !g_NumMolotov[ID_TO_INDEX(iOwner)]) {
|
1425 |
engfunc(EngFunc_SetModel, ent, "models/w_hegrenade.mdl");
|
1426 |
}
|
1427 |
}
|
1428 |
#endif
|
1429 |
#if defined DOD
|
1430 |
new iOwner;
|
1431 |
while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
|
1432 |
#if defined MOLOTOV_DEBUG
|
1433 |
client_print(0, print_chat, "reset_molotovs - found one Molotov!");
|
1434 |
#endif
|
1435 |
|
1436 |
iOwner = pev(ent, pev_owner);
|
1437 |
if (iOwner) {
|
1438 |
switch(get_user_team(iOwner)) {
|
1439 |
case ALLIES: { // (or British)
|
1440 |
engfunc(EngFunc_SetModel, ent, "models/w_grenade.mdl"); // Mills is the same model, so this is probably fine
|
1441 |
}
|
1442 |
case AXIS: {
|
1443 |
engfunc(EngFunc_SetModel, ent, "models/w_stick.mdl");
|
1444 |
}
|
1445 |
}
|
1446 |
}
|
1447 |
}
|
1448 |
#endif
|
1449 |
|
1450 |
}
|
1451 |
#endif
|
1452 |
|
1453 |
// Mods that show the model before it is thrown need the model set (I think)
|
1454 |
#if defined CSTRIKE || defined DOD
|
1455 |
stock set_molotovs() {
|
1456 |
new ent = g_MaxPlayers;
|
1457 |
#if defined CSTRIKE
|
1458 |
while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_hegrenade.mdl"))) {
|
1459 |
#if defined MOLOTOV_DEBUG
|
1460 |
client_print(0, print_chat, "set_molotovs - found one hegrenade!");
|
1461 |
#endif
|
1462 |
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
|
1463 |
}
|
1464 |
#endif
|
1465 |
#if defined DOD
|
1466 |
while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_grenade.mdl"))) {
|
1467 |
#if defined MOLOTOV_DEBUG
|
1468 |
client_print(0, print_chat, "set_molotovs - found one grenade!");
|
1469 |
#endif
|
1470 |
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
|
1471 |
}
|
1472 |
ent = g_MaxPlayers;
|
1473 |
while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_stick.mdl"))) {
|
1474 |
#if defined MOLOTOV_DEBUG
|
1475 |
client_print(0, print_chat, "set_molotovs - found one stick!");
|
1476 |
#endif
|
1477 |
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
|
1478 |
}
|
1479 |
ent = g_MaxPlayers;
|
1480 |
while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_mills.mdl"))) {
|
1481 |
#if defined MOLOTOV_DEBUG
|
1482 |
client_print(0, print_chat, "set_molotovs - found one Mills!");
|
1483 |
#endif
|
1484 |
engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
|
1485 |
}
|
1486 |
#endif
|
1487 |
|
1488 |
}
|
1489 |
#endif
|
1490 |
|
1491 |
// Show optional buy menu
|
1492 |
#if defined CSTRIKE
|
1493 |
public show_molotov_menu(id) {
|
1494 |
new menu[128];
|
1495 |
formatex(menu, charsmax(menu), "Buy Molotov Cocktail ($%d)?^n^n1. Yes^n2. No^n^n0. Exit", get_pcvar_num(pPrice));
|
1496 |
|
1497 |
// This shows the menu for 30 seconds, I tried first with get_cvar_num("mp_buytime")*60 , but it didn't work well
|
1498 |
// when using 0.5 as mp_buytime. If you want to, just change the time below.
|
1499 |
show_menu(id, MOLOTOV_MENU_KEYS, menu, 30);
|
1500 |
|
1501 |
return PLUGIN_HANDLED;
|
1502 |
}
|
1503 |
#endif
|
1504 |
|
1505 |
// Our menu function will get the player id and the key they pressed
|
1506 |
#if defined CSTRIKE
|
1507 |
public giveMolotov(id, key) {
|
1508 |
|
1509 |
//key will start at zero
|
1510 |
switch(key) {
|
1511 |
case 0: buy_molotov(id);
|
1512 |
//I don't think these messages are necessary.
|
1513 |
//case 1: client_print(id, print_center, "You have chosen not to buy a Molotov cocktail");
|
1514 |
//default: client_print(id, print_center, "You have chosen to exit the Molotov menu");
|
1515 |
}
|
1516 |
}
|
1517 |
#endif
|
1518 |
|
1519 |
// Set user frags (score) in TFC
|
1520 |
#if defined TFC
|
1521 |
stock tfc_set_user_frags(iIndex, iNewFrags) {
|
1522 |
if (is_linux_server()) {
|
1523 |
set_pdata_int(iIndex, 76, iNewFrags); // real_frags = 76 (on Linux) Required!
|
1524 |
} else {
|
1525 |
set_pdata_int(iIndex, 77, iNewFrags); // real_frags = 77 (on Windows) Required?
|
1526 |
} // Is there a mac version?
|
1527 |
|
1528 |
// As far as I can tell, real_frags is what should be set, and something copies it to m_iClientFrags.
|
1529 |
//set_pdata_int(iIndex, 643, iNewFrags); // m_iClientFrags = 643 (on Linux/Windows)
|
1530 |
|
1531 |
// Sometimes this is required, sometimes it isn't. I think what is happening is that in
|
1532 |
// the cases it doesn't seem to be required, pev_frags is getting updated internally.
|
1533 |
set_pev(iIndex, pev_frags, float(iNewFrags));
|
1534 |
|
1535 |
#if defined MOLOTOV_DEBUG
|
1536 |
mydump(iIndex, 65, 85);
|
1537 |
mydump(iIndex, 635, 655);
|
1538 |
#endif
|
1539 |
}
|
1540 |
#endif
|
1541 |
|
1542 |
// Return a user's frags, and verify that the TFC offsets haven't changed
|
1543 |
#if defined TFC
|
1544 |
stock tfc_get_user_frags(iIndex) {
|
1545 |
new iOffset = (is_linux_server() ? 76 : 77); // real_frags = 76 (Linux), 77 (Windows)
|
1546 |
new iFrags = get_user_frags(iIndex);
|
1547 |
|
1548 |
// This code is the easiest way to detect a change in the offsets and help prevent annoying troubleshooting.
|
1549 |
// get_user_frags seems to always return the correct value.
|
1550 |
if (iFrags != get_pdata_int(iIndex, iOffset)) {
|
1551 |
client_print(0, print_chat, "OFFSET CHANGED! get_user_frags(%d):%d; get_pdata_int(%d, %d):%d; Contact plugin author!", iIndex, iFrags, iIndex, iOffset, get_pdata_int(iIndex, iOffset));
|
1552 |
console_print(0, "WARNING! get_user_frags != real_frags! Contact plugin author!!!!!!!!!!!!!!!!!!!!");
|
1553 |
}
|
1554 |
|
1555 |
return get_pdata_int(iIndex, iOffset);
|
1556 |
}
|
1557 |
#endif
|
1558 |
|
1559 |
// This won't be compiled unless MOLOTOV_DEBUG is set (and only for TFC)
|
1560 |
#if defined TFC
|
1561 |
stock mydump(iIndex, iStart, iEnd) {
|
1562 |
|
1563 |
new sLine[512];
|
1564 |
new FILE[] = "addons/amxmodx/logs/pdatadump.log";
|
1565 |
|
1566 |
new sClassname[64];
|
1567 |
entity_get_string(iIndex, EV_SZ_classname, sClassname, charsmax(sClassname));
|
1568 |
format(sLine, charsmax(sLine), "Starting dump of entity %s %d", sClassname, iIndex);
|
1569 |
console_print(1, "%s to file %s", sLine, FILE);
|
1570 |
if (!write_file(FILE, sLine)) {
|
1571 |
console_print(1, "Error dumping to %s!", FILE);
|
1572 |
return PLUGIN_HANDLED;
|
1573 |
}
|
1574 |
|
1575 |
for (new i = iStart; i <= iEnd; i++) {
|
1576 |
format(sLine, charsmax(sLine), "%s %d: Offset %d:^t%d^t%f", sClassname, iIndex, i, get_pdata_int(iIndex, i), get_pdata_float(iIndex, i));
|
1577 |
if (!write_file(FILE, sLine)) {
|
1578 |
console_print(1, "Error dumping to %s!", FILE);
|
1579 |
return PLUGIN_HANDLED;
|
1580 |
}
|
1581 |
}
|
1582 |
|
1583 |
console_print(1, "Dump done. Check %s!", FILE);
|
1584 |
|
1585 |
return 1;
|
1586 |
}
|
1587 |
#endif
|