class BabelCacodemon : SyncEnabledMonster Replaces Cacodemon
{
	int user_loopCounter;
	double dodgeChance;

	int gasCounter;
	double gasAngle;

	default
	{
		Health 400;
		Radius 31;
		Height 56;
		Mass 100;
		Speed 8;
		PainChance 128;
		Monster;
		+FLOAT;
		+NOGRAVITY;
		SeeSound "caco/sight";
		PainSound "caco/pain";
		DeathSound "caco/death";
		ActiveSound "caco/active";
		Obituary "$OB_CACO";
		HitObituary "$OB_CACOHIT";
		BloodColor "blue";
		Species "Balloon";
		DamageFunction (10 * random(1, 6));
		+MissileMore;
		ReactionTime 4;
		PainChance "PlayerPistol", 196;
		DamageType "Monster";
		Tag "Cacodemon (Babel)";

		BabelMonster.FearEnabled		true;
		BabelMonster.FearThreshold		500.0;
		BabelMonster.FearMaxValue		600.0;
		BabelMonster.FearFallOff		2.0;
		BabelMonster.FearRecovery		3.0;
		BabelMonster.FearResistChance	0.0;
		BabelMonster.SyncChainCheckLOS	true;
		BabelMonster.AlternatePainSound	"caco/pain2";
	}

	override void Tick()
	{
		super.Tick();
		if(gasCounter > 0)
		{
			if(bool(CVar.FindCVar("egr_particle_toggle").GetInt()))
			{
				for(int i = 0; i < 4; i++)
				{
					A_SpawnParticle (0x999999, SPF_FULLBRIGHT, random(15,25), random(3,5), 0, 10*cos(angle+180),10*sin(angle+180),28,	(frandom(1.0, 3.0)*cos(gasAngle))+frandom(1.0, 4.0),(frandom(1.0, 3.0)*sin(gasAngle))+frandom(1.0, 4.0),frandom(-3.0, 3.0),	0,0,0, 0.7, -1);
				}
			}
			gasCounter--;
		}
	}

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

