
#include <amxmodx>

#include <engine>
#include <fakemeta>
#include <hamsandwich>
#include <cstrike>

#include <xs>

new const Plugin[] = "Super Shield"
new const Author[] = "joaquimandrade"
new const Version[] = "1.0"

new const ShieldClass[] = "weapon_shield"
new const SuperShieldClass[] = "super_shield"

new const ShieldDefaultModel[] = "models/w_shield.mdl"

const MaxSlots = 33
new MaxPlayers

new bool:HadShield[MaxSlots char]
new bool:HitShield[MaxSlots char] 
new bool:BlockDropMsg[MaxSlots char] 

new HandlingSuperShield[MaxSlots]

new Array:ShieldsList
new Array:SuperShieldsList

new CvarShieldDamageAbsorption 

const Float:ShieldLongerDistance = 150.0

new Float:ShieldHoldDistance[MaxSlots]

new Float:ShieldHoldDistanceMin = 50.0

const DMG_GRENADE = (1<<24)

new RicochetSounds[][] = {"weapons/ric_metal-1.wav","weapons/ric_metal-2.wav"}

const pev_isSuperShield = pev_iuser1

public plugin_init()
{
	register_plugin(Plugin,Version,Author)
	
	register_forward(FM_CmdStart,"cmdStart")
		
	RegisterHam(Ham_Spawn,ShieldClass,"shieldSpawn")
	RegisterHam(Ham_Touch,ShieldClass,"shieldTouchPre")
	RegisterHam(Ham_Touch,ShieldClass,"shieldTouchPost",1)
	
	RegisterHam(Ham_TraceAttack,"info_target","superShieldTraceAttack")
	RegisterHam(Ham_TakeDamage,"player","playerTakeDamage")
	
	RegisterHam(Ham_Weapon_PrimaryAttack,"weapon_knife","weaponCheckBlock")
	RegisterHam(Ham_Weapon_SecondaryAttack,"weapon_knife","weaponCheckBlock")
	
	RegisterHam(Ham_CS_Item_CanDrop,"weapon_knife","weaponCheckTransformShield")
	
	new weaponName[24];
	
	get_weaponname(1,weaponName,charsmax (weaponName));
	RegisterHam(Ham_Weapon_PrimaryAttack,weaponName,"weaponAttack");

	for (new i=3, lastWeaponID=30; i<=lastWeaponID; i++)
	{
		get_weaponname(i,weaponName,charsmax(weaponName));
		RegisterHam(Ham_Weapon_PrimaryAttack,weaponName,"weaponAttack");
	}
	
	RegisterHam(Ham_Weapon_SecondaryAttack,"weapon_knife","weaponAttack");

	register_event("HLTV","newRound","a","1=0","2=0")
	register_message(get_user_msgid("TextMsg"),"textMsg")
	
	CvarShieldDamageAbsorption = register_cvar("shield_dmg_absorption","100")
}

public plugin_precache()
{
	for(new i=0;i<sizeof RicochetSounds;i++)
		precache_sound(RicochetSounds[i])
	
	precache_model(ShieldDefaultModel)
}

public plugin_cfg()
{
	MaxPlayers = get_maxplayers()
	
	ShieldsList = ArrayCreate()
	SuperShieldsList = ArrayCreate()
}

public weaponAttack(id)
{
	HitShield{pev(id,pev_owner)} = false
}

public weaponCheckBlock(id)
{
	return HandlingSuperShield[pev(id,pev_owner)] ? HAM_SUPERCEDE : HAM_IGNORED
}

public textMsg(msgid,msgDest,id)
{
	if(BlockDropMsg{id} && get_msg_arg_int(1) == 4)
	{
		static const msg[] = "#Weapon_Cannot_Be_Dropped"
		new arg[sizeof msg] 
		
		get_msg_arg_string(2,arg,charsmax(arg))
		
		if(equal(msg,arg))
		{
			BlockDropMsg{id} = false
			return PLUGIN_HANDLED
		}
	}
	
	return PLUGIN_CONTINUE
}

public weaponCheckTransformShield(id)
{
	new owner = pev(id,pev_owner)
	new superShield = HandlingSuperShield[owner]
	
	if(superShield)
	{
		HandlingSuperShield[owner] = 0
		
		static Float:angles[3],Float:origin[3]
		
		pev(owner,pev_angles,angles)
		pev(superShield,pev_origin,origin)
		
		removeShield(SuperShieldsList,superShield)
		remove_entity(superShield)
		
		new shield = createShield(origin,angles)
			
		drop_to_floor(shield)
		
		BlockDropMsg{owner} = true;
	}
}

