Class Tier4Ammo : RandomSpawner2 replaces ClipBox
{
	Default
	{
		DropItem "PulseAmmo", 255, 1;
		DropItem "RipperAmmo", 255, 1;
	}
}

Class Tier4Weapon : RandomSpawner2 replaces Chaingun
{
	Default
	{
		DropItem "PulseGun", 255, 1;
		DropItem "Ripper2", 255, 1;
	}
}

Class PulseAmmo : Ammo
{
	Default
	{
		Tag "Pulse Cell";
		Inventory.PickupMessage "You picked up a Pulse Cell.";
		Inventory.Amount 25;
		Inventory.MaxAmount 199;
		Ammo.BackpackAmount 50;
		Ammo.BackpackMaxAmount 199;
		Ammo.DropAmount 10;
	}
	States
	{
	Spawn:
		PAMO A -1;
		Stop;
	}
}

Class PulseBallLight : DynamicLight
{
	double pulseofs;
	Default
	{
		DynamicLight.Type "Point";
		Args 64,255,0,10;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		pulseofs = FRandom[Pulse](0,360);
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target || target.InStateSequence(target.CurState,target.ResolveState("Death")) )
		{
			Destroy();
			return;
		}
		SetOrigin(target.pos,true);
		args[LIGHT_INTENSITY] = 10+sin(gametic*80+pulseofs)*5;
	}
}

Class PulseExplLight : DynamicLight
{
	double lifetime;
	Default
	{
		DynamicLight.Type "Point";
		ReactionTime 20;
		Args 64,255,0,30;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		lifetime = 1.0;
	}
	override void Tick()
	{
		Super.Tick();
		if ( globalfreeze || level.frozen ) return;
		args[LIGHT_RED] = 64*lifetime;
		args[LIGHT_GREEN] = 255*lifetime;
		lifetime -= 1./ReactionTime;
		if ( lifetime <= 0 ) Destroy();
	}
}

Class PulseBall : Actor
{
	Default
	{
		Obituary "%o ate %k's burning plasma death.";
		DamageType 'Pulsed';
		RenderStyle "Add";
		DamageFunction Random[Pulse](15,25);
		PROJECTILE;
		Scale 0.2;
		Speed 30;
		Radius 2;
		Height 0;
	}
	override void PostBeginPlay()
	{
		A_PlaySound("pulse/fly",CHAN_BODY,0.8,true,8.0);
		let l = Spawn("PulseBallLight",pos);
		l.target = self;
	}
	action void A_BallExp()
	{
		A_SetScale(0.45);
		A_PlaySound("pulse/hit",CHAN_BODY);
		A_SprayDecal("BoltScorch");
		Spawn("PulseExplLight",pos);
		int numpt = Random[Pulse](20,40);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Pulse](-1,1),FRandom[Pulse](-1,1),FRandom[Pulse](-1,1)).unit()*FRandom[Pulse](2,4);
			A_SpawnParticle("FFFFFF",SPF_FULLBRIGHT,Random[Pulse](20,40),FRandom[Pulse](2.4,4.8),0,0,0,0,pvel.x,pvel.y,pvel.z,0,0,0,1,-1,-0.1);
			A_SpawnParticle("40FF00",SPF_FULLBRIGHT,Random[Pulse](30,50),FRandom[Pulse](4.8,7.2),0,0,0,0,pvel.x,pvel.y,pvel.z,0,0,0,.5,-1,-0.1);
		}
	}
	action void A_Trail()
	{
		if ( Random[Pulse](0,4) ) return;
		int numpt = Random[Pulse](2,5);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Pulse](-1,1),FRandom[Pulse](-1,1),FRandom[Pulse](-1,1)).unit()*FRandom[Pulse](2,4);
			A_SpawnParticle("FFFFFF",SPF_FULLBRIGHT,Random[Pulse](10,20),FRandom[Pulse](1.2,2.4),0,0,0,0,pvel.x,pvel.y,pvel.z,0,0,0,1,-1,-0.1);
			A_SpawnParticle("40FF00",SPF_FULLBRIGHT,Random[Pulse](15,25),FRandom[Pulse](2.4,3.6),0,0,0,0,pvel.x,pvel.y,pvel.z,0,0,0,.5,-1,-0.1);
		}
	}
	States
	{
	Spawn:
		PBAL ABCDE 1 Bright A_Trail();
		Loop;
	Death:
		TNT1 A 0 A_BallExp();
		PBAL ABCDE 3 Bright;
		Stop;
	}
}

