#include <amxmodx>
#include <fakemeta_util>
#include <hamsandwich>
#include <cstrike>
#include <engine>
#include <zombieplague>

#define PLUGIN "Spear gun"
#define VERSION "1.0"
#define AUTHOR "Atrocraz"

#define CSW_SPEARGUN CSW_M249
#define weapon_speargun "weapon_m249"
#define old_w_model "models/w_m249.mdl"
#define WEAPONKEY 198414

#define speargun_V_MODEL "models/zm/v_speargun.mdl"
#define speargun_P_MODEL "models/zm/p_speargun.mdl"
#define speargun_W_MODEL "models/zm/w_speargun.mdl"
#define ROCKET_MODEL "models/zm/spear.mdl"

#define start_ammo 20
#define spear_speed 3000
#define spear_damage 50.0
#define explode_radius 150.0
#define explode_damage 40.0
#define spear_classname "spear"
#define spear_explode_time 1.0

new const Sounds[][] = 
{
	"weapons/speargun-1.wav",
	"weapons/speargun_clipin.wav",
	"weapons/speargun_draw.wav"
}

new const Float: draw_anim_time = 1.43
new const Float: reload_time = 1.13

new g_has_speargun[33], bool:g_draw[33], Float:g_time[33], can_attack[33], g_ammo[33], g_spear_id[33], bool:reload[33]
new g_MsgCurWeapon, g_MsgAmmoX, g_MsgWeaponList, g_exp_sprid, g_item

#define e_precache_model(%0)		engfunc(EngFunc_PrecacheModel, %0)
#define e_precache_sound(%0)		engfunc(EngFunc_PrecacheSound, %0)
#define e_precache_generic(%0)		engfunc(EngFunc_PrecacheGeneric, %0)

enum{
	no,
	can,
	shooting,
	reloading
}

public plugin_init() 
{
	register_plugin(PLUGIN, VERSION, AUTHOR)

	g_MsgCurWeapon = get_user_msgid("CurWeapon")
	g_MsgAmmoX = get_user_msgid("AmmoX")
	g_MsgWeaponList = get_user_msgid("WeaponList")

	register_forward(FM_SetModel, "fw_SetModel")
	RegisterHam(Ham_Item_AddToPlayer, weapon_speargun, "fw_Item_AddToPlayer_Post", 1)
	RegisterHam(Ham_Weapon_PrimaryAttack, weapon_speargun, "fw_Attack")
	RegisterHam(Ham_Player_PreThink, "player", "fw_PreThink", 1)
	RegisterHam(Ham_Item_Deploy, weapon_speargun, "fw_Item_Deploy_Post", 1)
	RegisterHam(Ham_Spawn, "player", "fw_Spawn_Post", 1)

	register_touch(spear_classname, "*", "fw_touch_world")
	register_think(spear_classname, "SpearExplode")

	register_clcmd("say get", "get_tk")
	register_clcmd("weapon_speargun", "WeaponList_Hook")
	server_cmd("sv_maxvelocity 8000")
	
	g_item = zp_register_extra_item("Speargun", 50, ZP_TEAM_HUMAN)
}

public WeaponList_Hook(id)
{
    	engclient_cmd(id, weapon_speargun)
    	return PLUGIN_HANDLED
}

public zp_extra_item_selected(id, itemid)
{
	if (itemid == g_item)
		get_tk(id)
}

public zp_user_infected_post(id)	g_has_speargun[id] = false

public get_tk(id)
{
	drop_weapons(id, 1)
	g_has_speargun[id]=true
	g_ammo[id] = start_ammo
	reload[id] = false
	fm_give_item(id, weapon_speargun)
	engclient_cmd(id, weapon_speargun)
	fake_weapon_draw(id, get_gametime())
}

public plugin_precache()
{
	e_precache_model(speargun_V_MODEL)
	e_precache_model(speargun_P_MODEL)
	e_precache_model(speargun_W_MODEL)
	e_precache_model(ROCKET_MODEL)

	for(new i=0;i<3;i++)
		e_precache_sound(Sounds[i])

	e_precache_generic("sprites/zm/640hud103.spr")
	e_precache_generic("sprites/zm/640hud12.spr")
	e_precache_generic("sprites/weapon_speargun.txt")
	g_exp_sprid = e_precache_model("sprites/zm/at4_bax.spr")
}

