From 7caf75b37ba6c09114a1bab141baddbed7c09f9b Mon Sep 17 00:00:00 2001 From: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Date: Mon, 22 Sep 2025 09:11:44 -0400 Subject: [PATCH] Converts mice to basic mobs (#29928) * Converts mice to basic mobs * Larger only targetting strat * Oops. * Linters * Code review suggestions * Linters * Merge conflict resolution * Snakes * Oops * Clogged Disposals * Updates persistent client * Linters --------- Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com> Co-authored-by: warriorstar-orion --- _maps/map_files/stations/boxstation.dmm | 4 +- _maps/map_files/stations/cerestation.dmm | 4 +- _maps/map_files/stations/deltastation.dmm | 4 +- _maps/map_files/stations/emeraldstation.dmm | 4 +- _maps/map_files/stations/metastation.dmm | 2 +- code/__DEFINES/mob_defines.dm | 2 +- .../subsystem/non_firing/SSlate_mapping.dm | 4 +- .../hunting_behavior/hunting_mouse.dm | 41 +++ .../basic_targeting_strategy.dm | 31 ++ .../pet_commands/pet_commands_basic.dm | 13 + code/datums/components/tameable.dm | 68 ++++ .../spell_targeting/matter_eater_targeting.dm | 2 +- code/datums/spells/shapeshift.dm | 2 +- code/game/verbs/suicide.dm | 2 +- code/modules/assembly/mousetrap.dm | 2 +- code/modules/events/blob_spawn.dm | 4 +- code/modules/events/clogged_disposals.dm | 2 +- code/modules/events/infestation.dm | 2 +- code/modules/food_and_drinks/food_base.dm | 2 +- .../mining/lavaland/loot/colossus_loot.dm | 2 +- .../mob/living/basic/friendly/mouse.dm | 309 ++++++++++++++++++ .../mob/living/basic/friendly/snake.dm | 2 +- .../living/carbon/human/species/tajaran.dm | 8 +- .../mob/living/carbon/human/species/unathi.dm | 12 +- .../living/carbon/human/species/vulpkanin.dm | 12 +- .../mob/living/simple_animal/friendly/cat.dm | 6 +- .../simple_animal/friendly/friendly_emote.dm | 4 +- .../living/simple_animal/friendly/mouse.dm | 245 -------------- code/modules/mob/mob.dm | 2 +- code/modules/mob/mob_misc_procs.dm | 6 + .../projectile/magic_projectiles.dm | 2 +- code/modules/research/strange_objects.dm | 4 +- paradise.dme | 4 +- tools/ci/check_simplemob_additions.py | 8 - 34 files changed, 530 insertions(+), 291 deletions(-) create mode 100644 code/datums/ai/basic_mobs/hunting_behavior/hunting_mouse.dm create mode 100644 code/datums/components/tameable.dm create mode 100644 code/modules/mob/living/basic/friendly/mouse.dm delete mode 100644 code/modules/mob/living/simple_animal/friendly/mouse.dm diff --git a/_maps/map_files/stations/boxstation.dmm b/_maps/map_files/stations/boxstation.dmm index ad0a08b187f..66730154a33 100644 --- a/_maps/map_files/stations/boxstation.dmm +++ b/_maps/map_files/stations/boxstation.dmm @@ -14423,7 +14423,7 @@ }, /area/station/security/checkpoint/secondary) "aZi" = ( -/mob/living/simple_animal/mouse/brown/tom, +/mob/living/basic/mouse/brown/tom, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "vault" @@ -21783,7 +21783,7 @@ /obj/machinery/atmospherics/pipe/simple/hidden/cyan{ dir = 4 }, -/mob/living/simple_animal/mouse/gray, +/mob/living/basic/mouse/gray, /turf/simulated/floor/plasteel, /area/station/maintenance/port) "bwx" = ( diff --git a/_maps/map_files/stations/cerestation.dmm b/_maps/map_files/stations/cerestation.dmm index 7a1adc0812a..74465b3b17a 100644 --- a/_maps/map_files/stations/cerestation.dmm +++ b/_maps/map_files/stations/cerestation.dmm @@ -29487,7 +29487,7 @@ /obj/structure/bed/dogbed{ name = "Brain's bed" }, -/mob/living/simple_animal/mouse/white/brain, +/mob/living/basic/mouse/white/brain, /obj/machinery/economy/vending/wallmed/directional/north, /turf/simulated/floor/plasteel{ dir = 1; @@ -90544,7 +90544,7 @@ "rRO" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, -/mob/living/simple_animal/mouse/brown/tom, +/mob/living/basic/mouse/brown/tom, /obj/machinery/hologram/holopad, /turf/simulated/floor/plasteel{ icon_state = "vault" diff --git a/_maps/map_files/stations/deltastation.dmm b/_maps/map_files/stations/deltastation.dmm index cdeb49c7208..260a601287c 100644 --- a/_maps/map_files/stations/deltastation.dmm +++ b/_maps/map_files/stations/deltastation.dmm @@ -81451,7 +81451,7 @@ /area/station/security/prisonershuttle) "rbC" = ( /obj/structure/bed/dogbed, -/mob/living/simple_animal/mouse/white/brain, +/mob/living/basic/mouse/white/brain, /turf/simulated/floor/plasteel{ icon_state = "whitegreen" }, @@ -86795,7 +86795,7 @@ c_tag = "Vault"; dir = 4 }, -/mob/living/simple_animal/mouse/brown/tom, +/mob/living/basic/mouse/brown/tom, /turf/simulated/floor/plasteel/dark, /area/station/command/vault) "tdA" = ( diff --git a/_maps/map_files/stations/emeraldstation.dmm b/_maps/map_files/stations/emeraldstation.dmm index 7c72f269540..d94f69df2d1 100644 --- a/_maps/map_files/stations/emeraldstation.dmm +++ b/_maps/map_files/stations/emeraldstation.dmm @@ -895,7 +895,7 @@ /turf/simulated/floor/plating, /area/station/maintenance/aft2) "alr" = ( -/mob/living/simple_animal/mouse/brown/tom, +/mob/living/basic/mouse/brown/tom, /turf/simulated/floor/greengrid, /area/station/command/vault) "alG" = ( @@ -17504,7 +17504,7 @@ /area/station/maintenance/security/fore) "dut" = ( /obj/structure/bed/dogbed, -/mob/living/simple_animal/mouse/white/brain, +/mob/living/basic/mouse/white/brain, /obj/machinery/atmospherics/pipe/manifold/hidden/supply, /obj/structure/disposalpipe/segment{ dir = 4 diff --git a/_maps/map_files/stations/metastation.dmm b/_maps/map_files/stations/metastation.dmm index ea3034f6fcb..b051012f024 100644 --- a/_maps/map_files/stations/metastation.dmm +++ b/_maps/map_files/stations/metastation.dmm @@ -42110,7 +42110,7 @@ /turf/simulated/floor/plating, /area/station/engineering/atmos/control) "gRu" = ( -/mob/living/simple_animal/mouse/brown/tom, +/mob/living/basic/mouse/brown/tom, /obj/structure/sink/directional/north, /turf/simulated/floor/plating, /area/station/maintenance/fore2) diff --git a/code/__DEFINES/mob_defines.dm b/code/__DEFINES/mob_defines.dm index ef83e5fd368..13acb49f0bf 100644 --- a/code/__DEFINES/mob_defines.dm +++ b/code/__DEFINES/mob_defines.dm @@ -248,7 +248,7 @@ #define isdog(A) (istype((A), /mob/living/simple_animal/pet/dog)) #define iscorgi(A) (istype((A), /mob/living/simple_animal/pet/dog/corgi)) -#define ismouse(A) (istype((A), /mob/living/simple_animal/mouse)) +#define ismouse(A) (istype((A), /mob/living/basic/mouse)) #define isbot(A) (istype((A), /mob/living/simple_animal/bot)) #define isguardian(A) (istype((A), /mob/living/simple_animal/hostile/guardian)) #define isnymph(A) (istype((A), /mob/living/simple_animal/diona)) diff --git a/code/controllers/subsystem/non_firing/SSlate_mapping.dm b/code/controllers/subsystem/non_firing/SSlate_mapping.dm index bf7e3b1b4ef..92b3a1020c1 100644 --- a/code/controllers/subsystem/non_firing/SSlate_mapping.dm +++ b/code/controllers/subsystem/non_firing/SSlate_mapping.dm @@ -67,8 +67,8 @@ SUBSYSTEM_DEF(late_mapping) for(var/i in 1 to mice_number) if(prob(1)) - new /mob/living/simple_animal/mouse/white/linter(pick_n_take(maintenance_turfs)) + new /mob/living/basic/mouse/white/linter(pick_n_take(maintenance_turfs)) else - new /mob/living/simple_animal/mouse(pick_n_take(maintenance_turfs)) + new /mob/living/basic/mouse(pick_n_take(maintenance_turfs)) log_debug("Spawned [mice_number] mice over in [stop_watch(watch)]s") diff --git a/code/datums/ai/basic_mobs/hunting_behavior/hunting_mouse.dm b/code/datums/ai/basic_mobs/hunting_behavior/hunting_mouse.dm new file mode 100644 index 00000000000..4fe86a44fdd --- /dev/null +++ b/code/datums/ai/basic_mobs/hunting_behavior/hunting_mouse.dm @@ -0,0 +1,41 @@ +// Mouse subtree to hunt down delicious cheese. +/datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese + finding_behavior = /datum/ai_behavior/find_hunt_target/mouse + hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/mouse + hunt_targets = list(/obj/item/food/sliced/cheesewedge) + hunt_range = 1 + finish_planning = FALSE + +// Mouse subtree to hunt down ... delicious cabling? +/datum/ai_planning_subtree/find_and_hunt_target/look_for_cables + target_key = BB_LOW_PRIORITY_HUNTING_TARGET + hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/mouse + finding_behavior = /datum/ai_behavior/find_hunt_target/mouse_cable + hunt_targets = list(/obj/structure/cable) + hunt_range = 1 + hunt_chance = 1 + finish_planning = FALSE + +/datum/ai_behavior/find_hunt_target/mouse + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +// When looking for a cable, we can only bite things we can reach. +/datum/ai_behavior/find_hunt_target/mouse_cable + +/datum/ai_behavior/find_hunt_target/mouse_cable/valid_dinner(mob/living/source, obj/structure/cable/dinner, radius) + . = ..() + if(!.) + return + + var/turf/simulated/floor/F = get_turf(dinner) + if(F.intact || F.transparent_floor) + return FALSE + var/obj/structure/cable/C = locate() in F + if(C) + return TRUE + return FALSE + +// Our hunts have a decent cooldown. +/datum/ai_behavior/hunt_target/interact_with_target/mouse + hunt_cooldown = 20 SECONDS + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION diff --git a/code/datums/ai/targeting_strategy/basic_targeting_strategy.dm b/code/datums/ai/targeting_strategy/basic_targeting_strategy.dm index ef80c889f4b..3d0369c7141 100644 --- a/code/datums/ai/targeting_strategy/basic_targeting_strategy.dm +++ b/code/datums/ai/targeting_strategy/basic_targeting_strategy.dm @@ -95,3 +95,34 @@ /datum/targeting_strategy/basic/always_check_factions check_factions_exactly = TRUE + +/// Subtype which searches for mobs of a size relative to ours +/datum/targeting_strategy/basic/of_size + /// If true, we will return mobs which are smaller than us. If false, larger. + var/find_smaller = TRUE + /// If true, we will return mobs which are the same size as us. + var/inclusive = TRUE + +/datum/targeting_strategy/basic/of_size/can_attack(mob/living/owner, atom/target, vision_range) + if(!isliving(target)) + return FALSE + . = ..() + if(!.) + return FALSE + + var/mob/living/mob_target = target + if(inclusive && owner.mob_size == mob_target.mob_size) + return TRUE + if(owner.mob_size > mob_target.mob_size) + return find_smaller + return !find_smaller + +// This is just using the default values but the subtype makes it clearer +/datum/targeting_strategy/basic/of_size/ours_or_smaller + +/datum/targeting_strategy/basic/of_size/larger + find_smaller = FALSE + inclusive = FALSE + +/datum/targeting_strategy/basic/of_size/smaller + inclusive = FALSE diff --git a/code/datums/components/pet_commands/pet_commands_basic.dm b/code/datums/components/pet_commands/pet_commands_basic.dm index 3017cd731e9..741877bf1b5 100644 --- a/code/datums/components/pet_commands/pet_commands_basic.dm +++ b/code/datums/components/pet_commands/pet_commands_basic.dm @@ -43,6 +43,8 @@ speech_commands = list("heel", "follow") ///the behavior we use to follow var/follow_behavior = /datum/ai_behavior/pet_follow_friend + /// should we activate immediately if we're doing nothing else and gain a friend? + var/activate_on_befriend = FALSE /datum/pet_command/follow/set_command_active(mob/living/parent, mob/living/commander) . = ..() @@ -55,6 +57,17 @@ controller.queue_behavior(follow_behavior, BB_CURRENT_PET_TARGET) return SUBTREE_RETURN_FINISH_PLANNING +/datum/pet_command/follow/add_new_friend(mob/living/tamer) + . = ..() + var/mob/living/parent = locateUID(parent_uid) + if(!parent) + return + if(activate_on_befriend && !parent.ai_controller.blackboard_key_exists(BB_ACTIVE_PET_COMMAND)) + try_activate_command(tamer) + +/// Like follow but start active +/datum/pet_command/follow/start_active + activate_on_befriend = TRUE /** * # Pet Command: Use ability * Use an an ability that does not require any targets diff --git a/code/datums/components/tameable.dm b/code/datums/components/tameable.dm new file mode 100644 index 00000000000..b32f5dd74c3 --- /dev/null +++ b/code/datums/components/tameable.dm @@ -0,0 +1,68 @@ +/// This component lets you make specific mobs tameable by feeding them +/datum/component/tameable + /// If true, this atom can only be domesticated by one person + var/unique + /// Starting success chance for taming. + var/tame_chance + /// Added success chance after every failed tame attempt. + var/bonus_tame_chance + /// Current chance to tame on interaction + var/current_tame_chance + +/datum/component/tameable/Initialize(food_types, tame_chance, bonus_tame_chance, unique = TRUE) + if(!ismob(parent)) + return COMPONENT_INCOMPATIBLE + + if(tame_chance) + src.tame_chance = tame_chance + src.current_tame_chance = tame_chance + if(bonus_tame_chance) + src.bonus_tame_chance = bonus_tame_chance + src.unique = unique + + if(food_types && !HAS_TRAIT(parent, TRAIT_MOB_EATER)) + parent.AddElement(/datum/element/basic_eating, food_types_ = food_types) + + RegisterSignal(parent, COMSIG_MOB_ATE, PROC_REF(try_tame)) + +/datum/component/tameable/proc/try_tame(atom/source, obj/item/food, mob/living/attacker) + SIGNAL_HANDLER + + if(isnull(attacker) || already_friends(attacker)) + return + + var/inform_tamer = FALSE + if(prob(current_tame_chance)) // note: lack of feedback message is deliberate, keep them guessing unless they're an expert! + on_tame(source, attacker, food, inform_tamer) + else + current_tame_chance += bonus_tame_chance + +/// Check if the passed mob is already considered one of our friends +/datum/component/tameable/proc/already_friends(mob/living/potential_friend) + if(!isliving(parent)) + return FALSE // Figure this out when we actually need it + var/mob/living/living_parent = parent + var/living_parent_id = living_parent.UID() + return living_parent.faction.Find(living_parent_id) + +/// Ran once taming succeeds +/datum/component/tameable/proc/on_tame(mob/source, mob/living/tamer, obj/item/food, inform_tamer = FALSE) + SIGNAL_HANDLER + source.tamed(tamer, food)//Run custom behavior if needed + + if(isliving(parent) && isliving(tamer)) + INVOKE_ASYNC(source, TYPE_PROC_REF(/mob/living, befriend), tamer) + if(unique) + qdel(src) + else + current_tame_chance = tame_chance + +/datum/component/tameable/proc/rename_pet(mob/living/animal, mob/living/tamer) + var/chosen_name = tgui_input_text(tamer, "Choose your pet's name!", "Name pet", animal.name, MAX_NAME_LEN) + if(QDELETED(animal) || chosen_name == animal.name) + return + if(!chosen_name) + to_chat(tamer, "Please enter a valid name.") + rename_pet(animal, tamer) + return + animal.rename_character(animal.name, chosen_name) diff --git a/code/datums/spell_targeting/matter_eater_targeting.dm b/code/datums/spell_targeting/matter_eater_targeting.dm index bb7efb91d0c..b739c1a2930 100644 --- a/code/datums/spell_targeting/matter_eater_targeting.dm +++ b/code/datums/spell_targeting/matter_eater_targeting.dm @@ -9,7 +9,7 @@ /mob/living/simple_animal/hostile, /mob/living/simple_animal/parrot, /mob/living/simple_animal/crab, - /mob/living/simple_animal/mouse, + /mob/living/basic/mouse, /mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/carbon/alien/larva, diff --git a/code/datums/spells/shapeshift.dm b/code/datums/spells/shapeshift.dm index fee6e1197c3..ac3e5004acc 100644 --- a/code/datums/spells/shapeshift.dm +++ b/code/datums/spells/shapeshift.dm @@ -11,7 +11,7 @@ var/shapeshift_type var/list/current_shapes = list() var/list/current_casters = list() - var/list/possible_shapes = list(/mob/living/simple_animal/mouse, + var/list/possible_shapes = list(/mob/living/basic/mouse, /mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/bot/ed209, /mob/living/simple_animal/hostile/construct/armoured) diff --git a/code/game/verbs/suicide.dm b/code/game/verbs/suicide.dm index f6b6fb203b7..51919c640a5 100644 --- a/code/game/verbs/suicide.dm +++ b/code/game/verbs/suicide.dm @@ -52,7 +52,7 @@ /mob/living/simple_animal/do_suicide() setOxyLoss((health * 1.5), TRUE) -/mob/living/simple_animal/mouse/do_suicide() +/mob/living/basic/mouse/do_suicide() visible_message("[src] is playing dead permanently! It looks like [p_theyre()] trying to commit suicide!") adjustOxyLoss(max(100 - getBruteLoss(100), 0)) diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 9f53b74059b..b9d77676226 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -66,7 +66,7 @@ affecting.receive_damage(1, 0) else if(ismouse(target)) - var/mob/living/simple_animal/mouse/M = target + var/mob/living/basic/mouse/M = target visible_message("SPLAT!") M.death() M.splat() diff --git a/code/modules/events/blob_spawn.dm b/code/modules/events/blob_spawn.dm index ec180703ab7..2bb5b87697c 100644 --- a/code/modules/events/blob_spawn.dm +++ b/code/modules/events/blob_spawn.dm @@ -13,7 +13,7 @@ INVOKE_ASYNC(src, PROC_REF(make_blob)) /datum/event/blob/proc/make_blob() - var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/simple_animal/mouse/blobinfected) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/basic/mouse/blobinfected) if(!length(candidates)) return kill() @@ -22,7 +22,7 @@ message_admins("Warning: No suitable vents detected for spawning blob mouse. Force picking from station vents regardless of state!") vents = get_valid_vent_spawns(unwelded_only = FALSE, min_network_size = 0) var/obj/vent = pick(vents) - var/mob/living/simple_animal/mouse/blobinfected/B = new(vent.loc) + var/mob/living/basic/mouse/blobinfected/B = new(vent.loc) var/mob/M = pick(candidates) B.key = M.key dust_if_respawnable(M) diff --git a/code/modules/events/clogged_disposals.dm b/code/modules/events/clogged_disposals.dm index 21812e8c5f8..9d49565efa1 100644 --- a/code/modules/events/clogged_disposals.dm +++ b/code/modules/events/clogged_disposals.dm @@ -48,7 +48,7 @@ /mob/living/basic/clown/goblin = 5, /mob/living/basic/goat = 5, // Friendly - /mob/living/simple_animal/mouse = 5, + /mob/living/basic/mouse = 5, /mob/living/simple_animal/diona = 5, /mob/living/simple_animal/crab = 5, /mob/living/simple_animal/pet/dog/corgi = 5, diff --git a/code/modules/events/infestation.dm b/code/modules/events/infestation.dm index 7ddea7436b6..76f45a2ce0b 100644 --- a/code/modules/events/infestation.dm +++ b/code/modules/events/infestation.dm @@ -53,7 +53,7 @@ vermin = rand(0, 2) switch(vermin) if(VERM_MICE) - spawn_types = list(/mob/living/simple_animal/mouse/gray, /mob/living/simple_animal/mouse/brown, /mob/living/simple_animal/mouse/white) + spawn_types = list(/mob/living/basic/mouse/gray, /mob/living/basic/mouse/brown, /mob/living/basic/mouse/white) max_number = 12 vermstring = "mice" if(VERM_LIZARDS) diff --git a/code/modules/food_and_drinks/food_base.dm b/code/modules/food_and_drinks/food_base.dm index 6e218d8a79c..e49cbbf4230 100644 --- a/code/modules/food_and_drinks/food_base.dm +++ b/code/modules/food_and_drinks/food_base.dm @@ -269,7 +269,7 @@ D.last_eaten = world.time D.taste(reagents) else if(ismouse(M)) - var/mob/living/simple_animal/mouse/N = M + var/mob/living/basic/mouse/N = M to_chat(N, "You nibble away at [src].") if(prob(50)) N.visible_message("[N] nibbles away at [src].", "") diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm index d47a2e511a2..8f721222a35 100644 --- a/code/modules/mining/lavaland/loot/colossus_loot.dm +++ b/code/modules/mining/lavaland/loot/colossus_loot.dm @@ -237,7 +237,7 @@ mobcheck = 1 break if(!mobcheck) - new /mob/living/simple_animal/mouse(get_step(src,dir)) //Just in case there aren't any animals on the station, this will leave you with a terrible option to possess if you feel like it + new /mob/living/basic/mouse(get_step(src,dir)) //Just in case there aren't any animals on the station, this will leave you with a terrible option to possess if you feel like it /obj/structure/closet/stasis name = "quantum entanglement stasis warp field" diff --git a/code/modules/mob/living/basic/friendly/mouse.dm b/code/modules/mob/living/basic/friendly/mouse.dm new file mode 100644 index 00000000000..33f916d35c9 --- /dev/null +++ b/code/modules/mob/living/basic/friendly/mouse.dm @@ -0,0 +1,309 @@ +/mob/living/basic/mouse + name = "mouse" + real_name = "mouse" + desc = "It's a small, disease-ridden rodent." + icon_state = "mouse_gray" + icon_living = "mouse_gray" + icon_dead = "mouse_gray_dead" + icon_resting = "mouse_gray_sleep" + speak_emote = list("squeeks","squeaks","squiks") + var/squeak_sound = 'sound/creatures/mousesqueak.ogg' + ai_controller = /datum/ai_controller/basic_controller/mouse + see_in_dark = 6 + maxHealth = 5 + health = 5 + butcher_results = list(/obj/item/food/meat = 1) + response_help_continuous = "pets" + response_help_simple = "pet" + response_disarm_continuous = "gently pushes aside" + response_disarm_simple = "gently push aside" + response_harm_continuous = "stamps on" + response_harm_simple = "stamp on" + faction = list("mouse") + density = FALSE + ventcrawler = VENTCRAWLER_ALWAYS + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_biotypes = MOB_ORGANIC | MOB_BEAST + mob_size = MOB_SIZE_TINY + var/mouse_color // brown, gray and white, leave blank for random + atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) + minimum_survivable_temperature = 223 // Below -50 Degrees Celcius + maximum_survivable_temperature = 323 // Above 50 Degrees Celcius + can_hide = TRUE + pass_door_while_hidden = TRUE + holder_type = /obj/item/holder/mouse + gold_core_spawnable = FRIENDLY_SPAWN + /// Probability that, if we successfully bite a shocked cable, that we will die to it. + var/cable_zap_prob = 85 + /// Food types that mice eat + var/static/list/food_types = list(/obj/item/food/sliced/cheesewedge, /obj/item/food/sliceable/cheesewheel) + +/mob/living/basic/mouse/Initialize(mapload) + . = ..() + AddElement(/datum/element/wears_collar) + AddComponent(/datum/component/squeak, list(squeak_sound = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) // as quiet as a mouse or whatever + AddComponent(/datum/component/swarming, 16, 16) // max_x, max_y + if(!mouse_color) + mouse_color = pick("brown", "gray", "white") + icon_state = "mouse_[mouse_color]" + icon_living = "mouse_[mouse_color]" + icon_dead = "mouse_[mouse_color]_dead" + icon_resting = "mouse_[mouse_color]_sleep" + update_appearance(UPDATE_ICON_STATE|UPDATE_DESC) + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) + ) + AddElement(/datum/element/connect_loc, loc_connections) + AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 100) + +/mob/living/basic/mouse/update_desc() + . = ..() + desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." + +/mob/living/basic/mouse/attack_hand(mob/living/carbon/human/M as mob) + if(M.a_intent == INTENT_HELP) + get_scooped(M, TRUE) + ..() + +/mob/living/basic/mouse/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) + . = ..() + if(!.) + return + + if(!proximity_flag) + return + + if(istype(attack_target, /obj/item/food/sliced/cheesewedge)) + try_consume_cheese(attack_target) + return TRUE + + if(istype(attack_target, /obj/structure/cable)) + try_bite_cable(attack_target) + return TRUE + +/// Signal proc for [COMSIG_ATOM_ENTERED]. Sends a lil' squeak to chat when someone walks over us. +/mob/living/basic/mouse/proc/on_entered(datum/source, atom/movable/entered) + SIGNAL_HANDLER // COMSIG_ATOM_ENTERED + + if(ishuman(entered) && stat == CONSCIOUS) + to_chat(entered, "Squeak!") + +/// Called when a mouse is hand-fed some cheese, it will stop being afraid of humans +/mob/living/basic/mouse/tamed(mob/living/tamer, obj/item/food/sliced/cheesewedge/cheese) + new /obj/effect/temp_visual/heart(loc) + faction |= "neutral" + try_consume_cheese(cheese) + ai_controller.cancel_actions() // Interrupt any current fleeing + +/mob/living/basic/mouse/proc/try_consume_cheese(obj/item/food/sliced/cheesewedge/cheese) + if(prob(90) || health < maxHealth) + visible_message( + "[src] nibbles [cheese].", + "You nibble [cheese][health < maxHealth ? ", restoring your health" : ""]." + ) + adjustHealth(-maxHealth) + else + visible_message( + "[src] nibbles through [cheese], attracting another mouse!", + "You nibble through [cheese], attracting another mouse!" + ) + new /mob/living/basic/mouse(loc) + qdel(cheese) + +/// Biting into a cable will cause a mouse to get shocked and die if applicable. Or do nothing if they're lucky. +/mob/living/basic/mouse/proc/try_bite_cable(obj/structure/cable/cable) + if(!prob(cable_zap_prob)) + return + var/turf/simulated/floor/F = get_turf(cable) + if(cable.get_available_power() && !HAS_TRAIT(src, TRAIT_SHOCKIMMUNE)) + visible_message("[src] chews through [cable]. It's toast!") + playsound(src, 'sound/effects/sparks2.ogg', 100, 1) + toast() // mmmm toasty. + else + visible_message("[src] chews through [cable].") + investigate_log("was chewed through by a mouse in [get_area(F)]([F.x], [F.y], [F.z] - [ADMIN_JMP(F)])",INVESTIGATE_WIRES) + cable.deconstruct() + +/mob/living/basic/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)// Prevents mouse from pulling things + if(istype(AM, /obj/item/food/sliced/cheesewedge)) + return ..() // Get dem + if(show_message) + to_chat(src, "You are too small to pull anything except cheese.") + return + +/mob/living/basic/mouse/proc/on_atom_entered(datum/source, atom/movable/entered) + if(ishuman(entered)) + if(stat == CONSCIOUS) + var/mob/M = entered + to_chat(M, "[bicon(src)] Squeek!") + +/mob/living/basic/mouse/proc/toast() + add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) + desc = "It's toast." + death() + +/mob/living/basic/mouse/proc/splat() + icon_dead = "mouse_[mouse_color]_splat" + icon_state = "mouse_[mouse_color]_splat" + +/mob/living/basic/mouse/death(gibbed) + // Only execute the below if we successfully died + playsound(src, squeak_sound, 40, 1) + . = ..(gibbed) + if(!.) + return FALSE + layer = MOB_LAYER + if(client) + client.persistent.time_died_as_mouse = world.time + +/* + * Mouse types + */ + +/mob/living/basic/mouse/white + mouse_color = "white" + icon_state = "mouse_white" + +/mob/living/basic/mouse/white/linter + name = "Linter" + +/mob/living/basic/mouse/white/linter/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_SHOCKIMMUNE, ROUNDSTART_TRAIT) + +/mob/living/basic/mouse/gray + mouse_color = "gray" + +/mob/living/basic/mouse/brown + mouse_color = "brown" + icon_state = "mouse_brown" + +// TOM IS ALIVE! SQUEEEEEEEE~K :) +/mob/living/basic/mouse/brown/tom + name = "Tom" + real_name = "Tom" + gold_core_spawnable = NO_SPAWN + +/mob/living/basic/mouse/brown/tom/update_desc() + . = ..() + desc = "Jerry the cat is not amused." + +/mob/living/basic/mouse/brown/tom/Initialize(mapload) + . = ..() + // Tom fears no cable. + ADD_TRAIT(src, TRAIT_SHOCKIMMUNE, SPECIES_TRAIT) + +/mob/living/basic/mouse/white/brain + name = "Brain" + real_name = "Brain" + gold_core_spawnable = NO_SPAWN + +/mob/living/basic/mouse/white/brain/update_desc() + . = ..() + desc = "Gee Virology, what are we going to do tonight? The same thing we do every night, try to take over the world!" + +// Boolog +/mob/living/basic/mouse/blobinfected + maxHealth = 100 + health = 100 + 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) + minimum_survivable_temperature = 0 + gold_core_spawnable = NO_SPAWN + var/bursted = FALSE + +/mob/living/basic/mouse/blobinfected/Initialize(mapload) + . = ..() + apply_status_effect(STATUS_EFFECT_BLOB_BURST, 120 SECONDS, CALLBACK(src, PROC_REF(burst), FALSE)) + +/mob/living/basic/mouse/blobinfected/Life() + if(ismob(loc)) // if someone ate it, burst immediately + burst(FALSE) + return ..() + +/mob/living/basic/mouse/blobinfected/death(gibbed) + . = ..(gibbed) + if(.) // Only burst if they actually died + burst(gibbed) + +/mob/living/basic/mouse/blobinfected/proc/burst(gibbed) + if(bursted) + return // Avoids double bursting in some situations + bursted = TRUE + var/turf/T = get_turf(src) + if(!is_station_level(T.z) || isspaceturf(T)) + to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") + apply_status_effect(STATUS_EFFECT_BLOB_BURST, 5 SECONDS, CALLBACK(src, PROC_REF(burst), FALSE)) + return FALSE + var/datum/mind/blobmind = mind + var/client/C = client + var/obj/structure/blob/core/core + if(istype(blobmind) && istype(C)) + core = new(T, C, 3) + core.lateblobtimer() + qdel(blobmind) // Delete the old mind. THe blob will make a new one + else + core = new(T) // Ghosts will be prompted to control it. + if(ismob(loc)) // in case some taj/etc ate the mouse. + var/mob/M = loc + M.gib() + if(!gibbed) + gib() + + if(core) + core.admin_spawned = admin_spawned + + SSticker.record_biohazard_start(BIOHAZARD_BLOB) + +/mob/living/basic/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) + to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") + to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") + +/mob/living/basic/mouse/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(!isdrone(user)) + user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \ + "It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + C.stored_comms["metal"] += 2 + C.stored_comms["glass"] += 1 + qdel(src) + return TRUE + return ..() + +/// The mouse AI controller +/datum/ai_controller/basic_controller/mouse + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/larger, // Use this to find people to run away from + BB_BASIC_MOB_FLEE_DISTANCE = 3, + ) + + ai_traits = AI_FLAG_STOP_MOVING_WHEN_PULLED + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + planning_subtrees = list( + // Try to speak, because it's cute + /datum/ai_planning_subtree/random_speech/mouse, + // Look for and execute hunts for cheese even if someone is looking at us + /datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese, + // Next priority is see if anyone is looking at us + /datum/ai_planning_subtree/simple_find_nearest_target_to_flee, + // Skedaddle + /datum/ai_planning_subtree/flee_target/mouse, + // Otherwise, look for and execute hunts for cabling + /datum/ai_planning_subtree/find_and_hunt_target/look_for_cables, + ) + +/// Don't look for anything to run away from if you are distracted by being adjacent to cheese +/datum/ai_planning_subtree/flee_target/mouse + +/datum/ai_planning_subtree/flee_target/mouse/select_behaviors(datum/ai_controller/controller, seconds_per_tick) + var/atom/hunted_cheese = controller.blackboard[BB_CURRENT_HUNTING_TARGET] + if(!isnull(hunted_cheese)) + return // We see some cheese, which is more important than our life + return ..() + +/datum/ai_planning_subtree/random_speech/mouse + speech_chance = 1 + speak = list("Squeak!", "SQUEAK!", "Squeak?") + sound = list('sound/creatures/mousesqueak.ogg') + emote_hear = list("squeeks.","squeaks.","squiks.") + emote_see = list("runs in a circle.", "shakes.", "scritches at something.") diff --git a/code/modules/mob/living/basic/friendly/snake.dm b/code/modules/mob/living/basic/friendly/snake.dm index 869c3d1b7a9..97df8855760 100644 --- a/code/modules/mob/living/basic/friendly/snake.dm +++ b/code/modules/mob/living/basic/friendly/snake.dm @@ -33,7 +33,7 @@ var/poison_type = "toxin" /// List of stuff (mice) that we want to eat var/static/list/edibles = list( - /mob/living/simple_animal/mouse, + /mob/living/basic/mouse, ) /mob/living/basic/snake/Initialize(mapload) diff --git a/code/modules/mob/living/carbon/human/species/tajaran.dm b/code/modules/mob/living/carbon/human/species/tajaran.dm index 7d7f4b980ac..5b5330bb54f 100644 --- a/code/modules/mob/living/carbon/human/species/tajaran.dm +++ b/code/modules/mob/living/carbon/human/species/tajaran.dm @@ -44,7 +44,13 @@ unless they choose otherwise by selecting the colourblind disability in character creation (darksight = 8 but colourblind).*/ ) - allowed_consumed_mobs = list(/mob/living/simple_animal/mouse, /mob/living/basic/chick, /mob/living/basic/butterfly, /mob/living/simple_animal/parrot, /mob/living/simple_animal/hostile/poison/bees) + allowed_consumed_mobs = list( + /mob/living/basic/mouse, + /mob/living/basic/chick, + /mob/living/basic/butterfly, + /mob/living/simple_animal/parrot, + /mob/living/simple_animal/hostile/poison/bees, + ) suicide_messages = list( "is attempting to bite their tongue off!", diff --git a/code/modules/mob/living/carbon/human/species/unathi.dm b/code/modules/mob/living/carbon/human/species/unathi.dm index f96a9b805bc..f9b4222f7fa 100644 --- a/code/modules/mob/living/carbon/human/species/unathi.dm +++ b/code/modules/mob/living/carbon/human/species/unathi.dm @@ -48,8 +48,16 @@ "eyes" = /obj/item/organ/internal/eyes/unathi //3 darksight. ) - allowed_consumed_mobs = list(/mob/living/simple_animal/mouse, /mob/living/basic/lizard, /mob/living/basic/chick, /mob/living/basic/chicken, - /mob/living/simple_animal/crab, /mob/living/basic/butterfly, /mob/living/simple_animal/parrot, /mob/living/simple_animal/hostile/poison/bees) + allowed_consumed_mobs = list( + /mob/living/basic/mouse, + /mob/living/basic/lizard, + /mob/living/basic/chick, + /mob/living/basic/chicken, + /mob/living/simple_animal/crab, + /mob/living/basic/butterfly, + /mob/living/simple_animal/parrot, + /mob/living/simple_animal/hostile/poison/bees, + ) suicide_messages = list( "is attempting to bite their tongue off!", diff --git a/code/modules/mob/living/carbon/human/species/vulpkanin.dm b/code/modules/mob/living/carbon/human/species/vulpkanin.dm index 55286796834..89c68723816 100644 --- a/code/modules/mob/living/carbon/human/species/vulpkanin.dm +++ b/code/modules/mob/living/carbon/human/species/vulpkanin.dm @@ -38,8 +38,16 @@ unless they choose otherwise by selecting the colourblind disability in character creation (darksight = 8 but colourblind).*/ ) - allowed_consumed_mobs = list(/mob/living/simple_animal/mouse, /mob/living/basic/lizard, /mob/living/basic/chick, /mob/living/basic/chicken, - /mob/living/simple_animal/crab, /mob/living/basic/butterfly, /mob/living/simple_animal/parrot, /mob/living/simple_animal/hostile/poison/bees) + allowed_consumed_mobs = list( + /mob/living/basic/mouse, + /mob/living/basic/lizard, + /mob/living/basic/chick, + /mob/living/basic/chicken, + /mob/living/simple_animal/crab, + /mob/living/basic/butterfly, + /mob/living/simple_animal/parrot, + /mob/living/simple_animal/hostile/poison/bees, + ) suicide_messages = list( "is attempting to bite their tongue off!", diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index a59ab213ee3..1cd8a0658e6 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -22,7 +22,7 @@ response_harm = "kicks" gold_core_spawnable = FRIENDLY_SPAWN var/turns_since_scan = 0 - var/mob/living/simple_animal/mouse/movement_target + var/mob/living/basic/mouse/movement_target var/eats_mice = 1 var/collar_icon_state = "cat" footstep_type = FOOTSTEP_MOB_CLAW @@ -138,7 +138,7 @@ //MICE! if(eats_mice && isturf(loc) && !incapacitated()) - for(var/mob/living/simple_animal/mouse/M in view(1, src)) + for(var/mob/living/basic/mouse/M in view(1, src)) if(!M.stat && Adjacent(M)) custom_emote(EMOTE_VISIBLE, "splats \the [M]!") M.death() @@ -168,7 +168,7 @@ movement_target = null stop_automated_movement = FALSE walk(src, 0) - for(var/mob/living/simple_animal/mouse/snack in oview(src,3)) + for(var/mob/living/basic/mouse/snack in oview(src,3)) if(isturf(snack.loc) && !snack.stat) movement_target = snack break diff --git a/code/modules/mob/living/simple_animal/friendly/friendly_emote.dm b/code/modules/mob/living/simple_animal/friendly/friendly_emote.dm index c8df5f54d75..3ce84e6cfdb 100644 --- a/code/modules/mob/living/simple_animal/friendly/friendly_emote.dm +++ b/code/modules/mob/living/simple_animal/friendly/friendly_emote.dm @@ -50,7 +50,7 @@ // Mouse /datum/emote/living/simple_animal/mouse - mob_type_allowed_typecache = list(/mob/living/simple_animal/mouse) + mob_type_allowed_typecache = list(/mob/living/basic/mouse) /datum/emote/living/simple_animal/mouse/squeak key = "squeak" @@ -60,7 +60,7 @@ emote_type = EMOTE_AUDIBLE /datum/emote/living/simple_animal/mouse/squeak/get_sound(mob/living/user) - var/mob/living/simple_animal/mouse/M = user + var/mob/living/basic/mouse/M = user return M.squeak_sound // cat diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm deleted file mode 100644 index 8c58d1974ed..00000000000 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ /dev/null @@ -1,245 +0,0 @@ -/mob/living/simple_animal/mouse - name = "mouse" - real_name = "mouse" - desc = "It's a small, disease-ridden rodent." - icon_state = "mouse_gray" - icon_living = "mouse_gray" - icon_dead = "mouse_gray_dead" - icon_resting = "mouse_gray_sleep" - speak = list("Squeek!","SQUEEK!","Squeek?") - speak_emote = list("squeeks","squeaks","squiks") - emote_hear = list("squeeks","squeaks","squiks") - emote_see = list("runs in a circle", "shakes", "scritches at something") - var/squeak_sound = 'sound/creatures/mousesqueak.ogg' - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - maxHealth = 5 - health = 5 - butcher_results = list(/obj/item/food/meat = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stamps on" - density = FALSE - ventcrawler = VENTCRAWLER_ALWAYS - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_biotypes = MOB_ORGANIC | MOB_BEAST - mob_size = MOB_SIZE_TINY - var/mouse_color //brown, gray and white, leave blank for random - atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 223 //Below -50 Degrees Celcius - maxbodytemp = 323 //Above 50 Degrees Celcius - can_hide = TRUE - pass_door_while_hidden = TRUE - holder_type = /obj/item/holder/mouse - gold_core_spawnable = FRIENDLY_SPAWN - var/chew_probability = 1 - -/mob/living/simple_animal/mouse/Initialize(mapload) - . = ..() - AddElement(/datum/element/wears_collar) - AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) //as quiet as a mouse or whatever - if(!mouse_color) - mouse_color = pick("brown", "gray", "white") - icon_state = "mouse_[mouse_color]" - icon_living = "mouse_[mouse_color]" - icon_dead = "mouse_[mouse_color]_dead" - icon_resting = "mouse_[mouse_color]_sleep" - update_appearance(UPDATE_ICON_STATE|UPDATE_DESC) - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered) - ) - AddElement(/datum/element/connect_loc, loc_connections) - -/mob/living/simple_animal/mouse/handle_automated_action() -#if defined(GAME_TESTS) || defined(MAP_TESTS) // DO NOT EAT MY CABLES DURING TESTS - return -#endif - if(!prob(chew_probability) || !isfloorturf(loc)) - return - var/turf/simulated/floor/F = get_turf(src) - if(F.intact || F.transparent_floor) - return - var/obj/structure/cable/C = locate() in F - if(!C || !prob(15)) - return - if(C.get_available_power() && !HAS_TRAIT(src, TRAIT_SHOCKIMMUNE)) - visible_message("[src] chews through [C]. It's toast!") - playsound(src, 'sound/effects/sparks2.ogg', 100, 1) - toast() // mmmm toasty. - else - visible_message("[src] chews through [C].") - investigate_log("was chewed through by a mouse in [get_area(F)]([F.x], [F.y], [F.z] - [ADMIN_JMP(F)])",INVESTIGATE_WIRES) - C.deconstruct() - -/mob/living/simple_animal/mouse/handle_automated_speech() - ..() - if(prob(speak_chance) && !incapacitated()) - playsound(src, squeak_sound, 100, 1) - -/mob/living/simple_animal/mouse/handle_automated_movement() - . = ..() - if(IS_HORIZONTAL(src)) - if(prob(1)) - stand_up() - else if(prob(5)) - custom_emote(EMOTE_AUDIBLE, "snuffles") - else if(prob(0.5)) - lay_down() - -/mob/living/simple_animal/mouse/update_desc() - . = ..() - desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." - -/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) - if(M.a_intent == INTENT_HELP) - get_scooped(M, TRUE) - ..() - -/mob/living/simple_animal/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)//Prevents mouse from pulling things - if(istype(AM, /obj/item/food/sliced/cheesewedge)) - return ..() // Get dem - if(show_message) - to_chat(src, "You are too small to pull anything except cheese.") - return - -/mob/living/simple_animal/mouse/proc/on_atom_entered(datum/source, atom/movable/entered) - if(ishuman(entered)) - if(stat == CONSCIOUS) - var/mob/M = entered - to_chat(M, "[bicon(src)] Squeek!") - -/mob/living/simple_animal/mouse/proc/toast() - add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) - desc = "It's toast." - death() - -/mob/living/simple_animal/mouse/proc/splat() - icon_dead = "mouse_[mouse_color]_splat" - icon_state = "mouse_[mouse_color]_splat" - -/mob/living/simple_animal/mouse/death(gibbed) - // Only execute the below if we successfully died - playsound(src, squeak_sound, 40, 1) - . = ..(gibbed) - if(!.) - return FALSE - layer = MOB_LAYER - if(client) - client.persistent.time_died_as_mouse = world.time - -/* - * Mouse types - */ - -/mob/living/simple_animal/mouse/white - mouse_color = "white" - icon_state = "mouse_white" - -/mob/living/simple_animal/mouse/white/linter - name = "Linter" - -/mob/living/simple_animal/mouse/white/linter/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_SHOCKIMMUNE, ROUNDSTART_TRAIT) - -/mob/living/simple_animal/mouse/gray - mouse_color = "gray" - -/mob/living/simple_animal/mouse/brown - mouse_color = "brown" - icon_state = "mouse_brown" - -//TOM IS ALIVE! SQUEEEEEEEE~K :) -/mob/living/simple_animal/mouse/brown/tom - name = "Tom" - real_name = "Tom" - response_harm = "splats" - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/mouse/brown/tom/update_desc() - . = ..() - desc = "Jerry the cat is not amused." - -/mob/living/simple_animal/mouse/brown/tom/Initialize(mapload) - . = ..() - // Tom fears no cable. - ADD_TRAIT(src, TRAIT_SHOCKIMMUNE, SPECIES_TRAIT) - -/mob/living/simple_animal/mouse/white/brain - name = "Brain" - real_name = "Brain" - response_harm = "splats" - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/mouse/white/brain/update_desc() - . = ..() - desc = "Gee Virology, what are we going to do tonight? The same thing we do every night, try to take over the world!" - -/mob/living/simple_animal/mouse/blobinfected - maxHealth = 100 - health = 100 - 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) - minbodytemp = 0 - gold_core_spawnable = NO_SPAWN - var/bursted = FALSE - -/mob/living/simple_animal/mouse/blobinfected/Initialize(mapload) - . = ..() - apply_status_effect(STATUS_EFFECT_BLOB_BURST, 120 SECONDS, CALLBACK(src, PROC_REF(burst), FALSE)) - -/mob/living/simple_animal/mouse/blobinfected/Life() - if(ismob(loc)) // if someone ate it, burst immediately - burst(FALSE) - return ..() - -/mob/living/simple_animal/mouse/blobinfected/death(gibbed) - . = ..(gibbed) - if(.) // Only burst if they actually died - burst(gibbed) - -/mob/living/simple_animal/mouse/blobinfected/proc/burst(gibbed) - if(bursted) - return // Avoids double bursting in some situations - bursted = TRUE - var/turf/T = get_turf(src) - if(!is_station_level(T.z) || isspaceturf(T)) - to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") - apply_status_effect(STATUS_EFFECT_BLOB_BURST, 5 SECONDS, CALLBACK(src, PROC_REF(burst), FALSE)) - return FALSE - var/datum/mind/blobmind = mind - var/client/C = client - var/obj/structure/blob/core/core - if(istype(blobmind) && istype(C)) - core = new(T, C, 3) - core.lateblobtimer() - qdel(blobmind) // Delete the old mind. THe blob will make a new one - else - core = new(T) // Ghosts will be prompted to control it. - if(ismob(loc)) // in case some taj/etc ate the mouse. - var/mob/M = loc - M.gib() - if(!gibbed) - gib() - - if(core) - core.admin_spawned = admin_spawned - - SSticker.record_biohazard_start(BIOHAZARD_BLOB) - -/mob/living/simple_animal/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) - to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") - to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") - -/mob/living/simple_animal/mouse/decompile_act(obj/item/matter_decompiler/C, mob/user) - if(!isdrone(user)) - user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \ - "It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.") - new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) - C.stored_comms["metal"] += 2 - C.stored_comms["glass"] += 1 - qdel(src) - return TRUE - return ..() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9e6dba11b35..b979a62c0f9 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1118,7 +1118,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ to_chat(src, "Unable to find any unwelded vents to spawn mice at.") return FALSE var/obj/vent_found = pick(found_vents) - var/mob/living/simple_animal/mouse/host = new(vent_found.loc) + var/mob/living/basic/mouse/host = new(vent_found.loc) host.ckey = src.ckey to_chat(host, "You are now a mouse, a small and fragile creature capable of scurrying through vents and under doors. Be careful who you reveal yourself to, for that will decide whether you receive cheese or death.") host.forceMove(vent_found) diff --git a/code/modules/mob/mob_misc_procs.dm b/code/modules/mob/mob_misc_procs.dm index 89b9ef156a7..e500712d61e 100644 --- a/code/modules/mob/mob_misc_procs.dm +++ b/code/modules/mob/mob_misc_procs.dm @@ -856,3 +856,9 @@ // No, I don't like it either. remove_from_all_data_huds() add_to_all_human_data_huds() + + +/// Called after the atom is 'tamed' for type-specific operations, Usually called by the tameable component but also other things. +/mob/proc/tamed(mob/living/tamer, obj/item/food) + return + diff --git a/code/modules/projectiles/projectile/magic_projectiles.dm b/code/modules/projectiles/projectile/magic_projectiles.dm index c1c97b46cc4..3a1b81efd8c 100644 --- a/code/modules/projectiles/projectile/magic_projectiles.dm +++ b/code/modules/projectiles/projectile/magic_projectiles.dm @@ -208,7 +208,7 @@ GLOBAL_LIST_INIT(wabbajack_docile_animals, list( "corgi" = /mob/living/simple_animal/pet/dog/corgi, "crab" = /mob/living/simple_animal/crab, "cat" = /mob/living/simple_animal/pet/cat, - "mouse" = /mob/living/simple_animal/mouse, + "mouse" = /mob/living/basic/mouse, "chicken" = /mob/living/basic/chicken, "cow" = /mob/living/basic/cow, "lizard" = /mob/living/basic/lizard, diff --git a/code/modules/research/strange_objects.dm b/code/modules/research/strange_objects.dm index 2215dec165f..c4080415187 100644 --- a/code/modules/research/strange_objects.dm +++ b/code/modules/research/strange_objects.dm @@ -49,7 +49,7 @@ /mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/dog/fox, - /mob/living/simple_animal/mouse, + /mob/living/basic/mouse, /mob/living/simple_animal/pet/dog/pug, /mob/living/basic/lizard, /mob/living/simple_animal/diona, @@ -131,7 +131,7 @@ /mob/living/simple_animal/crab, /mob/living/simple_animal/pet/dog/fox, /mob/living/basic/lizard, - /mob/living/simple_animal/mouse, + /mob/living/basic/mouse, /mob/living/simple_animal/pet/dog/pug, /mob/living/basic/bear/black, /mob/living/basic/bear/brown, diff --git a/paradise.dme b/paradise.dme index b46c9aaf6b1..b8bf23c60f8 100644 --- a/paradise.dme +++ b/paradise.dme @@ -500,6 +500,7 @@ #include "code\datums\ai\basic_mobs\hunting_behavior\find_and_hunt_target.dm" #include "code\datums\ai\basic_mobs\hunting_behavior\find_hunt_target.dm" #include "code\datums\ai\basic_mobs\hunting_behavior\hunt_target.dm" +#include "code\datums\ai\basic_mobs\hunting_behavior\hunting_mouse.dm" #include "code\datums\ai\basic_mobs\hunting_behavior\hunting_corpses.dm" #include "code\datums\ai\generic\find_and_set.dm" #include "code\datums\ai\generic\generic_behaviors.dm" @@ -571,6 +572,7 @@ #include "code\datums\components\sticky.dm" #include "code\datums\components\surgery_initiator.dm" #include "code\datums\components\swarming.dm" +#include "code\datums\components\tameable.dm" #include "code\datums\components\tether.dm" #include "code\datums\components\tilted.dm" #include "code\datums\components\tippable.dm" @@ -2457,6 +2459,7 @@ #include "code\modules\mob\living\basic\farm_animals\deer.dm" #include "code\modules\mob\living\basic\farm_animals\deer_ai.dm" #include "code\modules\mob\living\basic\farm_animals\pig.dm" +#include "code\modules\mob\living\basic\friendly\mouse.dm" #include "code\modules\mob\living\basic\friendly\basic_pet.dm" #include "code\modules\mob\living\basic\friendly\bunny.dm" #include "code\modules\mob\living\basic\friendly\butterfly.dm" @@ -2696,7 +2699,6 @@ #include "code\modules\mob\living\simple_animal\friendly\dog.dm" #include "code\modules\mob\living\simple_animal\friendly\fox.dm" #include "code\modules\mob\living\simple_animal\friendly\friendly_emote.dm" -#include "code\modules\mob\living\simple_animal\friendly\mouse.dm" #include "code\modules\mob\living\simple_animal\friendly\pet.dm" #include "code\modules\mob\living\simple_animal\friendly\spiderbot.dm" #include "code\modules\mob\living\simple_animal\hostile\angel_statue.dm" diff --git a/tools/ci/check_simplemob_additions.py b/tools/ci/check_simplemob_additions.py index df6dd933ac5..d9e2948cf78 100644 --- a/tools/ci/check_simplemob_additions.py +++ b/tools/ci/check_simplemob_additions.py @@ -191,14 +191,6 @@ BURNDOWN_LIST = { "/mob/living/simple_animal/hostile/winter/santa/stage_4", "/mob/living/simple_animal/hostile/winter/snowman", "/mob/living/simple_animal/hostile/winter/snowman/ranged", - "/mob/living/simple_animal/mouse", - "/mob/living/simple_animal/mouse/blobinfected", - "/mob/living/simple_animal/mouse/brown", - "/mob/living/simple_animal/mouse/brown/tom", - "/mob/living/simple_animal/mouse/gray", - "/mob/living/simple_animal/mouse/white", - "/mob/living/simple_animal/mouse/white/brain", - "/mob/living/simple_animal/mouse/white/linter", "/mob/living/simple_animal/parrot", "/mob/living/simple_animal/parrot/poly", "/mob/living/simple_animal/pet",