//Original scripts were weapon_plasmagun and weapon_aeolis from CSO weapons by Nero with help from Aperture
//Merged secondary attack from aeolis into secondary attack of this weapon by Garompa
//Then scrapped 90% of the aeolis stuff and replaced it with Four-Nines attempt at a beam
//I used weapon_teslagun from they hunger to compare and replace the instant beam with a constant beam and finetune the thing
//Added my own fixes to the beam on top
//Heavily using AI because I don't know how to code
//Sound looping fix taken from the aeolis, thanks Nero and Aperture for that
//Profit


namespace freezethrower
{
	
const int FREEZE_DEFAULT_GIVE					= 50;
const int FREEZE_MAX_CLIP 						= WEAPON_NOCLIP;
const int FREEZE_MAX_AMMO						= 200;

const float FREEZE_DAMAGE						= 20;
const float FREEZE_DAMAGE_RADIUS			= 95;
const float FREEZE_TIME_DELAY					= 0.15;
const float FREEZE_TIME_DRAW					= 0.7; 
const float FREEZE_TIME_IDLE						= 6.7;
const float FREEZE_TIME_FIRE_TO_IDLE		= 1.0;
const float FREEZE_BALL_SPEED				= 800;

const Vector2D FREEZE_RECOIL_STANDING_X	= Vector2D(-1.75, 1.75);
const Vector2D FREEZE_RECOIL_STANDING_Y	= Vector2D(0, 0);
const Vector2D FREEZE_RECOIL_DUCKING_X	= Vector2D(0, 0);
const Vector2D FREEZE_RECOIL_DUCKING_Y	= Vector2D(0, 0);

const string FREEZE_ANIMEXT						= "egon";

const string MODEL_VIEW							= "models/dn3d_r/weapons/v_freezeray.mdl";
const string MODEL_PLAYER						= "models/dn3d_r/weapons/p_freezeray.mdl";
const string MODEL_WORLD						= "models/dn3d_r/weapons/w_freezeray.mdl";

const string SPRITE_FREEZEBALL						= "models/dn3d_r/weapons/frozen_spikeball.mdl";
const string SPRITE_BALLEXPLODE					= "sprites/xspark2.spr";

//AEOLIS SHIT AND ICE BEAM STUFF -Garompa
const float FREEZE_DAMAGE_SPRAY		= 3;
const float FREEZE_DELAY_BEAM	= 0.05;
const string FREEZE_SOUND_SPRAY		= "dn3d_r/weapons/freezray.ogg";
const string FREEZE_SPRITE_SPRAY		= "sprites/xbeam1.spr";
const string FREEZE_SPRITE_SPRAY2		= "sprites/dn3d_r/snow1.spr";
const string FREEZE_SPRITE_HIT		= "sprites/animglow01.spr";
const float ANIM_SHOOT_DURATION = 1.03; // Duration of the ANIM_SHOOT animation

//BALLS OF ICE FIX
const string g_BallSpr				= "sprites/spray.spr";

enum FREEZE_e
{
	ANIM_IDLE = 0,
	ANIM_SHOOT,
	ANIM_DRAW
};

enum csowsounds_e
{
	SND_EMPTY = 0,
	SND_DRAW,
	SND_EXPLODE,
	SND_IDLE,
	SND_SHOOT
};

const array<string> pCSOWSounds =
{
	"custom_weapons/cs16/dryfire_rifle.wav",
	"dn3d_r/weapons/rpg_draw.ogg",
	"debris/impact_glass.wav",	
	"null.wav",
	"dn3d_r/sfx/freeze.ogg"
};

class weapon_freezethrower : CBaseCSOWeapon
{
	//AEOLIS SHIT  -Garompa
	private int m_iInAttack, m_iAnimate;
	
	//NEW BEAM ATTACK STUFF  -Garompa
	private bool m_bBeamActive = false; // Track if the beam is active
	private bool m_bSecondaryHeld = false; // Track if the secondary fire button is held down
	
