-- ugh ..... ugmhgg .... it appreas it is me azuline again .....
-- this elite is gay af.... just like me ... so it was only appropriate that i would do it ....

local SPRITE_PATH = path.combine(PATH, "Sprites/Elites/Empyrean")
local SOUND_PATH = path.combine(PATH, "Sounds/Elites/Empyrean")

local sprite_icon = Resources.sprite_load(NAMESPACE, "EliteIconEmpyrean", path.combine(SPRITE_PATH, "icon.png"), 1, 25, 22)
local splash_sprite = Resources.sprite_load(NAMESPACE, "ParticleEmpyreanSpawnSplash", path.combine(SPRITE_PATH, "splash.png"), 6)
local beam_sprite = Resources.sprite_load(NAMESPACE, "ParticleEmpyreanSpawnBeam", path.combine(SPRITE_PATH, "beam.png"), 11)
local star_sprite = Resources.sprite_load(NAMESPACE, "EmpyreanWormStar", path.combine(SPRITE_PATH, "star.png"), 8, 16, 16)

local gotanythingsharp = Resources.sfx_load(NAMESPACE, "EmpyreanSpawn", path.combine(SOUND_PATH, "beam.ogg"))
local sound_spawn = Resources.sfx_load(NAMESPACE, "EmpyreanSpawnShort", path.combine(SOUND_PATH, "spawn.ogg"))
local sound_spawn_alt = Resources.sfx_load(NAMESPACE, "EmpyreanSpawnShortAlt", path.combine(SOUND_PATH, "spawn_alt.ogg"))
local sound_spawn_worm = Resources.sfx_load(NAMESPACE, "EmpyreanSpawnWorm", path.combine(SOUND_PATH, "spawn_worm.ogg"))
local sound_star_spawn = Resources.sfx_load(NAMESPACE, "EmpyreanStarSpawn", path.combine(SOUND_PATH, "star_spawn.ogg"))
local sound_star_shoot = Resources.sfx_load(NAMESPACE, "EmpyreanStarShoot", path.combine(SOUND_PATH, "star_shoot.ogg"))
local sound_teleport1 = Resources.sfx_load(NAMESPACE, "EmpyreanTeleport1", path.combine(SOUND_PATH, "teleport1.ogg"))
local sound_teleport2 = Resources.sfx_load(NAMESPACE, "EmpyreanTeleport2", path.combine(SOUND_PATH, "teleport2.ogg"))
local sound_teleport3 = Resources.sfx_load(NAMESPACE, "EmpyreanTeleport3", path.combine(SOUND_PATH, "teleport3.ogg"))

local empy = Elite.new(NAMESPACE, "empyrean")
empy.healthbar_icon = sprite_icon
empy.palette = gm.constants.sElitePaletteDummy
empy.blend_col = Color.WHITE

GM.elite_generate_palettes()

local evil = Particle.new(NAMESPACE, "EmpyreanSpawnEvil")
evil:set_sprite(splash_sprite, true, true, false)
evil:set_life(15, 30)

local beam = Particle.new(NAMESPACE, "EmpyreanSpawnBeam")
beam:set_direction(270, 270, 0, 0)
beam:set_speed(8, 8, 0, 0)
beam:set_sprite(beam_sprite, true, true, false)
beam:set_life(10, 10)

local splash = Particle.new(NAMESPACE, "EmpyreanSpawnSplash")
splash:set_sprite(splash_sprite, true, true, false)
splash:set_scale(1.5, 1.5)
splash:set_life(15, 30)

local rainbowspark = Particle.new(NAMESPACE, "EmpyreanRainbowSpark")
rainbowspark:set_shape(Particle.SHAPE.line)
rainbowspark:set_blend(true)
rainbowspark:set_alpha3(1, 1, 0)
rainbowspark:set_size(0, 1, 0, 0.01)
rainbowspark:set_orientation(0, 0, 0, 0, true)
rainbowspark:set_speed(0, 4, -0.04, 0.2)
rainbowspark:set_direction(0, 180, 0, 10)
rainbowspark:set_scale(0.1, 0.1)
rainbowspark:set_life(20, 100)

