Files
SmArtKarandGitHub 479ac46740 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.
/🆑
2026-05-12 19:16:02 +01:00

238 lines
9.3 KiB
Plaintext

/// Slow moving mob which attempts to immobilise its target
/mob/living/basic/mining/goliath
name = "goliath"
desc = "A hulking, armor-plated beast with long tendrils arching from its back."
icon = 'icons/mob/simple/lavaland/lavaland_monsters_wide.dmi'
icon_state = "goliath"
icon_living = "goliath"
icon_dead = "goliath_dead"
pixel_x = -12
base_pixel_x = -12
gender = MALE // Female ones are the bipedal elites
speed = 12
basic_mob_flags = IMMUNE_TO_FISTS
maxHealth = 300
health = 300
friendly_verb_continuous = "wails at"
friendly_verb_simple = "wail at"
speak_emote = list("bellows")
obj_damage = 100
melee_damage_lower = 25
melee_damage_upper = 25
attack_sound = 'sound/items/weapons/punch1.ogg'
attack_verb_continuous = "pulverizes"
attack_verb_simple = "pulverize"
throw_blocked_message = "does nothing to the tough hide of"
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
ai_controller = /datum/ai_controller/basic_controller/goliath
crusher_loot = /obj/item/crusher_trophy/goliath_tentacle
butcher_results = list(/obj/item/food/meat/slab/goliath = 2, /obj/item/stack/sheet/bone = 2)
guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/goliath_hide = 1)
/// Icon state to use when tentacles are available
var/tentacle_warning_state = "goliath_preattack"
/// Can this kind of goliath be tamed?
var/tameable = TRUE
/// Can someone ride us around like a horse?
var/saddled = FALSE
/// Slight cooldown to prevent double-dipping if we use both abilities at once
COOLDOWN_DECLARE(ability_animation_cooldown)
/// Our base tentacles ability
var/datum/action/cooldown/mob_cooldown/goliath_tentacles/tentacles
/// Our melee tentacles ability
var/datum/action/cooldown/mob_cooldown/tentacle_burst/melee_tentacles
/// Our long-ranged tentacles ability
var/datum/action/cooldown/mob_cooldown/tentacle_grasp/tentacle_line
/// Things we want to eat off the floor (or a plate, we're not picky)
var/static/list/goliath_foods = list(/obj/item/food/grown/ash_flora, /obj/item/food/bait/worm)
/mob/living/basic/mining/goliath/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_TENTACLE_IMMUNE, INNATE_TRAIT)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY)
AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = goliath_foods)
AddElement(\
/datum/element/change_force_on_death,\
move_force = MOVE_FORCE_DEFAULT,\
move_resist = MOVE_RESIST_DEFAULT,\
pull_force = PULL_FORCE_DEFAULT,\
)
AddComponent(/datum/component/ai_target_timer)
AddComponentFrom(INNATE_TRAIT, /datum/component/shovel_hands)
if (tameable)
AddComponent(/datum/component/tameable, tame_chance = 10, bonus_tame_chance = 5)
tentacles = new (src)
tentacles.Grant(src)
melee_tentacles = new(src)
melee_tentacles.Grant(src)
AddComponent(/datum/component/revenge_ability, melee_tentacles, targeting = GET_TARGETING_STRATEGY(ai_controller.blackboard[BB_TARGETING_STRATEGY]), max_range = 1, target_self = TRUE)
tentacle_line = new (src)
tentacle_line.Grant(src)
AddComponent(/datum/component/revenge_ability, tentacle_line, targeting = GET_TARGETING_STRATEGY(ai_controller.blackboard[BB_TARGETING_STRATEGY]), min_range = 2, max_range = 9)
tentacles_ready()
RegisterSignal(src, COMSIG_MOB_ABILITY_FINISHED, PROC_REF(used_ability))
ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(goliath_foods))
ai_controller.set_blackboard_key(BB_GOLIATH_TENTACLES, tentacles)
update_appearance(UPDATE_OVERLAYS)
/mob/living/basic/mining/goliath/Destroy()
QDEL_NULL(tentacles)
QDEL_NULL(melee_tentacles)
QDEL_NULL(tentacle_line)
return ..()
/mob/living/basic/mining/goliath/examine(mob/user)
. = ..()
if (saddled)
. += span_info("Someone appears to have attached a saddle to this one.")
// Goliaths can summon tentacles more frequently as they take damage, scary.
/mob/living/basic/mining/goliath/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item, wound_clothing)
. = ..()
if (. <= 0)
return
if (tentacles.cooldown_time > 1 SECONDS)
tentacles.cooldown_time -= 1 SECONDS
/mob/living/basic/mining/goliath/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
if (!istype(attacking_item, /obj/item/goliath_saddle))
return ..()
if (!tameable)
balloon_alert(user, "doesn't fit!")
return
if (saddled)
balloon_alert(user, "already saddled!")
return
if (!HAS_TRAIT(src, TRAIT_TAMED))
balloon_alert(user, "too rowdy!")
return
balloon_alert(user, "affixing saddle...")
if (!do_after(user, delay = 5.5 SECONDS, target = src))
return
balloon_alert(user, "ready to ride")
qdel(attacking_item)
make_rideable()
/mob/living/basic/mining/goliath/proc/make_rideable()
saddled = TRUE
add_overlay("goliath_saddled")
AddElement(/datum/element/ridable, /datum/component/riding/creature/goliath)
/// When we use an ability, activate some kind of visual tell
/mob/living/basic/mining/goliath/proc/used_ability(mob/living/source, datum/action/cooldown/ability)
SIGNAL_HANDLER
if (stat == DEAD || ability.IsAvailable())
return // We died or the action failed for some reason like being out of range
if (istype(ability, /datum/action/cooldown/mob_cooldown/goliath_tentacles))
if (ability.cooldown_time <= 2 SECONDS)
return
icon_state = icon_living
addtimer(CALLBACK(src, PROC_REF(tentacles_ready)), ability.cooldown_time - 2 SECONDS, TIMER_DELETE_ME)
return
if (!COOLDOWN_FINISHED(src, ability_animation_cooldown))
return
COOLDOWN_START(src, ability_animation_cooldown, 2 SECONDS)
playsound(src, 'sound/effects/magic/demon_attack1.ogg', vol = 50, vary = TRUE)
Shake(1, 0, 1.5 SECONDS)
/// Called slightly before tentacles ability comes off cooldown, as a warning
/mob/living/basic/mining/goliath/proc/tentacles_ready()
if (stat == DEAD)
return
icon_state = tentacle_warning_state
// Copy entire faction rather than just placing user into faction, to avoid tentacle peril on station
/mob/living/basic/mining/goliath/befriend(mob/living/new_friend)
. = ..()
if(isnull(.))
return
SET_FACTION_AND_ALLIES_FROM(src, new_friend)
/mob/living/basic/mining/goliath/RangedAttack(atom/atom_target, modifiers)
tentacles?.Trigger(target = atom_target)
/mob/living/basic/mining/goliath/ranged_secondary_attack(atom/atom_target, modifiers)
tentacle_line?.Trigger(target = atom_target)
/mob/living/basic/mining/goliath/update_overlays()
. = ..()
if (stat != DEAD)
. += emissive_appearance(icon, "[icon_living]_e", src, effect_type = EMISSIVE_NO_BLOOM)
/// Version of the goliath that already starts saddled and doesn't require a lasso to be ridden.
/mob/living/basic/mining/goliath/deathmatch
saddled = TRUE
buckle_lying = 0
/mob/living/basic/mining/goliath/deathmatch/Initialize(mapload)
. = ..()
make_rideable()
/mob/living/basic/mining/goliath/deathmatch/make_rideable()
add_overlay("goliath_saddled")
AddElement(/datum/element/ridable, /datum/component/riding/creature/goliath/deathmatch)
/// Legacy Goliath mob with different sprites, largely the same behaviour
/mob/living/basic/mining/goliath/ancient
name = "ancient goliath"
desc = "A massive beast that uses long tentacles to ensnare its prey, threatening them is not advised under any conditions."
icon = 'icons/mob/simple/lavaland/lavaland_monsters_wide.dmi'
icon_state = "ancient_goliath"
icon_living = "ancient_goliath"
icon_dead = "ancient_goliath_dead"
tentacle_warning_state = "ancient_goliath_preattack"
tameable = FALSE
/// Rare Goliath variant which occasionally replaces the normal mining mob, releases shitloads of tentacles
/mob/living/basic/mining/goliath/ancient/immortal
name = "immortal goliath"
desc = "Goliaths are biologically immortal, and rare specimens have survived for centuries. \
This one is clearly ancient, and its tentacles constantly churn the earth around it."
maxHealth = 400
health = 400
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
var/list/tentacle_target_turfs
/mob/living/basic/mining/goliath/ancient/immortal/Life(seconds_per_tick)
. = ..()
if (!. || !isturf(loc))
return
if (!LAZYLEN(tentacle_target_turfs) || COOLDOWN_FINISHED(src, retarget_turfs_cooldown))
cache_nearby_turfs()
for (var/turf/target_turf in tentacle_target_turfs)
if (target_turf.is_blocked_turf(exclude_mobs = TRUE))
tentacle_target_turfs -= target_turf
continue
if (prob(10))
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)
. = ..()
if (loc == old_loc || stat == DEAD || !isturf(loc))
return
cache_nearby_turfs()
/// 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, 5 SECONDS)
LAZYCLEARLIST(tentacle_target_turfs)
for(var/turf/open/floor in orange(4, loc))
LAZYADD(tentacle_target_turfs, floor)
/// Use this to ride a goliath
/obj/item/goliath_saddle
name = "goliath saddle"
desc = "This rough saddle will give you a serviceable seat upon a goliath! Provided you can get one to stand still."
icon = 'icons/obj/mining.dmi'
icon_state = "goliath_saddle"