//Converted from SOHL-V1.9-Opposing-Force-Edition-master by Nero

namespace opfor_sporelauncher
{

const float OFW_DAMAGE				= 100;
const float OFW_TIME_DELAY			= 0.43f;
const float OFW_TIME_DRAW			= 1.0f;
const int OFW_MAX_CARRY				= 1000;
const int OFW_MAX_CLIP				= 5;
const int OFW_DEFAULT_GIVE			= 5;
const int OFW_WEIGHT				= 10;
const int OFSPORE_SLOT				= 0;
const int OFSPORE_POSITION			= 0;

const string OFW_ANIMEXT			= "shotgun";

const int SF_SPRITE_TEMPORARY		= 0x8000;

int g_sModelIndexTinySpit;
int g_sModelIndexSpore1;
int g_sModelIndexSpore3;

enum ofw_e
{
	ANIM_IDLE1 = 0,
	ANIM_FIDGET,
	ANIM_RELOAD_REACH,
	ANIM_RELOAD_LOAD,
	ANIM_RELOAD_AIM,
	ANIM_FIRE,
	ANIM_HOLSTER,
	ANIM_DRAW,
	ANIM_IDLE2,
	ANIM_RELOAD_AIMFAST
};


class weapon_ofsporelauncher : CBaseCustomWeapon
{
	private int m_iChargeLevel;

	void Spawn()
	{
		Precache();
		g_EntityFuncs.SetModel( self, "models/w_spore_launcher.mdl" );
		self.m_iDefaultAmmo = OFW_MAX_CARRY;

		pev.sequence = 0;
		pev.animtime = g_Engine.time + 0.1f;
		pev.framerate = 1.0f;

		self.FallInit();
	}

	void Precache()
	{
		BaseClass.Precache();
		self.PrecacheCustomModels();

		g_Game.PrecacheModel( "models/opfor/w_spore_launcher.mdl" );
		g_Game.PrecacheModel( "models/opfor/v_spore_launcher.mdl" );
		g_Game.PrecacheModel( "models/opfor/p_spore_launcher.mdl" );

		g_sModelIndexTinySpit = g_Game.PrecacheModel( "sprites/tinyspit.spr" );
		g_sModelIndexSpore1 = g_Game.PrecacheModel( "sprites/spore_exp_01.spr" );
		g_sModelIndexSpore3 = g_Game.PrecacheModel( "sprites/spore_exp_c_01.spr" );

		g_SoundSystem.PrecacheSound( "weapons/splauncher_altfire.wav" );
		g_SoundSystem.PrecacheSound( "weapons/splauncher_bounce.wav" );
		g_SoundSystem.PrecacheSound( "weapons/splauncher_fire.wav" );
		g_SoundSystem.PrecacheSound( "weapons/splauncher_impact.wav" );
		g_SoundSystem.PrecacheSound( "weapons/splauncher_pet.wav" );
		g_SoundSystem.PrecacheSound( "weapons/splauncher_reload.wav" );

		g_Game.PrecacheOther( "opfor_spore" );
		//g_Game.PrecacheOther( "spore" );

		//Precache these for downloading
		g_Game.PrecacheGeneric( "sprites/opfor/weapon_ofsporelauncher.txt" );
		g_Game.PrecacheGeneric( "sprites/opfor/640hud7.spr" ); //from opfor
		g_Game.PrecacheGeneric( "sprites/opfor/640hudof03.spr" ); //from opfor
		g_Game.PrecacheGeneric( "sprites/opfor/640hudof04.spr" ); //from opfor
		g_Game.PrecacheGeneric( "sprites/opfor/crosshairs.spr" ); //from opfor
		g_Game.PrecacheGeneric( "sprites/opfor/ofch1.spr" ); //from opfor
	}

	bool GetItemInfo( ItemInfo& out info )
	{
		info.iMaxAmmo1		= OFW_MAX_CARRY;
		info.iMaxAmmo2		= -1;
		info.iMaxClip		= OFW_MAX_CLIP;
		info.iSlot			= OFSPORE_SLOT;
		info.iPosition		= OFSPORE_POSITION;
		info.iFlags			= 0;
		info.iWeight		= OFW_WEIGHT;

		return true;
	}

