diff --git a/Elites/empyrean.lua b/Elites/empyrean.lua
new file mode 100644
index 00000000..d01c4979
--- /dev/null
+++ b/Elites/empyrean.lua
@@ -0,0 +1,656 @@
+-- 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 then
+ actor:get_data().empy_beam = 180
+ actor:get_data().empy_beam_over = 0
+ actor:sound_play(gotanythingsharp, 2, 1)
+ else
+ 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 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)
+ 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
+ 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
+ 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
+ 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
+ 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 -- 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)
\ No newline at end of file
diff --git a/Language/english.json b/Language/english.json
index afe1e878..636fa8cc 100644
--- a/Language/english.json
+++ b/Language/english.json
@@ -515,7 +515,10 @@
},
"elite" : {
- "poison.name" : "Poisoning %s"
+ "poison.name" : "Poisoning %s",
+ "empyrean.name" : "Empyrean %s",
+ "empyrean.text" : "Harbinger of Judgement",
+ "empyrean.worm" : "Prismatic Leviathan"
},
"stage" : {
"whistlingBasin.name" : "Whistling Basin",
diff --git a/Language/russian.json b/Language/russian.json
index 29a5125d..b8c365b3 100644
--- a/Language/russian.json
+++ b/Language/russian.json
@@ -461,7 +461,9 @@
},
"elite" : {
- "poison.name" : "%s яда"
+ "poison.name" : "%s яда",
+ "empyrean.name" : "%s Эмпиреи",
+ "empyrean.text" : "Предвестник Суда",
},
"stage" : {
"whistlingBasin.name" : "Свистящий Бассейн",
diff --git a/Sounds/Elites/Empyrean/beam.ogg b/Sounds/Elites/Empyrean/beam.ogg
new file mode 100644
index 00000000..fd77c31d
Binary files /dev/null and b/Sounds/Elites/Empyrean/beam.ogg differ
diff --git a/Sounds/Elites/Empyrean/spawn.ogg b/Sounds/Elites/Empyrean/spawn.ogg
new file mode 100644
index 00000000..08d6ec92
Binary files /dev/null and b/Sounds/Elites/Empyrean/spawn.ogg differ
diff --git a/Sounds/Elites/Empyrean/spawn_alt.ogg b/Sounds/Elites/Empyrean/spawn_alt.ogg
new file mode 100644
index 00000000..92942e1c
Binary files /dev/null and b/Sounds/Elites/Empyrean/spawn_alt.ogg differ
diff --git a/Sounds/Elites/Empyrean/spawn_worm.ogg b/Sounds/Elites/Empyrean/spawn_worm.ogg
new file mode 100644
index 00000000..82ef7fc1
Binary files /dev/null and b/Sounds/Elites/Empyrean/spawn_worm.ogg differ
diff --git a/Sounds/Elites/Empyrean/star_shoot.ogg b/Sounds/Elites/Empyrean/star_shoot.ogg
new file mode 100644
index 00000000..079f9d4d
Binary files /dev/null and b/Sounds/Elites/Empyrean/star_shoot.ogg differ
diff --git a/Sounds/Elites/Empyrean/star_spawn.ogg b/Sounds/Elites/Empyrean/star_spawn.ogg
new file mode 100644
index 00000000..e31d5828
Binary files /dev/null and b/Sounds/Elites/Empyrean/star_spawn.ogg differ
diff --git a/Sounds/Elites/Empyrean/teleport1.ogg b/Sounds/Elites/Empyrean/teleport1.ogg
new file mode 100644
index 00000000..1b1ae00d
Binary files /dev/null and b/Sounds/Elites/Empyrean/teleport1.ogg differ
diff --git a/Sounds/Elites/Empyrean/teleport2.ogg b/Sounds/Elites/Empyrean/teleport2.ogg
new file mode 100644
index 00000000..407dcc5a
Binary files /dev/null and b/Sounds/Elites/Empyrean/teleport2.ogg differ
diff --git a/Sounds/Elites/Empyrean/teleport3.ogg b/Sounds/Elites/Empyrean/teleport3.ogg
new file mode 100644
index 00000000..1407fd8a
Binary files /dev/null and b/Sounds/Elites/Empyrean/teleport3.ogg differ
diff --git a/Sprites/Elites/Empyrean/beam.png b/Sprites/Elites/Empyrean/beam.png
new file mode 100644
index 00000000..739e6179
Binary files /dev/null and b/Sprites/Elites/Empyrean/beam.png differ
diff --git a/Sprites/Elites/Empyrean/icon.png b/Sprites/Elites/Empyrean/icon.png
new file mode 100644
index 00000000..4bb3599e
Binary files /dev/null and b/Sprites/Elites/Empyrean/icon.png differ
diff --git a/Sprites/Elites/Empyrean/palette.png b/Sprites/Elites/Empyrean/palette.png
new file mode 100644
index 00000000..f84c2777
Binary files /dev/null and b/Sprites/Elites/Empyrean/palette.png differ
diff --git a/Sprites/Elites/Empyrean/splash.png b/Sprites/Elites/Empyrean/splash.png
new file mode 100644
index 00000000..32db3e6f
Binary files /dev/null and b/Sprites/Elites/Empyrean/splash.png differ
diff --git a/Sprites/Elites/Empyrean/star.png b/Sprites/Elites/Empyrean/star.png
new file mode 100644
index 00000000..d0bc8bf9
Binary files /dev/null and b/Sprites/Elites/Empyrean/star.png differ