View Single Post
LaMFo
New Member
Join Date: Jul 2021
Old 07-23-2021 , 14:16   Re: Molotov Cocktail only for Team_T
Reply With Quote #3

Code:
/*******************************************************************************
*
*   This program is free software; you can redistribute it and/or
*   modify it under the terms of the GNU General Public License
*   as published by the Free Software Foundation; either version 2
*   of the License, or version 3.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
*   In addition, as a special exception, the author gives permission to
*   link the code of this program with the Half-Life Game Engine ("HL
*   Engine") and Modified Game Libraries ("MODs") developed by Valve,
*   L.L.C ("Valve"). You must obey the GNU General Public License in all
*   respects for all of the code used other than the HL Engine and MODs
*   from Valve. If you modify this file, you must extend this exception
*   to your version of the file.
*
********************************************************************************


			Molotov Cocktail
			  Version 3.30
		Maintained by: DynamicBits (Andy)

* Commands:
- say molotov - Buy a Molotov
- say /molotov - Buy a Molotov
- molotov_give <player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give Molotov(s) to a player, a team, or everyone
- molotov_cocktail [0|1] - Enable/disable the plugin (If no arguments, show the status)
- molotov_override [0|1] - Enable/disable the standard grenade override (If no arguments, show the status)

* Cvars
- molotov_enabled <0|1> - (Default: 1) Enable(1)/disable(0) the plugin
- molotov_price <N> - (Default: 300) Set the Molotov price (Counter-Strike only)
- molotov_damage <N> - (Default: 50.0) Set the damage done by initial Molotov explosion
- molotov_radius <N> - (Default: 150.0) Set the radius of Molotov damage
- molotov_firetime <N> - (Default: 6) Duration (in seconds) of fire effects, sounds, etc.
- molotov_firedamage <N> - (Default: 3) Amount of damage done by fire effects (every 0.2 secs)
- molotov_ff <0|1|-1|-2> - (Default: 1) Set Molotov friendly fire status (Was molotov_tk)
  *  0 - Disable friendly fire for Molotovs (regardless of mp_friendlyfire)
  *  1 - Enable friendly fire for Molotovs (regardless of mp_friendlyfire)
  * -1 - Use mp_friendlyfire value
  * -2 - Check bit 5 (decimal: 16) of mp_teamplay (DOD and TFC only)
- molotov_override_he <0|1> - (Default: 0) Override the mod's standard grenade automatically with Molotov (Was molotov_tempoverride)
- molotov_max <N> - (Default: 1) Limit carried Molotovs to this amount
  * (Recommended: CSTRIKE: ≤ 10; DOD: ≤ 9; TFC: ≤ 4;)
- molotov_buyzone <0|1> - (Default: 1) Limit Molotov buying to buyzone (Counter-Strike only)
- molotov_menu <0|1> - (Default: 0) Enable menu at beginning of each round (Was amx_molotovmenu)

* Required Modules:
- Fakemeta
- Cstrike (Counter-Strike only)
- Csx (Counter-Strike only)
- Dodfun (Day of Defeat only)
- Dodx (Day of Defeat only)
- Tfcx (Team Fortress Classic only)
- Engine (TFC with debugging only)

* Changelog/Credit:
- DynamicBits
  * Version 3.30 (2014-04-13)
    - (Beta) Day of Defeat support was added
    - (Beta) Team Fortress Classic support was added
    - (Untested) Stats logging support was added
    - New values for molotov_ff were added
    - Friction/velocity after explosion was adjusted for realism
    - Bottle breaking sound was added
    - Molotov suicides no longer reward extra score points
    - Frag count calculations were fixed
    - Money calculations were fixed
    - Override no longer sets a negative number of Molotovs
    - Default price was changed to match standard grenades
    - Console text now goes to correct console
    - Text (non-VGUI) buy menu support was removed
    - Converted fun functions to fakemeta_util functions (removed fun include)
    - Various optimizations
    - A few typos were fixed
  * Version 3.20 (2008-11-20)
    - My first public release
    - Finally tracked down and fixed the intermittent crashing problem (I hope!)
    - Modified default damage values
    - molotov_cocktail/molotov_override commands now change settings *or* display status
    - Broken Molotov model stays closer to the explosion (looks more realistic)
    - Task IDs are now guaranteed to be unique
    - Modified anti-lag calculations to be more accurate (less likely to lag)
    - Changed amx_molotovmenu CVAR to molotov_menu
    - Changed molotov_tk CVAR to molotov_ff
    - Changed molotov_tempoverride CVAR to molotov_override_he
    - Preparation for support of mods other than Counter-Strike
    - Fixed lots of coding mistakes
    - Optimized several sections of code
    - Corrected grammar/typos
    - Clean up code (Removed unused code/unhelpful comments, fixed formatting, and semicolons!)
- Raffe (CantShoot)
  * (Unversioned release)
    - Originally fixed plugin to run on Linux servers
    - Added optional menu to purchase Molotov cocktails each round
    - Moved models and sounds into proper molotov/ subdirectories
    - Fixed Molotovs not being reset upon player disconnect
    - (Almost) fixed Molotovs not being removed for new round
    - Added @all/@ct/@t arguments to molotov_give command
    - Changed some models/sound
- [ --<-@ ] Black Rose
  * Version 3.0-3.1c ?
    - Unknown changes
- SAMURAI
  * Original plugin author


*/

#pragma semicolon 1

// Uncomment only the define that applies to your mod
#define CSTRIKE
//#define DOD
//#define TFC

// Uncomment the following line to enable debug logging.
//#define MOLOTOV_DEBUG


#include <amxmodx>
#include <amxmisc>
//#include <fakemeta>		// (runtime only)
#include <fakemeta_util>
#if defined CSTRIKE
	#include <cstrike>
	#include <csx>		// Used only for custom_weapon_* functions
#endif
#if defined DOD
	#include <dodfun>
	#include <dodx>		// Used only for custom_weapon_* functions
#endif
#if defined TFC
	#include <tfcx>
	#include <engine>	// Used only with debugging enabled
#endif

// If you really want to same some memory and know you won't have 32 players, you can change this.
#define MAX_PLAYERS		32
#define ADMIN_ACCESS		ADMIN_KICK

#define ANTI_LAGG		7	// Defines max calculations before a flame is spawned without check if on ground
// This is to prevent lag at really narrow ents where you could end up with 400 calculations per flame. Suggested: <= 10

#define MOLOTOV_HARD_LIMIT	10	// Maximum Molotov cocktails this code is capable of handling without bugs (per player)

#define ID_TO_INDEX(%0)		%0 - 1	// Use this macro rather than dim the arrays with 33

#define MOLOTOV_MENU_KEYS MENU_KEY_0|MENU_KEY_1|MENU_KEY_2			// Choices to look for with optional menu

// Task IDs
#define MOLOTOV_TASKID_RESET 	1000						// Set g_bReset to false after a short delay (task is TFC only)
#define MOLOTOV_TASKID_OFFSET	MOLOTOV_HARD_LIMIT
// These task IDs are dynamically set per-Molotov
#define MOLOTOV_TASKID_BASE1	2000						// By default, with 32 players, task ids
#define MOLOTOV_TASKID_BASE2	MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // from 2000 to 2959 can
#define MOLOTOV_TASKID_BASE3	MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * MAX_PLAYERS) // potentially be used used.

#define TEAM_UNASSIGNED 	0
#define TEAM_ONE		1
#define TEAM_TWO		2
#define TEAM_THREE		3
#define TEAM_FOUR		4
#define MC_TFC_PC_CIVILIAN	11	// Temporary workaround for AMXX bug 6042


new const g_PLUGIN[]  = "Molotov Cocktail";
new const g_AUTHORS[] = "DynamicBits";
new const g_VERSION[] = "3.30";

new pEnabled;				// Pointer to molotov_enabled
new pMlDamage;				// Pointer to molotov_damage
new pMlRadius;				// Pointer to molotov_radius
new pFireTime;				// Pointer to molotov_firetime
new pOverride;				// Pointer to molotov_override_he
new pMFF;				// Pointer to molotov_ff
new pFriendlyFire;			// Pointer to mp_friendlyfire
new pFireDmg;				// Pointer to molotov_firedamage
new pMaxMolotovs;			// Pointer to molotov_max
#if defined DOD || defined TFC
new pTeamPlay;				// Pointer to mp_teamplay
#endif
#if defined CSTRIKE
new pBuyZone;				// Pointer to molotov_buyzone
new pMolotovMenu;			// Pointer to molotov_menu
new pPrice;				// Pointer to molotov_price

