diff --git a/code/__DEFINES/ai/ai_defines.dm b/code/__DEFINES/ai/ai_defines.dm
index 3e69252c738..6f27ca7a03a 100644
--- a/code/__DEFINES/ai/ai_defines.dm
+++ b/code/__DEFINES/ai/ai_defines.dm
@@ -20,6 +20,7 @@
// How far should we, by default, be looking for interesting things to de-idle?
#define AI_DEFAULT_INTERESTING_DIST 10
#define AI_SIMPLE_INTERESTING_DIST 15
+#define AI_MEGAFAUNA_INTERESTING_DIST 30
/// Cooldown on planning if planning failed last time
diff --git a/code/__DEFINES/ai/bluespace_horror_blackboard_defines.dm b/code/__DEFINES/ai/bluespace_horror_blackboard_defines.dm
new file mode 100644
index 00000000000..47baffd29a4
--- /dev/null
+++ b/code/__DEFINES/ai/bluespace_horror_blackboard_defines.dm
@@ -0,0 +1,7 @@
+#define BB_HORROR_SUMMON_MOBS_ACTION "BB_HORROR_SUMMON_MOBS_ACTION"
+#define BB_HORROR_FIREBALL_FAN_ACTION "BB_HORROR_FIREBALL_FAN_ACTION"
+#define BB_HORROR_LIFESTEAL_BOLT_ACTION "BB_HORROR_LIFESTEAL_BOLT_ACTION"
+#define BB_HORROR_MAGIC_MISSILE_ACTION "BB_HORROR_MAGIC_MISSILE_ACTION"
+#define BB_HORROR_CHARGE_ACTION "BB_HORROR_CHARGE_ACTION"
+#define BB_HORROR_BLINK_ACTION "BB_HORROR_BLINK_ACTION"
+#define BB_HORROR_SPELL_COOLDOWN "BB_HORROR_SPELL_COOLDOWN"
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index b9ab785276f..949c9aab554 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -112,6 +112,8 @@
#define STATUS_EFFECT_GROUNDPOUND /datum/status_effect/stacking/ground_pound //if hit twice, the third attack will fling the target
+#define STATUS_EFFECT_BLUESPACE_THREADS /datum/status_effect/stacking/unstable_bluespace_threads // if hit twice, the third attack will cause something random
+
#define STATUS_EFFECT_TELEPORTSICK /datum/status_effect/teleport_sickness //increasing debuffs as you rapidly teleport.
#define STATUS_EFFECT_PACIFIED /datum/status_effect/pacifism //forces the pacifism trait
diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm
index 12e89638d29..7f0f8fb1865 100644
--- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm
+++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm
@@ -70,6 +70,7 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(
var/atom/target = pick_final_target(controller, filtered_targets)
controller.set_blackboard_key(target_key, target)
+ SEND_SIGNAL(controller.pawn, COMSIG_HOSTILE_FOUND_TARGET)
var/atom/potential_hiding_location = targeting_strategy.find_hidden_mobs(living_mob, target)
diff --git a/code/modules/mob/living/basic/hostile/megafauna/basic_megafauna.dm b/code/modules/mob/living/basic/hostile/megafauna/basic_megafauna.dm
new file mode 100644
index 00000000000..b6738239ebf
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/megafauna/basic_megafauna.dm
@@ -0,0 +1,210 @@
+// Base type for basic mob megafauna
+/mob/living/basic/megafauna
+ name = "megafauna"
+ desc = "Attack the weak point for massive damage."
+ health = 1000
+ maxHealth = 1000
+ a_intent = INTENT_HARM
+ sentience_type = SENTIENCE_BOSS
+ environment_smash = ENVIRONMENT_SMASH_RWALLS
+ mob_biotypes = MOB_ORGANIC | MOB_EPIC
+ obj_damage = 400
+ light_range = 3
+ faction = list("boss")
+ weather_immunities = list("lava","ash")
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
+ minimum_survivable_temperature = 0
+ maximum_survivable_temperature = INFINITY
+ speed = 0
+ move_force = MOVE_FORCE_OVERPOWERING
+ move_resist = MOVE_FORCE_OVERPOWERING
+ pull_force = MOVE_FORCE_OVERPOWERING
+ mob_size = MOB_SIZE_LARGE
+ layer = LARGE_MOB_LAYER // Looks weird with them slipping under mineral walls and cameras and shit otherwise
+ flags_2 = IMMUNE_TO_SHUTTLECRUSH_2
+ initial_traits = list(TRAIT_FLYING)
+ /// The loot the mob should drop if killed with a crusher
+ var/list/crusher_loot = list()
+ /// The type of medal it drops if on hard mode
+ var/medal_type
+ /// The type of boss for the medal it drops
+ var/score_type
+ /// Is the mob truly dead?
+ var/elimination = FALSE
+ /// The mob's internal GPS, if it has one
+ var/obj/item/gps/internal_gps
+ /// If this is a megafauna that should grant achievements, or have a gps signal
+ var/true_spawn = TRUE
+ /// Has someone enabled hard mode?
+ var/enraged = FALSE
+ /// Path of the hardmode loot disk, if applicable.
+ var/enraged_loot
+ /// How much ore should killing this give
+ var/difficulty_ore_modifier = 0
+ /// Actions to grant on Initialize
+ var/list/innate_actions = list()
+
+/mob/living/basic/megafauna/Initialize(mapload)
+ . = ..()
+ GLOB.alive_megafauna_list |= UID()
+ if(internal_gps && true_spawn)
+ internal_gps = new internal_gps(src)
+ grant_actions_by_list(innate_actions)
+ generate_random_loot()
+ RegisterSignal(src, , PROC_REF(hoverboard_deactivation))
+
+/mob/living/basic/megafauna/Destroy()
+ QDEL_NULL(internal_gps)
+ UnregisterSignal(src, COMSIG_HOSTILE_FOUND_TARGET)
+ GLOB.alive_megafauna_list -= UID()
+ return ..()
+
+/mob/living/basic/megafauna/melee_attack(atom/target, list/modifiers, ignore_cooldown)
+ . = ..()
+ if(isliving(target))
+ var/mob/living/L = target
+ if(L.stat == DEAD)
+ devour(L)
+
+/mob/living/basic/megafauna/can_die()
+ return ..() && health <= 0
+
+/mob/living/basic/megafauna/death(gibbed)
+ GLOB.alive_megafauna_list -= UID()
+ // lets normalize the icons a bit
+ pixel_x = 0
+ pixel_y = 0
+ // this happens before the parent call because `del_on_death` may be set
+ if(can_die() && !admin_spawned)
+ var/datum/status_effect/crusher_damage/C = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING)
+ if(C && length(crusher_loot) && C.total_damage >= maxHealth * 0.6)
+ spawn_crusher_loot()
+ if(enraged && length(loot) && enraged_loot) // Don't drop a disk if the boss drops no loot. Important for legion.
+ for(var/mob/living/M in urange(20, src)) // Yes big range, but for bubblegum arena
+ if(M.client)
+ loot += enraged_loot // Disk for each miner / borg.
+ if(!elimination) // used so the achievment only occurs for the last legion to die.
+ SSblackbox.record_feedback("tally", "megafauna_kills", 1, "[initial(name)]")
+ calc_ore_reward()
+ return ..()
+
+/// If the megafauna has a pool of random loot items to pick from, override this proc to have it be set on initialization
+/mob/living/basic/megafauna/proc/generate_random_loot()
+ return list()
+
+/// Handling the ore part of the mega reward, the higher the difficulty_ore_modifier the more ore will spawn
+/mob/living/basic/megafauna/proc/calc_ore_reward()
+ var/list/common_ore = list(
+ /obj/item/stack/ore/uranium,
+ /obj/item/stack/ore/silver,
+ /obj/item/stack/ore/gold,
+ /obj/item/stack/ore/plasma,
+ /obj/item/stack/ore/titanium)
+ var/list/rare_ore = list(
+ /obj/item/stack/ore/diamond,
+ /obj/item/stack/ore/bluespace_crystal)
+
+ for(var/ore in common_ore)
+ var/obj/item/stack/O = new ore(loc)
+ O.amount = roll(difficulty_ore_modifier * 2, 4 + difficulty_ore_modifier)
+ for(var/ore in rare_ore)
+ var/obj/item/stack/O = new ore(loc)
+ O.amount = roll(difficulty_ore_modifier, 4 + difficulty_ore_modifier)
+
+/mob/living/basic/megafauna/proc/spawn_crusher_loot()
+ loot += crusher_loot
+
+/mob/living/basic/megafauna/on_changed_z_level(turf/old_turf, turf/new_turf)
+ . = ..()
+ if(!istype(get_area(src), /area/shuttle))
+ unrage()
+
+/mob/living/basic/megafauna/onShuttleMove(turf/oldT, turf/T1, rotation, mob/caller_mob)
+ var/turf/oldloc = loc
+ . = ..()
+ if(!.)
+ return
+ var/turf/newloc = loc
+ message_admins("Megafauna [src] \
+ ([ADMIN_FLW(src,"FLW")]) \
+ moved via shuttle from ([oldloc.x], [oldloc.y], [oldloc.z]) to \
+ ([newloc.x], [newloc.y], [newloc.z])[caller_mob ? " called by [ADMIN_LOOKUP(caller_mob)]" : ""]")
+
+/mob/living/basic/megafauna/proc/devour(mob/living/L)
+ if(!L)
+ return FALSE
+ visible_message(
+ "[src] devours [L]!",
+ "You feast on [L], restoring your health!")
+ if(!is_station_level(z) || client) // NPC monsters won't heal while on station
+ adjustBruteLoss(-L.maxHealth/2)
+ L.gib()
+ return TRUE
+
+/mob/living/basic/megafauna/ex_act(severity, target)
+ switch(severity)
+ if(EXPLODE_DEVASTATE)
+ adjustBruteLoss(250)
+
+ if(EXPLODE_HEAVY)
+ adjustBruteLoss(100)
+
+ if(EXPLODE_LIGHT)
+ adjustBruteLoss(50)
+
+/// This proc is called by the HRD-MDE grenade to enrage the megafauna. This should increase the megafaunas attack speed if possible, give it new moves, or disable weak moves. This should be reverseable, and reverses on zlvl change.
+/mob/living/basic/megafauna/proc/enrage()
+ if(enraged || ((health / maxHealth) * 100 <= 80))
+ return
+ difficulty_ore_modifier += 4 // Hardmode only helps the station more and gives you bragging rights, no special items for hardmode
+ enraged = TRUE
+
+/mob/living/basic/megafauna/proc/unrage()
+ difficulty_ore_modifier -= 4
+ enraged = FALSE
+
+// MARK: PTL Interaction
+/mob/living/basic/megafauna/on_ptl_target(obj/machinery/power/transmission_laser/ptl)
+ ptl.RegisterSignal(src, COMSIG_MOB_DEATH, TYPE_PROC_REF(/obj/machinery/power/transmission_laser, untarget), ptl)
+ if(ptl?.firing)
+ on_ptl_fire(ptl)
+ return
+
+/mob/living/basic/megafauna/on_ptl_tick(obj/machinery/power/transmission_laser/ptl, output_level)
+ loot = list() // disable loot drops form the target to prevent cheese
+ if(10 * output_level * damage_coeff[BURN] / (1 MW) > health) // If we would kill the target dust it.
+ health = 0 // We need this so can_die() won't prevent dusting
+ visible_message("\The [src] is reduced to dust by the beam!")
+ dust()
+ else
+ adjustFireLoss(10 * output_level / (1 MW))
+
+/mob/living/basic/megafauna/on_ptl_untarget(obj/machinery/power/transmission_laser/ptl)
+ on_ptl_stop(ptl)
+ if(ptl)
+ ptl.UnregisterSignal(src, COMSIG_MOB_DEATH)
+
+/mob/living/basic/megafauna/on_ptl_fire(obj/machinery/power/transmission_laser/ptl)
+ var/orbital_strike = image(icon, src, "orbital_strike", FLY_LAYER, SOUTH)
+ add_overlay(orbital_strike)
+
+/mob/living/basic/megafauna/on_ptl_stop(obj/machinery/power/transmission_laser/ptl)
+ for(var/image/overlay in overlays)
+ if(overlay.name == "orbital_strike")
+ cut_overlay(overlay)
+
+/mob/living/basic/megafauna/proc/hoverboard_deactivation(source, target)
+ SIGNAL_HANDLER // COMSIG_HOSTILE_FOUND_TARGET
+ if(!isliving(target))
+ return
+ var/mob/living/L = target
+ if(!L.buckled)
+ return
+ if(!istype(L.buckled, /obj/tgvehicle/scooter/skateboard/hoverboard))
+ return
+ var/obj/tgvehicle/scooter/skateboard/hoverboard/cursed_board = L.buckled
+ // Not a visible message, as walls or such may be in the way
+ to_chat(L, "You hear a loud roar in the distance, and the lights on [cursed_board] begin to spark dangerously, as the board rumbles heavily!")
+ playsound(get_turf(src), 'sound/effects/tendril_destroyed.ogg', 200, FALSE, 50, TRUE, TRUE)
+ cursed_board.necropolis_curse()
diff --git a/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror.dm b/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror.dm
new file mode 100644
index 00000000000..8a8b03a2170
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror.dm
@@ -0,0 +1,182 @@
+/mob/living/basic/megafauna/bluespace_horror
+ name = "bluespace horror"
+ desc = "An indescribable creature, mutated from extended exposure to bluespace energies. There is no telling what it once was."
+ health = 1500
+ maxHealth = 1500
+ icon = 'icons/mob/lavaland/96x96megafauna.dmi'
+ icon_state = "bluespace_horror"
+ speak_emote = list("emanates")
+ melee_attack_cooldown_min = 1 SECONDS
+ damage_coeff = list(BRUTE = 0.75, BURN = 1.1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
+ melee_damage_lower = 35
+ melee_damage_upper = 45 // It's a megafauna. Don't get hit nerd.
+ attack_verb_simple = "strike"
+ attack_verb_continuous = "strikes"
+ attack_sound = 'sound/magic/ratvar_attack.ogg'
+ see_in_dark = 20 // I see you
+ pixel_x = -32
+ step_type = FOOTSTEP_MOB_HEAVY
+ is_ranged = TRUE
+ casing_type = /obj/item/ammo_casing/caseless/bluespace_shards
+ projectile_sound = 'sound/magic/wand_teleport.ogg'
+ ranged_burst_count = 2
+ ranged_burst_interval = 0.4
+ ranged_cooldown = 1.5 SECONDS
+ true_spawn = FALSE
+ ai_controller = /datum/ai_controller/basic_controller/bluespace_horror
+ innate_actions = list(
+ /datum/action/cooldown/mob_cooldown/bluespace_horror/summon_mobs = BB_HORROR_SUMMON_MOBS_ACTION,
+ /datum/action/cooldown/mob_cooldown/bluespace_horror/fireball_fan = BB_HORROR_FIREBALL_FAN_ACTION,
+ /datum/action/cooldown/mob_cooldown/bluespace_horror/lifesteal_bolt = BB_HORROR_LIFESTEAL_BOLT_ACTION,
+ /datum/action/cooldown/mob_cooldown/bluespace_horror/magic_missile = BB_HORROR_MAGIC_MISSILE_ACTION,
+ /datum/action/cooldown/mob_cooldown/bluespace_horror/charge = BB_HORROR_CHARGE_ACTION,
+ /datum/action/cooldown/mob_cooldown/bluespace_horror/blink = BB_HORROR_BLINK_ACTION)
+ /// Did we do the big attack?
+ var/final_burst = FALSE
+
+/mob/living/basic/megafauna/bluespace_horror/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/ai_retaliate)
+
+/mob/living/basic/megafauna/bluespace_horror/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
+ return TRUE
+
+/mob/living/basic/megafauna/bluespace_horror/melee_attack(atom/target, list/modifiers, ignore_cooldown)
+ if(!isliving(target))
+ return ..()
+ var/mob/living/L = target
+ var/datum/status_effect/stacking/unstable_bluespace_threads/G = L.has_status_effect(STATUS_EFFECT_BLUESPACE_THREADS)
+ if(!G)
+ L.apply_status_effect(STATUS_EFFECT_BLUESPACE_THREADS, 1, src)
+ return ..()
+ if(G.add_stacks(stacks_added = 1, attacker = src))
+ return ..()
+
+/mob/living/basic/megafauna/bluespace_horror/adjustHealth(amount, updating_health)
+ if(mind) // For whatever reason, if a player is in control, we want them in charge of their blinks
+ return ..()
+ var/datum/action/cooldown/mob_cooldown/bluespace_horror/blink/blink_action = ai_controller.blackboard[BB_HORROR_BLINK_ACTION]
+ if(blink_action.IsAvailable())
+ ai_controller.queue_behavior(/datum/ai_behavior/horror_blink_dodge, BB_HORROR_BLINK_ACTION)
+ return
+ . = ..()
+ if(health < maxHealth / 3 && !final_burst)
+ ai_controller.queue_behavior(/datum/ai_behavior/omega_fireball_fan)
+ final_burst = TRUE
+
+/// Proc used for various powers and spells
+/mob/living/basic/megafauna/bluespace_horror/proc/shoot_projectile(atom/target_atom, obj/projectile/thing_to_shoot, set_angle)
+ var/turf/startloc = get_turf(src)
+ var/turf/endloc = get_turf(target_atom)
+
+ if(!startloc || !endloc || endloc == loc)
+ return
+
+ var/obj/projectile/P = new thing_to_shoot(startloc)
+ P.preparePixelProjectile(endloc, startloc)
+ P.firer = src
+ P.firer_source_atom = src
+
+ if(isnum(set_angle))
+ P.fire(set_angle)
+ else
+ P.fire()
+
+/// WHen hitting a mob five times, does something random
+/mob/living/basic/megafauna/bluespace_horror/proc/instability_correction()
+ switch(rand(1,5))
+ // ZAP
+ if(1)
+ var/list/shock_mobs = list()
+ for(var/mob/living/C in view(get_turf(src), 5))
+ if(isliving(C) && !faction_check_mob(C))
+ shock_mobs += C
+ if(length(shock_mobs))
+ for(var/i in 1 to 3)
+ var/mob/living/sucker = pick_n_take(shock_mobs)
+ sucker.electrocute_act(rand(5, 25), "instability correction")
+ playsound(get_turf(sucker), 'sound/effects/eleczap.ogg', 75, TRUE)
+ Beam(sucker, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5)
+ // Vwoop!
+ if(2)
+ var/list/desto_turfs = list()
+ for(var/turf/possible in view(get_turf(src), 3))
+ if(possible.is_blocked_turf(exclude_mobs = TRUE))
+ continue
+ if(isspaceturf(possible))
+ continue
+ desto_turfs += possible
+ for(var/mob/living/C in oview(get_turf(src), 3))
+ C.forceMove(pick(desto_turfs))
+ to_chat(C, "You are teleported a short distance!")
+ do_sparks(3, TRUE, C)
+ // Yoink!
+ if(3)
+ for(var/mob/living/C in oview(get_turf(src), 5))
+ var/dir = get_dir(C.loc, loc)
+ var/turf/step = get_step(C, dir)
+ C.forceMove(step)
+ to_chat(C, "You feel a strong force pulling you towards [src]!")
+
+ // Shazam!
+ if(4)
+ for(var/mob/living/C in oview(get_turf(src), 1))
+ shoot_projectile(C, /obj/projectile/magic/lifesteal_bolt)
+ playsound(src, 'sound/magic/invoke_general.ogg', 200, TRUE, 2)
+ // Kablooey!
+ if(5)
+ playsound(src, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE)
+ new /obj/effect/temp_visual/stomp(loc)
+ for(var/mob/living/L in range(3, src))
+ if(faction_check(faction, L.faction, FALSE))
+ continue
+
+ L.visible_message("[L] was thrown by [src]!",
+ "You feel a strong force throwing you!",
+ "You hear a thud.")
+ var/atom/throw_target = get_edge_target_turf(L, get_dir(src, get_step_away(L, src)))
+ L.throw_at(throw_target, 4, 4)
+ var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+ var/armor = L.run_armor_check(def_zone = limb_to_hit, armor_type = MELEE, armor_penetration_percentage = 50)
+ L.apply_damage(20, BRUTE, limb_to_hit, armor)
+
+
+/obj/item/ammo_casing/caseless/bluespace_shards
+ name = "bluespace shards"
+ projectile_type = /obj/projectile/magic/bluespace_shards
+ fire_sound = 'sound/magic/wand_teleport.ogg'
+ pellets = 4
+ variance = 90
+
+/obj/projectile/magic/bluespace_shards
+ name = "bluespace shard"
+ icon_state = "u_laser_alt"
+ damage = 20
+ damage_type = BRUTE
+ nodamage = FALSE
+
+/datum/status_effect/stacking/unstable_bluespace_threads
+ id = "unstable_bluespace_threads"
+ tick_interval = 5 SECONDS
+ stack_threshold = 5
+ max_stacks = 5
+ reset_ticks_on_stack = TRUE
+ var/mob/living/basic/megafauna/bluespace_horror/latest_attacker
+
+/datum/status_effect/stacking/unstable_bluespace_threads/on_creation(mob/living/new_owner, stacks_to_apply, mob/living/attacker)
+ . = ..()
+ if(.)
+ latest_attacker = attacker
+
+/datum/status_effect/stacking/unstable_bluespace_threads/add_stacks(stacks_added, mob/living/attacker)
+ . = ..()
+ if(.)
+ latest_attacker = attacker
+ if(stacks != stack_threshold)
+ return TRUE
+
+/datum/status_effect/stacking/unstable_bluespace_threads/stacks_consumed_effect()
+ addtimer(CALLBACK(latest_attacker, TYPE_PROC_REF(/mob/living/basic/megafauna/bluespace_horror, instability_correction)), 1 SECONDS)
+
+/datum/status_effect/stacking/unstable_bluespace_threads/on_remove()
+ latest_attacker = null
diff --git a/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror_actions.dm b/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror_actions.dm
new file mode 100644
index 00000000000..05f0f8c1c4f
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror_actions.dm
@@ -0,0 +1,203 @@
+/datum/action/cooldown/mob_cooldown/bluespace_horror/summon_mobs
+ name = "Summon Mobs"
+ button_icon = 'icons/mob/actions/actions_cult.dmi'
+ button_icon_state = "telerune"
+ desc = "Summon a series of servants from bluespace to assist you."
+ click_to_activate = FALSE
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 90 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/summon_mobs/Activate(atom/target)
+ var/mob/living/basic/megafauna/bluespace_horror/summoner = owner
+ if(!istype(summoner))
+ to_chat(owner, "I am unable to summon servants!")
+ return
+
+ var/amount_to_spawn = rand(2, 5)
+ var/list/all_possible_dirs = GLOB.alldirs
+ for(var/i in 1 to amount_to_spawn)
+ var/mob/living/new_mob
+ var/spawn_loc = get_step(summoner, pick_n_take(all_possible_dirs))
+ new_mob = create_random_mob(spawn_loc, HOSTILE_SPAWN)
+ new /obj/effect/temp_visual/emp/pulse/cult(spawn_loc)
+ new_mob.faction = summoner.faction
+ StartCooldown()
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/charge
+ name = "Violent Charge"
+ button_icon_state = "OH_YEAAAAH"
+ desc = "Fully materialize in the material plane and charge forward!"
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 20 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/charge/Activate(atom/target)
+ . = ..()
+ var/dir_to_target = get_dir(get_turf(owner), get_turf(target))
+ var/turf/T = get_step(get_turf(owner), dir_to_target)
+ for(var/i in 1 to 9)
+ new /obj/effect/temp_visual/dragon_swoop/bluespace_horror(T)
+ T = get_step(T, dir_to_target)
+ owner.visible_message("[owner] prepares to charge!")
+ addtimer(CALLBACK(src, PROC_REF(charge_to), dir_to_target, 0), 4)
+ StartCooldown()
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/charge/proc/charge_to(move_dir, times_ran, list/hit_targets = list())
+ var/mob/living/basic/horror = owner
+ if(times_ran >= 9)
+ return
+ var/turf/T = get_step(get_turf(owner), move_dir)
+ if(ismineralturf(T))
+ var/turf/simulated/mineral/M = T
+ M.gets_drilled()
+ if(iswallturf(T))
+ T.dismantle_wall(TRUE)
+ for(var/obj/structure/window/W in T.contents)
+ W.take_damage(horror.obj_damage, BRUTE)
+ for(var/obj/machinery/door/D in T.contents)
+ D.take_damage(horror.obj_damage, BRUTE)
+ if(T.x > world.maxx - 2 || T.x < 2) // Keep them from runtiming
+ return
+ if(T.y > world.maxy - 2 || T.y < 2)
+ return
+ owner.forceMove(T)
+ playsound(owner, 'sound/effects/bang.ogg', 200, 1)
+ var/throwtarget = get_edge_target_turf(owner, move_dir)
+ for(var/mob/living/L in T.contents - owner)
+ if(owner.faction_check_mob(L))
+ continue
+ owner.visible_message("[owner] tramples and kicks [L]!")
+ to_chat(L, "[owner] tramples you and kicks you away!")
+ L.throw_at(throwtarget, 10, 1, owner)
+ L.Weaken(1 SECONDS) // Pain Train has no breaks.
+ if(L in hit_targets)
+ L.adjustBruteLoss(15)
+ else
+ hit_targets += L
+ L.adjustBruteLoss(25)
+ addtimer(CALLBACK(src, PROC_REF(charge_to), move_dir, (times_ran + 1), hit_targets), 0.7)
+
+// The visual effect which appears in front of the horror when it goes to charge.
+/obj/effect/temp_visual/dragon_swoop/bluespace_horror
+ icon_state = "rune_large_distorted"
+ color = "#000099"
+
+/obj/effect/temp_visual/dragon_swoop/bluespace_horror/Initialize(mapload)
+ . = ..()
+ transform *= 0.33
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/magic_missile
+ name = "Bluespace Missile"
+ button_icon_state = "magicm"
+ desc = "Conjure bluespace projectiles to seek nearby victims!"
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 40 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/magic_missile/Activate(atom/target)
+ . = ..()
+ var/mob/living/basic/megafauna/bluespace_horror/horror = owner
+ if(!istype(horror))
+ return
+
+ for(var/T in RANGE_TURFS(12, horror.loc) - horror.loc)
+ if(prob(10))
+ horror.shoot_projectile(T, /obj/projectile/magic/magic_missile/lesser/horror)
+ playsound(horror, 'sound/magic/magic_missile.ogg', 40, TRUE)
+ sleep(1)
+ horror.shoot_projectile(target, /obj/projectile/magic/magic_missile/lesser/horror)
+ playsound(horror, 'sound/magic/magic_missile.ogg', 40, TRUE)
+ StartCooldown()
+
+/obj/projectile/magic/magic_missile/lesser/horror
+ damage = 15
+ damage_type = BRUTE
+ nodamage = FALSE
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/fireball_fan
+ name = "Fireball Fan"
+ button_icon_state = "fireball"
+ desc = "Concentrate bluespace energy into a volatile volley of flame!"
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 20 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/fireball_fan/Activate(atom/target)
+ . = ..()
+ var/mob/living/basic/megafauna/bluespace_horror/horror = owner
+ if(!istype(horror))
+ return
+ var/angle_to_target = get_angle(horror, target)
+
+ var/variance = -90
+ for(var/i in 1 to 5)
+ horror.shoot_projectile(target, /obj/projectile/magic/fireball, angle_to_target + variance)
+ variance += 45
+ playsound(horror, 'sound/magic/fireball.ogg', 200, TRUE, 2)
+ StartCooldown()
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/lifesteal_bolt
+ name = "Lifesteal Bolt"
+ button_icon = 'icons/mob/actions/actions_cult.dmi'
+ button_icon_state = "revive"
+ desc = "Fire a fast-moving bolt that drains the vitality of those that live on the material plane."
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 10 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/lifesteal_bolt/Activate(atom/target)
+ . = ..()
+ var/mob/living/basic/megafauna/bluespace_horror/horror = owner
+ if(!istype(horror))
+ return
+
+ horror.shoot_projectile(target, /obj/projectile/magic/lifesteal_bolt)
+ playsound(horror, 'sound/magic/invoke_general.ogg', 200, TRUE, 2)
+ StartCooldown()
+
+/obj/projectile/magic/lifesteal_bolt
+ name = "lifesteal bolt"
+ icon_state = "arcane_barrage"
+ damage = 25
+ damage_type = BRUTE
+ nodamage = FALSE
+
+/obj/projectile/magic/lifesteal_bolt/on_hit(mob/living/carbon/target)
+ . = ..()
+ if(!.)
+ return .
+ if(isliving(target) && isliving(firer))
+ var/mob/living/LF = firer
+ target.apply_damage(5, CLONE)
+ LF.adjustBruteLoss(-25)
+ target.Beam(LF, icon_state = "drain_life", icon = 'icons/effects/effects.dmi', time = 2 SECONDS)
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/blink
+ name = "Blink"
+ button_icon_state = "blink"
+ desc = "Teleport away from your current location."
+ click_to_activate = FALSE
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 6 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/bluespace_horror/blink/Activate(atom/target)
+ . = ..()
+ var/list/turfs = list()
+ for(var/turf/T in range(target, 7))
+ if(T in range(target, 2))
+ continue
+ if(isspaceturf(T))
+ continue
+ if(T.density)
+ continue
+ turfs += T
+ var/turf/picked = pick(turfs)
+ var/datum/effect_system/smoke_spread/smoke
+ smoke = new /datum/effect_system/smoke_spread/bad()
+ smoke.set_up(2, FALSE, owner.loc)
+ smoke.start()
+ owner.forceMove(picked)
+ playsound(owner, 'sound/magic/blink.ogg', 150, TRUE, 2)
+ StartCooldown()
diff --git a/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror_ai.dm b/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror_ai.dm
new file mode 100644
index 00000000000..00b8421cddc
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/megafauna/bluespace_horror/bluespace_horror_ai.dm
@@ -0,0 +1,77 @@
+/datum/ai_controller/basic_controller/bluespace_horror
+ movement_delay = 1 SECONDS
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
+ BB_AGGRO_RANGE = 7,
+ )
+ ai_movement = /datum/ai_movement/jps
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+ interesting_dist = AI_MEGAFAUNA_INTERESTING_DIST
+ max_target_distance = 15
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/find_and_hunt_target/corpses/human,
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/targeted_mob_ability/horror_offensive_spellcasting,
+ /datum/ai_planning_subtree/ranged_skirmish,
+ /datum/ai_planning_subtree/attack_obstacle_in_path/walls,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ /datum/ai_planning_subtree/find_and_hunt_target/prowl,
+ /datum/ai_planning_subtree/attack_obstacle_in_path/prowl/walls,
+ )
+
+/datum/ai_planning_subtree/targeted_mob_ability/horror_offensive_spellcasting
+ ability_key = BB_HORROR_FIREBALL_FAN_ACTION
+ /// Minimum time between any spells
+ var/horror_global_spellcasting_delay = 2 SECONDS
+
+/datum/ai_planning_subtree/targeted_mob_ability/horror_offensive_spellcasting/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(controller.blackboard[BB_HORROR_SPELL_COOLDOWN] >= world.time)
+ return
+ var/list/ability_keys = list(BB_HORROR_FIREBALL_FAN_ACTION, BB_HORROR_LIFESTEAL_BOLT_ACTION, BB_HORROR_MAGIC_MISSILE_ACTION, BB_HORROR_CHARGE_ACTION, BB_HORROR_SUMMON_MOBS_ACTION)
+ ability_key = pick_n_take(ability_keys)
+ var/datum/action/cooldown/mob_cooldown/selected_action = controller.blackboard[ability_key]
+ while(selected_action && !selected_action.IsAvailable())
+ if(!ability_keys.len) // All spells are on cooldown
+ return
+ ability_key = pick_n_take(ability_keys)
+ selected_action = controller.blackboard[ability_key]
+ controller.set_blackboard_key(BB_HORROR_SPELL_COOLDOWN, world.time + horror_global_spellcasting_delay)
+ return ..()
+
+/// Dodge!
+/datum/ai_behavior/horror_blink_dodge
+ action_cooldown = 6 SECONDS
+ required_distance = 0
+ behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+
+/datum/ai_behavior/horror_blink_dodge/perform(seconds_per_tick, datum/ai_controller/controller, action_key)
+ . = ..()
+ var/datum/action/cooldown/mob_cooldown/bluespace_horror/blink/blink_action = controller.blackboard[action_key]
+ var/result = blink_action.Trigger()
+ if(result)
+ return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
+ return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
+
+/// Bring them down!
+/datum/ai_behavior/omega_fireball_fan
+ action_cooldown = 10 MINUTES
+ required_distance = 0
+
+/datum/ai_behavior/omega_fireball_fan/perform(seconds_per_tick, datum/ai_controller/controller, ...)
+ . = ..()
+ var/mob/living/basic/megafauna/bluespace_horror/user = controller.pawn
+ user.say("I WILL NOT BE BANISHED SO EASILY!")
+ spawn(1 SECONDS)
+ var/turf/start_turf = get_step(user, pick(GLOB.alldirs))
+ var/counter = rand(1, 16)
+ playsound(get_turf(user), 'sound/magic/invoke_general.ogg', 200, TRUE)
+ for(var/i in 1 to 80)
+ counter++
+ if(counter > 16)
+ counter = 1
+ var/obj/projectile/proj = pick(/obj/projectile/magic/lifesteal_bolt, /obj/projectile/magic/fireball, /obj/projectile/magic/magic_missile/lesser, /obj/projectile/magic/bluespace_shards)
+ user.shoot_projectile(start_turf, proj, counter * 22.5)
+ playsound(get_turf(user), 'sound/magic/staff_chaos.ogg', 50, TRUE)
+ sleep(1)
+ return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED
diff --git a/code/modules/mob/living/basic/hostile/megafauna/vox_armalis/vox_armalis.dm b/code/modules/mob/living/basic/hostile/megafauna/vox_armalis/vox_armalis.dm
new file mode 100644
index 00000000000..da1727a154a
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/megafauna/vox_armalis/vox_armalis.dm
@@ -0,0 +1,231 @@
+/mob/living/basic/megafauna/vox_armalis
+ name = "vox armalis"
+ desc = "A massive vox, clad in heavy carapace armor and wielding a massive spikethrower. Something this large and heavy should not be moving with such finesse."
+ health = 300
+ maxHealth = 300
+ gender = PLURAL
+ icon = 'icons/mob/lavaland/32x64fauna.dmi'
+ icon_state = "armalis"
+ icon_dead = "armalis_dead"
+ speak_emote = list("creels")
+ melee_attack_cooldown_min = 1 SECONDS
+ damage_coeff = list(BRUTE = 0.2, BURN = 0.2, TOX = 0.2, CLONE = 0, STAMINA = 0, OXY = 0)
+ melee_damage_lower = 25
+ melee_damage_upper = 30
+ attack_verb_simple = "punch"
+ attack_verb_continuous = "punches"
+ attack_sound = 'sound/weapons/resonator_blast.ogg'
+ response_help_continuous = "gestures at"
+ response_help_simple = "gesture at"
+ move_force = MOVE_FORCE_NORMAL
+ see_in_dark = 20 // I see you
+ step_type = FOOTSTEP_MOB_SHOE
+ is_ranged = TRUE
+ casing_type = /obj/item/ammo_casing/caseless/heavy_spike
+ projectile_sound = 'sound/weapons/bladeslice.ogg'
+ ranged_burst_count = 2
+ ranged_cooldown = 0.5 SECONDS // It's player controlled, it can have player fire rates.
+ initial_traits = list()
+ blood_color = "#2299FC"
+ butcher_time = 40 SECONDS
+ butcher_results = list(
+ /obj/item/food/fried_vox = 8,
+ /obj/item/organ/internal/cyberimp/brain/sensory_enhancer = 1,
+ /obj/item/salvage/loot/vox = 10,
+ /obj/item/organ/internal/brain/vox = 1,
+ )
+ true_spawn = FALSE
+ innate_actions = list(
+ /datum/action/cooldown/mob_cooldown/vox_armalis/swap_ammo,
+ /datum/action/cooldown/mob_cooldown/vox_armalis/activate_qani,
+ /datum/action/cooldown/mob_cooldown/vox_armalis/ignite_claws,
+ )
+ /// Are the claws on
+ var/plasma_claws = FALSE
+ /// Do we explode on death?
+ var/do_death_explosion = TRUE
+
+/mob/living/basic/megafauna/vox_armalis/Initialize(mapload)
+ . = ..()
+ add_language("Galactic Common")
+ add_language("Vox-pidgin")
+ set_default_language(GLOB.all_languages["Vox-pidgin"])
+ sight |= SEE_MOBS
+ var/datum/language/lang = GLOB.all_languages["Vox-pidgin"]
+ name = lang.get_random_name(gender)
+
+/mob/living/basic/megafauna/vox_armalis/IsAdvancedToolUser()
+ return TRUE
+
+/mob/living/basic/megafauna/vox_armalis/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
+ if(isspaceturf(loc))
+ new /obj/effect/particle_effect/ion_trails(get_turf(src), dir)
+ return TRUE
+
+/mob/living/basic/megafauna/vox_armalis/update_overlays()
+ . = ..()
+ overlays.Cut()
+ if(plasma_claws)
+ . += "armalis_plasma_claws"
+
+/mob/living/basic/megafauna/vox_armalis/ex_act(severity)
+ switch(severity)
+ if(EXPLODE_DEVASTATE)
+ adjustBruteLoss(75)
+
+ if(EXPLODE_HEAVY)
+ adjustBruteLoss(25)
+
+ if(EXPLODE_LIGHT)
+ adjustBruteLoss(10)
+
+/mob/living/basic/megafauna/vox_armalis/devour(mob/living/L)
+ return
+
+/mob/living/basic/megafauna/vox_armalis/death(gibbed)
+ plasma_claws = FALSE
+ update_icon(UPDATE_OVERLAYS)
+ transform = transform.Turn(90)
+ transform = transform.Translate(0, -16)
+ if(do_death_explosion)
+ death_explosion()
+ return ..()
+
+/mob/living/basic/megafauna/vox_armalis/gib() // Overwrites to make voxblood
+ if(!death(TRUE) && stat != DEAD)
+ return FALSE
+ // hide and freeze for the GC
+ notransform = TRUE
+ if(gib_nullifies_icon)
+ icon = null
+ invisibility = 101
+
+ playsound(src.loc, 'sound/goonstation/effects/gib.ogg', 50, 1)
+ for(var/i in 1 to 5)
+ new /obj/effect/gibspawner/vox(loc)
+ QDEL_IN(src, 0)
+ return TRUE
+
+/mob/living/basic/megafauna/vox_armalis/proc/death_explosion()
+ visible_message("[src] starts beeping ominously!")
+ for(var/i in 1 to 4)
+ playsound(loc, 'sound/items/timer.ogg', 30, 0)
+ sleep(1 SECONDS)
+ explosion(loc, 5, 11, 20, 26, flame_range = 26, cause = name)
+ qdel(src)
+
+/mob/living/basic/megafauna/vox_armalis/melee_attack(atom/target, list/modifiers, ignore_cooldown)
+ if(a_intent == INTENT_HARM)
+ if(ismob(target) && !plasma_claws)
+ var/mob/dustlung = target
+ var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
+ dustlung.throw_at(throw_target, 15, 9) // Like getting hit with a max-spool powerfist
+ return ..()
+ if(istype(target, /obj/machinery/door/airlock))
+ var/obj/machinery/door/airlock/A = target
+ try_open_airlock(A)
+ return
+ if(istype(target, /obj/machinery/door/firedoor))
+ var/obj/machinery/door/firedoor/A = target
+ if(A.density)
+ A.open()
+ else
+ A.close()
+ return
+ if(iswallturf(target))
+ return // We're not on kill intent. Don't smash.
+ if(ismachinery(target)) // We can interface with machines!
+ var/obj/machinery/machine = target
+ machine.attack_hand(src)
+ return
+
+/mob/living/basic/megafauna/vox_armalis/RangedAttack(atom/A, params)
+ if(a_intent != INTENT_HARM)
+ return // No shoot on friendly mode
+ . = ..()
+
+/mob/living/basic/megafauna/vox_armalis/proc/try_open_airlock(obj/machinery/door/airlock/D)
+ if(D.operating)
+ return
+ if(D.welded)
+ to_chat(src, "The door is welded.")
+ else if(D.locked)
+ to_chat(src, "The door is bolted.")
+ else if(D.allowed(src))
+ if(D.density)
+ D.open(TRUE)
+ else
+ D.close(TRUE)
+ return TRUE
+ visible_message("[src] forces the door!")
+ playsound(src.loc, "sparks", 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ if(D.density)
+ D.open(TRUE)
+ else
+ D.close(TRUE)
+
+/obj/item/ammo_casing/caseless/heavy_spike
+ name = "heavy alloy spike"
+ desc = "A large broadhead spike made out of a weird silvery metal."
+ projectile_type = /obj/projectile/bullet/hspike
+ muzzle_flash_effect = null
+ fire_sound = 'sound/weapons/bladeslice.ogg'
+
+/obj/item/ammo_casing/caseless/spike_flechettes
+ name = "spike flechettes"
+ desc = "A cluster of loosely-packed alloy spikes."
+ projectile_type = /obj/projectile/bullet/fspike
+ muzzle_flash_effect = null
+ fire_sound = 'sound/weapons/bladeslice.ogg'
+ pellets = 5
+ variance = 60
+
+/obj/item/ammo_casing/caseless/spike_penetrator
+ name = "spike penetrator"
+ desc = "A dense alloy spike with a reinforced, sharpened tip."
+ projectile_type = /obj/projectile/bullet/pspike
+ muzzle_flash_effect = null
+ fire_sound = 'sound/weapons/bladeslice.ogg'
+
+/obj/projectile/bullet/hspike
+ name = "heavy alloy spike"
+ desc = "It's about two feet of weird silvery metal with a wicked point."
+ damage = 35
+ knockdown = 3
+ armor_penetration_flat = 45
+ icon_state = "magspear"
+
+/obj/projectile/bullet/hspike/on_hit(atom/target, blocked = 0)
+ if((blocked < 100) && ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.bleed(50)
+ H.Immobilize(1 SECONDS)
+ ..()
+
+/obj/projectile/bullet/fspike
+ name = "alloy spike flechette"
+ desc = "A sharpened dart of silvery metal."
+ damage = 15
+ knockdown = 1
+ armor_penetration_flat = 15
+ icon_state = "magspear"
+
+/obj/projectile/bullet/fspike/on_hit(atom/target, blocked = 0)
+ if((blocked < 100) && ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.bleed(25)
+ ..()
+
+/obj/projectile/bullet/pspike
+ name = "alloy spike penetrator"
+ desc = "A 2-foot reinforced spike made of a silvery metal and sharpened to a dangerous point."
+ knockdown = 4
+ armor_penetration_flat = 100
+ icon_state = "magspear"
+ forcedodge = 8
+
+/obj/projectile/bullet/pspike/on_hit(atom/target, blocked = 0)
+ if((blocked < 100) && ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.bleed(75)
+ ..()
diff --git a/code/modules/mob/living/basic/hostile/megafauna/vox_armalis/vox_armalis_actions.dm b/code/modules/mob/living/basic/hostile/megafauna/vox_armalis/vox_armalis_actions.dm
new file mode 100644
index 00000000000..e4ca48055e4
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/megafauna/vox_armalis/vox_armalis_actions.dm
@@ -0,0 +1,132 @@
+/datum/action/cooldown/mob_cooldown/vox_armalis/activate_qani
+ name = "Activate Qani-Laaca"
+ button_icon = 'icons/obj/surgery.dmi'
+ button_icon_state = "sandy"
+ desc = "Activates an advanced Qani-Laaca implant, giving you high speed and the ability to dodge bullets for 20 seconds."
+ click_to_activate = FALSE
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 40 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/vox_armalis/activate_qani/Activate(atom/target)
+ var/mob/living/basic/megafauna/vox_armalis/armalis = owner
+ if(!istype(armalis))
+ return
+ RegisterSignal(armalis, COMSIG_MOVABLE_MOVED, PROC_REF(on_movement))
+ RegisterSignal(armalis, COMSIG_ATOM_PREHIT, PROC_REF(dodge_bullets))
+ armalis.next_move_modifier -= 0.5
+ armalis.speed -= 1
+ addtimer(CALLBACK(src, PROC_REF(end_qani)), 20 SECONDS)
+ to_chat(armalis, "A flood of suppressants and performance enhancers flood your system, while your augments go into overdrive.")
+ StartCooldown()
+
+/// Leaves an afterimage behind the mob when they move
+/datum/action/cooldown/mob_cooldown/vox_armalis/activate_qani/proc/on_movement(mob/living/L, atom/old_loc)
+ SIGNAL_HANDLER
+ if(HAS_TRAIT(L, TRAIT_IMMOBILIZED))
+ return NONE
+ new /obj/effect/temp_visual/decoy/mephedrone_afterimage(old_loc, L, 1.25 SECONDS)
+
+/// Tries to dodge incoming bullets if we aren't disabled for any reasons
+/datum/action/cooldown/mob_cooldown/vox_armalis/activate_qani/proc/dodge_bullets(mob/living/basic/megafauna/vox_armalis/armalis, obj/projectile/hitting_projectile)
+ SIGNAL_HANDLER
+
+ if(HAS_TRAIT(armalis, TRAIT_IMMOBILIZED))
+ return NONE
+ armalis.visible_message(
+ "[armalis] effortlessly dodges [hitting_projectile]!",
+ "You effortlessly evade [hitting_projectile]!",
+ )
+ playsound(armalis, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
+ armalis.add_filter("mephedrone_overdose_blur", 2, gauss_blur_filter(5))
+ addtimer(CALLBACK(armalis, TYPE_PROC_REF(/atom, remove_filter), "mephedrone_overdose_blur"), 0.5 SECONDS)
+ return ATOM_PREHIT_FAILURE
+
+/datum/action/cooldown/mob_cooldown/vox_armalis/activate_qani/proc/end_qani()
+ var/mob/living/basic/megafauna/vox_armalis/armalis = owner
+ if(!istype(armalis))
+ return
+ UnregisterSignal(armalis, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(armalis, COMSIG_ATOM_PREHIT)
+ armalis.next_move_modifier += 0.5
+ armalis.speed += 1
+ to_chat(armalis, "Your augments return to standard operation speeds as the suppressants wear off.")
+
+/datum/action/cooldown/mob_cooldown/vox_armalis/swap_ammo
+ name = "Switch Ammunition"
+ button_icon = 'icons/obj/guns/energy.dmi'
+ button_icon_state = "noisecannon"
+ desc = "Switches the ammunition that your heavy spikethrower fires between spike, flechette, and AP sabot."
+ click_to_activate = FALSE
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 0 SECONDS
+ shared_cooldown = NONE
+ /// The type of ammunition
+ var/ammo_type = 1
+
+/datum/action/cooldown/mob_cooldown/vox_armalis/swap_ammo/Activate(atom/target)
+ var/mob/living/basic/megafauna/vox_armalis/armalis = owner
+ if(!istype(armalis))
+ return
+
+ var/datum/component/ranged_attacks/comp = armalis.GetComponent(/datum/component/ranged_attacks)
+ if(!comp)
+ return
+
+ playsound(armalis, 'sound/weapons/kenetic_reload.ogg', 60, TRUE)
+ switch(ammo_type)
+ if(1)
+ ammo_type++
+ comp.casing_type = /obj/item/ammo_casing/caseless/spike_flechettes
+ comp.burst_shots = 1
+ comp.cooldown_time = 0.5 SECONDS
+ to_chat(armalis, "You switch your heavy spikethrower to flechette shot.")
+ if(2)
+ ammo_type++
+ comp.casing_type = /obj/item/ammo_casing/caseless/spike_penetrator
+ comp.burst_shots = 1
+ comp.cooldown_time = 1 SECONDS
+ to_chat(armalis, "You switch your heavy spikethrower to penetrating shot.")
+ if(3)
+ ammo_type = 1
+ comp.casing_type = /obj/item/ammo_casing/caseless/heavy_spike
+ comp.burst_shots = 2
+ comp.cooldown_time = 0.5 SECONDS
+ to_chat(armalis, "You switch your heavy spikethrower to heavy spike shot.")
+ StartCooldown()
+
+/datum/action/cooldown/mob_cooldown/vox_armalis/ignite_claws
+ name = "Toggle Plasma Talons"
+ button_icon = 'icons/obj/weapons/energy_melee.dmi'
+ button_icon_state = "pyro_claws"
+ desc = "Switches your plasma talons on or off, increasing your damage but preventing you from knocking targets back."
+ click_to_activate = FALSE
+ melee_cooldown_time = CLICK_CD_CLICK_ABILITY
+ cooldown_time = 0 SECONDS
+ shared_cooldown = NONE
+
+/datum/action/cooldown/mob_cooldown/vox_armalis/ignite_claws/Activate(atom/target)
+ var/mob/living/basic/megafauna/vox_armalis/armalis = owner
+ if(!istype(armalis))
+ return
+
+ if(armalis.plasma_claws)
+ armalis.plasma_claws = FALSE
+ armalis.melee_damage_lower = initial(armalis.melee_damage_lower)
+ armalis.melee_damage_upper = initial(armalis.melee_damage_upper)
+ armalis.attack_sound = initial(armalis.attack_sound)
+ armalis.attack_verb_simple = initial(armalis.attack_verb_simple)
+ armalis.attack_verb_continuous = initial(armalis.attack_verb_continuous)
+ playsound(armalis, 'sound/weapons/saberoff.ogg', 80, TRUE)
+ to_chat(armalis, "You switch off your plasma talons.")
+ else
+ armalis.plasma_claws = TRUE
+ armalis.melee_damage_lower += 10
+ armalis.melee_damage_upper += 20
+ armalis.attack_sound = 'sound/weapons/blade1.ogg'
+ armalis.attack_verb_simple = "slash"
+ armalis.attack_verb_continuous = "slashes"
+ playsound(armalis, 'sound/weapons/saberon.ogg', 80, TRUE)
+ to_chat(armalis, "You switch on your plasma talons.")
+ armalis.update_icon(UPDATE_OVERLAYS)
+ StartCooldown()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 337d2dbf53c..e0034ef893d 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1014,10 +1014,10 @@
to_chat(user, SPAN_NOTICE("You begin to butcher [src]..."))
playsound(loc, 'sound/weapons/slice.ogg', 50, TRUE, -1)
if(user.mind && HAS_TRAIT(user.mind, TRAIT_BUTCHER))
- if(do_mob(user, src, 3 SECONDS) && Adjacent(I))
+ if(do_mob(user, src, butcher_time / 2) && Adjacent(I))
harvest(user)
else
- if(do_mob(user, src, 8 SECONDS) && Adjacent(I))
+ if(do_mob(user, src, butcher_time) && Adjacent(I))
harvest(user)
return TRUE
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index f14de53f175..c991665c588 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -141,3 +141,6 @@
var/can_hide = FALSE
/// Allows a mob to pass unbolted doors while hidden
var/pass_door_while_hidden = FALSE
+
+ /// How long does it take to harvest a mob?
+ var/butcher_time = 8 SECONDS
diff --git a/icons/mob/lavaland/32x64fauna.dmi b/icons/mob/lavaland/32x64fauna.dmi
new file mode 100644
index 00000000000..06de099f3c4
Binary files /dev/null and b/icons/mob/lavaland/32x64fauna.dmi differ
diff --git a/icons/mob/lavaland/96x96megafauna.dmi b/icons/mob/lavaland/96x96megafauna.dmi
index e87cbff2669..e8af2416af2 100644
Binary files a/icons/mob/lavaland/96x96megafauna.dmi and b/icons/mob/lavaland/96x96megafauna.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 195da2b7679..948da1074d7 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -164,6 +164,7 @@
#include "code\__DEFINES\zoom.dm"
#include "code\__DEFINES\ai\ai_defines.dm"
#include "code\__DEFINES\ai\blackboard_defines.dm"
+#include "code\__DEFINES\ai\bluespace_horror_blackboard_defines.dm"
#include "code\__DEFINES\ai\changeling_giant_spider_ai_defines.dm"
#include "code\__DEFINES\ai\hostile_mech_blackboard_defines.dm"
#include "code\__DEFINES\ai\incursion_blackboard_defines.dm"
@@ -2681,6 +2682,12 @@
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider_ai.dm"
#include "code\modules\mob\living\basic\hostile\gorilla\gorilla.dm"
#include "code\modules\mob\living\basic\hostile\gorilla\gorilla_emote.dm"
+#include "code\modules\mob\living\basic\hostile\megafauna\basic_megafauna.dm"
+#include "code\modules\mob\living\basic\hostile\megafauna\bluespace_horror\bluespace_horror.dm"
+#include "code\modules\mob\living\basic\hostile\megafauna\bluespace_horror\bluespace_horror_actions.dm"
+#include "code\modules\mob\living\basic\hostile\megafauna\bluespace_horror\bluespace_horror_ai.dm"
+#include "code\modules\mob\living\basic\hostile\megafauna\vox_armalis\vox_armalis.dm"
+#include "code\modules\mob\living\basic\hostile\megafauna\vox_armalis\vox_armalis_actions.dm"
#include "code\modules\mob\living\basic\hostile\nether_mobs\blankbody.dm"
#include "code\modules\mob\living\basic\hostile\nether_mobs\creature.dm"
#include "code\modules\mob\living\basic\hostile\nether_mobs\faithless.dm"