// 2.1 Eggmobile - Ported from 2.1.25
// PLEASE LOAD LUA_CMMN, LUA_OJTF AND LUA_EMCM BEFORE THIS

freeslot(
	// Sprites
	//"SPR_21EM",
	"SPR_OUNI",
	// Objects
	"MT_21EGGMOBILE",
	"MT_21EGGMOBILE_BALL",
	"MT_OLDLASER",
	"MT_21EGGMOBILE_FIRE",
	"MT_21EGGMOBILE_TARGET",
	// States
	"S_21EGGMOBILE_STND",
	"S_21EGGMOBILE_LATK1",
	"S_21EGGMOBILE_LATK2",
	"S_21EGGMOBILE_LATK3",
	"S_21EGGMOBILE_LATK4",
	"S_21EGGMOBILE_LATK5",
	"S_21EGGMOBILE_LATK6",
	"S_21EGGMOBILE_LATK7",
	"S_21EGGMOBILE_LATK8",
	"S_21EGGMOBILE_LATK9",
	"S_21EGGMOBILE_LATK10",
	"S_21EGGMOBILE_RATK1",
	"S_21EGGMOBILE_RATK2",
	"S_21EGGMOBILE_RATK3",
	"S_21EGGMOBILE_RATK4",
	"S_21EGGMOBILE_RATK5",
	"S_21EGGMOBILE_RATK6",
	"S_21EGGMOBILE_RATK7",
	"S_21EGGMOBILE_RATK8",
	"S_21EGGMOBILE_RATK9",
	"S_21EGGMOBILE_RATK10",
	"S_21EGGMOBILE_PANIC1",
	"S_21EGGMOBILE_PANIC2",
	"S_21EGGMOBILE_PANIC3",
	"S_21EGGMOBILE_PANIC4",
	"S_21EGGMOBILE_PANIC5",
	"S_21EGGMOBILE_PANIC6",
	"S_21EGGMOBILE_PANIC7",
	"S_21EGGMOBILE_PANIC8",
	"S_21EGGMOBILE_PANIC9",
	"S_21EGGMOBILE_PANIC10",
	"S_21EGGMOBILE_PAIN",
	"S_21EGGMOBILE_PAIN2",
	//"S_21EGGMOBILE_DIE1",
	//"S_21EGGMOBILE_DIE2",
	//"S_21EGGMOBILE_DIE3",
	//"S_21EGGMOBILE_DIE4", // Originally went up to 14
	//"S_21EGGMOBILE_FLEE1",
	//"S_21EGGMOBILE_FLEE2",
	"S_21EGGMOBILE_BALL",
	"S_21EGGMOBILE_TARGET",
	"S_OLDLASER"
	// Add states for MT_EGGMOBILE_FIRE, if SPR_SFLM changes in the future
	/*
	"S_21SPINFIRE1",
	"S_21SPINFIRE2",
	"S_21SPINFIRE3",
	"S_21SPINFIRE4",
	"S_21SPINFIRE5",
	"S_21SPINFIRE6"
	*/
	)