    private CBeam@ pBeam; // Persistent beam entity
	private CSprite@ pEndSprite; // Effect sprite at the beam's end
    private float m_flNextAnimTime = 0.0; // Track when to restart the animation
	private float m_flNextAmmoConsumeTime = 0.0; // Track when to consume ammo next
	
	void Spawn()
	{
		self.Precache();
		g_EntityFuncs.SetModel( self, MODEL_WORLD );
		self.m_iDefaultAmmo = FREEZE_DEFAULT_GIVE;

		//AEOLIS SHIT  -Garompa
		m_iInAttack = 0;
		m_iAnimate = 0;
		
		//BEAM SHIT  -Garompa
		m_bSecondaryHeld = false;
		@pBeam = null; // Initialize beam entity
		@pEndSprite = null; // Initialize sprite to null

		self.FallInit();
	}

	void Precache()
	{
		self.PrecacheCustomModels();

		uint i;

		//g_Game.PrecacheModel( MODEL_VIEW );
		//g_Game.PrecacheModel( MODEL_PLAYER );
		//g_Game.PrecacheModel( MODEL_WORLD );
		//g_Game.PrecacheModel( SPRITE_FREEZEBALL );
		//g_Game.PrecacheModel( SPRITE_BALLEXPLODE );
		
		for( i = 0; i < pCSOWSounds.length(); ++i )
			g_SoundSystem.PrecacheSound( pCSOWSounds[i] );

		//Precache these for downloading
		for( i = 0; i < pCSOWSounds.length(); ++i )
			g_Game.PrecacheGeneric( "sound/" + pCSOWSounds[i] );

		//g_Game.PrecacheGeneric( "sprites/dn3d_r/weapons/weapon_freezethrower.txt" );
		//g_Game.PrecacheGeneric( "sprites/dn3d_r/weapons/freezeray.spr" );
		
		//AEOLIS SHIT
		//g_Game.PrecacheModel( FREEZE_SPRITE_SPRAY );
		//g_Game.PrecacheModel( FREEZE_SPRITE_SPRAY2 );
		//g_Game.PrecacheModel( FREEZE_SPRITE_HIT );
		//g_SoundSystem.PrecacheSound( FREEZE_SOUND_SPRAY );
		//g_Game.PrecacheGeneric( "sound/" + FREEZE_SOUND_SPRAY );
		
		//BALLS OF ICE
		//g_Game.PrecacheModel( g_BallSpr );
		
		//PRECACHE ALL RAW SO RESGUY CAN PACK THIS CRAP AUTOMATICALLY
		g_SoundSystem.PrecacheSound( "dn3d_r/weapons/freezray.ogg" );
		g_SoundSystem.PrecacheSound( "custom_weapons/cs16/dryfire_rifle.wav" );
		g_SoundSystem.PrecacheSound( "debris/impact_glass.wav" );
		g_SoundSystem.PrecacheSound( "null.wav" );
		g_SoundSystem.PrecacheSound( "dn3d_r/weapons/freezray.ogg" );
		g_SoundSystem.PrecacheSound( "dn3d_r/sfx/freeze.ogg" );
		g_Game.PrecacheGeneric( "sound/dn3d_r/weapons/freezray.ogg" );
		
		g_Game.PrecacheModel( "sprites/xbeam1.spr" );
		g_Game.PrecacheModel( "sprites/dn3d_r/snow1.spr" );
		g_Game.PrecacheModel( "sprites/animglow01.spr" );
		g_Game.PrecacheModel( "sprites/xspark2.spr" );
		
		g_Game.PrecacheModel( "models/dn3d_r/weapons/v_freezeray.mdl" );
		g_Game.PrecacheModel( "models/dn3d_r/weapons/p_freezeray.mdl" );
		g_Game.PrecacheModel( "models/dn3d_r/weapons/w_freezeray.mdl" );
		g_Game.PrecacheModel( "models/dn3d_r/weapons/frozen_spikeball.mdl" );
		
		g_Game.PrecacheGeneric( "sprites/dn3d_r/weapons/freezeray.spr" );
		g_Game.PrecacheGeneric( "sprites/dn3d_r/weapons/weapon_freezethrower.txt" );
		
	}