Class PulseBoltLight : DynamicLight
{
	bool lived;
	Default
	{
		DynamicLight.Type "Point";
		Args 32,128,0,40;
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target )
		{
			Destroy();
			return;
		}
		SetOrigin(target.pos,true);
		args[LIGHT_INTENSITY] = Random[Pulse](30,50);
	}
}

Class PulseBoltTracer : LineTracer
{
	Actor ignore;

	override ETraceStatus TraceCallback()
	{
		if ( Results.HitType == TRACE_HitActor )
		{
			if ( Results.HitActor == ignore ) return TRACE_Skip;
			if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
			return TRACE_Skip;
		}
		else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
		{
			if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BlockHitscan) )
				return TRACE_Stop;
			return TRACE_Skip;
		}
		return TRACE_Stop;
	}
}

Class PulseBoltCap : Actor
{
	Default
	{
		RenderStyle "Add";
		Radius 0.1;
		Height 0;
		+NOGRAVITY;
		+NOCLIP;
		+DONTSPLASH;
		Scale 0.15;
	}
	States
	{
	Spawn:
		PCAP ABCD 1 Bright;
		Loop;
	}
}

Class PulseBoltHit : Actor
{
	Default
	{
		RenderStyle "Add";
		Radius 0.1;
		Height 0;
		+NOGRAVITY;
		+NOCLIP;
		+DONTSPLASH;
		Scale 0.15;
	}
	States
	{
	Spawn:
		PHIT ABCD 1 Bright;
		Loop;
	}
}