createShield(Float:origin[3],Float:angles[3])
{
	new shield = create_entity(ShieldClass);
	
	entity_set_model(shield,ShieldDefaultModel)
	
	set_pev(shield,pev_angles,angles)
	
	static Float:mins[]= {-16.0,-16.0,-30.0};
	static Float:maxs[]= {16.0,16.0,30.0};
	
	entity_set_size(shield,mins,maxs)
	
	entity_set_origin(shield,origin)	
	
	DispatchSpawn(shield);
	
	return shield;
}


shieldVisualRicochet(Float:origin[3])
{
	engfunc(EngFunc_MessageBegin,MSG_PVS,SVC_TEMPENTITY,origin,0);     
	write_byte(TE_ARMOR_RICOCHET);
	engfunc(EngFunc_WriteCoord,origin[0]);
	engfunc(EngFunc_WriteCoord,origin[1]);
	engfunc(EngFunc_WriteCoord,origin[2]);
	write_byte(10);
	message_end()
	
	engfunc(EngFunc_MessageBegin,MSG_PVS,SVC_TEMPENTITY,origin,0);     
	write_byte(TE_SPARKS);
	engfunc(EngFunc_WriteCoord,origin[0]);
	engfunc(EngFunc_WriteCoord,origin[1]);
	engfunc(EngFunc_WriteCoord,origin[2]);
	message_end();
}

shieldSoundRicochet(Float:origin[3])
{
	engfunc(EngFunc_EmitAmbientSound,0,origin,RicochetSounds[random(sizeof RicochetSounds)],VOL_NORM + 10.0,ATTN_NORM,0,PITCH_NORM);
}

public superShieldTraceAttack(id,attackerID,Float:damage,Float:direction[3],tracehandle,damagebits)
{
	if((1 <= attackerID <= MaxPlayers) && pev(id,pev_isSuperShield))
	{
		static Float:hitPoint[3]
		
		get_tr2(tracehandle,TR_vecEndPos,hitPoint)
				
		shieldVisualRicochet(hitPoint)
		shieldSoundRicochet(hitPoint)

		HitShield{attackerID} = true;
	}
	
	return HAM_IGNORED
}

public playerTakeDamage(id,inflictorID,attackerID,Float:damage,damagebits)
{	
	if((1 <= attackerID <= MaxPlayers) && HitShield{attackerID} && (~damagebits & DMG_GRENADE))
	{
		new absorption = clamp(get_pcvar_num(CvarShieldDamageAbsorption),0,100)
		
		if(absorption == 100)
		{
			SetHamParamEntity(1,0);
		}
		else
		{
			SetHamParamFloat(4,damage *  float(100 - absorption) / 100.0)
		}
		
		return HAM_HANDLED
	}
	
	return HAM_IGNORED
}

public newRound()
{
	deleteEntitiesOnList(ShieldsList)
	deleteEntitiesOnList(SuperShieldsList)
	
	ArrayClear(ShieldsList)
	ArrayClear(SuperShieldsList)
}

deleteEntitiesOnList(Array:list)
{
	for(new i=0;i<ArraySize(list);i++)
	{
		remove_entity(ArrayGetCell(list,i))
	}
}

addShield(Array:list,id)
{	
	ArrayPushCell(list,id);
}

removeShield(Array:list,id)
{
	for(new i=0;i<ArraySize(list);i++)
	{
		if(ArrayGetCell(list,i) == id)
		{
			ArrayDeleteItem(list,i);
			break;
		}
	}
}

removeShieldByPosition(Array:list,position)
{
	ArrayDeleteItem(list,position);
}

public shieldSpawn(id)
{
	addShield(ShieldsList,id)
}
public shieldTouchPre(id,toucher)
{
	if(1 <= toucher <= MaxPlayers)
	{
		HadShield{toucher} = bool:cs_get_user_shield(toucher)
	}
}

public shieldTouchPost(id,toucher)
{
	if(1 <= toucher <= MaxPlayers)
	{
		if(!HadShield{toucher} && cs_get_user_shield(toucher))
		{
			removeShield(ShieldsList,id)
		}
	}
}