	bool AddToPlayer( CBasePlayer@ pPlayer )
	{
		if( BaseClass.AddToPlayer(pPlayer) )
		{
			@m_pPlayer = pPlayer;

			NetworkMessage m( MSG_ONE, NetworkMessages::WeapPickup, pPlayer.edict() );
				m.WriteLong( g_ItemRegistry.GetIdForName("weapon_ofsporelauncher") );
			m.End();

			return true;
		}

		return false;
	}

	bool Deploy()
	{
		bool bResult;
		{
			bResult = self.DefaultDeploy( self.GetV_Model("models/v_spore_launcher.mdl"), self.GetP_Model("models/p_spore_launcher.mdl"), ANIM_DRAW, OFW_ANIMEXT );
			self.m_flTimeWeaponIdle = self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + OFW_TIME_DRAW;

			return bResult;
		}
	}

	void Holster( int skipLocal = 0 )
	{
		self.m_fInReload = false;
		self.SendWeaponAnim( ANIM_HOLSTER );
	}

	void PrimaryAttack()
	{
		Shoot(true);
	}

	void SecondaryAttack()
	{
		Shoot(false);
	}

	void Shoot( bool bPrimaryFire )
	{
		if( self.m_iClip == 0 )
		{
			//self.PlayEmptySound();
			Reload();
			return;
		}

		if( m_iChargeLevel >= 1 )
			Charge(true);
		else
		{
			m_pPlayer.m_iWeaponVolume = LOUD_GUN_VOLUME;
			m_pPlayer.m_iWeaponFlash = NORMAL_GUN_FLASH;

			--self.m_iClip;

			Vector vecSrc = m_pPlayer.GetGunPosition();
			Vector vecAiming = m_pPlayer.GetAutoaimVector( (bPrimaryFire ? AUTOAIM_5DEGREES : AUTOAIM_2DEGREES) );
			Vector vecDir, vecVel;

			Math.MakeVectors( m_pPlayer.pev.v_angle );

			vecSrc = vecSrc + g_Engine.v_forward * 16;
			vecSrc = vecSrc + g_Engine.v_right * 8;
			vecSrc = vecSrc + g_Engine.v_up * -12;

			vecVel = g_Engine.v_forward * 600;
			vecDir = g_Engine.v_forward + g_Engine.v_right + g_Engine.v_up;
			vecDir = vecDir;

			if( bPrimaryFire )
				SpitContact( m_pPlayer.pev, vecSrc, vecVel );
			else
				SpitTimed( m_pPlayer.pev, vecSrc, vecVel, Math.RandomFloat(3, 5) );

			self.SendWeaponAnim( ANIM_FIRE );
			m_pPlayer.pev.punchangle.x = -5.0f;

			if( bPrimaryFire )
				g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "weapons/splauncher_fire.wav", VOL_NORM, ATTN_NORM, 0, 100 );
			else
				g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, "weapons/splauncher_altfire.wav", VOL_NORM, ATTN_NORM, 0, 100 );

			Vector vecSpitOffset, vecSpitDir;

			Vector forward, right, up;
			g_EngineFuncs.AngleVectors( m_pPlayer.pev.v_angle, forward, right, up );

			vecSpitDir.x = forward.x;
			vecSpitDir.y = forward.y;
			vecSpitDir.z = forward.z;

			vecSpitOffset = m_pPlayer.pev.origin;

			vecSpitOffset = vecSpitOffset + forward * 16;
			vecSpitOffset = vecSpitOffset + right * 8;
			vecSpitOffset = vecSpitOffset + up * 4;

			int iSpitModelIndex = g_EngineFuncs.ModelIndex( "sprites/tinyspit.spr" );

			NetworkMessage spitmsg( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY );
				spitmsg.WriteByte( TE_SPRITE_SPRAY );
				spitmsg.WriteCoord( vecSpitOffset.x );
				spitmsg.WriteCoord( vecSpitOffset.y );
				spitmsg.WriteCoord( vecSpitOffset.z );
				spitmsg.WriteCoord( vecSpitDir.x );
				spitmsg.WriteCoord( vecSpitDir.y );
				spitmsg.WriteCoord( vecSpitDir.z );
				spitmsg.WriteShort( iSpitModelIndex );
				spitmsg.WriteByte( 8 );
				spitmsg.WriteByte( 210 );//speed
				spitmsg.WriteByte( 25 );//noise ( client will divide by 100 )
			spitmsg.End();

			m_pPlayer.SetAnimation(PLAYER_ATTACK1);

			if( self.m_iClip == 0 && m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 )
				m_pPlayer.SetSuitUpdate( "!HEV_AMO0", false, 0 );

