Converts Watchers and Legions to basic mob AI (#29884)

* Converts basilisks, watchers

* Hivelords and legions

* Hivelordbrood balance adjustment

* New Linters

* Speech fix

---------

Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
This commit is contained in:
PollardTheDragon
2025-08-06 05:54:06 +00:00
committed by GitHub
parent d772497e80
commit 1054b263bb
26 changed files with 404 additions and 205 deletions
+1 -1
View File
@@ -786,7 +786,7 @@
qdel(src)
/obj/effect/mob_spawn/corpse/watcher
mob_type = /mob/living/simple_animal/hostile/asteroid/basilisk/watcher
mob_type = /mob/living/basic/mining/basilisk/watcher
icon = 'icons/mob/lavaland/watcher.dmi'
icon_state = "watcher_dead"
pixel_x = -12
+4 -2
View File
@@ -158,8 +158,10 @@ RESTRICT_TYPE(/mob/living/basic)
var/is_ranged = FALSE
/// How many shots in a burst?
var/ranged_burst_count = 1
/// How fast do we fire between bursts?
/// How fast do we fire between shots in a burst?
var/ranged_burst_interval = 0.2 SECONDS
/// Time between bursts
var/ranged_cooldown = 2 SECONDS
/// What projectile do we shoot?
var/projectile_type
/// What sound does it make when firing?
@@ -179,7 +181,7 @@ RESTRICT_TYPE(/mob/living/basic)
var/datum/action/innate/hide/hide = new()
hide.Grant(src)
if(is_ranged)
AddComponent(/datum/component/ranged_attacks, projectile_type = projectile_type, projectile_sound = projectile_sound, burst_shots = ranged_burst_count, burst_intervals = ranged_burst_interval)
AddComponent(/datum/component/ranged_attacks, cooldown_time = ranged_cooldown, projectile_type = projectile_type, projectile_sound = projectile_sound, burst_shots = ranged_burst_count, burst_intervals = ranged_burst_interval)
/mob/living/basic/Destroy()
if(nest)
@@ -1,20 +1,13 @@
//A beast that fire freezing blasts.
/mob/living/simple_animal/hostile/asteroid/basilisk
// A beast that fire freezing blasts.
/mob/living/basic/mining/basilisk
name = "basilisk"
desc = "A territorial beast, covered in a thick shell that absorbs energy. Its stare causes victims to freeze from the inside."
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
icon_state = "Basilisk"
icon_living = "Basilisk"
icon_aggro = "Basilisk_alert"
icon_dead = "Basilisk_dead"
icon_gib = "syndicate_gib"
mob_biotypes = MOB_ORGANIC | MOB_BEAST
move_to_delay = 20
projectiletype = /obj/item/projectile/temp/basilisk
projectilesound = 'sound/weapons/pierce.ogg'
ranged = TRUE
ranged_message = "stares"
throw_message = "does nothing against the hard shell of"
throw_blocked_message = "does nothing against the hard shell of"
speed = 3
maxHealth = 200
health = 200
@@ -22,17 +15,26 @@
obj_damage = 60
melee_damage_lower = 12
melee_damage_upper = 12
attacktext = "bites into"
attack_verb_continuous = "bites into"
attack_verb_simple = "bite into"
speak_emote = list("chitters")
attack_sound = 'sound/weapons/bladeslice.ogg'
turns_per_move = 5
gold_core_spawnable = HOSTILE_SPAWN
contains_xeno_organ = TRUE
surgery_container = /datum/xenobiology_surgery_container/basilisk
ai_controller = /datum/ai_controller/basic_controller/watcher
is_ranged = TRUE
ranged_cooldown = 3 SECONDS
projectile_type = /obj/item/projectile/temp/basilisk
projectile_sound = 'sound/weapons/pierce.ogg'
loot = list(/obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER},
/obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER})
/mob/living/basic/mining/basilisk/Initialize(mapload)
. = ..()
AddElement(/datum/element/ai_retaliate)
/obj/item/projectile/temp/basilisk
name = "freezing blast"
icon_state = "ice_2"
@@ -44,12 +46,7 @@
var/mob/living/silicon/robot/cyborg = target
cyborg.apply_damage(35, STAMINA)
/mob/living/simple_animal/hostile/asteroid/basilisk/GiveTarget(new_target)
if(..()) //we have a target
if(isliving(target) && !target.Adjacent(targets_from) && ranged_cooldown <= world.time)//No more being shot at point blank or spammed with RNG beams
OpenFire(target)
/mob/living/simple_animal/hostile/asteroid/basilisk/ex_act(severity)
/mob/living/basic/mining/basilisk/ex_act(severity)
switch(severity)
if(1)
gib()
@@ -58,13 +55,13 @@
if(3)
adjustBruteLoss(110)
/mob/living/simple_animal/hostile/asteroid/basilisk/space
/mob/living/basic/mining/basilisk/space
/mob/living/simple_animal/hostile/asteroid/basilisk/space/Process_Spacemove(movement_dir, continuous_move)
/mob/living/basic/mining/basilisk/space/Process_Spacemove(movement_dir, continuous_move)
return TRUE
//Watcher
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher
// Watcher
/mob/living/basic/mining/basilisk/watcher
name = "watcher"
desc = "A levitating, eye-like creature held aloft by winglike formations of sinew. A sharp spine of crystal protrudes from its body."
icon = 'icons/mob/lavaland/watcher.dmi'
@@ -73,36 +70,35 @@
icon_aggro = "watcher"
icon_dead = "watcher_dead"
pixel_x = -10
throw_message = "bounces harmlessly off of"
throw_blocked_message = "bounces harmlessly off of"
melee_damage_lower = 15
melee_damage_upper = 15
attacktext = "impales"
attack_verb_continuous = "impales"
attack_verb_simple = "impale"
speak_emote = list("telepathically cries")
stat_attack = UNCONSCIOUS
robust_searching = TRUE
crusher_loot = /obj/item/crusher_trophy/watcher_wing
loot = list()
butcher_results = list(/obj/item/stack/ore/diamond = 2, /obj/item/stack/sheet/sinew = 2, /obj/item/stack/sheet/bone = 1)
initial_traits = list(TRAIT_FLYING)
surgery_container = /datum/xenobiology_surgery_container/watcher
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/magmawing
/mob/living/basic/mining/basilisk/watcher/magmawing
name = "magmawing watcher"
desc = "When raised very close to lava, some watchers adapt to the extreme heat and use lava as both a weapon and wings."
icon_state = "watcher_magmawing"
icon_living = "watcher_magmawing"
icon_aggro = "watcher_magmawing"
icon_dead = "watcher_magmawing_dead"
maxHealth = 215 //Compensate for the lack of slowdown on projectiles with a bit of extra health
maxHealth = 215 // Compensate for the lack of slowdown on projectiles with a bit of extra health
health = 215
light_range = 3
light_power = 2.5
light_color = LIGHT_COLOR_LAVA
projectiletype = /obj/item/projectile/temp/basilisk/magmawing
projectile_type = /obj/item/projectile/temp/basilisk/magmawing
crusher_loot = /obj/item/crusher_trophy/blaster_tubes/magma_wing
crusher_drop_mod = 100 //These things are extremely rare (1/133 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
crusher_drop_mod = 100 // These things are extremely rare (1/133 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/icewing
/mob/living/basic/mining/basilisk/watcher/icewing
name = "icewing watcher"
desc = "Very rarely, some watchers will eke out an existence far from heat sources. In the absence of warmth, they become icy and fragile but fire much stronger freezing blasts."
icon_state = "watcher_icewing"
@@ -111,17 +107,17 @@
icon_dead = "watcher_icewing_dead"
maxHealth = 170
health = 170
projectiletype = /obj/item/projectile/temp/basilisk/icewing
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/bone = 1) //No sinew; the wings are too fragile to be usable
projectile_type = /obj/item/projectile/temp/basilisk/icewing
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/bone = 1) // No sinew; the wings are too fragile to be usable
crusher_loot = /obj/item/crusher_trophy/watcher_wing/ice_wing
crusher_drop_mod = 100 //These things are extremely rare (1/400 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
crusher_drop_mod = 100 // These things are extremely rare (1/400 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
/obj/item/projectile/temp/basilisk/magmawing
name = "scorching blast"
icon_state = "lava"
damage = 5
nodamage = FALSE
temperature = 500 //Heats you up!
temperature = 500 // Heats you up!
immolate = 1
/obj/item/projectile/temp/basilisk/icewing
@@ -135,5 +131,5 @@
if(istype(L))
L.apply_status_effect(/datum/status_effect/freon/watcher)
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril
fromtendril = TRUE
/mob/living/basic/mining/basilisk/watcher/tendril
from_tendril = TRUE
@@ -0,0 +1,19 @@
/datum/ai_controller/basic_controller/watcher
movement_delay = 2 SECONDS
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_AGGRO_RANGE = 4
)
ai_traits = AI_FLAG_PAUSE_DURING_DO_AFTER
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/target_retaliate/check_faction,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/ranged_skirmish/watcher,
/datum/ai_planning_subtree/attack_obstacle_in_path,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
)
/datum/ai_planning_subtree/ranged_skirmish/watcher
min_range = 0
@@ -1,149 +1,132 @@
/mob/living/simple_animal/hostile/asteroid/hivelord
/mob/living/basic/mining/hivelord
name = "hivelord"
desc = "A levitating swarm of tiny creatures which act as a single individual. When threatened or hunting they rapidly replicate additional short-lived bodies."
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
icon_state = "Hivelord"
icon_living = "Hivelord"
icon_aggro = "Hivelord_alert"
icon_dead = "Hivelord_dead"
icon_gib = "syndicate_gib"
mob_biotypes = MOB_ORGANIC
mouse_opacity = MOUSE_OPACITY_OPAQUE
move_to_delay = 14
ranged = TRUE
vision_range = 5
speed = 3
maxHealth = 75
health = 75
harm_intent_damage = 5
attacktext = "lashes out at"
attack_verb_continuous = "lashes out at"
attack_verb_simple = "lash out at"
speak_emote = list("telepathically cries")
attack_sound = 'sound/weapons/pierce.ogg'
throw_message = "falls right through the strange body of the"
ranged_cooldown_time = 20
throw_blocked_message = "falls right through the strange body of the"
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
retreat_distance = 3
minimum_distance = 3
pass_flags = PASSTABLE
butcher_results = list(/obj/item/organ/internal/regenerative_core = 1)
var/brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood
ai_controller = /datum/ai_controller/basic_controller/hivelord
var/brood_type = /mob/living/basic/mining/hivelordbrood
contains_xeno_organ = TRUE
surgery_container = /datum/xenobiology_surgery_container/hivelord
/mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(the_target)
if(world.time >= ranged_cooldown)
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/A = new brood_type(loc)
/mob/living/basic/mining/hivelord/spawn_crusher_loot()
loot += crusher_loot // we don't butcher
A.admin_spawned = admin_spawned
A.GiveTarget(target)
A.friends = friends
A.faction = faction.Copy()
ranged_cooldown = world.time + ranged_cooldown_time
/mob/living/simple_animal/hostile/asteroid/hivelord/AttackingTarget()
OpenFire()
return TRUE
/mob/living/simple_animal/hostile/asteroid/hivelord/spawn_crusher_loot()
loot += crusher_loot //we don't butcher
/mob/living/simple_animal/hostile/asteroid/hivelord/death(gibbed)
/mob/living/basic/mining/hivelord/death(gibbed)
// Only execute the below if we successfully died
. = ..(gibbed)
if(!.)
return FALSE
mouse_opacity = MOUSE_OPACITY_ICON
/mob/living/simple_animal/hostile/asteroid/hivelord/space
brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood/space
/mob/living/basic/mining/hivelord/space
brood_type = /mob/living/basic/mining/hivelordbrood/space
/mob/living/simple_animal/hostile/asteroid/hivelord/space/Process_Spacemove(movement_dir, continuous_move)
/mob/living/basic/mining/hivelord/space/Process_Spacemove(movement_dir, continuous_move)
return TRUE
//A fragile but rapidly produced creature
/mob/living/simple_animal/hostile/asteroid/hivelordbrood
// A fragile but rapidly produced creature
/mob/living/basic/mining/hivelordbrood
name = "hivelord brood"
desc = "A fragment of the original Hivelord, rallying behind its original. One isn't much of a threat, but..."
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
icon_state = "Hivelordbrood"
icon_living = "Hivelordbrood"
icon_aggro = "Hivelordbrood"
icon_dead = "Hivelordbrood"
icon_gib = "syndicate_gib"
mouse_opacity = MOUSE_OPACITY_OPAQUE
move_to_delay = 1
friendly = "buzzes near"
vision_range = 10
speed = 3
friendly_verb_continuous = "buzzes near"
friendly_verb_simple = "buzz near"
speed = 2
maxHealth = 1
health = 1
harm_intent_damage = 5
melee_damage_lower = 2
melee_damage_upper = 2
attacktext = "slashes"
melee_damage_lower = 10
melee_damage_upper = 10
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
speak_emote = list("telepathically cries")
attack_sound = 'sound/weapons/pierce.ogg'
throw_message = "falls right through the strange body of the"
throw_blocked_message = "falls right through the strange body of the"
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
pass_flags = PASSTABLE | PASSMOB
density = FALSE
del_on_death = TRUE
basic_mob_flags = DEL_ON_DEATH
ai_controller = /datum/ai_controller/basic_controller/hivelord_brood
initial_traits = list(TRAIT_FLYING)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/Initialize(mapload)
/mob/living/basic/mining/hivelordbrood/Initialize(mapload)
. = ..()
addtimer(CALLBACK(src, PROC_REF(death)), 100)
AddComponent(/datum/component/swarming)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/space
/mob/living/basic/mining/hivelordbrood/space
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/space/Process_Spacemove(movement_dir, continuous_move)
/mob/living/basic/mining/hivelordbrood/space/Process_Spacemove(movement_dir, continuous_move)
return TRUE
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood
/mob/living/basic/mining/hivelordbrood/blood
name = "blood brood"
desc = "A living string of blood and alien materials."
icon_state = "bloodbrood"
icon_living = "bloodbrood"
icon_aggro = "bloodbrood"
attacktext = "pierces"
attack_verb_continuous = "pierces"
attack_verb_simple = "pierce"
color = "#C80000"
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/death()
/mob/living/basic/mining/hivelordbrood/blood/death()
if(can_die() && loc)
// Splash the turf we are on with blood
reagents.reaction(get_turf(src))
return ..()
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/Initialize(mapload)
/mob/living/basic/mining/hivelordbrood/blood/Initialize(mapload)
. = ..()
create_reagents(30)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/AttackingTarget()
/mob/living/basic/mining/hivelordbrood/blood/melee_attack(atom/target, list/modifiers, ignore_cooldown)
..()
if(iscarbon(target))
transfer_reagents(target, 1)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/attack_hand(mob/living/carbon/human/M)
/mob/living/basic/mining/hivelordbrood/blood/attack_hand(mob/living/carbon/human/M)
if("\ref[M]" in faction)
reabsorb_host(M)
else
return ..()
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/attack_alien(mob/living/carbon/alien/humanoid/M)
/mob/living/basic/mining/hivelordbrood/blood/attack_alien(mob/living/carbon/alien/humanoid/M)
if("\ref[M]" in faction)
reabsorb_host(M)
else
return ..()
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/reabsorb_host(mob/living/carbon/C)
/mob/living/basic/mining/hivelordbrood/blood/proc/reabsorb_host(mob/living/carbon/C)
C.visible_message("<span class='notice'>[src] is reabsorbed by [C]'s body.</span>", \
"<span class='notice'>[src] is reabsorbed by your body.</span>")
transfer_reagents(C)
death()
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/transfer_reagents(mob/living/carbon/C, volume = 30)
/mob/living/basic/mining/hivelordbrood/blood/proc/transfer_reagents(mob/living/carbon/C, volume = 30)
if(!reagents.total_volume)
return
@@ -153,13 +136,13 @@
reagents.reaction(C, REAGENT_INGEST, fraction)
reagents.trans_to(C, volume)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/link_host(mob/living/carbon/C)
/mob/living/basic/mining/hivelordbrood/blood/proc/link_host(mob/living/carbon/C)
faction = list("\ref[src]", "\ref[C]") // Hostile to everyone except the host.
C.transfer_blood_to(src, 30)
color = mix_color_from_reagents(reagents.reagent_list)
//Legion
/mob/living/simple_animal/hostile/asteroid/hivelord/legion
// Legion
/mob/living/basic/mining/hivelord/legion
name = "legion"
desc = "You can still see what was once a person under the shifting mass of corruption."
icon_state = "legion"
@@ -172,18 +155,22 @@
melee_damage_lower = 15
melee_damage_upper = 15
speak_emote = list("echoes")
throw_message = "bounces harmlessly off of"
throw_blocked_message = "bounces harmlessly off of"
crusher_loot = /obj/item/crusher_trophy/legion_skull
loot = list(/obj/item/organ/internal/regenerative_core/legion)
butcher_results = null
brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion
del_on_death = TRUE
stat_attack = UNCONSCIOUS
robust_searching = TRUE
brood_type = /mob/living/basic/mining/hivelordbrood/legion
ai_controller = /datum/ai_controller/basic_controller/hivelord/legion
basic_mob_flags = DEL_ON_DEATH
var/dwarf_mob = FALSE
var/mob/living/carbon/human/stored_mob
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf
/mob/living/basic/mining/hivelord/legion/Initialize(mapload)
. = ..()
add_language("Galactic Common")
set_default_language(GLOB.all_languages["Galactic Common"])
/mob/living/basic/mining/hivelord/legion/dwarf
name = "dwarf legion"
desc = "You can still see what was once a dwarf under the shifting mass of corruption."
icon_state = "dwarf_legion"
@@ -192,21 +179,21 @@
icon_dead = "dwarf_legion"
maxHealth = 60
health = 60
speed = 2 //faster!
speed = 2 // faster!
crusher_drop_mod = 20
dwarf_mob = TRUE
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/tendril
fromtendril = TRUE
/mob/living/basic/mining/hivelord/legion/tendril
from_tendril = TRUE
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/death(gibbed)
/mob/living/basic/mining/hivelord/legion/death(gibbed)
visible_message("<span class='warning'>The skulls on [src] wail in anger as they flee from their dying host!</span>")
var/turf/T = get_turf(src)
if(T)
if(stored_mob)
stored_mob.forceMove(get_turf(src))
stored_mob = null
else if(fromtendril)
else if(from_tendril)
new /obj/effect/mob_spawn/human/corpse/charredskeleton(T)
else if(dwarf_mob)
new /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf(T)
@@ -214,38 +201,38 @@
new /obj/effect/mob_spawn/human/corpse/damaged/legioninfested(T)
..(gibbed)
//Legion skull
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion
// Legion skull
/mob/living/basic/mining/hivelordbrood/legion
name = "legion"
desc = "One of many."
icon_state = "legion_head"
icon_living = "legion_head"
icon_aggro = "legion_head"
icon_dead = "legion_head"
maxHealth = 5
health = 5
melee_damage_lower = 12
melee_damage_upper = 12
attacktext = "bites"
attack_verb_continuous = "bites"
attack_verb_simple = "bites"
speak_emote = list("echoes")
throw_message = "is shrugged off by"
stat_attack = UNCONSCIOUS
robust_searching = TRUE
throw_blocked_message = "is shrugged off by"
var/can_infest_dead = FALSE
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/Life(seconds, times_fired)
if(isturf(loc))
for(var/mob/living/carbon/human/H in view(src,1)) //Only for corpse right next to/on same tile
if(H.stat == UNCONSCIOUS || (can_infest_dead && H.stat == DEAD))
infest(H)
..()
/mob/living/basic/mining/hivelordbrood/legion/melee_attack(mob/living/carbon/human/target, list/modifiers, ignore_cooldown)
. = ..()
if(!ishuman(target))
return
if((target.stat == DEAD && can_infest_dead) || target.stat == UNCONSCIOUS)
infest(target)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/proc/infest(mob/living/carbon/human/H)
/mob/living/basic/mining/hivelordbrood/legion/proc/infest(mob/living/carbon/human/H)
if(H?.dna?.species && !H.dna.species.can_be_legion_infested())
return
visible_message("<span class='warning'>[name] burrows into the flesh of [H]!</span>")
var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L
if(HAS_TRAIT(H, TRAIT_DWARF)) //dwarf legions aren't just fluff!
L = new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/dwarf(H.loc)
var/mob/living/basic/mining/hivelord/legion/L
if(HAS_TRAIT(H, TRAIT_DWARF)) // dwarf legions aren't just fluff!
L = new /mob/living/basic/mining/hivelord/legion/dwarf(H.loc)
else
L = new(H.loc)
visible_message("<span class='warning'>[L] staggers to [L.p_their()] feet!</span>")
@@ -253,28 +240,29 @@
H.adjustBruteLoss(1000)
L.stored_mob = H
H.forceMove(L)
ai_controller?.set_blackboard_key(BB_LEGION_CORPSE, H)
qdel(src)
//Advanced Legion is slightly tougher to kill and can raise corpses (revive other legions)
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/advanced
stat_attack = DEAD
// Advanced Legion is slightly tougher to kill and can raise corpses (revive other legions)
/mob/living/basic/mining/hivelord/legion/advanced
maxHealth = 120
health = 120
brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/advanced
ai_controller = /datum/ai_controller/basic_controller/hivelord/legion/advanced
brood_type = /mob/living/basic/mining/hivelordbrood/legion/advanced
icon_state = "dwarf_legion"
icon_living = "dwarf_legion"
icon_aggro = "dwarf_legion"
icon_dead = "dwarf_legion"
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/advanced
stat_attack = DEAD
/mob/living/basic/mining/hivelordbrood/legion/advanced
ai_controller = /datum/ai_controller/basic_controller/hivelord_brood/advanced_legion
can_infest_dead = TRUE
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/advanced/tendril
fromtendril = TRUE
/mob/living/basic/mining/hivelord/legion/advanced/tendril
from_tendril = TRUE
//Legion that spawns Legions
/mob/living/simple_animal/hostile/asteroid/big_legion
// Legion that spawns Legions
/mob/living/basic/mining/big_legion
name = "big legion"
desc = "This monstrosity has clearly been corrupting for centuries, and is looking for a fight. Rumours claim it is capable of throwing the strongest of miners and his name is Billy."
icon = 'icons/mob/lavaland/64x64megafauna.dmi'
@@ -293,7 +281,7 @@
speed = 0
butcher_results = list(/obj/item/regen_mesh)
/mob/living/simple_animal/hostile/asteroid/big_legion/AttackingTarget()
/mob/living/basic/mining/big_legion/melee_attack(atom/target, list/modifiers, ignore_cooldown)
if(!isliving(target))
return ..()
var/mob/living/L = target
@@ -304,7 +292,7 @@
if(G.add_stacks(stacks_added = 1, attacker = src))
return ..()
/mob/living/simple_animal/hostile/asteroid/big_legion/proc/throw_mobs()
/mob/living/basic/mining/big_legion/proc/throw_mobs()
playsound(src, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE)
for(var/mob/living/L in range(3, src))
if(faction_check(faction, L.faction, FALSE))
@@ -319,7 +307,7 @@
var/armor = L.run_armor_check(def_zone = limb_to_hit, attack_flag = MELEE, armour_penetration_percentage = 50)
L.apply_damage(40, BRUTE, limb_to_hit, armor)
//Tendril-spawned Legion remains, the charred skeletons of those whose bodies sank into laval or fell into chasms.
// Tendril-spawned Legion remains, the charred skeletons of those whose bodies sank into laval or fell into chasms.
/obj/effect/mob_spawn/human/corpse/charredskeleton
name = "charred skeletal remains"
burn_damage = 1000
@@ -329,7 +317,7 @@
mob_species = /datum/species/skeleton/brittle
mob_color = "#454545"
//Legion infested mobs
// Legion infested mobs
/obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf/equip(mob/living/carbon/human/H)
. = ..()
@@ -0,0 +1,114 @@
/// Keep away and launch skulls at every opportunity
/datum/ai_controller/basic_controller/hivelord
movement_delay = 1.5 SECONDS
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
BB_AGGRO_RANGE = 5,
BB_BASIC_MOB_FLEE_DISTANCE = 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/summon_brood,
/datum/ai_planning_subtree/flee_target/legion,
)
/// With evil speech
/datum/ai_controller/basic_controller/hivelord/legion
planning_subtrees = list(
/datum/ai_planning_subtree/random_speech/legion,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/summon_brood,
/datum/ai_planning_subtree/flee_target/legion,
)
/datum/ai_controller/basic_controller/hivelord/legion/advanced
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_TARGET_MINIMUM_STAT = DEAD,
BB_AGGRO_RANGE = 5,
BB_BASIC_MOB_FLEE_DISTANCE = 3,
)
/// Chase and attack whatever we are targeting
/datum/ai_controller/basic_controller/hivelord_brood
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
BB_AGGRO_RANGE = 10,
)
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/basic_melee_attack_subtree,
)
/// Same as hivelords, but go after corpses too
/datum/ai_controller/basic_controller/hivelord_brood/advanced_legion
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_TARGET_MINIMUM_STAT = DEAD,
BB_AGGRO_RANGE = 10,
)
planning_subtrees = list(
/datum/ai_planning_subtree/find_and_hunt_target/corpses/human,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
)
/// Don't run away from friendlies
/datum/ai_planning_subtree/flee_target/legion
/datum/ai_planning_subtree/flee_target/legion/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/target = controller.blackboard[target_key]
if(QDELETED(target) || target.faction_check_mob(controller.pawn))
return // Only flee if we have a hostile target
return ..()
/// Make spooky sounds, if we have a corpse inside then impersonate them
/datum/ai_planning_subtree/random_speech/legion
speech_chance = 1
speak = list("Come...", "Legion...", "Why...?")
emote_hear = list("groans.", "wails.", "whimpers.")
emote_see = list("twitches.", "shudders.")
/datum/ai_planning_subtree/random_speech/legion/speak(datum/ai_controller/controller)
var/mob/living/carbon/human/victim = controller.blackboard[BB_LEGION_CORPSE]
if(QDELETED(victim) || prob(30))
return ..()
if(victim.mind && victim.mind.miming) // mimes cant talk
return
var/list/remembered_speech = controller.blackboard[BB_LEGION_RECENT_LINES] || list()
if(length(remembered_speech) && prob(50)) // Don't spam the radio
controller.queue_behavior(/datum/ai_behavior/perform_speech, pick(remembered_speech))
return
/// Create brood
/datum/ai_planning_subtree/summon_brood
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
/datum/ai_planning_subtree/summon_brood/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
var/mob/living/target = controller.blackboard[target_key]
if(!target)
// Only summon if we have a hostile target
return
controller.queue_behavior(/datum/ai_behavior/summon_brood)
/datum/ai_behavior/summon_brood
action_cooldown = 3 SECONDS
behavior_flags = AI_BEHAVIOR_MOVE_AND_PERFORM
required_distance = 0
/datum/ai_behavior/summon_brood/perform(seconds_per_tick, datum/ai_controller/controller)
var/mob/living/basic/mining/hivelord/summoner = controller.pawn
var/mob/living/basic/mining/hivelordbrood/A = new summoner.brood_type(summoner.loc)
A.admin_spawned = summoner.admin_spawned
A.faction = summoner.faction.Copy()
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
@@ -0,0 +1,53 @@
/mob/living/basic/mining
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
status_flags = NONE // don't inherit standard basicmob flags
mob_size = MOB_SIZE_LARGE
mob_biotypes = MOB_ORGANIC|MOB_BEAST
faction = list("mining")
weather_immunities = list("lava","ash")
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
maximum_survivable_temperature = INFINITY
obj_damage = 30
environment_smash = ENVIRONMENT_SMASH_WALLS
a_intent = INTENT_HARM
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
mob_size = MOB_SIZE_LARGE
melee_attack_cooldown_min = 1.5 SECONDS
melee_attack_cooldown_max = 2.5 SECONDS
/// Special loot drops for crushers
var/crusher_loot
/// What is the chance the mob drops it if all their health was taken by crusher attacks
var/crusher_drop_mod = 25
/// Whether or not the mob came from a tendril
var/from_tendril = FALSE
/// Alternate icon for mobs that are angry
var/icon_aggro = null
/// If we want the mob to have 66% resist from burn damage projectiles
var/has_laser_resist = TRUE
/// Message to output if throwing damage is absorbed
var/throw_blocked_message = "bounces off"
/mob/living/basic/mining/bullet_act(obj/item/projectile/P) // Reduces damage from most projectiles to curb off-screen kills
if(P.damage < 30 && P.damage_type != BRUTE && has_laser_resist)
P.damage = (P.damage / 3)
visible_message("<span class='danger'>[P] has a reduced effect on [src]!</span>")
..()
/mob/living/basic/mining/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) // No floor tiling them to death, wiseguy
if(isitem(AM))
var/obj/item/T = AM
if(T.throwforce <= 20)
visible_message("<span class='notice'>[T] [throw_blocked_message] [src.name]!</span>")
return
..()
/mob/living/basic/mining/death(gibbed)
var/datum/status_effect/crusher_damage/C = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING)
if(C && crusher_loot && prob((C.total_damage/maxHealth) * crusher_drop_mod)) // on average, you'll need to kill 4 creatures before getting the item
spawn_crusher_loot()
..(gibbed)
/mob/living/basic/mining/proc/spawn_crusher_loot()
butcher_results[crusher_loot] = 1
@@ -481,7 +481,7 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/hierophant/CanAttack(atom/the_target)
. = ..()
if(istype(the_target, /mob/living/simple_animal/hostile/asteroid/hivelordbrood)) //ignore temporary targets in favor of more permanent targets
if(istype(the_target, /mob/living/basic/mining/hivelordbrood)) //ignore temporary targets in favor of more permanent targets
return FALSE
/mob/living/simple_animal/hostile/megafauna/hierophant/GiveTarget(new_target)
@@ -105,7 +105,7 @@ Difficulty: Medium
if(. && ishuman(target))
var/mob/living/L = target
if(L.stat == UNCONSCIOUS)
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc)
var/mob/living/basic/mining/hivelordbrood/legion/A = new(loc)
A.infest(L)
/mob/living/simple_animal/hostile/megafauna/legion/OpenFire(the_target)
@@ -134,21 +134,21 @@ Difficulty: Medium
SLEEP_CHECK_DEATH(beam_time + 2 SECONDS)
firing_laser = FALSE
else if(prob(40))
var/mob/living/simple_animal/hostile/asteroid/big_legion/A = new(loc)
A.GiveTarget(target)
A.friends = friends
var/mob/living/basic/mining/big_legion/A = new(loc)
A.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target)
A.ai_controller.set_blackboard_key(BB_FRIENDS_LIST, friends)
A.faction = faction
visible_message("<span class='danger'>A monstrosity emerges from [src]</span>",
"<span class='userdanger'>You summon a big [A]!</span>")
ranged_cooldown = world.time + 5 SECONDS
else
var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/A
var/mob/living/basic/mining/hivelord/legion/A
if(enraged)
A = new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/advanced/tendril(loc)
A = new /mob/living/basic/mining/hivelord/legion/advanced/tendril(loc)
else
A = new /mob/living/simple_animal/hostile/asteroid/hivelord/legion/tendril(loc)
A.GiveTarget(target)
A.friends = friends
A = new /mob/living/basic/mining/hivelord/legion/tendril(loc)
A.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target)
A.ai_controller.set_blackboard_key(BB_FRIENDS_LIST, friends)
A.faction = faction
visible_message("<span class='danger'>A [A] emerges from [src]!</span>",
"<span class='userdanger'>You summon a [A]!</span>")
@@ -216,19 +216,19 @@ Difficulty: Medium
M.Scale(resize, resize)
transform = M
if(amount > 0 && (enraged || prob(33)))
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/A
var/mob/living/basic/mining/hivelordbrood/A
if(enraged)
A = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/advanced(loc)
A = new /mob/living/basic/mining/hivelordbrood/legion/advanced(loc)
else
A = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion(loc)
A = new /mob/living/basic/mining/hivelordbrood/legion(loc)
if(!enraged || prob(33))
A.GiveTarget(target)
A.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target)
else
for(var/mob/living/carbon/human/H in range(7, src))
if(H.stat == DEAD)
A.GiveTarget(H)
A.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, H)
break
A.friends = friends
A.ai_controller.set_blackboard_key(BB_FRIENDS_LIST, friends)
A.faction = faction
/obj/item/gps/internal/legion
@@ -338,9 +338,8 @@
/obj/item/crusher_trophy/legionnaire_spine/on_mark_detonation(mob/living/target, mob/living/user)
if(!prob(bonus_value) || target.stat == DEAD)
return
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion(user.loc)
A.GiveTarget(target)
A.friends += user
var/mob/living/basic/mining/hivelordbrood/legion/A = new /mob/living/basic/mining/hivelordbrood/legion(user.loc)
A.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, target)
A.faction = user.faction.Copy()
/obj/item/crusher_trophy/legionnaire_spine/attack_self__legacy__attackchain(mob/user)
@@ -352,8 +351,7 @@
to_chat(LivingUser, "<b>You need to wait longer to use this again.</b>")
return
LivingUser.visible_message("<span class='warning'>[LivingUser] shakes the [src] and summons a legion skull!</span>")
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/LegionSkull = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion(LivingUser.loc)
LegionSkull.friends += LivingUser
var/mob/living/basic/mining/hivelordbrood/legion/LegionSkull = new /mob/living/basic/mining/hivelordbrood/legion(LivingUser.loc)
LegionSkull.faction = LivingUser.faction.Copy()
next_use_time = world.time + 4 SECONDS
@@ -19,8 +19,8 @@
//Monsters
/datum/map_generator_module/splatter_layer/asteroid_monsters
spawnableTurfs = list()
spawnableAtoms = list(/mob/living/simple_animal/hostile/asteroid/basilisk = 10, \
/mob/living/simple_animal/hostile/asteroid/hivelord = 10, \
spawnableAtoms = list(/mob/living/basic/mining/basilisk = 10, \
/mob/living/basic/mining/hivelord = 10, \
/mob/living/simple_animal/hostile/asteroid/goliath = 10)