-- Lobster lamo
-- by Callmore
--fucked with by minenice

-- @minenice why did you make a constant case variable and then modify the contence of the table inside
-- so that it doesent work if there is more than one

freeslot(
	"SPR_GTLB",
	
	"MT_GREENTROPICS_LOBSTER",
	"S_GREENTROPICS_LOBSTER",
	
	"MT_GREENTROPICS_LOBSTER_SEGMENT",
	"S_GREENTROPICS_LOBSTER_BALL",
	"S_GREENTROPICS_LOBSTER_ARM",
	"S_GREENTROPICS_LOBSTER_GUN",
	"S_GREENTROPICS_LOBSTER_FLASH1",
	"S_GREENTROPICS_LOBSTER_FLASH2",
	"S_GREENTROPICS_LOBSTER_FLASH3",
	"S_GREENTROPICS_LOBSTER_FLASH4",
	
	"MT_GREENTROPICS_LOBSTER_PROJECTILE",
	"S_GREENTROPICS_LOBSTER_PROJECTILE",
	
	"sfx_gtlfir"
)

local MAX_RANGE = 3250 * FRACUNIT

mobjinfo[MT_GREENTROPICS_LOBSTER] = {
	doomednum = 2538,
	spawnstate = S_GREENTROPICS_LOBSTER,
	spawnhealth = 1000,
	radius = 32*FRACUNIT,
	height = 64*FRACUNIT,
	flags = MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
}

mobjinfo[MT_GREENTROPICS_LOBSTER_SEGMENT] = {
	spawnstate = S_INVISIBLE,
	spawnhealth = 1000,
	radius = 32*FRACUNIT,
	height = 64*FRACUNIT,
	flags = MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY--|MF_NOTHINK
}

mobjinfo[MT_GREENTROPICS_LOBSTER_PROJECTILE] = {
	spawnstate = S_GREENTROPICS_LOBSTER_PROJECTILE,
	spawnhealth = 1000,
	radius = 8*FRACUNIT,
	height = 16*FRACUNIT,
}

states[S_GREENTROPICS_LOBSTER] = {SPR_GTLB, A, -1, nil, 0, 0, S_GREENTROPICS_LOBSTER}
states[S_GREENTROPICS_LOBSTER_BALL] = {SPR_GTLB, B, -1, nil, 0, 0, S_GREENTROPICS_LOBSTER_BALL}
states[S_GREENTROPICS_LOBSTER_GUN] = {SPR_GTLB, C, -1, nil, 0, 0, S_GREENTROPICS_LOBSTER_GUN}
states[S_GREENTROPICS_LOBSTER_ARM] = {SPR_GTLB, D, -1, nil, 0, 0, S_GREENTROPICS_LOBSTER_ARM}
states[S_GREENTROPICS_LOBSTER_FLASH1] = {
	sprite = SPR_GTLB,
	frame = FF_ANIMATE|F,
	tics = 3*2,
	nextstate = S_NULL,
	var1 = 3,
	var2 = 2,
}
states[S_GREENTROPICS_LOBSTER_FLASH2] = {
	sprite = SPR_GTLB,
	frame = FF_ANIMATE|F,
	tics = 2*3,
	nextstate = S_NULL,
	var1 = 2,
	var2 = 3,
}
states[S_GREENTROPICS_LOBSTER_PROJECTILE] = {SPR_GTLB, E, -1, nil, 0, 0, S_GREENTROPICS_LOBSTER_PROJECTILE}

local ballOffset = {0, -96, -160, -96}

local armLower = {0, -64, -96, -96, -64}

local SHOT_REPEAT_FRAME = TICRATE+TICRATE/5
local LOBBER_GUN_YOFFSET = -32*FRACUNIT

local LOBBER_DEVIATION_MAX = 2*ANG1
local LOBBER_DEVIATION_LOOKAHEAD = 55
local LOBBER_HEAD_OFFSET = -72*FRACUNIT

local BALL_GAP = 278
local GUN_DIST = 574
local GUN_RECOIL = 256
local GUN_MUZZLE_OFFSET = 200
local GUN_MUZZLE_OFFSET_Y = 32
local ARM_SIDE_DIST = 190
local ARM_YOFFSET = -80*FRACUNIT
local ARM_FORWARD_DIST = 64

