dofile_once("mods/lewd_noita/files/sex_logic/controls_toggle.lua")
dofile_once("mods/lewd_noita/files/sex_logic/apply_parasite.lua")
dofile_once("mods/lewd_noita/files/womb/womb_utils.lua")
dofile_once("mods/lewd_noita/files/text/ln_text.lua")

-- global sd table contains tables for all sex scenes currently happening
-- when scene is created its id is used as index in sd table
if sd == nil then
	sd = {}
end

local scene = GetUpdatedEntityID()
local scene_x, scene_y = EntityGetTransform(scene)

SetRandomSeed(GameGetFrameNum(), GetUpdatedEntityID())

if sd[scene] == nil then
	nxml = dofile_once("mods/lewd_noita/nxml.lua")
	sd[scene] = {}

	sd[scene].scene_id = GetUpdatedEntityID()
	sd[scene].scene_name = EntityGetName(sd[scene].scene_id)

	-- chance for alternative scene to trigger
	if ModTextFileGetContent("mods/lewd_noita/files/sex_gfx/" .. sd[scene].scene_name .. "_alt.xml") ~= nil then
		if Random(0, 1) == 1 then
			sd[scene].scene_name = sd[scene].scene_name .. "_alt"
		end
	end

	-- check if "_double" or "_alt_double" for current scene exists
	if ModTextFileGetContent("mods/lewd_noita/files/sex_gfx/" .. sd[scene].scene_name .. "_double.xml") or
	ModTextFileGetContent("mods/lewd_noita/files/sex_gfx/" .. string.sub(sd[scene].scene_name, 1, -5) .. "_alt.xml") then
		sd[scene].check_double = true
	end

	sd[scene].sprite_metadata = ModTextFileGetContent("mods/lewd_noita/files/sex_gfx/" .. sd[scene].scene_name .. ".xml")
	sd[scene].meta_fly = nxml.parse(sd[scene].sprite_metadata).attr.ln_flying or "0"
	sd[scene].meta_preg = nxml.parse(sd[scene].sprite_metadata).attr.ln_pregnancy or "none"
	sd[scene].meta_cum = nxml.parse(sd[scene].sprite_metadata).attr.ln_cum_material or "ln_cum"
	sd[scene].meta_effect = nxml.parse(sd[scene].sprite_metadata).attr.ln_apply_effect or "none"
	sd[scene].stages = nxml.parse(sd[scene].sprite_metadata):all_of("RectAnimation")

	for i, rect in ipairs(sd[scene].stages) do
		if rect.attr.name == "cum_stage" then
			sd[scene].cum_stage = i
		end
	end

	sd[scene].current_frame_count = tonumber(sd[scene].stages[1].attr.frame_count)

	-- frame_wait * 60 to convert seconds to frames
	sd[scene].current_frame_wait = tonumber(sd[scene].stages[1].attr.frame_wait) * 60
	sd[scene].current_loop_duration = math.floor(sd[scene].current_frame_count * sd[scene].current_frame_wait)

	sd[scene].current_stage = 0
	sd[scene].current_loop = 0
	sd[scene].stage_start_frame = GameGetFrameNum()

	EntityAddComponent2(sd[scene].scene_id, "SpriteComponent", {
		image_file="mods/lewd_noita/files/sex_gfx/" .. sd[scene].scene_name .. ".xml",
		rect_animation=sd[scene].stages[sd[scene].current_stage + 1].attr.name,
		z_index=-2,
	})

	-- find out who is who
	for _, child in ipairs(EntityGetAllChildren(sd[scene].scene_id)) do
		if IsPlayer(child) == true or EntityGetName(child) == "$animal_kobold" then
			sd[scene].kobold_id = child
			disable_entity(sd[scene].kobold_id)
		elseif EntityHasTag(child, "enemy") == true then
			sd[scene].enemy_id = child
			disable_entity(sd[scene].enemy_id)

			-- store enemy name to show if enemy dies
			sd[scene].translated_enemy_name = GameTextGetTranslatedOrNot(EntityGetName(sd[scene].enemy_id))
		end
	end

	-- remove fog of war if scene falls down to unexplored area
	if IsPlayer(sd[scene].kobold_id) == true then
		local fog_of_war_remover = EntityCreateNew("ln_fow_remove")
		EntityAddComponent2(fog_of_war_remover, "InheritTransformComponent", {})
		EntityAddComponent2(fog_of_war_remover, "SpriteComponent", {
			image_file="data/particles/fog_of_war_hole_128.xml",
			fog_of_war_hole=true,
		})
		EntityAddChild(sd[scene].scene_id, fog_of_war_remover)
	end

	EntityAddComponent2(sd[scene].scene_id, "AudioComponent", {
		file="mods/lewd_noita/files/audio/Desktop/lewd_noita.bank",
		event_root="root",
	})

	-- this sound is used for birth
	EntityAddComponent2(sd[scene].scene_id, "AudioComponent", {
		file="data/audio/Desktop/animals.bank",
		event_root="animals/slime/damage",
	})

	-- worms should fuck off while scene plays since they will damage you if close
	EntityAddComponent2(sd[scene].scene_id, "WormAttractorComponent", {
		direction=-1,
		radius=300,
	})

	-- scene should fall down if enemy can't fly or does not fly during sex
	if sd[scene].meta_fly == "0" then
		EntityAddComponent2(sd[scene].scene_id, "VelocityComponent", {})
		EntityAddComponent2(sd[scene].scene_id, "SimplePhysicsComponent", {})
	end

	-- stop fungus explosion
	if sd[scene].scene_name == "fungus" then
		EntitySetComponentIsEnabled(sd[scene].enemy_id, EntityGetFirstComponent(sd[scene].enemy_id, "CollisionTriggerComponent"), false)
		EntitySetComponentIsEnabled(sd[scene].enemy_id, EntityGetFirstComponent(sd[scene].enemy_id, "CollisionTriggerComponent"), false)
		EntityRemoveComponent(sd[scene].enemy_id, EntityGetFirstComponentIncludingDisabled(sd[scene].enemy_id, "ParticleEmitterComponent"))
		EntityRemoveComponent(sd[scene].enemy_id, EntityGetFirstComponentIncludingDisabled(sd[scene].enemy_id, "ParticleEmitterComponent"))
	end

	-- add nymphomania perk effect
	if IsPlayer(sd[scene].kobold_id) == true and GameHasFlagRun("PERK_PICKED_LN_NYMPHOMANIA") == true then
		sd[scene].nymphomania = EntityCreateNew("ln_perk_effect_nymphomania")
		EntityAddComponent2(sd[scene].nymphomania, "InheritTransformComponent", {})
		EntityAddComponent2(sd[scene].nymphomania, "GameEffectComponent", {
			effect="PROTECTION_ALL",
			frames=4199,
		})
		EntityAddComponent2(sd[scene].nymphomania, "UIIconComponent", {
			icon_sprite_file="mods/lewd_noita/files/perks/nymphomania_status.png",
			name="Invulnerability",
			description="Your love for sex protects you from damage.",
			is_perk=false,
		})
		EntityAddChild(sd[scene].kobold_id, sd[scene].nymphomania)
	end

	-- check if wands have spells that emit particles (tentacle/firebolt/electric spells etc)
	-- those are disabled on scene start but if you switch wands during scene they are reset
	-- wands can be switched using hotkeys (ctrl, alt etc) which are not blocked even with ControlsComponent disabled
	if IsPlayer(sd[scene].kobold_id) == true then
		for _, ch in ipairs(EntityGetAllChildren(sd[scene].kobold_id)) do
			if EntityGetName(ch) == "inventory_quick" and EntityGetAllChildren(ch) ~= nil then
				for _, inv_ch in ipairs(EntityGetAllChildren(ch)) do
					if EntityHasTag(inv_ch, "wand") and EntityGetAllChildren(inv_ch) ~= nil then
						for _, card in ipairs(EntityGetAllChildren(inv_ch)) do
							if EntityGetFirstComponentIncludingDisabled(card, "ParticleEmitterComponent") ~= nil or EntityGetFirstComponentIncludingDisabled(card, "SpriteParticleEmitterComponent") ~= nil then
								sd[scene].check_emitting_wands = true
								break
							end
						end
					end
				end
			end
		end
	end
