/* AMX Mod X script.
*
*   Ultimate Gore Enhancement (amx_gore_ultimate.sma)
*   Copyright (C) 2003-2004  mike_cao / fizzarum / jtp10181
*
*   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 (at your option) any later version.
*
*   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 may extend this exception
*   to your version of the file, but you are not obligated to do so. If
*   you do not wish to do so, delete this exception statement from your
*   version.
*
****************************************************************************
*
*   Version 1.5 - 06/27/2006
*
*   by jtp10181 <jtp@jtpage.net>
*   Homepage: http://www.jtpage.net
*
*   Original code by:
*     by mike_cao <mike@mikecao.com> (plugin_gore)
*     fizzarum <tntmr2gg2@icqmail.com> (plugin_gore2)
*
****************************************************************************
*
*   This plugin adds gore effects. It is configured
*   with the cvar "amx_gore" using these flags:
*
*   a - Headshot blood
*   b - Extra blood effects
*   c - Bleeding on low health
*   d - Gib explosion (Explosives and damage over "amx_gore_exphp")
*   e - Extra Gory Mode
*   f - Hostage Gore (CS/CZ Only)
*
*   Default is: amx_gore "abcd"
*
*   New CVAR: amx_gore_exphp (default 160)
*         The amount of health that must be lost upon death
*         for the player to "explode"
*
*   Add the cvars to your amxx.cfg to change it and have it load every map
*
*   *NOTE*: the decal indexes are pulled form the servers decals.wad  If you
*	do not have the orginal decals.wad (for your mod) on the server the
*	decals may not appear as blood, but arrows, numbers, text, etc.
*
*   v1.5 - JTP10181 - 06/27/06
*	- Added support for ESF
*	- Added support for TS
*	- Added support for TFC
*	- Tweaked a lot of numbers
*	- Made gibs fly around more instead of in a big heap
*	- Added support for hostages in CS
*	- Switched all supporting mods to client_damage/death forwards
*
*   v1.4 - JTP10181 - 06/16/06
*	- Switched to Pointer CVAR commands
*	- Updated to work on "valve" mod by request
*	- Finally finished support for DoD
*	- Reduced the insane ammount of blood spray with gibs & extra gore enabled
*
*   v1.3.5 - JTP10181 - 03/05/06
*	- Fixed possible runtime errors if player disconnects during events
*
*   v1.3.4 - JTP10181 - 10/25/05
*	- Added knife to the gib_wpncheck check
*	- Fixed bug where if all damage was from falling the player would not bleed
*
*   v1.3.3 - JTP10181 - 09/25/04
*	- Made it really easy to change the weapons that cause explosion
*	- Minor code tweaks
*
*   v1.3.2 - JTP10181 - 09/24/04
*	- Fixed code to work on AMXModX 0.20
*	- Added new CVAR to adjust the HP loss that triggers a GIB explosion
*	- Used task for body hiding so items wont end up underground
*
*   v1.3.1 - JTP10181 - 06/02/04
*	- Fixed runtime error if victim is null on a damage or death event
*		Was happening in conjunction with superhero mod
*		Thanks to drummeridiot25 for testing it for me
*
*   v1.3 - JTP
*	- Automatic mod detection, no more recompiling for CZERO.
*	- Decal indexes verified for CZ, they work perfectly.
*	- Started working on DoD support
*
*   v1.2 - JTP
*	- Combined various gore plugins into one that has the best features
*		out of all of them.
*	- Plan to maintain this plugin if any issues/requests arrise.
*	- Added extra gory mode:
*		Classic headshot death with the sprays shooting up (from orginal plugin_gore)
*		More blood spraying on a gib explosion (from orginal plugin_gore)
*		Extra blood decals on damage and deaths
*	- Fixed divide by zero error in fx_blood and fx_gib_explode
*	- Minor tweaks here and there to some of the numbers
*	- Put in fix for CZERO decals from "euro" and "out" from AMX forums
*	- Fixed runtime error when the attacker was not able to be determinted.
*		get_user_origin was getting passed a "0" player index.
*
*
*   v1.03 - ( by fizzarum ) :
*	- Each hit now causes a blood stream depending on the positions of the
*		agressor and the victim.
*	- Reduce the previous headshot fx to a less extravagant thing
*	- The gib explosion now happens after a damage higher than 110 EVEN IF
*		the victim's head was hit
*	- A knife kill does not cause a gib explosion
*	- Minor changes on the bleeding effect, the position of the gibs
*
*   Thanks:
*	- mike_cao for the orginal plugin
*	- fizzarum on plugin_gore2.sma (for AMX)
*	- euro and out (AMX forums) for posting decal numbers for CZero
*	- SidLuke (AMX forums) for his version for DoD,
*		I grabbed some of that code for my DoD support
*
**************************************************************************/

#include <amxmodx>
#include <amxmisc>
#include <fun>
#include <engine>
#include <dodconst>
#include <tfcconst>
#include <tsconst>

