diff --git a/code/__DEFINES/ai/incursion_blackboard_defines.dm b/code/__DEFINES/ai/incursion_blackboard_defines.dm new file mode 100644 index 00000000000..327e6a1f548 --- /dev/null +++ b/code/__DEFINES/ai/incursion_blackboard_defines.dm @@ -0,0 +1,2 @@ +/// The skull action used by reanimators +#define BB_REANIMATOR_SKULL_ACTION "BB_reanimator_skull_action" diff --git a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm b/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm index d2691e9b3f2..b920cff9889 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm @@ -99,3 +99,21 @@ /datum/ai_planning_subtree/attack_obstacle_in_path/prowl attack_behaviour = /datum/ai_behavior/attack_obstructions/avoid_breaches target_key = BB_PROWL_TARGET + +/datum/ai_planning_subtree/attack_obstacle_in_path/walls + attack_behaviour = /datum/ai_behavior/attack_obstructions/walls + +/datum/ai_behavior/attack_obstructions/walls + can_attack_turfs = TRUE + +/datum/ai_planning_subtree/attack_obstacle_in_path/prowl/walls + attack_behaviour = /datum/ai_behavior/attack_obstructions/avoid_breaches/walls + +/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches + attack_behaviour = /datum/ai_behavior/attack_obstructions/avoid_breaches + +/datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls + attack_behaviour = /datum/ai_behavior/attack_obstructions/avoid_breaches/walls + +/datum/ai_behavior/attack_obstructions/avoid_breaches/walls + can_attack_turfs = TRUE diff --git a/code/datums/components/basic_mob_aggro_emote.dm b/code/datums/components/basic_mob_aggro_emote.dm index 18849790378..30996ef0381 100644 --- a/code/datums/components/basic_mob_aggro_emote.dm +++ b/code/datums/components/basic_mob_aggro_emote.dm @@ -16,6 +16,8 @@ var/aggro_sound /// Volume for the sound var/aggro_volume + /// List of possible things to say + var/list/say_list /datum/component/aggro_emote/Initialize( target_key = BB_BASIC_MOB_CURRENT_TARGET, @@ -26,6 +28,7 @@ emote_chance = 30, minimum_chance = 2, subtract_chance = 7, + list/say_list, ) . = ..() if(!isatom(parent)) @@ -40,6 +43,7 @@ src.emote_chance = emote_chance src.minimum_chance = minimum_chance src.subtract_chance = subtract_chance + src.say_list = say_list /datum/component/aggro_emote/RegisterWithParent() . = ..() @@ -65,3 +69,5 @@ mob_parent.emote("me", EMOTE_VISIBLE, "[pick(emote_list)] at [new_target].") if(aggro_sound) playsound(mob_parent.loc, aggro_sound, aggro_volume, TRUE) + if(say_list) + mob_parent.say(pick(say_list)) diff --git a/code/datums/components/spawner.dm b/code/datums/components/spawner.dm index 6b331752bd8..81731db2a29 100644 --- a/code/datums/components/spawner.dm +++ b/code/datums/components/spawner.dm @@ -47,7 +47,7 @@ if(spawn_delay > world.time) return spawn_delay = world.time + spawn_time - var/chosen_mob_type = pick(mob_types) + var/chosen_mob_type = pickweight(mob_types) var/mob/living/simple_animal/L = new chosen_mob_type(P.loc) L.admin_spawned = P.admin_spawned spawned_mobs += L @@ -91,5 +91,8 @@ COOLDOWN_START(src, last_rally, 30 SECONDS) for(var/mob/living/basic/rallied as anything in spawned_mobs) + if(rallied.ventcrawler == VENTCRAWLER_ALWAYS) + return // Don't rally ventcrawlers - they might have to walk the entire station! rallied.ai_controller.cancel_actions() - rallied.ai_controller.queue_behavior(/datum/ai_behavior/return_home/incursion_portal, BB_INCURSION_HOME_PORTAL) + rallied.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) + rallied.ai_controller.queue_behavior(/datum/ai_behavior/return_to_portal, BB_INCURSION_HOME_PORTAL) diff --git a/code/modules/events/demon_incursion.dm b/code/modules/events/demon_incursion.dm index d1e0318b1fb..6fb94087921 100644 --- a/code/modules/events/demon_incursion.dm +++ b/code/modules/events/demon_incursion.dm @@ -111,18 +111,31 @@ spawn_time = 5 SECONDS // Short spawn time initially, it gets updated after it spawns initial mobs max_mobs = 5 // We want a lot of mobs, but not too many max_integrity = 200 - mob_types = list(/mob/living/basic/netherworld/migo, - /mob/living/basic/netherworld, - /mob/living/basic/netherworld/blankbody, - /mob/living/basic/hellhound, - /mob/living/basic/skeleton, - /mob/living/basic/netherworld/faithless) + mob_types = list(/mob/living/basic/netherworld/migo = 6, // Generic mobs - just run at the target + /mob/living/basic/netherworld = 6, + /mob/living/basic/netherworld/faithless = 6, + /mob/living/basic/skeleton/incursion = 6, + /mob/living/basic/netherworld/blankbody = 5, + /mob/living/basic/hellhound/whelp = 5, // Specialized mobs - tank + /mob/living/basic/skeleton/incursion/security = 5, // Specialized mobs - ranged + /mob/living/basic/giant_spider/flesh_spider = 5, // Specialized mobs - poison/harasser + /mob/living/basic/skeleton/reanimator = 4, // Specialized mobs - Summoner + /mob/living/basic/skeleton/incursion/mobster = 1,) // Specialized mobs - ranged icon = 'icons/obj/structures/portal.dmi' icon_state = "portal" light_range = 4 light_power = 2 light_color = "#780606" spawner_type = /datum/component/spawner/demon_incursion_portal + /// Mob types that cannot have a special variant - pretty much anything with a unique AI controller + var/list/no_special_variants = list( + /mob/living/basic/hellhound/whelp, + /mob/living/basic/giant_spider/flesh_spider, + /mob/living/basic/skeleton/incursion/security, + /mob/living/basic/skeleton/incursion/mobster, + /mob/living/basic/skeleton/reanimator) + /// Chance that a mob type is special + var/special_chance = 15 /// The event that spawned this portal var/datum/event/demon_incursion/linked_incursion /// Percentage chance that a portal will spread every time spread() is called @@ -136,10 +149,10 @@ /// Time until next portal var/expansion_delay /// How fast does the portal spawn mobs after the initial spawns? - var/spawn_rate = 30 SECONDS + var/spawn_rate = 45 SECONDS /// How many initial mobs does it spawn? var/initial_spawns_min = 1 - var/initial_spawns_max = 4 + var/initial_spawns_max = 3 /// Are we spawning initial mobs? var/spawning_initial_mobs = TRUE /// Current tile spread distance @@ -217,9 +230,47 @@ var/mob/living/basic/new_mob = created_mob if(!istype(new_mob, /mob/living/basic)) return - if(new_mob.basic_mob_flags & DEL_ON_DEATH) + if(created_mob.type in no_special_variants) return - new_mob.AddComponent(/datum/component/incursion_mob_death) + if(!(new_mob.basic_mob_flags & DEL_ON_DEATH)) + new_mob.AddComponent(/datum/component/incursion_mob_death) + if(!prob(special_chance)) + return + var/special_type = pick("grappler", "enflamed", "hastened", "electrified", "juggernaut") + switch(special_type) + if("grappler") + new_mob.AddComponent(/datum/component/ranged_attacks, projectile_type = /obj/projectile/energy/demonic_grappler, burst_shots = 1, projectile_sound = 'sound/weapons/wave.ogg') + new_mob.name = "grappling " + new_mob.name + new_mob.ai_controller = new /datum/ai_controller/basic_controller/incursion/ranged(new_mob) + new_mob.update_appearance(UPDATE_NAME) + new_mob.color = "#5494DA" + if("enflamed") + new_mob.AddComponent(/datum/component/ranged_attacks, projectile_type = /obj/projectile/magic/fireball/small, burst_shots = 1, projectile_sound = 'sound/magic/fireball.ogg') + new_mob.name = "enflamed " + new_mob.name + new_mob.ai_controller = new /datum/ai_controller/basic_controller/incursion/ranged(new_mob) + new_mob.update_appearance(UPDATE_NAME) + new_mob.color = "#d4341f" + if("hastened") + new_mob.name = "hastened " + new_mob.name + new_mob.update_appearance(UPDATE_NAME) + new_mob.color = "#1fd437" + new_mob.speed = -1 + if("electrified") + new_mob.AddComponent(/datum/component/ranged_attacks, projectile_type = /obj/projectile/energy/demonic_shocker, burst_shots = 1, projectile_sound = 'sound/weapons/taser.ogg') + new_mob.name = "electrified " + new_mob.name + ADD_TRAIT(new_mob, TRAIT_SHOCKIMMUNE, "electrified") + new_mob.ai_controller = new /datum/ai_controller/basic_controller/incursion/ranged(new_mob) + new_mob.update_appearance(UPDATE_NAME) + new_mob.color = "#fcf7f6" + if("juggernaut") + new_mob.name = "juggernaut " + new_mob.name + new_mob.ai_controller = new /datum/ai_controller/basic_controller/incursion/juggernaut(new_mob) + new_mob.health = new_mob.health * 2 + new_mob.maxHealth = new_mob.maxHealth * 2 + new_mob.speed += 6 + new_mob.environment_smash = ENVIRONMENT_SMASH_WALLS // Puny wall. + new_mob.update_appearance(UPDATE_NAME) + new_mob.color = "#292827" /obj/structure/spawner/nether/demon_incursion/attacked_by(obj/item/attacker, mob/living/user) . = ..() diff --git a/code/modules/mob/living/basic/nether_mobs/blankbody.dm b/code/modules/mob/living/basic/hostile/nether_mobs/blankbody.dm similarity index 83% rename from code/modules/mob/living/basic/nether_mobs/blankbody.dm rename to code/modules/mob/living/basic/hostile/nether_mobs/blankbody.dm index 478ac1e6aa2..af631d43d21 100644 --- a/code/modules/mob/living/basic/nether_mobs/blankbody.dm +++ b/code/modules/mob/living/basic/hostile/nether_mobs/blankbody.dm @@ -6,6 +6,7 @@ icon_dead = "blank-dead" health = 100 maxHealth = 100 + a_intent = INTENT_HARM obj_damage = 50 melee_damage_lower = 5 melee_damage_upper = 10 @@ -17,9 +18,13 @@ death_message = "falls apart into a fine dust." /// The body/brain of the player turned into a blank, if the blank was turned var/mob/living/held_body + /// The held body's player is in control of the blank + var/is_original_mob = FALSE /mob/living/basic/netherworld/blankbody/death(gibbed) . = ..() if(held_body) held_body.forceMove(loc) + if(is_original_mob) + mind.transfer_to(held_body) qdel(src) diff --git a/code/modules/mob/living/basic/hostile/creature.dm b/code/modules/mob/living/basic/hostile/nether_mobs/creature.dm similarity index 100% rename from code/modules/mob/living/basic/hostile/creature.dm rename to code/modules/mob/living/basic/hostile/nether_mobs/creature.dm diff --git a/code/modules/mob/living/basic/nether_mobs/faithless.dm b/code/modules/mob/living/basic/hostile/nether_mobs/faithless.dm similarity index 96% rename from code/modules/mob/living/basic/nether_mobs/faithless.dm rename to code/modules/mob/living/basic/hostile/nether_mobs/faithless.dm index 0127fca53a6..6df564570e7 100644 --- a/code/modules/mob/living/basic/nether_mobs/faithless.dm +++ b/code/modules/mob/living/basic/hostile/nether_mobs/faithless.dm @@ -7,7 +7,7 @@ icon_dead = "faithless_dead" mob_biotypes = MOB_ORGANIC | MOB_HUMANOID melee_damage_lower = 15 - melee_damage_upper = 15 + melee_damage_upper = 20 harm_intent_damage = 10 obj_damage = 50 attack_verb_simple = "grip" diff --git a/code/modules/mob/living/basic/hostile/nether_mobs/flesh_spider.dm b/code/modules/mob/living/basic/hostile/nether_mobs/flesh_spider.dm new file mode 100644 index 00000000000..461720e92fe --- /dev/null +++ b/code/modules/mob/living/basic/hostile/nether_mobs/flesh_spider.dm @@ -0,0 +1,32 @@ +// Giant evil flesh spider +/mob/living/basic/giant_spider/flesh_spider + name = "flesh spider" + desc = "A horrifyingly grotesque mass of animated flesh shaped like a spider. A tar-like venom drips from its bony fangs." + icon_state = "flesh_spider" + icon_living = "flesh_spider" + icon_dead = "flesh_spider_dead" + mob_biotypes = MOB_ORGANIC + ventcrawler = VENTCRAWLER_ALWAYS + butcher_results = list(/obj/item/food/monstermeat/spidermeat = 4) + maxHealth = 70 + health = 70 + obj_damage = 30 + melee_damage_lower = 10 + melee_damage_upper = 15 + melee_attack_cooldown_min = 1 SECONDS + melee_attack_cooldown_max = 2 SECONDS + faction = list("nether") + ai_controller = /datum/ai_controller/basic_controller/incursion/flesh_spider + venom_per_bite = 10 + innate_actions = list() + gold_core_spawnable = NO_SPAWN + +/mob/living/basic/giant_spider/flesh_spider/Initialize(mapload) + . = ..() + AddComponent(/datum/component/event_tracker) + AddElement(/datum/element/ai_retaliate) + +/mob/living/basic/giant_spider/flesh_spider/event_cost() + . = list() + if(is_station_level((get_turf(src)).z) && stat != DEAD) + return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 0.5) diff --git a/code/modules/mob/living/basic/hostile/hellhound.dm b/code/modules/mob/living/basic/hostile/nether_mobs/hellhound.dm similarity index 98% rename from code/modules/mob/living/basic/hostile/hellhound.dm rename to code/modules/mob/living/basic/hostile/nether_mobs/hellhound.dm index 24435082f4c..8ad0e7e1306 100644 --- a/code/modules/mob/living/basic/hostile/hellhound.dm +++ b/code/modules/mob/living/basic/hostile/nether_mobs/hellhound.dm @@ -157,3 +157,8 @@ melee_damage_lower = 30 melee_damage_upper = 50 gold_core_spawnable = NO_SPAWN + +/mob/living/basic/hellhound/whelp + name = "hellhound whelp" + maxHealth = 150 + health = 150 diff --git a/code/modules/mob/living/basic/hostile/hellhound_ai.dm b/code/modules/mob/living/basic/hostile/nether_mobs/hellhound_ai.dm similarity index 96% rename from code/modules/mob/living/basic/hostile/hellhound_ai.dm rename to code/modules/mob/living/basic/hostile/nether_mobs/hellhound_ai.dm index 51b5a0894f2..acd3575bbcb 100644 --- a/code/modules/mob/living/basic/hostile/hellhound_ai.dm +++ b/code/modules/mob/living/basic/hostile/nether_mobs/hellhound_ai.dm @@ -4,6 +4,7 @@ ) planning_subtrees = list( /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate, /datum/ai_planning_subtree/attack_obstacle_in_path, /datum/ai_planning_subtree/basic_melee_attack_subtree, /datum/ai_planning_subtree/hellhound_rest, diff --git a/code/modules/mob/living/basic/hostile/nether_mobs/incursion_ai.dm b/code/modules/mob/living/basic/hostile/nether_mobs/incursion_ai.dm new file mode 100644 index 00000000000..6209107425a --- /dev/null +++ b/code/modules/mob/living/basic/hostile/nether_mobs/incursion_ai.dm @@ -0,0 +1,154 @@ +/// Prowls around when not attacking people +/datum/ai_controller/basic_controller/incursion + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_AGGRO_RANGE = 6 + ) + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + interesting_dist = AI_SIMPLE_INTERESTING_DIST + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/find_and_hunt_target/prowl, + /datum/ai_planning_subtree/attack_obstacle_in_path/prowl, + ) + +/datum/ai_controller/basic_controller/incursion/ranged + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/ranged_skirmish, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/find_and_hunt_target/prowl, + /datum/ai_planning_subtree/attack_obstacle_in_path/prowl, + ) + +/datum/ai_controller/basic_controller/incursion/juggernaut + ai_movement = /datum/ai_movement/jps + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/attack_obstacle_in_path/walls, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/find_and_hunt_target/prowl, + /datum/ai_planning_subtree/attack_obstacle_in_path/prowl/walls, + ) + +/datum/ai_controller/basic_controller/incursion/ranged_distance + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_RANGED_SKIRMISH_MIN_DISTANCE = 3, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 6, + BB_AGGRO_RANGE = 6, + ) + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, + /datum/ai_planning_subtree/maintain_distance, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/ranged_skirmish/avoid_friendly, + /datum/ai_planning_subtree/find_and_hunt_target/prowl, + /datum/ai_planning_subtree/attack_obstacle_in_path/prowl, + ) + +/datum/ai_controller/basic_controller/incursion/flesh_spider + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_AGGRO_RANGE = 6, + BB_VENT_SEARCH_RANGE = 6, + ) + ai_movement = /datum/ai_movement/jps + + ai_traits = AI_FLAG_PAUSE_DURING_DO_AFTER + + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/find_and_hunt_target/prowl, + /datum/ai_planning_subtree/attack_obstacle_in_path/prowl, + /datum/ai_planning_subtree/ventcrawl_find_target, + /datum/ai_planning_subtree/ventcrawl, + ) + +/datum/ai_controller/basic_controller/incursion/reanimator + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_RANGED_SKIRMISH_MIN_DISTANCE = 3, + BB_RANGED_SKIRMISH_MAX_DISTANCE = 4, + BB_TARGET_MINIMUM_STAT = UNCONSCIOUS, + BB_AGGRO_RANGE = 6 + ) + + ai_traits = AI_FLAG_PAUSE_DURING_DO_AFTER + + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/target_retaliate/check_faction, + /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, + /datum/ai_planning_subtree/conjure_skulls, + /datum/ai_planning_subtree/find_and_hunt_target/corpses/human/reanimator, + /datum/ai_planning_subtree/maintain_distance, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/ranged_skirmish, + /datum/ai_planning_subtree/find_and_hunt_target/prowl, + /datum/ai_planning_subtree/attack_obstacle_in_path/prowl, + ) + +/datum/ai_planning_subtree/find_and_hunt_target/corpses/human/reanimator + hunt_range = 6 + +/// Run the conjure skulls action +/datum/ai_planning_subtree/conjure_skulls + /// Key where the summoning action is stored + var/action_key = BB_REANIMATOR_SKULL_ACTION + /// Key where the target is stored + var/target_key = BB_BASIC_MOB_CURRENT_TARGET + +/datum/ai_planning_subtree/conjure_skulls/select_behaviors(datum/ai_controller/controller, seconds_per_tick) + if(!controller.blackboard_key_exists(action_key) || !controller.blackboard_key_exists(target_key)) + return + var/datum/action/cooldown/mob_cooldown/summon_skulls/skull_action = controller.blackboard[action_key] + if(skull_action.IsAvailable()) + controller.queue_behavior(/datum/ai_behavior/conjure_skulls, action_key, target_key) + return SUBTREE_RETURN_FINISH_PLANNING + +/// Nyeh heh heh! +/datum/ai_behavior/conjure_skulls + action_cooldown = 20 SECONDS // We don't want them doing this too quickly + required_distance = 0 + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/conjure_skulls/perform(seconds_per_tick, datum/ai_controller/controller, action_key, target_key) + . = ..() + var/datum/action/cooldown/mob_cooldown/summon_skulls/skull_action = controller.blackboard[action_key] + var/result = skull_action.Trigger() + if(result) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + +/datum/ai_behavior/return_to_portal + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/return_to_portal/setup(datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + var/list/possible_turfs = get_adjacent_open_turfs(target) + shuffle_inplace(possible_turfs) + for(var/turf/possible_turf as anything in possible_turfs) + if(!possible_turf.is_blocked_turf()) + set_movement_target(controller, possible_turf) + return TRUE + return FALSE + +/datum/ai_behavior/return_to_portal/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED diff --git a/code/modules/mob/living/basic/nether_mobs/migo.dm b/code/modules/mob/living/basic/hostile/nether_mobs/migo.dm similarity index 100% rename from code/modules/mob/living/basic/nether_mobs/migo.dm rename to code/modules/mob/living/basic/hostile/nether_mobs/migo.dm diff --git a/code/modules/mob/living/basic/nether_mobs/nether_mobs.dm b/code/modules/mob/living/basic/hostile/nether_mobs/nether_mobs.dm similarity index 70% rename from code/modules/mob/living/basic/nether_mobs/nether_mobs.dm rename to code/modules/mob/living/basic/hostile/nether_mobs/nether_mobs.dm index 0bdd7bdf478..631a0716a01 100644 --- a/code/modules/mob/living/basic/nether_mobs/nether_mobs.dm +++ b/code/modules/mob/living/basic/hostile/nether_mobs/nether_mobs.dm @@ -8,11 +8,11 @@ maxHealth = 80 obj_damage = 100 melee_damage_lower = 25 - melee_damage_upper = 50 + melee_damage_upper = 30 melee_attack_cooldown_min = 1.5 SECONDS melee_attack_cooldown_max = 2.5 SECONDS environment_smash = ENVIRONMENT_SMASH_STRUCTURES - ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/prowler + ai_controller = /datum/ai_controller/basic_controller/incursion attack_verb_simple = "chomp" attack_verb_continuous = "chomps" attack_sound = 'sound/weapons/bladeslice.ogg' @@ -24,17 +24,9 @@ contains_xeno_organ = TRUE surgery_container = /datum/xenobiology_surgery_container/sweating step_type = FOOTSTEP_MOB_SHOE - /// The chance of it being a grappler variant - var/grappler_chance = 20 /mob/living/basic/netherworld/Initialize(mapload) . = ..() - if(prob(grappler_chance)) - AddComponent(/datum/component/ranged_attacks, projectile_type = /obj/projectile/energy/demonic_grappler, projectile_sound = 'sound/weapons/wave.ogg') - name = "grappling " + name - ai_controller = new /datum/ai_controller/basic_controller/simple/simple_skirmisher/prowler(src) - update_appearance(UPDATE_NAME) - color = "#5494DA" AddElement(/datum/element/ai_retaliate) AddComponent(/datum/component/event_tracker, EVENT_DEMONIC) diff --git a/code/modules/mob/living/basic/hostile/nether_mobs/skeleton_mob.dm b/code/modules/mob/living/basic/hostile/nether_mobs/skeleton_mob.dm new file mode 100644 index 00000000000..5a220089e06 --- /dev/null +++ b/code/modules/mob/living/basic/hostile/nether_mobs/skeleton_mob.dm @@ -0,0 +1,272 @@ +/mob/living/basic/skeleton + name = "reanimated skeleton" + desc = "A real bonefied skeleton, doesn't seem like it wants to socialize." + icon = 'icons/mob/simple_human.dmi' + icon_state = "skeleton" + icon_living = "skeleton" + speak_emote = list("rattles") + mob_biotypes = MOB_UNDEAD | MOB_HUMANOID + maxHealth = 40 + health = 40 + a_intent = INTENT_HARM + melee_damage_lower = 15 + melee_damage_upper = 15 + harm_intent_damage = 5 + obj_damage = 50 + melee_attack_cooldown_min = 1.5 SECONDS + melee_attack_cooldown_max = 2.5 SECONDS + environment_smash = ENVIRONMENT_SMASH_STRUCTURES + minimum_survivable_temperature = 0 + maximum_survivable_temperature = 1500 + healable = FALSE + attack_verb_simple = "slash" + attack_verb_continuous = "slashes" + attack_sound = 'sound/hallucinations/growl1.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAM = 0, OXY = 1) + unsuitable_atmos_damage = 10 + see_in_dark = 8 + faction = list("skeleton") + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + basic_mob_flags = DEL_ON_DEATH + step_type = FOOTSTEP_MOB_SHOE + gold_core_spawnable = HOSTILE_SPAWN + loot = list(/obj/effect/decal/remains/human) + ai_controller = /datum/ai_controller/basic_controller/incursion + +/mob/living/basic/skeleton/Initialize(mapload) + . = ..() + AddElement(/datum/element/ai_retaliate) + add_language("Galactic Common") + set_default_language(GLOB.all_languages["Galactic Common"]) + +/mob/living/basic/skeleton/arctic + name = "undead arctic explorer" + desc = "The reanimated remains of some poor traveler." + icon_state = "arctic_skeleton" + icon_living = "arctic_skeleton" + maxHealth = 55 + health = 55 + weather_immunities = list("snow") + gold_core_spawnable = NO_SPAWN + melee_damage_lower = 17 + melee_damage_upper = 20 + death_message = "collapses into a pile of bones, its gear falling to the floor!" + loot = list(/obj/effect/decal/remains/human, + /obj/item/spear, + /obj/item/clothing/shoes/winterboots, + /obj/item/clothing/suit/hooded/wintercoat) + +/mob/living/basic/skeleton/warden + name = "skeleton warden" + desc = "The remains of a warden." + icon_state = "skeleton_warden" + icon_living = "skeleton_warden" + loot = list(/obj/effect/decal/cleanable/shreds, /mob/living/basic/skeleton/angered_warden) + maxHealth = 300 + health = 300 + death_message = null + gold_core_spawnable = NO_SPAWN + ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/skeleton_warden + +/mob/living/basic/skeleton/warden/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) + return TRUE + +/mob/living/basic/skeleton/angered_warden + name = "angered skeleton warden" // round 2 + desc = "An angry skeleton." + icon_state = "skeleton_warden_alt" + icon_living = "skeleton_warden_alt" + attack_verb_simple = "claw" + attack_verb_continuous = "claws" + maxHealth = 200 + health = 200 + speed = -1 + melee_damage_lower = 30 + melee_damage_upper = 30 + loot = list(/obj/effect/decal/remains/human, /obj/item/clothing/head/warden, /obj/item/card/sec_shuttle_ruin) + gold_core_spawnable = NO_SPAWN + ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/skeleton_warden + +/mob/living/basic/skeleton/angered_warden/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) + return TRUE + +/datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/skeleton_warden + idle_behavior = null // Don't idly wander + +/mob/living/basic/skeleton/incursion + name = "reinforced skeleton" + desc = "It's got a bone to pick with you." + health = 75 + maxHealth = 75 + gold_core_spawnable = NO_SPAWN + +/mob/living/basic/skeleton/incursion/Initialize(mapload) + . = ..() + AddComponent(/datum/component/event_tracker) + +/mob/living/basic/skeleton/incursion/event_cost() + . = list() + if(is_station_level((get_turf(src)).z) && stat != DEAD) + return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 0.5) + +/mob/living/basic/skeleton/incursion/security + name = "skeletal officer" + desc = "HALT! SKELECURITY!" + icon_state = "skeleton_officer" + icon_living = "skeleton_officer" + health = 90 + maxHealth = 90 + is_ranged = TRUE + projectile_type = /obj/projectile/beam/laser + projectile_sound = 'sound/weapons/gunshots/gunshot_lascarbine.ogg' + ranged_cooldown = 2.5 SECONDS + ai_controller = /datum/ai_controller/basic_controller/incursion/ranged_distance + +/mob/living/basic/skeleton/incursion/security/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_BASICMOB_POST_ATTACK_RANGED, PROC_REF(cycle_lever)) + AddComponent(/datum/component/aggro_emote, say_list = list("Halt! Security!", "Hands up!", "Get on the ground!", "Halt! Halt! Halt!", "They're resisting!", "Lethals authorized!")) + if(prob(20)) + loot += pick(/obj/item/clothing/head/helmet, /obj/item/clothing/suit/armor/vest/security) + +/mob/living/basic/skeleton/incursion/security/proc/cycle_lever() + sleep(0.5 SECONDS) + playsound(src, 'sound/weapons/gun_interactions/lever_action.ogg', 60, TRUE) + +/mob/living/basic/skeleton/incursion/mobster + name = "skeletal mobster" + desc = "THEY EXAMINED THE SKELETON! RATTLE EM, BOYS!" + icon_state = "skeleton_mobster" + icon_living = "skeleton_mobster" + is_ranged = TRUE + casing_type = /obj/item/ammo_casing/skeleton_smg + ranged_burst_count = 6 + ranged_burst_interval = 0.1 SECONDS + ranged_cooldown = 2.5 SECONDS + ai_controller = /datum/ai_controller/basic_controller/incursion/ranged_distance + +/mob/living/basic/skeleton/incursion/mobster/Initialize(mapload) + . = ..() + AddComponent(/datum/component/aggro_emote, say_list = list("Ice 'em!", "Rattle 'em, boys!", "I've got a bone to pick with you!", "Everyone's got a few skeletons in their closet!")) + if(prob(20)) + loot += pick(/obj/item/clothing/head/fedora, /obj/item/clothing/under/suit/mafia) + +/obj/item/ammo_casing/skeleton_smg + projectile_type = /obj/projectile/bullet/skeleton_smg + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL + +/obj/projectile/bullet/skeleton_smg + damage = 5 + +/mob/living/basic/skeleton/reanimator + name = "skeletal reanimator" + desc = "A dark necromancer from the depths of an unknowable darkness." + icon_state = "skeleton_reanimator" + icon_living = "skeleton_reanimator" + is_ranged = TRUE + projectile_type = /obj/projectile/magic/necrotic_bolt + ranged_burst_count = 2 + ranged_burst_interval = 0.35 SECONDS + projectile_sound = 'sound/magic/magic_missile.ogg' + ai_controller = /datum/ai_controller/basic_controller/incursion/reanimator + /// List of actions the reanimator has + var/list/reanimator_actions = list( + /datum/action/cooldown/mob_cooldown/summon_skulls = BB_REANIMATOR_SKULL_ACTION, + ) + +/mob/living/basic/skeleton/reanimator/Initialize(mapload) + . = ..() + grant_actions_by_list(reanimator_actions) + +/mob/living/basic/skeleton/reanimator/melee_attack(mob/living/carbon/human/target, list/modifiers, ignore_cooldown) + if(!ishuman(target)) + return ..() + if(target.stat != DEAD) + return ..() + new /obj/effect/temp_visual/cult/rune_spawn/rune7(target.loc, 5 SECONDS, "#252525") + new /obj/effect/temp_visual/cult/rune_spawn/rune7/inner(target.loc, 5 SECONDS, "#252525") + new /obj/effect/temp_visual/cult/rune_spawn/rune7/center(target.loc, 5 SECONDS, "#252525") + + // First, see if we can get the OG player. If not, choose from Dchat + var/mob/dead/observer/original_ghost = target.get_ghost() + if(original_ghost) + to_chat(original_ghost, "You are being revived by otherworldly forces! Return to your body if you want to be revived! (Verbs -> Ghost -> Re-enter corpse)") + window_flash(original_ghost.client) + SEND_SOUND(original_ghost, sound('sound/effects/genetics.ogg')) + if(do_after_once(src, 2 SECONDS, target = target, attempt_cancel_message = "You stop reanimating a corpse.", interaction_key = "reanimator_revive")) + sleep(3 SECONDS) // Locks the revitalizer down for 2 seconds, but gives the player 5 seconds to return + reanimate(target) + +/mob/living/basic/skeleton/reanimator/proc/reanimate(mob/living/carbon/human/H) + visible_message("[name] releases dark tendrils into the flesh of [H], morphing their corpse into a grotesque creature!") + var/mob/living/basic/netherworld/blankbody/blank = new(H.loc) + blank.name = "[H]" + blank.desc = "It's [H], but [H.p_their()] flesh has an ashy texture, and [H.p_their()] face is featureless save an eerie smile." + blank.faction = faction.Copy() + visible_message("[blank] staggers to [H.p_their()] feet!") + blank.held_body = H + H.forceMove(blank) + var/mob/dead/observer/ghost = H.get_ghost(TRUE) + if(!ghost) + H.mind.transfer_to(blank) + blank.is_original_mob = TRUE + return + + var/mob/dead/observer/chosen_ghost + var/list/candidates + candidates = SSghost_spawns.poll_candidates("Would you like to play as a Reanimated Blank?", ROLE_SENTIENT, FALSE, poll_time = 10 SECONDS, source = /mob/living/basic/netherworld/blankbody, role_cleanname = "blank") + if(length(candidates)) + chosen_ghost = pick(candidates) + if(chosen_ghost && blank.stat != DEAD) + blank.key = chosen_ghost.key + blank.cancel_camera() + dust_if_respawnable(chosen_ghost) + to_chat(blank, "You have been raised by the dead to serve as a footsoldier in the incursion. Strike down your foes!") + +/obj/projectile/magic/necrotic_bolt + name = "necrotic bolt" + damage = 10 + damage_type = BURN + nodamage = 0 + icon_state = "arcane_barrage" + +/datum/action/cooldown/mob_cooldown/summon_skulls + name = "Summon Skulls" + button_icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + button_icon_state = "legion_head" + desc = "Summon a pair of skulls to seek out and slay enemies." + click_to_activate = FALSE + melee_cooldown_time = CLICK_CD_CLICK_ABILITY + cooldown_time = 20 SECONDS + shared_cooldown = NONE + +/datum/action/cooldown/mob_cooldown/summon_skulls/Activate(atom/target) + var/mob/living/summoner = target + if(!istype(summoner)) + to_chat(target, "You are unable to summon skulls!") + return + + for(var/i in 1 to 2) + var/mob/living/basic/mining/hivelordbrood/reanimator/S = new(summoner.loc) + S.admin_spawned = summoner.admin_spawned + S.faction = summoner.faction.Copy() + S.Move(get_step(summoner.loc, pick(GLOB.alldirs))) + S.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, summoner.ai_controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) + new /obj/effect/temp_visual/emp/pulse/cult(S.loc) + StartCooldown() + +// A fragile skull produced by Skeleton Reanimators +/mob/living/basic/mining/hivelordbrood/reanimator + name = "reanimator skull" + desc = "A cursed skull." + icon_state = "legion_head" + icon_living = "legion_head" + icon_aggro = "legion_head" + icon_dead = "legion_head" + melee_damage_lower = 5 + attack_verb_continuous = "bites" + attack_verb_simple = "bites" + speak_emote = list("chatters") + throw_blocked_message = "is shrugged off by" + ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/prowler diff --git a/code/modules/mob/living/basic/hostile/skeleton_mob.dm b/code/modules/mob/living/basic/hostile/skeleton_mob.dm deleted file mode 100644 index 655cdfcf149..00000000000 --- a/code/modules/mob/living/basic/hostile/skeleton_mob.dm +++ /dev/null @@ -1,100 +0,0 @@ -/mob/living/basic/skeleton - name = "reanimated skeleton" - desc = "A real bonefied skeleton, doesn't seem like it wants to socialize." - icon = 'icons/mob/simple_human.dmi' - icon_state = "skeleton" - icon_living = "skeleton" - speak_emote = list("rattles") - mob_biotypes = MOB_UNDEAD | MOB_HUMANOID - maxHealth = 40 - health = 40 - a_intent = INTENT_HARM - melee_damage_lower = 15 - melee_damage_upper = 15 - harm_intent_damage = 5 - obj_damage = 50 - melee_attack_cooldown_min = 1.5 SECONDS - melee_attack_cooldown_max = 2.5 SECONDS - environment_smash = ENVIRONMENT_SMASH_STRUCTURES - minimum_survivable_temperature = 0 - maximum_survivable_temperature = 1500 - healable = FALSE - attack_verb_simple = "slash" - attack_verb_continuous = "slashes" - attack_sound = 'sound/hallucinations/growl1.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAM = 0, OXY = 1) - unsuitable_atmos_damage = 10 - see_in_dark = 8 - faction = list("skeleton") - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - basic_mob_flags = DEL_ON_DEATH - step_type = FOOTSTEP_MOB_SHOE - gold_core_spawnable = HOSTILE_SPAWN - loot = list(/obj/effect/decal/remains/human) - ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/prowler - -/mob/living/basic/skeleton/Initialize(mapload) - . = ..() - AddElement(/datum/element/ai_retaliate) - AddComponent(/datum/component/event_tracker) - -/mob/living/basic/skeleton/event_cost() - . = list() - if(is_station_level((get_turf(src)).z) && stat != DEAD) - return list(ASSIGNMENT_SECURITY = 0.5, ASSIGNMENT_CREW = 1, ASSIGNMENT_MEDICAL = 0.5) - - -/mob/living/basic/skeleton/arctic - name = "undead arctic explorer" - desc = "The reanimated remains of some poor traveler." - icon_state = "arctic_skeleton" - icon_living = "arctic_skeleton" - maxHealth = 55 - health = 55 - weather_immunities = list("snow") - gold_core_spawnable = NO_SPAWN - melee_damage_lower = 17 - melee_damage_upper = 20 - death_message = "collapses into a pile of bones, its gear falling to the floor!" - loot = list(/obj/effect/decal/remains/human, - /obj/item/spear, - /obj/item/clothing/shoes/winterboots, - /obj/item/clothing/suit/hooded/wintercoat) - -/mob/living/basic/skeleton/warden - name = "skeleton warden" - desc = "The remains of a warden." - icon_state = "skeleton_warden" - icon_living = "skeleton_warden" - loot = list(/obj/effect/decal/cleanable/shreds, /mob/living/basic/skeleton/angered_warden) - maxHealth = 300 - health = 300 - death_message = null - gold_core_spawnable = NO_SPAWN - ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/skeleton_warden - -/mob/living/basic/skeleton/warden/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) - return TRUE - -/mob/living/basic/skeleton/angered_warden - name = "angered skeleton warden" // round 2 - desc = "An angry skeleton." - icon_state = "skeleton_warden_alt" - icon_living = "skeleton_warden_alt" - attack_verb_simple = "claw" - attack_verb_continuous = "claws" - maxHealth = 200 - health = 200 - speed = -1 - melee_damage_lower = 30 - melee_damage_upper = 30 - loot = list(/obj/effect/decal/remains/human, /obj/item/clothing/head/warden, /obj/item/card/sec_shuttle_ruin) - gold_core_spawnable = NO_SPAWN - ai_controller = /datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/skeleton_warden - -/mob/living/basic/skeleton/angered_warden/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) - return TRUE - -/datum/ai_controller/basic_controller/simple/simple_hostile_obstacles/skeleton_warden - idle_behavior = null // Don't idly wander diff --git a/code/modules/projectiles/projectile/magic_projectiles.dm b/code/modules/projectiles/projectile/magic_projectiles.dm index ecb4bffdaf6..ae019e24eb6 100644 --- a/code/modules/projectiles/projectile/magic_projectiles.dm +++ b/code/modules/projectiles/projectile/magic_projectiles.dm @@ -94,6 +94,12 @@ exp_flash = 4 exp_fire= 5 +/obj/projectile/magic/fireball/small + name = "firebolt" + exp_heavy = -1 + exp_light = 1 + exp_fire = 3 + /obj/projectile/magic/resurrection name = "bolt of resurrection" icon_state = "ion" diff --git a/code/modules/projectiles/projectile/special_projectiles.dm b/code/modules/projectiles/projectile/special_projectiles.dm index c51298c1574..59b86e9bea9 100644 --- a/code/modules/projectiles/projectile/special_projectiles.dm +++ b/code/modules/projectiles/projectile/special_projectiles.dm @@ -300,14 +300,44 @@ pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE /obj/projectile/energy/demonic_grappler/on_hit(atom/target, blocked = 0) - if(isliving(target)) - var/turf/source_turf = get_turf(firer) - do_teleport(target, source_turf) - else - var/turf/miss_turf = get_step(target, get_dir(target, firer)) - do_teleport(firer, miss_turf) + var/turf/miss_turf = get_step(target, get_dir(target, firer)) + do_teleport(firer, miss_turf) return ..() +/obj/projectile/energy/demonic_shocker + name = "demonic shocker" + icon_state = "electrode" + nodamage = 1 + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + hitsound = 'sound/magic/lightningbolt.ogg' + /// Maximum amount of bounces + var/bounce_count = 3 + +/obj/projectile/energy/demonic_shocker/on_hit(atom/target, blocked = 0) + if(!isliving(target)) + return ..() + chain(target, firer) + return ..() + +/obj/projectile/energy/demonic_shocker/proc/chain(mob/living/target, mob/source) + var/mob/living/L = target + L.electrocute_act(20, firer, flags = SHOCK_NOGLOVES) + if(bounce_count <= 0) + return + + var/list/possible_targets = list() + for(var/mob/living/M in oview(3, target)) + if(firer == M || M.faction_check_mob(firer)) + continue + possible_targets += M + if(!length(possible_targets)) + return + + var/next_victim = pick(possible_targets) + bounce_count -= 1 + playsound(get_turf(target), 'sound/magic/lightningshock.ogg', 50, TRUE, -1) + chain(next_victim, target) + /obj/projectile/snowball name = "snowball" icon_state = "snowball" diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 59cd7d6f592..3426d93c47d 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ diff --git a/icons/mob/simple_human.dmi b/icons/mob/simple_human.dmi index c1f1a0ca035..30a46a56396 100644 Binary files a/icons/mob/simple_human.dmi and b/icons/mob/simple_human.dmi differ diff --git a/paradise.dme b/paradise.dme index 0bbf9e40a70..b8e73f90489 100644 --- a/paradise.dme +++ b/paradise.dme @@ -162,6 +162,7 @@ #include "code\__DEFINES\ai\ai_defines.dm" #include "code\__DEFINES\ai\blackboard_defines.dm" #include "code\__DEFINES\ai\changeling_giant_spider_ai_defines.dm" +#include "code\__DEFINES\ai\incursion_blackboard_defines.dm" #include "code\__DEFINES\dcs\ai_signals.dm" #include "code\__DEFINES\dcs\area_signals.dm" #include "code\__DEFINES\dcs\atom_signals.dm" @@ -2494,17 +2495,13 @@ #include "code\modules\mob\living\basic\hostile\bats.dm" #include "code\modules\mob\living\basic\hostile\bear.dm" #include "code\modules\mob\living\basic\hostile\carp.dm" -#include "code\modules\mob\living\basic\hostile\creature.dm" #include "code\modules\mob\living\basic\hostile\drakehound.dm" #include "code\modules\mob\living\basic\hostile\feral_cat.dm" -#include "code\modules\mob\living\basic\hostile\hellhound.dm" -#include "code\modules\mob\living\basic\hostile\hellhound_ai.dm" #include "code\modules\mob\living\basic\hostile\hivebot.dm" #include "code\modules\mob\living\basic\hostile\hivebot_ai.dm" #include "code\modules\mob\living\basic\hostile\jungle_animals.dm" #include "code\modules\mob\living\basic\hostile\killertomato.dm" #include "code\modules\mob\living\basic\hostile\pirate.dm" -#include "code\modules\mob\living\basic\hostile\skeleton_mob.dm" #include "code\modules\mob\living\basic\hostile\spiderlings.dm" #include "code\modules\mob\living\basic\hostile\soviet.dm" #include "code\modules\mob\living\basic\hostile\tree.dm" @@ -2534,6 +2531,16 @@ #include "code\modules\mob\living\basic\hostile\gorilla\gorilla_emote.dm" #include "code\modules\mob\living\basic\hostile\revenant\revenant.dm" #include "code\modules\mob\living\basic\hostile\revenant\revenant_abilities.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\blankbody.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\creature.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\faithless.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\flesh_spider.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\hellhound.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\hellhound_ai.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\incursion_ai.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\migo.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\nether_mobs.dm" +#include "code\modules\mob\living\basic\hostile\nether_mobs\skeleton_mob.dm" #include "code\modules\mob\living\basic\lavaland\goliath\goliath.dm" #include "code\modules\mob\living\basic\lavaland\goliath\goliath_actions.dm" #include "code\modules\mob\living\basic\lavaland\goliath\goliath_ai.dm" @@ -2551,10 +2558,6 @@ #include "code\modules\mob\living\basic\mining\mining_mobs.dm" #include "code\modules\mob\living\basic\mining\goldgrub\goldgrub.dm" #include "code\modules\mob\living\basic\mining\goldgrub\goldgrub_ai.dm" -#include "code\modules\mob\living\basic\nether_mobs\blankbody.dm" -#include "code\modules\mob\living\basic\nether_mobs\faithless.dm" -#include "code\modules\mob\living\basic\nether_mobs\migo.dm" -#include "code\modules\mob\living\basic\nether_mobs\nether_mobs.dm" #include "code\modules\mob\living\basic\retaliate\clown.dm" #include "code\modules\mob\living\basic\retaliate\combat_drone.dm" #include "code\modules\mob\living\basic\retaliate\kangaroo.dm"