Class BigAmmo : Ammo
{
	Default
	{
		Tag "$T_BIGAMMO";
		Inventory.Icon "I_BigM";
		Inventory.PickupMessage "";
		Inventory.Amount 10;
		Inventory.MaxAmount 12;
		Ammo.BackpackAmount 0;
		Ammo.BackpackMaxAmount 20;
		Ammo.DropAmount 10;
		Inventory.RespawnTics 2100;
		+INVENTORY.IGNORESKILL;
	}
	override String PickupMessage()
	{
		if ( PickupMsg.Length() > 0 ) return Super.PickupMessage();
		return String.Format("%s%d%s",StringTable.Localize("$I_BIGAMMOL"),Amount,StringTable.Localize("$I_BIGAMMOR"));
	}
	override bool TryPickup( in out Actor toucher )
	{
		if ( !sting_proto || !sting_dubious ) return false;	// not allowed
		return Super.TryPickup(toucher);
	}
	override void Tick()
	{
		Super.Tick();
		if ( sting_proto && sting_dubious ) return;
		if ( !Owner )
		{
			let r = Spawn((GetClass()=="BigAmmo")?"CellPack":"Cell",pos,ALLOW_REPLACE);
			r.spawnangle = spawnangle;
			r.spawnpoint = spawnpoint;
			r.angle = angle;
			r.pitch = pitch;
			r.roll = roll;
			r.special = special;
			r.args[0] = args[0];
			r.args[1] = args[1];
			r.args[2] = args[2];
			r.args[3] = args[3];
			r.args[4] = args[4];
			r.ChangeTid(tid);
			r.SpawnFlags = SpawnFlags&~MTF_SECRET;
			r.HandleSpawnFlags();
			r.SpawnFlags = SpawnFlags;
			r.bCountSecret = SpawnFlags&MTF_SECRET;
			r.vel = vel;
			r.master = master;
			r.target = target;
			r.tracer = tracer;
			r.bDropped = bDropped;
			Destroy();
		}
		else
		{
			Owner.RemoveInventory(self);
			Destroy();
		}
	}
	States
	{
	Spawn:
		BIGA A -1;
		Stop;
	}
}

Class BigAmmo2 : BigAmmo
{
	Default
	{
		Tag "$T_BIGAMMO2";
		Inventory.Amount 3;
		Ammo.DropAmount 3;
		+INVENTORY.IGNORESKILL;
	}
}

Class BigAmmo3 : BigAmmo
{
	Default
	{
		Tag "$T_BIGAMMO3";
		Inventory.PickupMessage "$I_BIGAMMO3";
		Inventory.Amount 1;
		Ammo.DropAmount 1;
		+INVENTORY.IGNORESKILL;
	}
}

Class FatRing : Actor
{
	Default
	{
		RenderStyle "Add";
		+NOGRAVITY;
		+NOBLOCKMAP;
	}
	override void Tick()
	{
		Super.Tick();
		if ( isFrozen() ) return;
		A_FadeOut(1/19.,0);
	}
	States
	{
	Spawn:
		FATR ABCDEFGHIJK 2 Bright;
		Stop;
	}
}

Class BigBlast : Actor
{
	Default
	{
		RenderStyle "Add";
		DamageType 'BigShot';
		Scale 2.;
		+NOGRAVITY;
		+NOBLOCKMAP;
		+NODAMAGETHRUST;
		+FORCERADIUSDMG;
		+FORCEXYBILLBOARD;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		A_AlertMonsters();
		A_Explode(50+special1,150);
		A_QuakeEx(4,4,4,10,0,300,"",QF_RELATIVE|QF_SCALEDOWN,falloff:150,rollintensity:0.2);
		A_PlaySound("big/blast",CHAN_VOICE,pitch:FRandom[BigGun](0.8,1.2));
		A_SprayDecal("RazorBlast",-172);
		UTMainHandler.DoBlast(self,150,80000);
		let r = Spawn("FatRing",pos);
		r.angle = angle;
		r.pitch = pitch;
		r.roll = FRandom[ExploS](0,360);
		r.scale *= FRandom[ExploS](0.8,1.1);
		Scale *= FRandom[ExploS](0.8,1.1);
		Scale.x *= RandomPick[ExploS](-1,1);
		Scale.y *= RandomPick[ExploS](-1,1);
		int numpt = Random[ExploS](30,40);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,4);
			let s = Spawn("UTSmoke",pos);
			s.vel = pvel;
			s.scale *= FRandom[ExploS](0.9,4.0);
			s.SetShade(Color(1,1,1)*Random[ExploS](128,255));
		}
		numpt = Random[ExploS](20,25);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
			let s = Spawn("UTSpark",pos);
			s.vel = pvel;
		}
		numpt = Random[ExploS](30,40);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
			let s = Spawn("UTChip",pos);
			s.vel = pvel;
			s.scale *= FRandom[ExploS](0.9,2.7);
		}
		Spawn("FlareXLight",pos);
	}
	States
	{
	Spawn:
		BGXP ABCDEFGH 4 Bright;
		Stop;
	}
}