createSuperShield(Float:origin[3],Float:angles[],model[],const class[])
{
	new superShield = create_entity("info_target")
	
	assert superShield;
	
	set_pev(superShield,pev_angles,angles)

	set_pev(superShield,pev_solid,SOLID_BBOX);
	set_pev(superShield,pev_movetype,MOVETYPE_NONE);

	set_pev(superShield,pev_classname,class);
	
	static Float:mins[]= {-14.0,-18.0,-3.0};
	static Float:maxs[]= {14.0,18.0,5.0};

	entity_set_model(superShield,model)
	entity_set_size(superShield,mins,maxs)
	entity_set_origin(superShield,origin)
	
	return superShield	
}

public cmdStart(id,uc_handle,random_seed)
{
	new handlingSuperShield = HandlingSuperShield[id]
	HandlingSuperShield[id] = 0;
	
	new buttons = get_uc(uc_handle,UC_Buttons)
	
	if( (buttons & IN_USE) && (cs_get_user_team(id) == CS_TEAM_CT))
	{
		static Float:start[3], Float:dest[3] , Float:viewOffset[3], Float:path[3]
		
		pev(id,pev_origin,start);
		pev(id,pev_view_ofs,viewOffset);
		xs_vec_add(start,viewOffset,start);
		
		pev(id,pev_v_angle,path);
		engfunc(EngFunc_MakeVectors,path);
		global_get(glb_v_forward,path);
		
		xs_vec_mul_scalar(path,ShieldLongerDistance,dest);
		xs_vec_add(start,dest,dest);
		
		engfunc(EngFunc_TraceLine,start,dest,0,id,0);
		
		get_tr2(0,TR_vecEndPos,dest)
		
		new hitEnt = get_tr2(0,TR_Hit)
		
		for(new i=0,superShield;i<ArraySize(SuperShieldsList);i++)
		{
			superShield = ArrayGetCell(SuperShieldsList,i);
			
			if(hitEnt == superShield)
			{
				if(!handlingSuperShield)
				{
					new Float:distance = vector_distance(start,dest)
					
					if(distance < ShieldHoldDistanceMin)
						distance = ShieldHoldDistanceMin
					
					ShieldHoldDistance[id] = distance
				}
				
				HandlingSuperShield[id] = superShield
										
				if(get_user_weapon(id) == CSW_KNIFE)
				{
					new isPressingAttack = buttons & IN_ATTACK
					new isPressingAttack2 = buttons & IN_ATTACK2
					
					static Float:angles[3]
					pev(superShield,pev_angles,angles)
					
					if(isPressingAttack && isPressingAttack2)
					{
						angles[2] += 1.0
					}
					else if(isPressingAttack)
					{
						angles[0] += 1.0
					}
					else if(isPressingAttack2)
					{
						angles[1] += 1.0
					}
					
					set_pev(superShield,pev_angles,angles)
				}
				
				xs_vec_mul_scalar(path,ShieldHoldDistance[id],dest);
				xs_vec_add(start,dest,dest);
				
				entity_set_origin(superShield,dest)
				
				break;
			}
		}
		
		if(!HandlingSuperShield[id])
		{
			for(new i=0,shield;i<ArraySize(ShieldsList);i++)
			{
				shield = ArrayGetCell(ShieldsList,i)
				
				engfunc(EngFunc_TraceModel,start,dest,HULL_POINT,shield,0)
				
				if(get_tr2(0,TR_pHit) == shield)
				{
					static model[100]
					pev(shield,pev_model,model,charsmax(model))
					
					static Float:origin[3]
					pev(shield,pev_origin,origin)
					
					static Float:angles[3]
					pev(shield,pev_angles,angles)
					
					removeShieldByPosition(ShieldsList,i)
					
					remove_entity(shield)
					
					new superShield = createSuperShield(origin,angles,model,SuperShieldClass);
					
					set_pev(superShield,pev_isSuperShield,true)
					
					addShield(SuperShieldsList,superShield);
					
					new Float:distance = vector_distance(start,dest)
					
					if(distance < ShieldHoldDistanceMin)
						distance = ShieldHoldDistanceMin
					
					ShieldHoldDistance[id] = distance
					
					HandlingSuperShield[id] = superShield
					
					break;
				}
			}
		}
	}
}