Class PeaceAmmo : Ammo
{
	Default
	{
		Inventory.Icon "I_Peace";
		Inventory.Amount 1;
		Inventory.MaxAmount 1;
		Ammo.BackpackAmount 0;
		Ammo.BackpackMaxAmount 2;
	}
	override bool TryPickup( in out Actor toucher )
	{
		if ( !sting_proto ) return false;	// not allowed
		return Super.TryPickup(toucher);
	}
	override void Tick()
	{
		Super.Tick();
		if ( sting_proto ) return;
		if ( Owner ) Owner.RemoveInventory(self);
		Destroy();
	}
}

Class PeaceLight : RocketLight
{
	Default
	{
		Args 255,128,112,32;
	}
}

Class PeaceTrail : Actor
{
	Default
	{
		RenderStyle "Add";
		Radius 0.1;
		Height 0;
		+NOBLOCKMAP;
		+NOGRAVITY;
		+DONTSPLASH;
		+FORCEXYBILLBOARD;
		+NOTELEPORT;
		Scale 0.25;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		let l = Spawn("PeaceLight",pos);
		l.target = self;
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target || !target.bMISSILE )
		{
			Destroy();
			return;
		}
		Vector3 dir = dt_Utility.Vec3FromAngle(target.angle,target.pitch);
		SetOrigin(level.Vec3Offset(target.pos,-dir*3),true);
	}
	States
	{
	Spawn:
		PFLA A -1 Bright;
		Stop;
	}
}

Class PeaceRocket : Actor
{
	Vector3 Acceleration;
	int ticcnt;
	Default
	{
		Obituary "$O_PEACE";
		DamageType 'PeaceDeath';
		Radius 2;
		Height 2;
		Speed 3.5;
		PROJECTILE;
		+SKYEXPLODE;
		+EXPLODEONWATER;
		+SEEKERMISSILE;
		+FORCERADIUSDMG;
		+NODAMAGETHRUST;
		+INTERPOLATEANGLES;
		+HITOWNER;
		+NOFRICTION;
		ReactionTime 9;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		let l = Spawn("PeaceTrail",pos);
		l.target = self;
		A_StartSound("peace/fly",CHAN_VOICE,CHANF_LOOPING,1.,2.5,.8);
		Acceleration = vel*1.67;
	}
	action void A_CheckForTargets()
	{
		let bi = BlockThingsIterator.Create(self,500);
		double mindist = double.infinity;
		while ( bi.Next() )
		{
			if ( !bi.Thing || (!bi.Thing.bISMONSTER && !bi.Thing.player) || (bi.Thing.Health <= 0) || (Distance3D(bi.Thing) > 500) || !CheckSight(bi.Thing) ) continue;
			if ( sting_peacehome && ((bi.Thing == target) || (target && target.IsFriend(bi.Thing))) ) continue;
			double dist = Distance3D(bi.Thing);
			if ( dist > mindist ) break;
			tracer = bi.Thing;
			mindist = dist;
		}
	}
	action void A_SeekTargets()
	{
		if ( !tracer || (tracer.Health <= 0) ) return;
		double mag = vel.length();
		vel = mag*(level.Vec3Diff(pos,tracer.Vec3Offset(0,0,tracer.height/2)).unit()*mag*6./TICRATE+vel).unit();
	}
	action void A_PeaceExplode()
	{
		bFORCEXYBILLBOARD = true;
		A_SetRenderStyle(1.0,STYLE_Add);
		A_SprayDecal("RocketBlast",50);
		A_NoGravity();
		A_SetScale(1.1);
		UTMainHandler.DoBlast(self,200,70000);
		A_Explode(100,200);
		A_QuakeEx(3,3,3,8,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:0.2);
		A_StartSound("eightball/explode",CHAN_VOICE);
		A_AlertMonsters();
		Spawn("RocketExplLight",pos);
		int numpt = Random[Peace](15,30);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Peace](-1,1),FRandom[Peace](-1,1),FRandom[Peace](-1,1)).unit()*FRandom[Peace](1,3);
			let s = Spawn("UTSmoke",pos);
			s.vel = pvel;
		}
		numpt = Random[Peace](10,20);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Peace](-1,1),FRandom[Peace](-1,1),FRandom[Peace](-1,1)).unit()*FRandom[Peace](2,6);
			let s = Spawn("UTSpark",pos);
			s.vel = pvel;
		}
		numpt = Random[Peace](35,70);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Peace](-1,1),FRandom[Peace](-1,1),FRandom[Peace](-1,1)).unit()*FRandom[Peace](2,12);
			let s = Spawn("UTChip",pos);
			s.vel = pvel;
			s.scale *= FRandom[Peace](0.9,2.7);
		}
	}
	States
	{
	Spawn:
		PEMR A 1
		{
			roll += 5.;
			A_SeekTargets();
			vel += Acceleration/TICRATE;
			double mag = vel.length();
			if ( mag > 0. )
			{
				Vector3 dir = vel/mag;
				if ( mag > 16 ) vel = vel.unit()*16;
				angle = atan2(dir.y,dir.x);
				pitch = asin(-dir.z);
				mag = Acceleration.length();
				Acceleration = dir*mag;
			}
			for ( int i=0; i<3; i++ )
			{
				let s = Spawn("UTSmoke",pos);
				s.vel = (FRandom[Peace](-0.2,0.2),FRandom[Peace](-0.2,0.2),FRandom[Peace](-0.2,0.2));
				s.vel += vel*0.1;
				s.SetShade(Color(1,1,1)*Random[Peace](32,48));
			}
			special1++;
			if ( special1 > 35 )
			{
				special1 = 0;
				A_CheckForTargets();
				A_CountDown();
			}
			// early check
			if ( GetAge() == 5 ) A_CheckForTargets();
		}
		Wait;
	Death:
		TNT1 A 0 A_PeaceExplode();
		SSMX ABCDEFGHIJ 2 Bright;
		Stop;
	}
}