local teleport = Particle.new(NAMESPACE, "EmpyreanTeleport")
teleport:set_shape(Particle.SHAPE.star)
teleport:set_alpha3(0.75, 0.75, 0)
teleport:set_color2(Color.WHITE, Color.WHITE)
teleport:set_orientation(0, 0, 0, 0, true)
teleport:set_speed(0, 3, -0.03, 0.05)
teleport:set_direction(0, 360, 0, 10)
teleport:set_scale(0.1, 0.1)
teleport:set_life(20, 100)

local telegraph = Particle.new(NAMESPACE, "EmpyreanStarTelegraph")
telegraph:set_shape(Particle.SHAPE.disk)
telegraph:set_alpha2(0.6, 0)
telegraph:set_blend(true)
telegraph:set_speed(6, 6, 0, 0)
telegraph:set_scale(0.1, 0.1)
telegraph:set_life(100, 100)

local empyorb = Item.new(NAMESPACE, "eliteOrbEmpyrean", true)
empyorb.is_hidden = true

local teleorb = Item.new(NAMESPACE, "elitePassiveTeleportEmpyrean", true)
teleorb.is_hidden = true

-- PUT ASPECTS HERE --
-- format is {"namespace-identifier", stack}
local aspects = {
	-- VANILLA --
	
	-- volatile
	{"ror-eliteOrbExplosiveShot", 2},
	{"ror-elitePassiveVolatile", 1},
	
	-- overloading
	{"ror-eliteOrbLightning", 1},
	{"ror-elitePassiveOverloading", 1},
	
	-- leeching
	{"ror-elitePassiveLeeching", 1},
	{"ror-eliteOrbLifesteal", 7},
	
	-- frenzied (minus their teleporting orb)
	{"ror-eliteOrbAttackSpeed", 3},
	{"ror-eliteOrbMoveSpeed", 5},
	
	-- blazing
	{"ror-eliteOrbFireTrail", 5},
	
	
	
	-- STARSTORM --
	
	-- poison
	{"ssr-eliteOrbPoison", 1}
}
function ssr_give_empyrean_aspects(actor)
	for _, aspect in ipairs(aspects) do
		local item = Item.find(aspect[1])
		
		-- if the elite already has these orbs, remove them
		if actor:item_stack_count(item) > 0 then
			actor:item_remove(item, actor:item_stack_count(item))
		end
 		
		-- give the orbs
		actor:item_give(item, aspect[2])
	end
	
	for id, stack in ipairs(actor.inventory_item_stack) do
		if stack > 0 then
			local item = Item.wrap(id - 1) -- subtract by one cuz lua tables start at 1 instead of 0
		end
	end
end

empy:clear_callbacks()
empy:onApply(function(actor)
	actor:item_give(empyorb) -- applies most empyrean effects
	actor:item_give(teleorb) -- makes it teleport to the player from any location
	
	-- stat changes are multiplicative with normal elite stat changes
	-- so max hp for example will be base * 12 * 2.8 = 33.6x total multiplier
	
	-- giga health
	actor.maxhp_base = actor.maxhp_base * 12
	actor.hp = actor.maxhp
	
	-- giga gold and exp
	if actor.exp_worth then
		actor.exp_worth = actor.exp_worth * 30 -- totals to 60x
	end
	
	-- immune to stun, knockback and fall damage
	actor.knockback_immune = true
	actor.stun_immune = true
	actor.fall_immune = true
	
	-- make maxhp_base modification take effect
	GM.actor_queue_dirty(actor) 
end)

empyorb:clear_callbacks()
empyorb:onAcquire(function(actor, stack)
	-- move the actor to the ground
	actor:move_contact_solid(270, -1)
	
	-- apply screenshake
	actor:screen_shake(4)
	
	-- play the spawn sound
	
	-- start the beam
	if gm.inside_view(actor.x, actor.y) == 1 and actor.team ~= 1 then
		actor:get_data().empy_beam = 180
		actor:get_data().empy_beam_over = 0
		actor:sound_play(gotanythingsharp, 2, 1)
	elseif gm.inside_view(actor.x, actor.y) ~= 1 or actor.team == 1 then
		actor:get_data().empy_beam = 0
		actor:get_data().empy_beam_over = 1
		actor:get_data().no_beam_loser = 5
		
		if Helper.chance(0.5) then
			actor:sound_play(sound_spawn, 2, 1)
		else
			actor:sound_play(sound_spawn_alt, 2, 1)
		end
		
		-- give them all elite aspects
		ssr_give_empyrean_aspects(actor)
	end
	
	-- remove the passive teleport orb since empyreans have a custom one
	if actor:item_stack_count(Item.find("ror-elitePassiveTeleport")) > 0 then 
		actor:item_remove(Item.find("ror-elitePassiveTeleport"), actor:item_stack_count(Item.find("ror-elitePassiveTeleport")))
	end
end)