Class PulseBolt : Actor
{
	const beamsize = 40.5;

	PulseBoltTracer t;
	double accdamage;
	int lasthit;
	Actor damagedactor;
	Actor weffect;
	int position;
	PulseBolt next;

	override void OnDestroy()
	{
		Super.OnDestroy();
		if ( next ) next.Destroy();
		if ( weffect ) weffect.Destroy();
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		t = new("PulseBoltTracer");
		t.ignore = target;
		let l = Spawn("PulseBoltLight",pos);
		l.target = self;
	}
	void CheckBeam( Vector3 x )
	{
		t.Trace(pos,cursector,x,beamsize,0);
		if ( t.Results.HitType != TRACE_HitNone )
		{
			if ( t.Results.HitType == TRACE_HitActor )
			{
				if ( !damagedactor )
				{
					accdamage = min(0.5*(level.time-lasthit),0.1);
					t.Results.HitActor.DamageMobj(self,target,Random[Pulse](65,75)*accdamage,'zapped');
					accdamage = 0;
				}
				else if ( t.Results.HitActor != damagedactor )
				{
					t.Results.HitActor.DamageMobj(self,target,Random[Pulse](65,75)*accdamage,'zapped');
					accdamage = 0;
				}
				lasthit = level.time;
				damagedactor = t.Results.HitActor;
				accdamage += 1./TICRATE;
				if ( accdamage > 0.22 )
				{
					t.Results.HitActor.DamageMobj(self,target,Random[Pulse](65,75)*accdamage,'zapped');
					accdamage = 0;
				}
			}
			Vector3 origin = Level.Vec3Diff(pos,t.Results.HitPos);
			int numpt = Random[Pulse](10,20)*!Random[Pulse](0,2);
			for ( int i=0; i<numpt; i++ )
			{
				Vector3 pvel = ((FRandom[Pulse](-.4,.5),FRandom[Pulse](-.4,.4),FRandom[Pulse](-.4,.4))-x).unit()*FRandom[Pulse](2,4);
				A_SpawnParticle("FFFFFF",SPF_FULLBRIGHT,Random[Pulse](20,40),FRandom[Pulse](2.4,4.8),0,origin.x,origin.y,origin.z,pvel.x,pvel.y,pvel.z,0,0,0,1,-1,-0.1);
				A_SpawnParticle("40FF00",SPF_FULLBRIGHT,Random[Pulse](30,50),FRandom[Pulse](4.8,7.2),0,origin.x,origin.y,origin.z,pvel.x,pvel.y,pvel.z,0,0,0,.5,-1,-0.1);
			}
			if ( weffect && (weffect is 'PulseBoltCap') )
			{
				weffect.Destroy();
				weffect = null;
			}
			if ( !weffect ) weffect = Spawn("PulseBoltHit",t.Results.HitPos-t.Results.HitVector);
			else weffect.SetOrigin(t.Results.HitPos-t.Results.HitVector,true);
			A_SprayDecal("BoltScorch",beamsize+8);
			if ( next )
			{
				accdamage += next.accdamage;
				next.Destroy();
				next = null;
			}
			return;
		}
		else if ( damagedactor )
		{
			damagedactor.DamageMobj(self,target,Random[Pulse](65,75)*accdamage,'zapped');
			accdamage = 0;
			damagedactor = null;
		}
		Vector3 origin = Level.Vec3Diff(pos,t.Results.HitPos);
		if ( position >= 19 )
		{
			int numpt = Random[Pulse](5,10)*!Random[Pulse](0,5);
			for ( int i=0; i<numpt; i++ )
			{
				Vector3 pvel = (t.Results.HitVector+(FRandom[Pulse](-.5,.5),FRandom[Pulse](-.5,.5),FRandom[Pulse](-.5,.5))).unit()*FRandom[Pulse](2,4);
				A_SpawnParticle("FFFFFF",SPF_FULLBRIGHT,Random[Pulse](20,40),FRandom[Pulse](2.4,4.8),0,origin.x,origin.y,origin.z,pvel.x,pvel.y,pvel.z,0,0,0,1,-1,-0.1);
				A_SpawnParticle("40FF00",SPF_FULLBRIGHT,Random[Pulse](30,50),FRandom[Pulse](4.8,7.2),0,origin.x,origin.y,origin.z,pvel.x,pvel.y,pvel.z,0,0,0,.5,-1,-0.1);
			}
			if ( weffect && (weffect is 'PulseBoltHit') )
			{
				weffect.Destroy();
				weffect = null;
			}
			if ( !weffect ) weffect = Spawn("PulseBoltCap",t.Results.HitPos-t.Results.HitVector);
			else weffect.SetOrigin(t.Results.HitPos-t.Results.HitVector,true);
		}
		else
		{
			if ( weffect )
			{
				weffect.Destroy();
				weffect = null;
			}
			if ( !next )
			{
				next = PulseBolt(Spawn("PulseBolt",t.Results.HitPos));
				next.angle = angle;
				next.pitch = pitch;
				next.target = target;
				next.position = position+1;
			}
			else next.UpdateBeam(self,x);
		}
	}
	void UpdateBeam( PulseBolt parent, Vector3 x )
	{
		frame = parent.frame;
		SetOrigin(parent.Vec3Offset(x.x*beamsize,x.y*beamsize,x.z*beamsize),true);
		A_SetAngle(parent.angle);
		A_SetPitch(parent.pitch);
		CheckBeam(x);
	}
	Default
	{
		RenderStyle "Add";
		Obituary "%o ate %k's burning plasma death.";
		Radius 0.1;
		Height 0;
		+NOGRAVITY;
		+NOCLIP;
		+DONTSPLASH;
		+INTERPOLATEANGLES;
	}
	States
	{
	Spawn:
		PBLT # -1;
		Stop;
	Dummy:
		PBLT ABCDE -1;
		Stop;
	}
}

Class StarterBolt : PulseBolt
{
	override void Tick()
	{
		Super.Tick();
		if ( globalfreeze || level.frozen ) return;
		if ( !target )
		{
			Destroy();
			return;
		}
		Vector3 x, y, z, origin;
		if ( target.player )
		{
			[x, y, z] = Matrix4.GetAxes(target.pitch,target.angle,target.roll);
			origin = (0,0,target.player.viewz-target.pos.z)+5.0*x+3.0*y-1.0*z;
		}
		else origin = (0,0,target.missileheight);
		SetOrigin(target.Vec3Offset(origin.x,origin.y,origin.z),true);
		A_SetAngle(target.angle);
		A_SetPitch(target.BulletSlope());
		frame++;
		if ( frame > 4 ) frame = 0;
		CheckBeam(x);
	}
}

