mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 23:27:34 +01:00
ice demon basic mobs (#78539)
## About The Pull Request ice demons are now basic mobs. they still have their behavior where they can teleport around the player, run away from him and shoot him. they now also have a new ability they will only use when they are on their last legs, they will spawn weak and slow afterimage clones of theirselves to attack the player. damaging these clones will also damage the original ice demons. ice demons can also now be very easily countered as they are very afraid of fires. they will run away from you if they see u holding a lit torch/flare/welding tool and while running away they will freeze the floors around them to try to slip u to stop u from chasing them. ice demons now also get a new unique trophy! this trophy will summon 2 friendly spirits that will help you kill ur target, but these spirits will dissappear after a very short while. https://github.com/tgstation/tgstation/assets/138636438/6a48fb15-f447-441a-91c6-48ca120dc22c ## Why It's Good For The Game ## Changelog 🆑 refactor: ice demons have been refactored into basic mbos. please report any bugs add: ice demons now have a unique trophy /🆑
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user