Class UTRocketAmmo : Ammo
{
	Default
	{
		Tag "Rocket Pack";
		Inventory.PickupMessage "You picked up a Rocket Pack.";
		Inventory.Amount 12;
		Inventory.MaxAmount 48;
		Ammo.BackpackAmount 12;
		Ammo.BackpackMaxAmount 48;
		Ammo.DropAmount 3;
	}
	States
	{
	Spawn:
		RPAK A -1;
		Stop;
	}
}

// There was no single rocket ammo in UT, so this one is also just improvised like the Redeemer ammo pickup
Class UTRocketAmmo2 : UTRocketAmmo
{
	Default
	{
		Tag "Single Rocket";
		Inventory.PickupMessage "You picked up a Single Rocket.";
		Inventory.Amount 1;
		Ammo.DropAmount 1;
	}
	States
	{
	Spawn:
		RCKT A -1;
		Stop;
	}
}

Class RocketLight : DynamicLight
{
	Default
	{
		DynamicLight.Type "Point";
		Args 255,224,128,32;
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target )
		{
			Destroy();
			return;
		}
		SetOrigin(target.pos,true);
	}
}

Class UTRocketTrail : Actor
{
	Default
	{
		RenderStyle "Add";
		Radius 0.1;
		Height 0;
		+NOBLOCKMAP;
		+NOGRAVITY;
		+DONTSPLASH;
		+FORCEXYBILLBOARD;
		Scale 0.2;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		let l = Spawn("RocketLight",pos);
		l.target = self;
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target || target.InStateSequence(target.CurState,target.FindState("Death")) )
		{
			Destroy();
			return;
		}
		SetOrigin(target.pos,true);
	}
	States
	{
	Spawn:
		RFLA A -1 Bright;
		Stop;
	}
}

Class RocketExplLight : SlugLight
{
	Default
	{
		Args 255,224,128,120;
	}
}

Class UTRocket : Actor
{
	Default
	{
		Obituary "%o was smacked down by %k's Rocket Launcher.";
		DamageFunction Random[Eightball](70,80);
		DamageType 'RocketDeath';
		Radius 2;
		Height 0;
		Speed 30;
		PROJECTILE;
		+SKYEXPLODE;
		+SEEKERMISSILE;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		let l = Spawn("UTRocketTrail",pos);
		l.target = self;
		A_PlaySound("utrl/fly",CHAN_VOICE,1.0,true,2.5);
	}
	action void A_RocketExplode()
	{
		bFORCEXYBILLBOARD = true;
		A_SetRenderStyle(1.0,STYLE_Add);
		A_SprayDecal("RocketBlast",150);
		A_NoGravity();
		A_SetScale(0.75);
		A_Explode(Random[Eightball](60,70),200);
		A_QuakeEx(3,3,3,8,0,300,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.2);
		A_PlaySound("utrl/explode",CHAN_VOICE);
		A_AlertMonsters();
		Spawn("RocketExplLight",pos);
		int numpt = Random[Eightball](15,30);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Eightball](-1,1),FRandom[Eightball](-1,1),FRandom[Eightball](-1,1)).unit()*FRandom[Eightball](1,4);
			let s = Spawn("UTSmoke",pos);
			s.vel = pvel;
		}
		numpt = Random[Eightball](10,20);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Eightball](-1,1),FRandom[Eightball](-1,1),FRandom[Eightball](-1,1)).unit()*FRandom[Eightball](2,8);
			let s = Spawn("UTSpark",pos);
			s.vel = pvel;
		}
		numpt = Random[Eightball](25,50);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Eightball](-1,1),FRandom[Eightball](-1,1),FRandom[Eightball](-1,1)).unit()*FRandom[Eightball](4,12);
			let s = Spawn("UTChip",pos);
			s.vel = pvel;
		}
	}
	States
	{
	Spawn:
		RCKT B 1
		{
			A_SetRoll(roll+30,SPF_INTERPOLATE);
			Vector3 dir = vel.unit();
			A_SetAngle(atan2(dir.y,dir.x),SPF_INTERPOLATE);
			A_SetPitch(asin(-dir.z),SPF_INTERPOLATE);
			if ( tracer ) A_SeekerMissile(0,2,SMF_PRECISE);
			Spawn("UTSmoke",pos);
		}
		Wait;
	Death:
		TNT1 A 0 A_RocketExplode();
		SEXP ABCDEFGHIJ 2 Bright;
		Stop;
	}
}