empyorb:onPostDraw(function(actor, stack)
	if actor:get_data().empy_beam and actor:get_data().empy_beam_over then
		if actor:get_data().empy_beam > 0 and actor:get_data().empy_beam_over == 0 then
			local width = gm.sprite_get_width(actor.mask_index) / 2 + 32
			local part_width = gm.round((((actor.x - actor.bbox_left) + (actor.bbox_right - actor.x)) / 2) * 1.5)
			local part_height = gm.round(((actor.y - actor.bbox_top) + (actor.bbox_bottom - actor.y)) / 2)
			local silhouette_y = actor.y - 16 - (16 * math.sin(gm.degtorad(270 + actor:get_data().empy_beam * 2)))

			if actor:get_data().empy_beam <= 10 then
				width = width * (1000 - (10 - actor:get_data().empy_beam) ^ 3) / 1000 -- make the beam shrink when its lifetime ends
			elseif actor:get_data().empy_beam > 168 then
				width = width * ((180 - actor:get_data().empy_beam) ^ 3) / 1000 -- make the beam widen when it appears
			elseif actor:get_data().empy_beam > 165 then
				width = width * (1.3 - ((169 - actor:get_data().empy_beam) / 10)) -- make the beam shrink back to its normal size after it widens
			else
				if math.random() >= 0.5 then
					-- create the black particles around the enemy
					evil:create_color(actor.x + math.random(-part_width, part_width), silhouette_y - part_height / 2 - math.random(part_height), Color.BLACK, 1, Particle.SYSTEM.middle)
					
					-- create the beam splashing particles
					local side = 1
					if math.random() >= 0.5 then
						side = -1
					end
					local ori = 180 + math.random(-45, 45) - 45 * side
					splash:set_orientation(ori, ori, 0, 0, false)
					splash:create_color(actor.x + (width + math.random(3)) * side, actor.bbox_bottom, Color.from_hsv(Global._current_frame % 360, 100, 100), 1, Particle.SYSTEM.middle)
				end
			end
			
			if actor:get_data().empy_beam > 15 and actor:get_data().empy_beam <= 170 then
				-- create the beam particles that touch the ground
				beam:create_color(actor.x - width - math.random(3), actor.bbox_bottom - 88, Color.from_hsv(Global._current_frame % 360, 100, 100), 1, Particle.SYSTEM.middle)
				beam:create_color(actor.x + width + math.random(3), actor.bbox_bottom - 88, Color.from_hsv(Global._current_frame % 360, 100, 100), 1, Particle.SYSTEM.middle)
				
				-- create the beam particles around the beam
				for i = 1, 22 do
					local rnd = math.random(80, 1728)
					if gm.inside_view(actor.x, actor.y - rnd) == 1 then
						beam:create_color(actor.x - width - math.random(3), actor.bbox_bottom - math.random(88, 1152), Color.from_hsv(Global._current_frame % 360, 100, 100), 1, Particle.SYSTEM.middle)
						beam:create_color(actor.x + width + math.random(3), actor.bbox_bottom - math.random(88, 1152), Color.from_hsv(Global._current_frame % 360, 100, 100), 1, Particle.SYSTEM.middle)
					end
				end
			end
			
			-- draw the beam
			if actor:get_data()._imalpha then
				gm.draw_set_alpha(actor:get_data()._imalpha)
			else
				gm.draw_set_alpha(1)
			end
			gm.draw_set_color(Color.WHITE)
			gm.draw_rectangle(actor.x - width, 0, actor.x + width, actor.bbox_bottom, false)
			
			-- make it black and move
			gm.draw_sprite_ext(actor.sprite_index, actor.image_index, actor.x, silhouette_y, actor.image_xscale, actor.image_yscale, actor.image_angle, Color.BLACK, math.min(1, ((180 - actor:get_data().empy_beam) ^ 3) / 1000))
			gm.draw_set_alpha(1)
		else
			-- make it rainbow
			if actor.object_index ~= gm.constants.oWorm then
				gm.draw_sprite_ext(actor.sprite_index, actor.image_index, actor.x, actor.y, actor.image_xscale, actor.image_yscale, actor.image_angle, Color.from_hsv(Global._current_frame % 360, 100, 100), actor.image_alpha)
			end
			
			actor.hud_health_color = Color.WHITE
		end
	end
end)