end

-- conditions for ending scene early
-- kobold dead or polymorphed
if EntityGetIsAlive(sd[scene].kobold_id) == false then

	-- enable enemy if it is still part of the scene
	if sd[scene].current_stage < #sd[scene].stages then
		if sd[scene].enemy_id ~= nil and EntityGetIsAlive(sd[scene].enemy_id) == true then
			enable_entity(sd[scene].enemy_id)
		end

		if sd[scene].second_enemy_id ~= nil and EntityGetIsAlive(sd[scene].second_enemy_id) == true then
			enable_entity(sd[scene].second_enemy_id)
		end
	end

	-- spawn entity that enables kobold after polymorph ends
	-- player entity is enabled by poly_player_enable.lua
	local poly_handler = EntityCreateNew("ln_poly_enemy_enable")
	EntitySetTransform(poly_handler, scene_x, scene_y)
	EntityAddComponent2(poly_handler, "LuaComponent", {
		script_source_file="mods/lewd_noita/files/sex_logic/poly_enemy_enable.lua"
	})
	EntityAddComponent2(poly_handler, "LifetimeComponent", {
		lifetime=1800
	})

	EntityKill(sd[scene].scene_id)
	sd[scene] = nil
	return
end

-- kobold ignited
if ComponentGetValue2(EntityGetFirstComponent(sd[scene].kobold_id, "DamageModelComponent"), "is_on_fire") == true then

	-- if enemy present and can ignite it stops fucking and scene ends
	if sd[scene].current_stage < #sd[scene].stages and
	sd[scene].enemy_id ~= nil and
	EntityGetIsAlive(sd[scene].enemy_id) == true and
	ComponentGetValue2(EntityGetFirstComponent(sd[scene].enemy_id, "DamageModelComponent"), "fire_probability_of_ignition") > 0 then

		enable_entity(sd[scene].enemy_id)

		-- enable second enemy
		if sd[scene].second_enemy_id ~= nil and EntityGetIsAlive(sd[scene].second_enemy_id) == true then
			enable_entity(sd[scene].second_enemy_id)
		end

		enable_entity(sd[scene].kobold_id)
		EntityKill(sd[scene].scene_id)
		sd[scene] = nil
		return
	elseif sd[scene].enemy_id == nil then

		-- finish birth faster
		if sd[scene].scene_name == "birth" then
			EntityLoad(ComponentGetValue2(EntityGetFirstComponent(sd[scene].scene_id, "VariableStorageComponent"), "value_string"), scene_x, scene_y)
			GameEntityPlaySound(sd[scene].scene_id, "explosion")
			EntityLoad("data/entities/particles/particle_explosion/explosion_glue.xml", scene_x, scene_y)
		end

		enable_entity(sd[scene].kobold_id)
		EntityKill(sd[scene].scene_id)
		sd[scene] = nil
		return
	end
end