	bool GetItemInfo( ItemInfo& out info )
	{
		info.iMaxAmmo1 	= FREEZE_MAX_AMMO;
		info.iMaxClip 		= FREEZE_MAX_CLIP;
		info.iSlot			= 5;
		info.iPosition		= 5;
		info.iWeight		= 20;

		return true;
	}

	bool AddToPlayer( CBasePlayer@ pPlayer )
	{
		if( !BaseClass.AddToPlayer(pPlayer) )
			return false;

		@m_pPlayer = pPlayer;

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

		if( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 )
			m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, FREEZE_DEFAULT_GIVE );

		return true;
	}

	bool PlayEmptySound()
	{
		if( self.m_bPlayEmptySound )
		{
			self.m_bPlayEmptySound = false;
			g_SoundSystem.EmitSound( m_pPlayer.edict(), CHAN_WEAPON, pCSOWSounds[SND_EMPTY], VOL_NORM, ATTN_NORM );
		}

		return false;
	}

	bool Deploy()
	{
		bool bResult;
		{
			bResult = self.DefaultDeploy( self.GetV_Model(MODEL_VIEW), self.GetP_Model(MODEL_PLAYER), ANIM_DRAW, FREEZE_ANIMEXT, 0, (m_bSwitchHands ? g_iCSOWHands : 0) );
			self.m_flTimeWeaponIdle = self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flNextTertiaryAttack = g_Engine.time + FREEZE_TIME_DRAW;
			g_SoundSystem.EmitSound( m_pPlayer.edict(), CHAN_STATIC, pCSOWSounds[SND_IDLE], 0.5, ATTN_NORM );

			return bResult;
		}
	}

	void UpdateOnRemove()
	{
		g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_STATIC, pCSOWSounds[SND_IDLE] );

