//assault rifles
#include "arifl/weapon_ins2akm"
#include "arifl/weapon_ins2m16a4"
#include "arifl/weapon_ins2galil"
#include "arifl/weapon_ins2asval"
#include "arifl/weapon_ins2l85a2"
#include "arifl/weapon_ins2ak74"
#include "arifl/weapon_ins2ak12"
#include "arifl/weapon_ins2f2000"
#include "arifl/weapon_ins2stg44"
//battle rifles
#include "brifl/weapon_ins2fnfal"
#include "brifl/weapon_ins2g3a3"
#include "brifl/weapon_ins2m14ebr"
#include "brifl/weapon_ins2fg42"
//rifles
#include "rifle/weapon_ins2garand"
#include "rifle/weapon_ins2enfield"
//handguns
#include "handg/weapon_ins2usp"
#include "handg/weapon_ins2beretta"
#include "handg/weapon_ins2c96"
#include "handg/weapon_ins2m29"
#include "handg/weapon_ins2deagle"
#include "handg/weapon_ins2m1911"
#include "handg/weapon_ins2glock17"
#include "handg/weapon_ins2makarov"
#include "handg/weapon_ins2webley"
#include "handg/weapon_ins2python"
//lmgs
#include "lmg/weapon_ins2lewis"
#include "lmg/weapon_ins2rpk"
#include "lmg/weapon_ins2m249"
#include "lmg/weapon_ins2mg42"
#include "lmg/weapon_ins2m60"
//explosives/launchers
#include "explo/weapon_ins2mk2"
#include "explo/weapon_ins2rpg7"
#include "explo/weapon_ins2law"
#include "explo/weapon_ins2stick"
#include "explo/weapon_ins2pzfaust"
#include "explo/weapon_ins2m79"
#include "explo/weapon_ins2pzschreck"
#include "explo/weapon_ins2at4"
//shotguns
#include "shotg/weapon_ins2m590"
#include "shotg/weapon_ins2m1014"
#include "shotg/weapon_ins2coach"
#include "shotg/weapon_ins2ithaca"
#include "shotg/weapon_ins2saiga12"
//smgs
#include "smg/weapon_ins2mp5k"
#include "smg/weapon_ins2ump45"
#include "smg/weapon_ins2mp40"
#include "smg/weapon_ins2l2a3"
#include "smg/weapon_ins2m1928"
#include "smg/weapon_ins2mp7"
#include "smg/weapon_ins2mp18"
#include "smg/weapon_ins2ppsh41"
//carbines
#include "carbn/weapon_ins2sks"
#include "carbn/weapon_ins2m4a1"
#include "carbn/weapon_ins2aks74u"
#include "carbn/weapon_ins2c96carb"
#include "carbn/weapon_ins2mk18"
//melees
#include "melee/weapon_ins2kabar"
#include "melee/weapon_ins2knuckles"
#include "melee/weapon_ins2kukri"
//sniper rifles
#include "srifl/weapon_ins2g43"
#include "srifl/weapon_ins2mosin"
#include "srifl/weapon_ins2dragunov"