-- kobold in water
-- it is possible to keep scene afloat by modifying velocity but need to figure out how to make it look smooth
-- black hole gravity script should have necessary code
if ComponentGetValue2(EntityGetFirstComponent(sd[scene].kobold_id, "DamageModelComponent"), "mAirAreWeInWater") == true then

	-- enemy leaves
	if sd[scene].enemy_id ~= nil and sd[scene].current_stage < #sd[scene].stages then
		if EntityGetIsAlive(sd[scene].enemy_id) == true then
			enable_entity(sd[scene].enemy_id)
		end

		if sd[scene].second_enemy_id ~= nil and EntityGetIsAlive(sd[scene].second_enemy_id) == true then
			enable_entity(sd[scene].second_enemy_id)
		end
	end

	-- finish birth faster
	if sd[scene].scene_name == "birth" then
		EntityLoad(ComponentGetValue2(EntityGetFirstComponent(sd[scene].scene_id, "VariableStorageComponent"), "value_string"), scene_x, scene_y)
		GameEntityPlaySound(sd[scene].scene_id, "explosion")
		EntityLoad("data/entities/particles/particle_explosion/explosion_glue.xml", scene_x, scene_y)
	end

	enable_entity(sd[scene].kobold_id)
	EntityKill(sd[scene].scene_id)
	sd[scene] = nil
	return
end

-- enemy dead or ignited
if sd[scene].enemy_id ~= nil and sd[scene].current_stage < #sd[scene].stages then
	if EntityGetIsAlive(sd[scene].enemy_id) == false or sd[scene].second_enemy_id ~= nil and EntityGetIsAlive(sd[scene].second_enemy_id) == false then

		-- if enemy is polymorphed we need to enable it after polymorph ends
		-- no easy way to tell if dead or polymorphed so polymorph enabler spawns just in case
		local poly_handler = EntityCreateNew("ln_poly_enemy_enable")
		EntitySetTransform(poly_handler, scene_x, scene_y)
		EntityAddComponent2(poly_handler, "LuaComponent", {
			script_source_file="mods/lewd_noita/files/sex_logic/poly_enemy_enable.lua"
		})
		EntityAddComponent2(poly_handler, "LifetimeComponent", {
			lifetime=1800
		})

		-- if we have two enemies we need to enable remaining enemy so we check both
		if EntityGetIsAlive(sd[scene].enemy_id) == true then
			enable_entity(sd[scene].enemy_id)
		end

		if sd[scene].second_enemy_id ~= nil and EntityGetIsAlive(sd[scene].second_enemy_id) == true then
			enable_entity(sd[scene].second_enemy_id)
		end

		enable_entity(sd[scene].kobold_id)
		EntityKill(sd[scene].scene_id)
		sd[scene] = nil
		return
	end

	if ComponentGetValue2(EntityGetFirstComponent(sd[scene].enemy_id, "DamageModelComponent"), "is_on_fire") == true or
	sd[scene].second_enemy_id ~= nil and ComponentGetValue2(EntityGetFirstComponent(sd[scene].second_enemy_id, "DamageModelComponent"), "is_on_fire") == true then

		-- if we have two enemies we need to enable both when one of them is ignited
		if EntityGetIsAlive(sd[scene].enemy_id) == true then
			enable_entity(sd[scene].enemy_id)
		end

		if sd[scene].second_enemy_id ~= nil and EntityGetIsAlive(sd[scene].second_enemy_id) == true then
			enable_entity(sd[scene].second_enemy_id)
		end

		enable_entity(sd[scene].kobold_id)
		EntityKill(sd[scene].scene_id)
		sd[scene] = nil
		return
	end
end

-- kobold damaged on last stage
if ComponentGetValue2(EntityGetFirstComponent(sd[scene].kobold_id, "DamageModelComponent"), "mLastDamageFrame") == GameGetFrameNum() - 1 then
	if sd[scene].current_stage == #sd[scene].stages then
		enable_entity(sd[scene].kobold_id)
		EntityKill(sd[scene].scene_id)
		sd[scene] = nil
		return
	end

	-- finish birth faster
	if sd[scene].scene_name == "birth" then
		EntityLoad(ComponentGetValue2(EntityGetFirstComponent(sd[scene].scene_id, "VariableStorageComponent"), "value_string"), scene_x, scene_y)
		GameEntityPlaySound(sd[scene].scene_id, "explosion")
		EntityLoad("data/entities/particles/particle_explosion/explosion_glue.xml", scene_x, scene_y)

		enable_entity(sd[scene].kobold_id)
		EntityKill(sd[scene].scene_id)
		sd[scene] = nil
		return
	end
end

-- failed_alchemist_b flies away with you attached to its dick for some time before you fall down
if sd[scene].scene_follows_enemy == true then
	local alch_x, alch_y = EntityGetTransform(sd[scene].enemy_id)
	EntitySetTransform(sd[scene].scene_id, alch_x, alch_y, 0)
end

-- https://noita.wiki.gg/wiki/Modding:_Utilities#Rotate_entity_depending_on_topology_of_terrain
if EntityGetFirstComponent(sd[scene].scene_id, "VelocityComponent") ~= nil then
	local vel_x, vel_y = ComponentGetValue2(EntityGetFirstComponent(sd[scene].scene_id, "VelocityComponent"), "mVelocity")
	if vel_y == 0 then
		local found_normal, normal_x, normal_y, approximate_distance_from_surface = GetSurfaceNormal(scene_x, scene_y, 10, 16)

		if found_normal then
			EntitySetTransform(sd[scene].scene_id, scene_x, scene_y, math.rad(-90) + math.atan2(normal_y, normal_x))
		end
	end
end

-- prevent accumulating falling speed which is released all at once if flying enemy dies before scene ends
-- https://noita.wiki.gg/wiki/Modding:_Utilities#Player_movement_checking
if EntityGetFirstComponent(sd[scene].scene_id, "VelocityComponent") == nil then
	local vel_x, vel_y = ComponentGetValue2(EntityGetFirstComponent(sd[scene].kobold_id, "CharacterDataComponent"), "mVelocity")
	if (vel_y > 0 and vel_y ~= 60) then
		ComponentSetValue2(EntityGetFirstComponent(sd[scene].kobold_id, "CharacterDataComponent"), "mVelocity", 0, 0)
	end