new g_msgScoreInfo;			// ScoreInfo message ID
#endif
new g_msgDeathMsg;			// DeathMsg message ID

new g_NumMolotov[MAX_PLAYERS];		// How many Molotovs each player has
#if defined CSTRIKE
new bool:g_bRestarted;			// Reset Molotovs after first round restart
#endif
new g_MaxPlayers;			// Max players (calculated at runtime to make loops more efficient)
new g_wpnMolotov;			// Custom weapon ID
new bool:g_bReset;			// Reset and stop explosions after round ends; Stop reset_tasks() from getting called once per player

new g_iFireSprite, g_iSmokeSprite[2];	// Handles to the precached sprites
new g_iMolotovOffset[MAX_PLAYERS];	// Offset used for a player's task ID

// The Pawn compiler does not optimize the DATA section. Any string that appears multiple times should be optimized with a global constant.
//   *Unused* constants do not affect the compiled size, however they create compiler warnings. (#pragma unused is a quick fix for the warnings.)
new const EVENT_ROUND_END[] = "event_round_end";
#if defined CSTRIKE
new const BUY_MOLOTOV[] = "buy_molotov";
new const WEAPON_HEGRENADE[] = "weapon_hegrenade";
#endif
#if defined DOD
new const WEAPON_HANDGRENADE[] = "weapon_handgrenade";
new const WEAPON_STICKGRENADE[] = "weapon_stickgrenade";
#endif
#if defined DOD || defined TFC
new const HUDTEXT[] = "HudText";
#endif
#if defined CSTRIKE || defined TFC
new const TEXTMSG[] = "TextMsg";
#endif

// Check for outdated tfcconst.inc file (and likely outdated AMX Mod X core/modules).
//   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.
//   In the meantime, I created the MC_TFC_PC_CIVILIAN define.
//#if defined TFC && TFC_PC_CIVILIAN != 11	// TFC_PC_CIVILIAN was (incorrectly) 10 in older versions
//	#error TFC_PC_CIVILIAN != 11. Update your tfcconst.inc include file. Get the latest AMX Mod X snapshots at www.amxmodx.org/snapshots.php
//#endif