void RegisterAll()
{
	//assault rifles
	INS2_AKM::Register();
	INS2_M16A4::Register();
	INS2_GALIL::Register();
	INS2_ASVAL::Register();
	INS2_L85A2::Register();
	INS2_AK74::Register();
	INS2_AK12::Register();
	INS2_F2000::Register();
	INS2_STG44::Register();
	//battle rifles
	INS2_FNFAL::Register();
	INS2_G3A3::Register();
	INS2_M14EBR::Register();
	INS2_FG42::Register();
	//rifles
	INS2_M1GARAND::Register();
	INS2_ENFIELD::Register();
	//handguns
	INS2_USP::Register();
	INS2_M9BERETTA::Register();
	INS2_C96::Register();
	INS2_M29::Register();
	INS2_DEAGLE::Register();
	INS2_M1911::Register();
	INS2_GLOCK17::Register();
	INS2_MAKAROV::Register();
	INS2_WEBLEY::Register();
	INS2_PYTHON::Register();
	//lmgs
	INS2_LEWIS::Register();
	INS2_RPK::Register();
	INS2_M249::Register();
	INS2_MG42::Register();
	INS2_M60::Register();
	//explosives/launchers
	INS2_MK2GRENADE::Register();
	INS2_RPG7::Register();
	INS2_LAW::Register();
	INS2_M24GRENADE::Register();
	INS2_PZFAUST::Register();
	INS2_M79::Register();
	INS2_PZSCHRECK::Register();
	INS2_AT4::Register();
	//shotguns
	INS2_M590::Register();
	INS2_M1014::Register();
	INS2_COACH::Register();
	INS2_ITHACA::Register();
	INS2_SAIGA12::Register();
	//smgs
	INS2_MP5K::Register();
	INS2_UMP45::Register();
	INS2_MP40::Register();
	INS2_STERLING::Register();
	INS2_MP7::Register();
	INS2_M1928::Register();
	INS2_MP18::Register();
	INS2_PPSH41::Register();
	//carbines
	INS2_C96CARBINE::Register();
	INS2_SKS::Register();
	INS2_M4A1::Register();
	INS2_AKS74U::Register();
	INS2_MK18::Register();
	//melees
	INS2_KABAR::Register();
	INS2_KNUCKLES::Register();
	INS2_KUKRI::Register();
	//sniper rifles
	INS2_G43::Register();
	INS2_MOSIN::Register();
	INS2_SVD::Register();
}

class CBaseCustomWeapon : ScriptBasePlayerWeaponEntity
{
	// Possible workaround for the SendWeaponAnim() access violation crash.
	// According to R4to0 this seems to provide at least some improvement.
	// GeckoN: TODO: Remove this once the core issue is addressed.
	protected CBasePlayer@ m_pPlayer
	{
		get const { return cast<CBasePlayer@>( self.m_hPlayer.GetEntity() ); }
		set { self.m_hPlayer = EHandle( @value ); }
	}

	protected bool m_fDropped;
	CBasePlayerItem@ DropItem()
	{
		m_fDropped = true;
		return self;
	}

	void GetDefaultShellInfo( CBasePlayer@ pPlayer, Vector& out ShellVelocity,
		Vector& out ShellOrigin, float forwardScale, float rightScale, float upScale )
	{
		Vector vecForward, vecRight, vecUp;

		g_EngineFuncs.AngleVectors( pPlayer.pev.v_angle, vecForward, vecRight, vecUp );

		const float fR = Math.RandomFloat( 50, 70 );
		const float fU = Math.RandomFloat( 100, 150 );

		for( int i = 0; i < 3; ++i )
		{
			ShellVelocity[i] = pPlayer.pev.velocity[i] + vecRight[i] * fR + vecUp[i] * fU + vecForward[i] * 25;
			ShellOrigin[i]   = pPlayer.pev.origin[i] + pPlayer.pev.view_ofs[i] + vecUp[i] * upScale + vecForward[i] * forwardScale + vecRight[i] * rightScale;
		}
	}
}

class CBaseCustomAmmo : ScriptBasePlayerAmmoEntity
{
	protected string m_strModel = "models/error.mdl";
	protected string m_strName;
	protected int m_iAmount = 0;
	protected int m_iMax = 0;

	protected string m_strPickupSound = "items/gunpickup2.wav";

	void Precache()
	{
		g_Game.PrecacheModel( m_strModel );

		g_SoundSystem.PrecacheSound( m_strPickupSound );
	}

	void Spawn()
	{
		Precache();
		g_EntityFuncs.SetModel( self, m_strModel );
		BaseClass.Spawn();
	}

	bool AddAmmo( CBaseEntity@ pOther )
	{
		if ( pOther.GiveAmmo( m_iAmount, m_strName, m_iMax, false ) == -1 )
			return false;

		g_SoundSystem.EmitSound( self.edict(), CHAN_ITEM, m_strPickupSound, 1, ATTN_NORM );

		return true;
	}
}

class CBaseCustomMonster : ScriptBaseMonsterEntity
{
	int SF_MONSTER_GAG = 2;
	int SF_MONSTER_FADECORPSE = 512;

