/********************************************************************
 * SHADIUS!
 * args[0] : The script to call (execute always) on pain - if 0, no script call.
 ********************************************************************/

class Shadius : SquareMonster
{
	int tristep;
	int burntics;
	bool isRetaliating;

	statelabel shapeAttack;
	ShadiusLeftHand leftHand;
	ShadiusRightHand rightHand;

	int phase;
	int phase2HP;
	int phase3HP;

	int ticsSinceRetaliation;
	const ticsUntilRetaliation = 350; // 10 seconds

	const handDeathDamage = 1000; // subtract 1/8 of max life when a hand dies. O:

	Default
	{
		Health 8000;
		Mass 0x7fffffff;
		Speed 14;
		Height 100;
		Radius 40;
		PainChance 8;
		BloodColor '00 be ff'; // damn, so close to 00beef. :P

		ActiveSound 'shadius/active';
		SeeSound 'shadius/sight';
		PainSound 'shadius/pain';
		DeathSound 'shadius/death';

		DamageFunction(random(50, 70)); // for charge attack

		Obituary '$OB_SHADIUS';
		Species 'E2Boss';
		+BOSS;
		+NORADIUSDMG;
		+DONTRIP;
		+DONTFALL;
		+NOGRAVITY;
		+FLOAT;
		+FLOATBOB;
		+NEVERFAST;
		+TELESTOMP;

		DamageFactor "Goo", 0.33;
		DamageFactor "Magic", 0.66;
		DamageFactor "Vacuum", 0.0;
	}

	States
	{
	Spawn:
		SHDS A 0 NoDelay A_SpawnShadiusHands();
	Idle:
		SHDS A 5 A_Look();
		Loop;
	Intro:
		SHDS A -1 A_SetHandState("Intro");
		Wait;
	IntroCharge:
		SHDS A 0 A_SetHandState("See");
		Goto ChargeAttack;
	See:
		SHDS A 2 A_ShadiusChase();
		Loop;
	Missile:
		SHDS A 0 A_ShadiusAttackChoice();
	ChargeAttack:
		SHDS BC 6 Bright A_FaceTarget();
		SHDS D 6 Bright A_ShadiusChargeWindup();
		SHDS I 15 Bright;
		SHDS A 0 A_ShadiusCharge();
		SHDS JLJLJLJLJLJLJLJLJLJLJLJLJL 1 Bright A_ShadiusChargeTrail();
		Goto See;
	EyeSummon:
		SHDS BCD 6 Bright A_FaceTarget();
		SHDS E 3 Bright A_SummonBossEye();
		SHDS FGH 3 Bright;
		Goto See;
	ShapeAttack:
		SHDS A 2 A_ShadiusShapeChoice();
		SHDS AAA 6 A_FaceTarget();
		SHDS BCD 6 Bright A_FaceTarget();
		SHDS I 3 Bright A_FaceTarget();
		SHDS J 3 Bright A_PlaySound("shadius/attack", CHAN_VOICE);
		SHDS K 3 Bright A_FaceTarget();
		SHDS A 0 Bright FindState(shapeAttack);
	RoundAttack:
		SHDS O 6 Bright A_ShadiusRoundAttack();
		SHDS K 6 Bright A_FaceTarget();
		Goto ShapeAttackEnd;
	TriangleAttack:
		SHDS KOKOKOKOK 2 Bright A_ShadiusTriangleAttack();
		SHDS O 4 Bright A_ShadiusTriangleAttack();
		SHDS K 4 Bright A_FaceTarget();
		Goto ShapeAttackEnd;
	SquareAttack:
		SHDS K 4 Bright A_ShadiusSquareAim();
		SHDS O 6 Bright A_ShadiusSquareAttack();
		SHDS K 6 Bright A_FaceTarget();
		Goto ShapeAttackEnd;
	ShapeAttackEnd:
		SHDS JICB 3 Bright;
		Goto See;
	Pain:
		SHDS P 2 A_ShadiusPain();
		SHDS Q 2;
		SHDS M 4;
		SHDS M 6 A_ShadiusTryRetaliate();
		SHDS A 6 A_ShadiusRetaliate();
		Goto See;
	BigPain:
		SHDS P 2 Bright A_ShadiusBigPain();
		SHDS QPQPQPQPQPQPQ 2 Bright;
		SHDS M 12 Bright;
		SHDS A 6 A_ShadiusRetaliate();
		Goto See;
	AdvancePhase:
		SHDS M 6 A_ShadiusRetaliate(true);
		Goto See;
	Manipulate:
		SHDS N 0 Bright A_ShadiusManipulate();
		SHDS N 5 Bright A_SpawnItemEx('BossFlamerRed', frandom(-20.0,20.0), frandom(-20.0,20.0), 0);
		Wait;
	ManipulateDone:
		SHDS N 0 Bright A_ShadiusManipulateDone();
		Goto See;
	Death:
		SHDS D 0 A_ShadiusDeath();
	DeathBurnLoop:
		SHDS D 1 A_ShadiusBurn();
		SHDS D 0 A_ShadiusBurnLoop();
		TNT1 A 35 A_ShadiusScatter();
		Stop;
	Death.Massacre:
		TNT1 A 0 A_RemoveShadiusHands();
		Goto Super::Death.Massacre;
	}

