Reworks goliaths, slightly changes watchers/legions/brimdemons/lobstrocities (#96028)

## About The Pull Request

Goliaths no longer instantly stun the mobs they hit with their tentacle
for full 10 seconds, instead leashing them to the spot they were grabbed
at or dragging them towards themselves. The ranged tetris-piece attack
has been changed to a full cross which tethers the target preventing
them from moving away more than 1 tile, while the line and the ring
attacks tether the mob directly to the goliath itself and drag them in.



https://github.com/user-attachments/assets/0b566a12-17ae-4a8a-97ac-5734c89c83d4

The tentacles can be manually removed after standing for 6 seconds, or
by hitting them for 75 damage total (3 PKC swings, or 5 bayonet hits).
They also naturally retract after 10/15 (cross/line and ring) seconds
like before.
Goliaths themselves have received a massive speed boost, going from 3
second movement delay to 1.2 seconds (150% buff) and no longer can
friendly fire with their tentacles (and ancient goliaths have their
trophy drop guaranteed)

Brimdemons, lobstrocities, watchers and legions also received some
changes:
- Brimdemons no longer can wound with their beams (they were basically
guaranteed to land a nasty burn wound with the initial blast), but are
now affected by laser armor and their beam DOT (not the initial 25 burn
damage upon firing) has been increased from 5 to 7. Their blasts can now
be interrupted by hitting them from their side or from their back
- Lobstrocities have had their retreat distance decreased from 8 to 6,
making them much less likely to randomly lose aggro on the miner (their
aggro range is 9 tiles, meaning that even a single tile of movement on
miner's part will result in lobsters losing interest in them when on CD)
- Watchers now try to stick to 3-5 (previously 4-6) tiles of distance
between themselves and their target, and legions try to maintain 4-6
tiles of distance as opposed to running away completely. This should
make fighting both of them more interesting and engaging, and make
dealing with them during vents less cancerous.

Sprites for the tentacle item and mob overlay are by thgvr from
https://github.com/shiptest-ss13/Shiptest/pull/2432

## Why It's Good For The Game

Goliaths are extremely outdated in their attack design, 10 second
hardstuns are basically a guaranteed death if there are any other mobs
around and aren't very engaging to fight on their own as they are very
easy to evade with 3 second movement delays.
Change to lobstrocities should just make their AI less jank, and
watcher/legion range changes should make them less slippery when
fighting with PKC or zero range PKA, or during vent defense.

## Changelog
🆑 SmArtKar, thgvr
balance: Goliaths no longer hardstun, but instead bind and drag their
targets in with their tentacles. They are, however, much faster now.
balance: Brimdemon beams can no longer wound, but deal a bit more DOT
damage. Their beams can also be interrupted by hitting them from the
side or back in melee.
balance: Watchers and legions now try to maintain a few tiles of
distance from their targets instead of retreating.
fix: Lobstrocity AI should no longer sometimes flee out of their aggro
range when retreating.
/🆑
This commit is contained in:
SmArtKar
2026-05-12 19:16:02 +01:00
committed by GitHub
parent 368fbde0e7
commit 479ac46740
27 changed files with 291 additions and 116 deletions
@@ -15,9 +15,16 @@
var/beam_duration = 2 SECONDS
/// How long do we wind up before firing?
var/charge_duration = 1 SECONDS
/// Have we been hit and have to abort the blast?
var/abort_blast = FALSE
/// A list of all the beam parts.
var/list/beam_parts = list()
/datum/action/cooldown/mob_cooldown/brimbeam/Grant(mob/granted_to)
. = ..()
if(owner)
owner.AddElement(/datum/element/relay_attackers)
/datum/action/cooldown/mob_cooldown/brimbeam/Destroy()
extinguish_laser()
return ..()
@@ -25,6 +32,7 @@
/datum/action/cooldown/mob_cooldown/brimbeam/Activate(atom/target)
StartCooldown(360 SECONDS)
abort_blast = FALSE
owner.face_atom(target)
owner.move_resist = MOVE_FORCE_VERY_STRONG
owner.balloon_alert_to_viewers("charging...")
@@ -32,17 +40,20 @@
var/mutable_appearance/direction_emissive = emissive_appearance('icons/mob/simple/lavaland/lavaland_monsters.dmi', "brimdemon_telegraph_dir", owner, alpha = 150, effect_type = EMISSIVE_NO_BLOOM)
owner.add_overlay(direction_overlay)
owner.add_overlay(direction_emissive)
RegisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_owner_attacked))
var/fully_charged = do_after(owner, delay = charge_duration, target = owner)
var/fully_charged = do_after(owner, delay = charge_duration, target = owner, extra_checks = CALLBACK(src, PROC_REF(beam_charge_check)))
owner.cut_overlay(direction_overlay)
owner.cut_overlay(direction_emissive)
if (!fully_charged)
UnregisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED)
StartCooldown()
return TRUE
if (!fire_laser())
var/static/list/fail_emotes = list("coughs.", "wheezes.", "belches out a puff of black smoke.")
owner.manual_emote(pick(fail_emotes))
UnregisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED)
StartCooldown()
return TRUE
@@ -51,11 +62,20 @@
demon.icon_state = demon.firing_icon_state
demon.update_appearance(UPDATE_OVERLAYS)
do_after(owner, delay = beam_duration, target = owner, hidden = TRUE)
do_after(owner, delay = beam_duration, target = owner, hidden = TRUE, extra_checks = CALLBACK(src, PROC_REF(beam_charge_check)))
UnregisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED)
extinguish_laser()
StartCooldown()
return TRUE
/datum/action/cooldown/mob_cooldown/brimbeam/proc/on_owner_attacked(datum/source, atom/attacker, attack_flags, direction)
SIGNAL_HANDLER
if (!(attack_flags & ATTACK_RANGED) && !(direction & owner.dir))
abort_blast = TRUE
/datum/action/cooldown/mob_cooldown/brimbeam/proc/beam_charge_check()
return !abort_blast
/// Create a laser in the direction we are facing
/datum/action/cooldown/mob_cooldown/brimbeam/proc/fire_laser()
owner.visible_message(span_danger("[owner] fires a brimbeam!"))
@@ -67,7 +87,7 @@
if(affected_turf.opacity)
break
var/blocked = FALSE
for(var/obj/potential_block in affected_turf.contents)
for(var/obj/potential_block in affected_turf)
if(potential_block.opacity)
blocked = TRUE
break
@@ -77,8 +97,8 @@
new_brimbeam.dir = owner.dir
beam_parts += new_brimbeam
new_brimbeam.assign_creator(owner)
for(var/mob/living/hit_mob in affected_turf.contents)
hit_mob.apply_damage(damage = 25, damagetype = BURN)
for(var/mob/living/hit_mob in affected_turf)
hit_mob.apply_damage(25, BURN, blocked = hit_mob.run_armor_check(null, LASER, silent = TRUE), wound_bonus = CANT_WOUND)
to_chat(hit_mob, span_userdanger("You're blasted by [owner]'s brimbeam!"))
RegisterSignal(new_brimbeam, COMSIG_QDELETING, PROC_REF(extinguish_laser)) // In case idk a singularity eats it or something
if(!length(beam_parts))
@@ -126,16 +146,10 @@
return ..()
/obj/effect/brimbeam/process()
var/atom/ignore = creator?.resolve()
var/ignore = creator?.resolve()
for(var/mob/living/hit_mob in get_turf(src))
if(hit_mob == ignore)
continue
damage(hit_mob)
/// Hurt the passed mob
/obj/effect/brimbeam/proc/damage(mob/living/hit_mob)
hit_mob.apply_damage(damage = 5, damagetype = BURN)
to_chat(hit_mob, span_danger("You're damaged by [src]!"))
if(hit_mob != ignore)
hit_mob.apply_damage(7, BURN, blocked = hit_mob.run_armor_check(null, LASER, silent = TRUE), wound_bonus = CANT_WOUND)
/// Ignore damage dealt to this mob
/obj/effect/brimbeam/proc/assign_creator(mob/living/maker)
@@ -9,7 +9,7 @@
pixel_x = -12
base_pixel_x = -12
gender = MALE // Female ones are the bipedal elites
speed = 30
speed = 12
basic_mob_flags = IMMUNE_TO_FISTS
maxHealth = 300
health = 300
@@ -197,7 +197,7 @@
This one is clearly ancient, and its tentacles constantly churn the earth around it."
maxHealth = 400
health = 400
crusher_drop_chance = 30 // Wow a whole 5% more likely, how generous
crusher_drop_chance = 100
/// Don't re-check nearby turfs for this long
COOLDOWN_DECLARE(retarget_turfs_cooldown)
/// List of places we might spawn a tentacle, if we're alive
@@ -214,7 +214,7 @@
tentacle_target_turfs -= target_turf
continue
if (prob(10))
new /obj/effect/goliath_tentacle(target_turf)
new /obj/effect/goliath_tentacle(target_turf, src)
/mob/living/basic/mining/goliath/ancient/immortal/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
. = ..()
@@ -224,7 +224,7 @@
/// Store nearby turfs in our list so we can pop them out later
/mob/living/basic/mining/goliath/ancient/immortal/proc/cache_nearby_turfs()
COOLDOWN_START(src, retarget_turfs_cooldown, 10 SECONDS)
COOLDOWN_START(src, retarget_turfs_cooldown, 5 SECONDS)
LAZYCLEARLIST(tentacle_target_turfs)
for(var/turf/open/floor in orange(4, loc))
LAZYADD(tentacle_target_turfs, floor)
@@ -20,12 +20,12 @@
return ..()
/datum/action/cooldown/mob_cooldown/goliath_tentacles/Activate(atom/target)
new /obj/effect/goliath_tentacle(target)
var/list/directions = GLOB.cardinals.Copy()
for(var/i in 1 to 3)
var/spawndir = pick_n_take(directions)
new /obj/effect/goliath_tentacle(target, owner)
for(var/spawndir in GLOB.cardinals)
var/turf/adjacent_target = get_step(target, spawndir)
if(adjacent_target)
if(isgroundlessturf(adjacent_target) && !islava(adjacent_target))
continue
if(isopenturf(adjacent_target) || ismineralturf(adjacent_target))
new /obj/effect/goliath_tentacle(adjacent_target)
if (isliving(target))
@@ -51,7 +51,7 @@
for (var/dir in directions)
var/turf/adjacent_target = get_step(target, dir)
if(adjacent_target)
new /obj/effect/goliath_tentacle(adjacent_target)
new /obj/effect/goliath_tentacle/drag(adjacent_target, owner)
owner.visible_message(span_warning("[owner] unleashes tentacles from the ground around it!"))
StartCooldown()
return TRUE
@@ -70,7 +70,7 @@
shared_cooldown = NONE
/datum/action/cooldown/mob_cooldown/tentacle_grasp/Activate(atom/target)
new /obj/effect/temp_visual/effect_trail/burrowed_tentacle(owner.loc, target)
new /obj/effect/temp_visual/effect_trail/burrowed_tentacle(owner.loc, target, owner)
if (isliving(target))
owner.visible_message(span_warning("[owner] reaches for [target] with its tentacles!"))
StartCooldown()
@@ -83,4 +83,20 @@
move_speed = 2
homing = FALSE
spawn_interval = 0.1 SECONDS
spawned_effect = /obj/effect/goliath_tentacle
spawned_effect = /obj/effect/goliath_tentacle/drag
/// Mob who fired us
var/mob/living/owner = null
/obj/effect/temp_visual/effect_trail/burrowed_tentacle/Initialize(mapload, atom/target, mob/living/owner)
. = ..()
src.owner = owner
/obj/effect/temp_visual/effect_trail/burrowed_tentacle/Destroy()
. = ..()
owner = null
/obj/effect/temp_visual/effect_trail/burrowed_tentacle/add_spawner()
AddComponent(/datum/component/spawner, spawn_types = list(spawned_effect), max_spawned = max_spawned, spawn_time = spawn_interval, spawn_callback = CALLBACK(src, PROC_REF(on_tentacle_spawned)))
/obj/effect/temp_visual/effect_trail/burrowed_tentacle/proc/on_tentacle_spawned(obj/effect/goliath_tentacle/tentacle)
tentacle.set_owner(owner)
@@ -36,7 +36,7 @@
return ..()
var/mob/living/target = controller.blackboard[target_key]
// Interrupt attack chain to use tentacles, unless the target is already tentacled
if (ismecha(target) || (isliving(target) && !target.has_status_effect(/datum/status_effect/incapacitating/stun/goliath_tentacled)))
if (ismecha(target) || (isliving(target) && !target.get_item_by_slot(ITEM_SLOT_LEGCUFFED)))
var/datum/action/cooldown/using_action = controller.blackboard[BB_GOLIATH_TENTACLES]
if (using_action?.IsAvailable())
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
@@ -48,7 +48,7 @@
/datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/target = controller.blackboard[target_key]
if (!(isliving(target) || ismecha(target)) || (isliving(target) && target.has_status_effect(/datum/status_effect/incapacitating/stun/goliath_tentacled)))
if (!(isliving(target) || ismecha(target)) || (isliving(target) && target.get_item_by_slot(ITEM_SLOT_LEGCUFFED)))
return // Target can be an item or already grabbed, we don't want to tentacle those
var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0
if (time_on_target < MIN_TIME_TO_TENTACLE)
@@ -13,9 +13,13 @@
/// Lower bound of damage to inflict
var/min_damage = 10
/// Upper bound of damage to inflict
var/max_damage = 15
var/max_damage = 15
/// Type of legcuff we spawn
var/trap_type = /obj/item/restraints/legcuffs/goliath_tentacle
/// Mob who fired the tentacle
var/mob/living/owner = null
/obj/effect/goliath_tentacle/Initialize(mapload)
/obj/effect/goliath_tentacle/Initialize(mapload, mob/living/goliath)
. = ..()
if (ismineralturf(loc))
var/turf/closed/mineral/floor = loc
@@ -28,11 +32,24 @@
deltimer(action_timer)
action_timer = addtimer(CALLBACK(src, PROC_REF(animate_grab)), 0.7 SECONDS, TIMER_STOPPABLE)
update_appearance(UPDATE_OVERLAYS)
set_owner(goliath)
/obj/effect/goliath_tentacle/Destroy()
deltimer(action_timer)
return ..()
/obj/effect/goliath_tentacle/proc/set_owner(mob/living/goliath)
if (owner)
UnregisterSignal(owner, COMSIG_QDELETING)
owner = goliath
if (owner)
RegisterSignal(owner, COMSIG_QDELETING, PROC_REF(on_owner_del))
/obj/effect/goliath_tentacle/proc/on_owner_del(datum/source)
SIGNAL_HANDLER
owner = null
retract()
/// Change to next icon state and set up grapple
/obj/effect/goliath_tentacle/proc/animate_grab()
icon_state = "goliath_tentacle_wiggle"
@@ -42,20 +59,31 @@
/// Grab everyone we share space with. If it's nobody, go home.
/obj/effect/goliath_tentacle/proc/grab()
var/trapped_mobs = FALSE
for (var/mob/living/victim in loc)
if (victim.stat == DEAD || HAS_TRAIT(victim, TRAIT_TENTACLE_IMMUNE))
if (victim.stat == DEAD || owner && victim.faction_check_atom(owner))
continue
if (HAS_TRAIT(victim, TRAIT_TENTACLE_IMMUNE) || SEND_SIGNAL(victim, COMSIG_GOLIATH_TENTACLED_GRABBED) & COMPONENT_GOLIATH_CANCEL_TENTACLE_GRAB)
continue
var/obj/item/restraints/legcuffs/goliath_tentacle/trap = new trap_type(loc, victim, src)
if (QDELETED(trap))
continue
balloon_alert(victim, "grabbed")
visible_message(span_danger("[src] grabs hold of [victim]!"))
victim.adjust_brute_loss(rand(min_damage, max_damage))
if (victim.apply_status_effect(/datum/status_effect/incapacitating/stun/goliath_tentacled, grapple_time, src))
buckle_mob(victim, TRUE)
SEND_SIGNAL(victim, COMSIG_GOLIATH_TENTACLED_GRABBED)
victim.apply_damage(rand(min_damage, max_damage), BRUTE, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG), wound_bonus = CANT_WOUND)
trapped_mobs = TRUE
for (var/obj/vehicle/sealed/mecha/mech in loc)
mech.take_damage(rand(min_damage, max_damage), damage_type = BRUTE, damage_flag = MELEE, sound_effect = TRUE)
if (!has_buckled_mobs())
// Already retracting
if (icon_state == "goliath_tentacle_retract")
return
if (!trapped_mobs)
retract()
return
deltimer(action_timer)
action_timer = addtimer(CALLBACK(src, PROC_REF(retract)), grapple_time, TIMER_STOPPABLE)
@@ -64,7 +92,6 @@
if (icon_state == "goliath_tentacle_retract")
return // Already retracting
SEND_SIGNAL(src, COMSIG_GOLIATH_TENTACLE_RETRACTING)
unbuckle_all_mobs(force = TRUE)
icon_state = "goliath_tentacle_retract"
update_appearance(UPDATE_OVERLAYS)
deltimer(action_timer)
@@ -77,54 +104,136 @@
/obj/effect/goliath_tentacle/attack_hand(mob/living/user, list/modifiers)
. = ..()
if (. || !has_buckled_mobs())
return
retract()
return TRUE
if(!.)
retract()
return TRUE
/// Goliath tentacle stun with special removal conditions
/datum/status_effect/incapacitating/stun/goliath_tentacled
id = "goliath_tentacled"
duration = 10 SECONDS
/// The tentacle that is tenderly holding us close
/// An alternative variant which drags the mob in towards the goliath
/obj/effect/goliath_tentacle/drag
trap_type = /obj/item/restraints/legcuffs/goliath_tentacle/drag
/obj/item/restraints/legcuffs/goliath_tentacle
name = "tentacle mass"
desc = "A writhing tentacle constricting one of your limbs."
icon_state = "goliath_tentacle"
slowdown = 4
breakouttime = 6 SECONDS
resistance_flags = LAVA_PROOF | FIRE_PROOF
obj_flags = CONDUCTS_ELECTRICITY | CAN_BE_HIT
item_flags = DROPDEL
legcuff_state = "goliath_tentacle"
uses_integrity = TRUE
max_integrity = 75 // 3 PKC swings, 5 bayonet swings
/// Tentacle we're attached to
var/obj/effect/goliath_tentacle/tentacle
/// Leash component linking the victim to the turf
var/datum/component/leash/leash
/// Beam between the victim and the tentacle tile
var/datum/beam/beam_effect
/datum/status_effect/incapacitating/stun/goliath_tentacled/on_creation(mob/living/new_owner, set_duration, obj/effect/goliath_tentacle/tentacle)
/obj/item/restraints/legcuffs/goliath_tentacle/Initialize(mapload, mob/living/target, obj/effect/goliath_tentacle/tentacle)
. = ..()
if (!.)
return
ADD_TRAIT(src, TRAIT_IGNORE_DEMOLITION, INNATE_TRAIT)
src.tentacle = tentacle
if (!target?.equip_to_slot_if_possible(src, ITEM_SLOT_LEGCUFFED, disable_warning = TRUE, bypass_equip_delay_self = TRUE))
return INITIALIZE_HINT_QDEL
/datum/status_effect/incapacitating/stun/goliath_tentacled/on_apply()
/obj/item/restraints/legcuffs/goliath_tentacle/Destroy(force)
. = ..()
RegisterSignal(owner, COMSIG_CARBON_PRE_MISC_HELP, PROC_REF(on_helped))
RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE), COMSIG_BRIMDUST_EXPLOSION), PROC_REF(release))
RegisterSignals(tentacle, list(COMSIG_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING), PROC_REF(on_tentacle_left))
if (tentacle)
tentacle.retract()
tentacle = null
if (leash)
UnregisterSignal(leash, COMSIG_QDELETING)
QDEL_NULL(leash)
QDEL_NULL(beam_effect)
/datum/status_effect/incapacitating/stun/goliath_tentacled/on_remove()
/obj/item/restraints/legcuffs/goliath_tentacle/equipped(mob/living/user, slot, initial)
. = ..()
UnregisterSignal(owner, list(COMSIG_CARBON_PRE_MISC_HELP, SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE), COMSIG_BRIMDUST_EXPLOSION))
if (isnull(tentacle))
if (slot != ITEM_SLOT_LEGCUFFED || leash)
return
UnregisterSignal(tentacle, list(COMSIG_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING))
leash_target(user)
/obj/item/restraints/legcuffs/goliath_tentacle/proc/release(datum/source)
SIGNAL_HANDLER
qdel(src)
/obj/item/restraints/legcuffs/goliath_tentacle/proc/on_tentacle_left(datum/source)
SIGNAL_HANDLER
if (tentacle)
UnregisterSignal(tentacle, list(COMSIG_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING))
tentacle = null
release()
/obj/item/restraints/legcuffs/goliath_tentacle/proc/leash_target(mob/living/user)
leash = user.AddComponent(/datum/component/leash, owner = get_turf(user), distance = 1, silent = TRUE)
beam_effect = user.Beam(get_turf(user), "goliath_tentacle", beam_type = /obj/effect/ebeam/goliath, emissive = FALSE)
RegisterSignal(beam_effect.visuals, COMSIG_CLICK, PROC_REF(on_beam_click))
RegisterSignals(user, list(SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE), COMSIG_BRIMDUST_EXPLOSION), PROC_REF(release))
RegisterSignals(tentacle, list(COMSIG_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING), PROC_REF(on_tentacle_left))
RegisterSignal(leash, COMSIG_QDELETING, PROC_REF(release))
/obj/item/restraints/legcuffs/goliath_tentacle/proc/on_beam_click(atom/source, atom/location, control, params, mob/user)
SIGNAL_HANDLER
INVOKE_ASYNC(src, PROC_REF(process_beam_click), source, location, params, user)
/obj/item/restraints/legcuffs/goliath_tentacle/proc/process_beam_click(atom/source, atom/location, params, mob/user)
if(world.time <= user.next_move)
return
var/obj/item/held_thing = user.get_active_held_item()
if (!istype(held_thing))
return
var/turf/nearest_turf = null
for (var/turf/line_turf in get_line(get_turf(src), get_turf(beam_effect.target)))
if (line_turf.IsReachableBy(user))
nearest_turf = line_turf
break
if (isnull(nearest_turf))
return
if (!user.can_perform_action(nearest_turf))
nearest_turf.balloon_alert(user, "cannot reach!")
return
held_thing.melee_attack_chain(user, src, params2list(params))
user.changeNext_move(held_thing.attack_speed)
playsound(user, held_thing.hitsound || 'sound/effects/blob/attackblob.ogg', held_thing.get_clamped_volume(), TRUE, extrarange = held_thing.stealthy_audio ? SILENCED_SOUND_EXTRARANGE : -1, falloff_distance = 0)
/obj/item/restraints/legcuffs/goliath_tentacle/play_attack_sound(damage_amount, damage_type, damage_flag)
return
/obj/item/restraints/legcuffs/goliath_tentacle/drag
/obj/item/restraints/legcuffs/goliath_tentacle/drag/Initialize(mapload, mob/living/target, obj/effect/goliath_tentacle/tentacle)
. = ..()
QDEL_IN(src, 15 SECONDS)
if (. != INITIALIZE_HINT_QDEL)
START_PROCESSING(SSprocessing, src)
/obj/item/restraints/legcuffs/goliath_tentacle/drag/Destroy(force)
. = ..()
STOP_PROCESSING(SSprocessing, src)
/obj/item/restraints/legcuffs/goliath_tentacle/drag/process(seconds_per_tick)
if (leash.distance <= 1)
return PROCESS_KILL
leash.set_distance(leash.distance - 1)
/obj/item/restraints/legcuffs/goliath_tentacle/drag/leash_target(mob/living/user)
if (!tentacle?.owner)
return ..()
leash = user.AddComponent(/datum/component/leash, owner = tentacle.owner, distance = get_dist(user, tentacle.owner), silent = TRUE)
beam_effect = user.Beam(tentacle.owner, "goliath_tentacle", beam_type = /obj/effect/ebeam/goliath, emissive = FALSE)
RegisterSignal(beam_effect.visuals, COMSIG_CLICK, PROC_REF(on_beam_click))
RegisterSignals(user, list(SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE), COMSIG_BRIMDUST_EXPLOSION), PROC_REF(release))
RegisterSignals(tentacle.owner, COMSIG_QDELETING, PROC_REF(on_tentacle_left))
RegisterSignal(leash, COMSIG_QDELETING, PROC_REF(release))
tentacle.retract()
tentacle = null
/// Some kind soul has rescued us
/datum/status_effect/incapacitating/stun/goliath_tentacled/proc/on_helped(mob/source, mob/helping)
SIGNAL_HANDLER
release()
source.visible_message(span_notice("[helping] rips [source] from the tentacle's grasp!"))
return COMPONENT_BLOCK_MISC_HELP
/// Something happened to make the tentacle let go
/datum/status_effect/incapacitating/stun/goliath_tentacled/proc/release()
SIGNAL_HANDLER
owner.remove_status_effect(/datum/status_effect/incapacitating/stun/goliath_tentacled)
/// Something happened to our associated tentacle
/datum/status_effect/incapacitating/stun/goliath_tentacled/proc/on_tentacle_left()
SIGNAL_HANDLER
UnregisterSignal(tentacle, list(COMSIG_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING)) // No endless loops for us please
tentacle = null
release()
/obj/effect/ebeam/goliath
name = "goliath tentacle"
mouse_opacity = MOUSE_OPACITY_ICON
@@ -75,7 +75,7 @@
/mob/living/basic/mining/legion/update_overlays()
. = ..()
if (stat != DEAD && has_emissive) // Shouldn't really happen but just in case
. += emissive_appearance(icon, "[icon_living]_e", src, effect_type = EMISSIVE_NO_BLOOM)
. += emissive_appearance(icon, "[icon_living]_e", src, effect_type = EMISSIVE_BLOOM)
/// Put a corpse in this guy
/mob/living/basic/mining/legion/proc/consume(mob/living/carbon/human/consumed)
@@ -5,7 +5,8 @@
BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining,
BB_TARGET_MINIMUM_STAT = HARD_CRIT,
BB_AGGRO_RANGE = 5, // Unobservant
BB_BASIC_MOB_FLEE_DISTANCE = 6,
BB_RANGED_SKIRMISH_MIN_DISTANCE = 4,
BB_RANGED_SKIRMISH_MAX_DISTANCE = 6,
)
ai_movement = /datum/ai_movement/basic_avoidance
@@ -15,8 +16,8 @@
/datum/ai_planning_subtree/call_reinforcements/mining,
/datum/ai_planning_subtree/random_speech/legion,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/maintain_distance,
/datum/ai_planning_subtree/targeted_mob_ability,
/datum/ai_planning_subtree/flee_target/legion,
)
/// Chase and attack whatever we are targeting, if it's friendly we will heal them
@@ -14,7 +14,7 @@
BB_LOBSTROSITY_EXPLOIT_TRAITS = list(TRAIT_INCAPACITATED, TRAIT_FLOORED, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT),
BB_LOBSTROSITY_FINGER_LUST = 0,
BB_LOBSTROSITY_NAIVE_HUNTER = FALSE,
BB_BASIC_MOB_FLEE_DISTANCE = 8,
BB_BASIC_MOB_FLEE_DISTANCE = 6,
BB_EAT_FOOD_COOLDOWN = 3 MINUTES,
BB_ONLY_FISH_WHILE_HUNGRY = TRUE,
BB_TARGET_PRIORITY_TRAIT = TRAIT_SCARY_FISHERMAN,
@@ -2,6 +2,8 @@
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_TARGET_PRIORITY_STRATEGY = /datum/target_priority_strategy/mining,
BB_RANGED_SKIRMISH_MIN_DISTANCE = 3,
BB_RANGED_SKIRMISH_MAX_DISTANCE = 5,
)
ai_movement = /datum/ai_movement/basic_avoidance