-- dont draw the healthbar while the beam is there
gm.pre_script_hook(gm.constants.draw_hp_bar, function(self, other, result, args)
	if not Wrap.wrap(self):get_data().empy_beam then return end
	
	if Wrap.wrap(self):get_data().empy_beam then
		if Wrap.wrap(self):get_data().empy_beam > 0 then
			return false
		end
	end
end)

local guarded = false

-- disable fall damage for the elite to prevent cheese
gm.pre_script_hook(gm.constants.actor_phy_on_landed, function(self, other, result, args)
	if not Instance.wrap(self).fall_immune then return end
	
    local real_self = Instance.wrap(self)
    if not gm.bool(self.invincible) and real_self.fall_immune == true then
        self.invincible = 1
        guarded = true
    end
end)

gm.post_script_hook(gm.constants.actor_phy_on_landed, function(self, other, result, args)
    if guarded then
        self.invincible = 0
        guarded = false
    end
end)

empyorb:onPostStep(function(actor, stack)
	if actor:get_data().empy_beam > 0 and actor:get_data().empy_beam_over == 0 then -- freeze the enemy
		-- store the enemy's certain stats so we can restore it later
		if not actor:get_data()._hmax then
			actor:get_data()._hmax = actor.pHmax
			actor:get_data()._vmax = actor.pVmax
			actor:get_data()._imspeed = actor.image_speed
			actor:get_data()._imalpha = actor.image_alpha
			actor:get_data()._intang = actor.intangible
		end
		
		-- make it not move
		actor.image_speed = 0.25
		actor.image_alpha = 0
		actor.pHmax = 0
		actor.pVmax = 0
		actor.intangible = true
		actor.activity = 50
		actor.__activity_handler_state = 50
		actor.state = 0
		
		-- smoother transition
		if actor:get_data()._imalpha then
			actor.image_alpha = actor:get_data()._imalpha * (10 - actor:get_data().empy_beam) / 10
		else
			actor.image_alpha = (10 - actor:get_data().empy_beam) / 10
		end
		
		-- (duke nukem voice) shake it baby
		actor:screen_shake(0.5)
		
		-- make it look like its floating
		if actor.sprite_fall then
			actor.sprite_index = actor.sprite_fall
		elseif actor.sprite_idle then
			actor.sprite_index = actor.sprite_idle
		end
		
		actor:get_data().empy_beam = actor:get_data().empy_beam - 1
	elseif actor:get_data().empy_beam_over == 0 and actor:get_data().empy_beam <= 0 then -- unfreeze the enemy
		actor:get_data().empy_beam_over = 1
		
		-- restore their speed
		actor.pHmax = actor:get_data()._hmax
		actor.pVmax = actor:get_data()._vmax
		actor.image_speed = actor:get_data()._imspeed
		actor.image_alpha = actor:get_data()._imalpha
		actor.intangible = actor:get_data()._intang
		
		--remove all the temporary variables
		actor:get_data()._hmax = nil
		actor:get_data()._vmax = nil
		actor:get_data()._imspeed = nil
		actor:get_data()._imalpha = nil
		actor:get_data()._intang = nil
		
		-- give them all elite aspects
		ssr_give_empyrean_aspects(actor)
		
		-- make the boss bar appear
		if actor.team ~= 1 and GM._mod_net_isHost() then
			local arr = Array.new({actor})
			local party = actor:actor_create_enemy_party_from_ids(arr)
			local director = gm._mod_game_getDirector()
			gm.call("register_boss_party@gml_Object_oDirectorControl_Create_0", director, director, party)
		end
		
		-- make them move again !! yippie!!
		actor.state = 1
		actor:skill_util_reset_activity_state()
	end
	
	if actor:get_data().no_beam_loser and gm._mod_net_isHost() then
		if actor:get_data().no_beam_loser > 0 then -- wait 5 frames before adding empyreans that didnt play the animation to the boss party (prevents issues with max hp being fucked up on the boss bar)
			actor:get_data().no_beam_loser = actor:get_data().no_beam_loser - 1
		else
			actor:get_data().no_beam_loser = nil
			if actor.team ~= 1 then -- make the boss bar appear
				local arr = Array.new({actor})
				local party = actor:actor_create_enemy_party_from_ids(arr)
				local director = gm._mod_game_getDirector()
				gm.call("register_boss_party@gml_Object_oDirectorControl_Create_0", director, director, party)
			end
		end
	end
	
	if gm.inside_view(actor.x, actor.y) == 1 and actor:get_data().empy_beam_over == 1 and actor:get_data().empy_beam <= 0 and Global._current_frame % 2 == 0 then
		rainbowspark:create_color(actor.x, actor.y, Color.from_hsv((Global._current_frame + actor.y * (0.75)) % 360, 100, 100), 1, Particle.SYSTEM.below)
	end
	
	if actor.object_index == gm.constants.oWorm or actor.object_index == gm.constants.oJellyG or actor.object_index == gm.constants.oJellyG2 then
		actor.image_blend = Color.from_hsv(Global._current_frame % 360, 65, 100)
	end
end)