Class PulseGun : UTWeapon
{
	int clipcount;
	double sangle;
	Actor beam;

	Property ClipCount : clipcount;

	override void PostRender()
	{
		if ( !CVar.GetCVar('flak_pulsereload').GetBool() ) return;
		Screen.DrawText(confont,Font.CR_GREEN,Screen.GetWidth()*0.01,Screen.GetHeight()*0.88,String.Format("Clip: % 2d / 50",clipcount));
	}

	action void A_Reloading()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		invoker.clipcount = Min(50,weap.Ammo1.Amount);
		A_PlaySound("pulse/reload",CHAN_WEAPON);
	}
	action void A_DrainAmmo()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		invoker.clipcount--;
		if ( !CVar.GetCVar('flak_pulsereload').GetBool() && (invoker.clipcount <= 0) ) invoker.clipcount = Min(50,weap.Ammo1.Amount);
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(32,128,255,128),1);
		A_AlertMonsters();
		Vector3 x, y, z;
		[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
		Vector3 origin = pos+(0,0,player.viewheight)+10.0*x+4.5*y-1.9*z;
		int numpt = Random[Pulse](2,5);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (x+(FRandom[Pulse](-.5,.5),FRandom[Pulse](-.5,.5),FRandom[Pulse](-.5,.5))).unit()*FRandom[Pulse](2,4);
			A_SpawnParticle("FFFFFF",SPF_FULLBRIGHT,Random[Pulse](10,20),FRandom[Pulse](1.2,2.4),0,origin.x-pos.x,origin.y-pos.y,origin.z-pos.z,pvel.x,pvel.y,pvel.z,0,0,0,1,-1,-0.1);
			A_SpawnParticle("40FF00",SPF_FULLBRIGHT,Random[Pulse](15,25),FRandom[Pulse](2.4,3.6),0,origin.x-pos.x,origin.y-pos.y,origin.z-pos.z,pvel.x,pvel.y,pvel.z,0,0,0,.5,-1,-0.1);
		}
	}
	action void A_PulseRefire( statelabel flash = null )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap || !player ) return;
		if ( (invoker.clipcount <= 0) || (weap.Ammo1.Amount <= 0) )
		{
			A_ClearRefire();
			if ( weap.bAltFire ) player.setpsprite(PSP_WEAPON,weap.FindState("AltRelease"));
			else player.setpsprite(PSP_WEAPON,weap.FindState("Release"));
			return;
		}
		A_Refire(flash);
	}
	action void A_PulseFire()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		invoker.clipcount--;
		if ( !CVar.GetCVar('flak_pulsereload').GetBool() && (invoker.clipcount <=0) ) invoker.clipcount = Min(50,weap.Ammo1.Amount);
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(32,128,255,128),1);
		A_AlertMonsters();
		A_QuakeEx(1,1,1,2,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05);
		A_Overlay(-2,"MuzzleFlash");
		A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
		A_OverlayRenderstyle(-2,STYLE_Add);
		Vector3 x, y, z;
		double a;
		[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
		Vector3 origin = pos+(0,0,player.viewheight)+10.0*x+4.0*y-1.5*z;
		origin += y*cos(invoker.sangle)*2.0+z*sin(invoker.sangle)*2.0;
		invoker.sangle += 100;
		Actor p = Spawn("PulseBall",origin);
		p.angle = angle;
		p.pitch = BulletSlope();
		p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
		p.target = self;
		int numpt = Random[Pulse](2,5);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (x+(FRandom[Pulse](-.5,.5),FRandom[Pulse](-.5,.5),FRandom[Pulse](-.5,.5))).unit()*FRandom[Pulse](2,4);
			A_SpawnParticle("FFFFFF",SPF_FULLBRIGHT,Random[Pulse](10,20),FRandom[Pulse](1.2,2.4),0,origin.x-pos.x,origin.y-pos.y,origin.z-pos.z,pvel.x,pvel.y,pvel.z,0,0,0,1,-1,-0.1);
			A_SpawnParticle("40FF00",SPF_FULLBRIGHT,Random[Pulse](15,25),FRandom[Pulse](2.4,3.6),0,origin.x-pos.x,origin.y-pos.y,origin.z-pos.z,pvel.x,pvel.y,pvel.z,0,0,0,.5,-1,-0.1);
		}
	}
	action void A_StartBeam()
	{
		A_PlaySound("pulse/bolt",CHAN_WEAPON,1.0,true);
		invoker.beam = Spawn("StarterBolt",pos);
		invoker.beam.target = self;
	}
	action void A_StopBeam()
	{
		A_StopSound(CHAN_WEAPON);
		if ( invoker.beam ) invoker.beam.Destroy();
	}
	override void OwnerDied()
	{
		Super.OwnerDied();
		if ( beam ) beam.Destroy();
	}
	Default
	{
		Tag "Pulse Gun";
		Inventory.PickupMessage "You got a Pulse Gun";
		Weapon.UpSound "pulse/select";
		Weapon.SlotNumber 5;
		Weapon.SelectionOrder 5;
		Weapon.AmmoType "PulseAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "PulseAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 60;
		PulseGun.ClipCount 50;
	}
	States
	{
	Spawn:
		PGNP A -1;
		Stop;
		PGNP B -1;
		Stop;
	Ready:
		PGNS ABCDEFGHIJKLMNOPQRSTUVW 1;
	Idle:
		PGNI A 1
		{
			A_CheckReload();
			if ( CVar.GetCVar('flak_pulsereload').GetBool() )
			{
				if ( (invoker.clipcount <= 0) && (invoker.Ammo1.Amount > 0) ) return A_Jump(255,"Reload");
				A_WeaponReady(WRF_ALLOWRELOAD);
			}
			else A_WeaponReady();
			return A_JumpIf(!Random[Pulse](0,300),1);
		}
		Wait;
		PGNI ABCDEFGHIJKLMNOPQRSTUVWXYZ 1
		{
			A_CheckReload();
			A_WeaponReady(WRF_ALLOWRELOAD);
		}
		Goto Idle;
	Fire:
		PGNI A 0 A_PlaySound("pulse/fire",CHAN_WEAPON,1.0,true);
	Hold:
		PGNF A 1 A_PulseFire();
		PGNF BCDEF 1;
		PNGF G 0 A_PulseRefire(1);
		Goto Release;
		PGNF G 1 A_PulseFire();
		PGNF HIJKL 1;
		PNGF M 0 A_PulseRefire(1);
		Goto Release;
		PGNF N 1 A_PulseFire();
		PGNF OPQRS 1;
		PNGF T 0 A_PulseRefire(1);
		Goto Release;
		PGNF U 1 A_PulseFire();
		PGNF VWXYZ 1;
		PGF2 A 0 A_PulseRefire(1);
		Goto Release;
		PGF2 A 1 A_PulseFire();
		PGF2 BCDEF 1;
		PGF2 G 0 A_PulseRefire(1);
		Goto Release;
		PGF2 H 1 A_PulseFire();
		PGF2 IJKLM 1;
		PGF2 N 0 A_PulseRefire();
		Goto Release;
	Release:
		PGNC A 0 A_PlaySound("pulse/down",CHAN_WEAPON);
		PGNC ABCDEFGHIJKLMNOPQRSTUVWXY 1;
		PGNC Y 0;
		Goto Idle;
	AltFire:
		PGBS ABCDE 1;
		PGBL A 0 A_StartBeam();
	AltHold:
		PGBL A 1 A_DrainAmmo();
		PGBL B 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL B 1;
		PGBL C 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL C 1;
		PGBL D 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL D 1;
		PGBL E 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL E 1;
		PGBL F 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL F 1;
		PGBL G 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL G 1;
		PGBL H 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL H 1;
		PGBL I 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL I 1;
		PGBL J 0 A_PulseRefire(1);
		Goto AltRelease;
		PGBL J 1;
		PGBL A 0 A_PulseRefire();
	AltRelease:
		PGBE A 0 A_StopBeam();
		PGBE ABCDE 1;
		Goto Idle;
	Reload:
		PGNI A 1;
		PGNI A 0 A_JumpIf(invoker.clipcount >= 50,"Idle");
		PGNR A 1 A_Reloading();
		PGNR BCDEFGHIJKLMNOPQRSTUVWXYZ 1;
		PGR2 ABCDEFGHIJKLMNOPQRSTUVWX 1;
		Goto Idle;
	Deselect:
		PGNS W 0 A_StopSound(CHAN_WEAPON);
		PGNS WVUTSRQPONMLKJIHGFEDCBA 1;
		PGNS A 1 A_Lower(int.max);
		Wait;
	Select:
		PGNS A 1 A_Raise(int.max);
		Wait;
	MuzzleFlash:
		PMUZ A 2 Bright;
		Stop;
	}
}