			self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + OFW_TIME_DELAY;

			self.m_flTimeWeaponIdle = g_Engine.time + Math.RandomFloat(10, 15);
			m_iChargeLevel = 0;
		}
	}

	void Reload()
	{
		if( (m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0 or self.m_iClip >= OFW_MAX_CLIP) or self.m_flNextPrimaryAttack > g_Engine.time or self.m_flNextSecondaryAttack > g_Engine.time )
			return;
		else
			Charge(false);
	}

	void Charge( bool m_BeginAttack )
	{
		if( (self.m_iClip != OFW_MAX_CLIP and m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) > 0) and !m_BeginAttack )
		{
			if( m_iChargeLevel == 0 )
			{
				self.SendWeaponAnim( ANIM_RELOAD_REACH );
				self.m_flTimeWeaponIdle = g_Engine.time + 0.7f;
				self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle;
				m_iChargeLevel = 1;
			}
			else if( m_iChargeLevel == 1 )
			{
				if( self.m_flTimeWeaponIdle < g_Engine.time )
				{
					m_iChargeLevel = 2;

					g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_ITEM, "weapons/splauncher_reload.wav", 1.0f, ATTN_NORM, 0, 85 + Math.RandomLong(0, 0x1f) );
					
					self.SendWeaponAnim( ANIM_RELOAD_LOAD );
					self.m_flTimeWeaponIdle = g_Engine.time + 1.0f;
					self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle;
				}
			}
			else
			{
				if( self.m_flTimeWeaponIdle < g_Engine.time )
				{
					int ammo = m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType );
					++self.m_iClip;
					--ammo;
					m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, ammo );
					m_iChargeLevel = 1;

					if( self.m_iClip == OFW_MAX_CLIP )
						self.m_flTimeWeaponIdle = g_Engine.time + 0.1f;
				}
			}
		}
		else
		{
			// reload debounce has timed out
			self.SendWeaponAnim( ANIM_RELOAD_AIM );
			m_iChargeLevel = 0;
			self.m_flTimeWeaponIdle = g_Engine.time + 0.86f;
			self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flTimeWeaponIdle;
		}
	}

	void WeaponIdle()
	{
		if( self.m_flTimeWeaponIdle > g_Engine.time and m_iChargeLevel == 0 )
			return;

		if( m_iChargeLevel != 0 )
			Charge(false);
		else
		{
			int iAnim;
			float flRand = Math.RandomFloat(0, 1);

			if( flRand <= 0.5f )
			{
				iAnim = ANIM_IDLE1;
				self.m_flTimeWeaponIdle = g_Engine.time + 2.03f;
			}
			else if( flRand <= 0.7f )
			{
				iAnim = ANIM_IDLE2;
				self.m_flTimeWeaponIdle = g_Engine.time + 4.03f;
			}
			else if( flRand <= 0.9f )
			{
				iAnim = ANIM_FIDGET;
				self.m_flTimeWeaponIdle = g_Engine.time + 4.03f;
				g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_ITEM, "weapons/splauncher_pet.wav", 0.7f, ATTN_NORM, 0, PITCH_NORM );
			}
			else
				self.m_flTimeWeaponIdle = g_Engine.time + Math.RandomFloat(10, 15);

			self.SendWeaponAnim(iAnim);
		}
	}
}

class opfor_spore : ScriptBaseMonsterEntity
{
	private CSprite@ m_pSprite;

	void Spawn()
	{
		Precache();

		pev.solid = SOLID_BBOX;

		g_EntityFuncs.SetModel( self, "models/spore.mdl" );
		g_EntityFuncs.SetSize( self.pev, Vector(-4, -4, -4), Vector(4, 4, 4) );

		g_EntityFuncs.SetOrigin( self, pev.origin );
		Math.MakeVectors(pev.angles);

		pev.gravity = 0.5f;
		pev.friction = 0.2f;
		Glow();

		if( pev.movetype == MOVETYPE_FLY )
		{
			SetThink( ThinkFunction(this.FlyThink) );
			SetTouch( TouchFunction(this.ExplodeThink) );
		}
		else
		{
			SetThink( ThinkFunction(this.FlyThink) );
			SetTouch( TouchFunction(this.BounceThink) );
		}

		pev.dmgtime = (g_Engine.time + pev.dmgtime);
		pev.dmg = OFW_DAMAGE;
		SetNextThink(0.1f);
	}