empyorb:onPostStatRecalc(function(actor)
	-- giga damage
	if actor.elite_type then
		if actor.elite_type == empy.value then
			actor.damage = actor.damage * (5.4 / 1.9) -- totals to 5.4x
			actor.cdr = actor.cdr * (0.5 / 0.3) -- totals to 50%
		end
	end
end)

teleorb:clear_callbacks()
teleorb:onAcquire(function(actor, stack)
	actor:get_data().empyrean_teleport = 480 + math.random(240)
end)

teleorb:onPostStep(function(actor, stack)
	if GM.actor_is_classic(actor) then
		if actor:get_data().empyrean_teleport > 0 then
			actor:get_data().empyrean_teleport = actor:get_data().empyrean_teleport - 1
		elseif not gm.actor_state_is_climb_state(actor.actor_state_current_id) then
			local targets = Instance.find_all(gm.constants.oP)
			local target_fin = nil
			
			-- go through all players and find one that is grounded and not climbing
			for _, target in ipairs(targets) do
				if not gm.bool(target.free) and not gm.actor_state_is_climb_state(target.actor_state_current_id) and gm.point_distance(actor.x, actor.y, target.x, target.y) > 200 and not target.dead then
					target_fin = target
					break
				end
			end
			
			-- disable skills for a second to prevent unfair deaths
			if gm._mod_net_isHost() then
				for i = 0, 3 do
					local skill = actor:get_active_skill(i)
					skill.use_next_frame = math.max(skill.use_next_frame, Global._current_frame + 60)
				end
			end
			
			-- teleport!
			if target_fin then
				if gm._mod_net_isHost() then
					gm.teleport_nearby(actor.id, target_fin.x - (80 + math.random(20)) * gm.sign(target_fin.pHspeed), target_fin.y) -- teleport to this fool
					actor:net_send_instance_message(25)
					
					actor.ghost_x = actor.x
					actor.ghost_y = actor.y
					actor.pVspeed = 0
					actor.pHspeed = 0
					actor.ai_tick_rate = 1
				end
				
				actor:get_data().empyrean_teleport = 480 + math.random(240)
				
				-- create some effects so you know youre about to die
				local circle = GM.instance_create(actor.x, actor.y, gm.constants.oEfCircle)
				circle.parent = actor
				circle.radius = gm.round((((actor.x - actor.bbox_left) + (actor.bbox_right - actor.x) + (actor.y - actor.bbox_top) + (actor.bbox_bottom - actor.y)) / 4) * 1.5 )
				
				local flash = GM.instance_create(actor.x, actor.y, gm.constants.oEfFlash)
				flash.parent = actor
				flash.rate = 0.02
				flash.image_alpha = 1
				
				for i = 1, 36 do
					teleport:set_direction(10 * i, 10 * i, 0, 10)
					teleport:set_color2(Color.WHITE, gm.round(math.random(360)))
					teleport:create(actor.x, actor.y, 1, Particle.SYSTEM.middle)
				end
				
				-- play one of these 3 sounds randomly
				local tpsound = math.random()
				if tpsound >= 0.67 then
					actor:sound_play(sound_teleport1, 1, 0.8 + math.random() * 0.2) 
				elseif tpsound >= 0.33 then
					actor:sound_play(sound_teleport2, 1, 0.8 + math.random() * 0.2)
				else
					actor:sound_play(sound_teleport3, 1, 0.8 + math.random() * 0.2)
				end
			end
		end
	end
end)

