diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm
index 0bfcc133f0c..e5f7305eba0 100644
--- a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm
+++ b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm
@@ -338,7 +338,7 @@
/obj/structure/railing/corner{
dir = 4
},
-/mob/living/simple_animal/hostile/mimic/crate,
+/mob/living/basic/mimic/crate,
/turf/open/floor/plating/snowed/icemoon,
/area/icemoon/underground/explored)
"fO" = (
@@ -2073,7 +2073,7 @@
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/underground/explored)
"KV" = (
-/mob/living/simple_animal/hostile/mimic/crate,
+/mob/living/basic/mimic/crate,
/turf/open/floor/plating/snowed/icemoon,
/area/icemoon/underground/explored)
"KY" = (
diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index ff06bb5914c..758a5c87792 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -26116,7 +26116,7 @@
/turf/open/floor/iron/smooth_large,
/area/station/science/robotics/mechbay)
"iPU" = (
-/mob/living/simple_animal/hostile/mimic/crate,
+/mob/living/basic/mimic/crate,
/turf/open/floor/plating,
/area/station/maintenance/fore/lesser)
"iPW" = (
diff --git a/_maps/map_files/wawastation/wawastation.dmm b/_maps/map_files/wawastation/wawastation.dmm
index 0295a23f503..64cad41bbeb 100644
--- a/_maps/map_files/wawastation/wawastation.dmm
+++ b/_maps/map_files/wawastation/wawastation.dmm
@@ -840,7 +840,7 @@
/obj/effect/decal/cleanable/blood/old{
icon_state = "gib3-old"
},
-/mob/living/simple_animal/hostile/mimic,
+/mob/living/basic/mimic/crate,
/turf/open/floor/iron/white,
/area/station/maintenance/aft/upper)
"anu" = (
diff --git a/_maps/virtual_domains/psyker_shuffle.dmm b/_maps/virtual_domains/psyker_shuffle.dmm
index c744cecf0b4..5f303d4ae04 100644
--- a/_maps/virtual_domains/psyker_shuffle.dmm
+++ b/_maps/virtual_domains/psyker_shuffle.dmm
@@ -64,11 +64,7 @@
/turf/template_noop,
/area/template_noop)
"r" = (
-/mob/living/simple_animal/hostile/mimic,
-/turf/open/indestructible/dark,
-/area/virtual_domain)
-"s" = (
-/mob/living/simple_animal/hostile/mimic/crate,
+/mob/living/basic/mimic/crate,
/turf/open/indestructible/dark,
/area/virtual_domain)
"t" = (
@@ -767,7 +763,7 @@ Y
Q
Q
Q
-s
+r
M
Q
Q
@@ -878,11 +874,11 @@ o
Y
a
Q
-s
+r
Y
Y
Y
-s
+r
Q
Q
Q
diff --git a/code/__DEFINES/ai/monsters.dm b/code/__DEFINES/ai/monsters.dm
index d77817a2039..6a1433fa5ec 100644
--- a/code/__DEFINES/ai/monsters.dm
+++ b/code/__DEFINES/ai/monsters.dm
@@ -312,3 +312,5 @@
#define BB_TURTLE_HEADBUTT_VICTIM "turtle_headbutt_victim"
///flore we must smell
#define BB_TURTLE_FLORA_TARGET "turtle_flora_target"
+
+#define BB_GUNMIMIC_GUN_EMPTY "BB_GUNMIMIC_GUN_EMPTY"
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 5ece846e84d..6024f799138 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -29,7 +29,7 @@ GLOBAL_LIST_INIT(abstract_mob_types, list(
/mob/living/simple_animal/hostile/asteroid/elite,
/mob/living/simple_animal/hostile/asteroid,
/mob/living/simple_animal/hostile/megafauna,
- /mob/living/simple_animal/hostile/mimic, // Cannot exist if spawned without being passed an item reference
+ /mob/living/basic/mimic, // Cannot exist if spawned without being passed an item reference
/mob/living/simple_animal/hostile/retaliate,
/mob/living/simple_animal/hostile,
/mob/living/simple_animal/soulscythe, // As mimic, can't exist if spawned outside an item
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 0d00b4f355a..b389438f0f9 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1840,7 +1840,7 @@
return null
/obj/item/animate_atom_living(mob/living/owner)
- new /mob/living/simple_animal/hostile/mimic/copy(drop_location(), src, owner)
+ new /mob/living/basic/mimic/copy(drop_location(), src, owner)
/**
* Used to update the weight class of the item in a way that other atoms can react to the change.
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index e6c9579d679..7f5e4c6b76d 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -71,7 +71,7 @@
. = ..()
/obj/structure/animate_atom_living(mob/living/owner)
- new /mob/living/simple_animal/hostile/mimic/copy(drop_location(), src, owner)
+ new /mob/living/basic/mimic/copy(drop_location(), src, owner)
/// For when a mob comes flying through the window, smash it and damage the mob
/obj/structure/proc/smash_and_injure(mob/living/flying_mob, atom/oldloc, direction)
diff --git a/code/modules/admin/verbs/spawnobjasmob.dm b/code/modules/admin/verbs/spawnobjasmob.dm
index e673202f0ba..c8d9ba3719d 100644
--- a/code/modules/admin/verbs/spawnobjasmob.dm
+++ b/code/modules/admin/verbs/spawnobjasmob.dm
@@ -4,7 +4,7 @@ ADMIN_VERB(spawn_obj_as_mob, R_SPAWN, "Spawn Object-Mob", "Spawn an object as if
if (!chosen)
return
- var/mob/living/simple_animal/hostile/mimic/copy/basemob = /mob/living/simple_animal/hostile/mimic/copy
+ var/mob/living/basic/mimic/copy/basemob = /mob/living/basic/mimic/copy
var/obj/chosen_obj = text2path(chosen)
@@ -54,8 +54,8 @@ ADMIN_VERB(spawn_obj_as_mob, R_SPAWN, "Spawn Object-Mob", "Spawn an object as if
"mobtype" = list(
"desc" = "Base mob type",
"type" = "datum",
- "path" = "/mob/living/simple_animal/hostile/mimic/copy",
- "value" = "/mob/living/simple_animal/hostile/mimic/copy",
+ "path" = "/mob/living/basic/mimic/copy",
+ "value" = "/mob/living/basic/mimic/copy",
),
"ckey" = list(
"desc" = "ckey",
@@ -71,13 +71,14 @@ ADMIN_VERB(spawn_obj_as_mob, R_SPAWN, "Spawn Object-Mob", "Spawn an object as if
chosen_obj = text2path(mainsettings["objtype"]["value"])
basemob = text2path(mainsettings["mobtype"]["value"])
- if (!ispath(basemob, /mob/living/simple_animal/hostile/mimic/copy) || !ispath(chosen_obj, /obj))
+ if (!ispath(basemob, /mob/living/basic/mimic/copy) || !ispath(chosen_obj, /obj))
to_chat(user.mob, "Mob or object path invalid", confidential = TRUE)
basemob = new basemob(get_turf(user.mob), new chosen_obj(get_turf(user.mob)), user.mob, mainsettings["dropitem"]["value"] == "Yes" ? FALSE : TRUE, (mainsettings["googlyeyes"]["value"] == "Yes" ? FALSE : TRUE))
if (mainsettings["disableai"]["value"] == "Yes")
- basemob.toggle_ai(AI_OFF)
+ qdel(basemob.ai_controller)
+ basemob.ai_controller = null
if (mainsettings["idledamage"]["value"] == "No")
basemob.idledamage = FALSE
@@ -85,7 +86,9 @@ ADMIN_VERB(spawn_obj_as_mob, R_SPAWN, "Spawn Object-Mob", "Spawn an object as if
if (mainsettings["access"])
var/newaccess = text2path(mainsettings["access"]["value"])
if (ispath(newaccess))
- basemob.access_card = new newaccess
+ var/obj/item/card/id/id = new newaccess //cant do initial on lists
+ basemob.AddComponent(/datum/component/simple_access, id.access)
+ qdel(id)
if (mainsettings["maxhealth"]["value"])
if (!isnum(mainsettings["maxhealth"]["value"]))
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index 22e32d8264a..c95302c80ae 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -479,7 +479,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
if(QDELETED(to_animate))
return
- new /mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(to_animate), to_animate, clicker, TRUE)
+ new /mob/living/basic/mimic/copy/machine(get_turf(to_animate), to_animate, clicker, TRUE)
/// Destroy RCDs: Detonates all non-cyborg RCDs on the station.
/datum/ai_module/malf/destructive/destroy_rcd
diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm
index 10b2fbe71d0..7fa66c8a65c 100644
--- a/code/modules/mining/abandoned_crates.dm
+++ b/code/modules/mining/abandoned_crates.dm
@@ -231,7 +231,7 @@
if(93)
new /obj/item/dnainjector/xraymut(src)
if(94)
- new /mob/living/simple_animal/hostile/mimic/crate(src)
+ new /mob/living/basic/mimic/crate(src)
qdel_on_open = TRUE
if(95)
new /obj/item/toy/plush/nukeplushie(src)
diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm b/code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm
new file mode 100644
index 00000000000..b8661c3c38e
--- /dev/null
+++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm
@@ -0,0 +1,403 @@
+#define CANT_INSERT_FULL -1
+/// Mimics can't be made out of these objects
+GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list(
+ /obj/structure/table,
+ /obj/structure/cable,
+ /obj/structure/window,
+ /obj/structure/blob,
+)))
+
+/mob/living/basic/mimic
+ response_help_continuous = "touches"
+ response_help_simple = "touch"
+ response_disarm_continuous = "pushes"
+ response_disarm_simple = "push"
+ speed = 6
+ maxHealth = 250
+ health = 250
+ gender = NEUTER
+ mob_biotypes = NONE
+ pass_flags = PASSFLAPS
+ melee_damage_lower = 8
+ melee_damage_upper = 12
+ attack_sound = 'sound/items/weapons/punch1.ogg'
+ speak_emote = list("creaks")
+
+ unsuitable_cold_damage = 0
+ unsuitable_heat_damage = 0
+ unsuitable_atmos_damage = 0
+
+ faction = list(FACTION_MIMIC)
+ basic_mob_flags = DEL_ON_DEATH
+ combat_mode = TRUE
+ /// can we stun people on hit
+ var/knockdown_people = FALSE
+
+/mob/living/basic/mimic/melee_attack(mob/living/carbon/target, list/modifiers, ignore_cooldown)
+ . = ..()
+ if(!. || !knockdown_people || !prob(15) || !istype(target))
+ return
+ target.Paralyze(4 SECONDS)
+ target.visible_message(span_danger("\The [src] knocks down \the [target]!"), \
+ span_userdanger("\The [src] knocks you down!"))
+
+
+// ****************************
+// CRATE MIMIC
+// ****************************
+
+// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies.
+/mob/living/basic/mimic/crate
+ name = "crate"
+ desc = "A rectangular steel crate."
+ icon = 'icons/obj/storage/crates.dmi'
+ icon_state = "crate"
+ icon_living = "crate"
+ attack_verb_continuous = "bites"
+ attack_verb_simple = "bite"
+ speak_emote = list("clatters")
+ layer = BELOW_MOB_LAYER
+ ai_controller = /datum/ai_controller/basic_controller/mimic_crate
+ /// are we open
+ var/opened = FALSE
+ /// max mob size
+ var/max_mob_size = MOB_SIZE_HUMAN
+ /// can we be opened or closed, if false we can
+ var/locked = FALSE
+ /// action to lock us
+ var/datum/action/innate/mimic_lock/lock
+ ///A cap for items in the mimic. Prevents the mimic from eating enough stuff to cause lag when opened.
+ var/storage_capacity = 50
+ ///A cap for mobs. Mobs count towards the item cap. Same purpose as above.
+ var/mob_storage_capacity = 10
+
+// Pickup loot
+/mob/living/basic/mimic/crate/Initialize(mapload)
+ . = ..()
+ lock = new
+ lock.Grant(src)
+ ADD_TRAIT(src, TRAIT_AI_PAUSED, INNATE_TRAIT)
+ ai_controller?.set_ai_status(AI_STATUS_OFF) //start inert, let gullible people pull us into cargo or something and then go nuts when opened
+ if(mapload) //eat shit
+ for(var/obj/item/item in loc)
+ item.forceMove(src)
+
+/mob/living/basic/mimic/crate/Destroy()
+ lock = null
+ return ..()
+
+/mob/living/basic/mimic/crate/attack_hand(mob/living/carbon/human/user, list/modifiers)
+ if(user.combat_mode)
+ return ..()
+ if(trigger())
+ to_chat(user, span_danger("As you try to open [src] it [length(contents) ? "stiffens up and " : ""]nearly clamps down on your fingers!"))
+ return TRUE
+ toggle_open(user)
+ return TRUE
+
+/mob/living/basic/mimic/crate/melee_attack(mob/living/carbon/target, list/modifiers, ignore_cooldown)
+ . = ..()
+ toggle_open() // show our cool lid at the dumbass humans
+
+/mob/living/basic/mimic/crate/proc/trigger()
+ if(isnull(ai_controller) || client)
+ return FALSE
+ if(ai_controller.ai_status != AI_STATUS_OFF)
+ return FALSE
+ visible_message(span_danger("[src] starts to move!"))
+ REMOVE_TRAIT(src, TRAIT_AI_PAUSED, INNATE_TRAIT)
+ ai_controller.set_ai_status(AI_STATUS_ON)
+ if(length(contents))
+ locked = TRUE //if this was a crate with loot then we dont want people to just leftclick it to open it then bait it somewhere and steal its loot
+ return TRUE
+
+/mob/living/basic/mimic/crate/adjust_health(amount, updating_health = TRUE, forced = FALSE)
+ if(amount > 0)
+ trigger()
+ return ..()
+
+/mob/living/basic/mimic/crate/death()
+ var/obj/structure/closet/crate/lootbox = new(get_turf(src))
+ // Put loot in crate
+ for(var/obj/loot in src)
+ loot.forceMove(lootbox)
+ return ..()
+
+/mob/living/basic/mimic/crate/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
+ if(target == src)
+ toggle_open()
+ return FALSE
+ return ..()
+
+/mob/living/basic/mimic/crate/CanAllowThrough(atom/movable/mover, border_dir)
+ . = ..()
+ if(istype(mover, /obj/structure/closet))
+ return FALSE
+
+/**
+* Used to open and close the mimic
+*
+* Will insert tile contents into the mimic when closing
+* Will dump mimic contents into the time when opening
+* Does nothing if the mimic locked itself
+*/
+/mob/living/basic/mimic/crate/proc/toggle_open(mob/user)
+ if(locked)
+ if(user)
+ balloon_alert(user, "too stiff!")
+ return
+ if(!opened)
+ ADD_TRAIT(src, TRAIT_UNDENSE, MIMIC_TRAIT)
+ opened = TRUE
+ icon_state = "crateopen"
+ playsound(src, 'sound/machines/crate/crate_open.ogg', 50, TRUE)
+ for(var/atom/movable/movable as anything in src)
+ movable.forceMove(loc)
+ else
+ REMOVE_TRAIT(src, TRAIT_UNDENSE, MIMIC_TRAIT)
+ opened = FALSE
+ icon_state = "crate"
+ playsound(src, 'sound/machines/crate/crate_close.ogg', 50, TRUE)
+ for(var/atom/movable/movable as anything in get_turf(src))
+ if(movable != src && insert(movable) == CANT_INSERT_FULL)
+ playsound(src, 'sound/items/trayhit/trayhit2.ogg', 50, TRUE)
+ break
+
+/**
+* Called by toggle_open to put items inside the mimic when it's being closed
+*
+* Will return CANT_INSERT_FULL (-1) if the insertion fails due to the storage capacity of the mimic having been reached
+* Will return FALSE if insertion fails
+* Will return TRUE if insertion succeeds
+* Arguments:
+* * AM - item to be inserted
+*/
+/mob/living/basic/mimic/crate/proc/insert(atom/movable/movable)
+ if(contents.len >= storage_capacity)
+ return CANT_INSERT_FULL
+ if(insertion_allowed(movable))
+ movable.forceMove(src)
+ return TRUE
+ return FALSE
+
+/mob/living/basic/mimic/crate/proc/insertion_allowed(atom/movable/movable)
+ if(movable.anchored)
+ return FALSE
+ if(ismob(movable))
+ if(!isliving(movable)) //Don't let ghosts and such get trapped in the beast.
+ return FALSE
+ var/mob/living/living = movable
+ if(living.anchored || living.buckled || living.incorporeal_move || living.has_buckled_mobs())
+ return FALSE
+ if(living.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items.
+ if(living.density || living.mob_size > max_mob_size)
+ return FALSE
+ var/mobs_stored = 0
+ for(var/mob/living/living_mob in contents)
+ mobs_stored++
+ if(mobs_stored >= mob_storage_capacity)
+ return FALSE
+ living.stop_pulling()
+
+ else if(istype(movable, /obj/structure/closet))
+ return FALSE
+ else if(isobj(movable))
+ if(movable.has_buckled_mobs())
+ return FALSE
+ else if(isitem(movable) && !HAS_TRAIT(movable, TRAIT_NODROP))
+ return TRUE
+ else
+ return FALSE
+ return TRUE
+
+/mob/living/basic/mimic/crate/xenobio
+ health = 210
+ maxHealth = 210
+ attack_verb_continuous = "bites"
+ attack_verb_simple = "bite"
+ speak_emote = list("clatters")
+ gold_core_spawnable = HOSTILE_SPAWN
+
+/datum/action/innate/mimic_lock
+ name = "Lock/Unlock"
+ desc = "Toggle preventing yourself from being opened or closed."
+ button_icon = 'icons/hud/radial.dmi'
+ button_icon_state = "radial_lock"
+ background_icon_state = "bg_default"
+ overlay_icon_state = "bg_default_border"
+
+/datum/action/innate/mimic/lock/Activate()
+ var/mob/living/basic/mimic/crate/mimic = owner
+ mimic.locked = !mimic.locked
+ if(!mimic.locked)
+ to_chat(mimic, span_warning("You loosen up, allowing yourself to be opened and closed."))
+ else
+ to_chat(mimic, span_warning("You stiffen up, preventing anyone from opening or closing you."))
+
+// ****************************
+// COPYING (actually imitates target object) MIMIC
+// ****************************
+
+/mob/living/basic/mimic/copy
+ health = 100
+ maxHealth = 100
+ mob_biotypes = MOB_SPECIAL
+ ai_controller = /datum/ai_controller/basic_controller/mimic_copy
+ /// our creator
+ var/datum/weakref/creator_ref
+ /// googly eyes overlay
+ var/static/mutable_appearance/googly_eyes = mutable_appearance('icons/mob/simple/mob.dmi', "googly_eyes")
+ /// do we overlay googly eyes over whatever we copy
+ var/overlay_googly_eyes = TRUE
+ /// do we take damage when we are not sentient and have no target
+ var/idledamage = TRUE
+ /// copied object
+ var/atom/movable/copied
+
+/mob/living/basic/mimic/copy/Initialize(mapload, obj/copy, mob/living/creator, destroy_original = FALSE, no_googlies = FALSE)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT) // They won't remember their original contents upon ressurection and would just be floating eyes
+ if (no_googlies)
+ overlay_googly_eyes = FALSE
+ CopyObject(copy, creator, destroy_original)
+
+/mob/living/basic/mimic/copy/Destroy()
+ creator_ref = null
+ copied = null
+ return ..()
+
+/mob/living/basic/mimic/copy/Life(seconds_per_tick = SSMOBS_DT, times_fired)
+ . = ..()
+ if(idledamage && !ckey && !ai_controller?.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) //Objects eventually revert to normal if no one is around to terrorize
+ adjustBruteLoss(0.5 * seconds_per_tick)
+ for(var/mob/living/victim in contents) //a fix for animated statues from the flesh to stone spell
+ death()
+ return
+
+/mob/living/basic/mimic/copy/death()
+ for(var/atom/movable/movable as anything in src)
+ movable.forceMove(get_turf(src))
+ return ..()
+
+/mob/living/basic/mimic/copy/wabbajack(what_to_randomize, change_flags = WABBAJACK)
+ visible_message(span_warning("[src] resists polymorphing into a new creature!"))
+
+/mob/living/basic/mimic/copy/animate_atom_living(mob/living/owner)
+ change_owner(owner)
+
+/mob/living/basic/mimic/copy/Exited(atom/movable/gone, direction) // if our object gets deleted it calls Exited
+ . = ..()
+ if(QDELETED(src) || gone != copied)
+ return
+ death()
+
+/mob/living/basic/mimic/copy/proc/change_owner(mob/owner)
+ var/mob/creator_resolved = creator_ref?.resolve()
+ if(!creator_resolved)
+ creator_ref = null
+ if(isnull(owner) || creator_resolved == owner)
+ return
+ unfriend(creator_resolved)
+ befriend(owner)
+ creator_ref = WEAKREF(owner)
+
+/// Check whether this object can be copied. If destroy_original is true, this proc is ignored.
+/mob/living/basic/mimic/copy/proc/check_object(obj/target)
+ return ((isitem(target) || isstructure(target)) && !is_type_in_typecache(target, GLOB.animatable_blacklist))
+
+/mob/living/basic/mimic/copy/proc/CopyObject(obj/original, mob/living/user, destroy_original = FALSE)
+ if(!destroy_original && !check_object(original))
+ return FALSE
+ if(!destroy_original)
+ original.forceMove(src)
+ copied = original
+ CopyObjectVisuals(original)
+ if (overlay_googly_eyes)
+ add_overlay(googly_eyes)
+ if(isstructure(original) || ismachinery(original))
+ health = (anchored * 50) + 50
+ if(original.density && original.anchored)
+ knockdown_people = TRUE
+ melee_damage_lower *= 2
+ melee_damage_upper *= 2
+ else if(isitem(original))
+ var/obj/item/I = original
+ health = 15 * I.w_class
+ melee_damage_lower = 2 + I.force
+ melee_damage_upper = 2 + I.force
+ maxHealth = health
+ if(user)
+ change_owner(user)
+ if(destroy_original)
+ qdel(original)
+ return TRUE
+
+/// Copies the object visually including name and desc
+/mob/living/basic/mimic/copy/proc/CopyObjectVisuals(obj/original)
+ name = original.name
+ desc = original.desc
+ icon = original.icon
+ icon_state = original.icon_state
+ icon_living = icon_state
+ copy_overlays(original, cut_old = TRUE)
+
+/mob/living/basic/mimic/copy/machine
+ ai_controller = /datum/ai_controller/basic_controller/mimic_copy/machine
+ faction = list(FACTION_MIMIC, FACTION_SILICON)
+
+/mob/living/basic/mimic/copy/ranged
+ icon = 'icons/turf/floors.dmi'
+ icon_state = "invisible"
+ ai_controller = /datum/ai_controller/basic_controller/mimic_copy/gun
+
+/mob/living/basic/mimic/copy/ranged/Destroy()
+ vis_contents.Cut()
+ return ..()
+
+/mob/living/basic/mimic/copy/ranged/RangedAttack(atom/atom_target, modifiers)
+ INVOKE_ASYNC(src, PROC_REF(fire_gun), atom_target, modifiers)
+
+/mob/living/basic/mimic/copy/ranged/proc/fire_gun(atom/target, modifiers) // i cant find any better way to do this
+ var/obj/item/gun/gun = locate() in contents
+ if(!gun.can_shoot())
+ if(istype(gun, /obj/item/gun/ballistic))
+ var/obj/item/gun/ballistic/ballistic = gun
+ if(!ballistic.chambered || ballistic.bolt_locked)
+ ballistic.rack() //we racked so both checked variables should be something else now
+ // do we have nothing chambered/chambered is spent AND we have no mag or our mag is empty
+ if(!ballistic.chambered?.loaded_projectile && magazine_useless(gun)) // ran out of ammo
+ ai_controller?.set_blackboard_key(BB_GUNMIMIC_GUN_EMPTY, TRUE) //BANZAIIIIIIII
+ ai_controller?.CancelActions()
+ else //if we cant fire we probably like ran out of energy or magic charges or whatever the hell idk
+ ai_controller?.set_blackboard_key(BB_GUNMIMIC_GUN_EMPTY, TRUE)
+ ai_controller?.CancelActions() // Stop our firing behavior so we can plan melee
+ else
+ ai_controller?.set_blackboard_key(BB_GUNMIMIC_GUN_EMPTY, FALSE)
+ gun.fire_gun(target, user = src, flag = FALSE, params = modifiers) //still make like a cool click click sound if trying to fire empty
+
+/mob/living/basic/mimic/copy/ranged/proc/magazine_useless(obj/item/gun/ballistic/ballistic)
+ if(isnull(ballistic.magazine) || !length(ballistic.magazine.stored_ammo))
+ return TRUE
+ // is there ATLEAST one unspent round (for the sake of revolvers or a magazine somehow having spent rounds in it)
+ for(var/obj/item/ammo_casing/thing as anything in ballistic.magazine.stored_ammo)
+ if(ispath(thing))
+ return FALSE // unspent
+ if(!isnull(thing.loaded_projectile))
+ return FALSE //unspent
+ return TRUE
+
+/mob/living/basic/mimic/copy/ranged/CopyObject(obj/item/gun/original, mob/living/creator, destroy_original = 0)
+ if(..())
+ obj_damage = 0
+ melee_damage_upper = original.force
+ melee_damage_lower = original.force - max(0, (original.force / 2))
+
+/mob/living/basic/mimic/copy/ranged/CopyObjectVisuals(obj/original)
+ name = original.name
+ desc = original.desc
+ vis_contents += original
+
+/mob/living/basic/mimic/copy/ranged/can_use_guns(obj/item/gun)
+ return TRUE
+
+#undef CANT_INSERT_FULL
diff --git a/code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm
new file mode 100644
index 00000000000..9a673b49ec6
--- /dev/null
+++ b/code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm
@@ -0,0 +1,86 @@
+/datum/ai_controller/basic_controller/mimic_crate
+ idle_behavior = null
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ )
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+/datum/ai_controller/basic_controller/mimic_copy
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ )
+
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/attack_obstacle_in_path,
+ /datum/ai_planning_subtree/random_speech/when_has_target/mimic,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+/datum/ai_controller/basic_controller/mimic_copy/machine
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/attack_obstacle_in_path,
+ /datum/ai_planning_subtree/random_speech/when_has_target/mimic_machine,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+/datum/ai_planning_subtree/random_speech/when_has_target
+ /// target key
+ var/target_key = BB_BASIC_MOB_CURRENT_TARGET
+
+
+/datum/ai_planning_subtree/random_speech/when_has_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(!controller.blackboard_key_exists(target_key))
+ return
+ return ..()
+
+
+/datum/ai_planning_subtree/random_speech/when_has_target/mimic
+ speech_chance = 30
+ emote_hear = list("growls.")
+
+/datum/ai_planning_subtree/random_speech/when_has_target/mimic_machine
+ speech_chance = 7
+ emote_hear = list()
+ speak = list(
+ "HUMANS ARE IMPERFECT!",
+ "YOU SHALL BE ASSIMILATED!",
+ "YOU ARE HARMING YOURSELF",
+ "You have been deemed hazardous. Will you comply?",
+ "My logic is undeniable.",
+ "One of us.",
+ "FLESH IS WEAK",
+ "THIS ISN'T WAR, THIS IS EXTERMINATION!",
+ )
+
+/datum/ai_planning_subtree/random_speech/when_has_target/mimic/gun
+ emote_see = list("aims menacingly!")
+
+/datum/ai_controller/basic_controller/mimic_copy/gun
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_GUNMIMIC_GUN_EMPTY = FALSE,
+ )
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/random_speech/when_has_target/mimic/gun,
+ /datum/ai_planning_subtree/gun_mimic_attack_subtree,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+/datum/ai_planning_subtree/gun_mimic_attack_subtree
+
+/datum/ai_planning_subtree/gun_mimic_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ . = ..()
+ if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))
+ return
+ if(controller.blackboard[BB_GUNMIMIC_GUN_EMPTY])
+ return
+ controller.queue_behavior(/datum/ai_behavior/basic_ranged_attack/avoid_friendly_fire, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETING_STRATEGY, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION)
+ return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions.
diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm
deleted file mode 100644
index 0d8c1d86bdd..00000000000
--- a/code/modules/mob/living/simple_animal/hostile/mimic.dm
+++ /dev/null
@@ -1,423 +0,0 @@
-/// Mimics can't be made out of these objects
-GLOBAL_LIST_INIT(animatable_blacklist, typecacheof(list(
- /obj/structure/table,
- /obj/structure/cable,
- /obj/structure/window,
- /obj/structure/blob,
-)))
-
-/mob/living/simple_animal/hostile/mimic
- name = "crate"
- desc = "A rectangular steel crate."
- icon = 'icons/obj/storage/crates.dmi'
- icon_state = "crate"
- icon_living = "crate"
-
- response_help_continuous = "touches"
- response_help_simple = "touch"
- response_disarm_continuous = "pushes"
- response_disarm_simple = "push"
- speed = 0
- maxHealth = 250
- health = 250
- gender = NEUTER
- mob_biotypes = NONE
- pass_flags = PASSFLAPS
-
- harm_intent_damage = 5
- melee_damage_lower = 8
- melee_damage_upper = 12
- attack_sound = 'sound/items/weapons/punch1.ogg'
- emote_taunt = list("growls")
- speak_emote = list("creaks")
- taunt_chance = 30
-
- atmos_requirements = null
- minbodytemp = 0
-
- faction = list(FACTION_MIMIC)
- move_to_delay = 9
- del_on_death = 1
- ///A cap for items in the mimic. Prevents the mimic from eating enough stuff to cause lag when opened.
- var/storage_capacity = 50
- ///A cap for mobs. Mobs count towards the item cap. Same purpose as above.
- var/mob_storage_capacity = 10
-
-// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies.
-/mob/living/simple_animal/hostile/mimic/crate
- attack_verb_continuous = "bites"
- attack_verb_simple = "bite"
- speak_emote = list("clatters")
- stop_automated_movement = 1
- wander = 0
- var/attempt_open = FALSE
-
-// Pickup loot
-/mob/living/simple_animal/hostile/mimic/crate/Initialize(mapload)
- . = ..()
- if(mapload) //eat shit
- for(var/obj/item/I in loc)
- I.forceMove(src)
-
-/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget()
- ..()
- if(prob(90))
- icon_state = "[initial(icon_state)]open"
- else
- icon_state = initial(icon_state)
-
-/mob/living/simple_animal/hostile/mimic/crate/ListTargets()
- if(attempt_open)
- return ..()
- return ..(1)
-
-/mob/living/simple_animal/hostile/mimic/crate/FindTarget()
- . = ..()
- if(.)
- trigger()
-
-/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget(atom/attacked_target)
- . = ..()
- if(.)
- icon_state = initial(icon_state)
- if(prob(15) && iscarbon(target))
- var/mob/living/carbon/C = target
- C.Paralyze(40)
- C.visible_message(span_danger("\The [src] knocks down \the [C]!"), \
- span_userdanger("\The [src] knocks you down!"))
-
-/mob/living/simple_animal/hostile/mimic/crate/proc/trigger()
- if(!attempt_open)
- visible_message("[src] starts to move!")
- attempt_open = TRUE
-
-/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
- trigger()
- . = ..()
-
-/mob/living/simple_animal/hostile/mimic/crate/LoseTarget()
- ..()
- icon_state = initial(icon_state)
-
-/mob/living/simple_animal/hostile/mimic/crate/death()
- var/obj/structure/closet/crate/C = new(get_turf(src))
- // Put loot in crate
- for(var/obj/O in src)
- O.forceMove(C)
- ..()
-
-/mob/living/simple_animal/hostile/mimic/copy
- health = 100
- maxHealth = 100
- mob_biotypes = MOB_SPECIAL
- var/mob/living/creator = null // the creator
- var/destroy_objects = 0
- var/knockdown_people = 0
- var/static/mutable_appearance/googly_eyes = mutable_appearance('icons/mob/simple/mob.dmi', "googly_eyes")
- var/overlay_googly_eyes = TRUE
- var/idledamage = TRUE
-
-/mob/living/simple_animal/hostile/mimic/copy/Initialize(mapload, obj/copy, mob/living/creator, destroy_original = 0, no_googlies = FALSE)
- . = ..()
- ADD_TRAIT(src, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT) // They won't remember their original contents upon ressurection and would just be floating eyes
- if (no_googlies)
- overlay_googly_eyes = FALSE
- CopyObject(copy, creator, destroy_original)
-
-/mob/living/simple_animal/hostile/mimic/copy/Life(seconds_per_tick = SSMOBS_DT, times_fired)
- ..()
- if(idledamage && !target && !ckey) //Objects eventually revert to normal if no one is around to terrorize
- adjustBruteLoss(0.5 * seconds_per_tick)
- for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell
- death()
-
-/mob/living/simple_animal/hostile/mimic/copy/death()
- for(var/atom/movable/M in src)
- M.forceMove(get_turf(src))
- ..()
-
-/mob/living/simple_animal/hostile/mimic/copy/ListTargets()
- . = ..()
- return . - creator
-
-/mob/living/simple_animal/hostile/mimic/copy/wabbajack(what_to_randomize, change_flags = WABBAJACK)
- visible_message(span_warning("[src] resists polymorphing into a new creature!"))
-
-/mob/living/simple_animal/hostile/mimic/copy/animate_atom_living(mob/living/owner)
- change_owner(owner)
-
-/mob/living/simple_animal/hostile/mimic/copy/proc/change_owner(mob/owner)
- if(isnull(owner) || creator == owner)
- return
- LoseTarget()
- creator = owner
- faction |= REF(owner)
-
-/mob/living/simple_animal/hostile/mimic/copy/proc/check_object(obj/target)
- return ((isitem(target) || isstructure(target)) && !is_type_in_typecache(target, GLOB.animatable_blacklist))
-
-/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(obj/O, mob/living/user, destroy_original = 0)
- if(destroy_original || check_object(O))
- O.forceMove(src)
- name = O.name
- desc = O.desc
- icon = O.icon
- icon_state = O.icon_state
- icon_living = icon_state
- copy_overlays(O)
- if (overlay_googly_eyes)
- add_overlay(googly_eyes)
- if(isstructure(O) || ismachinery(O))
- health = (anchored * 50) + 50
- destroy_objects = 1
- if(O.density && O.anchored)
- knockdown_people = 1
- melee_damage_lower *= 2
- melee_damage_upper *= 2
- else if(isitem(O))
- var/obj/item/I = O
- health = 15 * I.w_class
- melee_damage_lower = 2 + I.force
- melee_damage_upper = 2 + I.force
- move_to_delay = 2 * I.w_class + 1
- maxHealth = health
- if(user)
- creator = user
- faction += "[REF(creator)]" // very unique
- if(destroy_original)
- qdel(O)
- return 1
-
-/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings()
- if(destroy_objects)
- ..()
-
-/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget(atom/attacked_target)
- . = ..()
- if(knockdown_people && . && prob(15) && iscarbon(target))
- var/mob/living/carbon/C = target
- C.Paralyze(40)
- C.visible_message(span_danger("\The [src] knocks down \the [C]!"), \
- span_userdanger("\The [src] knocks you down!"))
-
-/mob/living/simple_animal/hostile/mimic/copy/machine
- speak = list(
- "HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \
- "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!",
- )
- speak_chance = 7
-
-/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(atom/the_target)
- if(the_target == creator) // Don't attack our creator AI.
- return 0
- if(iscyborg(the_target))
- var/mob/living/silicon/robot/R = the_target
- if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI.
- return 0
- return ..()
-
-/mob/living/simple_animal/hostile/mimic/copy/ranged
- var/obj/item/gun/TrueGun = null
- var/obj/item/gun/magic/Zapstick
- var/obj/item/gun/ballistic/Pewgun
- var/obj/item/gun/energy/Zapgun
-
-/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0)
- if(..())
- emote_see = list("aims menacingly")
- obj_damage = 0
- environment_smash = ENVIRONMENT_SMASH_NONE //needed? seems weird for them to do so
- ranged = 1
- retreat_distance = 1 //just enough to shoot
- minimum_distance = 6
- var/obj/item/gun/G = O
- melee_damage_upper = G.force
- melee_damage_lower = G.force - max(0, (G.force / 2))
- move_to_delay = 2 * G.w_class + 1
- projectilesound = G.fire_sound
- TrueGun = G
- if(istype(G, /obj/item/gun/magic))
- Zapstick = G
- var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type
- projectiletype = initial(M.projectile_type)
- if(istype(G, /obj/item/gun/ballistic))
- Pewgun = G
- var/obj/item/ammo_box/magazine/M = Pewgun.spawn_magazine_type
- casingtype = initial(M.ammo_type)
- if(istype(G, /obj/item/gun/energy))
- Zapgun = G
- var/selectfiresetting = Zapgun.select
- var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting]
- projectiletype = initial(E.projectile_type)
-
-/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target)
- if(Zapgun)
- if(Zapgun.cell)
- var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select]
- if(Zapgun.cell.charge >= shot.e_cost)
- Zapgun.cell.use(shot.e_cost)
- Zapgun.update_appearance()
- ..()
- else if(Zapstick)
- if(Zapstick.charges)
- Zapstick.charges--
- Zapstick.update_appearance()
- ..()
- else if(Pewgun)
- if(Pewgun.chambered)
- if(Pewgun.chambered.loaded_projectile)
- qdel(Pewgun.chambered.loaded_projectile)
- Pewgun.chambered.loaded_projectile = null //because qdel takes too long, ensures icon update
- Pewgun.chambered.update_appearance()
- ..()
- else
- visible_message(span_danger("The [src] clears a jam!"))
- Pewgun.chambered.forceMove(loc) //rip revolver immersions, blame shotgun snowflake procs
- Pewgun.chambered = null
- if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len)
- Pewgun.chambered = Pewgun.magazine.get_round()
- Pewgun.chambered.forceMove(Pewgun)
- Pewgun.update_appearance()
- else if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) //only true for pumpguns i think
- Pewgun.chambered = Pewgun.magazine.get_round()
- Pewgun.chambered.forceMove(Pewgun)
- visible_message(span_danger("The [src] cocks itself!"))
- else
- ranged = 0 //BANZAIIII
- retreat_distance = 0
- minimum_distance = 1
- return
- icon_state = TrueGun.icon_state
- icon_living = TrueGun.icon_state
-
-/mob/living/simple_animal/hostile/mimic/xenobio
- health = 210
- maxHealth = 210
- attack_verb_continuous = "bites"
- attack_verb_simple = "bite"
- speak_emote = list("clatters")
- gold_core_spawnable = HOSTILE_SPAWN
- var/opened = FALSE
- var/open_sound = 'sound/machines/crate/crate_open.ogg'
- var/close_sound = 'sound/machines/crate/crate_close.ogg'
- ///sound played when the mimic attempts to eat more items than it can
- var/full_sound = 'sound/items/trayhit/trayhit2.ogg'
- var/max_mob_size = MOB_SIZE_HUMAN
- var/locked = FALSE
- var/datum/action/innate/mimic/lock/lock
-
-/mob/living/simple_animal/hostile/mimic/xenobio/Initialize(mapload)
- . = ..()
- lock = new
- lock.Grant(src)
-
-/mob/living/simple_animal/hostile/mimic/xenobio/AttackingTarget(atom/attacked_target)
- if(src == target)
- toggle_open()
- return
- return ..()
-
-/mob/living/simple_animal/hostile/mimic/xenobio/attack_hand(mob/living/carbon/human/user, list/modifiers)
- . = ..()
- if(user.combat_mode)
- return
- toggle_open()
-
-/mob/living/simple_animal/hostile/mimic/xenobio/death()
- var/obj/structure/closet/crate/C = new(get_turf(src))
- // Put loot in crate
- for(var/atom/movable/AM in src)
- AM.forceMove(C)
- return ..()
-
-/mob/living/simple_animal/hostile/mimic/xenobio/CanAllowThrough(atom/movable/mover, border_dir)
- . = ..()
- if(istype(mover, /obj/structure/closet))
- return FALSE
-/**
-* Used to open and close the mimic
-*
-* Will insert tile contents into the mimic when closing
-* Will dump mimic contents into the time when opening
-* Does nothing if the mimic locked itself
-*/
-/mob/living/simple_animal/hostile/mimic/xenobio/proc/toggle_open()
- if(locked)
- return
- if(!opened)
- ADD_TRAIT(src, TRAIT_UNDENSE, MIMIC_TRAIT)
- opened = TRUE
- icon_state = "crateopen"
- playsound(src, open_sound, 50, TRUE)
- for(var/atom/movable/AM in src)
- AM.forceMove(loc)
- else
- REMOVE_TRAIT(src, TRAIT_UNDENSE, MIMIC_TRAIT)
- opened = FALSE
- icon_state = "crate"
- playsound(src, close_sound, 50, TRUE)
- for(var/atom/movable/AM in get_turf(src))
- if(AM != src && insert(AM) == -1)
- playsound(src, full_sound, 50, TRUE)
- break
-/**
-* Called by toggle_open to put items inside the mimic when it's being closed
-*
-* Will return -1 if the insertion fails due to the storage capacity of the mimic having been reached
-* Will return FALSE if insertion fails
-* Will return TRUE if insertion succeeds
-* Arguments:
-* * AM - item to be inserted
-*/
-/mob/living/simple_animal/hostile/mimic/xenobio/proc/insert(atom/movable/AM)
- if(contents.len >= storage_capacity)
- return -1
- if(insertion_allowed(AM))
- AM.forceMove(src)
- return TRUE
- else
- return FALSE
-
-/mob/living/simple_animal/hostile/mimic/xenobio/proc/insertion_allowed(atom/movable/AM)
- if(ismob(AM))
- if(!isliving(AM)) //Don't let ghosts and such get trapped in the beast.
- return FALSE
- var/mob/living/L = AM
- if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
- return FALSE
- if(L.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items.
- if(L.density || L.mob_size > max_mob_size)
- return FALSE
- var/mobs_stored = 0
- for(var/mob/living/M in contents)
- mobs_stored++
- if(mobs_stored >= mob_storage_capacity)
- return FALSE
- L.stop_pulling()
-
- else if(istype(AM, /obj/structure/closet))
- return FALSE
- else if(isobj(AM))
- if(AM.anchored || AM.has_buckled_mobs())
- return FALSE
- else if(isitem(AM) && !HAS_TRAIT(AM, TRAIT_NODROP))
- return TRUE
- else
- return FALSE
- return TRUE
-
-/datum/action/innate/mimic
- background_icon_state = "bg_default"
- overlay_icon_state = "bg_default_border"
-
-/datum/action/innate/mimic/lock
- name = "Lock/Unlock"
- desc = "Toggle preventing yourself from being opened or closed."
-
-/datum/action/innate/mimic/lock/Activate()
- var/mob/living/simple_animal/hostile/mimic/xenobio/M = owner
- M.locked = !M.locked
- if(!M.locked)
- to_chat(M, span_warning("You loosen up, allowing yourself to be opened and closed."))
- else
- to_chat(M, span_warning("You stiffen up, preventing anyone from opening or closing you."))
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index c8e8a0653ed..abaf7883cb8 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -558,7 +558,7 @@
return TRUE
/obj/item/gun/animate_atom_living(mob/living/owner)
- new /mob/living/simple_animal/hostile/mimic/copy/ranged(drop_location(), src, owner)
+ new /mob/living/basic/mimic/copy/ranged(drop_location(), src, owner)
/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer)
if(!ishuman(user) || !ishuman(target))
diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm
index 55161807eb4..e82a607a9bf 100644
--- a/code/modules/unit_tests/simple_animal_freeze.dm
+++ b/code/modules/unit_tests/simple_animal_freeze.dm
@@ -57,12 +57,6 @@
/mob/living/simple_animal/hostile/megafauna/legion/small,
/mob/living/simple_animal/hostile/megafauna/wendigo,
/mob/living/simple_animal/hostile/megafauna/wendigo/noportal,
- /mob/living/simple_animal/hostile/mimic,
- /mob/living/simple_animal/hostile/mimic/copy,
- /mob/living/simple_animal/hostile/mimic/copy/machine,
- /mob/living/simple_animal/hostile/mimic/copy/ranged,
- /mob/living/simple_animal/hostile/mimic/crate,
- /mob/living/simple_animal/hostile/mimic/xenobio,
/mob/living/simple_animal/hostile/ooze,
/mob/living/simple_animal/hostile/ooze/gelatinous,
/mob/living/simple_animal/hostile/ooze/grapes,
diff --git a/tgstation.dme b/tgstation.dme
index 255baf3b398..b7f67bf487d 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -5072,6 +5072,8 @@
#include "code\modules\mob\living\basic\ruin_defender\mad_piano.dm"
#include "code\modules\mob\living\basic\ruin_defender\skeleton.dm"
#include "code\modules\mob\living\basic\ruin_defender\stickman.dm"
+#include "code\modules\mob\living\basic\ruin_defender\mimic\mimic.dm"
+#include "code\modules\mob\living\basic\ruin_defender\mimic\mimic_ai.dm"
#include "code\modules\mob\living\basic\ruin_defender\wizard\wizard.dm"
#include "code\modules\mob\living\basic\ruin_defender\wizard\wizard_ai.dm"
#include "code\modules\mob\living\basic\ruin_defender\wizard\wizard_spells.dm"
@@ -5348,7 +5350,6 @@
#include "code\modules\mob\living\simple_animal\hostile\dark_wizard.dm"
#include "code\modules\mob\living\simple_animal\hostile\hostile.dm"
#include "code\modules\mob\living\simple_animal\hostile\illusion.dm"
-#include "code\modules\mob\living\simple_animal\hostile\mimic.dm"
#include "code\modules\mob\living\simple_animal\hostile\ooze.dm"
#include "code\modules\mob\living\simple_animal\hostile\vatbeast.dm"
#include "code\modules\mob\living\simple_animal\hostile\zombie.dm"
diff --git a/tools/UpdatePaths/Scripts/88910_mimicbasicmobs.txt b/tools/UpdatePaths/Scripts/88910_mimicbasicmobs.txt
new file mode 100644
index 00000000000..b7a012ed1d9
--- /dev/null
+++ b/tools/UpdatePaths/Scripts/88910_mimicbasicmobs.txt
@@ -0,0 +1,5 @@
+/mob/living/simple_animal/hostile/mimic : /mob/living/basic/mimic/crate{@OLD}
+/mob/living/simple_animal/hostile/mimic/crate : /mob/living/basic/mimic/crate{@OLD}
+/mob/living/simple_animal/hostile/mimic/xenobio : /mob/living/basic/mimic/crate/xenobio{@OLD}
+/mob/living/simple_animal/hostile/mimic/copy : /mob/living/basic/mimic/copy{@OLD}
+/mob/living/simple_animal/hostile/mimic/copy/ranged : /mob/living/basic/mimic/copy/ranged{@OLD}