#define MAX_PLAYERS 32
#define MAX_HOSTAGES 16

#define GORE_HEADSHOT       (1<<0) // "a"
#define GORE_BLOOD          (1<<1) // "b"
#define GORE_BLEEDING       (1<<2) // "c"
#define GORE_GIB            (1<<3) // "d"
#define GORE_EXTRA          (1<<4) // "e"
#define GORE_HOSTAGES       (1<<5) // "f"

#define TE_BLOODSPRITE        115
#define TE_BLOODSTREAM        101
#define TE_MODEL              106
#define TE_WORLDDECAL         116

//Valve Mod Weapon Constants
#define HLI_HEALTHKIT		1
#define HLI_ANTIDOTE		2
#define HLI_SECURITY		3
#define HLI_BATTERY			4

#define HLW_NONE			0
#define HLW_CROWBAR			1
#define HLW_GLOCK			2
#define HLW_PYTHON			3
#define HLW_MP5			4
#define HLW_CHAINGUN		5
#define HLW_CROSSBOW		6
#define HLW_SHOTGUN			7
#define HLW_RPG			8
#define HLW_GAUSS			9
#define HLW_EGON			10
#define HLW_HORNETGUN		11
#define HLW_HANDGRENADE		12
#define HLW_TRIPMINE		13
#define HLW_SATCHEL			14
#define HLW_SNARK			15
#define HLW_ALLWEAPONS		(~(1<<HLW_SUIT))
#define HLW_SUIT			31

new gHealthIndex[MAX_PLAYERS+1]

new mdl_gib_flesh
new mdl_gib_head
new mdl_gib_legbone
new mdl_gib_lung
new mdl_gib_meat
new mdl_gib_spine

new spr_blood_drop
new spr_blood_spray
new blood_small_num
new blood_large_num
new blood_small[8]
new blood_large[2]
new mod_name[32]
new Float:hostage_hp[MAX_HOSTAGES], hostages[MAX_HOSTAGES], host_num
new pcvar_gore, pcvar_gore_exphp

//Offsets to place blood is more realistic hit location
new Offset[8][3] = {{0,0,15},{0,0,30},{0,0,20},{0,0,12},{2,2,17},{2,2,17},{2,2,5},{2,2,5}}

// #####################################################################
// ##     Change which weapons cause GIB explosions on death HERE     ##
// #####################################################################