// Initialize the plugin
public plugin_init() {

	register_plugin(g_PLUGIN, g_VERSION, g_AUTHORS);
	server_print("[MC] ---- Molotov Cocktail %s loaded ----", g_VERSION);
	register_cvar("MolotovCocktail", g_VERSION, FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);

#if defined CSTRIKE
	register_menucmd(register_menuid("Buy Molotov Cocktail"), MOLOTOV_MENU_KEYS, "giveMolotov");

	#if defined MOLOTOV_DEBUG
	register_clcmd("molotov_menutest", "show_molotov_menu");
	#endif

	register_clcmd("say /molotov", BUY_MOLOTOV);
	register_clcmd("say molotov", BUY_MOLOTOV);
#endif
	register_concmd("molotov_give", "molotov_give", ADMIN_ACCESS, "<player|@all|@t|@ct|@al|@ax|@br|@b|@r|@y|@g> - Give free Molotov cocktails");
	register_concmd("molotov_override", "molotov_override", ADMIN_ACCESS, "[0|1] - Enable/disable the standard grenade override (If no arguments, show the status)");
	register_concmd("molotov_cocktail", "molotov_cocktail", ADMIN_ACCESS, "[0|1] - Enable/disable the plugin (If no arguments, show the status)");

	pEnabled = register_cvar("molotov_enabled", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pOverride = register_cvar("molotov_override_he", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pMlDamage = register_cvar("molotov_damage", "50.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pMlRadius = register_cvar("molotov_radius", "150.0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pFireTime = register_cvar("molotov_firetime", "6", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pFireDmg = register_cvar("molotov_firedamage", "3", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pMFF = register_cvar("molotov_ff", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pMaxMolotovs = register_cvar("molotov_max", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pFriendlyFire = register_cvar("mp_friendlyfire", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#if defined CSTRIKE
	pBuyZone = register_cvar("molotov_buyzone", "1", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pMolotovMenu = register_cvar("molotov_menu", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
	pPrice = register_cvar("molotov_price", "300", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#endif
#if defined DOD
	pTeamPlay = register_cvar("mp_teamplay", "0", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#endif
#if defined TFC
	pTeamPlay = register_cvar("mp_teamplay", "21", FCVAR_EXTDLL|FCVAR_SPONLY|FCVAR_PRINTABLEONLY);
#endif

	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).
#if defined CSTRIKE || defined DOD
	register_event("CurWeapon", "event_curweapon", "be", "1=1");
	register_event("HLTV", "event_new_round", "a", "1=0", "2=0");	// cstrike/dod new round; So far, I haven't found a TFC equivalent
#endif
#if defined CSTRIKE
	register_event(TEXTMSG, "event_gamerestart", "a", "2=#Game_Commencing", "2=#Game_will_restart_in");
#endif
#if defined DOD
	register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&VICTORY");	// Sent once per player on round end
#endif
#if defined TFC
	// Since TFC doesn't have any generic end of round event/message, specific messages need to be caught for certain maps.
	//   Maps that don't have traditional rounds (2fort, badlands, casbah, crossover2, cz2, ravelin, skate2, well, etc.) don't apply here.
	//   All of the default maps are accounted for. If there is demand for specific custom maps, I will add the appropriate message(s).
	new sCurrentMap[32];
	get_mapname(sCurrentMap, charsmax(sCurrentMap));
	if (!strcmp(sCurrentMap, "avanti")) {
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#italy_endround_win");		// Was Avanti originally called Italy?
	} else if ((!strcmp(sCurrentMap, "dustbowl")) || (!strcmp(sCurrentMap, "castleargh")) || (!strcmp(sCurrentMap, "castleargh2"))) {
		register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_one");	// Technically these are "stages," not "rounds"
		register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#dustbowl_blue_secures_two");
		//register_event(TEXTMSG, "event_round_end", "b", "2=#dustbowl_blue_caps");	// The map ends after this cap
	} else if (!strcmp(sCurrentMap, "epicenter")) {
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#dblmint_you_capped_flag");	// dblmint?!
	} else if (!strcmp(sCurrentMap, "flagrun")) {
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&you won this round!");
	} else if (!strcmp(sCurrentMap, "hunted")) {
		register_event(TEXTMSG, EVENT_ROUND_END, "b", "2=#hunted_target_killed");
	} else if (!strcmp(sCurrentMap, "push")) {
		register_event(TEXTMSG, EVENT_ROUND_END, "b", "2&_netname_scores");
	} else if (!strcmp(sCurrentMap, "rock2")) {
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=1 . . .^n");
	} else if (!strcmp(sCurrentMap, "warpath")) {
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_red_wins");
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#warpath_blue_wins");
	// ---------- Custom Maps ----------
	} else if (!strcmp(sCurrentMap, "castleargh3")) {
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&You have secured");		// This works for all four stages
	} else if (!strcmp(sCurrentMap, "hwguyz2")) {
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1=#2fort_you_capped_flag");
		register_event(HUDTEXT, EVENT_ROUND_END, "b", "1&Time Ran Out");
	}

	//#if defined MOLOTOV_DEBUG
	//register_event(TEXTMSG, "event_textmsg_a", "a");
	//register_event(TEXTMSG, "event_textmsg_b", "b");
	//register_event(HUDTEXT, "event_hudtext_a", "a");
	//register_event(HUDTEXT, "event_hudtext_b", "b");
	//#endif
#endif

#if defined CSTRIKE
	register_logevent(EVENT_ROUND_END, 2, "1=Round_End");
#endif

	register_forward(FM_EmitSound, "fw_emitsound");
#if defined TFC
	register_forward(FM_SetModel, "fw_setmodel_post", 1);
#endif

	g_MaxPlayers = get_maxplayers();

#if defined CSTRIKE
	g_msgScoreInfo = get_user_msgid("ScoreInfo");
#endif
	g_msgDeathMsg = get_user_msgid("DeathMsg");

	g_wpnMolotov = custom_weapon_add("molotov", 0, "molotov");	// I can hardly find any documentation or sample code for this. I have no
									//   idea if I'm using it correctly or not. I'm not even sure what it affects.
}

// These are primarily for catching messages in TFC to add custom round end triggers.
/*
#if defined MOLOTOV_DEBUG
public event_textmsg_a() {
	new sArg2[64];
	read_data(2, sArg2, charsmax(sArg2));
	
	client_print(0, print_chat, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
	console_print(0, "event_textmsg_a 1(%d) 2(%s)", read_data(1), sArg2);
}

public event_textmsg_b() {
	new sArg2[64];
	read_data(2, sArg2, charsmax(sArg2));

	client_print(0, print_chat, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
	console_print(0, "event_textmsg_b 1(%d) 2(%s)", read_data(1), sArg2);
}

public event_hudtext_a() {
	new sArg1[64];
	read_data(1, sArg1, charsmax(sArg1));

	client_print(0, print_chat, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
	console_print(0, "event_hudtext_a 1(%s) 2(%d)", sArg1, read_data(1));
}

public event_hudtext_b() {
	new sArg1[64];
	read_data(1, sArg1, charsmax(sArg1));

	client_print(0, print_chat, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
	console_print(0, "event_hudtext_b 1(%s) 2(%d)", sArg1, read_data(1));
}
#endif
*/

// Precache models and sound(s)
public plugin_precache() {

	g_iFireSprite = precache_model("sprites/flame.spr");

	g_iSmokeSprite[0] = precache_model("sprites/black_smoke3.spr");
#if defined DOD
	g_iSmokeSprite[1] = g_iSmokeSprite[0];			// steam1.spr shows a black background in dod
#else
	g_iSmokeSprite[1] = precache_model("sprites/steam1.spr");
#endif

#if defined CSTRIKE || defined DOD
	precache_model("models/molotov/p_molotov.mdl");
	precache_model("models/molotov/v_molotov.mdl");
#endif
	precache_model("models/molotov/w_molotov.mdl");
	precache_model("models/molotov/w_broke_molotov.mdl");

	precache_sound("molotov/molotov_fire.wav");

}

// Reset Molotovs so that a new player doesn't have any
public client_disconnected(id) {
	g_NumMolotov[ID_TO_INDEX(id)] = 0;
}

// Catch the first impact of the Molotov and start the sound/explosion
//   A Molotov cocktail should "explode" on impact, not after a set time.
public fw_emitsound(ent, channel, sample[]) {
#if defined CSTRIKE
	if (equal(sample[8], "he_bounce", 9)) {
#else
	#if defined DOD || defined TFC
	// 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)
	//   A fix would be to use FM_Touch or Ham_Touch or register_touch instead of FM_EmitSound
	if (equal(sample[8], "grenade_hit", 11)) {
	#endif
#endif

		new sModel[32];
		pev(ent, pev_model, sModel, charsmax(sModel));

		// Depending on where the Molotov lands, the EmitSound forward may get called 50+ times.
		// After the first hit, the model is changed to w_broke_molotov, so this code is skipped on successive calls
		if (equal(sModel[15], "w_molotov.mdl")) {
#if defined TFC
			set_pev(ent, pev_nextthink, 99999.0);		// For TFC, this is about the only way I can stop the explosion.
#endif
			// The glass breaking sound has a low range (ATTN_STATIC) so as not to be overpowering
			emit_sound(ent, CHAN_AUTO, "debris/glass2.wav", VOL_NORM, ATTN_STATIC, 0, PITCH_LOW);

			new Float:fFriction, Float:fVelocity[3];
			pev(ent, pev_friction, fFriction);
			fFriction *= 1.15;				// Increase friction to make it look more realistic
			set_pev(ent, pev_friction, fFriction);

			pev(ent, pev_velocity, fVelocity);
			fVelocity[0] *= 0.3;				// Decrease velocity because friction doesn't do it all
			fVelocity[1] *= 0.3;
			fVelocity[2] *= 0.3;
			set_pev(ent, pev_velocity, fVelocity);

			molotov_explode(ent);				// Replacement for normal grenade explosion

			return FMRES_SUPERCEDE;
		} else if (equal(sModel[15], "w_broke_molotov.")) {	// "mdl" is truncated because of the array size, which is OK
			return FMRES_SUPERCEDE;			// Don't play any sounds for bounces.
		}
	}
	
	return FMRES_IGNORED;
}

// Since TFC handles grenades differently, this is roughly equivalant to event_curweapon() used by cstrike and dod.
#if defined TFC
public fw_setmodel_post(ent, const model[]) {
	if (!pev_valid(ent)) {			// Check if it's a valid entity to prevent errors
		return FMRES_IGNORED;
	}

	new sClassname[32];
	pev(ent, pev_classname, sClassname, charsmax(sClassname));

	if (!get_pcvar_num(pEnabled) || !equal(sClassname, "normalgrenade")) {
		return FMRES_IGNORED;
	}

	new iOwner = pev(ent, pev_owner);
	if (!g_NumMolotov[ID_TO_INDEX(iOwner)] && !get_pcvar_num(pOverride)) {	// If no Molotovs and override is disabled, return
		return FMRES_IGNORED;
	}
	
	if (g_NumMolotov[ID_TO_INDEX(iOwner)] > 0) {	// Prevent negative values
		g_NumMolotov[ID_TO_INDEX(iOwner)]--;
	}

	set_pev(ent, pev_team, get_user_team(iOwner));
	custom_weapon_shot(g_wpnMolotov, iOwner);

	engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");

	return FMRES_HANDLED;
}
#endif

// When the player changes weapons to the Molotov, update the model
#if defined CSTRIKE || defined DOD
public event_curweapon(id) {

	if (!get_pcvar_num(pEnabled) || !is_user_alive(id)) {
		return PLUGIN_CONTINUE;
	}

	if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) {	// If no Molotovs and override is disabled, return
		return PLUGIN_CONTINUE;
	}

	new iWeaponID = get_user_weapon(id, _, _);
#if defined CSTRIKE
	if (iWeaponID != CSW_HEGRENADE) {
#else							// elseif *should* work, but there is a bug in the compiler
	#if defined DOD
	// current weapon is never set to DODW_MILLS_BOMB in this event; only DODW_HANDGRENADE/DODW_STICKGRENADE
	if ((iWeaponID != DODW_HANDGRENADE) && (iWeaponID != DODW_STICKGRENADE)) {
	#endif
#endif
		return PLUGIN_CONTINUE;
	}

	set_pev(id, pev_viewmodel2, "models/molotov/v_molotov.mdl");	// View model (First person)	*model2 doesn't require allocating the string
	set_pev(id, pev_weaponmodel2, "models/molotov/p_molotov.mdl");	// Player model (Third person)

#if defined DOD
	// I think 3 is correct, but it looks strange..
	set_pev(id, pev_weaponanim, 3);	// 0: "idle"; 1: "pullpin"; 2: "throw"; 3: "deploy"
#endif

	return PLUGIN_CONTINUE;
}
#endif

// Reset Molotovs on death
public event_deathmsg() {
#if defined MOLOTOV_DEBUG
	log_amx("[MC] ========== DeathMsg ========== K(%d) V(%d)", read_data(1), read_data(2));
#endif
	g_NumMolotov[ID_TO_INDEX(read_data(2))] = 0;
}

// cstrike only
#if defined CSTRIKE
public event_gamerestart() {
#if defined MOLOTOV_DEBUG
	log_amx("[MC] ========== Game Restart ==========");
#endif
	g_bRestarted = true;
}
#endif

// cstrike, dod, and tfc will all call this once per player on round end
public event_round_end() {
#if defined MOLOTOV_DEBUG
	log_amx("[MC] ========== Round End ==========");
#endif

	if (g_bReset == false) {
		reset_tasks();
		g_bReset = true;
#if defined TFC
		set_task(2.0, "cancel_reset", MOLOTOV_TASKID_RESET);	// TFC won't call event_new_round, so do that stuff here instead
#endif
	}
}

// cstrike and dod will call this once per round, but TFC won't.
#if defined CSTRIKE || defined DOD
public event_new_round(id) {
#if defined MOLOTOV_DEBUG
	log_amx("[MC] ========== New Round ==========");
#endif
	g_bReset = false;	// Stop blocking

	if (!get_pcvar_num(pEnabled)) {
		return PLUGIN_CONTINUE;
	}

	reset_tasks();						// This probably isn't needed anymore, but it shouldn't hurt anything

#if defined CSTRIKE
	if (get_pcvar_num(pMolotovMenu)) {
		if (get_pcvar_num(pOverride)) {
			client_print(id, print_center, "Molotov cocktails will replace purchased HE grenades");
		} else {
			show_molotov_menu(id);
		}
	}

	// For cstrike only, make sure the player didn't quickly purchase a Molotov before the first actual round
	if (g_bRestarted) {
		arrayset(g_NumMolotov, 0, sizeof(g_NumMolotov));	// Reset everyone to zero Molotovs
		g_bRestarted = false;
	}
#endif

	if (get_pcvar_num(pOverride)) {
		set_molotovs();
	} else {
		reset_molotovs();
	}

	return PLUGIN_CONTINUE;
}
#endif

// Enable/Disable/Get status of override
public molotov_override(id, level, cid) {

	if (!cmd_access(id, level, cid, 1)) {		// First argument (passed to molotov_override) is optional
		return PLUGIN_HANDLED;
	}

	if (!get_pcvar_num(pEnabled)) {
		return PLUGIN_HANDLED;
	}

	if (read_argc() == 1) {				// No arguments; Display status
		console_print(id, "Override is currently %s.", get_pcvar_num(pOverride) ? "enabled" : "disabled");
		return PLUGIN_HANDLED;
	}

	new sArg[2];
	read_argv(1, sArg, charsmax(sArg));

	new iArg = str_to_num(sArg);

	if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) {	// If less than 0 or greater than 1 or not a digit
		console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
		return PLUGIN_HANDLED;
	}

	if (iArg == get_pcvar_num(pOverride)) {
		console_print(id, "Override is already %s.", iArg ? "enabled" : "disabled");
		return PLUGIN_HANDLED;
	}

	set_pcvar_num(pOverride, iArg);
	console_print(id, "Override was %s.", iArg ? "enabled" : "disabled");

#if defined CSTRIKE || defined DOD
	if (iArg) {					// If plugin is enabled (checked above) and override is enabled, set models to Molotov
		set_molotovs();
	} else {
		reset_molotovs();
	}
#endif

	return PLUGIN_HANDLED;
}

// Enable/Disable/Get status of plugin
public molotov_cocktail(id, level, cid) {
	if (!cmd_access(id, level, cid, 1)) {		// First argument (passed to molotov_cocktail) is optional
		return PLUGIN_HANDLED;
	}

	if (read_argc() == 1) {				// No arguments; Display status
		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));
		return PLUGIN_HANDLED;
	}

	new sArg[2];
	read_argv(1, sArg, charsmax(sArg));

	new iArg = str_to_num(sArg);

	if ((iArg < 0) || (iArg > 1) || (!isdigit(sArg[0]))) {	// If less than 0 or greater than 1 or not a digit
		console_print(id, "Invalid argument(%s). Valid arguments are ^"0^" and ^"1^".", sArg);
		return PLUGIN_HANDLED;
	}

	if (iArg == get_pcvar_num(pEnabled)) {
		console_print(id, "Plugin is already %s.", iArg ? "enabled" : "disabled");
		return PLUGIN_HANDLED;
	}

	set_pcvar_num(pEnabled, iArg);
	console_print(id, "Plugin was %s.", iArg ? "enabled" : "disabled");

#if defined CSTRIKE || defined DOD
	if (iArg && get_pcvar_num(pOverride)) {		// If the plugin was enabled and override is enabled, set models to Molotov
		set_molotovs();
	} else {
		reset_molotovs();
	}
#endif

	return PLUGIN_HANDLED;
}

// Handle molotov_give console command
public molotov_give(id, level, cid) {
	if (!cmd_access(id, level, cid, 2)) {
		return PLUGIN_HANDLED;
	}

	new sArg1[16], iTarget;
	read_argv(1, sArg1, charsmax(sArg1));
#if defined MOLOTOV_DEBUG
	log_amx("[MC] molotov_give sArg1[0](%s)", sArg1[0]);
#endif
	new sAdmin[32];
	get_user_name(id, sAdmin, charsmax(sAdmin));
	new iGiveAmount = (get_pcvar_num(pMaxMolotovs) < MOLOTOV_HARD_LIMIT ? get_pcvar_num(pMaxMolotovs) : MOLOTOV_HARD_LIMIT);

	if (sArg1[0] == '@') {

		new iTargetTeam, sTeamName[32];
		new Players[MAX_PLAYERS], iNum;

		if (equali(sArg1[1], "all")) {
			iTargetTeam = 0;
		} 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
			iTargetTeam = TEAM_ONE;
		} else if (equali(sArg1[1], "ct") || equali(sArg1[1], "ax") || equali(sArg1[1], "r")) {	// CS_TEAM_CT or AXIS or Red
			iTargetTeam = TEAM_TWO;
#if defined TFC
		} else if (equali(sArg1[1], "y")) {	// Yellow
			iTargetTeam = TEAM_THREE;
		} else if (equali(sArg1[1], "g")) {	// Green
			iTargetTeam = TEAM_FOUR;
#endif
		}

		get_players(Players, iNum, "ach");	// alive, no bots, no HLTV

		for (new i = 0; i < iNum; ++i) {
			iTarget = Players[i];

			if ((iTargetTeam == 0) || (get_user_team(iTarget) == iTargetTeam)) {
				g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;

#if defined CSTRIKE
				fm_give_item(iTarget, WEAPON_HEGRENADE);
				cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
#endif
#if defined DOD
				// TODO - This sets the count, but it is not immediately updated on the HUD
				switch(get_user_team(iTarget)) {
					case ALLIES: {	// (or British)
						fm_give_item(iTarget, WEAPON_HANDGRENADE);
						dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
					}
					case AXIS: {
						fm_give_item(iTarget, WEAPON_STICKGRENADE);
						dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
					}
				}
#endif
#if defined TFC
				new iClass = pev(iTarget, pev_playerclass);
				if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) {	// No unselected/spectator, scout, or civilian
					tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount);	// Requires 1.8.3-dev-hg185 or newer
				}
#endif
#if defined CSTRIKE
				emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
#endif
#if defined DOD
				emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);	// "items/weaponpickup.wav" could work too, I suppose
#endif
#if defined TFC
				// Shotgun pumping sound for picking up grenades... That's how TFC does it!
				emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);	
#endif
			}
		}

		switch(iTargetTeam) {
			case 0: {
				sTeamName = "everyone";
			}
			case TEAM_ONE: {
#if defined CSTRIKE
				sTeamName = "all terrorists";
#endif
#if defined DOD
				sTeamName = "all allies";	// TODO - Allies or British
#endif
#if defined TFC
				sTeamName = "all blue";		// I *could* pull the team1_name value from the info_tfdetect entity (but only for some maps?)
#endif
			}
			case TEAM_TWO: {
#if defined CSTRIKE
				sTeamName = "all ct's";
#endif
#if defined DOD
				sTeamName = "all axis";
#endif
#if defined TFC
				sTeamName = "all red";
			}
			case TEAM_THREE: {
				sTeamName = "all yellow";
			}
			case TEAM_FOUR: {
				sTeamName = "all green";
#endif
			}
		}
		client_print(0, print_chat, "ADMIN %s has given %s %d Molotov cocktails!", sAdmin, sTeamName, iGiveAmount);

	} else {

		iTarget = cmd_target(id, sArg1, 6);

		if (!is_user_connected(iTarget) || !is_user_alive(iTarget)) {
			return PLUGIN_HANDLED;
		}

		g_NumMolotov[ID_TO_INDEX(iTarget)] = iGiveAmount;

#if defined CSTRIKE
		fm_give_item(iTarget, WEAPON_HEGRENADE);
		cs_set_user_bpammo(iTarget, CSW_HEGRENADE, iGiveAmount);
#endif
#if defined DOD
		switch(get_user_team(iTarget)) {
			case ALLIES: {	// (or British)
				fm_give_item(iTarget, WEAPON_HANDGRENADE);
				dod_set_user_ammo(iTarget, DODW_HANDGRENADE, iGiveAmount);
			}
			case AXIS: {
				fm_give_item(iTarget, WEAPON_STICKGRENADE);
				dod_set_user_ammo(iTarget, DODW_STICKGRENADE, iGiveAmount);
			}
		}
#endif
#if defined TFC
		new iClass = pev(iTarget, pev_playerclass);
		if ((iClass > 0) && (iClass != TFC_PC_SCOUT) && (iClass != MC_TFC_PC_CIVILIAN)) {	// No unselected/spectator, scout, or civilian
			tfc_setbammo(iTarget, TFC_AMMO_NADE1, iGiveAmount);				// Requires 1.8.3-dev-hg185 or newer
		}
#endif
#if defined CSTRIKE
				emit_sound(iTarget, CHAN_WEAPON, "items/gunpickup2.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
#endif
#if defined DOD
				emit_sound(iTarget, CHAN_WEAPON, "items/ammopickup.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);	// "items/weaponpickup.wav" could work too, I suppose
#endif
#if defined TFC
				// Shotgun pumping sound for picking up grenades... That's how TFC does it!
				emit_sound(iTarget, CHAN_WEAPON, "weapons/scock1.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);	
#endif

		client_print(iTarget, print_chat, "ADMIN %s has given you %d Molotov cocktails!", sAdmin, iGiveAmount);

	}
	return PLUGIN_HANDLED;
}

// Handle the /molotov command and molotov menu
#if defined CSTRIKE
public buy_molotov(id) 
{
	if((is_user_alive(id)) && (is_user_alive(id)) && (cs_get_user_team(id) == CS_TEAM_T))
	{
		if (!get_pcvar_num(pEnabled)) 
		{
			return PLUGIN_HANDLED;
		}

		//if (get_pcvar_num(pOverride)) {
		//	client_print(id, print_center, "Just buy a HE grenade and get Molotov automatically!");
		//	return PLUGIN_HANDLED;
		//}

		if (!is_user_alive(id)) 
		{
			client_print(id, print_center, "You can't buy Molotov cocktails because you are dead.");
			return PLUGIN_HANDLED;
		}

		if (!cs_get_user_buyzone(id) && get_pcvar_num(pBuyZone)) 
		{
			client_print(id, print_center, "You are not in a buyzone.");
			return PLUGIN_HANDLED;
		}

		new iMoney = cs_get_user_money(id);

		if (iMoney < get_pcvar_num(pPrice)) 
		{
			client_print(id, print_center, "You don't have enough $ to buy a Molotov cocktail.");
			return PLUGIN_HANDLED;
		}

		if (!g_NumMolotov[ID_TO_INDEX(id)] && user_has_weapon(id, CSW_HEGRENADE)) 
		{
			if (get_pcvar_num(pOverride)) 
			{
				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
			}
			else 
			{
				client_print(id, print_center, "You already have an HE Grenade.");
				return PLUGIN_HANDLED;
			}
		}

		if (g_NumMolotov[ID_TO_INDEX(id)] == get_pcvar_num(pMaxMolotovs)) 
		{
			if (g_NumMolotov[ID_TO_INDEX(id)] == 1) 
			{
				client_print(id, print_center, "You already have a Molotov cocktail.");
			} 
			else
			{
				client_print(id, print_center, "You already have %d Molotov cocktails.", g_NumMolotov[ID_TO_INDEX(id)]);
			}
			return PLUGIN_HANDLED;
		}

		cs_set_user_money(id, iMoney - get_pcvar_num(pPrice));
		fm_give_item(id, WEAPON_HEGRENADE);
		cs_set_user_bpammo(id, CSW_HEGRENADE, ++g_NumMolotov[ID_TO_INDEX(id)]);


		client_print(id, print_chat, "You got a Molotov cocktail!");
		return PLUGIN_HANDLED;
	}
	else 
	{
		client_print(id, print_chat, "You have no option for this item!");
	}
	return PLUGIN_HANDLED;
}
#endif

// Just before the grenade is thrown, change the model
#if defined CSTRIKE || defined DOD
public grenade_throw(id, ent, wid) {
#if defined CSTRIKE
	if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || wid != CSW_HEGRENADE) {
#else
	#if defined DOD
	// current weapon can be DODW_MILLS_BOMB in this forward, but not in CurWeapon
	if (!get_pcvar_num(pEnabled) || !is_user_connected(id) || ((wid != DODW_HANDGRENADE) && (wid != DODW_STICKGRENADE) && (wid != DODW_MILLS_BOMB))) {
	#endif
#endif
		return PLUGIN_CONTINUE;
	}

	if (!g_NumMolotov[ID_TO_INDEX(id)] && !get_pcvar_num(pOverride)) {	// If no Molotovs and override is disabled, return
		return PLUGIN_CONTINUE;
	}

	if (g_NumMolotov[ID_TO_INDEX(id)] > 0) {	// Prevent negative values
		g_NumMolotov[ID_TO_INDEX(id)]--;
	}

	engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
	set_pev(ent, pev_nextthink, 99999.0);

	custom_weapon_shot(g_wpnMolotov, id);

#if defined CSTRIKE	// dod sets the team, cstrike doesn't, TFC sets this in fw_setmodel_post()
	set_pev(ent, pev_team, get_user_team(id));
#endif

#if defined DOD
	//set_pev(id, pev_weaponanim, 0);		// 0:"idle"; 1:"pullpin"; 2:"throw"; 3:"deploy"
#endif

	return PLUGIN_HANDLED;
}
#endif

// Set up the explosion, sound, damage, etc.
molotov_explode(ent) {

	new param[7], iOrigin[3];
	new Float:fOrigin[3];
	new iOwner = pev(ent, pev_owner);
	// The broken bottle may continue to travel, but the fire will be centered around the explosion site, marked by this temporary info_target entity.
	new ent2 = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"));

	pev(ent, pev_origin, fOrigin);

#if defined MOLOTOV_DEBUG
	log_amx("[MC] molotov_explode ent(%d) owner(%d) ent2(%d) -----", ent, iOwner, ent2);
#endif

	param[0] = ent;
	param[1] = ent2;
	param[2] = iOwner;
	param[3] = pev(ent, pev_team);
	param[4] = iOrigin[0] = floatround(fOrigin[0]);
	param[5] = iOrigin[1] = floatround(fOrigin[1]);
	param[6] = iOrigin[2] = floatround(fOrigin[2]);

	engfunc(EngFunc_SetModel, ent, "models/molotov/w_broke_molotov.mdl");

	random_fire(iOrigin, ent2);
	radius_damage2(iOwner, param[3], fOrigin, get_pcvar_float(pMlDamage), get_pcvar_float(pMlRadius), DMG_BLAST, true);

	// If the round ends because of damage inflicted by the initial blast (in the previous line of code), skip any further Molotov effects.
	// g_bReset may already be set, so it is safe to check it at this point.
	if (g_bReset == true) {
		set_pev(ent, pev_flags, pev(ent, pev_flags) | FL_KILLME);	// Remove the Molotov and later cancel the explosion
		return PLUGIN_HANDLED;
	}

	new Float:FireTime = get_pcvar_float(pFireTime);

	if (++g_iMolotovOffset[ID_TO_INDEX(iOwner)] == MOLOTOV_HARD_LIMIT) {
		g_iMolotovOffset[ID_TO_INDEX(iOwner)] = 0;
	}

	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));
	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);
	// This task removes the broken Molotov and "info_target" entity once molotov_firetime has expired
	set_task(FireTime, "fire_stop", MOLOTOV_TASKID_BASE3 + (MOLOTOV_TASKID_OFFSET * (iOwner - 1)) + g_iMolotovOffset[ID_TO_INDEX(iOwner)], param, 7);

	return PLUGIN_CONTINUE;
}

// 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
#if defined TFC
public cancel_reset() {
	g_bReset = false;
}
#endif

// Make fire sounds
public fire_sound(param[]) {
	emit_sound(param[1], CHAN_AUTO, "molotov/molotov_fire.wav", VOL_NORM, ATTN_NORM, 0, PITCH_NORM);
}

// Remove Molotov entities
public fire_stop(param[]) {
	if (pev_valid(param[0])) { set_pev(param[0], pev_flags, pev(param[0], pev_flags) | FL_KILLME); }	// Molotov entity
	if (pev_valid(param[1])) { set_pev(param[1], pev_flags, pev(param[1], pev_flags) | FL_KILLME); }	// info_target entity
}

// Call visual effect and damage functions
public fire_damage(param[]) {

	new iOrigin[3], Float:fOrigin[3];
	iOrigin[0] = param[4];
	iOrigin[1] = param[5];
	iOrigin[2] = param[6];

	random_fire(iOrigin, param[1]);	// Visual effect

	IVecFVec(iOrigin, fOrigin);
	radius_damage2(param[2], param[3], fOrigin, get_pcvar_float(pFireDmg), get_pcvar_float(pMlRadius), DMG_BURN, false);	// Actual damage
}

// There is a radius_damage() in engine, so this was renamed.
stock radius_damage2(iAttacker, iAttackerTeam, Float:fOrigin[3], Float:fDamage, Float:fRange, iDmgType, bool:bCalc = true) {

	new Float:pOrigin[3], Float:fDist, Float:fTmpDmg;
	new i, iFF = get_pcvar_num(pMFF);

	if (iFF == -1) {			// Obey mp_friendlyfire
		iFF = get_pcvar_num(pFriendlyFire);
#if defined DOD || defined TFC
	} else if (iFF == -2) {		// Obey mp_teamplay (bit 5)
		new iTeamPlay = get_pcvar_num(pTeamPlay);
		if (iTeamPlay & (1 << 4)) {	// bit 5 (16 = teammates take no damage from explosive weaponfire)
			iFF = 0;
		}
#endif
	}	// else, leave it at 0 or 1

	while (i++ < g_MaxPlayers) {
		if (!is_user_alive(i)) {
			continue;
		}

#if defined TFC
		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
#else
		if ((iFF == 0) && (iAttackerTeam == get_user_team(i))) {
#endif
			continue;
		}

		pev(i, pev_origin, pOrigin);
		fDist = get_distance_f(fOrigin, pOrigin);

		if (fDist > fRange) {
			continue;
		}

		if (bCalc) {
			fTmpDmg = fDamage - (fDamage / fRange) * fDist;
		} else {
			fTmpDmg = fDamage;
		}

		if (floatround(fTmpDmg) > 0) {	// This eliminated the "[CSX] Invalid damage 0" error
			custom_weapon_dmg(g_wpnMolotov, iAttacker, i, floatround(fTmpDmg), 0);
		}

		if (pev(i, pev_health) <= fTmpDmg) {
			kill(iAttacker, i, iAttackerTeam);
		} else {
			fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
		}
	}
	
	// At this point, i is one higher than the highest possible player ID, so this loop only affects non-player entities
	while ((i = engfunc(EngFunc_FindEntityInSphere, i, fOrigin, fRange))) {	// Extra parentheses fix warning 211: possibly unintended assignment
		if (pev(i, pev_takedamage)) {
			if (bCalc) {
				pev(i, pev_origin, pOrigin);
				fTmpDmg = fDamage - (fDamage / fRange) * get_distance_f(fOrigin, pOrigin);
			} else {
				fTmpDmg = fDamage;
			}
			fm_fakedamage(i, "molotov", fTmpDmg, iDmgType);
		}
	}
}

// This stock only creates the visual effect. It does not handle any damage.
// I tried using TE_FIREFIELD, but I can't make it look good in dod.
stock random_fire(Origin[3], ent) {

	static iRange, iOrigin[3], g_g, i;

	iRange = get_pcvar_num(pMlRadius);

	for (i = 1; i <= 5; i++) {

		g_g = 1;

		iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
		iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
		iOrigin[2] = Origin[2];
		iOrigin[2] = ground_z(iOrigin, ent);

		while (get_distance(iOrigin, Origin) > iRange) {		// If iOrigin is too far away, recalculate its position

			iOrigin[0] = Origin[0] + random_num(-iRange, iRange);
			iOrigin[1] = Origin[1] + random_num(-iRange, iRange);
			iOrigin[2] = Origin[2];

			if (++g_g >= ANTI_LAGG) {
				iOrigin[2] = ground_z(iOrigin, ent, 1);
			} else {
				iOrigin[2] = ground_z(iOrigin, ent);
			}
		}

		new rand = random_num(5, 15);

		message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
		write_byte(TE_SPRITE);
		write_coord(iOrigin[0]);	// Position
		write_coord(iOrigin[1]);
		write_coord(iOrigin[2] + rand * 5);
		write_short(g_iFireSprite);	// Sprite index
		write_byte(rand);		// Scale
		write_byte(100);		// Brightness
		message_end();
	}

	// One smoke puff for each call to random_fire, regardless of number of flames
	message_begin(MSG_BROADCAST, SVC_TEMPENTITY);
	write_byte(TE_SMOKE);
	write_coord(iOrigin[0]);			// Position
	write_coord(iOrigin[1]);
	write_coord(iOrigin[2] + 120);
	write_short(g_iSmokeSprite[random_num(0, 1)]);	// Sprite index
	write_byte(random_num(10, 30));			// Scale
	write_byte(random_num(10, 20));			// Framerate
	message_end();

}

// Stop all visual effect/physical damage tasks
stock reset_tasks() {
#if defined MOLOTOV_DEBUG
	new tmpdbgid;
#endif
	for (new i; i < g_MaxPlayers; i++) {	// for 0..31
		for (new o; o < MOLOTOV_TASKID_OFFSET; o++) {
			if (task_exists(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
				remove_task(MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o);
#if defined MOLOTOV_DEBUG
				tmpdbgid = MOLOTOV_TASKID_BASE1 + (MOLOTOV_TASKID_OFFSET * i) + o;
				log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
#endif
			}

			if (task_exists(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o)) {
				remove_task(MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o);
#if defined MOLOTOV_DEBUG
				tmpdbgid = MOLOTOV_TASKID_BASE2 + (MOLOTOV_TASKID_OFFSET * i) + o;
				log_amx("[MC] %d exists. ----------==========----------", tmpdbgid);
#endif
			}
			// The third task for each Molotov is not stopped so it can remove the Molotov/info_target entities.
		}
	}
}

// This function handles the killing and scoring.
//   iKillerTeam is stored because the killer can disconnect before the Molotov kills someone, leading to inaccurate scoring
stock kill(iKiller, iVictim, iKillerTeam) {

//TFC:	DeathMsg, ScoreInfo, ScoreInfo		// One ScoreInfo for killer and one for victim (order varies)
//DOD:	DeathMsg, ScoreShort, Frags		// ScoreShort=victim, Frags=killer
// CS:	DeathMsg, Money, ScoreInfo, ScoreInfo

//	Scoreboard
// CSTRIKE:	Score Deaths
// DMC:		Frags Deaths
// DOD:		Score Kills Deaths
// HL:		Score Deaths
// HLOF:	Kills Deaths
// TFC:		Score Deaths
// Ricochet:	Points
/*				----- Attacker -----		------ Victim ------
				Score	Deaths	Kills		Score	Death	Kills
	CS  kill		+1	-	N/A		-	+1	N/A
	CS  team kill		-1	-	N/A		-	+1	N/A
	CS  suicide		-1	+1	N/A		---------------------
	CS  detonate/defuse	+3	-	N/A		---------------------
	DOD kill		-	-	+1		-	+1	-
	DOD team kill		-	-	-		-	+1	-	(mp_tkpenalty handles punishment)
	DOD suicide		-	+1	-		---------------------
	DOD cap			+1	-	-		---------------------
	TFC kill		+1	-	N/A		-	+1	N/A
	TFC team kill		-1	-	N/A		-	+1	N/A
	TFC suicide		-1	+1	N/A		---------------------
	TFC cap/control		varies	-	N/A		---------------------
*/


// ------------------------------------------------------------------------------------------------- DeathMsg (CS, DOD, TFC)
	// DeathMsg - Triggers HUD message and player console message
	// DOD and CSTRIKE have different formats for DeathMsg; most other mods should be default
	message_begin(MSG_ALL, g_msgDeathMsg, {0,0,0}, 0);
	write_byte(iKiller);		// Killer ID
	write_byte(iVictim);		// Victim ID
#if defined CSTRIKE
	write_byte(0);			// Is Headshot?
#endif	
#if defined DOD
	write_byte(0);			// Weapon ID - These don't match the DODW_* constants, and a custom weapon ID does not work, so we use "world"
#else
	write_string("molotov");	// Truncated Weapon Name
#endif
	message_end();
// ------------------------------------------------------------------------------------------------- /DeathMsg (CS, DOD, TFC)

	// This block of code actually kills the user (silently - DeathMsg was already created)
	new iVictimTeam = get_user_team(iVictim);
	new iMsgBlock = get_msg_block(g_msgDeathMsg);	// Store original block value
	set_msg_block(g_msgDeathMsg, BLOCK_ONCE);	// Start blocking DeathMsg

#if defined CSTRIKE

	new iKillerFrags = get_user_frags(iKiller);
	new iVictimFrags = get_user_frags(iVictim);

	// TFC and CS scoring are mostly the same. See TFC comment. (I did most of my testing with TFC first)
	if (iKiller != iVictim) {
		fm_set_user_frags(iVictim, iVictimFrags + 1);			// Add frag that user_kill() will remove
	}

	if (iKillerTeam != iVictimTeam) {
		iKillerFrags++;							// Killer's Score = Score + 1
	} else {
		iKillerFrags--;							// Killer's Score = Score - 1
	}
	fm_set_user_frags(iKiller, iKillerFrags);

	//	CSTRIKE Results	--------------------------------------------------------------------------------------------------
						//	DeathMsg	ScoreInfoMsg	KScore	KDeath	VScore	VDeath	Internally
	//user_kill(iVictim, 0);		//	Yes		Yes (Victim)	0	0	-1	+1	Everything matches
	//user_kill(iVictim, 1);		//	Yes		Yes (Victim)	0	0	-1	+1	VScore is different internally (unchanged)
	//user_silentkill(iVictim);		// 	No		Yes (Victim)	0	0	-1	+1	VScore is different internally (unchanged)

	//dllfunc(DLLFunc_ClientKill, iVictim);	//	Yes		Yes (Victim)	0	0	-1	+1	Everything matches
	//fm_user_kill(iVictim, 0);		//	Yes		Yes (Victim)	0	0	-1	+1	Everything matches
	//fm_user_kill(iVictim, 1);		//	Yes		Yes (Victim)	0	0	 0	+1	Everything matches (fm_user_kill adds 1 to VScore)

	// user_silentkill() blocks the DeathMsg, but it doesn't update the scoreboard properly
	// fm_user_kill() updates the scoreboard properly, but generates a DeathMsg with Victim=Killer, so we have to block that.
#endif

#if defined TFC
	new iVictimFrags = tfc_get_user_frags(iVictim);

	// TFC treats every type of kill in code as a suicide and takes away 1 frag from the victim.
	//   After *extensive* testing, I found that the easiest solution is to increment the victim frags
	//   (stored in pdata) beforehand and let the game decrement it and send out ScoreInfo messages.
	//   This applies to *victims* of team kills as well as normal kills. Suicides still lose a frag.
	if (iKiller != iVictim) {
		tfc_set_user_frags(iVictim, iVictimFrags + 1);
	}

	if ((iKillerTeam == iVictimTeam) || tfc_is_team_ally(iKillerTeam, iVictimTeam)) {	// TODO: tfc_is_team_ally() is broken in AMX Mod X
		tfc_set_user_frags(iKiller, get_user_frags(iKiller) - 1);	// Killer's Score = Score - 1
	} else {
		tfc_set_user_frags(iKiller, get_user_frags(iKiller) + 1);	// Killer's Score = Score + 1
	}

	//	TFC Results	--------------------------------------------------------------------------
						//	DeathMsg	ScoreInfoMsg	VFrag	Internally
	//user_kill(iVictim, 0);		//	Yes		Yes		-1	Everything matches
	//user_kill(iVictim, 1);		//	Yes		Yes		 0	VFrags doesn't match
	//user_silentkill(iVictim);		// 	No		Yes		 0	VFrags doesn't match

	//dllfunc(DLLFunc_ClientKill, iVictim);	//	Yes		Yes		-1	Everything matches
	//fm_user_kill(iVictim, 0);		//	Yes		Yes		-1	Everything matches
	//fm_user_kill(iVictim, 1);		//	Yes		Yes		-1	Everything matches

#endif

	// DOD just works..
	//	DOD Results	--------------------------------------------------------------------------------------------------
						//	DeathMsg	ScoreShortMsg	FragMsg	KFrag	VDeath	VFrag	Internally
	//user_kill(iVictim, 0);		//	Yes		Yes		N	0	+1	 0	Everything matches
	//user_kill(iVictim, 1);		//	Yes		Yes		N	0	+1	 0	Everything matches
	//user_silentkill(iVictim);		// 	No		Yes		N	0	+1	 0	Everything matches

	//dllfunc(DLLFunc_ClientKill, iVictim);	//	Yes		Yes		N	0	+1	 0	Everything matches
	//fm_user_kill(iVictim, 0);		//	Yes		Yes		N	0	+1	 0	Everything matches
	//fm_user_kill(iVictim, 1);		//	Yes		Yes		N	0	+1	+1	OK, but VFrags shouldn't be changed

	user_kill(iVictim, 0);
	set_msg_block(g_msgDeathMsg, iMsgBlock);		// Stop blocking DeathMsg

	//CSTRIKE client console messages:
	//Kill	"Player1 killed [P0D]M0rbid Desire (2) with molotov"
	//TK	"Player1 killed [POD]Kate_Winslet (2) with molotov"
	//Self	"Player1 killed self with molotov"

	//DOD client console messages: (DOD uses indexes instead of strings and a custom weapon name can't be sent to the player console)
	//Kill	"Player1 killed Sgt.Moving_Target with world"
	//TK	"Player1 killed his teammate Sgt.dontSHOOTiJUSTwannaTALK with world"
	//Self	"Player1 killed self"

	//TFC client console messages:
	//Kill	"Player1 killed [FoX]JesseJames with molotov"
	//TK	"Player1 killed [FoX]Barry with molotov"
	//Self	"Player1 killed self with molotov"


	// 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
	new sVictim[32], sVictimAuth[35], sVictimTeam[32];
	get_user_name(iVictim, sVictim, charsmax(sVictim));
	get_user_authid(iVictim, sVictimAuth, charsmax(sVictimAuth));
	get_user_team(iVictim, sVictimTeam, charsmax(sVictimTeam));	// TERRORIST, CT, Allies, Axis, #Dustbowl_team1 (Attackers/Blue), #Dustbowl_team2 (Defenders/Red)
	if (iKiller == iVictim) {
		log_message("^"%s<%d><%s><%s>^" committed suicide with ^"molotov^"", sVictim, get_user_userid(iVictim), sVictimAuth, sVictimTeam);
	} else if (is_user_connected(iKiller)) {
		new sKiller[32], sKillerAuth[35], sKillerTeam[32];
		get_user_name(iKiller, sKiller, charsmax(sKiller));
		get_user_authid(iKiller, sKillerAuth, charsmax(sKillerAuth));
		get_user_team(iKiller, sKillerTeam, charsmax(sKillerTeam));
		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);
	}
	// TODO: There currently isn't a log message for a kill by a disconnected player. The wiki doesn't show the expected format.

// ------------------------------------------------------------------------------------------------- Money (CS)
#if defined CSTRIKE
	new iMoney;
	if (iKillerTeam == iVictimTeam) {
		iMoney = cs_get_user_money(iKiller) - 3300;			// TODO - $1500 hostage kill penalty
		cs_set_user_money(iKiller, iMoney < 0 ? 0 : iMoney);
	} else {
		iMoney = cs_get_user_money(iKiller) + 300;
		cs_set_user_money(iKiller, iMoney > 16000 ? 16000 : iMoney);
	}
#endif
// ------------------------------------------------------------------------------------------------- /Money (CS)
// ------------------------------------------------------------------------------------------------- ScoreInfo (CS and TFC)
	// ScoreInfo - Updates scoreboard on clients (actual values are changed elsewhere)
	// TFC - ScoreInfo messages are sent automatically after killing a player.
	// CS - ScoreInfo is sent automatically for the victim, but not killer.
#if defined CSTRIKE
	message_begin(MSG_ALL, g_msgScoreInfo);		// Killer ScoreInfo
	write_byte(iKiller);
	write_short(iKillerFrags);
	write_short(get_user_deaths(iKiller));
	write_short(0);
	write_short(iKillerTeam);
	message_end();
#endif
// ------------------------------------------------------------------------------------------------- /ScoreInfo (CS and TFC)

#if defined DOD		
// ------------------------------------------------------------------------------------------------- ScoreShort (DOD)
	// ScoreShort is sent by user_kill()
// ------------------------------------------------------------------------------------------------- /ScoreShort (DOD)
// ------------------------------------------------------------------------------------------------- Frags (DOD)
	if (iKillerTeam != iVictimTeam) {	// Only give a frag if the player killed was an enemy (not suicide or TK)
		dod_set_user_kills(iKiller, dod_get_user_kills(iKiller) + 1, 1);	// These natives seem to work properly.
	}
// ------------------------------------------------------------------------------------------------- /Frags (DOD)
#endif
}

// Attempt to drop the passed coordinates to ground level
stock ground_z(iOrigin[3], ent, skip = 0, iRecursion = 0) {

	iOrigin[2] += random_num(5, 80);

	if (!pev_valid(ent)) {
		return iOrigin[2];
	}

	new Float:fOrigin[3];
	IVecFVec(iOrigin, fOrigin);
	set_pev(ent, pev_origin, fOrigin);
	engfunc(EngFunc_DropToFloor, ent);

	if (!skip && !engfunc(EngFunc_EntIsOnFloor, ent)) {
		if (iRecursion >= ANTI_LAGG) {
			skip = 1;
		}

		return ground_z(iOrigin, ent, skip, ++iRecursion);
	}

	pev(ent, pev_origin, fOrigin);

	return floatround(fOrigin[2]);
}

// If plugin or override is disabled, reset Molotov models to original models
#if defined CSTRIKE || defined DOD
stock reset_molotovs() {
	new ent = g_MaxPlayers;
#if defined CSTRIKE
	// TODO - My limited testing showed this code is pointless.
	//   It has no negative effect, so I'm leaving it for the 3.30 release.
	//   (I don't think "model" is a valid parameter.)
	new iOwner;
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
		iOwner = pev(ent, pev_owner);
#if defined MOLOTOV_DEBUG
		client_print(0, print_chat, "reset_molotovs - found one Molotov! Owner(%d)", iOwner);
#endif
		// If plugin is disabled or player owns no molotovs, reset their model
		if (!get_pcvar_num(pEnabled) || !g_NumMolotov[ID_TO_INDEX(iOwner)]) {
			engfunc(EngFunc_SetModel, ent, "models/w_hegrenade.mdl");
		}
	}
#endif
#if defined DOD
	new iOwner;
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/molotov/w_molotov.mdl"))) {
#if defined MOLOTOV_DEBUG
		client_print(0, print_chat, "reset_molotovs - found one Molotov!");
#endif

		iOwner = pev(ent, pev_owner);
		if (iOwner) {
			switch(get_user_team(iOwner)) {
				case ALLIES: {	// (or British)
					engfunc(EngFunc_SetModel, ent, "models/w_grenade.mdl");		// Mills is the same model, so this is probably fine
				}
				case AXIS: {
					engfunc(EngFunc_SetModel, ent, "models/w_stick.mdl");
				}
			}
		}
	}
#endif

}
#endif

// Mods that show the model before it is thrown need the model set (I think)
#if defined CSTRIKE || defined DOD
stock set_molotovs() {
	new ent = g_MaxPlayers;
#if defined CSTRIKE
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_hegrenade.mdl"))) {
#if defined MOLOTOV_DEBUG
		client_print(0, print_chat, "set_molotovs - found one hegrenade!");
#endif
		engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
	}
#endif
#if defined DOD
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_grenade.mdl"))) {
#if defined MOLOTOV_DEBUG
		client_print(0, print_chat, "set_molotovs - found one grenade!");
#endif
		engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
	}
	ent = g_MaxPlayers;
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_stick.mdl"))) {
#if defined MOLOTOV_DEBUG
		client_print(0, print_chat, "set_molotovs - found one stick!");
#endif
		engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
	}
	ent = g_MaxPlayers;
	while ((ent = engfunc(EngFunc_FindEntityByString, ent, "model", "models/w_mills.mdl"))) {
#if defined MOLOTOV_DEBUG
		client_print(0, print_chat, "set_molotovs - found one Mills!");
#endif
		engfunc(EngFunc_SetModel, ent, "models/molotov/w_molotov.mdl");
	}