		dodgeChance = 70.0;
	}

	void Dodge()
	{
		if(health > 0)
		{
			if(!holdingState && !isAfraid)
			{
				if(frandom(0.0, 100.0) <= dodgeChance)
				{
					//Console.Printf("%s dodging", self.GetClassName());
					SetState(ResolveState("DodgeNoSignal"));
				}
			}
		}
	}

	action state DodgeThink()
	{
		let Leftpuff = LineAttack(angle+90, 384, 0, 0, "", "CacoPuff", LAF_NORANDOMPUFFZ);
		let Rightpuff = LineAttack(angle-90, 384, 0, 0, "", "CacoPuff", LAF_NORANDOMPUFFZ);

		//Safe checks because apparently sometimes GZDoom shits the bed on this
		double LeftDist = 0;
		if(Leftpuff)
		{
			LeftDist = Distance2D(Leftpuff);
		}

		double RightDist = 0;
		if(Rightpuff)
		{
			RightDist = Distance2D(Rightpuff);
		}

		if(LeftDist < 160 && RightDist < 160)
		{
			invoker.ReleaseState();
			return ResolveState("CancelDodge");
		}
		if(LeftDist > RightDist)
		{
			return ResolveState("DodgeLeft");
		}
		else if (RightDist > LeftDist)
		{
			return ResolveState("DodgeRight");
		}
		else
		{
			if(frandom(0.0, 1.0) > 0.5)
			{
				return ResolveState("DodgeLeft");
			}
			else
			{
				return ResolveState("DodgeRight");
			}
		}
	}

	action void A_FaceTargetIfNotAfraid()
	{
		if(!invoker.isAfraid)
		{
			invoker.A_FaceTarget();
		}
	}

	action void A_CacoSpread()
	{
		if(!invoker.CheckMeleeRange())
		{
			let mid = CacoBall(invoker.A_SpawnProjectile("CacoBall", 32, 0, 0));
			let left = SideCacoBall(invoker.A_SpawnProjectile("SideCacoBall", 32, 0, -7));
			let right = SideCacoBall(invoker.A_SpawnProjectile("SideCacoBall", 32, 0, 7));
			if (mid)
			{
				if (left)
				{
					mid.leftSideBall = left;
					left.midVelocity = mid.vel;
				}
				if (right)
				{
					mid.rightSideBall = right;
					right.midVelocity = mid.vel;
				}
			}
		}
		else
		{
			invoker.A_CustomComboAttack("FastCacodemonBall", 32, 10 * random(1, 6), "imp/melee", "Monster");
		}
	}

	States
	{
	Spawn:
		TNT1 A 0 Nodelay
		{
			return ResolveState("Look");
		}
	Wander:
		HEAD A 10 Fast
		{
			A_Wander();
			A_Look();
		}
		Loop;
	Look:
		HEAD A 10 A_Look();
		Loop;
	See:
		TNT1 A 0
		{
			ReleaseState();
			return JumpIfAfraid("Fear");
		}
		HEAD A 3 Fast
		{
			A_ScaleVelocity(0.80);
			A_Chase();
		}
		Loop;
	Fear:
		HEAD A 3 Fast
		{
			bFRIGHTENED = true;
			isAfraid = true;
			A_SetSpeed(10);
			A_Chase("Melee", null);
		}
		TNT1 A 0 JumpIfHasFear("Fear");
		TNT1 A 0
		{
			currentFear = 0;
			isAfraid = false;
			bFRIGHTENED = false;
			ResetGroupFear();
			A_SetSpeed(8);
		}
		Goto See;


	/* Fuckhuge missile block */
	Missile:
		TNT1 A 0
		{
			TriggerSync(self.GetClassName(), 45.0);
		}
	MissileNoSignal:
		TNT1 A 0 JumpIfCannotSee("See");
		TNT1 A 0 HoldState();
		TNT1 A 0 A_JumpIfCloser(512, 2);
		TNT1 A 0 A_Jump(256, 2);
		TNT1 A 0 A_Jump(64, "Charge");
		TNT1 A 0 A_JumpIfCloser(1024, "FireSpread");
	FireFast:
		HEAD BC 5 A_FaceTarget();
		HEAD D 5 Bright;
	MissileOnly:
		TNT1 A 0 A_CustomComboAttack("FastCacodemonBall", 32, 10 * random(1, 6), "imp/melee", "Monster");
		Goto See;
	FireSpread:
		HEAD BC 5 A_FaceTarget();
		HEAD D 5 Bright A_CacoSpread();
		/*{
			A_SpawnProjectile("CacodemonBall", 32, 0, -5);
			A_SpawnProjectile("CacodemonBall", 32, 0, 5);
			A_CustomComboAttack("CacodemonBall", 32, 10 * random(1, 6), "imp/melee", "Monster");
		}*/
		Goto See;
	/* End of fuckhuge missile block */

	Melee:
		TNT1 A 0 HoldState();
	Charge:
		TNT1 A 0 A_JumpIfCloser(64, "FireSpread");
		HEAD B 4
		{
			bNOPAIN = true;
			A_PlaySound("caco/dodge", CHAN_AUTO);
			A_FaceTarget();
		}
		HEAD B 1
		{
			gasAngle = angle+180;
			gasCounter = 10;
			ThrustThing(int(angle*256/360), 15, 0, 0);
		}
		Goto SlowDownCharge;
	SlowDownCharge:
		HEAD CCCCC 1
		{
			A_ScaleVelocity(0.95);
			A_FaceTarget();
		}
		HEAD DDDDD 1
		{
			A_ScaleVelocity(0.90);
			A_FaceTarget();
		}
		TNT1 A 0
		{
			A_ScaleVelocity(0);
			bNOPAIN = false;
		}
		TNT1 A 0 JumpIfAfraid("Fear");
		Goto MissileOnly;
	Pain:
		HEAD E 3 Fast
		{
			A_ScaleVelocity(0.80);
		}
		HEAD E 3 Fast
		{
			A_ScaleVelocity(0.80);
			A_BabelPain();
		}
		TNT1 A 0 A_ScaleVelocity(0.80);
		HEAD F 3 Fast A_Jump(96, "Dodge");
	CancelDodge:
		HEAD F 3 Fast A_ScaleVelocity(0.80);
		TNT1 A 0 AddFear(random(0,50));
		TNT1 A 0 JumpIfAfraid("Fear");
		Goto See;
	Dodge:
		TNT1 A 0
		{
			A_ScaleVelocity(0.30);
			TriggerDodge();
		}
	DodgeNoSignal:
		TNT1 A 0
		{
			HoldState();
		}
		TNT1 A 0 A_FaceTargetIfNotAfraid();
		TNT1 A 0 DodgeThink();
		Goto See; //unreachable but just to be sure
	DodgeRight:
		HEAD F 1
		{
			bNOPAIN = true;
			A_PlaySound("caco/dodge", CHAN_AUTO);
		}
		HEAD F 5
		{
			gasAngle = angle+90;
			gasCounter = 10;
			ThrustThing(int(angle*256/360+192), 15, 0, 0);
		}
		Goto SlowDownDodge;
	DodgeLeft:
		HEAD F 1
		{
			bNOPAIN = true;
			A_PlaySound("caco/dodge", CHAN_AUTO);
		}
		HEAD F 5
		{
			gasAngle = angle-90;
			gasCounter = 10;
			ThrustThing(int(angle*256/360+64), 15, 0, 0);
		}
		Goto SlowDownDodge;
	SlowDownDodge:
		HEAD FFFEE 1
		{
			A_ScaleVelocity(0.95);
			A_FaceTargetIfNotAfraid();
		}
		HEAD EEEAA 1
		{
			A_ScaleVelocity(0.90);
			A_FaceTargetIfNotAfraid();
		}
		TNT1 A 0
		{
			A_ScaleVelocity(0);
			bNOPAIN = false;
		}
		TNT1 A 0 JumpIfHasFear("Fear");
		Goto FireFast;
	Death.Massacre:
		HEAD G 8;
		HEAD H 8 A_Scream();
		HEAD IJ 8;
		HEAD K 6 A_NoBlocking;
		HEAD LM 6;
		HEAD N -1 A_SetFloorClip;
		Stop;
	Death:
		TNT1 A 0 A_Jump(96, "Death.Monster");
		TNT1 A 0
		{
			TriggerSync(self.GetClassName(), 45.0);
			TriggerSync("BabelImp", 40.0);
			TriggerSync("BabelDemon", 40.0);
			TriggerSync("BabelZombieMan", 30.0);
			TriggerSync("BabelChaingunGuy", 20.0);
			TriggerSync("BabelShotgunGuy", 25.0);
			TriggerSync("BabelNazi", 75.0);
		}
	Death.Monster:
		HEAD G 8
		{
			if(CVar.FindCVar("egr_gore").GetInt())
			{
				for(user_loopCounter = 0; user_loopCounter < 3; user_loopCounter++)
				{
					A_SpawnItemEx("BlueBloodSpew", random(-10, 10), random(-10, 10), random(24, 56));
				}
			}
		}
		HEAD H 8
		{
			A_Scream();
			if(CVar.FindCVar("egr_gore").GetInt())
			{
				for(user_loopCounter = 0; user_loopCounter < 3; user_loopCounter++)
				{
					A_SpawnItemEx("BlueBloodSpew", random(-10, 10), random(-10, 10), random(24, 56));
				}
			}
		}
		HEAD IJ 8
		{
			if(CVar.FindCVar("egr_gore").GetInt())
			{
				for(user_loopCounter = 0; user_loopCounter < 3; user_loopCounter++)
				{
					A_SpawnItemEx("BlueBloodSpew", random(-10, 10), random(-10, 10), random(24, 56));
				}
			}
		}
		HEAD K 6 A_NoBlocking();
		HEAD LM 6;
		HEAD N -1 A_SetFloorClip();
		Stop;
	Raise:
		HEAD L 8
		{
			A_SetSpeed(8);
			A_UnSetFloorClip();
		}
		HEAD KJIHG 8;
		Goto See;
	}
}