	override void PostBeginPlay()
	{
		super.PostBeginPlay();

		// Set the HP thresholds for initiating the 2nd and 3rd fight phases.
		// These can be specified as args[1] and args[2] in a map (expressed as a
		// percentage of max health); if left blank, they default to 2/3 and 1/3
		// life. Easy to 'member, that.
		self.phase2HP = (args[1] != 0) ? (self.health * args[1] / 100) : self.health * 2 / 3;
		self.phase3HP = (args[2] != 0) ? (self.health * args[2] / 100) : self.health		 / 3;

		// Set the initial phase to 1; yeah, we're using 1-based indices; FIGHT ME
		self.phase = 1;
	}

	override void Tick()
	{
		super.Tick();

		// Spawn a persistent smoke trail each tick Shaddy's alive. Nasty habit.
		if(self.health > 0 && !level.frozen && !globalfreeze) {
			for(int i = 0; i < 5; i++) {
				A_SpawnItemEx('ShadiusSmoke', random(-30, 30) - 64.0, random(-30, 30), random(20, 80), random(-3, 3), random(-3, 3), random(-2,2));
			}
			// constantly tick upwards
			self.ticsSinceRetaliation++;
		}
	}

	override int TakeSpecialDamage(Actor inflictor, Actor source, int damage, Name damagetype) {
		int dam = super.TakeSpecialDamage(inflictor, source, damage, damagetype);

		// take half-damage if currently retaliating (since he's a sitting duckie);
		return self.isRetaliating ? (dam/2) : dam;
	}

	void A_SpawnShadiusHands()
	{
		bool b;
		Actor a;
		// [XA] Yeah, A_SpawnItemEx returns two values, resulting in this sort of madness. :P
		[b, a] = A_SpawnItemEx('ShadiusLeftHand', 0.0, -80.0, 8.0, 0, 0, 0, 0, SXF_SETMASTER | SXF_NOCHECKPOSITION, 0);
		self.leftHand = ShadiusLeftHand(a);
		[b, a] = A_SpawnItemEx('ShadiusRightHand', 0.0, 80.0, 8.0, 0, 0, 0, 0, SXF_SETMASTER | SXF_NOCHECKPOSITION, 0);
		self.rightHand = ShadiusRightHand(a);

		// poofy spawn effects
		A_SpawnItemEx('DarkSpectraPoof', 0.0,-80.0, 8.0, 0, 0, 0, 0, SXF_SETMASTER | SXF_NOCHECKPOSITION, 0);
		A_SpawnItemEx('DarkSpectraPoof', 0.0, 80.0, 8.0, 0, 0, 0, 0, SXF_SETMASTER | SXF_NOCHECKPOSITION, 0);
	}

	void A_RemoveShadiusHands()
	{
		// Paranoia checks; if for whatever reasons the hands fail to spawn,
		// it'd be nice not to have a VM abort. It's less fun than it sounds.
		if(self.leftHand) {
			self.leftHand.Destroy();
		}
		if(self.rightHand) {
			self.rightHand.Destroy();
		}
	}