public fw_PreThink(id)
{
	if(!g_has_speargun[id]) return HAM_IGNORED
	if(!is_user_alive(id)) return HAM_IGNORED

	static weapon, Float:current_time

	weapon = get_user_weapon(id)
	current_time = get_gametime()

	if(!g_draw[id])
	{
		if(current_time - g_time[id] > draw_anim_time)
		{
			if(g_ammo[id])
				UTIL_PlayWeaponAnimation (id, 0)
			else
				UTIL_PlayWeaponAnimation (id, 5)

			g_draw[id] = true

			if(g_spear_id[id] || reload[id])
				can_attack[id] = shooting
			else
				can_attack[id] = can
		}
	}
	if(weapon == CSW_SPEARGUN)
	{
		if(can_attack[id] == can && g_ammo[id] > 0)
		{
			if(pev(id, pev_button) & IN_ATTACK) 
			{
				UTIL_PlayWeaponAnimation(id, 1)
				can_attack[id] = shooting
				g_time[id] = current_time
				emit_sound(id, CHAN_WEAPON, Sounds[0], VOL_NORM, ATTN_NORM, 0, PITCH_NORM)
				g_ammo[id] --
				UpdateAmmo(id, CSW_SPEARGUN, g_ammo[id])
				Fire_Spear(id)
				reload[id] = true
			}
		}
		if(can_attack[id] == shooting)
		{
			if(pev(id, pev_button) & IN_ATTACK2 && g_spear_id[id])
				SpearExplode(g_spear_id[id])

			if(current_time - g_time[id] > 1.0 && !g_spear_id[id] && g_ammo[id] > 0)
			{
				g_time[id] = current_time
				can_attack[id] = reloading
				UTIL_PlayWeaponAnimation(id, 2)
			}
		}
		if(can_attack[id] == reloading)	
		{
			if(current_time - g_time[id] > reload_time)
			{
				can_attack[id] = can
				reload[id] = false
			}
		}
	}

	return HAM_HANDLED
}

public Fire_Spear(id)
{
	new ent = engfunc(EngFunc_CreateNamedEntity, engfunc(EngFunc_AllocString, "info_target"))
	g_spear_id[id] = ent
	
	engfunc(EngFunc_SetModel, ent, ROCKET_MODEL)
	set_pev(ent, pev_classname, spear_classname)
	set_pev(ent, pev_mins, Float:{-1.0, -1.0, -1.0})
	set_pev(ent, pev_maxs, Float:{1.0, 1.0, 1.0})

	static Float:vOrigin[3], Float:vUp[3], Float:fVelocity[3], Float:fAngle[3]

	pev(id, pev_v_angle, fAngle)
	pev(id, pev_origin, vOrigin)

	global_get(glb_v_up, vUp)
	new up = pev(id, pev_button) & IN_DUCK ? 7 : 17
	vOrigin[0] = vOrigin[0] + vUp[0] * up
	vOrigin[1] = vOrigin[1] + vUp[1] * up
	vOrigin[2] = vOrigin[2] + vUp[2] * up
	set_pev(ent, pev_origin, vOrigin)

	set_pev(ent, pev_movetype, MOVETYPE_FLY)
	VelocityByAim(id, spear_speed, fVelocity)

	set_pev(ent, pev_owner, id)
	set_pev(ent, pev_solid, SOLID_TRIGGER)

	set_pev(ent, pev_framerate, 1.0)
	set_pev(ent, pev_sequence, 0)

	entity_set_vector(ent, EV_VEC_velocity, fVelocity)

	engfunc(EngFunc_VecToAngles, fVelocity, fAngle)
	entity_set_vector(ent, EV_VEC_angles, fAngle)

	//set_pev(ent, pev_fuser3, spear_explode_time)
	entity_set_float(ent, EV_FL_nextthink, get_gametime() + spear_explode_time)
}

