/[svn]/hlds/cstrike/addons/amxmodx/scripting/molotov_cocktail.sma
ViewVC logotype

Contents of /hlds/cstrike/addons/amxmodx/scripting/molotov_cocktail.sma

Parent Directory Parent Directory | Revision Log Revision Log


Revision 61 - (show annotations) (download)
Sun Apr 13 20:37:54 2014 UTC (9 years, 11 months ago) by cstrike
File size: 61297 byte(s)
Added DOD/TFC support;
ALL:Added new molotov_ff values;
ALL:Adjusted post-blast friction/velocity;
ALL:Added glass break sound;
ALL:Fixed suicide scoring;
ALL:Converted fun calls to fakemeta;
ALL:Fixed console text not always going to the correct console;
ALL:Added custom weapon/log_message messages for stats logging plugins;
ALL:Made status message more detailed;
ALL:Fixed negative molotovs with override;
CSTRIKE:Fixed money calculations;
CSTRIKE:Removed text buy menu support;
CSTRIKE:Fixed molotov_menu/molotov_override_he conflict;
CSTRIKE:Default price changed to $300;
TFC:Fixed molotovs persisting in new round (on hunted);
TFC:Added workaround for incorrect TFC_PC_CIVILIAN value;
ALL:Fixed typos, poorly optimized code;
ALL:Updated version and date for v3.30 release;
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

Properties

Name Value
molotov 1

Contact webmaster
ViewVC Help
Powered by ViewVC RSS 2.0 feed