From 4b512f1239ebda17d74d99181ab97fcb7e81c23f Mon Sep 17 00:00:00 2001 From: jimmyl <70376633+mc-oofert@users.noreply.github.com> Date: Sun, 19 Jan 2025 22:29:49 +0100 Subject: [PATCH] makes mimics into basicmobs (#88910) ## About The Pull Request mimics are basicmobs now the only change not carried over worth mentioning is that all mimics are a consistent speed because i cant imagine a gun or object with aimbot going at you mach 2 would be very fun mimic crates had some stuff changed compared to their simple animal variant they open and close their lid when attacking (unless locked) to be like menacing or something like animals flash colors to ward off people attempting to open a nonsentient hostile crate mimic will make it lock itself (if it contains anything) and attack you mimics are a really stupid naming for these because like mimic crates pretend to be crates anything else inheriting from mimics are just used to make objects alive https://github.com/user-attachments/assets/34a733a4-45a3-409e-8a6a-b2a8c7540898 ranged mimics now use viscontents (they also keep trying to pointblank people for some reason i think thats ok though unless its a wand of fireball) ranged mimic (any ranged weapon animated by a bolt of animation) https://github.com/user-attachments/assets/c3f1d2f5-cfb8-46a9-a58c-255c53a034db ## Why It's Good For The Game fixes #85668 ## Changelog :cl: refactor: mimics (bolt of animation, malf ai Machine Override, etc) are basicmobs fix: crate mimics may now be opened /:cl: --------- Co-authored-by: Jacquerel --- ..._underground_abandoned_plasma_facility.dmm | 4 +- _maps/map_files/Birdshot/birdshot.dmm | 2 +- _maps/map_files/wawastation/wawastation.dmm | 2 +- _maps/virtual_domains/psyker_shuffle.dmm | 12 +- code/__DEFINES/ai/monsters.dm | 2 + code/_globalvars/lists/mobs.dm | 2 +- code/game/objects/items.dm | 2 +- code/game/objects/structures.dm | 2 +- code/modules/admin/verbs/spawnobjasmob.dm | 15 +- .../antagonists/malf_ai/malf_ai_modules.dm | 2 +- code/modules/mining/abandoned_crates.dm | 2 +- .../living/basic/ruin_defender/mimic/mimic.dm | 403 +++++++++++++++++ .../basic/ruin_defender/mimic/mimic_ai.dm | 86 ++++ .../mob/living/simple_animal/hostile/mimic.dm | 423 ------------------ code/modules/projectiles/gun.dm | 2 +- .../unit_tests/simple_animal_freeze.dm | 6 - tgstation.dme | 3 +- .../Scripts/88910_mimicbasicmobs.txt | 5 + 18 files changed, 521 insertions(+), 454 deletions(-) create mode 100644 code/modules/mob/living/basic/ruin_defender/mimic/mimic.dm create mode 100644 code/modules/mob/living/basic/ruin_defender/mimic/mimic_ai.dm delete mode 100644 code/modules/mob/living/simple_animal/hostile/mimic.dm create mode 100644 tools/UpdatePaths/Scripts/88910_mimicbasicmobs.txt 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}