public fw_touch_world(spear, world)
{
	if(!pev_valid(spear))
		return 
	if(world == pev(spear, pev_owner))
		return

	static classname[32]
	pev(world, pev_classname, classname, 31)

	if(equal(classname, spear_classname))
		return

	if(is_user_alive(world))// && (pev(spear, pev_iuser3) != world) && (0 < world < 33))
	{
		if(!zp_get_user_zombie(world))
			return

		static Float:Velocity[3]

		ExecuteHamB(Ham_TakeDamage, world, spear, pev(spear, pev_owner), spear_damage, DMG_BULLET)
		
		if(is_user_alive(world))
		{
			//set_pev(spear, pev_aiment, world)
			//set_pev(spear, pev_movetype, MOVETYPE_FOLLOW)

			pev(spear, pev_velocity, Velocity)
			Velocity[0] = Velocity[0] / 2
			Velocity[1] = Velocity[1] / 2
			Velocity[2] = (Velocity[2] / 2) + 10.0
			set_pev(world, pev_velocity, Velocity)

			SpearExplode(spear)
		}
		//set_pev(spear, pev_iuser3, world)
	}
	else
		set_pev(spear, pev_velocity, {0.0, 0.0, 0.0})
}

/*public SpearThink(spear)
{
	
}*/

public SpearExplode(spear)
{
	if(!pev_valid(spear))
		return

	static Float: fOrigin[3], victim
	pev(spear, pev_origin, fOrigin)

	victim = -1
	while((victim = engfunc(EngFunc_FindEntityInSphere, victim, fOrigin, explode_radius)) !=0)
	{
		if(!is_user_alive(victim) || !pev_valid(victim))
			continue

		if(!zp_get_user_zombie(victim))
			continue

		ExecuteHamB(Ham_TakeDamage, victim, spear, pev(spear, pev_owner), explode_damage, DMG_BULLET)
	}

	engfunc(EngFunc_MessageBegin, MSG_PAS, SVC_TEMPENTITY, fOrigin, 0)
	write_byte(TE_EXPLOSION)
	engfunc(EngFunc_WriteCoord, fOrigin[0])
	engfunc(EngFunc_WriteCoord, fOrigin[1])
	engfunc(EngFunc_WriteCoord, fOrigin[2])
	write_short(g_exp_sprid)
	write_byte(5)
	write_byte(20)
	write_byte(0)
	message_end()

	g_spear_id[pev(spear, pev_owner)] = 0
	remove_entity(spear)
}

public fw_Item_Deploy_Post(ent)
{
	static id
	id = get_pdata_cbase(ent, 41, 4)	

	if(g_has_speargun[id])
		fake_weapon_draw(id, get_gametime())
}

public fw_SetModel(entity, model[])
{
	if(!is_valid_ent(entity))
		return FMRES_IGNORED
	
	static szClassName[33]
	entity_get_string(entity, EV_SZ_classname, szClassName, charsmax(szClassName))
		
	if(!equal(szClassName, "weaponbox"))
		return FMRES_IGNORED
	
	static iOwner;	iOwner = entity_get_edict(entity, EV_ENT_owner)
	
	if(equal(model, old_w_model))
	{
		static iStoredAugID
		iStoredAugID = find_ent_by_owner(-1, weapon_speargun, entity)
	
		if(!is_valid_ent(iStoredAugID))
			return FMRES_IGNORED
	
		if(g_has_speargun[iOwner])
		{
			entity_set_int(iStoredAugID, EV_INT_impulse, WEAPONKEY)
			set_pev(iStoredAugID, pev_iuser3, g_ammo[iOwner])
			g_has_speargun[iOwner] = false
			entity_set_model(entity, speargun_W_MODEL)
			return FMRES_SUPERCEDE
		}
	}
	return FMRES_IGNORED
}

public fw_Item_AddToPlayer_Post(ent, id)
{
	if(!is_valid_ent(ent) || !is_user_connected(id))
		return HAM_IGNORED
	
	if(entity_get_int(ent, EV_INT_impulse) == WEAPONKEY)
	{
		g_has_speargun[id] = true
		g_ammo[id] = pev(ent, pev_iuser3)
		entity_set_int(ent, EV_INT_impulse, 0)
		fake_weapon_draw(id, get_gametime())
	}
	return HAM_IGNORED
}

public fw_Spawn_Post(id)
	if(is_user_alive(id))	
		if(get_user_weapon(id) == CSW_SPEARGUN)
			if(g_has_speargun[id])
				fake_weapon_draw(id, get_gametime())

public fw_Attack(entity)
{
	new id = get_pdata_cbase(entity, 41, 4)
	
	if(g_has_speargun[id])
		return HAM_SUPERCEDE

	return HAM_HANDLED
}