Class PeaceFragment : SentryFragment
{
	Default
	{
		Scale 0.5;
	}
}

Class PeaceBarrel : Actor
{
	Mixin UMissileFix;

	action void A_AlignSelf()
	{
		// find closest 3d floor for its normal
		F3DFloor ff = null;
		for ( int i=0; i<floorsector.Get3DFloorCount(); i++ )
		{
			if ( !(floorsector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
			ff = floorsector.Get3DFLoor(i);
			break;
		}
		Vector3 normal;
		if ( ff ) normal = -ff.top.Normal;
		else normal = floorsector.floorplane.Normal;
		// bless Gutawer
		Vector2 angv = (cos(angle),sin(angle));
		Vector3 x;
		if ( ff ) x = (angv,ff.top.ZAtPoint(pos.xy+angv)-pos.z).unit();
		else x = (angv,floorsector.floorplane.ZAtPoint(pos.xy+angv)-pos.z).unit();
		pitch = -asin(x.z);
		roll = asin(normal.x*angv.y-normal.y*angv.x);
	}
	action void A_FireRocket( int n )
	{
		A_StartSound("uflak/altfire",CHAN_WEAPON,CHANF_OVERLAP);
		A_AlertMonsters();
		Vector3 x, y, z;
		[x, y, z] = dt_Utility.GetAxes(angle,pitch,roll);
		Vector3 dir = z*1.3;
		Vector3 origin;
		switch ( n )
		{
		case 0:
			// front right
			dir = (dir+x+y).unit();
			origin = level.Vec3Offset(pos,z*4+x*4+y*4);
			break;
		case 1:
			// front left
			dir = (dir-x+y).unit();
			origin = level.Vec3Offset(pos,z*4-x*4+y*4);
			break;
		case 2:
			// back right
			dir = (dir+x-y).unit();
			origin = level.Vec3Offset(pos,z*4+x*4-y*4);
			break;
		case 3:
			// back left
			dir = (dir-x-y).unit();
			origin = level.Vec3Offset(pos,z*4-x*4-y*4);
			break;
		}
		let r = Spawn("PeaceRocket",origin);
		r.angle = atan2(dir.y,dir.x);
		r.pitch = asin(-dir.z);
		r.vel = dir*r.speed;
		r.target = target;
	}
	action void A_BlowUp()
	{
		bFORCEXYBILLBOARD = true;
		SetOrigin(Vec3Offset(0,0,16),false);
		A_SetRenderStyle(1.0,STYLE_Add);
		A_SprayDecal("RocketBlast",50);
		A_NoGravity();
		A_SetScale(bAMBUSH?1.8:1.2);
		A_Explode(bAMBUSH?500:125,bAMBUSH?200:100);
		A_StartSound("sentry/explode",CHAN_VOICE,CHANF_OVERLAP,pitch:.6);
		A_StartSound("eightball/explode",CHAN_VOICE,CHANF_OVERLAP,pitch:.8);
		A_AlertMonsters();
		A_QuakeEx(4,4,4,10,0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:0.2);
		UTMainHandler.DoBlast(self,bAMBUSH?200:100,50000);
		double ang, pt;
		for ( int i=0; i<16; i++ )
		{
			let f = Spawn("PeaceFragment",Vec3Offset(FRandom[EFrag](-8,8),FRandom[EFrag](-8,8),FRandom[EFrag](4,12)));
			ang = FRandom[EFrag](0,360);
			pt = FRandom[EFrag](-90,90);
			f.vel = dt_Utility.Vec3FromAngle(ang,pt)*FRandom[EFrag](4,20);
			f.vel.z += 3.;
		}
		int numpt = Random[ExploS](20,35);
		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,9);
			let s = Spawn("UTSmoke",pos);
			s.vel = pvel;
			s.scale *= 2.;
		}
		numpt = Random[ExploS](10,20);
		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](20,30);
		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);
	}
	Default
	{
		Obituary "$O_PEACEMK";
		Radius 6;
		Height 9;
		DamageType 'PeaceBarrelDeath';
		PROJECTILE;
		-NOGRAVITY;
		+SKYEXPLODE;
		+FORCERADIUSDMG;
		+NODAMAGETHRUST;
		+MOVEWITHSECTOR;
		+CANBOUNCEWATER;
		+BOUNCEAUTOOFF;
		+BOUNCEAUTOOFFFLOORONLY;
		+USEBOUNCESTATE;
		+INTERPOLATEANGLES;
		Speed 8;
		BounceType "Hexen";
		BounceFactor 0.3;	// otherwise it's too damn bouncy
		BounceSound "transloc/bounce";
		WallBounceFactor 0.8;
		Gravity 0.35;
	}
	States
	{
	Spawn:
		PEMM A -1;
		Stop;
	Bounce:
		PEMM A 0
		{
			if ( BlockingFloor ) A_AlignSelf();
			else pitch = roll = 0;
			if ( vel.length() < 2. ) ClearBounce();
		}
		Goto Spawn;
	Death:
		PEMM A 1 A_CheckFloor(1);
		Wait;
		PEMM A 4 A_AlignSelf();
		PEMM A 35
		{
			A_StartSound((special1<=0)?"eightball/seeklost":"eightball/seeklock",CHAN_VOICE,CHANF_OVERLAP);
			A_AlertMonsters();
			return A_JumpIf(--special1<0,1);
		}
		Wait;
		PEMM A 0 A_JumpIf(bAMBUSH,"Detonate");
		PEMM A 3 A_StartSound("peace/open",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
		PEMM B 3;
		PEMM C 3 A_StartSound("peace/open",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
		PEMM D 3 A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
		PEMM E 3 A_StartSound("peace/open",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
		PEMM F 3
		{
			A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
			A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.5,pitch:FRandom[Peace](0.8,1.2));
		}
		PEMM G 3 A_StartSound("peace/open",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
		PEMM H 3
		{
			A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
			A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.5,pitch:FRandom[Peace](0.8,1.2));
		}
		PEMM I 3;
		PEMM J 3
		{
			A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.8,pitch:FRandom[Peace](0.8,1.2));
			A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.5,pitch:FRandom[Peace](0.8,1.2));
		}
		PEMM K 3 A_StartSound("eightball/rotate",CHAN_BODY,CHANF_OVERLAP,.1);
		PEMM L 3 A_StartSound("transloc/bounce",CHAN_BODY,CHANF_OVERLAP,.5,pitch:FRandom[Peace](0.8,1.2));
		PEMM M 3;
		PEMM N 3 A_StartSound("eightball/rotate",CHAN_BODY,CHANF_OVERLAP,.1);
		PEMM OPQ 3;
		PEMM R 3 A_StartSound("eightball/rotate",CHAN_BODY,CHANF_OVERLAP,.1);
		PEMM STU 3;
		PEMM V 3 A_StartSound("eightball/rotate",CHAN_BODY,CHANF_OVERLAP,.1);
		PEMM XYZ[\] 3;
		PEMM ] 35;
		PEMM ] 0 A_FireRocket(0);
		PEML A 20;
		PEML A 0 A_FireRocket(1);
		PEML B 20;
		PEML B 0 A_FireRocket(2);
		PEML C 20;
		PEML C 0 A_FireRocket(3);
		PEML D 20;
	Detonate:
		PEMM A 20;
	BlowUp:
		TNT1 A 0 A_BlowUp();
		SSMX ABCDEFGHIJ 2 Bright;
		Stop;
	}
}

