Class RazorAmmo : Ammo
{
	Default
	{
		Tag "$T_RIPPERAMMO";
		Inventory.Icon "I_RazorA";
		Inventory.PickupMessage "$I_RIPPERAMMO";
		Inventory.Amount 25;
		Inventory.MaxAmount 75;
		Ammo.BackpackAmount 15;
		Ammo.BackpackMaxAmount 150;
		Ammo.DropAmount 15;
	}
	States
	{
	Spawn:
		BHOP A 8 A_CheckProximity(1,"PlayerPawn",80,1,CPXF_ANCESTOR|CPXF_CHECKSIGHT);
		Wait;
		BHOP AA 16;
		BHOP B 8 A_CheckProximity(1,"PlayerPawn",80,0,CPXF_ANCESTOR|CPXF_CHECKSIGHT|CPXF_EXACT);
		Goto Spawn+3;
		BHOP BB 16;
		Goto Spawn;
	}
}

Class RazorBlade : Actor
{
	Vector3 gvel, accel;
	double oldgangle, gangle, oldgpitch, gpitch;

	Default
	{
		Radius 2;
		Height 2;
		Speed 25;
		DamageFunction (int((30+special1*10)*((DamageType=='Decapitated')?3.5:1.0)));
		DamageType 'Shredded';
		Obituary "$O_RAZORJACK";
		BounceType "Hexen";
		ReactionTime 5;
		BounceFactor 1.0;
		WallBounceFactor 1.0;
		PROJECTILE;
		+USEBOUNCESTATE;
		+SKYEXPLODE;
		+CANBOUNCEWATER;
		+NODAMAGETHRUST;
		+DONTBOUNCEONSHOOTABLES;
		+ROLLSPRITE;
		+NOFRICTION;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		let t = Spawn("RazorBladeTrail",pos);
		t.target = self;
		frame = special1;
		if ( special1 > 0 )
		{
			vel *= 1.+.06+special1;
			A_StartSound("razorjack/hum",CHAN_VOICE,CHANF_LOOPING,1.,3.,3.125+special1*.2);
		}
		else A_StartSound("razorjack/hum",CHAN_VOICE,CHANF_LOOPING,1.,3.,3.125);
		Vector3 dir = vel.unit();
		A_SetAngle(atan2(dir.y,dir.x));
		A_SetPitch(asin(-dir.z));
		frame = 0;
		gvel = vel;
		oldgangle = gangle = angle;
		oldgpitch = gpitch = pitch;
	}
	override int SpecialMissileHit( Actor victim )
	{
		if ( pos.z > victim.pos.z+victim.height*0.81 ) DamageType = 'Decapitated';
		return -1;
	}
	override int DoSpecialDamage( Actor target, int damage, Name damagetype )
	{
		if ( !target.bNOBLOOD )
		{
			target.SpawnBlood(pos,AngleTo(target),damage);
			A_StartSound("razorjack/flesh");
			A_AlertMonsters();
		}
		UTMainHandler.DoKnockback(target,vel.unit(),15000);
		return damage;
	}
	void A_RazorAltOrient()
	{
		if ( !bAMBUSH || !target || !target.player || (target.Health <= 0) ) return;
		if ( sting_razoraim )
		{
			FLineTraceData d;
			target.LineTrace(target.angle,10000,target.pitch,0,target.player.viewheight,data:d);
			Vector3 SeekingDir = level.Vec3Diff(pos,d.HitLocation).unit();
			double MagnitudeVel = Vel.length();
			SeekingDir = (SeekingDir*0.1*MagnitudeVel+Vel).unit();
			vel = MagnitudeVel * SeekingDir;
			accel = SeekingDir * 25.;
			vel += accel/TICRATE;
			vel = vel.unit()*clamp(MagnitudeVel,10,15);
		}
		else
		{
			double dangle, dpitch;
			dangle = deltaangle(oldgangle,target.angle);
			dpitch = deltaangle(oldgpitch,target.pitch);
			gangle += dangle;
			gpitch += dpitch;
			vel += dt_Utility.Vec3FromAngle(gangle,gpitch)*15./TICRATE;
			double spd = vel.length();
			vel = vel*clamp(spd,10,15)/spd;
			gvel = vel;
			oldgangle = target.angle;
			oldgpitch = target.pitch;
		}
		Vector3 dir = vel.unit();
		A_SetAngle(atan2(dir.y,dir.x));
		A_SetPitch(asin(-dir.z));
	}
	action void A_RazorHit()
	{
		A_StartSound("razorjack/hit");
		A_AlertMonsters();
		A_SprayDecal("WallCrack",-20);
		int numpt = Random[Ripper](5,10);
		Vector3 x = dt_Utility.Vec3FromAngle(angle,pitch);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (-x+(FRandom[Ripper](-.8,.8),FRandom[Ripper](-.8,.8),FRandom[Ripper](-.8,.8))).unit()*FRandom[Ripper](0.1,1.2);
			let s = Spawn("UTSmoke",pos);
			s.vel = pvel;
			s.SetShade(Color(1,1,1)*Random[Ripper](128,192));
		}
		numpt = Random[Ripper](4,12);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Ripper](-1,1),FRandom[Ripper](-1,1),FRandom[Ripper](-1,1)).unit()*FRandom[Ripper](2,8);
			let s = Spawn("UTSpark",pos);
			s.vel = pvel;
		}
		numpt = Random[Ripper](4,8);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Ripper](-1,1),FRandom[Ripper](-1,1),FRandom[Ripper](-1,1)).unit()*FRandom[Ripper](2,8);
			let s = Spawn("UTChip",pos);
			s.vel = pvel;
		}
	}
	States
	{
	Spawn:
		RAZB # 1 A_RazorAltOrient();
		Wait;
	Bounce:
		RAZB # 0
		{
			bHITOWNER = true;	// technically the Unreal version sets this 0.2 seconds after being fired, but this works better
			Vector3 dir = vel.unit();
			A_SetAngle(atan2(dir.y,dir.x));
			A_SetPitch(asin(-dir.z));
			A_RazorHit();
			A_CountDown();
		}
		Goto Spawn;
	Death:
		TNT1 A 0 A_RazorHit();
	XDeath:
		TNT1 A 1 A_StopSound(CHAN_VOICE);
		Stop;
	}
}