	void Precache()
	{
		g_Game.PrecacheModel( "models/spore.mdl" );
		g_Game.PrecacheModel( "sprites/glow02.spr" );

		g_SoundSystem.PrecacheSound( "weapons/splauncher_impact.wav" );
		g_SoundSystem.PrecacheSound( "weapons/spore_hit1.wav" );
		g_SoundSystem.PrecacheSound( "weapons/spore_hit2.wav" );
		g_SoundSystem.PrecacheSound( "weapons/spore_hit3.wav" );
	}

	void Glow()
	{
		if( m_pSprite is null )
		{
			@m_pSprite = g_EntityFuncs.CreateSprite( "sprites/glow02.spr", pev.origin, false ); 
			m_pSprite.SetAttachment( self.edict(), 0 );
			m_pSprite.pev.scale = 0.75f;
			m_pSprite.SetTransparency( kRenderTransAdd, 150, 158, 19, 155, kRenderFxNoDissipation );
			m_pSprite.pev.spawnflags |= SF_SPRITE_TEMPORARY;
			m_pSprite.pev.flags |= FL_SKIPLOCALHOST;
		}
	}

	void FlyThink()
	{
		TraceResult tr;
		g_Utility.TraceLine( pev.origin, pev.origin + pev.velocity * 10, dont_ignore_monsters, self.edict(), tr);

		NetworkMessage message1( MSG_PAS, NetworkMessages::SVC_TEMPENTITY, pev.origin );
			message1.WriteByte( TE_SPRITE_SPRAY );
			message1.WriteCoord( pev.origin.x + Math.RandomLong(-5, 5) );
			message1.WriteCoord( pev.origin.y + Math.RandomLong(-5, 5) );
			message1.WriteCoord( pev.origin.z + Math.RandomLong(-5, 5) );
			message1.WriteCoord( tr.vecPlaneNormal.Normalize().x );
			message1.WriteCoord( tr.vecPlaneNormal.Normalize().y );
			message1.WriteCoord( tr.vecPlaneNormal.Normalize().z );
			message1.WriteShort( g_sModelIndexTinySpit );
			message1.WriteByte( Math.RandomLong(3, 8) ); // count
			message1.WriteByte( Math.RandomLong(10, 15) ); // speed
			message1.WriteByte( Math.RandomLong(4, 9) * 100 );
		message1.End();

		NetworkMessage message2( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY );
			message2.WriteByte( TE_DLIGHT);
			message2.WriteCoord( pev.origin.x );
			message2.WriteCoord( pev.origin.y );
			message2.WriteCoord( pev.origin.z );
			message2.WriteByte( 15 );     // radius
			message2.WriteByte( 0 );		// r
			message2.WriteByte( 180 );	// g
			message2.WriteByte( 0 );	// b
			message2.WriteByte( 1 );     // life * 10
			message2.WriteByte( 0 ); // decay
		message2.End();

		if( pev.movetype == MOVETYPE_BOUNCE )
		{
			if( pev.dmgtime <= g_Engine.time )
				Explode();
		}

		SetNextThink(0.001f);
	}