public gib_wpncheck(iWeapon)
{
	//This section is used for CS/CZ
	if (cstrike_running()) {
		switch (iWeapon) {
			case CSW_P228			: return false
			case CSW_SCOUT			: return false
			case CSW_HEGRENADE		: return true
			case CSW_XM1014		: return false
			case CSW_C4			: return true
			case CSW_MAC10			: return false
			case CSW_AUG			: return false
			case CSW_SMOKEGRENADE	: return true
			case CSW_ELITE			: return false
			case CSW_FIVESEVEN		: return false
			case CSW_UMP45			: return false
			case CSW_SG550			: return false
			case CSW_GALIL			: return false
			case CSW_FAMAS			: return false
			case CSW_USP			: return false
			case CSW_GLOCK18		: return false
			case CSW_AWP			: return false
			case CSW_MP5NAVY		: return false
			case CSW_M249			: return false
			case CSW_M3			: return false
			case CSW_M4A1			: return false
			case CSW_TMP			: return false
			case CSW_G3SG1			: return false
			case CSW_FLASHBANG		: return true
			case CSW_DEAGLE		: return false
			case CSW_SG552			: return false
			case CSW_AK47			: return false
			case CSW_KNIFE			: return false
			case CSW_P90			: return false
		}
	}
	//This section is used for DoD
	else if (equali(mod_name,"dod")) {
		switch (iWeapon) {
			case DODW_AMERKNIFE			: return false
			case DODW_GERKNIFE			: return false
			case DODW_COLT				: return false
			case DODW_LUGER			: return false
			case DODW_GARAND			: return false
			case DODW_SCOPED_KAR		: return false
			case DODW_THOMPSON			: return false
			case DODW_STG44			: return false
			case DODW_SPRINGFIELD		: return false
			case DODW_KAR				: return false
			case DODW_BAR				: return false
			case DODW_MP40				: return false
			case DODW_HANDGRENADE		: return true
			case DODW_STICKGRENADE		: return true
			case DODW_STICKGRENADE_EX	: return true
			case DODW_HANDGRENADE_EX		: return true
			case DODW_MG42				: return false
			case DODW_30_CAL			: return false
			case DODW_SPADE			: return false
			case DODW_M1_CARBINE		: return false
			case DODW_MG34				: return false
			case DODW_GREASEGUN			: return false
			case DODW_FG42				: return false
			case DODW_K43				: return false
			case DODW_ENFIELD			: return false
			case DODW_STEN				: return false
			case DODW_BREN				: return false
			case DODW_WEBLEY			: return false
			case DODW_BAZOOKA			: return true
			case DODW_PANZERSCHRECK		: return false
			case DODW_PIAT				: return false
			case DODW_SCOPED_FG42		: return false
			case DODW_FOLDING_CARBINE	: return false
			case DODW_KAR_BAYONET		: return false
			case DODW_SCOPED_ENFIELD		: return false
			case DODW_MILLS_BOMB		: return true
			case DODW_BRITKNIFE			: return false
			case DODW_GARAND_BUTT		: return false
			case DODW_ENFIELD_BAYONET	: return false
			case DODW_MORTAR			: return false
			case DODW_K43_BUTT			: return false
		}
	}
	//This section is used for TFC
	else if (equali(mod_name,"tfc")) {
		switch (iWeapon) {
			case TFC_WPN_NONE				: return false
			case TFC_WPN_TIMER				: return false
			case TFC_WPN_SENTRYGUN			: return false
			case TFC_WPN_MEDIKIT			: return false
			case TFC_WPN_SPANNER			: return false
			case TFC_WPN_AXE				: return false
			case TFC_WPN_SNIPERRIFLE			: return false
			case TFC_WPN_AUTORIFLE			: return false
			case TFC_WPN_SHOTGUN			: return false
			case TFC_WPN_SUPERSHOTGUN		: return false
			case TFC_WPN_NG				: return false
			case TFC_WPN_SUPERNG			: return false
			case TFC_WPN_GL				: return false
			case TFC_WPN_FLAMETHROWER		: return false
			case TFC_WPN_RPG				: return true
			case TFC_WPN_IC				: return false
			case TFC_WPN_FLAMES				: return false
			case TFC_WPN_AC				: return false
			case TFC_WPN_UNK18				: return false
			case TFC_WPN_UNK19				: return false
			case TFC_WPN_TRANQ				: return false
			case TFC_WPN_RAILGUN			: return false
			case TFC_WPN_PL				: return false
			case TFC_WPN_KNIFE				: return false
			case TFC_WPN_CALTROP			: return false
			case TFC_WPN_CONCUSSIONGRENADE	: return true
			case TFC_WPN_NORMALGRENADE		: return true
			case TFC_WPN_NAILGRENADE			: return true
			case TFC_WPN_MIRVGRENADE			: return true
			case TFC_WPN_NAPALMGRENADE		: return true
			case TFC_WPN_GASGRENADE			: return false
			case TFC_WPN_EMPGRENADE			: return false
		}

	}
	//This section is used for TS
	else if (equali(mod_name,"ts")) {
		switch (iWeapon) {
			case TSW_GLOCK18		: return false
			case TSW_UNK1			: return false
			case TSW_UZI			: return false
			case TSW_M3			: return false
			case TSW_M4A1			: return false
			case TSW_MP5SD			: return false
			case TSW_MP5K			: return false
			case TSW_ABERETTAS		: return false
			case TSW_MK23			: return false
			case TSW_AMK23			: return false
			case TSW_USAS			: return false
			case TSW_DEAGLE		: return false
			case TSW_AK47			: return false
			case TSW_57			: return false
			case TSW_AUG			: return false
			case TSW_AUZI			: return false
			case TSW_TMP			: return false
			case TSW_M82A1			: return false
			case TSW_MP7			: return false
			case TSW_SPAS			: return false
			case TSW_GCOLTS		: return false
			case TSW_GLOCK20		: return false
			case TSW_UMP			: return false
			case TSW_M61GRENADE		: return true
			case TSW_CKNIFE		: return false
			case TSW_MOSSBERG		: return false
			case TSW_M16A4			: return false
			case TSW_MK1			: return false
			case TSW_C4			: return true
			case TSW_A57			: return false
			case TSW_RBULL			: return false
			case TSW_M60E3			: return false
			case TSW_SAWED_OFF		: return true
			case TSW_KATANA		: return false
			case TSW_SKNIFE		: return false
			case TSW_KUNG_FU		: return false
			case TSW_TKNIFE		: return false
		}
	}
	//This section is used for ESF
	else if (equali(mod_name,"esf")) {
		//Always do GIB explosions on ESF
		return true
	}
	//This section is used for the "valve" mod
	else if (equali(mod_name,"valve")) {
		switch (iWeapon) {
			case HLW_NONE			: return false
			case HLW_CROWBAR		: return false
			case HLW_GLOCK			: return false
			case HLW_PYTHON		: return false
			case HLW_MP5			: return false
			case HLW_CHAINGUN		: return false
			case HLW_CROSSBOW		: return false
			case HLW_SHOTGUN		: return false
			case HLW_RPG			: return true
			case HLW_GAUSS			: return false
			case HLW_EGON			: return false
			case HLW_HORNETGUN		: return false
			case HLW_HANDGRENADE	: return true
			case HLW_TRIPMINE		: return true
			case HLW_SATCHEL		: return true
			case HLW_SNARK			: return false
		}
	}
	return false
}

// #####################################################################
// ##                     DO NOT EDIT BELOW HERE                      ##
// #####################################################################

/************************************************************
* PLUGIN FUNCTIONS
************************************************************/