Class RazorLight : DynamicLight
{
	Default
	{
		Args 160, 0, 240, 40;
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target )
		{
			Destroy();
			return;
		}
		Args[0] = int(160*target.alpha);
		Args[2] = int(240*target.alpha);
		SetOrigin(target.pos,true);
	}
}

Class RazorBladeTrail : Actor
{
	Default
	{
		RenderStyle "Add";
		+NOGRAVITY;
		+NOCLIP;
		+DONTSPLASH;
		+NOTELEPORT;
		Radius 0.1;
		Height 0;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		let l = Spawn("RazorLight",pos);
		l.target = self;
	}
	override void Tick()
	{
		if ( !target || target.InStateSequence(target.CurState,target.ResolveState("Death")) )
		{
			Destroy();
			return;
		}
		SetOrigin(target.pos,true);
		angle = target.angle;
		pitch = target.pitch;
		roll = target.roll;
		alpha = min(target.vel.length()/target.speed,1.);
	}
	States
	{
	Spawn:
		RAZB A -1 Bright;
		Stop;
	}
}

Class Razorjack : UnrealWeapon
{
	action void A_RazorFire( bool bAlt = false )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		if ( self is 'UTPlayer' )
			UTPlayer(self).PlayAttacking3();
		A_StartSound("razorjack/fire",CHAN_WEAPON,CHANF_OVERLAP,pitch:bAlt?1.:(1.+invoker.special1*.1));
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(16,255,0,255),1);
		A_AlertMonsters();
		A_QuakeEx(1,1,1,5,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.08);
		Vector3 x, y, z;
		[x, y, z] = dt_Utility.GetAxes(angle,pitch,roll);
		Vector3 origin = Vec2OffsetZ(0,0,player.viewz);
		if ( bAlt ) origin = level.Vec3Offset(origin,x*10-y*4-z*5);
		else origin = level.Vec3Offset(origin,x*10-z*9);
		Actor p;
		p = Spawn("RazorBlade",origin);
		if ( bAlt )
		{
			p.roll = 70;
			p.bAMBUSH = true;
		}
		else p.special1 = invoker.special1;
		p.angle = angle;
		p.pitch = BulletSlope();
		p.vel = dt_Utility.Vec3FromAngle(p.angle,p.pitch)*p.speed;
		p.target = self;
	}
	Default
	{
		Tag "$T_RAZORJACK";
		Inventory.PickupMessage "$I_RAZORJACK";
		Weapon.UpSound "razorjack/select";
		Weapon.SlotNumber 7;
		Weapon.SelectionOrder 1500;
		Weapon.SlotPriority 1;
		Weapon.AmmoType "RazorAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "RazorAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 15;
		UTWeapon.DropAmmo 10;
	}
	States
	{
	Spawn:
		RZRP A -1;
		Stop;
		RZRP B -1;
		Stop;
	Select:
		RZRS A 1 A_Raise(int.max);
		Wait;
	Ready:
		RZRS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1 A_WeaponReady(WRF_NOFIRE);
		RZS2 ABCD 1 A_WeaponReady(WRF_NOFIRE);
	Idle:
		RZRI ABCDEFGHIJKLMNOPQRSUVWXYZ 2
		{
			A_CheckReload();
			A_WeaponReady();
		}
		RZI2 ABCDE 2
		{
			A_CheckReload();
			A_WeaponReady();
		}
		Loop;
	Fire:
		RZRI A 0
		{
			invoker.special1 = 0;
			return A_JumpIf(sting_razor,"ChargeUp");
		}
	Release:
		RZRF A 0 A_RazorFire();
		RZRF ABCDEFGH 2;
		Goto Idle;
	ChargeUp:
		RZRI ABCDEFGHIJKLMNOPQRSUVWXYZ 1 A_JumpIf(!(player.cmd.buttons&BT_ATTACK),"Release");
		RZI2 ABCDE 1 A_JumpIf(!(player.cmd.buttons&BT_ATTACK),"Release");
		RZRI A 0 { invoker.special1 = 1; }
		RZRI ACEGIKMOQSVXZ 1 A_JumpIf(!(player.cmd.buttons&BT_ATTACK),"Release");
		RZI2 BD 1 A_JumpIf(!(player.cmd.buttons&BT_ATTACK),"Release");
		RZRI A 0 { invoker.special1 = 2; }
		RZRI ADGJMPSWZ 1 A_JumpIf(!(player.cmd.buttons&BT_ATTACK),"Release");
		RZI2 C 1 A_JumpIf(!(player.cmd.buttons&BT_ATTACK),"Release");
		RZRI A 0 { invoker.special1 = 3; }
		Goto Release;
	AltFire:
		RZRA ABCDEFGHIJKL 1;
	AltHold:
		RZRA M 0 A_RazorFire(true);
		RZRA MNOP 4;
		RZRA Q 0 A_ReFire("AltHold");
		RZRA QRSTU 2;
		Goto Idle;
	Deselect:
		RZRD ABCDEF 1;
		RZRD F 1 A_Lower(int.max);
		Wait;
	}
}