	void BounceThink( CBaseEntity@ pOther )
	{
		if( (pOther.pev.flags & FL_MONSTER) != 0 or pOther.IsPlayer() )
			Explode();

		if( g_EngineFuncs.PointContents(pev.origin) == CONTENTS_SKY )
		{
			if( m_pSprite !is null )
			{
				g_EntityFuncs.Remove( m_pSprite );
				@m_pSprite = null;
			}

			g_EntityFuncs.Remove( self );
			return;
		}

		// only do damage if we're moving fairly fast
		if( self.m_flNextAttack < g_Engine.time and pev.velocity.Length() > 100 )
		{
			entvars_t@ pevOwner = pev.owner.vars;

			if( pevOwner !is null )
			{
				TraceResult tr = g_Utility.GetGlobalTrace();
				g_WeaponFuncs.ClearMultiDamage();
				pOther.TraceAttack( pevOwner, 1, g_Engine.v_forward, tr, DMG_GENERIC );
				g_WeaponFuncs.ApplyMultiDamage( pev, pevOwner );
			}

			self.m_flNextAttack = g_Engine.time + 1.0f;
		}

		Vector vecTestVelocity;
		vecTestVelocity = pev.velocity;
		vecTestVelocity.z *= 0.45f;

		if( (pev.flags & FL_ONGROUND) != 0 )
		{
			pev.velocity = pev.velocity * 0.8f;
			pev.sequence = Math.RandomLong(1, 1);
		}
		else
		{
			// play bounce sound
			switch( Math.RandomLong(0, 2) )
			{
				case 0: g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, "weapons/spore_hit1.wav", 0.25f, ATTN_NORM ); break;
				case 1: g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, "weapons/spore_hit2.wav", 0.25f, ATTN_NORM ); break;
				case 2: g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, "weapons/spore_hit3.wav", 0.25f, ATTN_NORM ); break;
			}
		}

		pev.framerate = pev.velocity.Length() / 200.0f;
		if( pev.framerate > 1.0f )
			pev.framerate = 1;
		else if( pev.framerate < 0.5f )
			pev.framerate = 0;
	}

	void ExplodeThink( CBaseEntity@ pOther )
	{
		if( g_EngineFuncs.PointContents(pev.origin) == CONTENTS_SKY )
		{
			if( m_pSprite !is null )
			{
				g_EntityFuncs.Remove( m_pSprite );
				@m_pSprite = null;
			}

			g_EntityFuncs.Remove( self );
			return;
		}

		Explode();
	}

	void Explode()
	{
		SetTouch(null);
		SetThink(null);
		g_SoundSystem.EmitSound( self.edict(), CHAN_ITEM, "weapons/splauncher_impact.wav", 1.0f, ATTN_NORM );

		NetworkMessage message1( MSG_PAS, NetworkMessages::SVC_TEMPENTITY, pev.origin );
			message1.WriteByte( TE_SPRITE );		// This makes a dynamic light and the explosion sprites/sound
			message1.WriteCoord( pev.origin.x );	// Send to PAS because of the sound
			message1.WriteCoord( pev.origin.y );
			message1.WriteCoord( pev.origin.z );
			switch( Math.RandomLong(0, 1) )
			{
				case 0:
					message1.WriteShort(g_sModelIndexSpore1);
				break;
				case 1:
				default:
					message1.WriteShort(g_sModelIndexSpore3);
				break;
			}
			message1.WriteByte( 25 ); // scale * 10
			message1.WriteByte( 155 ); // framerate
		message1.End();

		NetworkMessage message2( MSG_PAS, NetworkMessages::SVC_TEMPENTITY, pev.origin );
			message2.WriteByte( TE_EXPLOSION );		// This makes a dynamic light and the explosion sprites/sound
			message2.WriteCoord( pev.origin.x );	// Send to PAS because of the sound
			message2.WriteCoord( pev.origin.y );
			message2.WriteCoord( pev.origin.z );
			message2.WriteShort( g_sModelIndexSpore1 );
			message2.WriteByte( int((pev.dmg - 50) * 0.60f) ); // scale * 10
			message2.WriteByte( 15 ); // framerate
			message2.WriteByte( TE_EXPLFLAG_NOSOUND );
		message2.End();

		TraceResult tr;
		g_Utility.TraceLine( pev.origin, pev.origin + pev.velocity * 15, dont_ignore_monsters, self.edict(), tr );

		NetworkMessage message3( MSG_PAS, NetworkMessages::SVC_TEMPENTITY, pev.origin );
			message3.WriteByte( TE_SPRITE_SPRAY );		// This makes a dynamic light and the explosion sprites/sound
			message3.WriteCoord( pev.origin.x );	// Send to PAS because of the sound
			message3.WriteCoord( pev.origin.y );
			message3.WriteCoord( pev.origin.z );
			message3.WriteCoord( tr.vecPlaneNormal.x );
			message3.WriteCoord( tr.vecPlaneNormal.y );
			message3.WriteCoord( tr.vecPlaneNormal.z );
			message3.WriteShort( g_sModelIndexTinySpit );
			message3.WriteByte( 50 ); // count
			message3.WriteByte( 30 ); // speed
			message3.WriteByte( 64 );
		message3.End();

		/*if( g_EngineFuncs.CVarGetFloat("r_particles") > 0 )
		{
			NetworkMessage message3( MSG_ALL, gmsgParticles );
				message3.WriteShort( 0 );
				message3.WriteByte( 0 );
				message3.WriteCoord( pev.origin.x );	// Send to PAS because of the sound
				message3.WriteCoord( pev.origin.y );
				message3.WriteCoord( pev.origin.z );
				message3.WriteCoord( 0 );
				message3.WriteCoord( 0 );
				message3.WriteCoord( 0 );
				message3.WriteShort( iDefaultSporeExplosion );
			message3.End();
		}*/

		NetworkMessage message4( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, pev.origin );
			message4.WriteByte( TE_DLIGHT );
			message4.WriteCoord( pev.origin.x );	// X
			message4.WriteCoord( pev.origin.y );	// Y
			message4.WriteCoord( pev.origin.z );	// Z
			message4.WriteByte( 20 );		// radius * 0.1
			message4.WriteByte( 0 );		// r
			message4.WriteByte( 180 );		// g
			message4.WriteByte( 0 );		// b
			message4.WriteByte( 20 );		// time * 10
			message4.WriteByte( 20 );		// decay * 0.1
		message4.End();

		entvars_t@ pevOwner;
		if( pev.owner !is null )
			@pevOwner = pev.owner.vars;
		else
			@pevOwner = null;

		g_WeaponFuncs.RadiusDamage( pev.origin, self.pev, pevOwner, pev.dmg, 128, CLASS_PLAYER_BIOWEAPON, DMG_GENERIC );

		if( pev.movetype == MOVETYPE_FLY )
		{
			g_Utility.TraceLine( pev.origin, pev.origin + pev.velocity * 10, dont_ignore_monsters, self.edict(), tr );
			g_Utility.DecalTrace( tr, DECAL_SPORESPLAT1 + Math.RandomLong(0, 2) );
		}

		pev.velocity = g_vecZero;

		if( m_pSprite !is null )
		{
			g_EntityFuncs.Remove( m_pSprite );
			@m_pSprite = null;
		}

		SetNextThink(0.3f);
		g_EntityFuncs.Remove( self );
	}

	void SetNextThink( float flDelay )
	{
		pev.nextthink = g_Engine.time + flDelay;
	}
}