public plugin_init()
{
	register_plugin("Ultimate Gore","1.5","JTP10181")

	get_modname(mod_name,31)

	if (cstrike_running()) {
		register_logevent("event_roundstart", 2, "1=Round_Start")
		register_event("TextMsg","event_host_damage","b","2=#Injured_Hostage")
		register_event("TextMsg","event_host_killed","b","2=#Killed_Hostage")
	}
	else if (equali(mod_name,"esf")) {
		register_event("DeathMsg","event_death","a")
		register_event("Health","event_damage","b")
	}
	else if (equali(mod_name,"valve")) {
		register_event("DeathMsg","event_death","a")
		register_event("Damage","event_damage","b","2!0")
	}

	register_event("ResetHUD","event_respawn","b")

	pcvar_gore = register_cvar("amx_gore","abcd")
	pcvar_gore_exphp = register_cvar("amx_gore_exphp","160")
	set_task(1.5,"event_blood",100,"",0,"b")

	// Blood decals and check needed modules are loaded
	if (equali(mod_name,"cstrike")) {

		if (!module_exists("csx")) set_fail_state("Module/Library ^"csx^" required for plugin. Check modules.ini.")

		blood_large = {204,205}
		blood_small = {190,191,192,193,194,196,197,0}
		blood_large_num = 2
		blood_small_num = 7
	}
	else if (equali(mod_name,"czero")) {

		if (!module_exists("csx")) set_fail_state("Module/Library ^"csx^" required for plugin. Check modules.ini.")

		blood_large = {216,217}
		blood_small = {202,203,204,205,206,207,208,209}
		blood_large_num = 2
		blood_small_num = 8
	}
	else if (equali(mod_name,"dod")) {

		if (!module_exists("dodx")) set_fail_state("Module/Library ^"dodx^" required for plugin. Check modules.ini.")

		blood_large = {217,218}
		blood_small = {203,204,205,206,207,208,209,210}
		blood_large_num = 2
		blood_small_num = 8
	}
	else if (equali(mod_name,"tfc")) {

		if (!module_exists("tfcx")) set_fail_state("Module/Library ^"tfcx^" required for plugin. Check modules.ini.")

		blood_large = {208,209}
		blood_small = {194,195,196,197,198,199,200,201}
		blood_large_num = 2
		blood_small_num = 8
	}
	else if (equali(mod_name,"ts")) {

		if (!module_exists("tsx")) set_fail_state("Module/Library ^"tsx^" required for plugin. Check modules.ini.")

		blood_large = {218,219}
		blood_small = {204,205,206,207,208,209,210,211}
		blood_large_num = 2
		blood_small_num = 8
	}
	//"valve" mod and others that use its decals.wad
	else {
		blood_large = {19,20}
		blood_small = {27,28,29,30,31,32,33,34}
		blood_large_num = 2
		blood_small_num = 8
	}

	//Setup jtp10181 CVAR
	new cvarString[256], shortName[16]
	copy(shortName,15,"gore")

	register_cvar("jtp10181","",FCVAR_SERVER|FCVAR_SPONLY)
	get_cvar_string("jtp10181",cvarString,255)

	if (strlen(cvarString) == 0) {
		formatex(cvarString,255,shortName)
		set_cvar_string("jtp10181",cvarString)
	}
	else if (contain(cvarString,shortName) == -1) {
		format(cvarString,255,"%s,%s",cvarString, shortName)
		set_cvar_string("jtp10181",cvarString)
	}
}

public plugin_natives()
{
	set_module_filter("module_filter")
	set_native_filter("native_filter")
}

public module_filter(const module[])
{
	if (!cstrike_running() && equali(module, "engine")) {
		return PLUGIN_HANDLED
	}

	return PLUGIN_CONTINUE
}

public native_filter(const name[], index, trap)
{
	if (!trap) return PLUGIN_HANDLED

	return PLUGIN_CONTINUE
}

public plugin_precache()
{
	spr_blood_drop = precache_model("sprites/blood.spr")
	spr_blood_spray = precache_model("sprites/bloodspray.spr")
	mdl_gib_flesh = precache_model("models/Fleshgibs.mdl")
	mdl_gib_meat = precache_model("models/GIB_B_Gib.mdl")
	mdl_gib_head = precache_model("models/GIB_Skull.mdl")

	if (!equali(mod_name,"dod")) {
		mdl_gib_spine = precache_model("models/GIB_B_Bone.mdl")
		mdl_gib_lung = precache_model("models/GIB_Lung.mdl")
		mdl_gib_legbone = precache_model("models/GIB_Legbone.mdl")
	}
}

public plugin_cfg()
{
	if (!cstrike_running()) return

	new iEnt = -1
	host_num = 0
	while ((iEnt = find_ent_by_class(iEnt,"hostage_entity")) != 0) {
		hostages[host_num++] = iEnt
		if (host_num >= MAX_HOSTAGES) break
	}
}

/************************************************************
* EVENTS
************************************************************/