end

-- disable spells that emit particles from wands if inventory slot switched this frame
if sd[scene].check_emitting_wands == true and
ComponentGetValue2(EntityGetFirstComponent(sd[scene].kobold_id, "Inventory2Component"), "mLastItemSwitchFrame") == GameGetFrameNum() then
	for _, ch in ipairs(EntityGetAllChildren(sd[scene].kobold_id)) do
		if EntityGetName(ch) == "inventory_quick" and EntityGetAllChildren(ch) ~= nil then
			for _, inv_ch in ipairs(EntityGetAllChildren(ch)) do
				if EntityHasTag(inv_ch, "wand") and EntityGetAllChildren(inv_ch) ~= nil then
					for _, card in ipairs(EntityGetAllChildren(inv_ch)) do
						if EntityGetFirstComponent(card, "ParticleEmitterComponent") ~= nil then
							EntitySetComponentIsEnabled(card, EntityGetFirstComponent(card, "ParticleEmitterComponent"), false)
						end
						if EntityGetFirstComponent(card, "SpriteParticleEmitterComponent") ~= nil then
							EntitySetComponentIsEnabled(card, EntityGetFirstComponent(card, "SpriteParticleEmitterComponent"), false)
						end
					end
				end
			end
		end
	end
end

-- execute every time animation loops (frame_count * frame_wait amount of frames passed)
-- loop takes 26 frames to play - execute on frame 26, 52, 78 and so on
-- ensures smooth transition to the next stage since it will only start after current animation is fully played
if (GameGetFrameNum() - sd[scene].stage_start_frame) % sd[scene].current_loop_duration == 0 then
	sd[scene].current_loop = sd[scene].current_loop + 1

	-- if this loop is first OR it is more than total amount of loops for current stage, which makes it first loop of next stage
	if sd[scene].current_loop == 1 or sd[scene].current_loop > sd[scene].stage_loops then
		sd[scene].current_stage = sd[scene].current_stage + 1
		sd[scene].current_loop = 1
		sd[scene].stage_start_frame = GameGetFrameNum()

		-- condition for scene end
		if sd[scene].current_stage > #sd[scene].stages then

			-- spawn enemies deleted at scene start
			if sd[scene].scene_name == "lurker" then
				EntityLoad("data/entities/animals/lurker.xml", scene_x + 10, scene_y)
				EntityLoad("data/entities/animals/lurker.xml", scene_x - 10, scene_y)
			elseif sd[scene].scene_name == "tentacle_vase_blue" then
				EntityLoad("mods/lewd_noita/files/enemies/tentacle_vase_blue_cum.xml", scene_x + 1, scene_y - 9)
			elseif sd[scene].scene_name == "tentacle_vase_pink" then
				EntityLoad("mods/lewd_noita/files/enemies/tentacle_vase_pink_cum.xml", scene_x + 1, scene_y - 9)
			end

			-- add sexfury perk effect or extend if already present
			if IsPlayer(sd[scene].kobold_id) == true and GameHasFlagRun("PERK_PICKED_LN_SEXFURY") == true then

				local sexfury_entity = nil

				for _, ch in ipairs(EntityGetAllChildren(sd[scene].kobold_id) or {}) do
					if EntityGetName(ch) == "ln_perk_effect_sexfury" then
						sexfury_entity = ch
						break
					end
				end

				if sexfury_entity == nil then
					sexfury_entity = EntityCreateNew("ln_perk_effect_sexfury")
					EntityAddComponent2(sexfury_entity, "InheritTransformComponent", {})
					EntityAddComponent2(sexfury_entity, "UIIconComponent", {
						icon_sprite_file="mods/lewd_noita/files/perks/sexfury_status.png",
						name="Butthurt",
						description="You're all out of bubblegum.",
						is_perk=false,
					})
					EntityAddComponent2(sexfury_entity, "GameEffectComponent", {
						effect="BERSERK",
						frames=3600,
					})
					EntityAddComponent2(sexfury_entity, "GameEffectComponent", {
						effect="MOVEMENT_FASTER_2X",
						frames=3600,
					})
					EntityAddComponent2(sexfury_entity, "GameEffectComponent", {
						effect="FASTER_LEVITATION",
						frames=3600,
					})
					EntityAddComponent2(sexfury_entity, "GameEffectComponent", {
						effect="MANA_REGENERATION",
						frames=3600,
					})
					EntityAddComponent2(sexfury_entity, "GameEffectComponent", {
						effect="KNOCKBACK_IMMUNITY",
						frames=3600,
					})
					EntityAddComponent2(sexfury_entity, "GameEffectComponent", {
						effect="STUN_PROTECTION_FREEZE",
						frames=3600,
					})
					EntityAddComponent2(sexfury_entity, "GameEffectComponent", {
						effect="STUN_PROTECTION_ELECTRICITY",
						frames=3600,
					})
					EntityAddChild(sd[scene].kobold_id, sexfury_entity)
				else
					for _, gec in ipairs(EntityGetComponent(sexfury_entity, "GameEffectComponent")) do
						ComponentSetValue2(gec, "frames", ComponentGetValue2(gec, "frames") + 3600)
					end
				end
			end

			enable_entity(sd[scene].kobold_id)
			EntityKill(sd[scene].scene_id)
			sd[scene] = nil
			return
		else
			-- change sprite animation
			ComponentSetValue2(EntityGetFirstComponent(sd[scene].scene_id, "SpriteComponent"), "rect_animation", sd[scene].stages[sd[scene].current_stage].attr.name)

			-- get amount of frames for next stage loop
			sd[scene].current_frame_count = tonumber(sd[scene].stages[sd[scene].current_stage].attr.frame_count)
			sd[scene].current_frame_wait = tonumber(sd[scene].stages[sd[scene].current_stage].attr.frame_wait) * 60
			sd[scene].current_loop_duration = math.floor(sd[scene].current_frame_count * sd[scene].current_frame_wait)

			-- last stage when kobold is left alone
			if sd[scene].current_stage == #sd[scene].stages then
				sd[scene].stage_loops = 4 + Random(0, 2)
				sd[scene].stage_sound = ""

				-- in case the scene was failed_alchemist_b, stop the scene from teleporting to its coordinates
				sd[scene].scene_follows_enemy = false

				if sd[scene].enemy_id ~= nil then
					enable_entity(sd[scene].enemy_id)

					if sd[scene].second_enemy_id ~= nil then
						enable_entity(sd[scene].second_enemy_id)
					end

					-- if kobold was fucked by flying enemy, she falls down when enemy stops fucking and leaves
					if EntityGetFirstComponent(sd[scene].scene_id, "VelocityComponent") == nil then
						EntityAddComponent2(sd[scene].scene_id, "VelocityComponent", {})
						EntityAddComponent2(sd[scene].scene_id, "SimplePhysicsComponent", {})
					end

					-- charm enemy and create circle of horny gas if charmer perk
					if IsPlayer(sd[scene].kobold_id) == true and GameHasFlagRun("PERK_PICKED_LN_CHARMER") == true then
						GetGameEffectLoadTo(sd[scene].enemy_id, "CHARM", false)
						local cloud = EntityCreateNew("ln_charmer_cloud")
						EntitySetTransform(cloud, scene_x, scene_y)
						EntityAddComponent2(cloud, "ParticleEmitterComponent", {
							emitted_material_name="ln_gas_horny_short",
							create_real_particles=true,
							image_animation_file="data/particles/image_emitters/circle_256.png",
							image_animation_speed=5,
							emission_interval_min_frames=1,
							emission_interval_max_frames=1,
							image_animation_loop=false,
						})
						EntityAddComponent2(cloud, "LifetimeComponent", {
							lifetime=60
						})
						EntityAddComponent2(cloud, "AudioComponent", {
							file="data/audio/Desktop/projectiles.bank",
							event_root="player_projectiles/circle_of",
						})
					end

					-- convert enemy to gold if gold digger perk
					if IsPlayer(sd[scene].kobold_id) == true and GameHasFlagRun("PERK_PICKED_LN_WHORE") == true then
						EntityConvertToMaterial(sd[scene].enemy_id, "gold")
						EntityInflictDamage(sd[scene].enemy_id, Random(1000, 10000), "NONE", "midas damage", "CONVERT_TO_MATERIAL", 0, 0, EntityGetWithTag("player_unit")[1])
					end

					-- increase max hp and kill enemy if succubus perk
					if IsPlayer(sd[scene].kobold_id) == true and GameHasFlagRun("PERK_PICKED_LN_SUCCUBUS") then
						local damage_model_comp = EntityGetFirstComponent(sd[scene].kobold_id, "DamageModelComponent")
						local max_hp = tonumber(ComponentGetValue2(damage_model_comp, "max_hp"))
						local max_hp_old = max_hp
						local max_hp_increase = math.min(ComponentGetValue2(EntityGetFirstComponent(sd[scene].enemy_id, "DamageModelComponent"), "hp"), 1)
						max_hp = max_hp + max_hp_increase

						ComponentSetValue2(damage_model_comp, "max_hp", max_hp)
						ComponentSetValue2(damage_model_comp, "max_hp_old", max_hp_old)
						ComponentSetValue2(damage_model_comp, "mLastMaxHpChangeFrame", GameGetFrameNum())

						GamePrint("Drained " .. math.floor(max_hp_increase * 25) .. " health from enemy!")
						EntityInflictDamage(sd[scene].enemy_id, Random(1000, 10000), "NONE", "succubus damage", "DISINTEGRATED", 0, 0, EntityGetWithTag("player_unit")[1])
					end

					-- chance to be glued by cum, sometimes glues you to enemy
					if Random(1, 5) == 1 then
						EntityLoad("data/entities/projectiles/glue.xml", scene_x, scene_y)
					end
				end

				if sd[scene].scene_name == "birth" then
					local entity_to_create = ComponentGetValue2(EntityGetFirstComponent(sd[scene].scene_id, "VariableStorageComponent"), "value_string")
					local child_enemy = EntityLoad(entity_to_create, scene_x, scene_y)

					-- card entity stays when picked up and charm particles keep emitting
					if entity_to_create ~= "data/entities/items/pickup/random_card.xml" then
						GetGameEffectLoadTo(child_enemy, "CHARM", false)
					end

					GameEntityPlaySound(sd[scene].scene_id, "explosion")
					EntityLoad("data/entities/particles/particle_explosion/explosion_glue.xml", scene_x, scene_y)
				elseif sd[scene].scene_name == "chest_mimic" then
					-- charm mimic to prevent infinite spell birth farm
					GetGameEffectLoadTo(sd[scene].enemy_id, "CHARM", false)
				elseif sd[scene].scene_name == "worm_tiny_head" then
					-- worm is killed when sex starts because otherwise it will keep damaging you with its jaws
					EntityLoad("data/entities/animals/worm_tiny.xml", scene_x, scene_y + 5)
				elseif sd[scene].scene_name == "miniblob" then
					for i = 1, Random(3, 6) do
						EntityLoad("data/entities/animals/miniblob.xml", scene_x, scene_y)
					end

					GameEntityPlaySound(sd[scene].scene_id, "explosion")
					EntityLoad("data/entities/particles/particle_explosion/explosion_glue.xml", scene_x, scene_y)
				elseif sd[scene].scene_name == "fungus" then
					-- continue fungus explosion we prevented when fungus started fucking
					ComponentSetValue2(EntityGetFirstComponentIncludingDisabled(sd[scene].enemy_id, "CollisionTriggerComponent"), "mTimer", Random(300, 600))
					EntitySetComponentIsEnabled(sd[scene].enemy_id, EntityGetFirstComponentIncludingDisabled(sd[scene].enemy_id, "CollisionTriggerComponent"), true)
					GameEntityPlaySound(sd[scene].enemy_id, "death_buildup")
					EntityAddComponent2(sd[scene].enemy_id, "ParticleEmitterComponent", {
						emitted_material_name="plasma_fading_pink",
						x_pos_offset_min=-4,
						y_pos_offset_min=-4,
						x_pos_offset_max=4,
						y_pos_offset_max=4,
						x_vel_min=-60,
						x_vel_max=60,
						y_vel_min=-120,
						y_vel_max=-40,
						count_min=90,
						count_max=150,
						lifetime_min=0.6,
						lifetime_max=3.8,
						create_real_particles=false,
						emit_cosmetic_particles=true,
						emission_interval_min_frames=10,
						emission_interval_max_frames=20,
						airflow_force=6,
						airflow_scale=1.0,
					})
				end

				-- add pregnancy
				if sd[scene].meta_preg ~= "none" then
					local pregnancy_entity = nil

					for _, ch in ipairs(EntityGetAllChildren(sd[scene].kobold_id) or {}) do
						if EntityGetName(ch) == "ln_pregnancy" then
							pregnancy_entity = ch
							break
						end
					end

					if pregnancy_entity == nil and Random(0, 1) == 1 then
						local pregnancy_entity = EntityCreateNew("ln_pregnancy")
						EntityAddComponent2(pregnancy_entity, "InheritTransformComponent", {})
						EntityAddComponent2(pregnancy_entity, "GameEffectComponent", {
							effect="CUSTOM",
							custom_effect_id="EFFECT_PREGNANCY",
						})
						EntityAddComponent2(pregnancy_entity, "UIIconComponent", {
							icon_sprite_file="mods/lewd_noita/files/status_effects/pregnant.png",
							name="Pregnant",
							description=ln_pregnancy_description(),
						})
						EntityAddComponent2(pregnancy_entity, "VariableStorageComponent", {
							value_string=sd[scene].meta_preg,
							value_int=GameGetFrameNum(),
						})
						EntityAddComponent2(pregnancy_entity, "LuaComponent", {
							script_source_file="mods/lewd_noita/files/sex_logic/start_birth.lua",
							execute_every_n_frame=Random(60, 90) * 60,
							remove_after_executed=true,
						})
						EntityAddChild(sd[scene].kobold_id, pregnancy_entity)
					end
				end

				-- apply sex effect
				if sd[scene].meta_effect ~= "none" then
					local sex_effect_entity = EntityLoad("mods/lewd_noita/files/status_effects/" .. sd[scene].meta_effect .. ".xml")
					ComponentSetValue2(EntityGetFirstComponent(sex_effect_entity, "GameEffectComponent"), "frames", Random(5, 10) * 60)
					EntityAddChild(sd[scene].kobold_id, sex_effect_entity)
				end

				-- parasite debuffs (only for player, and only if extreme content is enabled)
				local ln_extreme_enabled = ModSettingGet("lewd_noita.extreme_content_enabled")
				if ln_extreme_enabled == nil then ln_extreme_enabled = true end
				local UI_MSG = "mods/lewd_noita/files/sex_logic/ui_msg.png"
				if IsPlayer(sd[scene].kobold_id) and ln_extreme_enabled then
					local P_DIR = "mods/lewd_noita/files/status_effects/"
					local sn = sd[scene].scene_name
					local result = nil

					if sn == "bigfirebug" or sn == "firebug" or
					 sn == "maggot_2" or sn == "maggot_3" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_firebug", "mods/lewd_noita/files/status_effects/parasite_icon_firebug.png",
							"Tulikärpänen infestation",
							ln_parasite_icon_desc("parasite_firebug"),
							P_DIR .. "parasite_firebug.lua", 60)
						if result ~= nil then ln_print_infection("parasite_firebug", result, UI_MSG) end

					elseif sn == "fly" or sn == "flyswarm" or sn == "maggot_1" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_fly", "mods/lewd_noita/files/status_effects/parasite_icon_fly.png",
							"Lennokki colony",
							ln_parasite_icon_desc("parasite_fly"),
							P_DIR .. "parasite_fly.lua", 60)
						if result ~= nil then ln_print_infection("parasite_fly", result, UI_MSG) end

					elseif sn == "fungus" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_fungus", "mods/lewd_noita/files/status_effects/parasite_icon_fungus.png",
							"Sieni infection",
							ln_parasite_icon_desc("parasite_fungus"),
							P_DIR .. "parasite_fungus.lua", 120)
						if result ~= nil then ln_print_infection("parasite_fungus", result, UI_MSG) end

					elseif sn == "hornygasflower" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_hornygasflower", "mods/lewd_noita/files/status_effects/parasite_icon_flower.png",
							"Kukinta infection",
							ln_parasite_icon_desc("parasite_hornygasflower"),
							P_DIR .. "parasite_hornygasflower.lua", 60)
						if result ~= nil then ln_print_infection("parasite_hornygasflower", result, UI_MSG) end

					elseif sn == "webspider" or sn == "maggot_4" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_spider", "mods/lewd_noita/files/status_effects/parasite_icon_spider.png",
							"Hämähäkki brood",
							ln_parasite_icon_desc("parasite_spider"),
							P_DIR .. "parasite_spider.lua", 60)
						if result ~= nil then ln_print_infection("parasite_spider", result, UI_MSG) end

					elseif sn == "rat" or sn == "rat_alt" or sn == "rat_double" or
					 sn == "rat_alt_double" or sn == "skullrat" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_rat", "mods/lewd_noita/files/status_effects/parasite_icon_rat.png",
							"Rotta infestation",
							ln_parasite_icon_desc("parasite_rat"),
							P_DIR .. "parasite_rat.lua", 60)
						if result ~= nil then ln_print_infection("parasite_rat", result, UI_MSG) end

					elseif sn == "ant" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_ant", "mods/lewd_noita/files/status_effects/parasite_icon_ant.png",
							"Murkku colony",
							ln_parasite_icon_desc("parasite_ant"),
							P_DIR .. "parasite_ant.lua", 60)
						if result ~= nil then ln_print_infection("parasite_ant", result, UI_MSG) end

					elseif sn == "longleg" or sn == "longleg_alt" or sn == "longleg_double" then
						result = apply_parasite(sd[scene].kobold_id, "parasite_longleg", "mods/lewd_noita/files/status_effects/parasite_icon_longleg.png",
							"Hämis brood",
							ln_parasite_icon_desc("parasite_longleg"),
							P_DIR .. "parasite_longleg.lua", 60)
						if result ~= nil then ln_print_infection("parasite_longleg", result, UI_MSG) end
					end
				end
				-- Wolf bonding system (separate from parasites, same extreme content gate)
				if ln_extreme_enabled then
					dofile_once("mods/lewd_noita/files/status_effects/wolf_bonding.lua")
					local sn_wolf = sd[scene].scene_name
					if sn_wolf == "wolf" or sn_wolf == "wolf_cyber" or
					   sn_wolf == "wolf_double" or sn_wolf == "wolf_cyber_double" then
						if IsPlayer(sd[scene].kobold_id) then
							wolf_bond_increment(sd[scene].kobold_id, sd[scene].enemy_id)
						end
					end
				end
			elseif sd[scene].current_stage == sd[scene].cum_stage then
				sd[scene].stage_loops = 6 + Random(0, 3)
				sd[scene].stage_sound = "cum"
			elseif sd[scene].current_stage > 1 then
				sd[scene].stage_loops = 10 + Random(0, 10)
				sd[scene].stage_sound = "plap_hard"

				if sd[scene].scene_name == "failed_alchemist_b" and sd[scene].current_stage == 4 then
					sd[scene].stage_sound = ""
				end
			elseif sd[scene].current_stage == 1 then
				sd[scene].stage_loops = 20 + Random(0, 10)
				sd[scene].stage_sound = "plap_medium"
			end

			-- allow failed_alchemist_b to fly away with kobold attached to its dick
			if sd[scene].scene_name == "failed_alchemist_b" and sd[scene].current_stage == 4 then
				if sd[scene].scene_name == "failed_alchemist_b" then
					EntityRemoveFromParent(sd[scene].enemy_id)
					EntitySetComponentIsEnabled(sd[scene].enemy_id, EntityGetFirstComponentIncludingDisabled(sd[scene].enemy_id, "ControlsComponent"), true)
					EntityRemoveComponent(sd[scene].scene_id, EntityGetFirstComponent(sd[scene].scene_id, "VelocityComponent"))
					EntityRemoveComponent(sd[scene].scene_id, EntityGetFirstComponent(sd[scene].scene_id, "SimplePhysicsComponent"))
					sd[scene].scene_follows_enemy = true
				end
			end

			-- for stages that play only once instead of looping
			if sd[scene].stages[sd[scene].current_stage].attr.loop == "0" then
				sd[scene].stage_loops = 1
				sd[scene].stage_sound = ""
			end

		end
	end

	-- check for double penetration scene if second enemy approaches
	if sd[scene].current_stage < #sd[scene].stages and sd[scene].check_double == true then
		local close_enemies = EntityGetInRadiusWithTag(scene_x, scene_y, 10, "enemy")

		if #close_enemies > 1 then

			-- get scene name without "_alt" to check it against enemy sprite name
			-- because enemy sprite name obviously won't have "_alt" at the end
			local name_minus_alt = sd[scene].scene_name

			if string.find(sd[scene].scene_name, "_alt") ~= nil then
				name_minus_alt = string.sub(sd[scene].scene_name, 1, -5)
			end

			for _, enemy in ipairs(close_enemies) do
				local enemy_name = ComponentGetValue2(EntityGetFirstComponent(enemy, "SpriteComponent"), "image_file"):match("([^/]+)%.xml$")

				-- EntityGetParent check to make sure we don't get the very enemy that is already fucking us
				if enemy_name == name_minus_alt and EntityGetParent(enemy) == 0 then
					sd[scene].second_enemy_id = enemy

					-- if current scene has "_alt" but no "_alt_double" for that scene exists, remove "_alt" to get default "_double" instead
					if ModTextFileGetContent("mods/lewd_noita/files/sex_gfx/" .. sd[scene].scene_name .. "_double" .. ".xml") == nil then
						sd[scene].scene_name = string.sub(sd[scene].scene_name, 1, -5)
					end

					-- restart scene with "_double" added to scene name
					sd[scene].scene_name = sd[scene].scene_name .. "_double"

					sd[scene].sprite_metadata = ModTextFileGetContent("mods/lewd_noita/files/sex_gfx/" .. sd[scene].scene_name .. ".xml")
					sd[scene].stages = nxml.parse(sd[scene].sprite_metadata):all_of("RectAnimation")
					sd[scene].current_frame_count = tonumber(sd[scene].stages[1].attr.frame_count)

					-- frame_wait * 60 to convert seconds to frames
					sd[scene].current_frame_wait = tonumber(sd[scene].stages[1].attr.frame_wait) * 60
					sd[scene].current_loop_duration = math.floor(sd[scene].current_frame_count * sd[scene].current_frame_wait)

					sd[scene].current_stage = 0
					sd[scene].current_loop = 0
					EntityRemoveComponent(sd[scene].scene_id, EntityGetFirstComponent(sd[scene].scene_id, "SpriteComponent"))
					EntityAddComponent2(sd[scene].scene_id, "SpriteComponent", {
						image_file="mods/lewd_noita/files/sex_gfx/" .. sd[scene].scene_name .. ".xml",
						rect_animation=sd[scene].stages[sd[scene].current_stage + 1].attr.name,
						z_index=-2,
					})

					EntityAddChild(sd[scene].scene_id, sd[scene].second_enemy_id)
					disable_entity(sd[scene].second_enemy_id)

					if IsPlayer(sd[scene].kobold_id) == true then
						GamePrint("Another " .. sd[scene].translated_enemy_name .. " joined the party!")
					end

					break
				end
			end
		end
	end