	void A_SetHandState(statelabel st)
	{
		if(self.leftHand && self.leftHand.health > 0) {
			leftHand.SetState(leftHand.FindState(st));
		}
		if(self.rightHand && self.rightHand.health > 0) {
			rightHand.SetState(rightHand.FindState(st));
		}
	}

	void A_HandCast(class<Actor> runeClass)
	{
		if(self.leftHand && self.leftHand.health > 0) {
			self.leftHand.A_ShadiusHandCast(runeClass);
		}
		if(self.rightHand && self.rightHand.health > 0) {
			self.rightHand.A_ShadiusHandCast(runeClass);
		}
	}

	state A_ShadiusChase()
	{
		// Jump state to advance the fight phase if we've crossed
		// the threshold and Mr. Shads isn't busy with something.
		// the actual phase advancement is done in A_ShadiusRetaliate, which
		// is called from the AdvancePhase state.
		if( (self.phase < 2 && self.health <= self.phase2HP)
		 || (self.phase < 3 && self.health <= self.phase3HP) ) {
			return FindState('AdvancePhase');
		}

		// Otherwise, just give chase.
		A_Chase();
		return null;
	}

	state A_ShadiusAttackChoice()
	{
		// only do the charge attack when in phase 2 and 3
		if (self.phase >= 2) {
			return A_Jump(256, 'EyeSummon', 'ChargeAttack', 'ShapeAttack', 'ShapeAttack');
		} else {
			return A_Jump(256, 'EyeSummon', 'ShapeAttack', 'ShapeAttack');
		}
	}

	void A_ShadiusShapeChoice()
	{
		A_PlaySound("shadius/attack/start", CHAN_BODY);
		switch(random(1, 3)) {
			case 1:
				A_HandCast('ShadiusTriangleRune');
				shapeAttack = 'TriangleAttack';
				tristep = 0;
				break;
			case 2:
				A_HandCast('ShadiusCircleRune');
				shapeAttack = 'RoundAttack';
				break;
			case 3:
				A_HandCast('ShadiusSquareRune');
				shapeAttack = 'SquareAttack';
		}

		// in phase 1, the hands enter a special cast animation. not so in phases
		// 2 and 3, where the hands are allowed to attack independently.
		if (self.phase == 1) {
			A_SetHandState('Cast');
		}
	}

	void A_ShadiusRoundAttack()
	{
		// shoot at the target's feet, for maximum splash damage
		Vector3 targetVector = (self.Vec3To(self.target) - (0, 0, self.target.height/2)).Unit();
		double targetPitch = asin(-targetVector.z);
		A_FaceTarget();
		A_SpawnProjectile('ShadiusMarbleShot', 24, 0, 0, CMF_ABSOLUTEPITCH, targetPitch);
	}

	void A_ShadiusTriangleAttack()
	{
		Vector3 targetVector = (self.Vec3To(self.target)).Unit();
		double targetPitch = asin(-targetVector.z);
		A_FaceTarget();
		A_PlaySound("shadius/attack/triangle", CHAN_ITEM);
		A_SpawnProjectile('ShadiusTriangleShot', 24, 0, frandom(-4, 4) * tristep, CMF_AIMDIRECTION, frandom(0, 10) + targetPitch);
		A_SpawnProjectile('ShadiusTriangleShot', 24, 0, frandom(-4, 4) * tristep, 0, 0);
		A_SpawnProjectile('ShadiusTriangleShot', 24, 0, frandom(-4, 4) * tristep, CMF_AIMDIRECTION, frandom(0, -10) + targetPitch);
		tristep++;
	}

	void A_ShadiusSquareAim()
	{
		A_FaceTarget(0, 0);
	}

	void A_ShadiusSquareAttack()
	{
		self.AttackSound = "shadius/attack/square";
		A_CustomRailgun( 0, color1: "", color2:			 "",												pufftype: "BulletPuff", sparsity: 64.0, spawnclass: "ShadiusSquareTrail");
		A_CustomRailgun(40, color1: "", color2: "00BEFF", flags: RGF_FULLBRIGHT, pufftype: "ShadiusSquarePuff");
		self.AttackSound = "";
		self.pitch = 0;
	}

