#region Gnus
//$color 14

class Sandman : GuncasterWeapon
{
    Default
    {
		//$Title Sandman
		Inventory.PickupSound "Guncaster/Laugh1";
		Inventory.PickupMessage "$GETSANDMAN";
		Obituary "%o was blast'n'shredded into pieces %k's ''Sandman''.";
		Weapon.UpSound "Sandman/Up";
		Weapon.SelectionOrder 7;
		Weapon.AmmoGive 40;
		Weapon.AmmoType "PulverizerAmmo";
		Weapon.AmmoUse 5;
		Weapon.SlotNumber 4;
		Weapon.BobStyle 'InverseSmooth';
		Weapon.BobSpeed 1.0;
		Weapon.BobRangeY 1.0;
		Weapon.BobRangeX 0.8;
		//+AMMO_OPTIONAL
	}
	
	//Strat drop.
	override void PostBeginPlay()
	{
		if(!bDROPPED && random(0,20) == 0)
		{
			A_DropItem("SandmanStratGiver",-1,256);
		}
		
		Super.PostBeginPlay();
		Return;
	}
	
	//Hexen handling.
	override Void AttachToOwner (actor user) {
		if(gameinfo.gametype == Game_HEXEN) {
			AmmoGive1 = 30;
			AmmoType1 = "BlueMana";
			AmmoUse1 = 3;
		}
		
		Super.AttachToOwner(user);
	}
	
	//A little helper for my sanity regarding returning to firing.
	action state GC_SandmanReFire() {
		//Fire.
		If(!(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) &&
			!invoker.weapSwitch && !CountInv("MenuWheelActive") && 
			GetPlayerInput(INPUT_BUTTONS) & BT_ATTACK) {
			if(CountInv("SandmanStratActive")) { Return ResolveState("StratMainReFire"); }
			Return ResolveState("MainFire");
		}
		if(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) {
			//Gunzerk Fire.
			If(OverlayID() == 3 && !invoker.weapSwitch && !CountInv("MenuWheelActive") && 
				GetPlayerInput(INPUT_BUTTONS) & BT_ATTACK) {
				if(CountInv("SandmanStratActive")) { Return ResolveState("StratMainReFire"); }
				Return ResolveState("MainFire");
			}
			//Gunzerk left fire.
			If(OverlayID() == -3 && !invoker.WeapSwitch && !CountInv("MenuWheelActive") &&
				GetPlayerInput(INPUT_BUTTONS) & BT_ALTATTACK) {
				if(CountInv("SandmanStratActive")) { Return ResolveState("StratMainReFire"); }
				Return ResolveState("MainFire");
			}
		}
		Return ResolveState(Null);
	}
	
	//Strat mode.
	const ver_fov	= 16;
	const hor_fov	= 38;
	
	// kd: We don't like certain targets, so the Sandman shouldn't pick them
	// up at all. Like your friends or statues, it's nonsense.
	bool ShouldEject (Actor mo) const {
		if(mo == NULL) {
			return false;
		}
		
		if(
		!ActorEx.IsInPlayerFov(mo, owner.player, hor_fov, ver_fov) ||
		mo.health < 1 ||
		!mo.IsHostile(owner) ||
		mo.bdormant ||
		mo.binvulnerable) {
			return true;
		}
		
		return false;
	}
	
	bool is_up;
	
	bool has_strat;
	bool old_has_strat;
	bool just_got_strat;
	bool target_is_in_fov;
	
	Gc_SandmanVisor visor_handler;
	
	bool		is_ejecting;
	FadeTick	eject_t;
	
	Gc_FovClosestMonster closest_target;
	Actor locked_mo;
	Actor old_locked_mo;
	