#endif

}
#endif

// Show optional buy menu
#if defined CSTRIKE
public show_molotov_menu(id) {
	new menu[128];
	formatex(menu, charsmax(menu), "Buy Molotov Cocktail ($%d)?^n^n1. Yes^n2. No^n^n0. Exit", get_pcvar_num(pPrice));

	// This shows the menu for 30 seconds, I tried first with get_cvar_num("mp_buytime")*60 , but it didn't work well
	// when using 0.5 as mp_buytime. If you want to, just change the time below.
	show_menu(id, MOLOTOV_MENU_KEYS, menu, 30);

	return PLUGIN_HANDLED;
}
#endif

// Our menu function will get the player id and the key they pressed
#if defined CSTRIKE
public giveMolotov(id, key) {

	//key will start at zero
	switch(key) {
		case 0: buy_molotov(id);
		//I don't think these messages are necessary.
		//case 1: client_print(id, print_center, "You have chosen not to buy a Molotov cocktail");
		//default: client_print(id, print_center, "You have chosen to exit the Molotov menu");
	}
}
#endif

// Set user frags (score) in TFC
#if defined TFC
stock tfc_set_user_frags(iIndex, iNewFrags) {
	if (is_linux_server()) {
		set_pdata_int(iIndex, 76, iNewFrags);		// real_frags = 76 (on Linux)		Required!
	} else {
		set_pdata_int(iIndex, 77, iNewFrags);		// real_frags = 77 (on Windows)		Required?
	}	// Is there a mac version?

	// As far as I can tell, real_frags is what should be set, and something copies it to m_iClientFrags.
	//set_pdata_int(iIndex, 643, iNewFrags);		// m_iClientFrags = 643 (on Linux/Windows)

	// Sometimes this is required, sometimes it isn't. I think what is happening is that in
	//   the cases it doesn't seem to be required, pev_frags is getting updated internally.
	set_pev(iIndex, pev_frags, float(iNewFrags));

#if defined MOLOTOV_DEBUG
	mydump(iIndex, 65, 85);
	mydump(iIndex, 635, 655);
#endif
}
#endif