// A_Boss1Laser ported from 2.1.25
function A_21Boss1Laser(actor, var1, var2)
	local x, y, z, floorz, speed
	local i
	local angle
	local point
	
	if not actor.target
		return true
	end

	if var2 == 0
		x = actor.x + P_ReturnThrustX(actor, actor.angle+ANGLE_90, FixedMul(43*FRACUNIT, actor.scale))
		y = actor.y + P_ReturnThrustY(actor, actor.angle+ANGLE_90, FixedMul(43*FRACUNIT, actor.scale))
		if (actor.eflags & MFE_VERTICALFLIP)
			z = actor.z + actor.height - FixedMul(56*FRACUNIT, actor.scale) - mobjinfo[var1].height
		else
			z = actor.z + FixedMul(56*FRACUNIT, actor.scale)
		end
		//break
	elseif var2 == 1
		x = actor.x + P_ReturnThrustX(actor, actor.angle-ANGLE_90, FixedMul(43*FRACUNIT, actor.scale))
		y = actor.y + P_ReturnThrustY(actor, actor.angle-ANGLE_90, FixedMul(43*FRACUNIT, actor.scale))
		if (actor.eflags & MFE_VERTICALFLIP)
			z = actor.z + actor.height - FixedMul(56*FRACUNIT, actor.scale) - mobjinfo[var1].height
		else
			z = actor.z + FixedMul(56*FRACUNIT, actor.scale)
		end
		//break
	else
		x = actor.x
		y = actor.y
		z = actor.z + actor.height/2
		//break
	end
	
	if not (actor.firing)
		actor.angle = R_PointToAngle2(x, y, actor.target.x, actor.target.y)
		if mobjinfo[var1].seesound
			S_StartSound(actor, mobjinfo[var1].seesound)
		end
		if not (actor.spawnpoint and (actor.spawnpoint.options & MTF_AMBUSH))
			point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor.angle, actor.radius), y + P_ReturnThrustY(actor, actor.angle, actor.radius), actor.z - actor.height / 2, MT_21EGGMOBILE_TARGET) // Replace with MT_21EGGMOBILE_TARGET if necessary
			point.fuse = actor.tics+1
			point.target = actor.target
			actor.target = point
			point.followPlayer = true // Let it follow the player
		else // Don't aim for the player, aim for the target
			point = P_SpawnMobj(actor.target.x, actor.target.y, actor.target.z, MT_21EGGMOBILE_TARGET) // Replace with MT_21EGGMOBILE_TARGET if necessary
			point.fuse = actor.tics+1
			point.target = actor.target
			actor.target = point
		end
	elseif actor.target// and not (actor.spawnpoint and (actor.spawnpoint.options & MTF_AMBUSH))
		actor.angle = R_PointToAngle2(x, y, actor.target.x, actor.target.y)
	end
	
	// Make sure the actor points to the target and not the player
	if not actor.laserTarget or not actor.laserTarget.valid
		actor.laserTarget = point
	end
	
	if actor.spawnpoint and (actor.spawnpoint.options & MTF_AMBUSH)
		angle = FixedAngle(FixedDiv(actor.tics*160*FRACUNIT, states[actor.state].tics*FRACUNIT) + 10*FRACUNIT)
	else
		angle = R_PointToAngle2(z + (mobjinfo[var1].height / 2), 0, actor.target.z, R_PointToDist2(x, y, actor.target.x, actor.target.y))
	end
	point = P_SpawnMobj(x, y, z, var1)
	point.target = actor
	point.angle = actor.angle
	speed = point.radius*2
	point.momz = FixedMul(cos(angle), speed)
	point.momx = FixedMul(sin(angle), FixedMul(cos(point.angle), speed))
	point.momy = FixedMul(sin(angle), FixedMul(sin(point.angle), speed))
	
	for i=0, 256, 1 do
		local mo = P_SpawnMobj(point.x, point.y, point.z, point.type)
		mo.angle = point.angle
		//P_UnsetThingPosition(mo)
		mo.flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY
		//P_SetThingPosition(mo)
		
		x = point.x
		y = point.y
		z = point.z
		
		if P_RailThinker(point)
			break
		end
	end
	
	floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_21EGGMOBILE_FIRE].height) // Relace MT_EGGMOBILE_FIRE with MT_21EGGMOBILE_FIRE if necessary
	if (z - floorz < mobjinfo[MT_21EGGMOBILE_FIRE].height / 2)
		point = P_SpawnMobj(x, y, floorz+1, MT_21EGGMOBILE_FIRE)
		point.target = actor
		point.destscale = 3*FRACUNIT
		point.scalespeed = FRACUNIT / (2^2)
		point.fuse = TICRATE
	end
	
	if actor.tics > 1
		actor.firing = true
	else
		actor.firing = false
	end
end

