mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 21:18:37 +01:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user