	void A_SummonBossEye()
	{
		A_PlaySound("shadius/attack/eyes", CHAN_ITEM);
		A_SpawnItemEx('BossSpyEye', 96.0, 0.0, 32.0, 32.0, 0.0, 0.0, 0.0, SXF_SETMASTER | SXF_TRANSFERPOINTERS);
		A_SpawnItemEx('BossFire', 96.0, 0.0, 24.0, 0.0, 0.0, 0.0, 0.0);
	}

	void A_SummonShadlings(int count = 4)
	{
		int angleStep = 360 / count;
		for(int i = 0; i < count; i++) {
			A_SpawnProjectile('ShadlingSpawner', 80, 0, i * angleStep);
		}
	}

	void A_StunMinions()
	{
		Actor a;
		ThinkerIterator iter = ThinkerIterator.Create('SquareMonster');
		while(a = Actor(iter.Next())) {
			if(a.master == self && a.health > 0) {
				a.SetState(a.FindState('Stun'));
			}
		}
	}

	void A_KillMinions()
	{
		Actor a;
		ThinkerIterator iter = ThinkerIterator.Create('SquareMonster');
		while(a = Actor(iter.Next())) {
			if(a.master == self && a.health > 0) {
				a.A_Die();
			}
		}
	}

	void A_ShadiusChargeWindup()
	{
		A_FaceTarget();
		A_PlaySound("shadius/attack/charge", CHAN_VOICE);
	}

	void A_ShadiusCharge()
	{
		A_SkullAttack(40);
	}

	void A_ShadiusChargeTrail()
	{
		A_SpawnItem('BossFlamer');
	}

	void A_ShadiusPain()
	{
		A_Stop();
		A_Pain();
	}

	void A_ShadiusBigPain()
	{
		A_Stop();
		A_PlaySound("shadius/pain/big", CHAN_VOICE);
	}

	void A_ShadiusHandAsplode(Actor source, Actor inflictor)
	{
		// knock off a chunk of Shadius's life when one of its hands is killed.
		self.isRetaliating = false; // to prevent it from being halved
		DamageMobj(inflictor, source, handDeathDamage, 'None');
		if(self.health > 0) {
			self.SetState(FindState('BigPain'));
		}
	}

	state A_ShadiusTryRetaliate()
	{
		// "try" to retaliate (assuming we haven't just done it).
		// in Phase 1, don't.
		// in Phase 2, do it half the time.
		// in phase 3, do it every time.
		if (ticsSinceRetaliation < ticsUntilRetaliation || phase == 1 || (phase == 2 && (random(0, 1) == 0)) ) {
			return FindState('See');
		}
		return null;
	}

	void A_ShadiusRetaliate(bool advancePhase = false)
	{
		if(advancePhase) {
			// bump the phase and increase aggressiveness!
			self.phase++;
			self.minMissileChance -= 20;
			if(leftHand) {
				leftHand.minMissileChance -= 20;
			}
			if(rightHand) {
				rightHand.minMissileChance -= 20;
			}
		}
		if(args[0] > 0) {
			self.bNoPain = true;
			self.isRetaliating = true;
			int nextPhase = advancePhase ? self.phase : 0;
			ACS_ExecuteAlways(args[0], 0, nextPhase);
		}
		self.ticsSinceRetaliation = 0; // just in case
		A_SummonShadlings();
	}

	void A_ShadiusManipulate()
	{
		A_FaceTarget();
		A_PlaySound("shadius/spellcast", CHAN_VOICE);
		A_PlaySound("shadius/spellcast/hum", CHAN_BODY);
		A_SetHandState('Manipulate');
	}

	void A_ShadiusManipulateDone()
	{
		self.bNoPain = false;
		self.isRetaliating = false;
		self.ticsSinceRetaliation = 0;
		A_SetHandState('See');
	}