		BaseClass.UpdateOnRemove();
	}

	void PrimaryAttack()
	{	
		if( self.pev.iuser1 == 1)
        {
		    Deploy();
			return;
        }
		
		// OLD WAY, CANNOT SHOOT UNDERWATER
		/*
		if ( m_pPlayer.pev.waterlevel == WATERLEVEL_HEAD or m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 )
		{
			self.PlayEmptySound();
			self.m_flNextPrimaryAttack = g_Engine.time + 0.2f;
			return;
		}
		*/
		
		// NEW WAY, CAN SHOOT UNDERWATER
		if ( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 )
		{
			self.PlayEmptySound();
			self.m_flNextPrimaryAttack = g_Engine.time + 0.2f;
			return;
		}

		m_pPlayer.m_iWeaponVolume = NORMAL_GUN_VOLUME;
		m_pPlayer.m_iWeaponFlash = BRIGHT_GUN_FLASH; //Doesn't do anything?

		m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) - 1 );

		g_SoundSystem.EmitSound( m_pPlayer.edict(), CHAN_WEAPON, pCSOWSounds[SND_SHOOT], 1, ATTN_NORM );

		self.SendWeaponAnim( Math.RandomLong(ANIM_SHOOT, ANIM_SHOOT), 0, (m_bSwitchHands ? g_iCSOWHands : 0) );
		m_pPlayer.SetAnimation( PLAYER_ATTACK1 );

		Vector2D vec2dRecoilX = (m_pPlayer.pev.flags & FL_DUCKING != 0) ? FREEZE_RECOIL_DUCKING_X : FREEZE_RECOIL_STANDING_X;
		Vector2D vec2dRecoilY = (m_pPlayer.pev.flags & FL_DUCKING != 0) ? FREEZE_RECOIL_DUCKING_Y : FREEZE_RECOIL_STANDING_Y;

		m_pPlayer.pev.punchangle.x = Math.RandomFloat( vec2dRecoilX.x, vec2dRecoilX.y );
		m_pPlayer.pev.punchangle.y = Math.RandomFloat( vec2dRecoilY.x, vec2dRecoilY.y );

		self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = self.m_flNextTertiaryAttack = g_Engine.time + FREEZE_TIME_DELAY;
		self.m_flTimeWeaponIdle = g_Engine.time + FREEZE_TIME_FIRE_TO_IDLE;

		m_pPlayer.pev.effects = int(m_pPlayer.pev.effects) | EF_MUZZLEFLASH;

		Vector vecOrigin = m_pPlayer.GetGunPosition() + g_Engine.v_forward * 48 + g_Engine.v_right * 10 + g_Engine.v_up * -5;

		Math.MakeVectors( m_pPlayer.pev.v_angle + m_pPlayer.pev.punchangle );
		Vector vecVelocity = g_Engine.v_forward * FREEZE_BALL_SPEED;

		//If the player is moving, increase the spread
		if( m_pPlayer.pev.velocity.Length() > 0.0 )
			vecVelocity = vecVelocity + g_Engine.v_right * Math.RandomFloat(-16, 16) + g_Engine.v_up * Math.RandomFloat(-16, 16);
		else
			vecVelocity = vecVelocity + g_Engine.v_right * Math.RandomFloat(-8, 8) + g_Engine.v_up * Math.RandomFloat(-8, 8);

		dictionary keys;
		keys[ "origin" ] = vecOrigin.ToString();
		keys[ "velocity" ] = vecVelocity.ToString();

		CBaseEntity@ pFreeze = g_EntityFuncs.CreateEntity( "freezeball", keys, false );
		@pFreeze.pev.owner = m_pPlayer.edict();

		g_EntityFuncs.DispatchSpawn( pFreeze.edict() );
	}
	
    void SecondaryAttack()
    {
        if (m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) < 1)
        {
            self.PlayEmptySound();
            self.m_flNextSecondaryAttack = g_Engine.time + ANIM_SHOOT_DURATION;
            return;
        }
		
		// Check if the beam is already active
		if (!m_bBeamActive)
		{
			m_bBeamActive = true;
			g_SoundSystem.EmitSoundDyn(m_pPlayer.edict(), CHAN_STATIC, FREEZE_SOUND_SPRAY, 0.95, ATTN_NORM, SND_FORCE_LOOP, 95 + Math.RandomLong( 0, 10 ) );
		}
	
        // Start the beam attack if the button is not already held down
        if (!m_bSecondaryHeld)
        {
            m_bSecondaryHeld = true;
            m_bBeamActive = true;
            self.m_flNextSecondaryAttack = g_Engine.time + 0.1;
            self.m_flTimeWeaponIdle = g_Engine.time + 0.1;
			
            // Start thinking to update the beam
            self.pev.nextthink = g_Engine.time + FREEZE_DELAY_BEAM; // Update every 0.05 seconds
            SetThink(ThinkFunction(this.BeamThink));

            // Start the ANIM_SHOOT animation
            self.SendWeaponAnim(ANIM_SHOOT, 0, (m_bSwitchHands ? g_iCSOWHands : 0));
            m_flNextAnimTime = g_Engine.time + ANIM_SHOOT_DURATION; // Restart animation every 0.5 seconds
        }
    }
	
	void snow_trails(Vector start, Vector end, string sprite = FREEZE_SPRITE_SPRAY2, uint8 count = 2, uint8 life = 0, uint8 scale = 1, uint8 speed = 16, uint8 speedNoise = 8)
	{
		NetworkMessage m(MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY);
		m.WriteByte(TE_SPRITETRAIL);
		m.WriteCoord(start.x);
		m.WriteCoord(start.y);
		m.WriteCoord(start.z);
		m.WriteCoord(end.x);
		m.WriteCoord(end.y);
		m.WriteCoord(end.z);
		m.WriteShort(g_EngineFuncs.ModelIndex(sprite));
		m.WriteByte(count);
		m.WriteByte(life);
		m.WriteByte(scale);
		m.WriteByte(speedNoise);
		m.WriteByte(speed);
		m.End();
	}

    void BeamThink()
    {
        if (!m_bBeamActive)
        {
            // Stop thinking if the beam is no longer active
            self.pev.nextthink = 0;
            RemoveBeam();
            return;
        }

        // Restart the ANIM_SHOOT animation at regular intervals
        if (g_Engine.time >= m_flNextAnimTime)
        {
            self.SendWeaponAnim(ANIM_SHOOT, 0, (m_bSwitchHands ? g_iCSOWHands : 0));
            m_flNextAnimTime = g_Engine.time + ANIM_SHOOT_DURATION; // Restart animation every 0.5 seconds
        }
		
        // Get the player's gun position and adjust for the muzzle
        Vector vecSrc = m_pPlayer.GetGunPosition();
        Math.MakeVectors(m_pPlayer.pev.v_angle); // Ensure vectors are updated

        // Adjust the beam origin to align with the weapon's muzzle
        vecSrc = vecSrc + g_Engine.v_forward * 32 + g_Engine.v_right * 8 + g_Engine.v_up * -12;

        // Calculate the beam end point
        Vector vecEnd = vecSrc + m_pPlayer.GetAutoaimVector(0) * 2048;

        TraceResult tr;
        g_Utility.TraceLine(vecSrc, vecEnd, dont_ignore_monsters, m_pPlayer.edict(), tr);

        // Create or update the beam entity
        if (pBeam is null)
        {
            @pBeam = g_EntityFuncs.CreateBeam(FREEZE_SPRITE_SPRAY, 4); // Use your beam sprite
            pBeam.SetBrightness(255);
            pBeam.SetFlags(0);
            pBeam.pev.spawnflags |= SF_BEAM_TEMPORARY | SF_BEAM_SPARKSTART | SF_BEAM_SPARKEND;
            @pBeam.pev.owner = @m_pPlayer.edict();
            pBeam.SetScrollRate(Math.RandomLong(90, 128));
            pBeam.SetNoise(4);
			pBeam.SetColor(150, 250, 255);
			pBeam.SetWidth(80);
			pBeam.SetEndAttachment(1);
        }

        // Update the beam's start and end positions
        pBeam.SetStartPos(vecSrc);
        pBeam.SetEndPos(tr.vecEndPos);
		
		// Create an effect sprite at the beam's end
		if (pEndSprite is null)
		{
			@pEndSprite = g_EntityFuncs.CreateSprite(FREEZE_SPRITE_HIT, tr.vecEndPos, true); // Use your sprite
			pEndSprite.SetTransparency(kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone); // Set transparency and color
			pEndSprite.SetScale(1); // Adjust sprite scale
			pEndSprite.pev.framerate = 24; // Increase animation speed
			pEndSprite.pev.rendermode = kRenderGlow; // Set glow render mode
			pEndSprite.pev.renderamt = 255; // Ensure full brightness
		}
		else
		{
			// Update the sprite's position to the beam's end point
			pEndSprite.pev.origin = tr.vecEndPos;
		}
		
		// Create a sprite trail at the beam's end point
		if (tr.flFraction < 1.0) // If the beam hits something
		{
			// Create a sprite trail
			snow_trails(
				tr.vecEndPos, // Start position (beam's end point)
				tr.vecEndPos + Vector(0, 0, 10), // End position (slightly above the beam's end point)
				FREEZE_SPRITE_SPRAY2, // Sprite to use (replace with your desired sprite)
				1, // Number of sprites to spawn
				1, // Life time (seconds * 0.1)
				4, // Sprite scale (scale * 0.1)
				8, // Initial speed
				24 // Speed noise (randomization)
			);
		}

        if (tr.flFraction < 1.0)
        {
            CBaseEntity@ pHit = g_EntityFuncs.Instance(tr.pHit);
            if (pHit !is null && pHit.pev.takedamage != DAMAGE_NO)
            {
                g_WeaponFuncs.ClearMultiDamage();
                pHit.TraceAttack(m_pPlayer.pev, FREEZE_DAMAGE_SPRAY, vecEnd - vecSrc, tr, DMG_FREEZE);
                g_WeaponFuncs.ApplyMultiDamage(m_pPlayer.pev, m_pPlayer.pev);
            }
        }

        // Consume ammo over time
		if (g_Engine.time >= m_flNextAmmoConsumeTime)
		{
			m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType, m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) - 1);
			m_flNextAmmoConsumeTime = g_Engine.time + 0.2; // Consume ammo every 0.2 seconds
		}

        // Stop the beam if out of ammo
        if (m_pPlayer.m_rgAmmo(self.m_iPrimaryAmmoType) <= 0)
        {
            m_bBeamActive = false;
            m_bSecondaryHeld = false;
            self.pev.nextthink = 0; // Stop thinking
            RemoveBeam();
            return;
        }

        // Continue thinking
        self.pev.nextthink = g_Engine.time + FREEZE_DELAY_BEAM;
    }

    void RemoveBeam()
    {
        if (pBeam !is null)
        {
            g_EntityFuncs.Remove(pBeam);
            @pBeam = null;
        }

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

	void Holster( int skipLocal = 0 )
	{
		g_SoundSystem.StopSound( m_pPlayer.edict(), CHAN_STATIC, pCSOWSounds[SND_IDLE] );

		// Stop the beam when holstering
        m_bSecondaryHeld = false;
        self.pev.nextthink = 0; // Stop thinking
        RemoveBeam();
        BaseClass.Holster(skipLocal);
	}

	void WeaponIdle()
	{
		self.ResetEmptySound();
		
		
		if( m_iInAttack > 0 )
		{
			self.m_flNextPrimaryAttack = g_Engine.time + 0.1f;
			self.m_flTimeWeaponIdle = g_Engine.time + 0.1f;

			m_iInAttack = 0;
			m_iAnimate = 0;

			return;
		}
		
		
		if( self.m_flTimeWeaponIdle > g_Engine.time )
			return;
			
		// Stop the beam if the player is idle
        m_bBeamActive = false;
        m_bSecondaryHeld = false;
        self.pev.nextthink = 0; // Stop thinking
        RemoveBeam();

		self.SendWeaponAnim( ANIM_IDLE, 0, (m_bSwitchHands ? g_iCSOWHands : 0) );
		self.m_flTimeWeaponIdle = g_Engine.time + FREEZE_TIME_IDLE;
	}
	
	    void ItemPostFrame()
    {
        // Check if the secondary fire button is released
        if ((m_pPlayer.pev.button & IN_ATTACK2) == 0 && m_bSecondaryHeld)
        {
			g_SoundSystem.StopSound(m_pPlayer.edict(), CHAN_STATIC, FREEZE_SOUND_SPRAY);
            m_bSecondaryHeld = false;
            m_bBeamActive = false;
            self.pev.nextthink = 0; // Stop thinking
            RemoveBeam();
        }

        BaseClass.ItemPostFrame();
    }
}