local BURST_WARNFRM = TICRATE+TICRATE/1

local function lerp(a, b, f)
	return a + FixedMul(b - a, f)
end

local function getYBob(x)
	return FixedMul(sin((x) * (ANG1 * 5)) * 10, mapobjectscale)
end

local ARM_SIDE_DIR = {ANGLE_90, ANGLE_270}
local ARM_SIDE_DIR_HALF = {ANGLE_45, ANGLE_315}

local function headThink(mo)
	if not mo.gt_init then
		mo.gt_init = true
		mo.gt_x = mo.x
		mo.gt_y = mo.y
		mo.gt_z = mo.z
		
		if mo.spawnpoint and mo.spawnpoint.valid then
			mo.gt_timeOffset = mo.spawnpoint.extrainfo
		else
			mo.gt_timeOffset = leveltime
		end
		
		mo.scale = $*5
		
		mo.gt_gunRecoilProg = {0, 0}
		
		mo.gt_segments = {}
		for i = 1, 4 do
			local ball = P_SpawnMobj(mo.gt_x, mo.gt_y, mo.gt_z, MT_GREENTROPICS_LOBSTER_SEGMENT)
			ball.scale = $*5
			ball.gt_timeOffset = (i * 8) - mo.gt_timeOffset
			ball.state = S_GREENTROPICS_LOBSTER_BALL
			mo.gt_segments[i] = ball
		end
		
		mo.gt_arm = {}
		mo.gt_gun = {}
		for arm = 1, 2 do
			mo.gt_arm[arm] = {}
			for i = 1, 5 do
				local ball = P_SpawnMobj(mo.gt_x, mo.gt_y, mo.gt_z, MT_GREENTROPICS_LOBSTER_SEGMENT)
				ball.scale = $*3
				ball.gt_timeOffset = ((6 - i) * 8) - mo.gt_timeOffset
				ball.state = S_GREENTROPICS_LOBSTER_ARM
				mo.gt_arm[arm][i] = ball
			end
			
			mo.gt_gun[arm] = P_SpawnMobj(mo.gt_x, mo.gt_y, mo.gt_z, MT_GREENTROPICS_LOBSTER_SEGMENT)
			mo.gt_gun[arm].state = S_GREENTROPICS_LOBSTER_GUN
			mo.gt_gun[arm].scale = $*4
		end
	end
	
	-- Find closest player
	local closest = nil
	local closest_dist = UINT32_MAX
	for p in players.iterate do
		if p and p.valid and p.mo and p.mo.valid then
			local dist = R_PointToDist2(mo.x, mo.y, p.mo.x, p.mo.y)
			if not closest or dist < closest_dist then
				closest_dist = R_PointToDist2(mo.x, mo.y, p.mo.x, p.mo.y)
				closest = p
			end
		end
	end
	if closest then
		-- turn towards (in-front) player
		mo.angle = $ + min(max(R_PointToAngle2(mo.x, mo.y, closest.mo.x + (closest.mo.momx * LOBBER_DEVIATION_LOOKAHEAD), closest.mo.y + (closest.mo.momy * LOBBER_DEVIATION_LOOKAHEAD)) - mo.angle, -LOBBER_DEVIATION_MAX), LOBBER_DEVIATION_MAX)
	end
	
	if mo.gt_gunRecoilProg[1] then mo.gt_gunRecoilProg[1] = $-1 end
	if mo.gt_gunRecoilProg[2] then mo.gt_gunRecoilProg[2] = $-1 end
	
	-- Head
	P_TeleportMove(mo,
				   mo.gt_x,
				   mo.gt_y,
				   mo.gt_z + (BALL_GAP * 4 * mapobjectscale) + getYBob(leveltime + mo.gt_timeOffset) + FixedMul(LOBBER_HEAD_OFFSET, mapobjectscale))
	
	-- Body
	for i = 1, 4 do
		local ball = mo.gt_segments[i]
		ball.angle = mo.angle
		P_TeleportMove(ball,
					   mo.gt_x + cos(mo.angle) * ballOffset[i],
					   mo.gt_y + sin(mo.angle) * ballOffset[i],
					   mo.gt_z + (BALL_GAP * (i - 1) * mapobjectscale) + getYBob(leveltime + ball.gt_timeOffset))
	end
	
	-- Guns
	for side = 1, 2 do
		--print("recoil progress "..mo.gt_gunRecoilProg[side].." for gun "..side)
		local rf = FixedDiv(mo.gt_gunRecoilProg[side], 8)
		local gun = mo.gt_gun[side]
		gun.angle = mo.angle
		P_TeleportMove(gun,
			mo.gt_x + cos(mo.angle + ARM_SIDE_DIR_HALF[side]) * GUN_DIST - cos(mo.angle) * lerp(0, FixedMul(GUN_RECOIL, mapobjectscale), FixedMul(rf, rf)),
			mo.gt_y + sin(mo.angle + ARM_SIDE_DIR_HALF[side]) * GUN_DIST - sin(mo.angle) * lerp(0, FixedMul(GUN_RECOIL, mapobjectscale), FixedMul(rf, rf)),
			mo.gt_z + (BALL_GAP * 4 * mapobjectscale) + ARM_YOFFSET + LOBBER_GUN_YOFFSET + getYBob(leveltime + mo.gt_timeOffset))
	end
	
	-- Arms
	for arm = 1, 2 do
		local startX = mo.gt_x + cos(mo.angle + ARM_SIDE_DIR[arm]) * ARM_SIDE_DIST - cos(mo.angle) * ARM_FORWARD_DIST
		local startY = mo.gt_y + sin(mo.angle + ARM_SIDE_DIR[arm]) * ARM_SIDE_DIST - sin(mo.angle) * ARM_FORWARD_DIST
		local startZ = mo.gt_z + (BALL_GAP * 4 * mapobjectscale) + ARM_YOFFSET
		local endX = mo.gt_gun[arm].x
		local endY = mo.gt_gun[arm].y
		local endZ = mo.gt_gun[arm].z
		for i = 1, 5 do
			local ball = mo.gt_arm[arm][i]
			ball.angle = mo.angle
			P_TeleportMove(ball,
						   lerp(startX, endX, FixedDiv(i - 1, 5)),
						   lerp(startY, endY, FixedDiv(i - 1, 5)),
						   lerp(startZ, endZ, FixedDiv(i - 1, 5)) + armLower[i] * FRACUNIT + getYBob(leveltime + ball.gt_timeOffset))
		end
	end
	
	if not closest then return end
	-- Fire!
	if leveltime % SHOT_REPEAT_FRAME == 0 then
		local fireFrom = (leveltime / SHOT_REPEAT_FRAME) % 2 and 2 or 1
		mo.gt_gunRecoilProg[fireFrom] = 8
		
		S_StartSound(mo.gt_gun[fireFrom], sfx_s263)
		S_StartSound(mo.gt_gun[fireFrom], sfx_s3k77)
		
		local orb = P_SpawnMobj(mo.gt_gun[fireFrom].x, mo.gt_gun[fireFrom].y, mo.gt_gun[fireFrom].z, MT_GREENTROPICS_LOBSTER_PROJECTILE)
		local rnd = P_RandomFixed() * 8
		orb.angle = mo.gt_gun[fireFrom].angle
		orb.momx = FixedMul(FixedMul(cos(orb.angle), rnd + 28 * FRACUNIT), mapobjectscale)
		orb.momy = FixedMul(FixedMul(sin(orb.angle), rnd + 28 * FRACUNIT), mapobjectscale)
		orb.momz = FixedMul(P_RandomFixed() * 8 - 5*FRACUNIT, mapobjectscale)
		orb.fuse = BURST_WARNFRM
		
		local flash = P_SpawnMobj(mo.gt_gun[fireFrom].x + cos(mo.angle) * GUN_MUZZLE_OFFSET,
								  mo.gt_gun[fireFrom].y + sin(mo.angle) * GUN_MUZZLE_OFFSET,
								  mo.gt_gun[fireFrom].z + GUN_MUZZLE_OFFSET_Y * FRACUNIT, MT_THOK)
		flash.state = S_GREENTROPICS_LOBSTER_FLASH2
		flash.scale = $ * 3
		flash.destscale = $ * 5
	end