//For "valve", ESF
public event_damage(iVictim)
{
	if (iVictim <= 0 || iVictim > MAX_PLAYERS) return

	new iWeapon, iHitPlace, iAgressor
	iAgressor = get_user_attacker(iVictim,iWeapon,iHitPlace)
	process_damage(iAgressor, iVictim, iHitPlace)
}

//Forward for CS/CZ, DoD, TFC, TS
public client_damage(attacker,victim,damage,wpnindex,hitplace)
{
	if (victim <= 0 || victim > MAX_PLAYERS) return
	process_damage(attacker, victim, hitplace)
}

//This will process the damage info for all mods
process_damage(iAgressor, iVictim, iHitPlace)
{
	new iFlags = get_gore_flags()

	//Don't want to do this if the player just died
	new vHealth = get_user_health(iVictim)
	if (vHealth <= 0 || vHealth >= gHealthIndex[iVictim]) return

	gHealthIndex[iVictim] = vHealth

	//Check to make sure its a player
	if (iAgressor <= 0 || iAgressor > MAX_PLAYERS) {
		iAgressor = iVictim
		iHitPlace = 0
	}

	if (!is_user_connected(iVictim) || !is_user_connected(iAgressor)) return

	//Crash check
	if (iHitPlace < 0 || iHitPlace > 7) iHitPlace = 0

	if (iFlags&GORE_BLOOD) {
		new iOrigin[3]
		new iOrigin2[3]
		get_user_origin(iVictim,iOrigin)
		get_user_origin(iAgressor,iOrigin2)
		fx_blood(iOrigin,iOrigin2,iHitPlace)
		fx_blood_small(iOrigin,8)
		if (iFlags&GORE_EXTRA) {
			fx_blood(iOrigin,iOrigin2,iHitPlace)
			fx_blood(iOrigin,iOrigin2,iHitPlace)
			fx_blood(iOrigin,iOrigin2,iHitPlace)
			fx_blood_small(iOrigin,4)
		}
	}
}

//Hostage Event for CS/CZ
public event_host_damage(iAgressor)
{
	new Float:vHeatlh, iVictim, iHitPlace = 0
	new iFlags = get_gore_flags()

	if (!(iFlags&GORE_HOSTAGES)) return

	//Find what hostage was injured
	for (new x = 0; x < host_num; x++) {
		if (!is_valid_ent(hostages[x])) continue
		if (hostage_hp[x] <= 0.0) continue
		vHeatlh = entity_get_float(hostages[x], EV_FL_health)

		if (vHeatlh > 0.0 && vHeatlh < hostage_hp[x]) {
			iVictim = hostages[x]
		}

		hostage_hp[x] = vHeatlh
	}

	//No valid hostage entity found
	if (iVictim <= 0) return

	//Check to make sure its a player
	if (iAgressor <= 0 || iAgressor > MAX_PLAYERS) {
		iAgressor = iVictim
	}

	if (iFlags&GORE_BLOOD) {
		new iOrigin[3], Float:vOrigin[3]
		new iOrigin2[3], Float:vOrigin2[3]

		entity_get_vector(iVictim, EV_VEC_origin, vOrigin)
		FVecIVec(vOrigin,iOrigin)
		entity_get_vector(iAgressor, EV_VEC_origin, vOrigin2)
		FVecIVec(vOrigin2,iOrigin2)

		//Add height to Hostage origin
		iOrigin[2] += 36

		fx_blood(iOrigin,iOrigin2,iHitPlace)
		fx_blood_small(iOrigin,8)
		if (iFlags&GORE_EXTRA) {
			fx_blood(iOrigin,iOrigin2,iHitPlace)
			fx_blood(iOrigin,iOrigin2,iHitPlace)
			fx_blood(iOrigin,iOrigin2,iHitPlace)
			fx_blood_small(iOrigin,4)
		}
	}
}

//For "valve", ESF
public event_death()
{
	new iVictim = read_data(2)
	new iWeapon, iHitPlace

	if (iVictim <= 0 || iVictim > MAX_PLAYERS) return
	new iAgressor = get_user_attacker(iVictim,iWeapon,iHitPlace)

	process_death(iAgressor, iVictim, iWeapon, iHitPlace)
}

//Forward for CS/CZ, DoD, TFC, TS
public client_death(killer,victim,wpnindex,hitplace)
{
	if (victim <= 0 || victim > MAX_PLAYERS) return
	process_death(killer, victim, wpnindex, hitplace)
}