class CacoBall : BabelFastProjectile Replaces CacodemonBall
{
	SideCacoBall leftSideBall;
	SideCacoBall rightSideBall;
	default
	{
		Radius 6;
		Height 8;
		Speed 10;
		FastSpeed 20;
		Damage 5;
		Projectile;
		+RANDOMIZE;
		RenderStyle "Add";
		Alpha 1;
		SeeSound "caco/attack";
		DeathSound "caco/shotx";
		decal "DoomImpScorch";
		DamageType "Monster";

		BabelFastProjectile.ParticleColors 0xff0000, 0xffaa00, 0xee33ff;
		BabelFastProjectile.ParticleSizeRange 2, 4;
		BabelFastProjectile.ParticleLifeTime 25, 35;
		BabelFastProjectile.ParticleCubeOffsets 2, 2;
		BabelFastProjectile.ParticleCubeAccelerations 0.1, 0.1;
		BabelFastProjectile.LerpOffset 1.0;
		BabelFastProjectile.TrailDetail 1;
		BabelFastProjectile.HeightCorrection 0.0;
		BabelFastProjectile.StartingAlpha 0.7;
		BabelFastProjectile.VariableDirection false;
	}

	//Hypothetically useful. In practice, strange.
	void KillSideBalls()
	{
		if(leftSideBall)
		{
			let deathState = leftSideBall.ResolveState("Death");
			if(!leftSideBall.InStateSequence(leftSideBall.CurState, deathState))
			{
				leftSideBall.SetState(deathState);
				leftSideBall.vel = self.vel;
			}
		}
		if(rightSideBall)
		{
			let deathState = rightSideBall.ResolveState("Death");
			if(!rightSideBall.InStateSequence(rightSideBall.CurState, deathState))
			{
				rightSideBall.SetState(deathState);
				rightSideBall.vel = self.vel;
			}
		}
	}