	private void HandleTargeting () {
		
		// kd: Targets under your crosshair are preferred, even if you already
		// have a target.
		Actor considered_mo = ActorEx.BetterAimTarget(PlayerPawn(owner));
		
		// kd: We don't want to aim at stuff like statues or invulnerable
		// monsters.
		if(ShouldEject(considered_mo)) {
			considered_mo = NULL;
		}
		
		// If your crosshair doesn't pick up anything, the Sandman will scan
		// the environment over a span of 3 tics.
		if(!locked_mo && !considered_mo) {
			bool just_lost_target = locked_mo && !old_locked_mo;
			
			if(just_lost_target || closest_target.IsDone()) {
				closest_target.Reset();
				closest_target.Begin(
					ver_fov,
					hor_fov,
					ray_cols: 30,
					ray_rows: 7,
					scan_time: 3);
			}
			
			closest_target.NextStep();
			considered_mo = closest_target.Get();
			
			if(ShouldEject(considered_mo)) {
				considered_mo = NULL;
			}
		}
		
		// kd: Output.
		if(considered_mo) {
			old_locked_mo = locked_mo;
			locked_mo = considered_mo;
			is_ejecting = false;
			eject_t.Force(0);
		}
		
		// kd: Maybe start eject sequence. This allows you to keep the target
		// a little bit longer, so bars are less annoying (but you can do zany
		// stuff like blasting a guy who just teleported into a different
		// dimension... it's a Sandman feature now).
		
		// But don't do that if the target died.
		if(locked_mo && locked_mo.health < 1) {
			old_locked_mo = locked_mo;
			locked_mo = NULL;
		}
		
		else if(!is_ejecting && ShouldEject(locked_mo)) {
			is_ejecting = true;
			eject_t.To(10, from: 0);
		}
		
		if(is_ejecting) {
			eject_t.Next();
			
			if(eject_t.IsDone()) {
				old_locked_mo = locked_mo;
				locked_mo = NULL;
				is_ejecting = false;
			}
		}
	}
	
	// kd: TaRgEt FiNdInG
	override void Tick () {
		if(owner) {
			old_has_strat		= has_strat;
			has_strat			= 0 < owner.CountInv("SandmanStratActive");
			just_got_strat		= has_strat && !old_has_strat;
			
			if(is_up && has_strat) {
				
				HandleTargeting();
			}
			
			else {
				locked_mo = NULL;
			}
			
			// kd: The visor is updated manually here. That's because otherwise,
			// there's a chance for your visor to update before the weapon. This can
			// cause the crosshair to jitter if the target teleports.
			
			visor_handler =
				Gc_SandmanVisor(Gc_SandmanVisor.Find("Gc_SandmanVisor"));
			
			if(visor_handler) {
				visor_handler.SetTargetState(locked_mo, is_ejecting);
				visor_handler.Update();
			}
		}
		
		super.Tick();
	}
	
	void ResetAim () {
		has_strat = 0 < owner.CountInv("SandmanStratActive");
		closest_target.Reset();
		locked_mo = NULL;
	}
	
	int GunzerkShot;
	int GunzerkShot2;
	
	double GunzX;
	double GunzY;
	