// Return a user's frags, and verify that the TFC offsets haven't changed
#if defined TFC
stock tfc_get_user_frags(iIndex) {
	new iOffset = (is_linux_server() ? 76 : 77);	// real_frags = 76 (Linux), 77 (Windows)
	new iFrags = get_user_frags(iIndex);

	// This code is the easiest way to detect a change in the offsets and help prevent annoying troubleshooting.
	// get_user_frags seems to always return the correct value.
	if (iFrags != get_pdata_int(iIndex, iOffset)) {
		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));
		console_print(0, "WARNING! get_user_frags != real_frags! Contact plugin author!!!!!!!!!!!!!!!!!!!!");
	}

	return get_pdata_int(iIndex, iOffset);
}
#endif

// This won't be compiled unless MOLOTOV_DEBUG is set (and only for TFC)
#if defined TFC
stock mydump(iIndex, iStart, iEnd) {

	new sLine[512];
	new FILE[] = "addons/amxmodx/logs/pdatadump.log";

	new sClassname[64];
	entity_get_string(iIndex, EV_SZ_classname, sClassname, charsmax(sClassname));
	format(sLine, charsmax(sLine), "Starting dump of entity %s %d", sClassname, iIndex);
	console_print(1, "%s to file %s", sLine, FILE);
	if (!write_file(FILE, sLine)) {
		console_print(1, "Error dumping to %s!", FILE);
		return PLUGIN_HANDLED;
	}

	for (new i = iStart; i <= iEnd; i++) {
		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));
		if (!write_file(FILE, sLine)) {
			console_print(1, "Error dumping to %s!", FILE);
			return PLUGIN_HANDLED;
		}
	}

	console_print(1, "Dump done. Check %s!", FILE);

	return 1;
}
#endif
LaMFo is offline