function A_21FocusTarget(actor, var1, var2)
	if actor.target and actor.followPlayer
		local speed = FixedMul(actor.info.speed, actor.scale)
		local dist = R_PointToDist2(actor.x, actor.y, actor.target.x, actor.target.y)
		local vangle = R_PointToAngle2(actor.z, 0, actor.target.z + (actor.target.height / 2), dist)
		local hangle = R_PointToAngle2(actor.x, actor.y, actor.target.x, actor.target.y)
		if var1 == 0
			actor.momx = $ - actor.momx / (2^4)
			actor.momy = $ - actor.momy / (2^4)
			actor.momz = $ - actor.momz / (2^4)
			actor.momz = $ + FixedMul(cos(vangle), speed)
			actor.momx = $ + FixedMul(sin(vangle), FixedMul(cos(hangle), speed))
			actor.momy = $ + FixedMul(sin(vangle), FixedMul(sin(hangle), speed))
		elseif var1 == 1
			if dist > speed
				actor.momz = FixedMul(cos(vangle), speed)
				actor.momx = FixedMul(sin(vangle), FixedMul(cos(hangle), speed))
				actor.momy = FixedMul(sin(vangle), FixedMul(sin(hangle), speed))
			else
				actor.momx = 0
				actor.momy = 0
				actor.momz = 0
				P_TeleportMove(actor, actor.x, actor.y, actor.target.z + (actor.target.height / 2))
				P_TryMove(actor, actor.target.x, actor.target.y, true)
			end
		end
	end
end

function A_21Boss1Spikeballs(actor, var1, var2)
	local ball = P_SpawnMobj(actor.x, actor.y, actor.z, MT_21EGGMOBILE_BALL)
	ball.target = actor
	ball.movedir = FixedAngle(FixedMul(FixedDiv(var1*(2^FRACBITS), var2*(2^FRACBITS)), 360*(2^FRACBITS)))
	ball.threshold = ball.radius + actor.radius + ball.info.painchance
	
	S_StartSound(ball, ball.info.seesound)
end

// Make the 2.1 Egg Mobile spawn 128 FRACUNITS off the ground if its map thing's z-position hasn't been set
addHook("MapThingSpawn", function(mobj, mapthing)
	local offset
	
	if mapthing.z ~= 0
		offset = mapthing.z
	else
		offset = 128*FRACUNIT
	end
	
	if (mapthing.options & MTF_OBJECTFLIP)
		if offset ~= 0
			mobj.z = mobj.z - offset
		else
			mobj.z = mobj.ceilingz
		end
	else
		if offset ~= 0
			mobj.z = mobj.z + offset
		else
			mobj.z = mobj.floorz
		end
	end
	
end, MT_21EGGMOBILE)

// Initializes variables for the 2.1 Egg Mobile
addHook("MobjSpawn", function(mobj)
	mobj.laserTarget = nil
	// The following variable is used to get around some hardcoded nonsense regarding MF2_FIRING,
	// specifically in terms of it automatically changing the angle of the mobj.
	// Change this if the aforementioned "hardcoded nonsense" has been taken care of 
	mobj.firing = false
end, MT_21EGGMOBILE)

addHook("MobjSpawn", function(mobj)
	mobj.followPlayer = false
end, MT_21EGGMOBILE_TARGET)

// Prevents the 2.1 Egg Mobile from stopping when it approaches the player in its panic state
addHook("MobjMoveCollide", function(tmthing, thing)
	return false
end, MT_21EGGMOBILE)

// Prevents the 2.1 Egg Mobile from bouncing off of the walls in "SKULLFLY" mode
addHook("MobjMoveBlocked", function(mobj)
	mobj.flags2 = $ & ~MF2_SKULLFLY
	mobj.momx = 0
	mobj.momy = 0
	mobj.momz = 0
	return true
end, MT_21EGGMOBILE)

addHook("MobjDamage", function(target, inflictor, source)
	if (target.flags2 & MF2_SKULLFLY)
		target.flags2 = $ & ~MF2_SKULLFLY
	end
	if target.firing
		target.firing = false
	end
end, MT_21EGGMOBILE)