elseif (GameGetFrameNum() - (sd[scene].stage_start_frame + math.floor(sd[scene].current_loop_duration / 2))) % sd[scene].current_loop_duration == 0 then
	-- play plap/cum sound in the middle of animation instead of very first frame
	-- the rest of the script is here to sync it with sounds for better immersion
	GameEntityPlaySound(sd[scene].scene_id, sd[scene].stage_sound)

	if sd[scene].current_stage == sd[scene].cum_stage and sd[scene].meta_cum ~= "none" then
		sd[scene].cumshot_amount = Random(25, 35)
		GameCreateParticle(sd[scene].meta_cum, scene_x, scene_y, math.floor(sd[scene].cumshot_amount / 2), 5, -10, false, false, true)

		if sd[scene].enemy_id ~= nil then

			-- add cum leaking
			local cum_leaking_entity = nil

			for _, ch in ipairs(EntityGetAllChildren(sd[scene].kobold_id) or {}) do
				if EntityGetName(ch) == "ln_cum_leaking" then
					cum_leaking_entity = ch
					break
				end
			end

			if cum_leaking_entity == nil then
				cum_leaking_entity = EntityCreateNew("ln_cum_leaking")
				EntityAddComponent2(cum_leaking_entity, "InheritTransformComponent", {})
				EntityAddComponent2(cum_leaking_entity, "MaterialInventoryComponent", {})
				EntityAddComponent2(cum_leaking_entity, "LuaComponent", {
					script_source_file="mods/lewd_noita/files/sex_logic/cum_leaking.lua",
				})
				EntityAddChild(sd[scene].kobold_id, cum_leaking_entity)
			end

			local cum_total = 0

			for mat_id, mat_amount in ipairs(ComponentGetValue2(EntityGetFirstComponent(cum_leaking_entity, "MaterialInventoryComponent"), "count_per_material_type")) do
				if mat_id - 1 == CellFactory_GetType(sd[scene].meta_cum) then
					AddMaterialInventoryMaterial(cum_leaking_entity, sd[scene].meta_cum, mat_amount + sd[scene].cumshot_amount)
					cum_total = cum_total + sd[scene].cumshot_amount
				end

				if mat_amount ~= nil and mat_amount > 0 then
					cum_total = cum_total + mat_amount
				end
			end
			
			-- Deposit cum into womb MIC for tracking/UI
			if IsPlayer(sd[scene].kobold_id) then
				local womb = getWombEntity(sd[scene].kobold_id)
				if womb ~= nil then
					addWombMaterial(womb, sd[scene].meta_cum, sd[scene].cumshot_amount)
				end
			end

			-- damage if too much cum
			if cum_total > 1500 then
				local damage = ComponentGetValue2(EntityGetFirstComponent(sd[scene].kobold_id, "DamageModelComponent"), "hp") / 100  * sd[scene].cumshot_amount / 3
				EntityInflictDamage(sd[scene].kobold_id, damage, "NONE", "cum overflow", "NORMAL", 0, 0, sd[scene].kobold_id)
			end

			-- explode if way too much cum
			if cum_total > 2000 then
				EntityInflictDamage(sd[scene].kobold_id, 100, "NONE", "cum overflow", "NORMAL", 0, 0, sd[scene].kobold_id)

				-- EntityInflictDamage kills kobold before explosion can happen, hence separate entity instead of adding ExplosionComponent to kobold
				local cum_explosion = EntityCreateNew("ln_cum_explosion")
				EntitySetTransform(cum_explosion, scene_x, scene_y)
				local exp_comp = EntityAddComponent2(cum_explosion, "ExplosionComponent", {
					trigger="ON_TIMER",
				})
				ComponentObjectSetValue2(exp_comp, "config_explosion", "explosion_sprite", "data/particles/explosion_025_fuel.xml")
				ComponentObjectSetValue2(exp_comp, "config_explosion", "load_this_entity", "data/entities/particles/particle_explosion/main_pink.xml")
				ComponentObjectSetValue2(exp_comp, "config_explosion", "audio_event_name", "explosions/liquid")
				ComponentObjectSetValue2(exp_comp, "config_explosion", "spark_material", "ln_cum")
				ComponentObjectSetValue2(exp_comp, "config_explosion", "sparks_count_min", 100)
				ComponentObjectSetValue2(exp_comp, "config_explosion", "sparks_count_max", 200)
				ComponentObjectSetValue2(exp_comp, "config_explosion", "ray_energy", 6000000)
				ComponentObjectSetValue2(exp_comp, "config_explosion", "max_durability_to_destroy", 12)
				ComponentObjectSetValue2(exp_comp, "config_explosion", "create_cell_material", "ln_cum")

				EntityKill(cum_leaking_entity)
			end
		end
	end
end