From ade3fb28136ebc708bc5f563393086cffeb49c5b Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 8 Oct 2023 14:06:23 +0200 Subject: [PATCH] [MIRROR] ice demon basic mobs [MDB IGNORE] (#24188) * ice demon basic mobs * Modular updates * Modular updates --------- Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Co-authored-by: Giz <13398309+vinylspiders@users.noreply.github.com> --- code/__DEFINES/ai/monsters.dm | 16 +++ .../basic_subtrees/maintain_distance.dm | 36 +++++- .../basic_subtrees/ranged_skirmish.dm | 26 ++-- .../teleport_away_from_target.dm | 56 +++++++++ .../ai/hunting_behavior/hunting_behaviors.dm | 4 +- code/datums/components/joint_damage.dm | 35 ++++++ code/datums/mapgen/Cavegens/IcemoonCaves.dm | 4 +- .../structures/icemoon/cave_entrance.dm | 2 +- .../mining/equipment/kinetic_crusher.dm | 33 +++++ .../basic/icemoon/ice_demon/ice_demon.dm | 89 +++++++++++++ .../icemoon/ice_demon/ice_demon_abilities.dm | 117 +++++++++++++++++ .../basic/icemoon/ice_demon/ice_demon_ai.dm | 118 ++++++++++++++++++ .../basic/lavaland/watcher/watcher_ai.dm | 5 +- .../hostile/mining_mobs/ice_demon.dm | 85 ------------- .../unit_tests/simple_animal_freeze.dm | 1 - icons/obj/mining_zones/artefacts.dmi | Bin 47492 -> 48023 bytes .../mob/living/basic/icemoon/ice_demon.dm | 3 + .../ashwalkers/code/effects/ash_rituals.dm | 2 +- tgstation.dme | 7 +- .../UpdatePaths/Scripts/78539_ice_demons.txt | 1 + 20 files changed, 528 insertions(+), 112 deletions(-) create mode 100644 code/datums/ai/basic_mobs/basic_subtrees/teleport_away_from_target.dm create mode 100644 code/datums/components/joint_damage.dm create mode 100644 code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm create mode 100644 code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_abilities.dm create mode 100644 code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm delete mode 100644 code/modules/mob/living/simple_animal/hostile/mining_mobs/ice_demon.dm create mode 100644 modular_skyrat/master_files/code/modules/mob/living/basic/icemoon/ice_demon.dm create mode 100644 tools/UpdatePaths/Scripts/78539_ice_demons.txt diff --git a/code/__DEFINES/ai/monsters.dm b/code/__DEFINES/ai/monsters.dm index be9a4be34cd..89670d4f346 100644 --- a/code/__DEFINES/ai/monsters.dm +++ b/code/__DEFINES/ai/monsters.dm @@ -147,6 +147,22 @@ /// key holds the tray we will beam #define BB_BEAMABLE_HYDROPLANT_TARGET "beamable_hydroplant_target" +//ice demons +///the list of items we are afraid of +#define BB_LIST_SCARY_ITEMS "list_scary_items" +///our teleportation ability +#define BB_DEMON_TELEPORT_ABILITY "demon_teleport_ability" +///the destination of our teleport ability +#define BB_TELEPORT_DESTINATION "teleport_destination" +///the ability to clone ourself +#define BB_DEMON_CLONE_ABILITY "demon_clone_ability" +///our slippery ice ability +#define BB_DEMON_SLIP_ABILITY "demon_slip_ability" +///the turf we are escaping too +#define BB_ESCAPE_DESTINATION "escape_destination" +///how far away we will be from our target before teleporting +#define BB_MINIMUM_DISTANCE_RANGE "minimum_distance_range" + /// Corpse we have consumed #define BB_LEGION_CORPSE "legion_corpse" /// Things our target recently said diff --git a/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm b/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm index c09e7cdbf75..2a85e9e902b 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/maintain_distance.dm @@ -8,6 +8,8 @@ var/maximum_distance = 6 /// How far do we look for our target? var/view_distance = 10 + /// the run away behavior we will use + var/run_away_behavior = /datum/ai_behavior/step_away /datum/ai_planning_subtree/maintain_distance/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) . = ..() @@ -16,12 +18,15 @@ return // Don't run away from cucumbers, they're not snakes var/range = get_dist(controller.pawn, target) if (range < minimum_distance) - controller.queue_behavior(/datum/ai_behavior/step_away, target_key) + controller.queue_behavior(run_away_behavior, target_key, minimum_distance) return if (range > maximum_distance) controller.queue_behavior(/datum/ai_behavior/pursue_to_range, target_key, maximum_distance) return +/datum/ai_planning_subtree/maintain_distance/cover_minimum_distance + run_away_behavior = /datum/ai_behavior/cover_minimum_distance + /// Take one step away /datum/ai_behavior/step_away behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION @@ -80,3 +85,32 @@ if (!QDELETED(current_target) && get_dist(controller.pawn, current_target) > range) return finish_action(controller, succeeded = TRUE) + +///instead of taking a single step, we cover the entire distance +/datum/ai_behavior/cover_minimum_distance + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + required_distance = 0 + action_cooldown = 0.2 SECONDS + +/datum/ai_behavior/cover_minimum_distance/setup(datum/ai_controller/controller, target_key, minimum_distance) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + var/required_distance = minimum_distance - get_dist(controller.pawn, target) //the distance we need to move + var/distance = 0 + var/turf/chosen_turf + for(var/turf/open/potential_turf in oview(required_distance, controller.pawn)) + var/new_distance_from_target = get_dist(potential_turf, target) + if(potential_turf.is_blocked_turf()) + continue + if(new_distance_from_target > distance) + chosen_turf = potential_turf + distance = new_distance_from_target + if(isnull(chosen_turf)) + return FALSE + set_movement_target(controller, target = chosen_turf) + +/datum/ai_behavior/cover_minimum_distance/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + finish_action(controller, succeeded = TRUE) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm b/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm index 1ff752d925f..be395f3dfe4 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm @@ -4,28 +4,28 @@ /// Blackboard key holding target atom var/target_key = BB_BASIC_MOB_CURRENT_TARGET /// What AI behaviour do we actually run? - var/datum/ai_behavior/ranged_skirmish/attack_behavior = /datum/ai_behavior/ranged_skirmish - -/datum/ai_planning_subtree/ranged_skirmish/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - . = ..() - if(!controller.blackboard_key_exists(target_key)) - return - controller.queue_behavior(attack_behavior, target_key, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) - -/// How often will we try to perform our ranged attack? -/datum/ai_behavior/ranged_skirmish - action_cooldown = 1 SECONDS + var/attack_behavior = /datum/ai_behavior/ranged_skirmish /// If target is further away than this we don't fire var/max_range = 9 /// If target is closer than this we don't fire var/min_range = 2 -/datum/ai_behavior/ranged_skirmish/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) +/datum/ai_planning_subtree/ranged_skirmish/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + . = ..() + if(!controller.blackboard_key_exists(target_key)) + return + controller.queue_behavior(attack_behavior, target_key, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION, max_range, min_range) + +/// How often will we try to perform our ranged attack? +/datum/ai_behavior/ranged_skirmish + action_cooldown = 1 SECONDS + +/datum/ai_behavior/ranged_skirmish/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key, max_range, min_range) . = ..() var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key] return !QDELETED(target) -/datum/ai_behavior/ranged_skirmish/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) +/datum/ai_behavior/ranged_skirmish/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key, max_range, min_range) . = ..() var/atom/target = controller.blackboard[target_key] if (QDELETED(target)) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/teleport_away_from_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/teleport_away_from_target.dm new file mode 100644 index 00000000000..dadba992e9f --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/teleport_away_from_target.dm @@ -0,0 +1,56 @@ +///behavior to activate ability to escape from target +/datum/ai_planning_subtree/teleport_away_from_target + ///minimum distance away from the target before we execute behavior + var/minimum_distance = 2 + ///the ability we will execute + var/ability_key + +/datum/ai_planning_subtree/teleport_away_from_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + return + var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + var/distance_from_target = get_dist(target, controller.pawn) + if(distance_from_target >= minimum_distance) + controller.clear_blackboard_key(BB_ESCAPE_DESTINATION) + return + var/datum/action/cooldown/ability = controller.blackboard[ability_key] + if(!ability?.IsAvailable()) + return + var/turf/location_turf = controller.blackboard[BB_ESCAPE_DESTINATION] + + if(isnull(location_turf)) + controller.queue_behavior(/datum/ai_behavior/find_furthest_turf_from_target, BB_BASIC_MOB_CURRENT_TARGET, BB_ESCAPE_DESTINATION, minimum_distance) + return SUBTREE_RETURN_FINISH_PLANNING + + if(get_dist(location_turf, target) < minimum_distance || !can_see(controller.pawn, location_turf)) //target moved close too close or we moved too far since finding the target turf + controller.clear_blackboard_key(BB_ESCAPE_DESTINATION) + return + + controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target, ability_key, BB_ESCAPE_DESTINATION) + +///find furtherst turf target so we may teleport to it +/datum/ai_behavior/find_furthest_turf_from_target + +/datum/ai_behavior/find_furthest_turf_from_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, set_key, range) + var/mob/living/living_target = controller.blackboard[target_key] + if(QDELETED(living_target)) + return + + var/distance = 0 + var/turf/chosen_turf + for(var/turf/open/potential_destination in oview(range, living_target)) + if(potential_destination.is_blocked_turf()) + continue + var/new_distance_to_target = get_dist(potential_destination, living_target) + if(new_distance_to_target > distance) + chosen_turf = potential_destination + distance = new_distance_to_target + if(distance == range) + break //we have already found the max distance + + if(isnull(chosen_turf)) + finish_action(controller, FALSE) + return + + controller.set_blackboard_key(set_key, chosen_turf) + finish_action(controller, TRUE) diff --git a/code/datums/ai/hunting_behavior/hunting_behaviors.dm b/code/datums/ai/hunting_behavior/hunting_behaviors.dm index ba7c7f2ffba..468cfed33fb 100644 --- a/code/datums/ai/hunting_behavior/hunting_behaviors.dm +++ b/code/datums/ai/hunting_behavior/hunting_behaviors.dm @@ -94,8 +94,6 @@ var/atom/hunted = controller.blackboard[hunting_target_key] if(QDELETED(hunted)) - //Target is gone for some reason. forget about this task! - controller[hunting_target_key] = null finish_action(controller, FALSE, hunting_target_key) else target_caught(hunter, hunted) @@ -136,7 +134,7 @@ /datum/ai_behavior/hunt_target/use_ability_on_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key) var/datum/action/cooldown/ability = controller.blackboard[ability_key] - if(QDELETED(ability) || !ability.IsAvailable()) + if(!ability?.IsAvailable()) finish_action(controller, FALSE, hunting_target_key) return ..() diff --git a/code/datums/components/joint_damage.dm b/code/datums/components/joint_damage.dm new file mode 100644 index 00000000000..5397bd307ca --- /dev/null +++ b/code/datums/components/joint_damage.dm @@ -0,0 +1,35 @@ +/* + * A component given to mobs to damage a linked mob + */ +/datum/component/joint_damage + ///the mob we will damage + var/datum/weakref/overlord_mob + ///our last health count + var/previous_health_count + +/datum/component/joint_damage/Initialize(mob/overlord_mob) + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + var/mob/living/parent_mob = parent + previous_health_count = parent_mob.health + if(overlord_mob) + src.overlord_mob = WEAKREF(overlord_mob) + +/datum/component/joint_damage/RegisterWithParent() + RegisterSignal(parent, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(damage_overlord)) + RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(damage_overlord)) + +/datum/component/joint_damage/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_LIVING_HEALTH_UPDATE, COMSIG_LIVING_DEATH)) + +/datum/component/joint_damage/Destroy() + overlord_mob = null + return ..() + +/datum/component/joint_damage/proc/damage_overlord(mob/living/source) + SIGNAL_HANDLER + + var/mob/living/overlord_to_damage = overlord_mob?.resolve() + if(!isnull(overlord_to_damage)) + overlord_to_damage.adjustBruteLoss(previous_health_count - source.health) ///damage or heal overlord + previous_health_count = source.health diff --git a/code/datums/mapgen/Cavegens/IcemoonCaves.dm b/code/datums/mapgen/Cavegens/IcemoonCaves.dm index 7d7437ccda6..b0fcd471db2 100644 --- a/code/datums/mapgen/Cavegens/IcemoonCaves.dm +++ b/code/datums/mapgen/Cavegens/IcemoonCaves.dm @@ -62,12 +62,12 @@ weighted_closed_turf_types = list(/turf/closed/mineral/random/snow/underground = 1) weighted_mob_spawn_list = list( SPAWN_MEGAFAUNA = 1, + /mob/living/basic/mining/ice_demon = 100, /mob/living/basic/mining/ice_whelp = 60, /mob/living/basic/mining/legion/snow = 100, - /mob/living/simple_animal/hostile/asteroid/ice_demon = 100, /obj/structure/spawner/ice_moon/demonic_portal = 6, - /obj/structure/spawner/ice_moon/demonic_portal/snowlegion = 6, /obj/structure/spawner/ice_moon/demonic_portal/ice_whelp = 6, + /obj/structure/spawner/ice_moon/demonic_portal/snowlegion = 6, ) weighted_megafauna_spawn_list = list(/mob/living/simple_animal/hostile/megafauna/colossus = 1) weighted_flora_spawn_list = list( diff --git a/code/game/objects/structures/icemoon/cave_entrance.dm b/code/game/objects/structures/icemoon/cave_entrance.dm index 2a90270ef73..add1f278569 100644 --- a/code/game/objects/structures/icemoon/cave_entrance.dm +++ b/code/game/objects/structures/icemoon/cave_entrance.dm @@ -80,7 +80,7 @@ GLOBAL_LIST_INIT(ore_probability, list( name = "demonic portal" desc = "A portal that goes to another world, normal creatures couldn't survive there." icon_state = "nether" - mob_types = list(/mob/living/simple_animal/hostile/asteroid/ice_demon) + mob_types = list(/mob/living/basic/mining/ice_demon) light_range = 1 light_color = COLOR_SOFT_RED mob_gps_id = "WT|B" // watcher | bluespace diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index caee1bbac8d..4ceddb09854 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -437,3 +437,36 @@ chaser.monster_damage_boost = FALSE // Weaker cuz no cooldown chaser.damage = 20 log_combat(user, target, "fired a chaser at", src) + +/obj/item/crusher_trophy/ice_demon_cube + name = "demonic cube" + desc = "A stone cold cube dropped from an ice demon." + icon_state = "ice_demon_cube" + denied_type = /obj/item/crusher_trophy/ice_demon_cube + ///how many will we summon? + var/summon_amount = 2 + ///cooldown to summon demons upon the target + COOLDOWN_DECLARE(summon_cooldown) + +/obj/item/crusher_trophy/ice_demon_cube/effect_desc() + return "mark detonation to unleash demonic ice clones upon the target" + +/obj/item/crusher_trophy/ice_demon_cube/on_mark_detonation(mob/living/target, mob/living/user) + if(isnull(target) || !COOLDOWN_FINISHED(src, summon_cooldown)) + return + for(var/i in 1 to summon_amount) + var/turf/drop_off = find_dropoff_turf(target, user) + var/mob/living/basic/mining/demon_afterimage/crusher/friend = new(drop_off) + friend.faction = list(FACTION_NEUTRAL) + friend.befriend(user) + friend.ai_controller?.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target) + COOLDOWN_START(src, summon_cooldown, 30 SECONDS) + +///try to make them spawn all around the target to surround him +/obj/item/crusher_trophy/ice_demon_cube/proc/find_dropoff_turf(mob/living/target, mob/living/user) + var/list/turfs_list = get_adjacent_open_turfs(target) + for(var/turf/possible_turf in turfs_list) + if(possible_turf.is_blocked_turf()) + continue + return possible_turf + return get_turf(user) diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm new file mode 100644 index 00000000000..960f875365b --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm @@ -0,0 +1,89 @@ +/mob/living/basic/mining/ice_demon + name = "demonic watcher" + desc = "A creature formed entirely out of ice, bluespace energy emanates from inside of it." + icon = 'icons/mob/simple/icemoon/icemoon_monsters.dmi' + icon_state = "ice_demon" + icon_living = "ice_demon" + icon_gib = "syndicate_gib" + mob_biotypes = MOB_ORGANIC|MOB_BEAST + mouse_opacity = MOUSE_OPACITY_ICON + basic_mob_flags = DEL_ON_DEATH + speed = 2 + maxHealth = 150 + health = 150 + obj_damage = 40 + melee_damage_lower = 15 + melee_damage_upper = 15 + attack_verb_continuous = "slices" + attack_verb_simple = "slice" + attack_sound = 'sound/weapons/bladeslice.ogg' + attack_vis_effect = ATTACK_EFFECT_SLASH + move_force = MOVE_FORCE_VERY_STRONG + move_resist = MOVE_FORCE_VERY_STRONG + pull_force = MOVE_FORCE_VERY_STRONG + crusher_loot = /obj/item/crusher_trophy/ice_demon_cube + ai_controller = /datum/ai_controller/basic_controller/ice_demon + death_message = "fades as the energies that tied it to this world dissipate." + death_sound = 'sound/magic/demon_dies.ogg' + +/mob/living/basic/mining/ice_demon/Initialize(mapload) + . = ..() + var/datum/action/cooldown/mob_cooldown/slippery_ice_floors/ice_floor = new(src) + ice_floor.Grant(src) + ai_controller.set_blackboard_key(BB_DEMON_SLIP_ABILITY, ice_floor) + var/datum/action/cooldown/mob_cooldown/ice_demon_teleport/demon_teleport = new(src) + demon_teleport.Grant(src) + ai_controller.set_blackboard_key(BB_DEMON_TELEPORT_ABILITY, demon_teleport) + var/datum/action/cooldown/spell/conjure/create_afterimages/afterimage = new(src) + afterimage.Grant(src) + ai_controller.set_blackboard_key(BB_DEMON_CLONE_ABILITY, afterimage) + AddComponent(\ + /datum/component/ranged_attacks,\ + projectile_type = /obj/projectile/temp/ice_demon,\ + projectile_sound = 'sound/weapons/pierce.ogg',\ + ) + var/static/list/death_loot = list(/obj/item/stack/ore/bluespace_crystal = 3) + AddElement(/datum/element/death_drops, death_loot) + AddElement(/datum/element/simple_flying) + +/mob/living/basic/mining/ice_demon/death(gibbed) + if(prob(5)) + new /obj/item/raw_anomaly_core/bluespace(loc) + return ..() + +/mob/living/basic/mining/demon_afterimage + name = "afterimage demonic watcher" + desc = "Is this some sort of illusion?" + icon = 'icons/mob/simple/icemoon/icemoon_monsters.dmi' + icon_state = "ice_demon" + icon_living = "ice_demon" + icon_gib = "syndicate_gib" + mob_biotypes = MOB_ORGANIC|MOB_BEAST + mouse_opacity = MOUSE_OPACITY_ICON + basic_mob_flags = DEL_ON_DEATH + speed = 5 + maxHealth = 20 + health = 20 + melee_damage_lower = 5 + melee_damage_upper = 5 + attack_verb_continuous = "slices" + attack_verb_simple = "slice" + attack_sound = 'sound/weapons/bladeslice.ogg' + alpha = 80 + ai_controller = /datum/ai_controller/basic_controller/ice_demon/afterimage + ///how long do we exist for + var/existence_period = 15 SECONDS + +/mob/living/basic/mining/demon_afterimage/Initialize(mapload) + . = ..() + AddElement(/datum/element/simple_flying) + AddElement(/datum/element/temporary_atom, life_time = existence_period) + +///afterimage subtypes summoned by the crusher +/mob/living/basic/mining/demon_afterimage/crusher + speed = 2 + health = 60 + maxHealth = 60 + melee_damage_lower = 10 + melee_damage_upper = 10 + existence_period = 7 SECONDS diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_abilities.dm b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_abilities.dm new file mode 100644 index 00000000000..79c9ee9bd58 --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_abilities.dm @@ -0,0 +1,117 @@ +/obj/projectile/temp/ice_demon + name = "ice blast" + icon_state = "ice_2" + damage = 5 + damage_type = BURN + armor_flag = ENERGY + speed = 1 + pixel_speed_multiplier = 0.25 + temperature = -75 + +/datum/action/cooldown/mob_cooldown/ice_demon_teleport + name = "Bluespace Teleport" + desc = "Teleport towards a destination target!" + button_icon = 'icons/obj/ore.dmi' + button_icon_state = "bluespace_crystal" + cooldown_time = 3 SECONDS + melee_cooldown_time = 0 SECONDS + ///time delay before teleport + var/time_delay = 0.5 SECONDS + +/datum/action/cooldown/mob_cooldown/ice_demon_teleport/Activate(atom/target_atom) + if(isclosedturf(get_turf(target_atom))) + owner.balloon_alert(owner, "blocked!") + return FALSE + animate(owner, transform = matrix().Scale(0.8), time = time_delay, easing = SINE_EASING) + addtimer(CALLBACK(src, PROC_REF(teleport_to_turf), target_atom), time_delay) + StartCooldown() + return TRUE + +/datum/action/cooldown/mob_cooldown/ice_demon_teleport/proc/teleport_to_turf(atom/target) + animate(owner, transform = matrix(), time = 0.5 SECONDS, easing = SINE_EASING) + do_teleport(teleatom = owner, destination = target, channel = TELEPORT_CHANNEL_BLUESPACE, forced = TRUE) + +/datum/action/cooldown/mob_cooldown/slippery_ice_floors + name = "Iced Floors" + desc = "Summon slippery ice floors all around!" + button_icon = 'icons/turf/floors/ice_turf.dmi' + button_icon_state = "ice_turf-6" + cooldown_time = 2 SECONDS + click_to_activate = FALSE + melee_cooldown_time = 0 SECONDS + ///perimeter we will spawn the iced floors on + var/radius = 1 + ///intervals we will spawn the ice floors in + var/spread_duration = 0.2 SECONDS + +/datum/action/cooldown/mob_cooldown/slippery_ice_floors/Activate(atom/target_atom) + for(var/i in 0 to radius) + var/list/list_of_turfs = border_diamond_range_turfs(owner, i) + addtimer(CALLBACK(src, PROC_REF(spawn_icy_floors), list_of_turfs), i * spread_duration) + StartCooldown() + return TRUE + +/datum/action/cooldown/mob_cooldown/slippery_ice_floors/proc/spawn_icy_floors(list/list_of_turfs) + if(!length(list_of_turfs)) + return + for(var/turf/location in list_of_turfs) + if(isnull(location)) + continue + if(isclosedturf(location) || isspaceturf(location)) + continue + new /obj/effect/temp_visual/slippery_ice(location) + +/obj/effect/temp_visual/slippery_ice + name = "slippery acid" + icon = 'icons/turf/floors/ice_turf.dmi' + icon_state = "ice_turf-6" + layer = BELOW_MOB_LAYER + plane = GAME_PLANE + anchored = TRUE + duration = 3 SECONDS + alpha = 100 + /// how long does it take for the effect to phase in + var/phase_in_period = 2 SECONDS + +/obj/effect/temp_visual/slippery_ice/Initialize(mapload) + . = ..() + animate(src, alpha = 160, time = phase_in_period) + animate(alpha = 0, time = duration - phase_in_period) /// slowly fade out of existence + addtimer(CALLBACK(src, PROC_REF(add_slippery_component), phase_in_period)) //only become slippery after we phased in + +/obj/effect/temp_visual/slippery_ice/proc/add_slippery_component() + AddComponent(/datum/component/slippery, 2 SECONDS) + +/datum/action/cooldown/spell/conjure/create_afterimages + name = "Create After Images" + button_icon = 'icons/mob/simple/icemoon/icemoon_monsters.dmi' + button_icon_state = "ice_demon" + spell_requirements = NONE + cooldown_time = 1 MINUTES + summon_type = list(/mob/living/basic/mining/demon_afterimage) + summon_radius = 1 + summon_amount = 2 + ///max number of after images + var/max_afterimages = 2 + ///How many clones do we have summoned + var/number_of_afterimages = 0 + +/datum/action/cooldown/spell/conjure/create_afterimages/can_cast_spell(feedback = TRUE) + . = ..() + if(!.) + return FALSE + if(number_of_afterimages >= max_afterimages) + return FALSE + return TRUE + +/datum/action/cooldown/spell/conjure/create_afterimages/post_summon(atom/summoned_object, atom/cast_on) + var/mob/living/basic/created_copy = summoned_object + created_copy.AddComponent(/datum/component/joint_damage, overlord_mob = owner) + RegisterSignals(created_copy, list(COMSIG_QDELETING, COMSIG_LIVING_DEATH), PROC_REF(delete_copy)) + number_of_afterimages++ + +/datum/action/cooldown/spell/conjure/create_afterimages/proc/delete_copy(mob/source) + SIGNAL_HANDLER + + UnregisterSignal(source, list(COMSIG_QDELETING, COMSIG_LIVING_DEATH)) + number_of_afterimages-- diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm new file mode 100644 index 00000000000..b17d5c1a30d --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon_ai.dm @@ -0,0 +1,118 @@ +/datum/ai_controller/basic_controller/ice_demon + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_LIST_SCARY_ITEMS = list( + /obj/item/weldingtool, + /obj/item/flashlight/flare, + ), + BB_BASIC_MOB_FLEEING = TRUE, + BB_MINIMUM_DISTANCE_RANGE = 3, + ) + + 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/flee_target/ice_demon, + /datum/ai_planning_subtree/ranged_skirmish/ice_demon, + /datum/ai_planning_subtree/maintain_distance/cover_minimum_distance/ice_demon, + /datum/ai_planning_subtree/teleport_away_from_target, + /datum/ai_planning_subtree/find_and_hunt_target/teleport_destination, + /datum/ai_planning_subtree/targeted_mob_ability/summon_afterimages, + ) + + +/datum/ai_planning_subtree/maintain_distance/cover_minimum_distance/ice_demon + maximum_distance = 7 + +/datum/ai_planning_subtree/teleport_away_from_target + ability_key = BB_DEMON_TELEPORT_ABILITY + +/datum/ai_planning_subtree/find_and_hunt_target/teleport_destination + target_key = BB_TELEPORT_DESTINATION + hunting_behavior = /datum/ai_behavior/hunt_target/use_ability_on_target/demon_teleport + finding_behavior = /datum/ai_behavior/find_valid_teleport_location + hunt_targets = list(/turf/open) + hunt_range = 3 + finish_planning = FALSE + +/datum/ai_planning_subtree/find_and_hunt_target/teleport_destination/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + return + if(controller.blackboard_key_exists(BB_ESCAPE_DESTINATION)) + controller.clear_blackboard_key(BB_TELEPORT_DESTINATION) + return + var/datum/action/cooldown/ability = controller.blackboard[BB_DEMON_TELEPORT_ABILITY] + if(!ability?.IsAvailable()) + return + return ..() + +/datum/ai_behavior/find_valid_teleport_location + +/datum/ai_behavior/find_valid_teleport_location/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, types_to_hunt, hunt_range) + . = ..() + var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + var/list/possible_turfs = list() + + if(QDELETED(target)) + finish_action(controller, FALSE) + return + + for(var/turf/open/potential_turf in oview(hunt_range, target)) //we check for turfs around the target + if(potential_turf.is_blocked_turf()) + continue + if(!can_see(target, potential_turf, hunt_range)) + continue + possible_turfs += potential_turf + + if(!length(possible_turfs)) + finish_action(controller, FALSE) + return + + controller.set_blackboard_key(hunting_target_key, pick(possible_turfs)) + finish_action(controller, TRUE) + +/datum/ai_behavior/hunt_target/use_ability_on_target/demon_teleport + hunt_cooldown = 2 SECONDS + ability_key = BB_DEMON_TELEPORT_ABILITY + behavior_flags = NONE + +/datum/ai_planning_subtree/targeted_mob_ability/summon_afterimages + ability_key = BB_DEMON_CLONE_ABILITY + +/datum/ai_planning_subtree/targeted_mob_ability/summon_afterimages/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + if(living_pawn.health / living_pawn.maxHealth > 0.5) //only use this ability when under half health + return + return ..() + +/datum/ai_planning_subtree/flee_target/ice_demon + +/datum/ai_planning_subtree/flee_target/ice_demon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + if(QDELETED(target)) + return + if(!iscarbon(target)) + return + var/mob/living/carbon/human_target = target + + for(var/obj/held_item in human_target.held_items) + if(!is_type_in_list(held_item, controller.blackboard[BB_LIST_SCARY_ITEMS])) + continue + if(!held_item.light_on) + continue + var/datum/action/cooldown/slip_ability = controller.blackboard[BB_DEMON_SLIP_ABILITY] + if(slip_ability?.IsAvailable()) + controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_DEMON_SLIP_ABILITY) + return ..() + +/datum/ai_planning_subtree/ranged_skirmish/ice_demon + min_range = 0 + +/datum/ai_controller/basic_controller/ice_demon/afterimage + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/flee_target/ice_demon, //even the afterimages are afraid of flames! + /datum/ai_planning_subtree/basic_melee_attack_subtree, + ) + diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm index a25234817f3..348bbcfcaa7 100644 --- a/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm +++ b/code/modules/mob/living/basic/lavaland/watcher/watcher_ai.dm @@ -27,13 +27,10 @@ return ..() /datum/ai_planning_subtree/ranged_skirmish/watcher - attack_behavior = /datum/ai_behavior/ranged_skirmish/watcher + min_range = 0 /datum/ai_planning_subtree/ranged_skirmish/watcher/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] if (QDELETED(target) || HAS_TRAIT(target, TRAIT_OVERWATCHED)) return // Don't bully people who are playing red light green light return ..() - -/datum/ai_behavior/ranged_skirmish/watcher - min_range = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/ice_demon.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/ice_demon.dm deleted file mode 100644 index 25d1ea9da74..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/ice_demon.dm +++ /dev/null @@ -1,85 +0,0 @@ -/mob/living/simple_animal/hostile/asteroid/ice_demon - name = "demonic watcher" - desc = "A creature formed entirely out of ice, bluespace energy emanates from inside of it." - icon = 'icons/mob/simple/icemoon/icemoon_monsters.dmi' - icon_state = "ice_demon" - icon_living = "ice_demon" - icon_dead = "ice_demon_dead" - icon_gib = "syndicate_gib" - mob_biotypes = MOB_ORGANIC|MOB_BEAST - mouse_opacity = MOUSE_OPACITY_ICON - speak_emote = list("telepathically cries") - speed = 2 - move_to_delay = 2 - projectiletype = /obj/projectile/temp/ice_demon - projectilesound = 'sound/weapons/pierce.ogg' - ranged = TRUE - ranged_message = "manifests ice" - ranged_cooldown_time = 1.5 SECONDS - minimum_distance = 3 - retreat_distance = 3 - maxHealth = 150 - health = 150 - obj_damage = 40 - melee_damage_lower = 15 - melee_damage_upper = 15 - attack_verb_continuous = "slices" - attack_verb_simple = "slice" - attack_sound = 'sound/weapons/bladeslice.ogg' - attack_vis_effect = ATTACK_EFFECT_SLASH - vision_range = 9 - aggro_vision_range = 9 - move_force = MOVE_FORCE_VERY_STRONG - move_resist = MOVE_FORCE_VERY_STRONG - pull_force = MOVE_FORCE_VERY_STRONG - del_on_death = TRUE - loot = list() - crusher_loot = /obj/item/crusher_trophy/demon_core /// SKYRAT EDIT CHANGE - ORIGINAL : crusher_loot = /obj/item/crusher_trophy/watcher_wing/ice_wing - death_message = "fades as the energies that tied it to this world dissipate." - death_sound = 'sound/magic/demon_dies.ogg' - stat_attack = HARD_CRIT - robust_searching = TRUE - footstep_type = FOOTSTEP_MOB_CLAW - /// Distance the demon will teleport from the target - var/teleport_distance = 3 - -/mob/living/simple_animal/hostile/asteroid/ice_demon/Initialize(mapload) - . = ..() - AddElement(/datum/element/simple_flying) - -/obj/projectile/temp/ice_demon - name = "ice blast" - icon_state = "ice_2" - damage = 5 - damage_type = BURN - armor_flag = ENERGY - speed = 1 - pixel_speed_multiplier = 0.25 - range = 200 - temperature = -75 - -/mob/living/simple_animal/hostile/asteroid/ice_demon/OpenFire() - ranged_cooldown = world.time + ranged_cooldown_time - // Sentient ice demons teleporting has been linked to server crashes - if(client) - return ..() - if(teleport_distance <= 0) - return ..() - var/list/possible_ends = view(teleport_distance, target.loc) - view(teleport_distance - 1, target.loc) - for(var/turf/closed/turf_to_remove in possible_ends) - possible_ends -= turf_to_remove - if(!possible_ends.len) - return ..() - var/turf/end = pick(possible_ends) - do_teleport(src, end, 0, channel=TELEPORT_CHANNEL_BLUESPACE, forced = TRUE) - SLEEP_CHECK_DEATH(8, src) - return ..() - -/mob/living/simple_animal/hostile/asteroid/ice_demon/death(gibbed) - move_force = MOVE_FORCE_DEFAULT - move_resist = MOVE_RESIST_DEFAULT - pull_force = PULL_FORCE_DEFAULT - new /obj/item/stack/ore/bluespace_crystal(loc, 3) - if(prob(5)) - new /obj/item/raw_anomaly_core/bluespace(loc) - return ..() diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 84da6f68478..92416001b79 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -64,7 +64,6 @@ /mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch, /mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck, /mob/living/simple_animal/hostile/asteroid/gutlunch/guthen, - /mob/living/simple_animal/hostile/asteroid/ice_demon, /mob/living/simple_animal/hostile/asteroid/polarbear, /mob/living/simple_animal/hostile/asteroid/polarbear/lesser, /mob/living/simple_animal/hostile/asteroid/wolf, diff --git a/icons/obj/mining_zones/artefacts.dmi b/icons/obj/mining_zones/artefacts.dmi index f3f7d00e4eef8f3c82cbbd739c191aaba17ae3b7..d4c603834d21bec61a21b57203eb6086bab45dfa 100644 GIT binary patch delta 4613 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 Vnz2wZ(*O~gzOIQ*&3z~M{{YTx>ly$6 delta 4078 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 /obj/item/crusher_trophy/demon_core +/mob/living/basic/mining/ice_demon + crusher_loot = /obj/item/crusher_trophy/demon_core diff --git a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm index ba1dd931c3a..7b38a565698 100644 --- a/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm +++ b/modular_skyrat/modules/ashwalkers/code/effects/ash_rituals.dm @@ -198,7 +198,7 @@ /datum/ash_ritual/summon_icemoon_creature/ritual_success(obj/effect/ash_rune/success_rune) . = ..() var/mob_type = pick( - /mob/living/simple_animal/hostile/asteroid/ice_demon, + /mob/living/basic/mining/ice_demon, /mob/living/basic/mining/ice_whelp, /mob/living/basic/mining/lobstrosity, /mob/living/simple_animal/hostile/asteroid/polarbear, diff --git a/tgstation.dme b/tgstation.dme index fee2981a998..4a8d75fedad 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -922,6 +922,7 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\stare_at_thing.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\target_retaliate.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\targeted_mob_ability.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\teleport_away_from_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\tipped_subtree.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\travel_to_point.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\use_mob_ability.dm" @@ -1100,6 +1101,7 @@ #include "code\datums\components\itembound.dm" #include "code\datums\components\itempicky.dm" #include "code\datums\components\jetpack.dm" +#include "code\datums\components\joint_damage.dm" #include "code\datums\components\jousting.dm" #include "code\datums\components\keep_me_secure.dm" #include "code\datums\components\knockoff.dm" @@ -4465,6 +4467,9 @@ #include "code\modules\mob\living\basic\heretic\raw_prophet.dm" #include "code\modules\mob\living\basic\heretic\rust_walker.dm" #include "code\modules\mob\living\basic\heretic\star_gazer.dm" +#include "code\modules\mob\living\basic\icemoon\ice_demon\ice_demon.dm" +#include "code\modules\mob\living\basic\icemoon\ice_demon\ice_demon_abilities.dm" +#include "code\modules\mob\living\basic\icemoon\ice_demon\ice_demon_ai.dm" #include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp.dm" #include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp_abilities.dm" #include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp_ai.dm" @@ -4828,7 +4833,6 @@ #include "code\modules\mob\living\simple_animal\hostile\megafauna\wendigo.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\curse_blob.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\gutlunch.dm" -#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\ice_demon.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\mining_mobs.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\polarbear.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\wolf.dm" @@ -6149,6 +6153,7 @@ #include "modular_skyrat\master_files\code\modules\mob\living\living.dm" #include "modular_skyrat\master_files\code\modules\mob\living\living_defines.dm" #include "modular_skyrat\master_files\code\modules\mob\living\living_movement.dm" +#include "modular_skyrat\master_files\code\modules\mob\living\basic\icemoon\ice_demon.dm" #include "modular_skyrat\master_files\code\modules\mob\living\carbon\death.dm" #include "modular_skyrat\master_files\code\modules\mob\living\carbon\human.dm" #include "modular_skyrat\master_files\code\modules\mob\living\carbon\human_helpers.dm" diff --git a/tools/UpdatePaths/Scripts/78539_ice_demons.txt b/tools/UpdatePaths/Scripts/78539_ice_demons.txt new file mode 100644 index 00000000000..f387d200500 --- /dev/null +++ b/tools/UpdatePaths/Scripts/78539_ice_demons.txt @@ -0,0 +1 @@ +/mob/living/simple_animal/hostile/asteroid/ice_demon : /mob/living/basic/mining/ice_demon{@OLD} \ No newline at end of file