// Ported BossThinker behavior for 2.1 Eggmobile from 2.1.25
addHook("BossThinker", function(mobj)
	
	if mobj.health < mobj.info.damage+1 and (leveltime & 1) and mobj.health > 0
		P_SpawnMobj(mobj.x, mobj.y, mobj.z, MT_SMOKE) // Replace MT_SMOKE with MT_21SMOKE if necessary
	end
	if mobj.flags2 & MF2_SKULLFLY
		P_SpawnGhostMobj(mobj)
		
		/*
		local spawnmobj
		spawnmobj = P_SpawnMobj(mobj.x, mobj.y, mobj.z, mobj.info.painchance)
		spawnmobj.target = mobj
		spawnmobj.color = SKINCOLOR_GREY
		*/
	end
	
	// P_Boss1Thinker ported from 2.1.25 with some fixups from 2.2
	if ((mobj.flags2 & MF2_FRET) and mobj.state == mobj.info.spawnstate)
		mobj.flags2 = $ & ~(MF2_FRET|MF2_SKULLFLY)
		mobj.momx = 0
		mobj.momy = 0
		mobj.momz = 0
	end
	
	if not mobj.tracer
		A_OldBossJetFumes(mobj, 0, 0)
	end
	
	if not mobj.target or not (mobj.target.flags & MF_SHOOTABLE)
		if mobj.target and mobj.target.health and mobj.target.type == MT_21EGGMOBILE_TARGET // Oh, we're just firing our laser.
			return true // It's okay, then.
		end
		
		if mobj.health <= 0
			if P_BossTargetPlayer(mobj, false) and mobj.info.mass // Bid farewell!
				S_StartSound(mobj, mobj.info.mass)
			end
			
			return true
		end
		
		// look for a new target
		if P_BossTargetPlayer(mobj, false) and mobj.info.seesound
			S_StartSound(mobj, mobj.info.seesound)
			
			return true
		end
	end
	
	if mobj.state ~= mobj.info.spawnstate and mobj.health > 0 and (mobj.flags & MF_FLOAT) and not (mobj.flags2 & MF2_SKULLFLY)
		mobj.momz = FixedMul(mobj.momz, 7*FRACUNIT/8)
	end
	
	if mobj.state == mobj.info.meleestate or (mobj.state == mobj.info.missilestate and mobj.health > mobj.info.damage)
		mobj.angle = R_PointToAngle2(mobj.x, mobj.y, mobj.target.x, mobj.target.y)
	end
	
	// Prevents the 2.1 Egg Mobile from floating downwards when the player gets too close
	if not (mobj.flags2 & MF2_INFLOAT)
		mobj.flags2 = $|MF2_INFLOAT
	end
	
	return true
end, MT_21EGGMOBILE)

addHook("BossDeath", function(mo)
	A_21BossDeath(mo)
	return true
end, MT_21EGGMOBILE)

addHook("MobjThinker", function(mobj)
	if mobj.laserTarget and mobj.laserTarget.valid
		mobj.target = mobj.laserTarget
	end
	
	// Continuously run A_21Boss1Laser when it is called
	if (mobj.firing) and mobj.target and mobj.health > 0
		if states[mobj.state].action == A_21Boss1Laser
			A_21Boss1Laser(mobj, states[mobj.state].var1, states[mobj.state].var2)
		end
	end
end, MT_21EGGMOBILE)

addHook("MobjThinker", function(mobj)
	if mobj.scale == mobj.destscale
		mobj.destscale = FRACUNIT
		mobj.scalespeed = FRACUNIT / 8
	end
end, MT_21EGGMOBILE_FIRE)

mobjinfo[MT_21EGGMOBILE] = {
	//$Name 2.1 Eggmobile
	//$Sprite 21EMA1
	//$Category Bosses
	//$Flags4Text End level on death
	//$Flags8Text Alternate laser attack
	doomednum = 4040,
	spawnstate = S_21EGGMOBILE_STND,
	spawnhealth = 8,
	seestate = S_21EGGMOBILE_STND,
	reactiontime = 45,
	painstate = S_21EGGMOBILE_PAIN,
	painchance = MT_THOK,
	painsound = sfx_odmpai,
	meleestate = S_21EGGMOBILE_LATK1,
	missilestate = S_21EGGMOBILE_RATK1,
	deathstate = S_21EGGMOBILE_DIE1,
	xdeathstate = S_21EGGMOBILE_FLEE1,
	deathsound = sfx_cybdth,
	speed = 4,
	radius = 24*FRACUNIT,
	height = 76*FRACUNIT,
	damage = 3,
	activesound = sfx_telept,
	flags = MF_SPECIAL|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_BOSS,
	raisestate = S_21EGGMOBILE_PANIC1
}