opfor_spore@ SpitTimed( entvars_t@ pevOwner, Vector vecStart, Vector vecVelocity, float time )
{
	CBaseEntity@ cbeSpore = g_EntityFuncs.CreateEntity( "opfor_spore", null,  false );
	opfor_spore@ pSpore = cast<opfor_spore@>(CastToScriptClass(cbeSpore));
	g_EntityFuncs.SetOrigin( pSpore.self, vecStart );
	pSpore.pev.movetype = MOVETYPE_BOUNCE;
	@pSpore.pev.owner = pevOwner.pContainingEntity;
	pSpore.pev.velocity = vecVelocity;
	pSpore.pev.velocity = pSpore.pev.velocity + g_Engine.v_forward * 700;
	pSpore.pev.angles = Math.VecToAngles( pSpore.pev.velocity );
	pSpore.pev.dmgtime = (time <= 0 ? 2.5f : time);
	pSpore.pev.sequence = Math.RandomLong(3, 6);
	pSpore.pev.framerate = 1.0f;
	g_EntityFuncs.DispatchSpawn( pSpore.self.edict() );
	//pSpore.Spawn();

	return pSpore;
}

opfor_spore@ SpitContact( entvars_t@ pevOwner, Vector vecStart, Vector vecVelocity )
{
	CBaseEntity@ cbeSpore = g_EntityFuncs.CreateEntity( "opfor_spore", null,  false );
	opfor_spore@ pSpore = cast<opfor_spore@>(CastToScriptClass(cbeSpore));
	g_EntityFuncs.SetOrigin( pSpore.self, vecStart );
	pSpore.pev.movetype = MOVETYPE_FLY;
	@pSpore.pev.owner = pevOwner.pContainingEntity;
	pSpore.pev.velocity = vecVelocity;
	pSpore.pev.velocity = pSpore.pev.velocity + g_Engine.v_forward * 1500;
	pSpore.pev.angles = Math.VecToAngles( pSpore.pev.velocity );
	g_EntityFuncs.DispatchSpawn( pSpore.self.edict() );
	//pSpore.Spawn();

	return pSpore;
}

void Register()
{
	g_CustomEntityFuncs.RegisterCustomEntity( "opfor_sporelauncher::weapon_ofsporelauncher", "weapon_ofsporelauncher" );
	g_CustomEntityFuncs.RegisterCustomEntity( "opfor_sporelauncher::opfor_spore", "opfor_spore" );
	g_ItemRegistry.RegisterWeapon( "weapon_ofsporelauncher", "custom_weapons/opfor", "sporeclip" ); //opfor_spores
}

} //namespace opfor_sporelauncher END