Class HitListEntry
{
	Actor hitactor;
	Vector3 hitlocation, x;
	int hitdamage;
}

Class BigTracer : LineTracer
{
	Actor ignoreme;
	Array<Line> ShootThroughList;
	Array<HitListEntry> hitlist;
	double penetration;	// please don't laugh

	override ETraceStatus TraceCallback()
	{
		if ( Results.HitType == TRACE_HitActor )
		{
			if ( Results.HitActor == ignoreme ) return TRACE_Skip;
			if ( Results.HitActor.bSHOOTABLE )
			{
				let ent = new("HitListEntry");
				ent.hitactor = Results.HitActor;
				ent.hitlocation = Results.HitPos;
				ent.x = Results.HitVector;
				ent.hitdamage = min(Results.HitActor.health,int(penetration));
				hitlist.Push(ent);
				penetration = max(0,penetration-ent.hitdamage*.2);
				if ( (penetration <= 0) || (hitlist.Size() > 9) )
					return TRACE_Abort;
				return TRACE_Skip;
			}
			return TRACE_Skip;
		}
		else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
		{
			if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
				return TRACE_Stop;
			ShootThroughList.Push(Results.HitLine);
			return TRACE_Skip;
		}
		return TRACE_Stop;
	}
}

Class BigCasing : UTCasing
{
	Default
	{
		BounceSound "big/shell";	// much heavier sound
	}
}

// hambagah?
Class BigMag : UTCasing
{
	Default
	{
		BounceSound "big/bounce";	// much heavier sound
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		heat = 0.;
	}
	States
	{
	Death:
		PCAS A -1
		{
			pitch = clamp(Normalize180(pitch),-5,5);
			angle = FRandom[Junk](0,360);
		}
		Stop;
	}
}