Class UTGrenade : UTRocket
{
	double rollvel, pitchvel, anglevel;

	Default
	{
		DamageFunction Random[Eightball](80,90);
		DamageType 'GrenadeDeath';
		-NOGRAVITY;
		+USEBOUNCESTATE;
		BounceType "Doom";
		BounceFactor 0.75;
		ReactionTime 85;
		Speed 20;
	}
	override void PostBeginPlay()
	{
		Actor.PostBeginPlay();
		rollvel = FRandom[Eightball](-8,8);
		pitchvel = FRandom[Eightball](-8,8);
		anglevel = FRandom[Eightball](-8,8);
		ReactionTime += Random[Eightball](0,20);
	}
	States
	{
	Spawn:
		RCKT A 1
		{
			A_SetAngle(angle+anglevel,SPF_INTERPOLATE);
			A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
			A_SetRoll(roll+rollvel,SPF_INTERPOLATE);
			A_Countdown();
		}
		Wait;
	Bounce:
		RCKT A 0
		{
			A_PlaySound("utrl/bounce");
			rollvel = FRandom[Eightball](-16,16);
			pitchvel = FRandom[Eightball](-16,16);
			anglevel = FRandom[Eightball](-16,16);
		}
		Goto Spawn;
	}
}

Class UTRocketLauncher : UTWeapon
{
	bool LockedOn;
	Actor LockedTarget;
	TextureID lockontex;
	int locktics;

	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		lockontex = TexMan.CheckForTexture("Crosshr6",TexMan.Type_Any);
	}

	override void PostRender()
	{
		if ( LockedTarget ) Screen.DrawTexture(lockontex,false,(Screen.GetWidth()-16)*0.5,(Screen.GetHeight()-16)*0.5);
	}

	override void Tick()
	{
		Super.Tick();
		if ( !Owner ) return;
		if ( LockedOn && (!LockedTarget || (LockedTarget.Health <= 0) || LockedTarget.bKilled || LockedTarget.bCorpse || !LockedTarget.bShootable) )
		{
			LockedTarget = null;
			LockedOn = false;
			Owner.A_PlaySound("utrl/seeklost",CHAN_6);
		}
		if ( LockedTarget ) A_SetCrosshair(99);
		else A_SetCrosshair(0);
	}

	// consumes 1 ammo
	action void A_LoadRocket( bool checktarget = true )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		if ( weap.bAltFire )
		{
			invoker.LockedTarget = null;
			invoker.LockedOn = false;
		}
		if ( checktarget && !weap.bAltFire ) A_CheckTarget();
	}

	// refire that is ignored if there's no ammo
	action void A_LoadedRefire( statelabel flash = null )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		A_Refire(flash);
	}

	// fire all the rockets (or grenades)
	action void A_FireRockets( int num )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.bAltFire ) A_PlaySound("utrl/altfire",CHAN_WEAPON);
		else A_PlaySound("utrl/fire",CHAN_WEAPON);
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(64,255,0,0),1);
		A_AlertMonsters();
		A_QuakeEx(2+num,2+num,2+num,6+num,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1+num*0.05);
		Vector3 x, y, z, x2, y2, z2;
		double a, s;
		[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
		Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+6.0*y-6.0*z;
		[x2, y2, z2] = Matrix4.GetAxes(BulletSlope(),angle,roll);
		Actor p;
		if ( weap.bAltFire )
		{
			// grenades
			for ( int i=0; i<num; i++ )
			{
				a = FRandom[Eightball](0,360);
				s = FRandom[Eightball](0,0.1);
				Vector3 dir = (x2+cos(a)*y2*s+sin(a)*z2*s).unit();
				p = Spawn("UTGrenade",origin);
				p.vel = dir*p.speed*FRandom[Eightball](1.0,1.2);
				p.vel.z += 5;
				p.target = self;
			}
		}
		else if ( num <= 1 )
		{
			// single rocket
			p = Spawn("UTRocket",origin+cos(a)*y*s+sin(a)*z*s);
			p.vel = (x2+cos(a)*y2*s*0.01+sin(a)*z2*s*0.01).unit()*p.speed;
			p.target = self;
			p.tracer = invoker.LockedTarget;
		}
		else if ( player.cmd.buttons&BT_ALTATTACK )
		{
			// rockets ("tight wad" as UT calls it)
			double step = 360/num;
			a = 90;
			s = num?1.2:0.0;
			for ( int i=0; i<num; i++ )
			{
				p = Spawn("UTRocket",origin+cos(a)*y*s+sin(a)*z*s);
				p.vel = (x2+cos(a)*y2*s*0.01+sin(a)*z2*s*0.01).unit()*p.speed;
				p.target = self;
				p.tracer = invoker.LockedTarget;
				a += step;
			}
		}
		else
		{
			// rockets (wide spread)
			double range = (num-1);
			double step = range/(num-1);
			s = -range*0.5;
			for ( int i=0; i<num; i++ )
			{
				p = Spawn("UTRocket",origin+sin(s)*y*2);
				p.vel = (x2+sin(s)*y2).unit()*p.speed;
				p.target = self;
				p.tracer = invoker.LockedTarget;
				s += step;
			}
		}
	}

	// lock-on check
	action void A_CheckTarget()
	{
		let t = ThinkerIterator.Create("Actor");
		Actor a;
		double closest = double.max;
		invoker.LockedTarget = null;
		while ( a = Actor(t.Next()) )
		{
			if ( !a.bSHOOTABLE || (a.Health <= 0) || a.bKilled || a.bCorpse || (a == self) || isTeammate(a) || !CheckSight(a) ) continue;
			Vector3 viewdir = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
			Vector3 reldir = level.Vec3Diff(Vec2OffsetZ(0,0,player.viewz),a.Vec2OffsetZ(0,0,a.pos.z+a.height*0.5));
			double reldist = reldir.length();
			if ( reldist > 2000 ) continue;
			if ( reldir.unit() dot viewdir < 0.99 ) continue;
			if ( reldist < closest )
			{
				closest = reldist;
				invoker.LockedTarget = a;
			}
		}
		if ( invoker.LockedTarget ) A_PlaySound("utrl/seeklock",CHAN_6);
		else if ( invoker.LockedOn ) A_PlaySound("utrl/seeklost",CHAN_6);
		if ( invoker.LockedTarget ) invoker.LockedOn = true;
	}

	Default
	{
		Tag "Rocket Launcher";
		Inventory.PickupMessage "You got the Rocket Launcher.";
		Weapon.UpSound "utrl/select";
		Weapon.SlotNumber 9;
		Weapon.SelectionOrder 1;
		Weapon.AmmoType "UTRocketAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "UTRocketAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 6;
	}
	States
	{
	Spawn:
		EBLP A -1;
		Stop;
		EBLP B -1;
		Stop;
	Select:
		EBLS A 1 A_Raise(int.max);
		Wait;
	Ready:
		EBLS ABCDEFGHIJKLMNOPQRST 1;
	Idle:
		EBLI A 1
		{
			invoker.locktics = 0;
			A_CheckReload();
			A_WeaponReady();
		}
		EBLI A 1
		{
			A_CheckReload();
			A_WeaponReady();
			invoker.locktics++;
			if ( invoker.locktics > 42 )
			{
				invoker.locktics = 0;
				A_CheckTarget();
			}
		}
		Wait;
	Fire:
	AltFire:
		// one is loaded already
		EBLI A 3 A_LoadRocket(false);
		EBLI A 0 A_LoadedRefire(1);
		Goto FireOne;
		// load two
		EBLI A 2;
		EBL1 A 0;
		EBR1 A 2 A_PlaySound("utrl/rotate",CHAN_WEAPON,0.1);
		EBR1 B 0 A_Refire(1);
		Goto FireOne;
		EBR1 B 2;
		EBR1 C 0 A_Refire(1);
		Goto FireOne;
		EBR1 C 2;
		EBR1 D 0 A_Refire(1);
		Goto FireOne;
		EBR1 D 2;
		EBR1 E 0 A_Refire(1);
		Goto FireOne;
		EBR1 E 2;
		EBR1 F 0 A_Refire(1);
		Goto FireOne;
		EBR1 F 2;
		EBR1 G 0 A_Refire(1);
		Goto FireOne;
		EBR1 G 2;
		EBL2 A 0 A_Refire(1);
		Goto FireOne;
		EBL2 A 3 A_PlaySound("utrl/load",CHAN_WEAPON);
		EBL2 B 0 A_Refire(1);
		Goto FireOne;
		EBL2 B 3;
		EBL2 C 0 A_Refire(1);
		Goto FireOne;
		EBL2 C 3;
		EBL2 D 0 A_Refire(1);
		Goto FireOne;
		EBL2 D 3;
		EBL2 E 0 A_Refire(1);
		Goto FireOne;
		EBL2 E 3;
		EBL2 F 0 A_Refire(1);
		Goto FireOne;
		EBL2 F 3;
		EBL2 G 0 A_Refire(1);
		Goto FireOne;
		EBL2 G 3 A_LoadRocket();
		EBR2 A 0 A_LoadedRefire(1);
		Goto FireTwo;
		// load three
		EBR2 A 2 A_PlaySound("utrl/rotate",CHAN_WEAPON,0.1);
		EBR2 B 0 A_Refire(1);
		Goto FireTwo;
		EBR2 B 2;
		EBR2 C 0 A_Refire(1);
		Goto FireTwo;
		EBR2 C 2;
		EBR2 D 0 A_Refire(1);
		Goto FireTwo;
		EBR2 D 2;
		EBR2 E 0 A_Refire(1);
		Goto FireTwo;
		EBR2 E 2;
		EBR2 F 0 A_Refire(1);
		Goto FireTwo;
		EBR2 F 2;
		EBR2 G 0 A_Refire(1);
		Goto FireTwo;
		EBR2 G 2;
		EBL3 A 0 A_Refire(1);
		Goto FireTwo;
		EBL3 A 3 A_PlaySound("utrl/load",CHAN_WEAPON);
		EBL3 B 0 A_Refire(1);
		Goto FireTwo;
		EBL3 B 3;
		EBL3 C 0 A_Refire(1);
		Goto FireTwo;
		EBL3 C 3;
		EBL3 D 0 A_Refire(1);
		Goto FireTwo;
		EBL3 D 3;
		EBL3 E 0 A_Refire(1);
		Goto FireTwo;
		EBL3 E 3;
		EBL3 F 0 A_Refire(1);
		Goto FireTwo;
		EBL3 F 3;
		EBL3 G 0 A_Refire(1);
		Goto FireTwo;
		EBL3 G 3 A_LoadRocket();
		EBR3 A 0 A_LoadedRefire(1);
		Goto FireThree;
		// load four
		EBR3 A 2 A_PlaySound("utrl/rotate",CHAN_WEAPON,0.1);
		EBR3 B 0 A_Refire(1);
		Goto FireThree;
		EBR3 B 2;
		EBR3 C 0 A_Refire(1);
		Goto FireThree;
		EBR3 C 2;
		EBR3 D 0 A_Refire(1);
		Goto FireThree;
		EBR3 D 2;
		EBR3 E 0 A_Refire(1);
		Goto FireThree;
		EBR3 E 2;
		EBR3 F 0 A_Refire(1);
		Goto FireThree;
		EBR3 F 2;
		EBR3 G 0 A_Refire(1);
		Goto FireThree;
		EBR3 G 2;
		EBL4 A 0 A_Refire(1);
		Goto FireThree;
		EBL4 A 3 A_PlaySound("utrl/load",CHAN_WEAPON);
		EBL4 B 0 A_Refire(1);
		Goto FireThree;
		EBL4 B 3;
		EBL4 C 0 A_Refire(1);
		Goto FireThree;
		EBL4 C 3;
		EBL4 D 0 A_Refire(1);
		Goto FireThree;
		EBL4 D 3;
		EBL4 E 0 A_Refire(1);
		Goto FireThree;
		EBL4 E 3;
		EBL4 F 0 A_Refire(1);
		Goto FireThree;
		EBL4 F 3;
		EBL4 G 0 A_Refire(1);
		Goto FireThree;
		EBL4 G 3 A_LoadRocket();
		EBR4 A 0 A_LoadedRefire(1);
		Goto FireFour;
		// load five
		EBR4 A 2 A_PlaySound("utrl/rotate",CHAN_WEAPON,0.1);
		EBR4 B 0 A_Refire(1);
		Goto FireFour;
		EBR4 B 2;
		EBR4 C 0 A_Refire(1);
		Goto FireFour;
		EBR4 C 2;
		EBR4 D 0 A_Refire(1);
		Goto FireFour;
		EBR4 D 2;
		EBR4 E 0 A_Refire(1);
		Goto FireFour;
		EBR4 E 2;
		EBR4 F 0 A_Refire(1);
		Goto FireFour;
		EBR4 F 2;
		EBR4 G 0 A_Refire(1);
		Goto FireFour;
		EBR4 G 2;
		EBL5 A 0 A_Refire(1);
		Goto FireFour;
		EBL5 A 3 A_PlaySound("utrl/load",CHAN_WEAPON);
		EBL5 B 0 A_Refire(1);
		Goto FireFour;
		EBL5 B 3;
		EBL5 C 0 A_Refire(1);
		Goto FireFour;
		EBL5 C 3;
		EBL5 D 0 A_Refire(1);
		Goto FireFour;
		EBL5 D 3;
		EBL5 E 0 A_Refire(1);
		Goto FireFour;
		EBL5 E 3;
		EBL5 F 0 A_Refire(1);
		Goto FireFour;
		EBL5 F 3;
		EBL5 G 0 A_Refire(1);
		Goto FireFour;
		EBL5 G 3 A_LoadRocket();
		EBR5 A 0 A_LoadedRefire(1);
		Goto FireFive;
		// load six
		EBR5 A 2 A_PlaySound("utrl/rotate",CHAN_WEAPON,0.1);
		EBR5 B 0 A_Refire(1);
		Goto FireFive;
		EBR5 B 2;
		EBR5 C 0 A_Refire(1);
		Goto FireFive;
		EBR5 C 2;
		EBR5 D 0 A_Refire(1);
		Goto FireFive;
		EBR5 D 2;
		EBR5 E 0 A_Refire(1);
		Goto FireFive;
		EBR5 E 2;
		EBR5 F 0 A_Refire(1);
		Goto FireFive;
		EBR5 F 2;
		EBR5 G 0 A_Refire(1);
		Goto FireFive;
		EBR5 G 2;
		EBL6 A 0 A_Refire(1);
		Goto FireFive;
		EBL6 A 3 A_PlaySound("utrl/load",CHAN_WEAPON);
		EBL6 B 0 A_Refire(1);
		Goto FireFive;
		EBL6 B 3;
		EBL6 C 0 A_Refire(1);
		Goto FireFive;
		EBL6 C 3;
		EBL6 D 0 A_Refire(1);
		Goto FireFive;
		EBL6 D 3;
		EBL6 E 0 A_Refire(1);
		Goto FireFive;
		EBL6 E 3;
		EBL6 F 0 A_Refire(1);
		Goto FireFive;
		EBL6 F 3 A_LoadRocket();
		Goto FireSix;
	FireOne:
		EBF1 A 0 A_FireRockets(1);
		EBF1 ABCDEFGH 2;
		EBLI A 2;
		Goto Idle;
	FireTwo:
		EBF2 A 0 A_FireRockets(2);
		EBF2 ABCDEFGHIJK 2;
		EBLI A 2;
		Goto Idle;
	FireThree:
		EBF3 A 0 A_FireRockets(3);
		EBF3 ABCDEFGHIJ 2;
		EBLI A 2;
		Goto Idle;
	FireFour:
		EBF4 A 0 A_FireRockets(4);
		EBF4 ABCDEFGHIJK 2;
		EBLI A 2;
		Goto Idle;
	FireFive:
		EBF5 A 0 A_FireRockets(5);
		EBF5 ABCDEFGHIJKLM 2;
		EBLI A 2;
		Goto Idle;
	FireSix:
		EBF6 A 0 A_FireRockets(6);
		EBF6 ABCDEFGHIJKLMNOP 2;
		EBLI A 2;
		Goto Idle;
	Deselect:
		EBLD ABCDEFGHIJK 1;
		EBLD K 1 A_Lower(int.max);
		Wait;
	}
}