//This will process the death info for all mods
process_death(iAgressor, iVictim, iWeapon, iHitPlace)
{
	new iOrigin[3],iOrigin2[3]
	new iFlags = get_gore_flags()

	//Check to make sure its a player
	if (iAgressor <= 0 || iAgressor > MAX_PLAYERS) {
		iAgressor = iVictim
		iWeapon = 0
		iHitPlace = 0
	}

	if (!is_user_connected(iVictim) || !is_user_connected(iAgressor)) return

	get_user_origin(iVictim,iOrigin)
	get_user_origin(iAgressor,iOrigin2)

	if (iFlags&GORE_HEADSHOT && iHitPlace == HIT_HEAD) {
		fx_headshot(iOrigin)
	}

	if (iFlags&GORE_GIB && (gib_wpncheck(iWeapon) || gHealthIndex[iVictim] - get_user_health(iVictim) >= get_pcvar_num(pcvar_gore_exphp))) {

		// Effects
		fx_trans(iVictim,0)
		fx_gib_explode(iOrigin,iOrigin2)
		fx_blood_large(iOrigin,4)
		fx_blood_small(iOrigin,12)
		if (iFlags&GORE_EXTRA && !equali(mod_name,"dod")) {
			fx_extra_blood(iOrigin)
			fx_blood_large(iOrigin,2)
			fx_blood_small(iOrigin,4)
		}
		fx_trans(iVictim,0)

		if (!equali(mod_name,"esf")) {
			set_task(0.5,"hide_body",iVictim)
		}
	}
	else {
		fx_blood_small(iOrigin,12)
		if (iFlags&GORE_EXTRA && !equali(mod_name,"dod")) {
			fx_extra_blood(iOrigin)
			fx_blood_large(iOrigin,2)
			fx_blood_small(iOrigin,4)
		}
	}
}

//Hostage Event for CS/CZ
public event_host_killed(iAgressor)
{
	new Float:vHeatlh, HP_Loss, iVictim, iWeapon = 0, clip, ammo
	new iFlags = get_gore_flags()

	if (!(iFlags&GORE_HOSTAGES)) return

	//Find what hostage was killed
	for (new x = 0; x < host_num; x++) {
		if (!is_valid_ent(hostages[x])) continue
		if (hostage_hp[x] <= 0.0) continue

		vHeatlh = entity_get_float(hostages[x], EV_FL_health)

		if (vHeatlh <= 0.0 && vHeatlh < hostage_hp[x]) {
			iVictim = hostages[x]
			HP_Loss = floatround(hostage_hp[x] - vHeatlh)
		}

		hostage_hp[x] = vHeatlh
	}

	//No valid hostage entity found
	if (iVictim <= 0) return

	//Check to make sure its a player
	if (iAgressor <= 0 || iAgressor > MAX_PLAYERS) {
		iAgressor = iVictim
	}
	else if (is_user_alive(iAgressor)) {
		iWeapon =  get_user_weapon(iAgressor,clip,ammo)
	}

	new iOrigin[3], Float:vOrigin[3]
	new iOrigin2[3], Float:vOrigin2[3]

	entity_get_vector(iVictim, EV_VEC_origin, vOrigin)
	FVecIVec(vOrigin,iOrigin)
	entity_get_vector(iAgressor, EV_VEC_origin, vOrigin2)
	FVecIVec(vOrigin2,iOrigin2)

	//Add height to Hostage origin
	iOrigin[2] += 36


	if (iFlags&GORE_GIB && (gib_wpncheck(iWeapon) || HP_Loss >= floatround(get_pcvar_float(pcvar_gore_exphp) * 0.6))) {

		// Effects
		entity_set_int(iVictim,EV_INT_rendermode,kRenderTransAlpha)
		entity_set_float(iVictim,EV_FL_renderamt,0.0)
		fx_gib_explode(iOrigin,iOrigin2)
		fx_blood_large(iOrigin,4)
		fx_blood_small(iOrigin,12)
		if (iFlags&GORE_EXTRA) {
			fx_extra_blood(iOrigin)
			fx_blood_large(iOrigin,2)
			fx_blood_small(iOrigin,4)
		}
		entity_set_int(iVictim,EV_INT_rendermode,kRenderTransAlpha)
		entity_set_float(iVictim,EV_FL_renderamt,0.0)
	}
	else {
		fx_blood_small(iOrigin,12)
		if (iFlags&GORE_EXTRA) {
			fx_extra_blood(iOrigin)
			fx_blood_large(iOrigin,2)
			fx_blood_small(iOrigin,4)
		}
	}
}

public hide_body(id)
{
	if (!is_user_connected(id) || is_user_alive(id) ) return

	fx_trans(id,0)
	new iOrigin[3]
	get_user_origin(id,iOrigin)
	iOrigin[2] -= 20
	set_user_origin(id,iOrigin)
}

