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:
Ben10Omintrix
2023-10-08 05:04:35 +03:00
committed by GitHub
parent d6285f9366
commit 032419f30f
18 changed files with 523 additions and 111 deletions
@@ -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)