	void A_ShadiusDeath()
	{
		A_Scream();
		A_Stop();
		A_StunMinions();
		A_RemoveShadiusHands();
		A_Quake(9, 200, 0, 1024, "");
		self.burntics = 160;
	}

	void A_ShadiusBurn()
	{
		A_SpawnItem('BossSmokerDeath');
	}

	state A_ShadiusBurnLoop()
	{
		self.burntics--;
		if(self.burntics > 0) {
			return FindState('DeathBurnLoop');
		}
		return null;
	}

	void A_ShadiusScatter()
	{
		A_KillMinions();
		A_ShadiusScatterRing('ShadiusRoundShotQuiet'   , 66);
		A_ShadiusScatterRing('ShadiusSquareShotQuiet'  , 44);
		A_ShadiusScatterRing('ShadiusTriangleShotQuiet', 22);
		A_ShadiusScatterRing('ShadiusRoundShotQuiet'   ,  0);
		A_ShadiusScatterRing('ShadiusTriangleShotQuiet',-22);
		A_ShadiusScatterRing('ShadiusSquareShotQuiet'  ,-44);
		A_ShadiusScatterRing('ShadiusRoundShotQuiet'   ,-66);
	}

	void A_ShadiusScatterRing(Class<Actor> spawntype, double pitch)
	{
		// spawn a big ol' ring of projectiles
		for(int i = 0; i < 18; i++) {
			A_SpawnProjectile(spawntype, 32, 0, i * 20, 2, pitch);
		}
	}
}

/********************************************************************
 * Shadius Attacks
 ********************************************************************/

class ShadiusMarbleShot : Actor
{
	Default
	{
		Translation "32:47=1:7", "146:156=1:7";
		Speed 32;
		Radius 15;
		Height 30;
		Gravity 0.3;
		Scale 1.75;
		SeeSound "shadius/attack/circle";
		DeathSound "shadius/attack/circle/hit";
		Species 'E2Boss';

		DamageFunction (20);

		Projectile;
		+THRUSPECIES;
	}

	States
	{
	Spawn:
		MBAL BBCCDD 1 Bright NoDelay A_SpawnProjectile("ShadiusMarbleTrail", 15);
		Loop;
	Death:
		MISL B	 1 Bright A_ShadiusMarbleExplode();
		MISL BBB 1 Bright A_ShadiusMarbleSmoke();
		MISL C	 1 Bright A_ShadiusMarbleFragment();
		MISL CCC 1 Bright A_ShadiusMarbleSmoke();
		MISL D	 1 Bright A_ShadiusMarbleFragment();
		MISL DDD 1 Bright A_ShadiusMarbleSmoke();
		MISL E	 1 Bright A_ShadiusMarbleFragment();
		MISL EEE 1 Bright A_ShadiusMarbleSmoke();
		MISL F	 1 Bright A_ShadiusMarbleFragment();
		MISL FFF 1 Bright A_ShadiusMarbleSmoke();
		MISL A 33; // For sound
		Stop;
	}

	void A_ShadiusMarbleSmoke()
	{
		A_SpawnItemEx("ShadeSmokeRandom", random[SquareEffectRandom](-40,40), random[SquareEffectRandom](-40,40), 0, 0, 0, random[SquareEffectRandom](1, 4) * 0.5, 0, 0, 16);
	}

	void A_ShadiusMarbleExplode()
	{
		A_Quake(3, 25, 0, 512, "blank");
		A_Explode(90, 128, 0);
		A_ShadiusMarbleFragment();
	}

	void A_ShadiusMarbleFragment()
	{
		A_SpawnItemEx('ShadiusMarbleExploder', 0, 0, 0, random(20, 80), 0, frandom(4, 0), frandom(0, 360), SXF_TRANSFERPOINTERS);
		A_ShadiusMarbleSmoke();
	}
}

class ShadiusMarbleTrail : Actor // [XA] MarbazookaTrail copypasta 9_6
{
	Default
	{
		Translation "0:0=4:4";
		RenderStyle 'Add';
		Alpha 1.0;
		Scale 2.0;

		+CLIENTSIDEONLY;
		+NOINTERACTION;
	}
	States
	{
	Spawn:
		SMOK A 0 Bright NoDelay A_SetScale(scale.x * 0.9);
		SMOK A 1 Bright A_FadeOut(0.05);
		Loop;
	}
}