	States
	{
	Spawn:
		BAL2 AB 4 Bright;
		Loop;
	Death:
		TNT1 A 0
		{
			trailSpawn = false;
			//KillSideBalls();
		}
		BAL2 CDE 6 Bright;
		Stop;
	}
}

class SideCacoBall : CacoBall
{
	Vector3 midVelocity;
	int framesBeforeVelAdjust;
	bool adjusted;
	override void BeginPlay()
	{
		super.BeginPlay();
		framesBeforeVelAdjust = 25;
		adjusted = false; //optimization, probably
	}
	override void Tick()
	{
		if(!adjusted)
		{
			if(framesBeforeVelAdjust > 0)
			{
				framesBeforeVelAdjust--;
			}
			else
			{
				self.velStore = midVelocity; //adjust trails to match
				self.vel = midVelocity;
				adjusted = true;
			}
		}
		super.Tick();
	}
}

class FastCacodemonBall : CacoBall
{
	default
	{
		Speed 20;
		FastSpeed 40;
		decal "DoomImpScorch";
		DamageType "Monster";

		BabelFastProjectile.LerpOffset 0.7;
		BabelFastProjectile.TrailDetail 2;
		BabelFastProjectile.ParticleSizeRange 2, 3;
		BabelFastProjectile.ParticleCubeAccelerations 0.15, 0.15;
		BabelFastProjectile.StartingAlpha 0.7;
	}
}

class CacoPuff : AutoAimPuff {}