-- empyrean worms !!!
-- make the worm bodies rainbow too and also make them create sparks
gm.pre_code_execute("gml_Object_oWormBody_Step_2", function(self, other)
	if self.parent.elite_type ~= empy.value then return end
	
	if self.parent.elite_type == empy.value then
		self.image_blend = Color.from_hsv((Global._current_frame + (self.m_id - self.parent.m_id) * 18) % 360, 65, 100)
		
		if gm.inside_view(self.x, self.y) == 1 and Global._current_frame % 3 == 0 then
			rainbowspark:create_color(self.x, self.y, Color.from_hsv((Global._current_frame + self.y * (0.75)) % 360, 100, 100), 1, Particle.SYSTEM.middle)
		end
	end
end)

-- make the worm bodies set their first alarm (needed to shoot the orbs)
gm.pre_code_execute("gml_Object_oWorm_Alarm_4", function(self, other)
	if self.elite_type ~= empy.value then return end
	
	if self.elite_type == empy.value then
		for _, segment in ipairs(self.body) do
			segment:alarm_set(1, 150 + (segment.m_id - self.m_id) * 2)
		end
	end
end)

-- play the spawn sound
gm.pre_code_execute("gml_Object_oWorm_Alarm_3", function(self, other)
	if self.elite_type ~= empy.value then return end
	
	if self.elite_type == empy.value then
		gm.sound_play_global(sound_spawn_worm, 1, 1)
	end
end)

-- special empyrean worm projectile
local oStarStorm = Object.new(NAMESPACE, "EmpyreanWormStar")
oStarStorm.obj_sprite = star_sprite
oStarStorm:clear_callbacks()

oStarStorm:onCreate(function(self)
	self.life = 600
	self.damage = 1
	self.targetX = 0
	self.targetY = 0
	self.team = 2
	self.image_speed = 0.4
	self.image_alpha = 0.75
	self.direction = 0
	self.speed = 0
	
	self:sound_play(sound_star_spawn, 0.5, 0.8 + math.random() * 0.2)
end)

oStarStorm:onDraw(function(self)
	gm.draw_set_alpha(0.4)
	gm.draw_circle_colour(self.x, self.y, 20, self.image_blend, self.image_blend, false)
	gm.draw_set_alpha(1)
end)

oStarStorm:onStep(function(self)
	if self.life > 0 then
		self.life = self.life - 1
	else
		self:destroy()
	end
	
	if not self.spawn_speed then
		self.spawn_speed = self.speed
	end
	
	if self.life >= 570 then -- do this for the first 30 frames after spawning
		self.speed = self.speed - self.spawn_speed / 30
	elseif self.life == 481 then -- make it idle for a second and a half, then >>
		local flash = GM.instance_create(self.x, self.y, gm.constants.oEfFlash) -- >> make it flash
		flash.parent = self
		flash.rate = 0.03
		flash.image_alpha = 0.6
		
		self:sound_play(sound_star_shoot, 0.5, 0.8 + math.random() * 0.2) -- >> play this sound
		
		self.direction = gm.point_direction(self.x, self.y, self.targetX, self.targetY) -- >> make it aim at the player
	elseif self.life >= 480 then -- while idling, create telegraph particles
		if self.life % 10 == 0 then
			telegraph:set_direction(gm.point_direction(self.x, self.y, self.targetX, self.targetY), gm.point_direction(self.x, self.y, self.targetX, self.targetY), 0, 0)
			telegraph:create_color(self.x, self.y, self.image_blend, 1)
		end
	elseif self.life >= 470 then -- once were done idling, descrease its speed to create a wind up effect for 10 frames
		self.speed = self.speed - 0.4
	else
		self.speed = math.min(30, self.speed + 0.6) -- then start increasing its speed for the rest of the duration
		
		if self.life % 3 == 0 then -- create trails cuz theyre cool
			local trail = GM.instance_create(self.x, self.y, gm.constants.oEfTrail)
			trail.sprite_index = self.sprite_index
			trail.image_index = self.image_index
			trail.image_blend = self.image_blend
			trail.image_alpha = self.image_alpha
			trail.image_xscale = self.image_xscale
			trail.image_yscale = self.image_yscale
			trail.direction = self.direction
			trail.speed = self.speed * 0.75
			trail.depth = self.depth + 1
		end
		
		for _, actor in ipairs(self:get_collisions(gm.constants.pActorCollisionBase)) do -- make it deal damage if it hits the opposite team
			if self:attack_collision_canhit(actor) and Instance.exists(self.parent) then
				if gm._mod_net_isHost() then
					local attack = self.parent:fire_direct(actor, self.damage / self.parent.damage)
				end

				self:sound_play(gm.constants.wExplosiveShot, 1, 0.8 + math.random() * 0.2)
				self:destroy()
			end
		end
	end
	
	if gm.inside_view(self.x, self.y) == 1 and self.life % 5 == 0 then
		rainbowspark:create_color(self.x, self.y, self.image_blend, 1, Particle.SYSTEM.middle)
	end
end)