class ShadiusMarbleExploder : Actor
{
	Default
	{
		Translation "146:156=1:7";
		Radius 4;
		Height 8;
		Speed 40;
		Damage 0;
		ReactionTime 1;
		Alpha 1.0;
		RenderStyle 'Add';

		Projectile;
		+THRUACTORS;
		+NODAMAGETHRUST;
	}

	States
	{
	Spawn:
		TNT1 A 2 A_CountDown;
		Loop;
	Death:
		TNT1 A 1 Bright;
		MISL B 3 Bright A_PlaySound("weapons/rocklx");
		MISL C 3 Bright A_Explode(90, 128, 0);
		MISL D 4 Bright A_SpawnItemEx("ShadeSmokeRandom", random[SquareEffectRandom](-15,15), random[SquareEffectRandom](-15,15), 0, 0, 0, random[SquareEffectRandom](1, 4) * 0.5, 0, 0, 96);
		MISL E 4 Bright A_SpawnItemEx("ShadeSmokeRandom", random[SquareEffectRandom](-15,15), random[SquareEffectRandom](-15,15), 0, 0, 0, random[SquareEffectRandom](1, 4) * 0.5, 0, 0, 96);
		MISL F 4 Bright A_SpawnItemEx("ShadeSmokeRandom", random[SquareEffectRandom](-15,15), random[SquareEffectRandom](-15,15), 0, 0, 0, random[SquareEffectRandom](1, 4) * 0.5, 0, 0, 96);
		Stop;
	}
}

class ShadiusSquareTrail : Actor
{
	Default
	{
		Radius 4;
		Height 4;
		RenderStyle 'Add';
		Alpha 1.0;
		Scale 0.125;
		+NOINTERACTION;
	}

	States
	{
	Spawn:
		SHX3 A 1 Bright A_SquareTrailFade();
		Wait;
	}

	void A_SquareTrailFade()
	{
		A_FadeOut(0.0286);
		scale *= 1.05;
	}
}

class ShadiusSquarePuff : Actor
{
	Default
	{
		Radius 4;
		Height 4;
		+NOINTERACTION;
		+ALWAYSPUFF;
		+PUFFONACTORS;
	}

	States
	{
	Spawn:
		TNT1 A 0 NoDelay A_PlaySound("shadius/attack/square");
		Stop;
	}
}

class ShadlingSpawner : Actor //SpectraBall
{
	Default
	{
		DamageFunction(50);
		RenderStyle 'Normal';
		Speed 8;
		Translation "116:120=59:63";

		// [XA] Temporary copypasta until SpectraBall and HeavyBall get zscripted:
		PROJECTILE;
		+STRIFEDAMAGE;
		+FORCEXYBILLBOARD;
		Radius 4;
		Height 4;
		DeathSound 'ovolt/shotx';
		DamageType 'Magic';
		Scale 0.5;
	}

	States
	{
	Spawn:
		LASR ABABABAB 3 Bright;
		LASR A 0 A_SpawnShadling();
		Stop;
	}

	void A_SpawnShadling()
	{
		bool a;
		Actor b;
		A_SpawnItemEx('DarkSpectraPoof');
		[a, b] = A_SpawnItemEx('Shadling', 0, 0, 0, 0, 0, 0, angle);
		if(b) {
			b.master = self.target; // self.target is Shadius.
		}
	}
}

/********************************************************************
 * Shadius Hands
 ********************************************************************/

class ShadiusLeftHand : SquareMonster
{
	double handOffset;
	double handSpread;

	int runeTics;
	class<Actor> runeType;

	property HandOffset:handOffset;
	property HandSpread:handSpread;

