mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-14 08:34:16 +01:00
Crashed Vulpkanin Ship - Lavaland Ruin (#32110)
* Charon's Revolver * Dog tags * Ash Whelps * Ash walkers and resist adjustment * Some providence stuff * Minor wording adjustment * Desc change * Ruin, salvage, and ruin assets * linters * Mo linters * quick * Various fixes * Variety of fixes * Update * Naming * FIX
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
/mob/living/basic/ash_walker
|
||||
name = "ash walker"
|
||||
desc = "These reptillian creatures appear to be related to the Unathi, but seem significantly less evolved."
|
||||
icon = 'icons/mob/simple_human.dmi'
|
||||
icon_state = "ashwalker"
|
||||
icon_living = "ashwalker"
|
||||
icon_dead = "ashwalker_dead" // Does not actually exist. del_on_death.
|
||||
icon_gib = "ashwalker_gib" // Does not actually exist. del_on_death.
|
||||
mob_biotypes = MOB_ORGANIC | MOB_HUMANOID
|
||||
speed = -0.8
|
||||
harm_intent_damage = 5
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 15
|
||||
melee_attack_cooldown_min = 1 SECONDS
|
||||
melee_attack_cooldown_max = 1.5 SECONDS
|
||||
attack_verb_continuous = "stabs"
|
||||
attack_verb_simple = "stab"
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
a_intent = INTENT_HARM
|
||||
ai_controller = /datum/ai_controller/basic_controller/simple/ash_walker
|
||||
faction = list("ashwalker")
|
||||
weather_immunities = list("lava","ash")
|
||||
unsuitable_atmos_damage = 0
|
||||
minimum_survivable_temperature = 0
|
||||
maximum_survivable_temperature = INFINITY
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/iron{layer = ABOVE_MOB_LAYER})
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
sentience_type = SENTIENCE_OTHER
|
||||
step_type = FOOTSTEP_MOB_SHOE
|
||||
|
||||
/mob/living/basic/ash_walker/Initialize(mapload)
|
||||
. = ..()
|
||||
add_language("Sinta'unathi")
|
||||
set_default_language(GLOB.all_languages["Sinta'unathi"])
|
||||
gender = pick(MALE, FEMALE)
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
|
||||
/mob/living/basic/ash_walker/tough
|
||||
name = "ash walker hunter"
|
||||
icon_state = "ashwalker_hunter"
|
||||
icon_living = "ashwalker_hunter"
|
||||
damage_coeff = list(BRUTE = 0.9, BURN = 0.6, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/plasma{layer = ABOVE_MOB_LAYER})
|
||||
|
||||
/mob/living/basic/ash_walker/veteran
|
||||
name = "ash walker slayer"
|
||||
icon_state = "ashwalker_veteran"
|
||||
icon_living = "ashwalker_veteran"
|
||||
damage_coeff = list(BRUTE = 0.8, BURN = 0.4, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
|
||||
armor_penetration_flat = 15
|
||||
melee_damage_lower = 15
|
||||
melee_damage_upper = 20
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/silver{layer = ABOVE_MOB_LAYER})
|
||||
|
||||
/mob/living/basic/ash_walker/elite
|
||||
name = "ash walker dragonslayer"
|
||||
icon_state = "ashwalker_elite"
|
||||
icon_living = "ashwalker_elite"
|
||||
damage_coeff = list(BRUTE = 0.5, BURN = 0, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
|
||||
armor_penetration_flat = 25
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 25
|
||||
loot = list(/obj/effect/mob_spawn/human/corpse/ashwalker,
|
||||
/obj/item/spear,
|
||||
/obj/item/stack/ore/gold{layer = ABOVE_MOB_LAYER})
|
||||
|
||||
/datum/ai_controller/basic_controller/simple/ash_walker
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
|
||||
)
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/generic_resist,
|
||||
/datum/ai_planning_subtree/random_speech/ash_walker,
|
||||
/datum/ai_planning_subtree/target_retaliate,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/prowl/lavaland,
|
||||
)
|
||||
|
||||
/datum/ai_planning_subtree/random_speech/ash_walker
|
||||
speech_chance = 10
|
||||
emote_see = list("grunts.", "hisses.", "inspects their claws.")
|
||||
speak = list(
|
||||
"As the necropolis wills.",
|
||||
"It's been too long since we've had a good hunt.",
|
||||
"Don't sleep in the shade too long. The cold dreams will find you.",
|
||||
"I heard the dust singing across the rocks today.",
|
||||
"The young ones think the lava fields are a brave place. They will find good hunts there.",
|
||||
"Did you see that star fall?",
|
||||
"Sometimes, I feel like they're watching me.",
|
||||
"I heard someone got a goliath the other day. Delicious.",
|
||||
"Think we'll see an offworlder today?",
|
||||
"Words are cheap.")
|
||||
@@ -0,0 +1,123 @@
|
||||
/mob/living/basic/mining/ash_whelp
|
||||
name = "ash whelp"
|
||||
desc = "The offspring of an ash drake, weak in comparison but still terrifying."
|
||||
icon_state = "ash_whelp"
|
||||
icon_living = "ash_whelp"
|
||||
icon_dead = "ash_whelp_dead"
|
||||
butcher_results = list(
|
||||
/obj/item/stack/ore/gold = 3,
|
||||
/obj/item/stack/sheet/animalhide/ashdrake = 1,
|
||||
/obj/item/stack/sheet/bone = 10,
|
||||
/obj/item/stack/sheet/sinew = 2,
|
||||
)
|
||||
crusher_loot = list(/obj/item/crusher_trophy/tail_spike)
|
||||
speed = 12
|
||||
|
||||
maxHealth = 300
|
||||
health = 300
|
||||
obj_damage = 40
|
||||
armor_penetration_flat = 20
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 25
|
||||
|
||||
response_help_continuous = "pets"
|
||||
response_help_simple = "pet"
|
||||
attack_verb_continuous = "chomps"
|
||||
attack_verb_simple = "chomp"
|
||||
throw_blocked_message = "does nothing to the scaled hide of the"
|
||||
death_message = "collapses on its side."
|
||||
death_sound = 'sound/misc/demon_dies.ogg'
|
||||
attack_sound = 'sound/misc/demon_attack1.ogg'
|
||||
|
||||
faction = list("mining")
|
||||
|
||||
move_force = MOVE_FORCE_STRONG
|
||||
move_resist = MOVE_FORCE_STRONG
|
||||
pull_force = MOVE_FORCE_STRONG
|
||||
|
||||
ai_controller = /datum/ai_controller/basic_controller/ash_whelp
|
||||
var/list/innate_actions = list(
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath = BB_WHELP_STRAIGHTLINE_FIRE,
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption/fire = BB_WHELP_WIDESPREAD_FIRE,
|
||||
)
|
||||
|
||||
/// How much we will heal when consuming a target
|
||||
var/heal_on_cannibalize = 5
|
||||
|
||||
/mob/living/basic/mining/ash_whelp/Initialize(mapload)
|
||||
. = ..()
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
AddComponent(/datum/component/ai_target_timer)
|
||||
AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HEAVY)
|
||||
grant_actions_by_list(innate_actions)
|
||||
|
||||
/mob/living/basic/mining/ash_whelp/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
|
||||
. = ..()
|
||||
|
||||
if(istype(target, /obj/structure/flora/ash/rock))
|
||||
create_sculpture(target)
|
||||
return FALSE
|
||||
|
||||
if(!istype(target, type))
|
||||
return TRUE
|
||||
|
||||
var/mob/living/victim = target
|
||||
if(victim.stat != DEAD)
|
||||
return TRUE
|
||||
|
||||
if(istype(target, /mob/living/basic/mining/ash_whelp))
|
||||
cannibalize_victim(victim)
|
||||
return FALSE
|
||||
return FALSE
|
||||
|
||||
/// Carve a stone into a beautiful self-portrait
|
||||
/mob/living/basic/mining/ash_whelp/proc/create_sculpture(atom/target)
|
||||
visible_message("[src] starts to sculpt [target].")
|
||||
if(!do_after(src, 5 SECONDS, target = target))
|
||||
return
|
||||
var/obj/structure/whelp_statue/dragon_statue = new(get_turf(target))
|
||||
dragon_statue.name = "statue of [src]"
|
||||
dragon_statue.desc = "Let this serve as a warning."
|
||||
qdel(target)
|
||||
|
||||
/// Gib and consume our fellow ash drakes
|
||||
/mob/living/basic/mining/ash_whelp/proc/cannibalize_victim(mob/living/target)
|
||||
start_pulling(target)
|
||||
visible_message("[src] starts to consume [target].")
|
||||
if(!do_after(src, 5 SECONDS, target))
|
||||
return
|
||||
target.gib()
|
||||
adjustBruteLoss(-1 * heal_on_cannibalize)
|
||||
|
||||
/// Ice whelp, the cold variant of ash whelps.
|
||||
/mob/living/basic/mining/ash_whelp/ice
|
||||
name = "ice whelp"
|
||||
desc = "The offspring of an ash drake, weak in comparison but still terrifying. This one exudes an icy aura."
|
||||
icon_state = "ice_whelp"
|
||||
icon_living = "ice_whelp"
|
||||
icon_dead = "ice_whelp_dead"
|
||||
innate_actions = list(
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice = BB_WHELP_STRAIGHTLINE_FIRE,
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption = BB_WHELP_WIDESPREAD_FIRE,
|
||||
)
|
||||
|
||||
/// Ash whelp statue formed by them carving a rock
|
||||
/obj/structure/whelp_statue
|
||||
name = "statue of an ash whelp"
|
||||
desc = "A basalt sculpture of a proud and regal drake. Its eyes are six glowing gemstones."
|
||||
icon = 'icons/obj/structures/statues.dmi'
|
||||
icon_state = "drake"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
integrity_failure = 100
|
||||
layer = EDGED_TURF_LAYER
|
||||
|
||||
/obj/structure/whelp_statue/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
. = ..()
|
||||
update_icon(UPDATE_ICON_STATE)
|
||||
|
||||
/obj/structure/whelp_statue/update_icon_state()
|
||||
if(broken)
|
||||
icon_state = "drake_headless"
|
||||
return
|
||||
icon_state = "drake"
|
||||
@@ -0,0 +1,209 @@
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath
|
||||
name = "Fire Breath"
|
||||
button_icon_state = "fireball"
|
||||
desc = "Breathe a line of flames towards the target."
|
||||
cooldown_time = 3 SECONDS
|
||||
/// The range of the fire
|
||||
var/fire_range = 15
|
||||
/// The sound played when you use this ability
|
||||
var/fire_sound = 'sound/magic/fireball.ogg'
|
||||
/// Time to wait between spawning each fire turf
|
||||
var/fire_delay = 0.15 SECONDS
|
||||
/// How hot is our fire
|
||||
var/fire_temperature = 1000
|
||||
/// 'How much' fire do we expose the turf to?
|
||||
var/fire_volume = 50
|
||||
/// How much damage do you take when engulfed?
|
||||
var/fire_damage = 20
|
||||
/// How much damage to mechs take when engulfed?
|
||||
var/mech_damage = 45
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/Activate(atom/target_atom)
|
||||
disable_cooldown_actions()
|
||||
attack_sequence(target_atom)
|
||||
StartCooldown()
|
||||
enable_cooldown_actions()
|
||||
return TRUE
|
||||
|
||||
/// Apply our specific fire breathing shape, in proc form so we can override it in subtypes
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/attack_sequence(atom/target)
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
fire_line(target)
|
||||
|
||||
/// Breathe fire in a line towards the target, optionally rotated at an offset from the target
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/fire_line(atom/target, offset)
|
||||
if(isnull(target))
|
||||
return
|
||||
var/turf/target_turf = get_ranged_target_turf_direct(owner, target, fire_range, offset)
|
||||
var/list/turfs = get_line(owner, target_turf) - get_turf(owner)
|
||||
INVOKE_ASYNC(src, PROC_REF(progressive_fire_line), turfs)
|
||||
|
||||
/// Creates fire with a delay on the list of targeted turfs
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/progressive_fire_line(list/burn_turfs)
|
||||
if(QDELETED(owner) || owner.stat == DEAD)
|
||||
return
|
||||
// Guys we have already hit, no double dipping
|
||||
var/list/hit_list = list(owner) // also don't burn ourselves
|
||||
for(var/turf/target_turf in burn_turfs)
|
||||
if(target_turf.is_blocked_turf(exclude_mobs = TRUE))
|
||||
return
|
||||
burn_turf(target_turf, hit_list, owner)
|
||||
sleep(fire_delay)
|
||||
|
||||
/// Finally spawn the actual fire, spawns the fire hotspot in case you want to recolour it or something
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/burn_turf(turf/fire_turf, list/hit_list, mob/living/source)
|
||||
var/obj/effect/hotspot/hotspot = new /obj/effect/hotspot/fake(fire_turf)
|
||||
hotspot.temperature = fire_temperature
|
||||
hotspot.recolor()
|
||||
fire_turf.hotspot_expose(fire_temperature, fire_volume)
|
||||
|
||||
for(var/mob/living/barbecued in fire_turf.contents)
|
||||
if(barbecued in hit_list)
|
||||
continue
|
||||
hit_list |= barbecued
|
||||
on_burn_mob(barbecued, source)
|
||||
|
||||
for(var/obj/mecha/robotron in fire_turf.contents)
|
||||
if(robotron in hit_list)
|
||||
continue
|
||||
hit_list |= robotron
|
||||
robotron.take_damage(mech_damage, BURN, FIRE)
|
||||
|
||||
return
|
||||
|
||||
/// Do something unpleasant to someone we set on fire
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/proc/on_burn_mob(mob/living/barbecued, mob/living/source)
|
||||
if(fire_temperature <= TCMB)
|
||||
barbecued.apply_status_effect(/datum/status_effect/ice_block_talisman, 3 SECONDS)
|
||||
to_chat(barbecued, SPAN_USERDANGER("You're frozen solid by [source]'s icy breath!"))
|
||||
else
|
||||
to_chat(barbecued, SPAN_USERDANGER("You are burned by [source]'s fire breath!"))
|
||||
barbecued.adjustFireLoss(fire_damage)
|
||||
|
||||
/// Shoot three lines of fire in a sort of fork pattern approximating a cone
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/cone
|
||||
name = "Fire Cone"
|
||||
desc = "Breathe several lines of fire directed at a target."
|
||||
/// The angles relative to the target that shoot lines of fire
|
||||
var/list/angles = list(-40, 0, 40)
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/cone/attack_sequence(atom/target)
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
for(var/offset in angles)
|
||||
fire_line(target, offset)
|
||||
|
||||
/// Shoot fire in a whole bunch of directions
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire
|
||||
name = "Mass Fire"
|
||||
button_icon = 'icons/effects/fire.dmi'
|
||||
button_icon_state = "light"
|
||||
desc = "Breathe flames in all directions."
|
||||
cooldown_time = 10.5 SECONDS
|
||||
click_to_activate = FALSE
|
||||
/// How many fire lines do we produce to turn a full circle?
|
||||
var/sectors = 12
|
||||
/// How long do we wait between each spin?
|
||||
var/breath_delay = 2.5 SECONDS
|
||||
/// How many full circles do we perform?
|
||||
var/total_spins = 3
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/Activate(atom/target_atom)
|
||||
target_atom = get_step(owner, owner.dir) // Just shoot it forwards, we don't need to click on someone forthis one
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/attack_sequence(atom/target)
|
||||
var/queued_spins = 0
|
||||
for(var/i in 1 to total_spins)
|
||||
var/delay = queued_spins * breath_delay
|
||||
queued_spins++
|
||||
addtimer(CALLBACK(src, PROC_REF(fire_spin), target, queued_spins), delay)
|
||||
|
||||
/// Breathe fire in a circle, with a slight angle offset based on which of our several circles it is
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/proc/fire_spin(target, spin_count)
|
||||
if(QDELETED(owner) || owner.stat == DEAD)
|
||||
return // Too dead to spin
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
var/angle_increment = 360 / sectors
|
||||
var/additional_offset = spin_count * angle_increment / 2
|
||||
for(var/i in 1 to sectors)
|
||||
fire_line(target, (angle_increment * i) + (additional_offset))
|
||||
|
||||
/// Breathe "fire" in a line (it's freezing cold)
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice
|
||||
name = "Ice Breath"
|
||||
desc = "Fire a cold line of fire towards the enemy!"
|
||||
cooldown_time = 6 SECONDS
|
||||
fire_range = 7
|
||||
fire_damage = 10
|
||||
fire_delay = 0.85 DECISECONDS
|
||||
fire_temperature = TCMB
|
||||
/// Time to warn people about what we are doing
|
||||
var/forecast_delay = 0.6 SECONDS
|
||||
/// What turf are we aiming at?
|
||||
var/turf/target_turf
|
||||
/// Overlay we show when we're about to fire
|
||||
var/image/forecast_overlay
|
||||
/// Icon state used foroverlay
|
||||
var/forecast_overlay_state = "ice_whelp_telegraph_dir"
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/New(Target, original)
|
||||
. = ..()
|
||||
forecast_overlay = image('icons/mob/lavaland/lavaland_monsters.dmi', forecast_overlay_state)
|
||||
|
||||
/// Apply our specific fire breathing shape, in proc form so we can override it in subtypes
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/attack_sequence(atom/target)
|
||||
target_turf = get_turf(target)
|
||||
INVOKE_ASYNC(src, PROC_REF(attack_forecast))
|
||||
|
||||
/// Charge up before we breathe fire
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/proc/attack_forecast()
|
||||
owner.face_atom(target_turf)
|
||||
owner.Shake(pixelshiftx = 1, pixelshifty = 0, duration = forecast_delay)
|
||||
forecast_overlay.dir = (get_dir(owner, target_turf))
|
||||
owner.add_overlay(forecast_overlay)
|
||||
var/succeeded = do_after(owner, delay = forecast_delay, target = owner, hidden = TRUE)
|
||||
owner.cut_overlay(forecast_overlay)
|
||||
if(succeeded)
|
||||
playsound(owner.loc, fire_sound, 200, TRUE)
|
||||
breath_attack()
|
||||
|
||||
/// Actually breathe fire
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/proc/breath_attack()
|
||||
owner.face_atom(target_turf)
|
||||
fire_line(target_turf)
|
||||
target_turf = null
|
||||
|
||||
/// Breathe really cold fire an area around them
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption
|
||||
name = "Ice Eruption"
|
||||
desc = "Unleash cold fire in all directions"
|
||||
button_icon = 'icons/effects/fire.dmi'
|
||||
button_icon_state = "light"
|
||||
click_to_activate = FALSE
|
||||
fire_range = 3
|
||||
forecast_delay = 1 SECONDS
|
||||
fire_delay = 1.5 DECISECONDS
|
||||
forecast_overlay_state = "ice_whelp_telegraph_all"
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption/breath_attack()
|
||||
target_turf = null
|
||||
|
||||
var/list/hit_list = list(owner)
|
||||
var/list/nearby_turfs = list()
|
||||
for(var/turf/simulated/floor/target_turf in circle_range(owner, fire_range))
|
||||
if(target_turf.is_blocked_turf(exclude_mobs = TRUE))
|
||||
continue
|
||||
var/turf_dist = get_dist(owner, target_turf)
|
||||
if(turf_dist == 0)
|
||||
continue
|
||||
LAZYADDASSOCLIST(nearby_turfs, "[turf_dist]", target_turf)
|
||||
|
||||
for(var/i in 1 to fire_range)
|
||||
for(var/turf/simulated/floor/kindling as anything in nearby_turfs["[i]"])
|
||||
burn_turf(kindling, hit_list, owner)
|
||||
sleep(fire_delay)
|
||||
|
||||
///Fire subtype forash whelps
|
||||
/datum/action/cooldown/mob_cooldown/fire_breath/ice/eruption/fire
|
||||
name = "Eruption"
|
||||
fire_temperature = 1000
|
||||
@@ -0,0 +1,163 @@
|
||||
/// We won't use fire breath unless we have had the same target forthis long
|
||||
#define MIN_TIME_TO_BREATH_WEAPON 2 SECONDS
|
||||
|
||||
/datum/ai_controller/basic_controller/ash_whelp
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
|
||||
BB_AGGRO_RANGE = 5,
|
||||
)
|
||||
|
||||
ai_movement = /datum/ai_movement/basic_avoidance
|
||||
idle_behavior = /datum/idle_behavior/idle_random_walk
|
||||
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/generic_resist,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ash_whelp,
|
||||
/datum/ai_planning_subtree/attack_obstacle_in_path,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/ash_whelp,
|
||||
/datum/ai_planning_subtree/sculpt_statues,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/corpses/ash_whelp,
|
||||
/datum/ai_planning_subtree/burn_trees,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/prowl/lavaland,
|
||||
)
|
||||
|
||||
/// Cancel melee attacks when we have our breath weapon
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/ash_whelp
|
||||
melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/ash_whelp
|
||||
|
||||
/// Cancel melee attacks when we have our breath weapon
|
||||
/datum/ai_behavior/basic_melee_attack/ash_whelp
|
||||
|
||||
/datum/ai_behavior/basic_melee_attack/ash_whelp/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key)
|
||||
var/datum/action/cooldown/breath_weapon = controller.blackboard[BB_TARGETED_ACTION]
|
||||
if(breath_weapon?.IsAvailable())
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
|
||||
return ..()
|
||||
|
||||
/// Find other tasty dragons
|
||||
/datum/ai_planning_subtree/find_and_hunt_target/corpses/ash_whelp
|
||||
target_key = BB_TARGET_CANNIBAL
|
||||
finding_behavior = /datum/ai_behavior/find_hunt_target/corpses/dragon_corpse
|
||||
hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise
|
||||
hunt_targets = list(/mob/living/basic/mining/ash_whelp)
|
||||
hunt_range = 10
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/corpses/dragon_corpse
|
||||
|
||||
/datum/ai_behavior/find_hunt_target/corpses/dragon_corpse/valid_dinner(mob/living/source, mob/living/dinner, radius)
|
||||
if(dinner.pulledby) // someone already got him before us
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/// Eat other dragons
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
|
||||
/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key)
|
||||
var/mob/living/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target) || target.stat != DEAD || target.pulledby) // we were too slow
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
return ..()
|
||||
|
||||
/datum/ai_behavior/cannibalize/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
/// Subtree to find icy rocks and create sculptures out of them
|
||||
/datum/ai_planning_subtree/sculpt_statues
|
||||
|
||||
/datum/ai_planning_subtree/sculpt_statues/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(controller.blackboard_key_exists(BB_TARGET_ROCK))
|
||||
controller.queue_behavior(/datum/ai_behavior/sculpt_statue, BB_TARGET_ROCK)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_TARGET_ROCK, /obj/structure/flora/ash/rock)
|
||||
|
||||
/datum/ai_behavior/sculpt_statue
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
action_cooldown = 5 MINUTES
|
||||
|
||||
/datum/ai_behavior/sculpt_statue/setup(datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/obj/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
/datum/ai_behavior/sculpt_statue/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
if(!controller.ai_interact(target = target_key, intent = INTENT_HELP))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/sculpt_statue/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
controller.clear_blackboard_key(target_key)
|
||||
|
||||
|
||||
/// Select appropriate ability based on range
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ash_whelp
|
||||
operational_datums = list(/datum/component/ai_target_timer)
|
||||
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ash_whelp/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/mob/living/target = controller.blackboard[target_key]
|
||||
if(isnull(target))
|
||||
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
|
||||
|
||||
var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0
|
||||
if(time_on_target < MIN_TIME_TO_BREATH_WEAPON)
|
||||
return // We need to spend some time acquiring our target first
|
||||
|
||||
var/dist_to_target = get_dist(controller.pawn, target)
|
||||
var/datum/action/cooldown/mob_cooldown/fire_breath/short_range_ability = controller.blackboard[BB_WHELP_WIDESPREAD_FIRE]
|
||||
if(isnull(short_range_ability) || dist_to_target > short_range_ability.fire_range)
|
||||
ability_key = BB_WHELP_STRAIGHTLINE_FIRE
|
||||
return ..()
|
||||
|
||||
ability_key = BB_WHELP_WIDESPREAD_FIRE
|
||||
return ..()
|
||||
|
||||
/// subtree to look fortrees and burn them with our flamethrower
|
||||
/datum/ai_planning_subtree/burn_trees
|
||||
|
||||
/datum/ai_planning_subtree/burn_trees/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
var/datum/action/cooldown/using_action = controller.blackboard[BB_WHELP_STRAIGHTLINE_FIRE]
|
||||
if(!using_action?.IsAvailable())
|
||||
return
|
||||
|
||||
if(controller.blackboard_key_exists(BB_TARGET_TREE))
|
||||
controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees, BB_WHELP_STRAIGHTLINE_FIRE, BB_TARGET_TREE)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
controller.queue_behavior(/datum/ai_behavior/set_target_tree, BB_TARGET_TREE)
|
||||
|
||||
/datum/ai_behavior/set_target_tree
|
||||
|
||||
/datum/ai_behavior/set_target_tree/perform(seconds_per_tick, datum/ai_controller/controller, tree_key)
|
||||
. = ..()
|
||||
var/mob/living_pawn = controller.pawn
|
||||
var/list/possible_trees = list()
|
||||
|
||||
for(var/obj/structure/flora/tree/possible_tree in oview(9, living_pawn))
|
||||
possible_trees += possible_tree
|
||||
|
||||
if(!length(possible_trees))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||
|
||||
controller.set_blackboard_key(tree_key, pick(possible_trees))
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees
|
||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
|
||||
required_distance = 2
|
||||
action_cooldown = 2 MINUTES
|
||||
|
||||
/datum/ai_behavior/targeted_mob_ability/and_clear_target/burn_trees/setup(datum/ai_controller/controller, ability_key, target_key)
|
||||
. = ..()
|
||||
var/obj/target = controller.blackboard[target_key]
|
||||
if(QDELETED(target))
|
||||
return FALSE
|
||||
set_movement_target(controller, target)
|
||||
|
||||
#undef MIN_TIME_TO_BREATH_WEAPON
|
||||
Reference in New Issue
Block a user