end
addHook("MobjThinker", headThink, MT_GREENTROPICS_LOBSTER)

local shouldFlash = {
	-- 1 3 4 7 8 9 13 14 15 16 21 22 23 24 25 31 32 33 34 35 36
	[1] = true,
	[3] = true,
	[4] = true,
	[7] = true,
	[8] = true,
	[9] = true,
	[13] = true,
	[14] = true,
	[15] = true,
	[16] = true,
	[21] = true,
	[22] = true,
	[23] = true,
	[24] = true,
	[25] = true,
	[31] = true,
	[32] = true,
	[33] = true,
	[34] = true,
	[35] = true,
	[36] = true,
	[43] = true,
	[44] = true,
	[45] = true,
	[46] = true,
	[47] = true,
	[48] = true,
	[49] = true,
	[57] = true,
	[58] = true,
	[59] = true,
	[60] = true,
	[61] = true,
	[62] = true,
	[63] = true,
	[64] = true,
	[73] = true,
	[74] = true,
	[75] = true,
	[76] = true,
	[77] = true,
	[78] = true,
	[79] = true,
	[80] = true,
	[81] = true,
	[91] = true,
	[92] = true,
	[93] = true,
	[94] = true,
	[95] = true,
	[96] = true,
	[97] = true,
	[98] = true,
	[99] = true,
	[100] = true,
}