public event_blood()
{
	new iFlags = get_gore_flags()
	if (!(iFlags&GORE_BLEEDING)) return

	new iPlayer, iPlayers[MAX_PLAYERS], iNumPlayers, iOrigin[3]
	get_players(iPlayers,iNumPlayers,"a")
	for (new i = 0; i < iNumPlayers; i++) {
		iPlayer = iPlayers[i]
		gHealthIndex[iPlayer] = get_user_health(iPlayer)
		if (gHealthIndex[iPlayer] < 20) {
			get_user_origin(iPlayer,iOrigin)
			fx_bleed(iOrigin)
			fx_blood_small(iOrigin,3)
		}
	}

	if (!(iFlags&GORE_HOSTAGES)) return

	//Hostage Bleeding
	if (cstrike_running()) {
		new Float:vOrigin[3]
		for (new x = 0; x < host_num; x++) {
			if (!is_valid_ent(hostages[x])) continue
			hostage_hp[x] = entity_get_float(hostages[x], EV_FL_health)
			if (hostage_hp[x] > 0.0 && hostage_hp[x] < 20.0) {
				entity_get_vector(hostages[x], EV_VEC_origin, vOrigin)
				FVecIVec(vOrigin,iOrigin)
				iOrigin[2] += 36
				fx_bleed(iOrigin)
				fx_blood_small(iOrigin,3)
			}
		}
	}
}

public event_respawn(id)
{
	if (is_user_alive(id)) {
		//Stop body hiding task if it has not run yet
		remove_task(id)
		//Prevents People from going invisible randomly
		fx_trans(id,255)
		//Save clients current Health
		gHealthIndex[id] = get_user_health(id)
	}
}

public event_roundstart(id)
{
	set_task(0.1,"roundstart_delay",100)
}

public roundstart_delay()
{
	for ( new id = 1; id <= MAX_PLAYERS; id++ ) {
		if (is_user_alive(id)) {
			//Prevents People from going invisible randomly
			fx_trans(id,255)
			//Save clients current Health
			gHealthIndex[id] = get_user_health(id)
		}
	}

	if (!cstrike_running()) return
	for (new x = 0; x < host_num; x++) {
		if (!is_valid_ent(hostages[x])) continue
		entity_set_int(hostages[x],EV_INT_rendermode,kRenderNormal)
		hostage_hp[x] = entity_get_float(hostages[x], EV_FL_health)
	}
}

public get_gore_flags()
{
	new sFlags[24]
	get_pcvar_string(pcvar_gore,sFlags,23)
	return read_flags(sFlags)
}

/************************************************************
* FX FUNCTIONS
************************************************************/

fx_trans(id,amount)
{
	set_user_rendering(id,kRenderFxNone,0,0,0,kRenderTransAlpha,amount)
}

fx_blood(origin[3],origin2[3],HitPlace)
{
	//Crash Checks
	if (HitPlace < 0 || HitPlace > 7) HitPlace = 0
	new rDistance = get_distance(origin,origin2) ? get_distance(origin,origin2) : 1

	new rX = ((origin[0]-origin2[0]) * 300) / rDistance
	new rY = ((origin[1]-origin2[1]) * 300) / rDistance
	new rZ = ((origin[2]-origin2[2]) * 300) / rDistance

	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(TE_BLOODSTREAM)
	write_coord(origin[0]+Offset[HitPlace][0])
	write_coord(origin[1]+Offset[HitPlace][1])
	write_coord(origin[2]+Offset[HitPlace][2])
	write_coord(rX) // x
	write_coord(rY) // y
	write_coord(rZ) // z
	write_byte(70) // color
	write_byte(random_num(100,200)) // speed
	message_end()
}

fx_bleed(origin[3])
{
	// Blood spray
	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(TE_BLOODSTREAM)
	write_coord(origin[0])
	write_coord(origin[1])
	write_coord(origin[2]+10)
	write_coord(random_num(-360,360)) // x
	write_coord(random_num(-360,360)) // y
	write_coord(-10) // z
	write_byte(70) // color
	write_byte(random_num(50,100)) // speed
	message_end()
}

fx_blood_small(origin[3],num)
{
	if (equali(mod_name,"esf")) return

	// Write Small splash decal
	for (new j = 0; j < num; j++) {
		message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
		write_byte(TE_WORLDDECAL)
		write_coord(origin[0]+random_num(-100,100))
		write_coord(origin[1]+random_num(-100,100))
		write_coord(origin[2]-36)
		write_byte(blood_small[random_num(0,blood_small_num - 1)]) // index
		message_end()
	}
}

fx_blood_large(origin[3],num)
{
	if (equali(mod_name,"esf")) return

	// Write Large splash decal
	for (new i = 0; i < num; i++) {
		message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
		write_byte(TE_WORLDDECAL)
		write_coord(origin[0]+random_num(-50,50))
		write_coord(origin[1]+random_num(-50,50))
		write_coord(origin[2]-36)
		write_byte(blood_large[random_num(0,blood_large_num - 1)]) // index
		message_end()
	}
}