class freezeball : ScriptBaseEntity
{
	void Spawn()
	{
		self.Precache();

		g_EntityFuncs.SetModel( self, SPRITE_FREEZEBALL );
		g_EntityFuncs.SetSize( self.pev, Vector(-1, -1, -1), Vector(1, 1, 1) );
		g_EntityFuncs.SetOrigin( self, self.pev.origin );

		pev.movetype		= MOVETYPE_FLY;
		pev.solid			= SOLID_BBOX;
		pev.scale			= Math.RandomFloat( 1.8, 2.1 );
		pev.dmg				= FREEZE_DAMAGE;
		
		pev.sequence = 0; // Use the first animation sequence
		pev.frame = 0; // Start at frame 0
		pev.framerate = 1.0; // Normal playback speed
		

		self.pev.nextthink = g_Engine.time + 0.1f;
		SetThink(ThinkFunction(this.BallThink));
		SetTouch( TouchFunction(this.FreezeBallTouch) );
	}

		void BallThink()
	{
		self.pev.nextthink = g_Engine.time + 0.03f;

		int r = 55, g = 255, b = 255;
		
		NetworkMessage glowdl( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY );
			glowdl.WriteByte( TE_DLIGHT );
			glowdl.WriteCoord( self.pev.origin.x );
			glowdl.WriteCoord( self.pev.origin.y );
			glowdl.WriteCoord( self.pev.origin.z );
			glowdl.WriteByte( 12 );//radius
			glowdl.WriteByte( int(r) );
			glowdl.WriteByte( int(g) );
			glowdl.WriteByte( int(b) );
			glowdl.WriteByte( 4 );//life
			glowdl.WriteByte( 128 );//decay
		glowdl.End();

		self.pev.frame += 1.0f;
		self.pev.scale += 0.09f;
		
		if( self.pev.frame > 31 )
		{
			self.pev.frame = 0;
			Explode();
	
			return;
		}
	}
	