-- make the worm shoot the star storm
gm.pre_code_execute("gml_Object_oWormBody_Alarm_1", function(self, other)
	if self.parent.elite_type ~= empy.value then return end
	
	if self.parent.elite_type == empy.value then
		self:alarm_set(1, 330)
	
		if self.parent.target then
			local star = oStarStorm:create(self.x, self.y)
			star.parent = self.parent
			star.team = self.parent.team
			star.targetX = self.parent.target.x
			star.targetY = self.parent.target.y
			star.damage = self.parent.damage * 0.07
			if Helper.chance(0.5) then
				star.direction = self.image_angle - 90 + math.random(-45, 45)
			else
				star.direction = self.image_angle + 90 + math.random(-45, 45)
			end
			star.speed = math.random(4, 8)
			star.image_blend = self.image_blend
		end
	end
end)

-- make the warning change color
gm.pre_code_execute("gml_Object_oWormWarning_Step_0", function(self, other)
	if self.image_blend ~= Color.from_hsv((Global._current_frame - 1) % 360, 65, 100) then return end
	
	if self.image_blend == Color.from_hsv((Global._current_frame - 1) % 360, 65, 100) then -- check what color it was on the previous frame
		self.image_blend = Color.from_hsv(Global._current_frame % 360, 65, 100)
	end
end)

local blacklist = {
	["lemrider"] = true, -- the spawn anim breaks since its 2 of them at once, also doesnt actually do most elite effects
	["bramble"] = true, -- requires major fixes that im not sure are even possible
 	["spitter"] = true, -- technically fully works but FUCK no
}

local whitelist = {
	["jellyfish"] = true,
	["magmaWorm"] = true,
	["swift"] = true, -- lmao cope
	["archerBug"] = true,
	["colossus"] = true,
	["ancientWisp"] = true,
	["ifrit"] = true,
	["wanderingVagrant"] = true,
	["youngVagrant"] = true,
}

Callback.add(Callback.TYPE.onGameStart, "SSResetEmpyreanChance", function()
	GM._mod_game_getDirector().__ssr_empyrean_chance = 0.02 -- higher chance so you see your first empyrean sooner
end)

Callback.add(Callback.TYPE.onEliteInit, "SSSpawnEmpyrean", function(actor)
	if GM._mod_game_getDirector().stages_passed <= 8 then return end -- only spawns if its stage 9+
	if not GM._mod_net_isHost() then return end
	
	if actor.elite_type ~= empy.value then -- if the actor is not already empyrean
		local all_monster_cards = Monster_Card.find_all()
		local chance = GM._mod_game_getDirector().__ssr_empyrean_chance -- a value from 0 to 1
		local diff = math.max(1, (GM._mod_game_getDirector().enemy_buff - 16) / 4)
		for i, card in ipairs(all_monster_cards) do
			if card.object_id == actor.object_index then -- if the actor has a monster card
				if not blacklist[card.identifier] and (card.can_be_blighted == true or whitelist[card.identifier]) then -- if the actor is not blacklisted and can be blighted or is in the whitelist
					local cost = math.min(4, math.max(1, card.spawn_cost / 40 * diff))
					if Helper.chance(chance / cost) then
						GM.elite_set(actor, empy.value) -- make it empyrean
						GM._mod_game_getDirector().__ssr_empyrean_chance = 0.005 * diff -- reset the chance
					else
						GM._mod_game_getDirector().__ssr_empyrean_chance = GM._mod_game_getDirector().__ssr_empyrean_chance + 0.002 * diff -- increase the chance on fail
					end
					break
				end
			end
		end
	end
end)