Class BigGun : UnrealWeapon
{
	int ClipCount;
	bool ClipOut;
	BigTracer t;

	property ClipCount : ClipCount;

	override bool TryPickup( in out Actor toucher )
	{
		if ( !sting_proto || !sting_dubious ) return false;	// not allowed
		return Super.TryPickup(toucher);
	}
	override void Tick()
	{
		Super.Tick();
		if ( sting_proto && sting_dubious ) return;
		if ( !Owner )
		{
			let r = Spawn("BFG9000",pos,ALLOW_REPLACE);
			r.spawnangle = spawnangle;
			r.spawnpoint = spawnpoint;
			r.angle = angle;
			r.pitch = pitch;
			r.roll = roll;
			r.special = special;
			r.args[0] = args[0];
			r.args[1] = args[1];
			r.args[2] = args[2];
			r.args[3] = args[3];
			r.args[4] = args[4];
			r.ChangeTid(tid);
			r.SpawnFlags = SpawnFlags&~MTF_SECRET;
			r.HandleSpawnFlags();
			r.SpawnFlags = SpawnFlags;
			r.bCountSecret = SpawnFlags&MTF_SECRET;
			r.vel = vel;
			r.master = master;
			r.target = target;
			r.tracer = tracer;
			r.bDropped = bDropped;
			Destroy();
		}
		else
		{
			Owner.RemoveInventory(self);
			Destroy();
		}
	}
	override int, int, bool, bool GetClipAmount()
	{
		return ClipOut?-1:ClipCount, -1, (ClipCount<2), false;
	}
	override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount )
	{
		if ( ClipCount > 0 ) return true;
		return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
	}
	action void A_Eject()
	{
		Vector3 x, y, z;
		[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
		Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),4*x+y*12-z*4);
		let c = Spawn("BigCasing",origin);
		c.vel = x*FRandom[Junk](-1.5,1.5)+y*FRandom[Junk](2,4)+z*FRandom[Junk](-1,2);
	}
	action void A_BigFire( bool bAlt = false )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( invoker.clipcount <= 0 ) return;
		invoker.clipcount--;
		A_PlaySound("big/fire",CHAN_WEAPON);
		A_AlertMonsters();
		A_Overlay(-2,"MuzzleFlash");
		A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
		A_OverlayRenderstyle(-2,STYLE_Add);
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(32,255,128,0),1);
		A_QuakeEx(2,2,2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollintensity:0.12);
		for ( int i=0; i<6; i++ )
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.04,-0.2),FRandom[BigGun](-0.2,0.2)),FRandom[BigGun](6,9),FRandom[BigGun](-2,-3),Random[BigGun](2,4),SWING_Spring,Random[BigGun](3,4),FRandom[BigGun](1.5,2.3));
		Vector3 x, y, z, x2, y2, z2;
		[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
		vel -= x*(player.onground?9.5:2.5);
		vel.z += (player.onground?2.8:.25);
		Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+y*5-z*1);
		[x2, y2, z2] = dt_CoordUtil.GetAxes(BulletSlope(),angle,roll);
		double a = FRandom[BigGun](0,360), s = FRandom[BigGun](0,bAlt?0.2:0.05);
		Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
		if ( !invoker.t ) invoker.t = new("BigTracer");
		invoker.t.ignoreme = self;
		invoker.t.hitlist.Clear();
		invoker.t.penetration = 200.;
		invoker.t.ShootThroughList.Clear();
		invoker.t.Trace(origin,level.PointInSector(origin.xy),dir,10000.,0);
		for ( int i=0; i<invoker.t.ShootThroughList.Size(); i++ )
			invoker.t.ShootThroughList[i].Activate(self,0,SPAC_PCross);
		for ( int i=5; i<invoker.t.Results.Distance; i+=10 )
		{
			if ( !Random[Boolet](0,bAlt?3:2) ) continue;
			let b = Actor.Spawn("UTBubble",level.Vec3Offset(origin,dir*i));
			b.Scale *= FRandom[Boolet](0.4,0.6);
		}
		for ( int i=0; i<invoker.t.hitlist.Size(); i++ )
		{
			let l = invoker.t.hitlist[i];
			l.hitactor.DamageMobj(invoker,self,l.hitdamage,'BigShot',DMG_THRUSTLESS);
			UTMainHandler.DoKnockback(l.hitactor,l.x,90000.);
			let b = Spawn("BigBlast",l.hitlocation-l.x*8.);
			b.target = self;
			b.special1 = l.hitdamage;
			b.angle = atan2(l.x.y,l.x.x);
			b.pitch = asin(-l.x.z);
		}
		if ( invoker.t.Results.HitType != TRACE_HitNone )
		{
			Vector3 hitnormal = -invoker.t.Results.HitVector;
			if ( invoker.t.Results.HitType == TRACE_HitFloor )
			{
				if ( invoker.t.Results.FFloor ) hitnormal = -invoker.t.Results.FFloor.top.Normal;
				else hitnormal = invoker.t.Results.HitSector.floorplane.Normal;
			}
			else if ( invoker.t.Results.HitType == TRACE_HitCeiling )
			{
				if ( invoker.t.Results.FFloor ) hitnormal = -invoker.t.Results.FFloor.bottom.Normal;
				else hitnormal = invoker.t.Results.HitSector.ceilingplane.Normal;
			}
			else if ( invoker.t.Results.HitType == TRACE_HitWall )
			{
				hitnormal = (-invoker.t.Results.HitLine.delta.y,invoker.t.Results.HitLine.delta.x,0).unit();
				if ( !invoker.t.Results.Side ) hitnormal *= -1;
			}
			let b = Spawn("BigBlast",invoker.t.Results.HitPos+hitnormal*8.);
			b.target = self;
			b.special1 = int(invoker.t.penetration);
			b.angle = atan2(hitnormal.y,hitnormal.x);
			b.pitch = asin(-hitnormal.z);
			if ( invoker.t.Results.HitLine ) invoker.t.Results.HitLine.RemoteActivate(self,invoker.t.Results.Side,SPAC_Impact,invoker.t.Results.HitPos);
		}
		int numpt = 50;
		for ( int i=0; i<numpt; i++ )
		{
			let s = Spawn("UTStaticViewSmoke",origin);
			UTViewSmoke(s).ofs = (10,5,-1);
			UTViewSmoke(s).vvel += (FRandom[BigGun](-0.05,0.5),FRandom[BigGun](-0.3,0.3),FRandom[BigGun](-0.3,0.3));
			s.target = self;
			s.scale *= 1.5;
			s.alpha *= 0.6;
		}
	}
	action bool A_BigCheckForReload( bool bDryFire = false )
	{
		let weap = Weapon(invoker);
		if ( invoker.clipcount <= 0 )
		{
			if ( weap.Ammo1.Amount > 0 )
			{
				player.SetPSprite(PSP_WEAPON,invoker.FindState("Reload"));
				return true;
			}
			else if ( bDryFire )
			{
				player.SetPSprite(PSP_WEAPON,invoker.FindState("DryFire"));
				return true;
			}
		}
		return false;
	}
	Default
	{
		Tag "$T_BIGGUN";
		Obituary "$O_BIGGUN";
		Inventory.PickupMessage "$I_BIGGUN";
		Weapon.UpSound "big/select";
		Weapon.SlotNumber 9;
		Weapon.SelectionOrder 50;
		Weapon.SlotPriority 0.8;
		Weapon.AmmoType "BigAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "BigAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 4;
		Inventory.RespawnTics 2100;
		+INVENTORY.IGNORESKILL;
		UTWeapon.DropAmmo 4;
		BigGun.ClipCount 4;
		+EXTREMEDEATH;
		+UNREALWEAPON.NOFIRSTGIVE;
	}
	States
	{
	Spawn:
		BIGP A -1;
		Stop;
		BIGP B -1;
		Stop;
	Select:
		BIGS A 1 A_Raise(int.max);
		Wait;
	Ready:
		BIGS A 0
		{
			invoker.clipout = false;
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.3,-0.2),FRandom[BigGun](0.4,0.5)),3,0,6,SWING_Spring,2,1.8);
		}
		BIGS ABC 2 A_WeaponReady(WRF_NOFIRE);
		BIGS D 0
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](0.2,0.3),FRandom[BigGun](-0.8,-1.2)),3,0,6,SWING_Spring,2,2.0);
		}
		BIGS DEFGHI 2 A_WeaponReady(WRF_NOFIRE);
		Goto Idle;
	Dummy:
		TNT1 A 1
		{
			A_CheckReload();
			if ( !A_BigCheckForReload() )
			{
				if ( (invoker.clipCount < invoker.default.clipcount) && (invoker.Ammo1.Amount > 0) )
					A_WeaponReady(WRF_ALLOWRELOAD);
				else A_WeaponReady();
			}
		}
		Wait;
	Idle:
		BIGI A 0 A_Overlay(-9999,"Dummy");
		BIGI A 20 A_Jump(80,"Twiddle1","Twiddle1","Twiddle2");
		Wait;
	Twiddle1:
		BIGW A 3
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.2,-0.3),FRandom[BigGun](0.2,0.3)),2,0,12,SWING_Spring,5,2);
		}
		BIGW BCDEFGHIJKLMNO 3;
		Goto Idle;
	Twiddle2:
		BIGT A 2
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.2,-0.3),FRandom[BigGun](0.2,0.3)),3,0,6,SWING_Spring,5,2);
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.2,-0.3),FRandom[BigGun](-0.4,-0.6)),4,0,12,SWING_Spring,6,1.4);
		}
		BIGT BCDEFGHIJKLMNOP 2;
		BIGT Q 2
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.3,-0.2),FRandom[BigGun](0.4,0.5)),3,0.5,6,SWING_Spring,2,1.5);
		}
		BIGT RSTUVWXY 2;
		Goto Idle;
	Fire:
		BIGF A 0
		{
			if ( !A_BigCheckForReload(true) )
				A_Overlay(-9999,"Null");
		}
		BIGF AB 2;
		BIGF C 2 A_BigFire();
		BIGF DEF 2;
		BIGF G 2 A_Eject();
		BIGF HIJKLMNOP 2;
		BIGI A 0 A_Refire("Fire");
		Goto Idle;
	AltFire:
		BIGF A 0
		{
			if ( !A_BigCheckForReload(true) )
				A_Overlay(-9999,"Null");
		}
		BIGF AB 3;
		Goto AltHold;
	AltHold:
		BIGF C 1 A_BigFire();
		BIGF DEF 1;
		BIGF G 1 A_Eject();
		BIGF HIJK 1;
		BIGF L 0 A_Refire(1);
		Goto AltRelease;
		BIGF L 1 A_JumpIf(invoker.clipcount<=0,"AltRelease");
		Goto AltHold;
	AltRelease:
		BIGF LMNOP 3;
		Goto Idle;
	DryFire:
		BIGF A 0 A_Overlay(-9999,"Null");
		BIGF AB 2;
		BIGF C 2
		{
			A_PlaySound("big/dry",CHAN_WEAPON,Dampener.Active(self)?.3:1.);
			if ( !Dampener.Active(self) ) A_AlertMonsters();
		}
		BIGF LMNOP 2;
		Goto Idle;
	Reload:
		BIGR A 0
		{
			A_ClearRefire();
			A_Overlay(-9999,"Null");
			UTMainHandler.DoSwing(self,(FRandom[BigGun](0.7,0.3),FRandom[BigGun](0.4,0.3)),3,0.2,10,SWING_Spring,70,0.8);
		}
		BIGR ABCDEFGHIJKL 2;
		BIGR M 2
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](0.3,0.4),FRandom[BigGun](0.6,0.8)),6,-1,3,SWING_Spring,2,3);
			invoker.clipout = true;
			A_QuakeEx(2,2,2,5,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollintensity:0.18);
			A_PlaySound("big/punch",CHAN_WEAPON,Dampener.Active(self)?.3:1.);
			if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading();
		}
		BIGR NOPQRSTUVW 2;
		BIGR X 2
		{
			Vector3 x, y, z, origin;
			[x,y,z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
			origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*5.-z*9.);
			let c = Spawn("BigMag",origin);
			c.angle = angle;
			c.pitch = pitch;
			c.vel = vel*.5+x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-.5,.5)-z*FRandom[Junk](1,2);
		}
		BIGR YZ[\] 2;
		BIR2 ABCDEF 2;
		BIR2 G 2
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](0.2,0.3),FRandom[BigGun](0.3,0.5)),6,-1,3,SWING_Spring,2,3);
			invoker.clipout = false;
			let aadd = min(invoker.ammo1.amount,invoker.default.clipcount-invoker.clipcount);
			invoker.clipcount += aadd;
			invoker.Ammo1.Amount -= aadd;
			A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollintensity:0.12);
			A_PlaySound("big/reload",CHAN_WEAPON,Dampener.Active(self)?.3:1.);
			if ( self is 'UTPlayer' ) UTPlayer(self).PlayReloading();
		}
		BIR2 HIJKL 2;
		BIR2 M 2
		{
			invoker.PlayUpSound(self);
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.3,-0.2),FRandom[BigGun](0.4,0.5)),3,0.5,7,SWING_Spring,3,1.5);
		}
		BIR2 NOP 2;
		BIR2 Q 2
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](0.2,0.3),FRandom[BigGun](-0.8,-1.2)),3,-0.5,7,SWING_Spring,3,1.5);
		}
		BIR2 RSTUVWXYZ[\] 2;
		BIR3 A 2
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.3,-0.2),FRandom[BigGun](0.4,0.5)),2,-0.5,3,SWING_Spring,1,2.);
		}
		BIR3 BCDEF 2;
		Goto Idle;
	Deselect:
		BIGD A 1
		{
			A_Overlay(-9999,"Null");
			UTMainHandler.DoSwing(self,(FRandom[BigGun](0.2,0.3),FRandom[BigGun](-0.8,-1.2)),3,0,6,SWING_Spring,2,1.5);
		}
		BIGD BC 1;
		BIGD D 1
		{
			UTMainHandler.DoSwing(self,(FRandom[BigGun](-0.3,-0.2),FRandom[BigGun](0.4,0.5)),3,0,6,SWING_Spring,2,1.5);
		}
		BIGD EFGHIJK 1;
		BIGD K 1 A_Lower(int.max);
		Wait;
	MuzzleFlash:
		BGMF A 2 Bright
		{
			let l = Spawn("SniperLight",pos);
			l.args[0] = 255;
			l.args[1] = 176;
			l.args[2] = 16;
			l.target = self;
		}
		Stop;
	}
}
