From 7e41cd3c0b63a73257d616e91ef2f0a117daca46 Mon Sep 17 00:00:00 2001 From: Comxy Date: Sun, 5 Feb 2023 20:42:07 +0100 Subject: [PATCH] Netherworld Mobs Refractor (#73086) ## About The Pull Request This PR refactors netherworld mobs into basic mobs as best as possible. Also makes some of them run faster when they are getting damaged or deal more damage. Now the mobs might be able to keep up a little with the players. ## Why It's Good For The Game Makes the mobs have better movement and more dynamic movement. Makes the quality of these mobs better. ## Changelog :cl: add: Added new damage buffs for netherworld mobs refactor: Refactors netherworld mobs into basic mobs /:cl: --- _maps/RandomRuins/SpaceRuins/bigderelict1.dmm | 8 +- _maps/RandomZLevels/SnowCabin.dmm | 2 +- _maps/RandomZLevels/snowdin.dmm | 16 +- _maps/shuttles/whiteship_kilo.dmm | 20 +- _maps/templates/lazy_templates/wizard_den.dmm | 2 +- .../basic_ai_behaviors/basic_attacking.dm | 3 + .../basic_subtrees/simple_attack_target.dm | 3 + code/datums/components/damage_buffs.dm | 25 ++ code/datums/memory/_memory.dm | 2 +- code/game/objects/items/dice.dm | 2 +- .../objects/structures/petrified_statue.dm | 4 +- code/game/objects/structures/spawner.dm | 45 ++++ code/modules/art/statues.dm | 2 +- .../space_fauna/netherworld/blankbody.dm | 78 ++++++ .../basic/space_fauna/netherworld/creature.dm | 140 ++++++++++ .../space_fauna/netherworld/migo.dm} | 240 ++++++------------ .../space_fauna/statue}/statue.dm | 129 +++++----- code/modules/mob/living/living.dm | 2 +- code/modules/projectiles/projectile/magic.dm | 2 +- .../vatgrowing/samples/cell_lines/common.dm | 4 +- .../spells/spell_types/conjure/creatures.dm | 2 +- .../unit_tests/simple_animal_freeze.dm | 4 - tgstation.dme | 7 +- .../Scripts/73086_refractor_netherworld.txt | 4 + 24 files changed, 487 insertions(+), 259 deletions(-) create mode 100644 code/datums/components/damage_buffs.dm create mode 100644 code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm create mode 100644 code/modules/mob/living/basic/space_fauna/netherworld/creature.dm rename code/modules/mob/living/{simple_animal/hostile/netherworld.dm => basic/space_fauna/netherworld/migo.dm} (51%) rename code/modules/mob/living/{simple_animal/hostile => basic/space_fauna/statue}/statue.dm (56%) create mode 100644 tools/UpdatePaths/Scripts/73086_refractor_netherworld.txt diff --git a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm index ab5c0c6932d..38ae9c03b03 100644 --- a/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm +++ b/_maps/RandomRuins/SpaceRuins/bigderelict1.dmm @@ -617,7 +617,7 @@ /area/ruin/space/has_grav/derelictoutpost) "cC" = ( /obj/structure/alien/weeds/creature, -/mob/living/simple_animal/hostile/netherworld{ +/mob/living/basic/creature{ desc = "Awh its so sm-OH GOD WHAT THE FUCK."; health = 25; maxHealth = 25; @@ -633,7 +633,7 @@ icon_state = "trails_1"; name = "dried blood trail" }, -/mob/living/simple_animal/hostile/netherworld{ +/mob/living/basic/creature{ desc = "Awh its so sm-OH GOD WHAT THE FUCK."; health = 25; maxHealth = 25; @@ -822,7 +822,7 @@ /area/ruin/space/has_grav/derelictoutpost) "dl" = ( /obj/structure/alien/weeds/creature, -/mob/living/simple_animal/hostile/netherworld{ +/mob/living/basic/creature{ name = "Miss Tiggles" }, /turf/open/floor/iron, @@ -1476,7 +1476,7 @@ /area/ruin/space/has_grav/derelictoutpost) "RH" = ( /obj/structure/alien/weeds/creature, -/mob/living/simple_animal/hostile/netherworld{ +/mob/living/basic/creature{ desc = "Awh its so sm-OH GOD WHAT THE FUCK."; health = 25; maxHealth = 25; diff --git a/_maps/RandomZLevels/SnowCabin.dmm b/_maps/RandomZLevels/SnowCabin.dmm index e74589fd2cb..3a92cd714b5 100644 --- a/_maps/RandomZLevels/SnowCabin.dmm +++ b/_maps/RandomZLevels/SnowCabin.dmm @@ -4575,7 +4575,7 @@ desc = "It looks like fancy glitter to me."; name = "icy wind" }, -/mob/living/simple_animal/hostile/netherworld/statue{ +/mob/living/basic/statue{ desc = "Just a snowman. Just a snowman. Oh god, it's just a snowman."; faction = list("statue","mining"); health = 5000; diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm index 1b53a3221cf..34e271eb0e6 100644 --- a/_maps/RandomZLevels/snowdin.dmm +++ b/_maps/RandomZLevels/snowdin.dmm @@ -2848,7 +2848,7 @@ }, /area/awaymission/snowdin/cave/cavern) "in" = ( -/mob/living/simple_animal/hostile/netherworld/blankbody{ +/mob/living/basic/blankbody{ desc = "It's Caleb Reed, but their flesh has an ashy texture, and their face is featureless save an eerie smile."; name = "Caleb Reed" }, @@ -3546,7 +3546,7 @@ /turf/open/floor/plating, /area/awaymission/snowdin/post/garage) "jO" = ( -/mob/living/simple_animal/hostile/netherworld/blankbody{ +/mob/living/basic/blankbody{ desc = "It's Jacob Ullman, but their flesh has an ashy texture, and their face is featureless save an eerie smile."; name = "Jacob Ullman" }, @@ -6461,7 +6461,7 @@ /turf/open/floor/wood, /area/awaymission/snowdin/igloo) "rZ" = ( -/mob/living/simple_animal/hostile/netherworld/migo, +/mob/living/basic/migo, /turf/open/floor/engine/cult, /area/awaymission/snowdin/post/cavern2) "sa" = ( @@ -7491,7 +7491,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, -/mob/living/simple_animal/hostile/netherworld/migo, +/mob/living/basic/migo, /turf/open/floor/iron, /area/awaymission/snowdin/post/mining_dock) "xB" = ( @@ -8649,7 +8649,7 @@ /turf/open/misc/asteroid/snow, /area/awaymission/snowdin/cave) "BJ" = ( -/mob/living/simple_animal/hostile/netherworld/blankbody{ +/mob/living/basic/blankbody{ desc = "It's Caleb Reed, but their flesh has an ashy texture, and their face is featureless save an eerie smile."; name = "Caleb Reed" }, @@ -9757,7 +9757,7 @@ /turf/open/floor/iron, /area/awaymission/snowdin/post/mining_dock) "Hb" = ( -/mob/living/simple_animal/hostile/netherworld/migo, +/mob/living/basic/migo, /turf/open/floor/iron, /area/awaymission/snowdin/post/mining_dock) "Hc" = ( @@ -9956,7 +9956,7 @@ /area/awaymission/snowdin/post/mining_main/mechbay) "HQ" = ( /obj/effect/turf_decal/stripes/line, -/mob/living/simple_animal/hostile/netherworld/migo, +/mob/living/basic/migo, /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/awaymission/snowdin/post/mining_main/mechbay) @@ -12839,7 +12839,7 @@ /turf/open/misc/asteroid/snow, /area/awaymission/snowdin/cave) "Vd" = ( -/mob/living/simple_animal/hostile/netherworld/migo, +/mob/living/basic/migo, /turf/open/floor/iron/white, /area/awaymission/snowdin/post/mining_main/robotics) "Ve" = ( diff --git a/_maps/shuttles/whiteship_kilo.dmm b/_maps/shuttles/whiteship_kilo.dmm index f832cbe7be6..e9448ce0c67 100644 --- a/_maps/shuttles/whiteship_kilo.dmm +++ b/_maps/shuttles/whiteship_kilo.dmm @@ -40,8 +40,8 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, -/mob/living/simple_animal/hostile/netherworld/migo{ - environment_smash = 0 +/mob/living/basic/migo{ + obj_damage = 0 }, /turf/open/floor/plating, /area/shuttle/abandoned/engine) @@ -381,8 +381,8 @@ /obj/structure/chair/office/light{ dir = 4 }, -/mob/living/simple_animal/hostile/netherworld/blankbody{ - environment_smash = 0 +/mob/living/basic/blankbody{ + obj_damage = 0 }, /obj/effect/decal/cleanable/blood, /obj/item/paper/crumpled/bloody{ @@ -433,8 +433,8 @@ /obj/structure/chair{ dir = 4 }, -/mob/living/simple_animal/hostile/netherworld/migo{ - environment_smash = 0 +/mob/living/basic/migo{ + obj_damage = 0 }, /turf/open/floor/bamboo, /area/shuttle/abandoned/bar) @@ -753,8 +753,8 @@ dir = 1 }, /obj/effect/decal/cleanable/dirt, -/mob/living/simple_animal/hostile/netherworld{ - environment_smash = 0 +/mob/living/basic/creature{ + obj_damage = 0 }, /obj/effect/decal/cleanable/greenglow, /turf/open/floor/plating, @@ -1112,8 +1112,8 @@ /area/shuttle/abandoned/engine) "Wj" = ( /obj/effect/decal/cleanable/dirt, -/mob/living/simple_animal/hostile/netherworld{ - environment_smash = 0 +/mob/living/basic/creature{ + obj_damage = 0 }, /turf/open/floor/plating, /area/shuttle/abandoned/cargo) diff --git a/_maps/templates/lazy_templates/wizard_den.dmm b/_maps/templates/lazy_templates/wizard_den.dmm index 3fe8ac99751..672648de5e1 100644 --- a/_maps/templates/lazy_templates/wizard_den.dmm +++ b/_maps/templates/lazy_templates/wizard_den.dmm @@ -188,7 +188,7 @@ /area/centcom/wizard_station) "tv" = ( /obj/effect/decal/cleanable/blood/splatter, -/mob/living/simple_animal/hostile/netherworld{ +/mob/living/basic/creature{ name = "Experiment 35b" }, /turf/open/floor/grass, diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm index ad96aa4dd20..7c457d143c3 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm @@ -41,6 +41,9 @@ if(!succeeded) controller.blackboard -= target_key +/datum/ai_behavior/basic_melee_attack/average_speed + action_cooldown = 1 SECONDS + /datum/ai_behavior/basic_ranged_attack action_cooldown = 0.6 SECONDS behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm index 4231f72a8be..a68f25f9c20 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm @@ -11,6 +11,9 @@ controller.queue_behavior(melee_attack_behavior, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) return SUBTREE_RETURN_FINISH_PLANNING //we are going into battle...no distractions. +/datum/ai_planning_subtree/basic_melee_attack_subtree/average_speed + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/average_speed + //If you give this to something without the element you are a dumbass. /datum/ai_planning_subtree/basic_ranged_attack_subtree var/datum/ai_behavior/basic_ranged_attack/ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack diff --git a/code/datums/components/damage_buffs.dm b/code/datums/components/damage_buffs.dm new file mode 100644 index 00000000000..f13e8f1f64b --- /dev/null +++ b/code/datums/components/damage_buffs.dm @@ -0,0 +1,25 @@ +/** + * Attached to a mob so it can change or do actions based on the fact it got attacked. + */ +/datum/component/damage_buffs + /// Callback to a mob for health changes + var/datum/callback/health_callback + +/datum/component/damage_buffs/Initialize(datum/callback/health_callback) + if(!ismob(parent)) + return ELEMENT_INCOMPATIBLE + + src.health_callback = health_callback + parent.AddElement(/datum/element/relay_attackers) + +/datum/component/damage_buffs/RegisterWithParent() + RegisterSignal(parent, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) + +/datum/component/damage_buffs/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_ATOM_WAS_ATTACKED) + +/// Add an attacking atom to a blackboard list of things which attacked us +/datum/component/damage_buffs/proc/on_attacked(mob/victim, atom/attacker) + SIGNAL_HANDLER + + health_callback?.InvokeAsync(attacker) diff --git a/code/datums/memory/_memory.dm b/code/datums/memory/_memory.dm index b5e87d0567a..506542a3bbd 100644 --- a/code/datums/memory/_memory.dm +++ b/code/datums/memory/_memory.dm @@ -237,6 +237,7 @@ /mob/living/basic/pet/dog/breaddog, /mob/living/basic/pet/dog/corgi, /mob/living/basic/pet/dog/pug, + /mob/living/basic/statue, /mob/living/basic/stickman, /mob/living/basic/stickman/dog, /mob/living/simple_animal/butterfly, @@ -256,7 +257,6 @@ /mob/living/simple_animal/hostile/megafauna/dragon/lesser, /mob/living/simple_animal/hostile/morph, /mob/living/simple_animal/hostile/mushroom, - /mob/living/simple_animal/hostile/netherworld/statue, /mob/living/simple_animal/hostile/retaliate/bat, /mob/living/simple_animal/hostile/retaliate/goat, /mob/living/simple_animal/parrot, diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm index 12e66f1e43a..789f6250441 100644 --- a/code/game/objects/items/dice.dm +++ b/code/game/objects/items/dice.dm @@ -351,7 +351,7 @@ for(var/direction in GLOB.alldirs) var/turf/stepped_turf = get_step(get_turf(user), direction) do_sparks(3, FALSE, stepped_turf) - new /mob/living/simple_animal/hostile/netherworld(stepped_turf) + new /mob/living/basic/creature(stepped_turf) if(4) //Destroy Equipment selected_turf.visible_message(span_userdanger("Everything [user] is holding and wearing disappears!")) diff --git a/code/game/objects/structures/petrified_statue.dm b/code/game/objects/structures/petrified_statue.dm index 9314544cbf4..d7860c610bd 100644 --- a/code/game/objects/structures/petrified_statue.dm +++ b/code/game/objects/structures/petrified_statue.dm @@ -43,8 +43,8 @@ /obj/structure/statue/petrified/Destroy() - if(istype(src.loc, /mob/living/simple_animal/hostile/netherworld/statue)) - var/mob/living/simple_animal/hostile/netherworld/statue/S = src.loc + if(istype(src.loc, /mob/living/basic/statue)) + var/mob/living/basic/statue/S = src.loc forceMove(S.loc) if(S.mind) if(petrified_mob) diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index e6b9f344d72..e1f17470ef1 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -92,3 +92,48 @@ name = "wumborian fugu den" desc = "A den housing a nest of wumborian fugus, how do they all even fit in there?" mob_types = list(/mob/living/simple_animal/hostile/asteroid/fugu) + +/obj/structure/spawner/nether + name = "netherworld link" + desc = null //see examine() + icon_state = "nether" + max_integrity = 50 + spawn_time = 60 SECONDS + max_mobs = 15 + icon = 'icons/mob/simple/lavaland/nest.dmi' + spawn_text = "crawls through" + mob_types = list(/mob/living/basic/migo, /mob/living/basic/creature, /mob/living/basic/blankbody) + faction = list("nether") + +/obj/structure/spawner/nether/Initialize(mapload) + . = ..() + START_PROCESSING(SSprocessing, src) + +/obj/structure/spawner/nether/examine(mob/user) + . = ..() + if(isskeleton(user) || iszombie(user)) + . += "A direct link to another dimension full of creatures very happy to see you. [span_nicegreen("You can see your house from here!")]" + else + . += "A direct link to another dimension full of creatures not very happy to see you. [span_warning("Entering the link would be a very bad idea.")]" + +/obj/structure/spawner/nether/attack_hand(mob/user, list/modifiers) + . = ..() + if(isskeleton(user) || iszombie(user)) + to_chat(user, span_notice("You don't feel like going home yet...")) + else + user.visible_message(span_warning("[user] is violently pulled into the link!"), \ + span_userdanger("Touching the portal, you are quickly pulled through into a world of unimaginable horror!")) + contents.Add(user) + +/obj/structure/spawner/nether/process(delta_time) + for(var/mob/living/living_mob in contents) + if(living_mob) + playsound(src, 'sound/magic/demon_consume.ogg', 50, TRUE) + living_mob.adjustBruteLoss(60 * delta_time) + new /obj/effect/gibspawner/generic(get_turf(living_mob), living_mob) + if(living_mob.stat == DEAD) + var/mob/living/basic/blankbody/newmob = new(loc) + newmob.name = "[living_mob]" + newmob.desc = "It's [living_mob], but [living_mob.p_their()] flesh has an ashy texture, and [living_mob.p_their()] face is featureless save an eerie smile." + src.visible_message(span_warning("[living_mob] reemerges from the link!")) + qdel(living_mob) diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm index 639234f27a7..ee50d822ebb 100644 --- a/code/modules/art/statues.dm +++ b/code/modules/art/statues.dm @@ -298,7 +298,7 @@ AddElement(/datum/element/eyestab) AddElement(/datum/element/wall_engraver) //deals 200 damage to statues, meaning you can actually kill one in ~250 hits - AddElement(/datum/element/bane, target_type = /mob/living/simple_animal/hostile/netherworld/statue, damage_multiplier = 40) + AddElement(/datum/element/bane, target_type = /mob/living/basic/statue, damage_multiplier = 40) /obj/item/chisel/Destroy() prepared_block = null diff --git a/code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm b/code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm new file mode 100644 index 00000000000..69a06c2f6dc --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm @@ -0,0 +1,78 @@ +/mob/living/basic/blankbody + name = "blank body" + desc = "This looks human enough, but its flesh has an ashy texture, and it's face is featureless save an eerie smile." + icon_state = "blank-body" + icon_living = "blank-body" + icon_dead = "blank-dead" + health = 100 + maxHealth = 100 + obj_damage = 50 + melee_damage_lower = 2 + melee_damage_upper = 6 + speed = 1 + attack_verb_continuous = "punches" + attack_verb_simple = "punch" + attack_sound = 'sound/weapons/bladeslice.ogg' + attack_vis_effect = ATTACK_EFFECT_SLASH + faction = list("nether") + speak_emote = list("screams") + death_message = "falls apart into a fine dust." + unsuitable_atmos_damage = 0 + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + + ai_controller = /datum/ai_controller/basic_controller/blankbody + /// Used for mobs that get spawned in a spawner appearently. + var/datum/component/spawner/nest + +/mob/living/basic/blankbody/Initialize(mapload) + . = ..() + var/datum/callback/health_changes_callback = CALLBACK(src, PROC_REF(health_check)) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_NETHER, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 0) + AddComponent(/datum/component/damage_buffs, health_changes_callback) + +/mob/living/basic/blankbody/proc/health_check(mob/living/attacker) + if(health < maxHealth * 0.25) + health_low_behaviour() + else if (health < maxHealth * 0.5) + health_medium_behaviour() + else if (health < maxHealth * 0.75) + health_high_behaviour() + else + health_full_behaviour() + +/mob/living/basic/blankbody/proc/health_full_behaviour() + melee_damage_lower = 2 + melee_damage_upper = 6 + +/mob/living/basic/blankbody/proc/health_high_behaviour() + melee_damage_lower = 4 + melee_damage_upper = 8 + +/mob/living/basic/blankbody/proc/health_medium_behaviour() + melee_damage_lower = 8 + melee_damage_upper = 12 + +/mob/living/basic/blankbody/proc/health_low_behaviour() + melee_damage_lower = 10 + melee_damage_upper = 20 + +/mob/living/basic/blankbody/Destroy() + if(nest) + nest.spawned_mobs -= src + nest = null + return ..() + +/datum/ai_controller/basic_controller/blankbody + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/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/basic_melee_attack_subtree/average_speed, + ) diff --git a/code/modules/mob/living/basic/space_fauna/netherworld/creature.dm b/code/modules/mob/living/basic/space_fauna/netherworld/creature.dm new file mode 100644 index 00000000000..0eb46d8b95a --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/netherworld/creature.dm @@ -0,0 +1,140 @@ +/mob/living/basic/creature + name = "creature" + desc = "A sanity-destroying otherthing from the netherworld." + icon_state = "otherthing" + icon_living = "otherthing" + icon_dead = "otherthing-dead" + health = 50 + maxHealth = 50 + obj_damage = 50 + melee_damage_lower = 20 + melee_damage_upper = 30 + speed = 2 + attack_verb_continuous = "slashes" + attack_verb_simple = "slash" + gold_core_spawnable = HOSTILE_SPAWN + attack_sound = 'sound/weapons/bite.ogg' + attack_vis_effect = ATTACK_EFFECT_BITE + faction = list("nether") + speak_emote = list("screams") + death_message = "gets his head split open." + unsuitable_atmos_damage = 0 + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + + ai_controller = /datum/ai_controller/basic_controller/creature + /// Used for checking if the mob is phased or not. + var/is_phased = FALSE + /// Used for mobs that get spawned in a spawner appearently. + var/datum/component/spawner/nest + +/mob/living/basic/creature/Initialize(mapload) + . = ..() + var/datum/callback/health_changes_callback = CALLBACK(src, PROC_REF(health_check)) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_NETHER, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 0) + AddComponent(/datum/component/damage_buffs, health_changes_callback) + var/datum/action/innate/creature/teleport/teleport = new(src) + teleport.Grant(src) + +/mob/living/basic/creature/proc/health_check(mob/living/attacker) + if(health < maxHealth * 0.25) + health_low_behaviour() + else if (health < maxHealth * 0.5) + health_medium_behaviour() + else if (health < maxHealth * 0.75) + health_high_behaviour() + else + health_full_behaviour() + +/mob/living/basic/creature/proc/health_full_behaviour() + melee_damage_lower = 20 + melee_damage_upper = 30 + set_varspeed(2) + +/mob/living/basic/creature/proc/health_high_behaviour() + melee_damage_lower = 25 + melee_damage_upper = 40 + set_varspeed(1.5) + +/mob/living/basic/creature/proc/health_medium_behaviour() + melee_damage_lower = 30 + melee_damage_upper = 50 + set_varspeed(1) + +/mob/living/basic/creature/proc/health_low_behaviour() + melee_damage_lower = 35 + melee_damage_upper = 60 + set_varspeed(0.5) + +/mob/living/basic/creature/proc/can_be_seen(turf/location) + // Check for darkness + if(location?.lighting_object) + if(location.get_lumcount() < 0.1) // No one can see us in the darkness, right? + return null + + // We aren't in darkness, loop for viewers. + var/list/check_list = list(src) + if(location) + check_list += location + + // This loop will, at most, loop twice. + for(var/atom/check in check_list) + for(var/mob/living/mob_target in oview(src, 7)) // They probably cannot see us if we cannot see them... can they? + if(mob_target.client && !mob_target.is_blind() && !mob_target.has_unlimited_silicon_privilege) + return mob_target + for(var/obj/vehicle/sealed/mecha/mecha_mob_target in oview(src, 7)) + for(var/mob/mechamob_target as anything in mecha_mob_target.occupants) + if(mechamob_target.client && !mechamob_target.is_blind()) + return mechamob_target + return null + +/datum/action/innate/creature + background_icon_state = "bg_default" + overlay_icon_state = "bg_default_border" + +/datum/action/innate/creature/teleport + name = "Teleport" + desc = "Teleport to wherever you want, as long as you aren't seen." + +/datum/action/innate/creature/teleport/Activate() + var/mob/living/basic/creature/owner_mob = owner + var/obj/effect/dummy/phased_mob/holder = null + if(owner_mob.stat == DEAD) + return + var/turf/owner_turf = get_turf(owner_mob) + if (owner_mob.can_be_seen(owner_turf) || !do_after(owner_mob, 60, target = owner_turf)) + to_chat(owner_mob, span_warning("You can't phase in or out while being observed and you must stay still!")) + return + if (get_dist(owner_mob, owner_turf) != 0 || owner_mob.can_be_seen(owner_turf)) + to_chat(owner_mob, span_warning("Action cancelled, as you moved while reappearing or someone is now viewing your location.")) + return + if(owner_mob.is_phased) + holder = owner_mob.loc + holder.eject_jaunter() + holder = null + owner_mob.is_phased = FALSE + playsound(get_turf(owner_mob), 'sound/effects/podwoosh.ogg', 50, TRUE, -1) + else + playsound(get_turf(owner_mob), 'sound/effects/podwoosh.ogg', 50, TRUE, -1) + holder = new /obj/effect/dummy/phased_mob(owner_turf, owner_mob) + owner_mob.is_phased = TRUE + +/mob/living/basic/creature/Destroy() + if(nest) + nest.spawned_mobs -= src + nest = null + return ..() + +/datum/ai_controller/basic_controller/creature + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/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/basic_melee_attack_subtree/average_speed, + ) diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm similarity index 51% rename from code/modules/mob/living/simple_animal/hostile/netherworld.dm rename to code/modules/mob/living/basic/space_fauna/netherworld/migo.dm index 857f188f7f3..62d5d233851 100644 --- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm +++ b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm @@ -1,120 +1,77 @@ -/mob/living/simple_animal/hostile/netherworld - name = "creature" - desc = "A sanity-destroying otherthing from the netherworld." - icon_state = "otherthing" - icon_living = "otherthing" - icon_dead = "otherthing-dead" - health = 50 - maxHealth = 50 - obj_damage = 5 - melee_damage_lower = 25 - melee_damage_upper = 50 - attack_verb_continuous = "slashes" - attack_verb_simple = "slash" - attack_sound = 'sound/weapons/bladeslice.ogg' - attack_vis_effect = ATTACK_EFFECT_SLASH // I always thought they bit. Guess I was wrong. - faction = list("nether") - speak_emote = list("screams") - gold_core_spawnable = HOSTILE_SPAWN - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - var/phaser = TRUE - var/is_phased = FALSE - -/mob/living/simple_animal/hostile/netherworld/Initialize(mapload) - . = ..() - if(phaser) - var/datum/action/innate/creature/teleport/teleport = new(src) - teleport.Grant(src) - add_cell_sample() - -/mob/living/simple_animal/hostile/netherworld/add_cell_sample() - AddElement(/datum/element/swabable, CELL_LINE_TABLE_NETHER, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 0) - -/datum/action/innate/creature - background_icon_state = "bg_default" - overlay_icon_state = "bg_default_border" - -/datum/action/innate/creature/teleport - name = "Teleport" - desc = "Teleport to wherever you want, as long as you aren't seen." - -/datum/action/innate/creature/teleport/Activate() - var/mob/living/simple_animal/hostile/netherworld/N = owner - var/obj/effect/dummy/phased_mob/holder = null - if(N.stat == DEAD) - return - var/turf/T = get_turf(N) - if (N.can_be_seen(T) || !do_after(N, 60, target = T)) - to_chat(N, span_warning("You can't phase in or out while being observed and you must stay still!")) - return - if (get_dist(N, T) != 0 || N.can_be_seen(T)) - to_chat(N, span_warning("Action cancelled, as you moved while reappearing or someone is now viewing your location.")) - return - if(N.is_phased) - holder = N.loc - holder.eject_jaunter() - holder = null - N.is_phased = FALSE - playsound(get_turf(N), 'sound/effects/podwoosh.ogg', 50, TRUE, -1) - else - playsound(get_turf(N), 'sound/effects/podwoosh.ogg', 50, TRUE, -1) - holder = new /obj/effect/dummy/phased_mob(T, N) - N.is_phased = TRUE - -/mob/living/simple_animal/hostile/netherworld/proc/can_be_seen(turf/location) - // Check for darkness - if(location?.lighting_object) - if(location.get_lumcount()<0.1) // No one can see us in the darkness, right? - return null - - // We aren't in darkness, loop for viewers. - var/list/check_list = list(src) - if(location) - check_list += location - - // This loop will, at most, loop twice. - for(var/atom/check in check_list) - for(var/mob/living/M in viewers(world.view + 1, check) - src) - if(M.client && CanAttack(M) && !M.has_unlimited_silicon_privilege) - if(!M.is_blind()) - return M - for(var/obj/vehicle/sealed/mecha/M in view(world.view + 1, check)) //assuming if you can see them they can see you - for(var/mob/mechamob as anything in M.occupants) - if(mechamob.client && !mechamob.is_blind()) - return mechamob - return null - -/mob/living/simple_animal/hostile/netherworld/migo +/mob/living/basic/migo name = "mi-go" desc = "A pinkish, fungoid crustacean-like creature with numerous pairs of clawed appendages and a head covered with waving antennae." - speak_emote = list("screams", "clicks", "chitters", "barks", "moans", "growls", "meows", "reverberates", "roars", "squeaks", "rattles", "exclaims", "yells", "remarks", "mumbles", "jabbers", "stutters", "seethes") - health = 80 - maxHealth = 80 icon_state = "mi-go" icon_living = "mi-go" icon_dead = "mi-go-dead" + health = 80 + maxHealth = 80 + obj_damage = 50 + melee_damage_lower = 25 + melee_damage_upper = 50 + speed = 1 attack_verb_continuous = "lacerates" attack_verb_simple = "lacerate" - speed = -0.5 - var/static/list/migo_sounds + gold_core_spawnable = HOSTILE_SPAWN + attack_sound = 'sound/weapons/bladeslice.ogg' + attack_vis_effect = ATTACK_EFFECT_SLASH + faction = list("nether") + speak_emote = list("screams", "clicks", "chitters", "barks", "moans", "growls", "meows", "reverberates", "roars", "squeaks", "rattles", "exclaims", "yells", "remarks", "mumbles", "jabbers", "stutters", "seethes") death_message = "wails as its form turns into a pulpy mush." death_sound = 'sound/voice/hiss6.ogg' - phaser = FALSE + unsuitable_atmos_damage = 0 + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE -/mob/living/simple_animal/hostile/netherworld/migo/Initialize(mapload) + ai_controller = /datum/ai_controller/basic_controller/migo + var/static/list/migo_sounds + /// Odds migo will dodge + var/dodge_prob = 10 + /// Used for mobs that get spawned in a spawner appearently. + var/datum/component/spawner/nest + +/mob/living/basic/migo/Initialize(mapload) . = ..() migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/voice/beepsky/creep.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/medbot/patchedup.ogg', 'sound/voice/medbot/feelbetter.ogg', 'sound/voice/human/manlaugh1.ogg', 'sound/voice/human/womanlaugh.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/clockcultalr.ogg', 'sound/ambience/antag/ling_alert.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/ambience/antag/monkey.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion_distant.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/runtime/hyperspace/hyperspace_begin.ogg', 'sound/runtime/hyperspace/hyperspace_end.ogg', 'sound/effects/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/ratvar_reveal.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desecration-01.ogg', 'sound/misc/desecration-02.ogg', 'sound/misc/desecration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/misc/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/beam_sniper.ogg', 'sound/weapons/chainsawhit.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gun/pistol/shot_suppressed.ogg', 'sound/weapons/gun/pistol/shot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlockclose.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlockopen.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/triple_beep.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/default/outbreak5.ogg', 'sound/ai/default/outbreak7.ogg', 'sound/ai/default/poweroff.ogg', 'sound/ai/default/radiation.ogg', 'sound/ai/default/shuttlecalled.ogg', 'sound/ai/default/shuttledock.ogg', 'sound/ai/default/shuttlerecalled.ogg', 'sound/ai/default/aimalf.ogg') //hahahaha fuck you code divers + var/datum/callback/health_changes_callback = CALLBACK(src, PROC_REF(health_check)) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_NETHER, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 0) + AddComponent(/datum/component/damage_buffs, health_changes_callback) -/mob/living/simple_animal/hostile/netherworld/migo/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/basic/migo/proc/health_check(mob/living/attacker) + if(health < maxHealth * 0.25) + health_low_behaviour() + else if (health < maxHealth * 0.5) + health_medium_behaviour() + else if (health < maxHealth * 0.75) + health_high_behaviour() + else + health_full_behaviour() + +/mob/living/basic/migo/proc/health_full_behaviour() + set_varspeed(1) + dodge_prob = 10 + +/mob/living/basic/migo/proc/health_high_behaviour() + set_varspeed(0.5) + dodge_prob = 20 + +/mob/living/basic/migo/proc/health_medium_behaviour() + set_varspeed(0) + dodge_prob = 30 + +/mob/living/basic/migo/proc/health_low_behaviour() + set_varspeed(-0.5) + dodge_prob = 50 + +/mob/living/basic/migo/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) ..() if(stat) return var/chosen_sound = pick(migo_sounds) playsound(src, chosen_sound, 50, TRUE) -/mob/living/simple_animal/hostile/netherworld/migo/Life(delta_time = SSMOBS_DT, times_fired) +/mob/living/basic/migo/Life(delta_time = SSMOBS_DT, times_fired) ..() if(stat) return @@ -122,64 +79,35 @@ var/chosen_sound = pick(migo_sounds) playsound(src, chosen_sound, 50, TRUE) -/mob/living/simple_animal/hostile/netherworld/blankbody - name = "blank body" - desc = "This looks human enough, but its flesh has an ashy texture, and it's face is featureless save an eerie smile." - icon_state = "blank-body" - icon_living = "blank-body" - icon_dead = "blank-dead" - gold_core_spawnable = NO_SPAWN - health = 100 - maxHealth = 100 - melee_damage_lower = 5 - melee_damage_upper = 10 - attack_verb_continuous = "punches" - attack_verb_simple = "punch" - death_message = "falls apart into a fine dust." - phaser = FALSE - -/obj/structure/spawner/nether - name = "netherworld link" - desc = null //see examine() - icon_state = "nether" - max_integrity = 50 - spawn_time = 60 SECONDS - max_mobs = 15 - icon = 'icons/mob/simple/lavaland/nest.dmi' - spawn_text = "crawls through" - mob_types = list(/mob/living/simple_animal/hostile/netherworld/migo, /mob/living/simple_animal/hostile/netherworld, /mob/living/simple_animal/hostile/netherworld/blankbody) - faction = list("nether") - -/obj/structure/spawner/nether/Initialize(mapload) - .=..() - START_PROCESSING(SSprocessing, src) - -/obj/structure/spawner/nether/examine(mob/user) - . = ..() - if(isskeleton(user) || iszombie(user)) - . += "A direct link to another dimension full of creatures very happy to see you. [span_nicegreen("You can see your house from here!")]" +/mob/living/basic/migo/Move(atom/newloc, dir, step_x, step_y) + if(!ckey && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) + return dodge(newloc, dir) else - . += "A direct link to another dimension full of creatures not very happy to see you. [span_warning("Entering the link would be a very bad idea.")]" + return ..() -/obj/structure/spawner/nether/attack_hand(mob/user, list/modifiers) - . = ..() - if(isskeleton(user) || iszombie(user)) - to_chat(user, span_notice("You don't feel like going home yet...")) - else - user.visible_message(span_warning("[user] is violently pulled into the link!"), \ - span_userdanger("Touching the portal, you are quickly pulled through into a world of unimaginable horror!")) - contents.Add(user) +/mob/living/basic/migo/proc/dodge(moving_to, move_direction) + //Assuming we move towards the target we want to swerve toward them to get closer + var/cdir = turn(move_direction, 45) + var/ccdir = turn(move_direction, -45) + . = Move(get_step(loc,pick(cdir, ccdir))) + if(!.)//Can't dodge there so we just carry on + . = Move(moving_to, move_direction) -/obj/structure/spawner/nether/process(delta_time) - for(var/mob/living/M in contents) - if(M) - playsound(src, 'sound/magic/demon_consume.ogg', 50, TRUE) - M.adjustBruteLoss(60 * delta_time) - new /obj/effect/gibspawner/generic(get_turf(M), M) - if(M.stat == DEAD) - var/mob/living/simple_animal/hostile/netherworld/blankbody/blank - blank = new(loc) - blank.name = "[M]" - blank.desc = "It's [M], but [M.p_their()] flesh has an ashy texture, and [M.p_their()] face is featureless save an eerie smile." - src.visible_message(span_warning("[M] reemerges from the link!")) - qdel(M) +/mob/living/basic/migo/Destroy() + if(nest) + nest.spawned_mobs -= src + nest = null + return ..() + +/datum/ai_controller/basic_controller/migo + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/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/basic_melee_attack_subtree/average_speed, + ) diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/basic/space_fauna/statue/statue.dm similarity index 56% rename from code/modules/mob/living/simple_animal/hostile/statue.dm rename to code/modules/mob/living/basic/space_fauna/statue/statue.dm index 4bd8b7fcec2..0620c7a3375 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/basic/space_fauna/statue/statue.dm @@ -1,6 +1,6 @@ // A mob which only moves when it isn't being watched by living beings. -/mob/living/simple_animal/hostile/netherworld/statue +/mob/living/basic/statue name = "statue" // matches the name of the statue with the flesh-to-stone spell desc = "An incredibly lifelike marble carving. Its eyes seem to follow you..." // same as an ordinary statue with the added "eye following you" description icon = 'icons/obj/art/statue.dmi' @@ -20,8 +20,6 @@ speed = -1 maxHealth = 50000 health = 50000 - healable = 0 - harm_intent_damage = 10 obj_damage = 100 melee_damage_lower = 68 melee_damage_upper = 83 @@ -30,21 +28,18 @@ attack_sound = 'sound/hallucinations/growl1.ogg' attack_vis_effect = ATTACK_EFFECT_CLAW - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - faction = list("statue") - move_to_delay = 0 // Very fast + speak_emote = list("screams") + death_message = "falls apart into a fine dust." + unsuitable_atmos_damage = 0 + unsuitable_cold_damage = 0 + unsuitable_heat_damage = 0 animate_movement = NO_STEPS // Do not animate movement, you jump around as you're a scary statue. hud_possible = list(ANTAG_HUD) see_in_dark = 13 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - vision_range = 12 - aggro_vision_range = 12 - - search_objects = 1 // So that it can see through walls sight = SEE_SELF|SEE_MOBS|SEE_OBJS|SEE_TURFS @@ -52,15 +47,20 @@ move_resist = MOVE_FORCE_EXTREMELY_STRONG pull_force = MOVE_FORCE_EXTREMELY_STRONG - var/cannot_be_seen = 1 + ai_controller = /datum/ai_controller/basic_controller/statue + /// Loot this mob drops on death. + var/loot + /// Stores the creator in here if it has one. var/mob/living/creator = null // No movement while seen code. -/mob/living/simple_animal/hostile/netherworld/statue/Initialize(mapload, mob/living/creator) +/mob/living/basic/statue/Initialize(mapload, mob/living/creator) . = ..() - // Give spells + if(LAZYLEN(loot)) + AddElement(/datum/element/death_drops, loot) + // Give spells var/datum/action/cooldown/spell/aoe/flicker_lights/flicker = new(src) flicker.Grant(src) var/datum/action/cooldown/spell/aoe/blindness/blind = new(src) @@ -72,80 +72,58 @@ if(creator) src.creator = creator -/mob/living/simple_animal/hostile/netherworld/statue/add_cell_sample() - return - -/mob/living/simple_animal/hostile/netherworld/statue/med_hud_set_health() +/mob/living/basic/statue/med_hud_set_health() return //we're a statue we're invincible -/mob/living/simple_animal/hostile/netherworld/statue/med_hud_set_status() +/mob/living/basic/statue/med_hud_set_status() return //we're a statue we're invincible -/mob/living/simple_animal/hostile/netherworld/statue/Move(turf/NewLoc) +/mob/living/basic/statue/Move(turf/NewLoc) if(can_be_seen(NewLoc)) if(client) to_chat(src, span_warning("You cannot move, there are eyes on you!")) return return ..() -/mob/living/simple_animal/hostile/netherworld/statue/Life(delta_time = SSMOBS_DT, times_fired) - ..() - if(!client && target) // If we have a target and we're AI controlled - var/mob/watching = can_be_seen() - // If they're not our target - if(watching && watching != target) - // This one is closer. - if(get_dist(watching, src) > get_dist(target, src)) - LoseTarget() - GiveTarget(watching) - -/mob/living/simple_animal/hostile/netherworld/statue/AttackingTarget() - if(can_be_seen(get_turf(loc))) - if(client) - to_chat(src, span_warning("You cannot attack, there are eyes on you!")) - return FALSE - else - return ..() - -/mob/living/simple_animal/hostile/netherworld/statue/DestroyPathToTarget() +/mob/living/basic/statue/face_atom() if(!can_be_seen(get_turf(loc))) ..() -/mob/living/simple_animal/hostile/netherworld/statue/face_atom() - if(!can_be_seen(get_turf(loc))) - ..() - -/mob/living/simple_animal/hostile/netherworld/statue/can_speak(allow_mimes = FALSE) +/mob/living/basic/statue/can_speak(allow_mimes = FALSE) return FALSE // We're a statue, of course we can't talk. // Cannot talk -/mob/living/simple_animal/hostile/netherworld/statue/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) +/mob/living/basic/statue/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null, message_range = 7, datum/saymode/saymode = null) return // Turn to dust when gibbed -/mob/living/simple_animal/hostile/netherworld/statue/gib() +/mob/living/basic/statue/gib() dust() +/mob/living/basic/statue/proc/can_be_seen(turf/location) + // Check for darkness + if(location?.lighting_object) + if(location.get_lumcount() < 0.1) // No one can see us in the darkness, right? + return null -// Stop attacking clientless mobs + // We aren't in darkness, loop for viewers. + var/list/check_list = list(src) + if(location) + check_list += location -/mob/living/simple_animal/hostile/netherworld/statue/CanAttack(atom/the_target) - if(isliving(the_target)) - var/mob/living/L = the_target - if(!L.client && !L.ckey) - return FALSE - return ..() - -// Don't attack your creator if there is one - -/mob/living/simple_animal/hostile/netherworld/statue/ListTargets() - . = ..() - return . - creator - -/mob/living/simple_animal/hostile/netherworld/statue/sentience_act() - faction -= FACTION_NEUTRAL + // This loop will, at most, loop twice. + for(var/atom/check in check_list) + for(var/mob/living/mob_target in oview(src, 7)) // They probably cannot see us if we cannot see them... can they? + if(mob_target.client && !mob_target.is_blind() && !mob_target.has_unlimited_silicon_privilege) + if(!istype(mob_target, /mob/living/basic/statue)) + return mob_target + for(var/obj/vehicle/sealed/mecha/mecha_mob_target in oview(src, 7)) + for(var/mob/mechamob_target as anything in mecha_mob_target.occupants) + if(mechamob_target.client && !mechamob_target.is_blind()) + return mechamob_target + return null // Statue powers @@ -196,3 +174,28 @@ /datum/action/cooldown/spell/aoe/blindness/cast_on_thing_in_aoe(mob/living/victim, atom/caster) victim.adjust_temp_blindness(8 SECONDS) + +/datum/ai_controller/basic_controller/statue + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic(), + BB_LOW_PRIORITY_HUNTING_TARGET = null, // lights + ) + + ai_movement = /datum/ai_movement/basic_avoidance + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree/statue, + /datum/ai_planning_subtree/find_and_hunt_target/look_for_light_fixtures, + ) + +/datum/ai_planning_subtree/basic_melee_attack_subtree/statue + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/statue + +/datum/ai_behavior/basic_melee_attack/statue + action_cooldown = 1 SECONDS + +/datum/ai_behavior/basic_melee_attack/statue/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) + . = ..() + var/mob/living/basic/statue/statue_mob = controller.pawn + if(statue_mob.can_be_seen(get_turf(statue_mob))) + return FALSE diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 459e619ded2..8a7c6b09b32 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1345,7 +1345,7 @@ /mob/living/basic/carp, /mob/living/simple_animal/hostile/bear, /mob/living/simple_animal/hostile/mushroom, - /mob/living/simple_animal/hostile/netherworld/statue, + /mob/living/basic/statue, /mob/living/simple_animal/hostile/retaliate/bat, /mob/living/simple_animal/hostile/retaliate/goat, /mob/living/simple_animal/hostile/killertomato, diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index b1b826c11b5..f6ffb668297 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -198,7 +198,7 @@ var/obj/structure/statue/petrified/P = src if(P.petrified_mob) var/mob/living/L = P.petrified_mob - var/mob/living/simple_animal/hostile/netherworld/statue/S = new(P.loc, owner) + var/mob/living/basic/statue/S = new(P.loc, owner) S.name = "statue of [L.name]" if(owner) S.faction = list("[REF(owner)]") diff --git a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm index f8ec8dd381d..8fd2b67d3e4 100644 --- a/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm +++ b/code/modules/research/xenobiology/vatgrowing/samples/cell_lines/common.dm @@ -471,10 +471,10 @@ /datum/reagent/consumable/liquidgibs = -2) virus_suspectibility = 0 - resulting_atoms = list(/mob/living/simple_animal/hostile/netherworld = 1) + resulting_atoms = list() /datum/micro_organism/cell_line/netherworld/succeed_growing(obj/machinery/plumbing/growing_vat/vat) - var/random_result = pick(typesof(/mob/living/simple_animal/hostile/netherworld) - /mob/living/simple_animal/hostile/netherworld/statue) //i looked myself, pretty much all of them are reasonably strong and somewhat on the same level. except migo is the jackpot and the blank body is whiff. + var/random_result = pick(/mob/living/basic/creature, /mob/living/basic/migo, /mob/living/basic/blankbody) //i looked myself, pretty much all of them are reasonably strong and somewhat on the same level. except migo is the jackpot and the blank body is whiff. resulting_atoms = list() resulting_atoms[random_result] = 1 return ..() diff --git a/code/modules/spells/spell_types/conjure/creatures.dm b/code/modules/spells/spell_types/conjure/creatures.dm index c51d4d114df..9aeede68a66 100644 --- a/code/modules/spells/spell_types/conjure/creatures.dm +++ b/code/modules/spells/spell_types/conjure/creatures.dm @@ -11,5 +11,5 @@ spell_requirements = NONE summon_radius = 3 - summon_type = list(/mob/living/simple_animal/hostile/netherworld) + summon_type = list(/mob/living/basic/creature) summon_amount = 10 diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 2521607ea45..3e4f7f0cf51 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -244,10 +244,6 @@ /mob/living/simple_animal/hostile/nanotrasen/ranged/assault, /mob/living/simple_animal/hostile/nanotrasen/ranged/smg, /mob/living/simple_animal/hostile/nanotrasen/screaming, - /mob/living/simple_animal/hostile/netherworld, - /mob/living/simple_animal/hostile/netherworld/blankbody, - /mob/living/simple_animal/hostile/netherworld/migo, - /mob/living/simple_animal/hostile/netherworld/statue, /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 b14e2bc66c5..123e1a43827 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -858,6 +858,7 @@ #include "code\datums\components\cult_ritual_item.dm" #include "code\datums\components\curse_of_hunger.dm" #include "code\datums\components\customizable_reagent_holder.dm" +#include "code\datums\components\damage_buffs.dm" #include "code\datums\components\deadchat_control.dm" #include "code\datums\components\dejavu.dm" #include "code\datums\components\deployable.dm" @@ -3763,6 +3764,10 @@ #include "code\modules\mob\living\basic\space_fauna\carp\magicarp.dm" #include "code\modules\mob\living\basic\space_fauna\carp\megacarp.dm" #include "code\modules\mob\living\basic\space_fauna\faithless\faithless.dm" +#include "code\modules\mob\living\basic\space_fauna\netherworld\blankbody.dm" +#include "code\modules\mob\living\basic\space_fauna\netherworld\creature.dm" +#include "code\modules\mob\living\basic\space_fauna\netherworld\migo.dm" +#include "code\modules\mob\living\basic\space_fauna\statue\statue.dm" #include "code\modules\mob\living\basic\syndicate\syndicate.dm" #include "code\modules\mob\living\basic\syndicate\syndicate_ai.dm" #include "code\modules\mob\living\basic\vermin\axolotl.dm" @@ -3988,7 +3993,6 @@ #include "code\modules\mob\living\simple_animal\hostile\morph.dm" #include "code\modules\mob\living\simple_animal\hostile\mushroom.dm" #include "code\modules\mob\living\simple_animal\hostile\nanotrasen.dm" -#include "code\modules\mob\living\simple_animal\hostile\netherworld.dm" #include "code\modules\mob\living\simple_animal\hostile\ooze.dm" #include "code\modules\mob\living\simple_animal\hostile\pirate.dm" #include "code\modules\mob\living\simple_animal\hostile\regalrat.dm" @@ -3997,7 +4001,6 @@ #include "code\modules\mob\living\simple_animal\hostile\slaughter_demon.dm" #include "code\modules\mob\living\simple_animal\hostile\smspider.dm" #include "code\modules\mob\living\simple_animal\hostile\space_dragon.dm" -#include "code\modules\mob\living\simple_animal\hostile\statue.dm" #include "code\modules\mob\living\simple_animal\hostile\tree.dm" #include "code\modules\mob\living\simple_animal\hostile\vatbeast.dm" #include "code\modules\mob\living\simple_animal\hostile\venus_human_trap.dm" diff --git a/tools/UpdatePaths/Scripts/73086_refractor_netherworld.txt b/tools/UpdatePaths/Scripts/73086_refractor_netherworld.txt new file mode 100644 index 00000000000..6d819b13508 --- /dev/null +++ b/tools/UpdatePaths/Scripts/73086_refractor_netherworld.txt @@ -0,0 +1,4 @@ +/mob/living/simple_animal/hostile/netherworld/blankbody : /mob/living/basic/blankbody{@OLD} +/mob/living/simple_animal/hostile/netherworld : /mob/living/basic/creature{@OLD} +/mob/living/simple_animal/hostile/netherworld/migo : /mob/living/basic/migo{@OLD} +/mob/living/simple_animal/hostile/netherworld/statue : /mob/living/basic/statue{@OLD}