	void FreezeBallTouch( CBaseEntity@ pOther )
	{
		if( g_EngineFuncs.PointContents( pev.origin ) == CONTENTS_SKY )
		{
			g_EntityFuncs.Remove( self );
			return;
		}

		if( pOther.edict() is pev.owner or pOther.pev.classname == "freezeball" )
			return;

		Explode();
	}

	void Explode()
	{
		Vector vecOrigin = pev.origin;

		NetworkMessage m1( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY );
			m1.WriteByte( TE_WORLDDECAL );
			m1.WriteCoord( vecOrigin.x );
			m1.WriteCoord( vecOrigin.y );
			m1.WriteCoord( vecOrigin.z );
			m1.WriteByte( Math.RandomLong(11, 13) );
		m1.End();

		TraceResult tr;
		vecOrigin = pev.origin - pev.velocity.Normalize() * 32;
		g_Utility.TraceLine( vecOrigin, vecOrigin + pev.velocity.Normalize() * 64, ignore_monsters, self.edict(), tr );

		// Pull out of the wall a bit
		if( tr.flFraction != 1.0f )
			vecOrigin = tr.vecEndPos + (tr.vecPlaneNormal * (pev.dmg - 12) * 0.6f);

		NetworkMessage m2( MSG_BROADCAST, NetworkMessages::SVC_TEMPENTITY );
			m2.WriteByte( TE_EXPLOSION );
			m2.WriteCoord( vecOrigin.x );
			m2.WriteCoord( vecOrigin.y );
			m2.WriteCoord( vecOrigin.z );
			m2.WriteShort( g_EngineFuncs.ModelIndex(SPRITE_BALLEXPLODE) );
			m2.WriteByte( 12 ); //scale
			m2.WriteByte( 30 ); //framerate
			m2.WriteByte( TE_EXPLFLAG_NODLIGHTS | TE_EXPLFLAG_NOSOUND );
		m2.End();

		g_SoundSystem.EmitSound( self.edict(), CHAN_BODY, pCSOWSounds[SND_EXPLODE], VOL_NORM, ATTN_NORM );
		g_WeaponFuncs.RadiusDamage( vecOrigin, self.pev, pev.owner.vars, pev.dmg, FREEZE_DAMAGE_RADIUS, CLASS_PLAYER_ALLY, DMG_FREEZE | DMG_NEVERGIB );
			
		g_EntityFuncs.Remove( self );
	}
}


void Register()
{
	g_CustomEntityFuncs.RegisterCustomEntity( "freezethrower::freezeball", "freezeball" );
	g_CustomEntityFuncs.RegisterCustomEntity( "freezethrower::weapon_freezethrower", "weapon_freezethrower" );
	g_ItemRegistry.RegisterWeapon( "weapon_freezethrower", "dn3d_r/weapons", "uranium" );
}

} //namespace freezethrower END