-- Floating Item Spawner by Ashnal
-- This script is designed to be repacked with map pk3s/wads, but otherwise not modified. If you need it modified (for example if the map thing number conflicts with something else), please let me know.

-- Literally just spits out floating papersprite items (the ones that you drop when exploded) at the location of its spawnpoint, similar to an item box

-- Thing number is 1777. Parameter in ZoneBuilder +1 is the item type, corresponding to the kartdebugitem list, or a list below for reference
-- Angle is the amount to give, anything over 255 will cap the amount to 255, 0 will result in 1. Otherwise, angle shown in ZB = amount.
-- Setting the AMBUSH flag will cause the spawned floating item to have no gravity and stay in place
-- Setting the SPECIAL flag will cause the spawner to only ever spawn one item, creating a one time pickup
-- Setting the EXTRA flag will ensure that a player can never pick up more than the amount designated from the pickup. 
--    For example, if you put 3 spawners near each other that give a player 2 sneakers and set EXTRA on them, they will gain 2 sneaker on the first one they drive over, but then if they drive over another, they will be unable to collect them
--    If a player has some of the given item, but not as many as you've set on the spawner, they will be given
-- Floating items behave as the normal in game one, including sliding down physics slopes and gravity (if you dont set Ambush flag)

/*
Sneaker 0
Rocket Sneaker 1
Invincibility 2
Banana 3
Eggman 4
Orbinaut 5
Jawz 6
Mine 7
Ballhog 8
Self-Propelled Bomb 9
Grow 10
Shrink 11
Lightning Shield 12
Hyudoro 13
Pogo Spring 14
Kitchen Sink 15
*/

if not floatingitemspawner then rawset(_G, "floatingitemspawner", true) else return end

local MT_FLOATINGITEM = MT_FLOATINGITEM
local MF_NOGRAVITY = MF_NOGRAVITY
local MTF_AMBUSH = MTF_AMBUSH
local MTF_OBJECTSPECIAL = MTF_OBJECTSPECIAL
local MF_NOCLIPTHING = MF_NOCLIPTHING
local MF2_NIGHTSPULL = MF2_NIGHTSPULL
local INT32_MAX = INT32_MAX
local ANG1 = ANG1
local MTF_EXTRA = 1 -- Doesnt exist as a real flag in source code, but exists in ZB

freeslot("MT_FLOATINGITEMSPAWNER")

mobjinfo[MT_FLOATINGITEMSPAWNER] = {
	doomednum = 1777,
	spawnstate = S_INVISIBLE,
	flags = MF_NOCLIP|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOBLOCKMAP|MF_NOCLIPTHING
}

addHook("MobjSpawn", function(mo)
	mo.cooldown = 0
end, MT_FLOATINGITEMSPAWNER)

local function touched(special, toucher)
	if not (special.spawnedbyspawner and special.spawner and special.spawner.spawnpoint.options & MTF_EXTRA and toucher.player and not toucher.player.kartstuff[k_itemheld] and toucher.player.kartstuff[k_itemtype] == special.threshold and toucher.player.kartstuff[k_itemamount] > 0) then return end
	if toucher.player.kartstuff[k_itemamount] >= special.movecount then return true end -- prevent collection, the player has as many or more than this would give
	special.movecount = $ - toucher.player.kartstuff[k_itemamount] -- they have less, adjust amount to bring player up to the amount desired
end
addHook("TouchSpecial", touched, MT_FLOATINGITEM)

local xitemhooked = false
local basecooldown = 5*TICRATE/2
addHook("MobjThinker", function(mo)
	if (mo.spawneditem and mo.spawneditem.valid) then -- If the reference goes invalid, we know that a player picked up the floating item
		-- Nothing
	elseif mo.cooldown then
		mo.cooldown = max(0, $-1)
	else

		local typetospawn = MT_FLOATINGITEM
        if xItemLib then
			if not xitemhooked then
				addHook("TouchSpecial", touched, MT_FLOATINGXITEM)
				xitemhooked = true
			end
			typetospawn = MT_FLOATINGXITEM
        end
		
		mo.spawneditem = P_SpawnMobj(mo.x, mo.y, mo.z+P_MobjFlip(mo), typetospawn)
		if mo.spawneditem and mo.spawneditem.valid then
            
			if mo.spawnpoint.options & MTF_AMBUSH then
			    mo.spawneditem.flags = $|MF_NOGRAVITY
				mo.spawneditem.flags2 = $|MF2_NIGHTSPULL  -- This and the next line are some options I picked up from hardcode to allow the item to float while still animating and behaving properly
				mo.spawneditem.tracer = mo.spawneditem	  -- Has to be set to something or hardcode calls P_RemoveMobj on it
            else
				mo.spawneditem.momz = P_MobjFlip(mo)*1*mapobjectscale
				mo.spawneditem.flags = $|MF_NOCLIPTHING -- Vanilla does this, and removes it when it touches the ground
			end
			
			mo.spawneditem.spawnedbyspawner = true -- In case a thinker elsewhere wants to check it or something
			mo.spawneditem.spawner = mo
			mo.spawneditem.fuse = INT32_MAX -- Prevents kmp itemfuse from removing it, an effectively infinite timer
			
			-- Cheap spawn animation, it starts really small and scales up into visibility
			mo.spawneditem.scale = mo.spawneditem.scale/12
			mo.spawneditem.destscale = mapobjectscale
			mo.spawneditem.scalespeed = (mapobjectscale-mo.spawneditem.scale)/20
			
			-- This sets the type and amount
			mo.spawneditem.threshold = mo.spawnpoint.extrainfo+1 -- Parameter in ZB goes from 0-15, and items (excepting SAD and NONE) range from 1-16, so +1 to align them
			mo.spawneditem.movecount = max(min(mo.angle/ANG1, 255), 1)
			
			mo.cooldown = basecooldown
			
			if mo.spawnpoint.options & MTF_OBJECTSPECIAL then
				P_RemoveMobj(mo) -- If flagged with special, then just remove yourself to create a one time spawn
			end
		end
	end
	return true
end, MT_FLOATINGITEMSPAWNER)