UpdateAmmo(id, CSW_ID, BpAmmo)
{
	message_begin(MSG_ONE_UNRELIABLE, g_MsgCurWeapon, _, id)
	write_byte(1)
	write_byte(CSW_ID)
	write_byte(-1)
	message_end()
    
	message_begin(MSG_ONE_UNRELIABLE, g_MsgAmmoX, _, id)
	write_byte(3)
	write_byte(BpAmmo)
	message_end()
    
	cs_set_user_bpammo(id, CSW_ID, BpAmmo)
}

stock UTIL_PlayWeaponAnimation(const Player, const Sequence)
{
	 set_pev(Player, pev_weaponanim, Sequence)
	 
	 message_begin(MSG_ONE_UNRELIABLE, SVC_WEAPONANIM, .player = Player)
	 write_byte(Sequence)
	 write_byte(pev(Player, pev_body))
	 message_end()
}

stock fake_weapon_draw(id, Float:current_time)
{
	set_pev(id, pev_viewmodel2, speargun_V_MODEL)
	set_pev(id, pev_weaponmodel2, speargun_P_MODEL)
	if(g_ammo[id])
		UTIL_PlayWeaponAnimation (id, 3)
	else
		UTIL_PlayWeaponAnimation (id, 4)

	set_weapons_timeidle(id ,99999999.0)
	set_player_nextattack(id ,99999999.0)

	can_attack[id] = no
	g_draw[id] = false
	g_time[id] = current_time

	message_begin(MSG_ONE_UNRELIABLE, g_MsgWeaponList, {0,0,0}, id)
	write_string("weapon_speargun")
	write_byte(3)
	write_byte(200)
	write_byte(-1)
	write_byte(-1)
	write_byte(0)
	write_byte(4)
	write_byte(CSW_SPEARGUN)
	message_end()

	UpdateAmmo(id, CSW_SPEARGUN, g_ammo[id])
}

stock set_weapons_timeidle(id, Float:TimeIdle)
{
	new entwpn = get_user_weapon(id)==CSW_SPEARGUN
	if (pev_valid(entwpn)) 
	{
		set_pdata_float(entwpn, 46, TimeIdle, 4)
		set_pdata_float(entwpn, 47, TimeIdle, 4)
		set_pdata_float(entwpn, 48, TimeIdle + 1.0, 4)
	}
}

stock set_player_nextattack(id, Float:nexttime)
{
	const m_flNextAttack = 83
	set_pdata_float(id, m_flNextAttack, nexttime, 5)
}

/*stock find_closest_bone_to_gunshot(victim, Float:endtrace[3])
{
	new Float:angles[3], Float:origin[3], Float:dist = 50.00, Float:curorigin[3], bone_nr
	for (new i = 1; i <= 54; i ++)
	{
  		engfunc(EngFunc_GetBonePosition, victim, i, curorigin, angles)
		xs_vec_sub(curorigin, endtrace, angles)

		if (xs_vec_len(angles) <= dist)
		{
			origin = curorigin
			dist = xs_vec_len(angles)
			bone_nr = i
		}
		if (dist <= 2.0)
			break
	}
	return bone_nr
}*/

const PRIMARY_WEAPONS_BIT_SUM = (1<<CSW_SCOUT)|(1<<CSW_XM1014)|(1<<CSW_P90)|(1<<CSW_P90)|(1<<CSW_P90)|(1<<CSW_SG550)|
(1<<CSW_P90)|(1<<CSW_FAMAS)|(1<<CSW_AWP)|(1<<CSW_P90)|(1<<CSW_M249)|(1<<CSW_M3)|(1<<CSW_M4A1)|(1<<CSW_TMP)|(1<<CSW_M3)|
(1<<CSW_P90)|(1<<CSW_AK47)|(1<<CSW_P90)

stock drop_weapons(id, dropwhat)
{
	static weapons[32], num, i, weaponid
	num = 0
	get_user_weapons(id, weapons, num)
	
	for (i = 0; i < num; i++)
	{
		weaponid = weapons[i]
		
		if (dropwhat == 1 && ((1<<weaponid) & PRIMARY_WEAPONS_BIT_SUM))
		{
			static wname[32]
			get_weaponname(weaponid, wname, sizeof wname - 1)
			engclient_cmd(id, "drop", wname)
		}
	}
}