	States
	{
	Spawn:
		DMSG X -1;
		Stop;

	SelectStuff:
		TNT1 A 0 {
			//A_Overlay(99,"Debug");
			invoker.closest_target.AttachTo(invoker.owner.player);
			invoker.GunzerkShot = 0;
			invoker.GunzerkShot2 = 0;
			invoker.ResetAim();
			A_SetCrosshair(61);
			
			invoker.is_up = true;
		}
		Goto SelectLoop;
		
	DeselectStuff:
		TNT1 A 0 {
			invoker.is_up = false;
		}
		Goto DeselectLoop;
	/*Debug:
		TNT1 A 35 {
			A_Log(String.Format("WeapAction %i", invoker.WeapAction));
			A_Log(String.Format("LeftHand %i", invoker.LeftHand));
		}
		Loop;*/

	//STRAT
	
	StratSwitch:
		"####" A 0 A_JumpIf(CountInv("SandmanStratActive"),"StratSwitchOff");
		"####" A 0 {
			invoker.LoopRight = 0;
			invoker.LeftHand = 1;
			A_GiveInventory("SandmanStratActive",1);
			A_PlaySound("Matriarch/Pull",5,0.7);
			if(GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking")) { A_Overlay(3,"LeftStratSwitch"); }
			//else { A_Overlay(-2,"RightStrat2"); }
		}
		"####" AAAAAAAAA 1 {
			static const double x[] = {1.0, 3.0, 6.0, 10.0, 12.0,
									   13.0, 14.0, 15.0, 14.0};
			static const double y[] = {1.0, 2.0, 3.0, 5.0, 8.0,
									   13.0, 14.0, 15.0, 14.0};
			int g = invoker.LoopRight++;				
			A_OverlayOffset(OverlayID(), x[g] + invoker.GunzX, y[g] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 { invoker.LoopRight = 0; }
		"####" AAAAA 1 {
			static const double x[] = {13.0, 9.0, 4.0, 2.0, 1.0};
			static const double y[] = {10.0, 6.0, 3.0, 2.0, 1.0};
			int g = invoker.LoopRight++;
			A_OverlayOffset(OverlayID(), x[g] + invoker.GunzX, y[g] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 {
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY,WOF_INTERPOLATE);
			invoker.LoopRight = 0;
			invoker.LeftHand = 0;
		}
		Goto RightActive;
	LeftStratSwitch:
		DMSG A 0;
		"####" A 0 {
			invoker.LoopLeft = 0;
			A_OverlayFlags(OverlayID(),PSPF_FLIP|PSPF_MIRROR,1);
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY);
		}
		"####" AAAAAAAAA 1 {
			static const double x2[] = {1.0, 3.0, 6.0, 10.0, 12.0,
										13.0, 14.0, 15.0, 14.0};
			static const double y2[] = {1.0, 2.0, 3.0, 5.0, 8.0,
										13.0, 14.0, 15.0, 14.0};
			int g2 = invoker.LoopLeft++;
			A_OverlayOffset(OverlayID(), x2[g2] + invoker.GunzX, y2[g2] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 { invoker.LoopLeft = 0; }
		"####" AAAAA 1 {
			static const double x2[] = {13.0, 9.0, 4.0, 2.0, 1.0};
			static const double y2[] = {10.0, 6.0, 3.0, 2.0, 1.0};
			int g2 = invoker.LoopLeft++;
			A_OverlayOffset(OverlayID(), x2[g2] + invoker.GunzX, y2[g2] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 {
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY,WOF_INTERPOLATE);
			invoker.LoopLeft = 0;
		}
		Goto LeftActive;
		
	StratSwitchOff:
		"####" A 0 {
			invoker.LoopRight = 0;
			invoker.LeftHand = 1;
			A_TakeInventory("SandmanStratActive",1);
			A_PlaySound("Matriarch/Pull",5,0.7);
			if(GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking")) { A_Overlay(3,"LeftStratSwitchOff"); }
			//else { A_Overlay(-2,"RightStrat2"); }
		}
		"####" AAAAAAAAA 1 {
			static const double x[] = {1.0, 3.0, 6.0, 10.0, 12.0,
									   13.0, 14.0, 15.0, 14.0};
			static const double y[] = {1.0, 2.0, 3.0, 5.0, 8.0,
									   13.0, 14.0, 15.0, 14.0};
			int g = invoker.LoopRight++;
			A_OverlayOffset(OverlayID(), x[g] + invoker.GunzX, y[g] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 { invoker.LoopRight = 0; }
		"####" AAAAA 1 {
			static const double x[] = {13.0, 9.0, 4.0, 2.0, 1.0};
			static const double y[] = {10.0, 6.0, 3.0, 2.0, 1.0};
			int g = invoker.LoopRight++;
			A_OverlayOffset(OverlayID(), x[g] + invoker.GunzX, y[g] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 {
			A_OverlayOffset(OverlayID(),invoker.GunzX, invoker.GunzY, WOF_INTERPOLATE);
			invoker.LeftHand = 0;
			invoker.LoopRight = 0;
		}
		Goto RightActive;
	LeftStratSwitchOff:
		DMSG A 0;
		"####" A 0 {
			invoker.LoopLeft = 0;
			A_OverlayFlags(OverlayID(),PSPF_FLIP|PSPF_MIRROR,1);
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY);
		}
		"####" AAAAAAAAA 1 {
			static const double x2[] = {1.0, 3.0, 6.0, 10.0, 12.0,
										13.0, 14.0, 15.0, 14.0};
			static const double y2[] = {1.0, 2.0, 3.0, 5.0, 8.0,
										13.0, 14.0, 15.0, 14.0};
			int g2 = invoker.LoopLeft++;
			A_OverlayOffset(OverlayID(), x2[g2] + invoker.GunzX, y2[g2] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 { invoker.LoopLeft = 0; }
		"####" AAAAA 1 {
			static const double x2[] = {13.0, 9.0, 4.0, 2.0, 1.0};
			static const double y2[] = {10.0, 6.0, 3.0, 2.0, 1.0};
			int g2 = invoker.LoopLeft++;
			A_OverlayOffset(OverlayID(), x2[g2] + invoker.GunzX, y2[g2] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 {
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY,WOF_INTERPOLATE);
			invoker.LoopLeft = 0;
		}
		Goto LeftActive;
	
	RightStrat2:
		"####" A 0 A_JumpIf(CountInv("SandmanStratActive"),2);
		"####" A 0;
		"####" AAAAAAAAAAAAAA 1 {
			static const double x[] = {1.0, 3.0, 6.0, 10.0, 12.0, 13.0, 14.0,
									   15.0, 14.0, 13.0, 9.0, 4.0, 2.0, 1.0};
			static const double y[] = {1.0, 2.0, 3.0, 5.0, 8.0, 13.0, 14.0,
									   15.0, 14.0, 10.0, 6.0, 3.0, 2.0, 1.0};
			int g2 = invoker.LoopLeft++;	
			A_OverlayOffset(OverlayID(), x[g2] + invoker.GunzX, y[g2] + invoker.GunzY, WOF_INTERPOLATE);
		}
		"####" A 0 { invoker.LoopLeft = 0; }
		Goto RightActive;
	
	//RIGHT SIDE
	
	RightActive:
		"####" "#" 0 {
			if(GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking")) {
				invoker.GunzX = 25;
				invoker.GunzY = 5;
			} else {
				invoker.GunzX = 0;
				invoker.GunzY = 0;
			}
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY);
		}
		DMSG A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking") || invoker.HandCast || invoker.LeftHand,7);
		DDSG A 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		DNSG A 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		DCSG A 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		DUSG A 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		DYSG A 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		DRSG A 0;
		"####" "#" 1;
		"####" "#" 0 {
			//Strat switch.
			If(CountInv("SandmanStrat") &&
				!invoker.weapSwitch && !invoker.LeftHand && !invoker.WeapAction &&
				!CountInv("MenuWheelActive") && GetPlayerInput(INPUT_BUTTONS) & BT_RELOAD) {
				Return ResolveState("StratSwitch");
			}
			
			//Fire.
			If(!(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) && !invoker.HandCast &&
				!invoker.weapSwitch && !invoker.LeftHand && !CountInv("MenuWheelActive") &&
				GetPlayerInput(INPUT_BUTTONS) & BT_ATTACK) {
				Return ResolveState("MainFireStart");
			}
			
			//Gunzerk fire.
			If((GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) &&
				!CountInv("MenuWheelActive") && !invoker.HandCast &&
				!invoker.WeapSwitch && GetPlayerInput(INPUT_BUTTONS) & BT_ALTATTACK) {
				Return ResolveState("MainFireStart");
			}
			Return ResolveState(Null);
		}
		Loop;
	
	
	//FIRE
	
	MainFireStart:
		"####" "#" 0 A_JumpIf(CountInv("SandmanStratActive"),"StratMainFire");
		DMSG A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking"),7);
		DDSG A 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		DNSG A 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		DCSG A 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		DUSG A 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		DYSG A 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		DRSG A 0;
		"####" "#" 0 {
			if(!(GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking")) && OverlayID() == -3) { invoker.LeftHand = 1; }
			invoker.WeapAction++;
			A_PlaySound("Sandman/Start",6,0.8);
		}
		"####" BBCCD 1;
		"####" ABCDABCDABCD 1;
	MainFire:
		"####" "#" 0 A_JumpIf(CountInv("SandmanStratActive"),"StratMainFire");
		"####" "#" 0 {
			//Gunzerk recoil modifier.
			invoker.GMod = GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking") ? 0.5 : 1.0;
			
			//The meat and potatoes.
			A_FireBullets(5,2,-1,70+random(15,30),"SandmanPuff",FBF_NORANDOMPUFFZ|FBF_NORANDOM|FBF_USEAMMO,2048);
			
			//Cruft.
			A_PlaySound("Sandman/Loop",6,0.8,1);
			if(random(0,32) > 1) {
				A_PlaySound("Sandman/Fire",1,0.6);
				A_PlaySound("Sandman/Add",5,0.6);
			}
			A_Recoil(1.5 * invoker.Gmod * ((CountInv("PowerDeadmaker") || CountInv("PowerSuitToken")) ? 0.1 : 1.0) * GetCvar("GC_PhysicalRecoil"));
			A_AlertMonsters();
			if(GetCvar("GC_QuakeRecoil")) { A_QuakeEx(2, 2, 3, 15, 0, 64, "", QF_SCALEDOWN); }
			A_SetPitch(pitch - (1.0 + frandom(1.0,0.0)) * GetCvar("GC_WeaponRecoil") * invoker.Gmod * ((CountInv("PowerDeadmaker") || CountInv("PowerSuitToken")) ? 0.2 : 0.8), SPF_INTERPOLATE);
			A_SetAngle(angle + ((OverlayID() == -3 ? -1.5 : 1.5) + frandom(1.0,-1.0)) * GetCvar("GC_WeaponRecoil") * invoker.Gmod * ((CountInv("PowerDeadmaker") || CountInv("PowerSuitToken")) ? 0.2 : 1.0), SPF_INTERPOLATE);
			
			//Gunsmoke.
			A_FireProjectile("Gunsmoke_Large",
					frandom(12.0,-12.0),							//Angle.
					0,												//Ammo use.
					((GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) ? 3.0 : 2.0) * (OverlayID() == 3 ? -1.0 : 1.0) + frandom(2.0,-2.0), //Xpos.
					4.0 + frandom(2.0,-2.0),						//Ypos.
					FPF_NOAUTOAIM,									//Flags.
					frandom(4.0,-4.0));								//Pitch.
			
			//Casings.
			if(OverlayID() == -3 && (GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking"))) {
				A_FireProjectile("SandmanCasingSpawner",0,0,30,-18,FPF_NOAUTOAIM);
			} else if (OverlayID() == 3 && (GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking"))){
				A_FireProjectile("SandmanCasingSpawnerLeft",0,0,-30,-18,FPF_NOAUTOAIM);
			} else {
				A_FireProjectile("SandmanCasingSpawner",0,0,20,-16,FPF_NOAUTOAIM);
			}
		}
		DMSF A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking"),7);
		DDSF A 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		DNSF A 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		DCSF A 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		DUSF A 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		DYSF A 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		DRSF A 0;
		"####" A 1 Bright {
			A_OverlayOffset(OverlayID(),13+invoker.GunzX+frandom(6.0,-6.0),13+invoker.GunzY+frandom(6.0,-6.0),WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.985); }
		}
		"####" B 1 Bright {
			A_OverlayOffset(OverlayID(),13+invoker.GunzX+frandom(6.0,-6.0),13+invoker.GunzY+frandom(6.0,-6.0),WOF_INTERPOLATE);
		}
		"####" C 1 Bright {
			//Gunzerk recoil modifier.
			invoker.GMod = GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking") ? 0.5 : 1.0;
			
			//The meat and potatoes.
			A_FireBullets(3,1,-1,70+random(15,30),"SandmanPuff",FBF_NORANDOMPUFFZ|FBF_NORANDOM|FBF_USEAMMO,2048);
			
			//Cruft.
			if(random(0,32) > 1) {
				A_PlaySound("Sandman/Fire",1,0.6);
				A_PlaySound("Sandman/Add",5,0.6);
			}
			A_Recoil(1.5 * invoker.Gmod * (CountInv("PowerDeadmaker") ? 0.1 : 1.0) * (CountInv("PowerSuitToken") ? 0.1 : 1.0) * GetCvar("GC_PhysicalRecoil"));
			A_AlertMonsters();
			
			//Casings.
			if(OverlayID() == -3 && (GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking"))) {
				A_FireProjectile("SandmanCasingSpawner",0,0,30,-18,FPF_NOAUTOAIM);
			} else if (OverlayID() == 3 && (GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking"))){
				A_FireProjectile("SandmanCasingSpawnerLeft",0,0,-30,-18,FPF_NOAUTOAIM);
			} else {
				A_FireProjectile("SandmanCasingSpawner",0,0,20,-16,FPF_NOAUTOAIM);
			}
		
			A_OverlayOffset(OverlayID(),13+invoker.GunzX+frandom(6.0,-6.0),13+invoker.GunzY+frandom(6.0,-6.0),WOF_INTERPOLATE);
		}
		"####" D 1 Bright {
			A_OverlayOffset(OverlayID(),13+invoker.GunzX+frandom(6.0,-6.0),13+invoker.GunzY+frandom(6.0,-6.0),WOF_INTERPOLATE);
		}
		//End of firing.
		"####" "#" 0 {
			Return GC_SandmanReFire();
		}
		DMSG A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking"),7);
		DDSG A 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		DNSG A 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		DCSG A 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		DUSG A 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		DYSG A 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		DRSG A 0;
		"####" A 1 {
			A_PlaySound("Sandman/Stop",6,0.8);
			A_OverlayOffset(OverlayID(),14+invoker.GunzX,14+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.99); }
			A_SetPitch(pitch - 0.25 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? -0.25 : 0.25) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" B 1 {
			A_OverlayOffset(OverlayID(),15+invoker.GunzX,15+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.9925); }
			A_SetPitch(pitch - 0.5 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? -0.5 : 0.5) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" C 1 {
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.995); }
			A_SetPitch(pitch + 0.5 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? -0.5 : 0.5) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" D 1 {
			A_OverlayOffset(OverlayID(),14+invoker.GunzX,14+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.9975); }
			A_SetPitch(pitch + 0.25 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? 0.25 : -0.25) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" A 1 {
			A_OverlayOffset(OverlayID(),12+invoker.GunzX,12+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.999); }
			Return GC_SandmanReFire();
		}
		"####" B 1 {
			A_OverlayOffset(OverlayID(),10+invoker.GunzX,10+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(1.0); }
			Return GC_SandmanReFire();
		}
		"####" C 1 {
			A_OverlayOffset(OverlayID(),7+invoker.GunzX,7+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" D 1 {
			A_OverlayOffset(OverlayID(),5+invoker.GunzX,5+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" A 1 {
			A_OverlayOffset(OverlayID(),4+invoker.GunzX,4+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" B 1 {
			A_OverlayOffset(OverlayID(),3+invoker.GunzX,3+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" C 1 {
			A_OverlayOffset(OverlayID(),2+invoker.GunzX,2+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" D 1 {
			A_OverlayOffset(OverlayID(),1+invoker.GunzX,1+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" A 1 {
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" BCDABCDABCDABBCCCDDDBBBBCCCCDDDD 1 { Return GC_SandmanReFire(); }
		"####" A 0 {
				if(OverlayID() == -3) { invoker.LoopRight = 0; } else { invoker.LoopLeft = 0; }
				invoker.WeapAction--;
				invoker.LeftHand = 0;
		}
		//"####" A 6;
		"####" A 0 A_JumpIf(OverlayID() == 3,"LeftActive");
		Goto RightActive;
	
	//STRAT FIRE
	
	StratMainFire:
		"####" "#" 0 {
			invoker.WeapAction++;
			if(!(GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking")) && OverlayID() == -3) { invoker.LeftHand = 1; }
		}
	StratMainReFire:
		"####" "#" 0 {
			//Gunzerk recoil modifier.
			invoker.GMod = GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking") ? 0.5 : 1.0;
			
			//The meat and potatoes.
			let elmo = invoker.locked_mo; // lockedmo -> L-mo -> elmo lolllllll
			
			if(invoker.locked_mo && invoker.has_strat) {
				// kd: If you have a target, that means you can see it. If you
				// can see it, the smart gun should hit it no matter what!
				// WHOOOOOOOOOOOOOOOOOO!!!
				
				// (also pill0w pls check if all ptrs set correctly kthx)
				
				let spawn_pos = elmo.vec3offset(0, 0, elmo.height * 0.5);
				Actor puff = Spawn("SandmanPuff", spawn_pos);
				
				if(puff) {
					puff.target = invoker.owner;
					
					if(0 < elmo.health) {
						elmo.DamageMobj(
							puff,
							invoker.owner,
							70 + random(15, 30),
							"Matriarch",
							DMG_USEANGLE | DMG_INFLICTOR_IS_PUFF,
							invoker.owner.angle);
					}
				}
				
				if(!sv_infiniteammo) {
					A_TakeInventory(invoker.ammotype1, 5);
				}
			}
			
			else {
				A_FireBullets(3,1,1,70+random(15,30),"SandmanPuff",FBF_NORANDOMPUFFZ|FBF_NORANDOM|FBF_USEAMMO,2048);
			}
			
			//Cruft.
			A_PlaySound("Sandman/Fire",1,0.6);
			A_PlaySound("Sandman/Add",5,0.6);
			//A_PlaySound("Sandman/Rotate",6,0.8);
			//A_SetBlend("Yellow",0.1,8);
			A_Recoil(1.25 * invoker.Gmod * ((CountInv("PowerDeadmaker") || CountInv("PowerSuitToken")) ? 0.1 : 1.0) * GetCvar("GC_PhysicalRecoil"));
			A_AlertMonsters();
			if(GetCvar("GC_QuakeRecoil")) { A_QuakeEx(1, 1, 2, 15, 0, 64, "", QF_SCALEDOWN); }
			
			//Gunsmoke.
			A_FireProjectile("Gunsmoke_Large",
					frandom(12.0,-12.0),							//Angle.
					0,												//Ammo use.
					((GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) ? 3.0 : 2.0) * (OverlayID() == 3 ? -1.0 : 1.0) + frandom(2.0,-2.0), //Xpos.
					4.0 + frandom(2.0,-2.0),						//Ypos.
					FPF_NOAUTOAIM,									//Flags.
					frandom(4.0,-4.0));								//Pitch.
		}
		DMSF A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking"),7);
		DDSF A 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		DNSF A 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		DCSF A 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		DUSF A 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		DYSF A 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		DRSF A 0;
		"####" A 1 Bright {
			A_OverlayOffset(OverlayID(),1+invoker.GunzX,1+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.995); }
			A_SetPitch(pitch - 1.0 * GetCvar("GC_WeaponRecoil") * invoker.Gmod * ((CountInv("PowerDeadmaker") || CountInv("PowerSuitToken")) ? 0.2 : 1.0), SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? -1.0 : 1.0) * GetCvar("GC_WeaponRecoil") * invoker.Gmod * ((CountInv("PowerDeadmaker") || CountInv("PowerSuitToken")) ? 0.2 : 1.0), SPF_INTERPOLATE);
		}
		DMSG A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking"),7);
		DDSG A 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		DNSG A 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		DCSG A 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		DUSG A 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		DYSG A 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		DRSG A 0;
		"####" B 1 {
			A_OverlayOffset(OverlayID(),7+invoker.GunzX+frandom(3.0,-3.0),7+invoker.GunzY+frandom(3.0,-3.0),WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.99); }
		}
		"####" C 1 {
			A_OverlayOffset(OverlayID(),11+invoker.GunzX+frandom(2.0,-2.0),11+invoker.GunzY+frandom(2.0,-2.0),WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.9875); }
		}
		"####" D 1 {
			A_OverlayOffset(OverlayID(),13+invoker.GunzX+frandom(1.0,-1.0),13+invoker.GunzY+frandom(1.0,-1.0),WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.99); }
			
			//Casings.
			if(OverlayID() == -3 && (GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking"))) {
				A_FireProjectile("SandmanCasingSpawner",0,0,30,-18,FPF_NOAUTOAIM);
			} else if (OverlayID() == 3 && (GetCvar("GC_Gunslinger") || CountInv("PowerGunzerking"))){
				A_FireProjectile("SandmanCasingSpawnerLeft",0,0,-30,-18,FPF_NOAUTOAIM);
			} else {
				A_FireProjectile("SandmanCasingSpawner",0,0,20,-16,FPF_NOAUTOAIM);
			}
		}
		
		"####" "#" 0 {
			Return GC_SandmanReFire();
		}
		
		"####" A 1 {
			A_OverlayOffset(OverlayID(),14+invoker.GunzX,14+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.99); }
			A_SetPitch(pitch - 0.25 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? -0.25 : 0.25) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" B 1 {
			A_OverlayOffset(OverlayID(),15+invoker.GunzX,15+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.9925); }
			A_SetPitch(pitch - 0.5 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? -0.5 : 0.5) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" C 1 {
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.995); }
			A_SetPitch(pitch + 0.5 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? -0.5 : 0.5) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" D 1 {
			A_OverlayOffset(OverlayID(),14+invoker.GunzX,14+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.9975); }
			A_SetPitch(pitch + 0.25 * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			A_SetAngle(angle + (OverlayID() == -3 ? 0.25 : -0.25) * GetCvar("GC_WeaponRecoil") * invoker.Gmod, SPF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" A 1 {
			A_OverlayOffset(OverlayID(),12+invoker.GunzX,12+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(0.999); }
			Return GC_SandmanReFire();
		}
		"####" B 1 {
			A_OverlayOffset(OverlayID(),10+invoker.GunzX,10+invoker.GunzY,WOF_INTERPOLATE);
			if(GetCvar("GC_ZoomRecoil")) { A_ZoomFactor(1.0); }
			Return GC_SandmanReFire();
		}
		"####" C 1 {
			A_OverlayOffset(OverlayID(),7+invoker.GunzX,7+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" D 1 {
			A_OverlayOffset(OverlayID(),5+invoker.GunzX,5+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" A 1 {
			A_OverlayOffset(OverlayID(),4+invoker.GunzX,4+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" B 1 {
			A_OverlayOffset(OverlayID(),3+invoker.GunzX,3+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" C 1 {
			A_OverlayOffset(OverlayID(),2+invoker.GunzX,2+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" D 1 {
			A_OverlayOffset(OverlayID(),1+invoker.GunzX,1+invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" A 1 {
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY,WOF_INTERPOLATE);
			Return GC_SandmanReFire();
		}
		"####" BBCDDD 1 { GC_SandmanReFire(); }
		"####" A 0 {
					if(OverlayID() == -3) { invoker.LoopRight = 0; } else { invoker.LoopLeft = 0; }
				   invoker.LeftHand = 0;
				   invoker.WeapAction--;
		}
		//"####" A 6;
		"####" A 0 A_JumpIf(OverlayID() == 3,"LeftActive");
		Goto RightActive;
		
	//LEFT SIDE
			
	LeftActive:
		TNT1 A 0 {
			if(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) {
				A_OverlayFlags(3,PSPF_MIRROR|PSPF_FLIP,1);
			} else {
				A_OverlayFlags(3,PSPF_MIRROR|PSPF_FLIP,0);
			}
			A_OverlayOffset(OverlayID(),invoker.GunzX,invoker.GunzY);
		}
		TNT1 A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking"),5);
		TNT1 A 1;
		TNT1 A 0 A_JumpIf(!invoker.WeapSwitch && !invoker.LeftHand &&
				!(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) &&
				!CountInv("MenuWheelActive") &&
				(CountInv("FirebreathActive") || CountInv("DragonshoutActive")) && //Shoddy fix.
				GetPlayerInput(INPUT_BUTTONS) & BT_ALTATTACK, "Cast");
		TNT1 A 0 A_JumpIf(!invoker.WeapSwitch && !invoker.LeftHand &&
				!(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) &&
				!CountInv("MenuWheelActive") &&
				GetPlayerInput(INPUT_BUTTONS) & BT_ALTATTACK, "HandCast");
		TNT1 A 0 A_JumpIf(!invoker.WeapSwitch && !CountInv("MenuWheelActive") &&
				!invoker.LeftHand && !(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking")) && 
				GetCvar("GC_Trogdor") && GetPlayerInput(INPUT_BUTTONS) & BT_ZOOM,"DeadPunchEnter");
		Loop;
		DMSG A 0 A_JumpIf(GetCVar("GC_Gunslinger") || CountInv("PowerGunzerking") || invoker.LeftHand,7);
		DDSG A 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		DNSG A 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		DCSG A 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		DUSG A 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		DYSG A 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		DRSG A 0;
		"####" A 1;
		"####" A 0 A_JumpIf(!invoker.WeapSwitch && GetPlayerInput(INPUT_BUTTONS) & BT_ATTACK, "MainFireStart");
        Loop;
		
	//CAST.
		
	CastHandLaunch:
		TNT1 A 1 {
			invoker.HandCast = 1;
		}
		TNT1 A 0 A_Overlay(4,"CastUnhand");
		TNT1 A 3;
		Goto CastLaunch+3;
	CastUnhand:
		CYDH L 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		CYNH L 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		CYCH L 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		CYUH L 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		CYYH L 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		CYGH L 0;
		"####" "#" 1 A_OverlayOffset(OverlayID(),72,111);
		"####" "#" 1 A_OverlayOffset(OverlayID(),61,131,WOF_INTERPOLATE);
		"####" "#" 1 A_OverlayOffset(OverlayID(),52,153,WOF_INTERPOLATE);
		"####" "#" 1 A_OverlayOffset(OverlayID(),44,173,WOF_INTERPOLATE);
		Stop; //This part is important.
	CastHandEnd:
		TNT1 A 0 {
			invoker.HandCast = 1;
			A_Overlay(4,"CastUphand");
		}
		TNT1 A 3;
		TNT1 A 1 { invoker.HandCast = 0; }
		Goto LeftActive;
	CastUphand:
		CYDH L 0 A_JumpIf(CountInv("PowerDeadmaker") && CountInv("PowerSuitDraugh"),6);
		CYNH L 0 A_JumpIf(CountInv("PowerDeadmaker"),5);
		CYCH L 0 A_JumpIf(CountInv("PowerSuitToken"),4);
		CYUH L 0 A_JumpIf(CountInv("PowerSuitProtect"),3);
		CYYH L 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		CYGH L 0;
		"####" "#" 1 A_OverlayOffset(OverlayID(),44,173);
		"####" "#" 1 A_OverlayOffset(OverlayID(),52,153,WOF_INTERPOLATE);
		"####" "#" 1 A_OverlayOffset(OverlayID(),61,131,WOF_INTERPOLATE);
		"####" "#" 1 A_OverlayOffset(OverlayID(),72,111,WOF_INTERPOLATE);
		Stop;
		
	//DEADMAKER PUNCH.
	
	DeadPunchEnter:
		TNT1 A 7 { invoker.LeftHand = 1; A_Overlay(4,"DeadPunchEnterAnim"); }
		Goto DeadPunchStart;
	DeadPunchEnterAnim:
		TNT1 A 1;
		CYDH L 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		CYNH L 0;
		"####" "#" 1 A_OverlayOffset(OverlayID(),85,106);
		"####" "#" 1 A_OverlayOffset(OverlayID(),82,125,WOF_INTERPOLATE);
		"####" "#" 1 A_OverlayOffset(OverlayID(),78,151,WOF_INTERPOLATE);
		"####" "#" 1 A_OverlayOffset(OverlayID(),69,176,WOF_INTERPOLATE);
		Stop;
	DeadPunchExit:
		TNT1 A 4 { invoker.LeftHand = 1; A_Overlay(4,"DeadPunchExitAnim"); }
		Goto LeftActive;
	DeadPunchExitAnim:
		CYDH L 0 A_JumpIf(CountInv("PowerSuitDraugh"),2);
		CYNH L 0;
		"####" "#" 1 A_OverlayOffset(OverlayID(),69,176);
		"####" "#" 1 A_OverlayOffset(OverlayID(),78,151);
		"####" "#" 1 A_OverlayOffset(OverlayID(),82,125);
		"####" "#" 1 { A_OverlayOffset(OverlayID(),90,100); invoker.LeftHand = 0; }
		Stop;
	}
}

#endregion