	Default
	{
		Health 2000;
		Mass 0x7fffffff;
		Radius 16;
		Height 32;
		Species 'E2Boss';
		BloodColor '00 be ff';
		Obituary '$OB_SHADIUS';

		-SOLID;
		-COUNTKILL;
		+SHOOTABLE;
		+NOGRAVITY;
		+NOPAIN;
		+LOOKALLAROUND;

		MinMissileChance 210;
		ShadiusLeftHand.HandOffset -80.0;
		ShadiusLeftHand.HandSpread 0;

		DamageFactor "Magic", 0.66;
		DamageFactor "Vacuum", 0.0;
		DamageFactor "Goo", 0.0;
	}

	States
	{
	Spawn:
		SHDL A 1 NoDelay A_Look();
		Wait;
	See:
		#### A 1 A_ShadiusHandChase();
		Loop;
	Missile:
		#### BCCCC 5;
		#### BD 2;
		#### E 4 Bright A_ShadiusHandMissile();
		#### D 4;
		Goto See;
	Cast:
		#### BCCCCBADEEED 5;
		Goto See;
	Manipulate:
		#### G -1;
		Wait;
	Intro:
		#### A -1;
		Wait;
	Death:
		#### F -1 A_ShadiusHandDeath();
		Wait;
	}

	override void Tick() {
		super.Tick();
		if(!level.frozen && !globalfreeze) {

			// stick to master
			A_Warp(AAPTR_MASTER, 0.0, handOffset, 8.0, 0, WARPF_NOCHECKPOSITION | WARPF_INTERPOLATE | WARPF_BOB);

			 // spawn runes
			if(self.runeTics > 0) {
				if(self.runeTics % 4 == 0) {
					A_SpawnItemEx(self.runeType, 1, 0, 28, frandom(0, 2), 0, 2, frandom(-90, 90), SXF_CLIENTSIDE);
				}
				self.runeTics--;
			}
		}
	}

	override void Die(Actor source, Actor inflictor, int dmgflags)
	{
		super.Die(source, inflictor, dmgflags);
		let the_shadiator = Shadius(self.master);
		if(the_shadiator) {
			the_shadiator.A_ShadiusHandAsplode(source, inflictor);
		}
	}

	void A_ShadiusHandChase()
	{
		A_Chase(null, 'Missile', CHF_NOPLAYACTIVE | CHF_DONTMOVE);
	}

	void A_ShadiusHandMissile(double spread = 0)
	{
		A_SpawnProjectile('ShadiusHandBall', 16, 0, frandom(-handSpread, handSpread));
	}

	void A_ShadiusHandCast(class<Actor> runeType)
	{
		self.runeTics = 50;
		self.runeType = runeType;
	}

	void A_ShadiusHandDeath()
	{
		A_NoBlocking();
		A_SpawnItem("GibSpawnerShadiusHand");
		self.handOffset *= 0.75;
	}
}

class ShadiusRightHand : ShadiusLeftHand
{
	Default
	{
		ShadiusLeftHand.HandOffset 80.0;
		ShadiusLeftHand.HandSpread 15;
	}

	States
	{
	Spawn:
		SHDR A 1 NoDelay A_Look();
		Wait;
	}
}

class ShadiusHandBall : Actor
{
	const acceleration = 0.75;
	const topSpeed = 50;

	Default
	{
		RenderStyle 'Normal';
		Radius 16;
		Damage 10;
		Speed 2;
		Translation "116:120=1:5";
		SeeSound "shadius/handball";
		DeathSound "shadius/handball/death";
		Species 'E2Boss';
		Projectile;
		+THRUSPECIES;
		+STRIFEDAMAGE;
		+SEEKERMISSILE;
	}

	States
	{
	Spawn:
		LASR AA 1 Bright A_HandBallAccel();
		LASR A 0 Bright A_HandBallSmoke();
		LASR BB 1 Bright A_HandBallAccel();
		LASR B 0 Bright A_HandBallSmoke();
		Loop;
	Death:
		TNT1 A 0 A_HandBallPoof();
		TNT1 A 0 A_HandBallSmoke(5);
		TNT1 A 0 A_Explode(20, 64);
		TNT1 AAAAA 3 Bright A_HandBallSmoke();
		Stop;
	}

	void A_HandBallAccel()
	{
		if(self.vel.Length() < self.topSpeed) {
			self.vel += (self.vel.Unit() * self.acceleration);
		}
	}