local function projectileThink(mo)
	if P_IsObjectOnGround(mo) then
		if not mo.gt_hasBounced then
			mo.gt_hasBounced = true
			mo.momz = 5 * FRACUNIT
		end
		mo.friction = $ / 2
	end
	
	if shouldFlash[mo.fuse] then
		mo.color = SKINCOLOR_RED
		mo.colorized = true
		if not shouldFlash[mo.fuse + 1] then
			S_StartSound(mo, sfx_deton)
		end
	else
		mo.color = SKINCOLOR_NONE
		mo.colorized = false
	end
end
addHook("MobjThinker", projectileThink, MT_GREENTROPICS_LOBSTER_PROJECTILE)

local function burstRing(mo)
	local radius = mo.radius>>FRACBITS
	local height = mo.height>>FRACBITS
	for i = 0, 16 do
		local dust = P_SpawnMobj(mo.x, mo.y, mo.z, MT_SMOKE)
		dust.state = S_OPAQUESMOKE1
		dust.angle = (ANGLE_180/8) * i
		dust.scale = mo.scale
		dust.destscale = mo.scale*10
		dust.scalespeed = mo.scale/12
		P_InstaThrust(dust, dust.angle, FixedMul(20*FRACUNIT, mo.scale))
	end
end

local function projectileFuse(mo)
	-- Snippet from wiki
	-- https://wiki.srb2.org/wiki/Lua/Kart/Functions
	K_SpawnKartExplosion(mo.x, mo.y, mo.z,
		FixedMul((3 * (192 * FRACUNIT)) / 2, mapobjectscale) + (32 * mapobjectscale), 32, MT_MINEEXPLOSION,
		i * (ANGLE_45 / 4), true, false, nil)
	local flash = P_SpawnMobj(mo.x,
							  mo.y,
							  mo.z, MT_THOK)
	flash.state = S_GREENTROPICS_LOBSTER_FLASH1
	flash.scale = $ * 7
	flash.destscale = $ * 12
	P_SpawnMobj(mo.x, mo.y, mo.z, MT_MINEEXPLOSIONSOUND)
	
	local smoldering = P_SpawnMobj(mo.x, mo.y, mo.z, MT_SMOLDERING)
	K_MatchGenericExtraFlags(smoldering, mo)
	burstRing(mo)

	smoldering.tics = TICRATE*3
end
addHook("MobjFuse", projectileFuse, MT_GREENTROPICS_LOBSTER_PROJECTILE)