states[S_21EGGMOBILE_STND] =	{SPR_21EM,	A,	1,	A_Boss1Chase,	0,	0,	S_21EGGMOBILE_STND}

states[S_21EGGMOBILE_LATK1] =	{SPR_21EM,	B,	3,	A_FaceTarget,	0,				0,	S_21EGGMOBILE_LATK2}
states[S_21EGGMOBILE_LATK2] =	{SPR_21EM,	C,	15,	nil,			0,				0,	S_21EGGMOBILE_LATK3}
states[S_21EGGMOBILE_LATK3] =	{SPR_21EM,	D,	2,	A_FaceTarget,	0,				0,	S_21EGGMOBILE_LATK4}
states[S_21EGGMOBILE_LATK4] =	{SPR_21EM,	E,	1,	nil,			0,				0,	S_21EGGMOBILE_LATK5}
states[S_21EGGMOBILE_LATK5] =	{SPR_21EM,	F,	1,	nil,			0,				0,	S_21EGGMOBILE_LATK6}
states[S_21EGGMOBILE_LATK6] =	{SPR_21EM,	G,	1,	nil,			0,				0,	S_21EGGMOBILE_LATK7}
states[S_21EGGMOBILE_LATK7] =	{SPR_21EM,	H,	1,	nil,			0,				0,	S_21EGGMOBILE_LATK8}
states[S_21EGGMOBILE_LATK8] =	{SPR_21EM,	I,	45,	A_21Boss1Laser,	MT_OLDLASER,	0,	S_21EGGMOBILE_LATK9}
states[S_21EGGMOBILE_LATK9] =	{SPR_21EM,	J,	10,	nil,			0,				0,	S_21EGGMOBILE_LATK10}
states[S_21EGGMOBILE_LATK10] =	{SPR_21EM,	K,	2,	A_FaceTarget,	0,				0,	S_21EGGMOBILE_STND}

states[S_21EGGMOBILE_RATK1] =	{SPR_21EM,	L,	3,	A_FaceTarget,	0,				0,	S_21EGGMOBILE_RATK2}
states[S_21EGGMOBILE_RATK2] =	{SPR_21EM,	M,	15,	nil,			0,				0,	S_21EGGMOBILE_RATK3}
states[S_21EGGMOBILE_RATK3] =	{SPR_21EM,	N,	2,	A_FaceTarget,	0,				0,	S_21EGGMOBILE_RATK4}
states[S_21EGGMOBILE_RATK4] =	{SPR_21EM,	O,	1,	nil,			0,				0,	S_21EGGMOBILE_RATK5}
states[S_21EGGMOBILE_RATK5] =	{SPR_21EM,	P,	1,	nil,			0,				0,	S_21EGGMOBILE_RATK6}
states[S_21EGGMOBILE_RATK6] =	{SPR_21EM,	Q,	1,	nil,			0,				0,	S_21EGGMOBILE_RATK7}
states[S_21EGGMOBILE_RATK7] =	{SPR_21EM,	R,	1,	nil,			0,				0,	S_21EGGMOBILE_RATK8}
states[S_21EGGMOBILE_RATK8] =	{SPR_21EM,	S,	45,	A_21Boss1Laser,	MT_OLDLASER,	1,	S_21EGGMOBILE_RATK9}
states[S_21EGGMOBILE_RATK9] =	{SPR_21EM,	T,	10,	nil,			0,				0,	S_21EGGMOBILE_RATK10}
states[S_21EGGMOBILE_RATK10] =	{SPR_21EM,	U,	2,	A_FaceTarget,	0,				0,	S_21EGGMOBILE_STND}

