[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>
This commit is contained in:
SkyratBot
2023-10-08 08:06:23 -04:00
committed by GitHub
co-authored by Ben10Omintrix Giz
parent afa41aae57
commit ade3fb2813
20 changed files with 528 additions and 112 deletions
+16
View File
@@ -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
@@ -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)
@@ -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))
@@ -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)
@@ -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 ..()
+35
View File
@@ -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
+2 -2
View File
@@ -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(
@@ -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
@@ -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)
@@ -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
@@ -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--
@@ -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,
)
@@ -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
@@ -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 ..()
@@ -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,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

@@ -0,0 +1,3 @@
// crusher loot /obj/item/crusher_trophy/ice_demon_cube -> /obj/item/crusher_trophy/demon_core
/mob/living/basic/mining/ice_demon
crusher_loot = /obj/item/crusher_trophy/demon_core
@@ -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,
+6 -1
View File
@@ -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"
@@ -0,0 +1 @@
/mob/living/simple_animal/hostile/asteroid/ice_demon : /mob/living/basic/mining/ice_demon{@OLD}