	void A_HandBallSmoke(int count = 1)
	{
		for(int i = 0; i < count; i++) {
			A_SpawnItemEx("ShadeSmokeRandom", random[SquareEffectRandom](-20,20), random[SquareEffectRandom](-20,20), 0, 0, 0, random[SquareEffectRandom](1, 4) * 0.5, 0, 0, 96);
		}
	}

	void A_HandBallPoof(int count = 20)
	{
		for(int i = 0; i < count; i++) {
			A_SpawnItemEx("ShadiusPuff", frandom[SquareEffectRandom](-6.0,6.0), frandom[SquareEffectRandom](-6.0,6.0), frandom[SquareEffectRandom](16,32), frandom[SquareEffectRandom](-1.0,1.0), frandom[SquareEffectRandom](-1.0,1.0), frandom[SquareEffectRandom](-1.0,1.0), 2);
		}
	}
}

/********************************************************************
 * Shadius Effects & Misc
 ********************************************************************/

class ShadiusTriangleRune : Actor
{
	Default
	{
		Radius 4;
		Height 4;
		ReactionTime 60;
		RenderStyle 'Add';
		Scale 0.5;
		Alpha 0.8;
		+NOINTERACTION;
	}

	States
	{
	Spawn:
		SHX1 A 0 Bright;
	Fade:
		#### A 1 Bright A_FadeOut(0.05);
		Loop;
	}
}

class ShadiusCircleRune : ShadiusTriangleRune
{
	States
	{
	Spawn:
		SHX2 A 0 Bright;
		Goto Fade;
	}
}

class ShadiusSquareRune : ShadiusTriangleRune
{
	States
	{
	Spawn:
		SHX3 A 0 Bright;
		Goto Fade;
	}
}

class ShadiusFlamethrower : Actor
{
	Default
	{
		Radius 8;
		Height 16;
		+NOGRAVITY;
	}
	States
	{
	Spawn:
		TNT1 A 0 NoDelay A_PlaySound("world/flamethrow", CHAN_BODY);
	SpawnLoop:
		TNT1 AAAAA 2 A_ShadiusFlamethrower();
		TNT1 A 0 A_PlaySound("world/flamethrow/short", CHAN_VOICE);
		Loop;
	}

	virtual void A_ShadiusFlamethrower()
	{
		A_SpawnProjectile('BossFireDamaging', 0, 0, random(-8,8), 2, random(-8,8));
	}
}

class ShadiusFloorFlamethrower : ShadiusFlamethrower
{
	override void A_ShadiusFlamethrower()
	{
		A_SpawnProjectile('BossFireDamaging', random(-2,2), random(-2,2), 0, 2, -90);
	}
}

class ShadiusRubbleSpawner : Actor
{
	Default
	{
		+NOGRAVITY;
	}

	States
	{
	Spawn:
		TNT1 A 0;
		TNT1 A 10 A_SpawnItemEx('ShadiusRubbleDrop', frandom(-256.0,256.0), frandom(-256.0,256.0), 0, frandom(-1.0,1.0), frandom(-1.0,1.0), -12.0, 0, 0, 192);
		Loop;
	}
}

class ShadiusRubbleDrop : RandomSpawner
{
	Default {
		DropItem 'ShadiusRubble1';
		DropItem 'ShadiusRubble2';
		DropItem 'ShadiusRubble3';
	}
}

class ShadiusRubble1 : Actor
{
	Default
	{
		Projectile;
		Damage 1;
		Speed 1;
		Radius 6;
		Height 6;
	}

	States
	{
	Spawn:
		RUB1 ABC 3;
		Loop;
	Death:
		RUBD A 0 A_Jump(256, 1, 2, 3);
		RUBD A 200;
		Stop;
		RUBD B 200;
		Stop;
		RUBD C 200;
		Stop;
	}
}

class ShadiusRubble2 : ShadiusRubble1
{
	States
	{
	Spawn:
		RUB2 ABC 3;
		Loop;
	}
}

class ShadiusRubble3 : ShadiusRubble1
{
	States
	{
	Spawn:
		RUB3 ABCD 3;
		Loop;
	}
}