Class Peacemaker : UnrealWeapon
{
	override bool TryPickup( in out Actor toucher )
	{
		if ( !sting_proto ) return false;	// not allowed
		return Super.TryPickup(toucher);
	}
	override void Tick()
	{
		Super.Tick();
		if ( sting_proto ) return;
		if ( Owner ) Owner.RemoveInventory(self);
		Destroy();
	}
	action void A_PeacemakerThrow( bool bAlt = false )
	{
		let weap = Weapon(invoker);
		if ( weap.Ammo1.Amount <= 0 ) return;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		invoker.FireEffect();
		A_StartSound("peace/throw",CHAN_WEAPON);
		Vector3 x, y, z;
		[x, y, z] = dt_Utility.GetAxes(angle,pitch,roll);
		Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+y*5-z*3);
		let p = Spawn("PeaceBarrel",origin);
		if ( bAlt ) p.bAMBUSH = true;
		p.special1 = invoker.special2;
		p.angle = angle;
		p.pitch = BulletSlope();
		p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
		p.vel.z += 2.;
		p.target = self;
		if ( weap.Ammo1.Amount <= 0 ) player.SetPSprite(PSP_WEAPON,invoker.FindState("EmptyIdle"));
		else
		{
			invoker.PlayUpSound(self);
			player.SetPSprite(PSP_WEAPON,invoker.FindState("Ready"));
		}
	}
	action void A_StartCount()
	{
		invoker.special1 = invoker.special2 = 0;
		A_StartSound("peace/set",CHAN_WEAPONMISC,CHANF_OVERLAP,.4);
	}
	action void A_CountUp( Statelabel next )
	{
		invoker.special1++;
		if ( invoker.special1 > 20 )
		{
			invoker.special2++;
			if ( invoker.special2 <= 9 )
			{
				A_StartSound("peace/set",CHAN_WEAPONMISC,CHANF_OVERLAP,.4);
				player.FindPSprite(PSP_WEAPON).frame = invoker.special2;
			}
			invoker.special1 = 0;
		}
		if ( (invoker.special2 >= 10) || !(player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK)) )
		{
			invoker.special2 = min(9,invoker.special2);
			player.SetPSprite(PSP_WEAPON,invoker.FindState(next));
		}
	}
	Default
	{
		Tag "$T_PEACE";
		Inventory.PickupMessage "$I_PEACE";
		Weapon.UpSound "peace/select";
		Weapon.SlotNumber 8;
		Weapon.SelectionOrder 1050;
		Weapon.SlotPriority 0.9;
		Weapon.AmmoType "PeaceAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "PeaceAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 1;
		UTWeapon.DropAmmo 1;
	}
	States
	{
	Spawn:
		PEMP A -1;
		Stop;
		PEMP B -1;
		Stop;
	Select:
		PEMS A 1 A_Raise(int.max);
		Wait;
	Ready:
		PEMS ABCDEFGHIJ 2 A_WeaponReady(WRF_NOFIRE);
		PEMS K 0 A_StartSound("peace/up",CHAN_WEAPONMISC,volume:.4);
		PEMS KLMNOPQRST 2 A_WeaponReady(WRF_NOFIRE);
	Idle:
		PEMI A 1
		{
			A_CheckReload();
			A_WeaponReady();
		}
		Wait;
	EmptyIdle:
		TNT1 A 1
		{
			let weap = Weapon(invoker);
			if ( weap.Ammo1.Amount > 0 )
				return ResolveState("Ready");
			A_CheckReload();
			A_WeaponReady(WRF_NOFIRE);
			return ResolveState(null);
		}
		Wait;
	Fire:
		PEMC A 1 A_StartCount();
		PEMC # 1 A_CountUp(1);
		Wait;
		PEMF ABCD 2;
		PEMF E 0 A_StartSound("peace/down",CHAN_WEAPONMISC,volume:.4);
		PEMF EFG 2;
		PEMF HI 2; // hello
		PEMF I -1 A_PeacemakerThrow();
		Stop;
	AltFire:
		PEMC A 1 A_StartCount();
		PEMC # 1 A_CountUp(1);
		Wait;
		PEMF ABCD 2;
		PEMF E 0 A_StartSound("peace/down",CHAN_WEAPONMISC,volume:.4);
		PEMF EFG 2;
		PEMF HI 2; // howdy
		PEMF I -1 A_PeacemakerThrow(true);
		Stop;
	Deselect:
		PEMD A 0 A_JumpIfNoAmmo("EmptyDeselect");
		PEMD ABCDEFGHI 1;
		PEMD J 1 A_Lower(int.max);
	EmptyDeselect:
		TNT1 A 1 A_Lower(int.max);
		Wait;
	}
}
