Class UMiniAmmo : Ammo
{
	Default
	{
		Tag "$T_MINIAMMO";
		Inventory.Icon "I_ShellA";
		Inventory.PickupMessage "";
		Inventory.Amount 50;
		Inventory.MaxAmount 200;
		Ammo.BackpackAmount 25;
		Ammo.BackpackMaxAmount 400;
		Ammo.DropAmount 20;
	}
	override String PickupMessage()
	{
		if ( PickupMsg.Length() > 0 ) return Super.PickupMessage();
		return String.Format("%s%d%s",StringTable.Localize("$I_MINIAMMOL"),Amount,StringTable.Localize("$I_MINIAMMOR"));
	}
	States
	{
	Spawn:
		MAMO A -1;
		Stop;
	}
}

Class UMinigun : UnrealWeapon
{
	int bcnt, tcnt;

	action void A_FireBullet( bool alt = false )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		A_Overlay(-2,"MuzzleFlash",true);
		A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
		A_OverlayRenderstyle(-2,STYLE_Add);
		invoker.bcnt++;
		if ( (alt && (invoker.bcnt < 2)) || (!alt && (invoker.bcnt < 4)) ) return;
		invoker.bcnt = 0;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(32,255,255,0),1);
		if ( !Dampener.Active(self) ) A_AlertMonsters();
		A_SoundVolume(CHAN_WEAPON,Dampener.Active(self)?.1:1.);
		if ( alt )
		{
			A_QuakeEx(2,2,2,8,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12);
			UTMainHandler.DoSwing(self,(FRandom[Minigun](-1,1),FRandom[Minigun](-1,1)),0.5,0,2,SWING_Spring,0,3);
		}
		else
		{
			A_QuakeEx(1,1,1,8,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.08);
			UTMainHandler.DoSwing(self,(FRandom[Minigun](-1,1),FRandom[Minigun](-1,1)),0.3,0,2,SWING_Spring,0,3);
		}
		let l = Spawn("MinigunLight",pos);
		l.target = self;
		if ( !alt ) MinigunLight(l).cnt--;
		Vector3 x, y, z, x2, y2, z2;
		[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
		Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+y*3-z*3);
		double a = FRandom[Minigun](0,360), s = FRandom[Minigun](0,alt?0.05:0.02);
		[x2, y2, z2] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll);
		Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
		FLineTraceData d;
		LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
		UTBulletTrail.DoTrail(self,origin,dir,10000,alt?5:3);
		if ( d.HitType == TRACE_HitActor )
		{
			int dmg = Random[Minigun](8,14); // fun fact: the Minigun is one of the few weapons that has actual RNG damage in Unreal
			dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
			double mm = 500;
			if ( FRandom[Minigun](0,1) < 0.2 ) mm *= 2.;
			UTMainHandler.DoKnockback(d.HitActor,d.HitDir,dmg*mm);
			if ( d.HitActor.bNOBLOOD )
			{
				let p = Spawn("BulletImpact",d.HitLocation);
				p.scale *= 0.75;
				p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
				p.pitch = asin(d.HitDir.z);
			}
			else
			{
				d.HitActor.TraceBleed(dmg,self);
				d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
			}
		}
		else if ( d.HitType != TRACE_HitNone )
		{
			Vector3 hitnormal = -d.HitDir;
			if ( d.HitType == TRACE_HitFloor )
			{
				if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
				else hitnormal = d.HitSector.floorplane.Normal;
			}
			else if ( d.HitType == TRACE_HitCeiling )
			{
				if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
				else hitnormal = d.HitSector.ceilingplane.Normal;
			}
			else if ( d.HitType == TRACE_HitWall )
			{
				hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
				if ( !d.LineSide ) hitnormal *= -1;
			}
			let p = Spawn("BulletImpact",d.HitLocation+hitnormal*0.01);
			p.scale *= 0.75;
			p.angle = atan2(hitnormal.y,hitnormal.x);
			p.pitch = asin(-hitnormal.z);
			if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
		}
		if ( !Random[Minigun](0,1) )
		{
			let t = Spawn("MinigunTracer",level.Vec3Offset(origin,x*20));
			t.angle = atan2(dir.y,dir.x);
			t.pitch = asin(-dir.z);
			MinigunTracer(t).dest = d.HitLocation;
		}
		for ( int i=0; i<2; i++ )
		{
			let s = Spawn("UTViewSmoke",origin);
			UTViewSmoke(s).ofs = (10,3,-3);
			s.scale *= 1.5;
			s.alpha *= 0.6;
			UTViewSmoke(s).vvel += (FRandom[Minigun](0.2,0.8),FRandom[Minigun](-0.3,0.3),FRandom[Minigun](-0.3,0.3));
			s.target = self;
		}
		origin = level.Vec3Offset(origin,x*4+y*3-z*12);
		let c = Spawn("UCasing",origin);
		c.angle = angle;
		c.pitch = pitch;
		c.vel = x*FRandom[Junk](-1.5,1.5)-y*FRandom[Junk](2,4)+z*FRandom[Junk](-2,1);
	}

	action void A_MinigunRefire()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap || !player ) return;
		if ( weap.Ammo1.Amount <= 0 )
		{
			A_ClearRefire();
			return;
		}
		weap.bAltFire = !!(player.cmd.buttons&BT_ALTATTACK);
		A_Refire("Hold");
	}

	Default
	{
		Tag "$T_MINIGUN";
		Inventory.PickupMessage "$I_MINIGUN";
		Weapon.UpSound "minigun/select";
		Weapon.SlotNumber 0;
		Weapon.SelectionOrder 500;
		Weapon.SlotPriority 1;
		Weapon.AmmoType "UMiniAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "UMiniAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 50;
		UTWeapon.DropAmmo 25;
	}
	States
	{
	Spawn:
		MGNP A -1;
		Stop;
		MGNP B -1;
		Stop;
	Select:
		MGNS A 1 A_Raise(int.max);
		Wait;
	Ready:
		MGNS ABCDEFGHIJKLMNOPQRSTUVW 1 A_WeaponReady(WRF_NOFIRE);
	Idle:
		MGNI A 1
		{
			A_CheckReload();
			A_WeaponReady();
		}
		Wait;
	FireDummy:
		TNT1 A 1
		{
			let weap = Weapon(invoker);
			if ( !(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)) || (weap.Ammo1.Amount <= 0) )
			{
				A_StopSound(CHAN_WEAPON);
				if ( Health > 0 ) player.SetPSprite(PSP_WEAPON,invoker.FindState("Release"));
				return ResolveState("Null");
			}
			return ResolveState(null);
		}
		Wait;
	Fire:
	AltFire:
		MGNW A 0 A_PlaySound("umini/wind",CHAN_WEAPON,Dampener.Active(self)?.1:1.);
		MGNW ABCDEFGHIJKLMNO 1;
		MGNW O 0 A_Refire(1);
		Goto Release;
	Hold:
		MGNF A 0
		{
			A_Overlay(-9999,"FireDummy");
			A_PlaySound("umini/fire",CHAN_WEAPON,Dampener.Active(self)?.1:1.,true);
		}
		MGNF ABCDEFGHIJKLMNO 1 A_FireBullet(false);
		MGNF A 0 A_JumpIf(player.cmd.buttons&BT_ALTATTACK,1);
		Goto Hold+1;
	AltHold:
		MGNA A 0
		{
			A_Overlay(-9999,"FireDummy");
			A_PlaySound("umini/altfire",CHAN_WEAPON,Dampener.Active(self)?.1:1.,true);
		}
		MGNA ABCDEFGHIJKLMNOPQRST 1 A_FireBullet(true);
		Goto AltHold+1;
	Release:
		MGNU A 0 A_PlaySound("umini/unwind",CHAN_WEAPON,Dampener.Active(self)?.1:1.);
		MGNU ABCDEFGHIJKLMNO 2 A_MinigunRefire();
		Goto Idle;
	Deselect:
		MGND A 0 A_StopSound(CHAN_WEAPON);
		MGND ABCDEFGHIJ 1;
		MGND J 1 A_Lower(int.max);
		Wait;
	MuzzleFlash:
		MMUZ # 2 Bright
		{
			let psp = player.FindPSprite(OverlayID());
			psp.frame = Random[Minigun](0,8);
		}
		Stop;
	}
}