fx_gib_explode(origin[3],origin2[3])
{
	new flesh[2]
	flesh[0] = mdl_gib_flesh
	flesh[1] = mdl_gib_meat
	new mult, gibtime = 400 //40 seconds

	if (equali(mod_name,"esf"))		mult = 400
	else if (equali(mod_name,"ts"))	mult = 140
	else							mult = 80

	new rDistance = get_distance(origin,origin2) ? get_distance(origin,origin2) : 1
	new rX = ((origin[0]-origin2[0]) * mult) / rDistance
	new rY = ((origin[1]-origin2[1]) * mult) / rDistance
	new rZ = ((origin[2]-origin2[2]) * mult) / rDistance
	new rXm = rX >= 0 ? 1 : -1
	new rYm = rY >= 0 ? 1 : -1
	new rZm = rZ >= 0 ? 1 : -1

	// Gib explosions

	// Head
	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(TE_MODEL)
	write_coord(origin[0])
	write_coord(origin[1])
	write_coord(origin[2]+40)
	write_coord(rX + (rXm * random_num(0,80)))
	write_coord(rY + (rYm * random_num(0,80)))
	write_coord(rZ + (rZm * random_num(80,200)))
	write_angle(random_num(0,360))
	write_short(mdl_gib_head)
	write_byte(0) // bounce
	write_byte(gibtime) // life
	message_end()

	// Parts
	for(new i = 0; i < 4; i++) {
		message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
		write_byte(TE_MODEL)
		write_coord(origin[0])
		write_coord(origin[1])
		write_coord(origin[2])
		write_coord(rX + (rXm * random_num(0,80)))
		write_coord(rY + (rYm * random_num(0,80)))
		write_coord(rZ + (rZm * random_num(80,200)))
		write_angle(random_num(0,360))
		write_short(flesh[random_num(0,1)])
		write_byte(0) // bounce
		write_byte(gibtime) // life
		message_end()
	}

	if (!equali(mod_name,"dod")) {

		// Spine
		message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
		write_byte(TE_MODEL)
		write_coord(origin[0])
		write_coord(origin[1])
		write_coord(origin[2]+30)
		write_coord(rX + (rXm * random_num(0,80)))
		write_coord(rY + (rYm * random_num(0,80)))
		write_coord(rZ + (rZm * random_num(80,200)))
		write_angle(random_num(0,360))
		write_short(mdl_gib_spine)
		write_byte(0) // bounce
		write_byte(gibtime) // life
		message_end()

		// Lung
		for(new i = 0; i <= 1; i++) {
			message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
			write_byte(TE_MODEL)
			write_coord(origin[0])
			write_coord(origin[1])
			write_coord(origin[2]+10)
			write_coord(rX + (rXm * random_num(0,80)))
			write_coord(rY + (rYm * random_num(0,80)))
			write_coord(rZ + (rZm * random_num(80,200)))
			write_angle(random_num(0,360))
			write_short(mdl_gib_lung)
			write_byte(0) // bounce
			write_byte(gibtime) // life
			message_end()
		}

		//Legs
		for(new i = 0; i <= 1; i++) {
			message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
			write_byte(TE_MODEL)
			write_coord(origin[0])
			write_coord(origin[1])
			write_coord(origin[2]-10)
			write_coord(rX + (rXm * random_num(0,80)))
			write_coord(rY + (rYm * random_num(0,80)))
			write_coord(rZ + (rZm * random_num(80,200)))
			write_angle(random_num(0,360))
			write_short(mdl_gib_legbone)
			write_byte(0) // bounce
			write_byte(gibtime) // life
			message_end()
		}
	}

	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(TE_BLOODSPRITE)
	write_coord(origin[0])
	write_coord(origin[1])
	write_coord(origin[2]+20)
	write_short(spr_blood_spray)
	write_short(spr_blood_drop)
	write_byte(248) // color index
	write_byte(10) // size
	message_end()
}

fx_extra_blood(origin[3])
{
	new x, y, z

	for(new i = 0; i < 3; i++) {
		x = random_num(-15,15)
		y = random_num(-15,15)
		z = random_num(-20,25)
		for(new j = 0; j < 2; j++) {
			message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
			write_byte(TE_BLOODSPRITE)
			write_coord(origin[0]+(x*j))
			write_coord(origin[1]+(y*j))
			write_coord(origin[2]+(z*j))
			write_short(spr_blood_spray)
			write_short(spr_blood_drop)
			write_byte(248) // color index
			write_byte(15) // size
			message_end()
		}
	}
}

fx_headshot(origin[3])
{
	new iFlags = get_gore_flags()
	new Sprays

	if (equali(mod_name,"dod"))	Sprays = 4
	else 					Sprays = 8

	if (iFlags&GORE_EXTRA) {
		// Blood sprays
		for (new i = 0; i < Sprays; i++) {
			message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
			write_byte(TE_BLOODSTREAM)
			write_coord(origin[0])
			write_coord(origin[1])
			write_coord(origin[2]+40)
			write_coord(random_num(-30,30)) // x
			write_coord(random_num(-30,30)) // y
			write_coord(random_num(80,300)) // z
			write_byte(70) // color
			write_byte(random_num(100,200)) // speed
			message_end()
		}
	}

	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(TE_BLOODSPRITE)
	write_coord(origin[0])
	write_coord(origin[1])
	write_coord(origin[2]+40)
	write_short(spr_blood_spray)
	write_short(spr_blood_drop)
	write_byte(248) // color index
	write_byte(15) // size
	message_end()
}