	Vector VecCheckToss( edict_t@& in pEdict, const Vector& in vecSpot1, Vector vecSpot2, const float flGravityAdj = 1.0 )
	{
		TraceResult	tr;
		Vector		vecMidPoint;// halfway point between Spot1 and Spot2
		Vector		vecApex;// highest point 
		Vector		vecScale;
		Vector		vecGrenadeVel;
		Vector		vecTemp;
		//float flGravity = g_EngineFuncs.CVarGetFloat( "sv_gravity" ) * flGravityAdj;
		float flGravity = 800.0f * flGravityAdj;

		if( vecSpot2.z - vecSpot1.z > 500 )
			return g_vecZero; // to high, fail

		g_EngineFuncs.MakeVectors(pev.angles);

		// toss a little bit to the left or right, not right down on the enemy's bean (head). 
		vecSpot2 = vecSpot2 + g_Engine.v_right * ( Math.RandomFloat(-8,8) + Math.RandomFloat(-16,16) );
		vecSpot2 = vecSpot2 + g_Engine.v_forward * ( Math.RandomFloat(-8,8) + Math.RandomFloat(-16,16) );

		// calculate the midpoint and apex of the 'triangle'
		// UNDONE: normalize any Z position differences between spot1 and spot2 so that triangle is always RIGHT

		// How much time does it take to get there?

		// get a rough idea of how high it can be thrown
		vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
		g_Utility.TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,500), ignore_monsters, self.edict(), tr);
		vecMidPoint = tr.vecEndPos;
		// (subtract 15 so the grenade doesn't hit the ceiling)
		vecMidPoint.z -= 15;

		if( vecMidPoint.z < vecSpot1.z || vecMidPoint.z < vecSpot2.z )
			return g_vecZero; // to not enough space, fail

		// How high should the grenade travel to reach the apex
		float distance1 = (vecMidPoint.z - vecSpot1.z);
		float distance2 = (vecMidPoint.z - vecSpot2.z);

		// How long will it take for the grenade to travel this distance
		float time1 = sqrt( distance1 / (0.5 * flGravity) );
		float time2 = sqrt( distance2 / (0.5 * flGravity) );

		if( time1 < 0.1 )
			return g_vecZero; // too close

		// how hard to throw sideways to get there in time.
		vecGrenadeVel = (vecSpot2 - vecSpot1) / (time1 + time2);
		// how hard upwards to reach the apex at the right time.
		vecGrenadeVel.z = flGravity * time1;

		// find the apex
		vecApex  = vecSpot1 + vecGrenadeVel * time1;
		vecApex.z = vecMidPoint.z;

		g_Utility.TraceLine(vecSpot1, vecApex, dont_ignore_monsters, self.edict(), tr);
		if (tr.flFraction != 1.0)
			return g_vecZero; // fail!

		// UNDONE: either ignore monsters or change it to not care if we hit our enemy
		g_Utility.TraceLine(vecSpot2, vecApex, ignore_monsters, self.edict(), tr); 
		if (tr.flFraction != 1.0)
			return g_vecZero; // fail!

		return vecGrenadeVel;
	}

	Vector VecCheckThrow( edict_t@& in pEdict, const Vector& in vecSpot1, Vector vecSpot2, const float flSpeed, const float flGravityAdj = 1.0 )
	{
		//float flGravity = g_EngineFuncs.CVarGetFloat( "sv_gravity" ) * flGravityAdj;
		float flGravity = 800.0f * flGravityAdj;

		Vector vecGrenadeVel = (vecSpot2 - vecSpot1);

		// throw at a constant time
		float time = vecGrenadeVel.Length( ) / flSpeed;
		vecGrenadeVel = vecGrenadeVel * (1.0 / time);

		// adjust upward toss to compensate for gravity loss
		vecGrenadeVel.z += flGravity * time * 0.5;

		Vector vecApex = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
		vecApex.z += 0.5 * flGravity * (time * 0.5) * (time * 0.5);

		TraceResult tr;
		g_Utility.TraceLine(vecSpot1, vecApex, dont_ignore_monsters, pEdict, tr);
		if (tr.flFraction != 1.0)
				return g_vecZero; // fail!

		g_Utility.TraceLine(vecSpot2, vecApex, ignore_monsters, pEdict, tr);
		if (tr.flFraction != 1.0)
				return g_vecZero; // fail!

		return vecGrenadeVel;
	}
}