states[S_21EGGMOBILE_PANIC1] =	{SPR_21EM,	D,	12,	nil,					0,	0,	S_21EGGMOBILE_PANIC2}
states[S_21EGGMOBILE_PANIC2] =	{SPR_21EM,	E,	4,	A_21Boss1Spikeballs,	0,	4,	S_21EGGMOBILE_PANIC3}
states[S_21EGGMOBILE_PANIC3] =	{SPR_21EM,	D,	5,	nil,					0,	0,	S_21EGGMOBILE_PANIC4}
states[S_21EGGMOBILE_PANIC4] =	{SPR_21EM,	E,	4,	A_21Boss1Spikeballs,	1,	4,	S_21EGGMOBILE_PANIC5}
states[S_21EGGMOBILE_PANIC5] =	{SPR_21EM,	D,	5,	nil,					0,	0,	S_21EGGMOBILE_PANIC6}
states[S_21EGGMOBILE_PANIC6] =	{SPR_21EM,	E,	4,	A_21Boss1Spikeballs,	2,	4,	S_21EGGMOBILE_PANIC7}
states[S_21EGGMOBILE_PANIC7] =	{SPR_21EM,	D,	5,	nil,					0,	0,	S_21EGGMOBILE_PANIC8}
states[S_21EGGMOBILE_PANIC8] =	{SPR_21EM,	E,	4,	A_21Boss1Spikeballs,	3,	4,	S_21EGGMOBILE_PANIC9}
states[S_21EGGMOBILE_PANIC9] =	{SPR_21EM,	D,	5,	nil,					0,	0,	S_21EGGMOBILE_PANIC10}
states[S_21EGGMOBILE_PANIC10] =	{SPR_21EM,	A,	35,	A_SkullAttack,			0,	0,	S_21EGGMOBILE_STND}

states[S_21EGGMOBILE_PAIN] =	{SPR_21EM,	V,	24,	A_Pain,			0,	0,	S_21EGGMOBILE_PAIN2}
states[S_21EGGMOBILE_PAIN2] =	{SPR_21EM,	V,	16,	A_SkullAttack,	1,	1,	S_21EGGMOBILE_STND}

// 2.1 Egg Mobile Spikeball

mobjinfo[MT_21EGGMOBILE_BALL] = {
	doomednum = -1,
	spawnstate = S_21EGGMOBILE_BALL,
	spawnhealth = 1,
	seesound = sfx_cannon,
	reactiontime = 1,
	painchance = 40*FRACUNIT,
	speed = 2*FRACUNIT,
	radius = 13*FRACUNIT,
	height = 26*FRACUNIT,
	damage = 8*FRACUNIT,
	mass = DMG_SPIKE,
	flags = MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT // MF_NOCLIP and MF_NOCLIPTHING (present in 2.1's spikeball) result in this object being intangible when attached to a boss
}

// Replace A_UnidusBall with a 2.1 variant, if it changes in the future
states[S_21EGGMOBILE_BALL] = {SPR_OUNI, B, 1, A_UnidusBall, 2, 0, S_21EGGMOBILE_BALL}

// 2.1 Egg Mobile Target (required for the laser to function properly)

mobjinfo[MT_21EGGMOBILE_TARGET] = {
	doomednum = -1,
	spawnstate = S_21EGGMOBILE_TARGET,
	spawnhealth = 1,
	speed = 2*FRACUNIT,
	flags = MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY
}

states[S_21EGGMOBILE_TARGET] = {SPR_NULL, 0, 1, A_21FocusTarget, 0, 0, S_21EGGMOBILE_TARGET}

// 2.1 Egg Mobile Laser

mobjinfo[MT_OLDLASER] = {
	doomednum = -1,
	spawnstate = S_OLDLASER,
	spawnhealth = 1000,
	seesound = sfx_orlaun,
	reactiontime = 8,
	speed = 20*FRACUNIT,
	radius = 11*FRACUNIT,
	height = 8*FRACUNIT,
	mass = 100,
	damage = 20,
	flags = MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY
}

states[S_OLDLASER] = {SPR_MISL, A|FF_FULLBRIGHT, 2, nil, 0, 0, S_NULL}

// 2.1 Egg Mobile Laser Fire
mobjinfo[MT_21EGGMOBILE_FIRE] = {
	doomednum = -1,
	spawnstate = S_SPINFIRE1,
	spawnhealth = 1,
	reactiontime = 8,
	radius = 8*FRACUNIT,
	height = 14*FRACUNIT,
	mass = 100,
	damage = 1,
	flags = MF_NOBLOCKMAP|MF_NOGRAVITY|MF_FIRE|MF_PAIN
}
