#include <amxmodx>
#include <fakemeta>

static const PLUGIN_NAME[] 	= "Bloody Corpses"
static const PLUGIN_AUTHOR[] 	= "se7h"
static const PLUGIN_VERSION[]	= "1.2"

new g_fwid, g_max_clients, g_guns_eventids_bitsum
new gSprBloodDecal[8] = { 190, 191, 192, 193, 194, 195, 196, 197 }
new gradius[4] = { 14, 13, 11, 14 }
new gorigin[33][4][3], bool:g_is_dead[33], Float:gfCorpsestay[33], Float:gfDeathtime[33]
new spr_blood_drop, spr_blood_spray
new knife_sound[] = "weapons/knife_hitwall1.wav"

new hitflesh[4][] = 
{
	"weapons/knife_hit1.wav",
	"weapons/knife_hit2.wav",
	"weapons/knife_hit3.wav",
	"weapons/knife_hit4.wav"
}

new const g_guns_events[][] =
{
	"events/awp.sc",
	"events/g3sg1.sc",
	"events/ak47.sc",
	"events/scout.sc",
	"events/m249.sc",
	"events/m4a1.sc",
	"events/sg552.sc",
	"events/aug.sc",
	"events/sg550.sc",
	"events/m3.sc",
	"events/xm1014.sc",
	"events/usp.sc",
	"events/mac10.sc",
	"events/ump45.sc",
	"events/fiveseven.sc",
	"events/p90.sc",
	"events/deagle.sc",
	"events/p228.sc",
	"events/glock18.sc",
	"events/mp5n.sc",
	"events/tmp.sc",
	"events/elite_left.sc",
	"events/elite_right.sc",
	"events/galil.sc",
	"events/famas.sc"
}

public plugin_init()
{
	register_plugin(PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR)

	unregister_forward(FM_PrecacheEvent, g_fwid, 1)
	register_forward(FM_PlaybackEvent, "fwPlaybackEvent")
	register_forward(FM_EmitSound, "fwEmitSound")

	register_event("ClCorpse", "event_cl_corpse", "a")
	register_event("HLTV", "new_round", "a", "1=0", "2=0")

	g_max_clients = global_get(glb_maxClients)
}

public plugin_precache()
{
	for(new i=0; i<4; i++)
		precache_sound(hitflesh[i])

	spr_blood_drop = precache_model("sprites/blood.spr")
	spr_blood_spray = precache_model("sprites/bloodspray.spr")

	g_fwid = register_forward(FM_PrecacheEvent, "fwPrecacheEvent", 1)
}

public fwPrecacheEvent(type, const name[])
{
	for(new i=0; i<sizeof g_guns_events; i++)
	{
		if (equal(g_guns_events[i], name))
		{
			g_guns_eventids_bitsum |= (1<<get_orig_retval())
			return FMRES_HANDLED
		}
	}

	return FMRES_IGNORED
}

public client_connect(id) 
{ 
    if(!is_user_bot(id)) 
        query_client_cvar(id, "cl_corpsestay", "func_cvar")
}

public func_cvar(id, const cvar[], const value[]) 
	gfCorpsestay[id] = str_to_float(value)

public new_round()
{
	arrayset(g_is_dead, false, sizeof g_is_dead)
	
	static p[32], num, i
	get_players(p, num, "ac") 
	for(i=0; i<num; i++)
		query_client_cvar(p[i], "cl_corpsestay", "func_cvar")
}

public event_cl_corpse()
{
	static id
	id = read_data(12)

	if(!is_user_connected(id))
		return

	g_is_dead[id] = true

	gfDeathtime[id] = get_gametime() + 10.0

	static Float:origin1[3], Float:origin2[3], Float:angle[3]

	engfunc(EngFunc_GetBonePosition, id, 6, origin1, angle)
	FVecIVec(origin1, gorigin[id][0])

	engfunc(EngFunc_GetBonePosition, id, 2, origin2, angle)
	FVecIVec(origin2, gorigin[id][1])

	engfunc(EngFunc_GetBonePosition, id, 32, origin1, angle)
	engfunc(EngFunc_GetBonePosition, id, 37, origin2, angle)
	mid_point(origin1, origin2, gorigin[id][2])

	engfunc(EngFunc_GetBonePosition, id, 33, origin1, angle)
	engfunc(EngFunc_GetBonePosition, id, 38, origin2, angle)
	mid_point(origin1, origin2, gorigin[id][3])
}

public fwEmitSound(id, channel, sound[])
{
	if(!(1 <= id <= g_max_clients) || g_is_dead[id] || !equali(sound, knife_sound) || get_user_weapon(id) != CSW_KNIFE)
		return FMRES_IGNORED

	if(!(pev(id, pev_button) & (IN_ATTACK | IN_ATTACK2)))
		return FMRES_IGNORED

	static Aim[3], Float:fAim[3], body
	body = find_dead_body(id, Aim)
	
	if(!body || !g_is_dead[body] || ((gfDeathtime[body] + gfCorpsestay[id]) < get_gametime()))
		return FMRES_IGNORED

	func_fx_bloodsprite(Aim)
	func_fx_blood_decal(Aim)
	IVecFVec(Aim, fAim)
	engfunc(EngFunc_EmitAmbientSound, 0, fAim, hitflesh[random(3)], VOL_NORM, ATTN_NORM, 0, PITCH_NORM)

	return FMRES_SUPERCEDE
}

public fwPlaybackEvent(flags, id, eventid)
{
	if(!(g_guns_eventids_bitsum & (1<<eventid)) || !(1 <= id <= g_max_clients))
		return FMRES_IGNORED

	static Aim[3], Float:fAim[3], body
	body = find_dead_body(id, Aim)
	
	if(!body || !g_is_dead[body] || ((gfDeathtime[body] + gfCorpsestay[id]) < get_gametime()))
		return FMRES_IGNORED

	func_fx_bloodsprite(Aim)
	func_fx_blood_decal(Aim)
	IVecFVec(Aim, fAim)
	engfunc(EngFunc_EmitAmbientSound, 0, fAim, hitflesh[random(3)], VOL_NORM, ATTN_NORM, 0, PITCH_NORM)

	return FMRES_IGNORED
}

find_dead_body(id, Aim[3], body = 0)
{
	static i, j
	get_user_origin(id, Aim, 3)
	for(i=1; i<=g_max_clients; i++)
	{
		for(j=0; j<4; j++)
		{
			if((get_distance(Aim, gorigin[i][j]) < gradius[j]) && (!body || body == i))
				return i
		}
	}

	return 0
}

stock func_fx_bloodsprite(origin[])
{
	message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
	write_byte(TE_BLOODSPRITE)
	write_coord(origin[0])
	write_coord(origin[1])
	write_coord(origin[2] + random_num(5,15))
	write_short(spr_blood_spray)
	write_short(spr_blood_drop)
	write_byte(248)
	write_byte(random_num(6,8))
	message_end()
}

stock func_fx_blood_decal(origin[])
{
	message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
	write_byte(TE_WORLDDECAL)
	write_coord(origin[0])
	write_coord(origin[1])
	write_coord(origin[2])
	write_byte(gSprBloodDecal[random(sizeof gSprBloodDecal)])
	message_end()
}

stock mid_point(const Float:origin1[], const Float:origin2[], out[])
{
	out[0] = floatround(origin1[0] + origin2[0])/2
	out[1] = floatround(origin1[1] + origin2[1])/2
	out[2] = floatround(origin1[2] + origin2[2])/2
}

