diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm
index 0f0b47cb810..653b784e44a 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_survivalpod.dmm
@@ -65,9 +65,7 @@
/area/lavaland/surface/outdoors)
"o" = (
/obj/effect/decal/cleanable/blood,
-/mob/living/simple_animal/hostile/asteroid/goliath/beast{
- health = 0
- },
+/obj/effect/mob_spawn/corpse/goliath,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"p" = (
diff --git a/code/__DEFINES/ai/blackboard_defines.dm b/code/__DEFINES/ai/blackboard_defines.dm
index c74daddbd25..d6f98e271c5 100644
--- a/code/__DEFINES/ai/blackboard_defines.dm
+++ b/code/__DEFINES/ai/blackboard_defines.dm
@@ -53,6 +53,8 @@
#define BB_OBSTACLE_TARGETING_WHITELIST "BB_targeting_whitelist"
/// some behaviors that check current_target also set this on deep crit mobs
#define BB_BASIC_MOB_EXECUTION_TARGET "BB_basic_execution_target"
+/// Blackboard key storing how long your targeting strategy has held a particular target
+#define BB_BASIC_MOB_HAS_TARGET_TIME "BB_BASIC_MOB_HAS_TARGET_TIME"
//Hunting BB keys
@@ -230,3 +232,9 @@
// Malf Drones
/// Are we active?
#define BB_MALF_DRONE_PASSIVE "BB_MALF_DRONE_PASSIVE"
+
+// Goliath AI keys
+/// Key where we store the tentacleing ability
+#define BB_GOLIATH_TENTACLES "BB_GOLIATH_TENTACLES"
+/// Key where goliath stores a hole it wants to get into
+#define BB_GOLIATH_HOLE_TARGET "BB_GOLIATH_HOLE"
diff --git a/code/__DEFINES/dcs/mob_signals.dm b/code/__DEFINES/dcs/mob_signals.dm
index a619004bed4..dc6f8fd0f6f 100644
--- a/code/__DEFINES/dcs/mob_signals.dm
+++ b/code/__DEFINES/dcs/mob_signals.dm
@@ -279,5 +279,15 @@
/// From base of /datum/action/cooldown/proc/set_statpanel_format(): (list/stat_panel_data)
#define COMSIG_ACTION_SET_STATPANEL "ability_set_statpanel"
+/// Fired by a mob which has been grabbed by a goliath
+#define COMSIG_GOLIATH_TENTACLED_GRABBED "goliath_tentacle_grabbed"
+/// Fired by a goliath tentacle which is returning to the earth
+#define COMSIG_GOLIATH_TENTACLE_RETRACTING "goliath_tentacle_retracting"
+
+/// Called from /mob/living/carbon/help_shake_act, before any hugs have occurred. (mob/living/helper)
+#define COMSIG_CARBON_PRE_MISC_HELP "carbon_pre_misc_help"
+ /// Stops the rest of help act (hugging, etc) from occurring
+ #define COMPONENT_BLOCK_MISC_HELP (1<<0)
+
/// From base of /client/Move(): (direction, old_dir)
#define COMSIG_MOB_CLIENT_MOVED "mob_client_moved"
diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm
index 07c2f716e03..cc56db5b1cc 100644
--- a/code/__HELPERS/trait_helpers.dm
+++ b/code/__HELPERS/trait_helpers.dm
@@ -513,6 +513,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_CANNOT_PULL "pullblocked"
/// Abstract condition that prevents movement if being pulled and might be resisted against. Handcuffs and straight jackets, basically.
#define TRAIT_RESTRAINED "restrained"
+#define TRAIT_FROM_TENDRIL "from_tendril"
+#define TRAIT_TENTACLE_IMMUNE "tentacle_immune"
///Traits given by station traits
#define STATION_TRAIT_BANANIUM_SHIPMENTS "station_trait_bananium_shipments"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 47d641ce4fb..473b2767a84 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -121,6 +121,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_RELAYING_ATTACKER" = TRAIT_RELAYING_ATTACKER,
"TRAIT_LOUD" = TRAIT_LOUD,
"TRAIT_WIRE_BLIND" = TRAIT_WIRE_BLIND,
+ "TRAIT_FROM_TENDRIL" = TRAIT_FROM_TENDRIL,
+ "TRAIT_TENTACLE_IMMUNE" = TRAIT_TENTACLE_IMMUNE,
"TRAIT_BOOTS_OF_JUMPING" = TRAIT_BOOTS_OF_JUMPING
),
diff --git a/code/datums/components/ai_has_target_timer.dm b/code/datums/components/ai_has_target_timer.dm
new file mode 100644
index 00000000000..12aaf814a7a
--- /dev/null
+++ b/code/datums/components/ai_has_target_timer.dm
@@ -0,0 +1,79 @@
+/// Increments a blackboard key while the attached mob is engaged with a particular target, does nothing else on its own
+/datum/component/ai_target_timer
+ /// Blackboard key to store data inside
+ var/increment_key
+ /// Blackboard key to watch to indicate whether we are 'in combat'
+ var/target_key
+ /// Amount of time we have spent focused on one target
+ var/time_on_target = 0
+ /// The last target we had
+ var/atom/last_target
+ /// Timer used to see if you
+ var/reset_clock_timer
+
+/datum/component/ai_target_timer/Initialize(increment_key = BB_BASIC_MOB_HAS_TARGET_TIME, target_key = BB_BASIC_MOB_CURRENT_TARGET)
+ . = ..()
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+ var/mob/living/mob_parent = parent
+ if(isnull(mob_parent.ai_controller))
+ return COMPONENT_INCOMPATIBLE
+ src.increment_key = increment_key
+ src.target_key = target_key
+
+/datum/component/ai_target_timer/RegisterWithParent()
+ . = ..()
+ RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_SET(target_key), PROC_REF(changed_target))
+ RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key), PROC_REF(lost_target))
+ ADD_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type)
+
+/datum/component/ai_target_timer/UnregisterFromParent()
+ finalise_losing_target()
+ UnregisterSignal(parent, list(COMSIG_AI_BLACKBOARD_KEY_SET(target_key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key)))
+ REMOVE_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type)
+ return ..()
+
+/datum/component/ai_target_timer/Destroy(force)
+ finalise_losing_target()
+ return ..()
+
+/// When we get a new target, reset the timer and start processing
+/datum/component/ai_target_timer/proc/changed_target(mob/living/source)
+ SIGNAL_HANDLER
+ var/mob/living/living_parent = parent
+ var/atom/new_target = living_parent.ai_controller.blackboard[target_key]
+ deltimer(reset_clock_timer)
+ if(new_target == last_target)
+ return
+ time_on_target = 0
+ store_current_time()
+ START_PROCESSING(SSdcs, src)
+ if(!isnull(last_target))
+ UnregisterSignal(last_target, COMSIG_PARENT_QDELETING)
+ RegisterSignal(new_target, COMSIG_PARENT_QDELETING, PROC_REF(finalise_losing_target))
+ last_target = new_target
+
+/// When we lose our target, start a short timer in case we reacquire it very quickly
+/datum/component/ai_target_timer/proc/lost_target()
+ SIGNAL_HANDLER
+ reset_clock_timer = addtimer(CALLBACK(src, PROC_REF(finalise_losing_target)), 3 SECONDS, TIMER_STOPPABLE | TIMER_DELETE_ME)
+
+/// Called if we have had no target for long enough
+/datum/component/ai_target_timer/proc/finalise_losing_target()
+ deltimer(reset_clock_timer)
+ STOP_PROCESSING(SSdcs, src)
+ if(!isnull(last_target))
+ UnregisterSignal(last_target, COMSIG_PARENT_QDELETING)
+ last_target = null
+ time_on_target = 0
+ if(!QDELETED(parent))
+ store_current_time()
+
+/// Store the current time on our timer in our blackboard key
+/datum/component/ai_target_timer/proc/store_current_time()
+ var/mob/living/living_parent = parent
+ living_parent.ai_controller.set_blackboard_key(increment_key, time_on_target)
+
+/datum/component/ai_target_timer/process(seconds_per_tick)
+ time_on_target += seconds_per_tick SECONDS
+ store_current_time()
diff --git a/code/datums/components/spawner.dm b/code/datums/components/spawner.dm
index a289377cf67..6b331752bd8 100644
--- a/code/datums/components/spawner.dm
+++ b/code/datums/components/spawner.dm
@@ -53,6 +53,7 @@
spawned_mobs += L
L.nest = src
L.faction = src.faction
+ ADD_TRAIT(L, TRAIT_FROM_TENDRIL, INNATE_TRAIT)
P.visible_message("[L] [spawn_text] [P].")
P.on_mob_spawn(L)
return L
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 8067481defc..3ff685831fa 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -647,11 +647,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
return INITIALIZE_HINT_QDEL
/obj/effect/landmark/mob_spawner/goliath
- mobtype = /mob/living/simple_animal/hostile/asteroid/goliath/beast
+ mobtype = /mob/living/basic/mining/goliath
/obj/effect/landmark/mob_spawner/goliath/Initialize(mapload)
if(prob(1))
- mobtype = /mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient
+ mobtype = /mob/living/basic/mining/goliath/ancient
. = ..()
/obj/effect/landmark/mob_spawner/legion
diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm
index a2e0c09600b..b69b523b9ed 100644
--- a/code/game/objects/structures/lavaland/necropolis_tendril.dm
+++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm
@@ -9,7 +9,7 @@
faction = list("mining")
max_mobs = 3
max_integrity = 250
- mob_types = list(/mob/living/basic/mining/basilisk/watcher/tendril)
+ mob_types = list(/mob/living/basic/mining/basilisk/watcher)
move_resist = INFINITY // just killing it tears a massive hole in the ground, let's not move it
resistance_flags = FIRE_PROOF | LAVA_PROOF
@@ -17,10 +17,10 @@
var/obj/effect/light_emitter/tendril/emitted_light
/obj/structure/spawner/lavaland/goliath
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril)
+ mob_types = list(/mob/living/basic/mining/goliath)
/obj/structure/spawner/lavaland/legion
- mob_types = list(/mob/living/basic/mining/hivelord/legion/tendril)
+ mob_types = list(/mob/living/basic/mining/hivelord/legion)
GLOBAL_LIST_EMPTY(tendrils)
diff --git a/code/game/objects/structures/monster_spawner.dm b/code/game/objects/structures/monster_spawner.dm
index 0a6897c2139..7ed71b15440 100644
--- a/code/game/objects/structures/monster_spawner.dm
+++ b/code/game/objects/structures/monster_spawner.dm
@@ -53,7 +53,12 @@
max_mobs = 3
icon = 'icons/mob/nest.dmi'
spawn_text = "crawls out of"
- mob_types = list(/mob/living/basic/mining/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/basic/mining/hivelord, /mob/living/basic/mining/basilisk)
+ mob_types = list(
+ /mob/living/basic/mining/goldgrub,
+ /mob/living/basic/mining/goliath,
+ /mob/living/basic/mining/hivelord,
+ /mob/living/basic/mining/basilisk,
+ )
faction = list("mining")
/obj/structure/spawner/mining/goldgrub
@@ -64,10 +69,10 @@
/obj/structure/spawner/mining/goliath
name = "goliath den"
desc = "A den housing a nest of goliaths, oh god why?"
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goliath)
+ mob_types = list(/mob/living/basic/mining/goliath)
/obj/structure/spawner/mining/goliath/space
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goliath/space)
+ mob_types = list(/mob/living/basic/mining/goliath/space)
/obj/structure/spawner/mining/hivelord
name = "hivelord den"
diff --git a/code/game/objects/structures/nest.dm b/code/game/objects/structures/nest.dm
index 9220eacd61d..b86a0f6a252 100644
--- a/code/game/objects/structures/nest.dm
+++ b/code/game/objects/structures/nest.dm
@@ -71,7 +71,10 @@
visible_message("\A [spawned_mob.name] crawls out of \the [name]!")
/obj/structure/nest/lavaland
- spawn_mob_options = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast, /mob/living/basic/mining/goldgrub)
+ spawn_mob_options = list(
+ /mob/living/basic/mining/goliath,
+ /mob/living/basic/mining/goldgrub,
+ )
/obj/structure/nest/carppuppy
spawn_mob_options = list(/mob/living/basic/carp, /mob/living/simple_animal/pet/dog/corgi/puppy/void)
diff --git a/code/modules/awaymissions/mob_spawn.dm b/code/modules/awaymissions/mob_spawn.dm
index c40d91d3480..16c1721acad 100644
--- a/code/modules/awaymissions/mob_spawn.dm
+++ b/code/modules/awaymissions/mob_spawn.dm
@@ -802,7 +802,7 @@
pixel_x = -12
/obj/effect/mob_spawn/corpse/goliath
- mob_type = /mob/living/simple_animal/hostile/asteroid/goliath/beast
+ mob_type = /mob/living/basic/mining/goliath
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
icon_state = "goliath_dead"
pixel_x = -12
diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
new file mode 100644
index 00000000000..fff35144332
--- /dev/null
+++ b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
@@ -0,0 +1,147 @@
+/// A slow but strong beast that tries to stun using its tentacles.
+/mob/living/basic/mining/goliath
+ name = "goliath"
+ desc = "A massive beast that uses long tentacles to ensnare its prey, threatening them is not advised under any conditions."
+ icon_state = "goliath"
+ icon_living = "goliath"
+ icon_aggro = "goliath"
+ icon_dead = "goliath_dead"
+ icon_gib = "syndicate_gib"
+ speak_emote = list("bellows")
+ speed = 5
+ maxHealth = 300
+ health = 300
+ harm_intent_damage = 1 // Only the manliest of men can kill a Goliath with only their fists.
+ obj_damage = 100
+ melee_damage_lower = 25
+ melee_damage_upper = 25
+ attack_verb_simple = "pulverize"
+ attack_verb_continuous = "pulverizes"
+ throw_blocked_message = "does nothing to the rocky hide of the"
+ move_force = MOVE_FORCE_VERY_STRONG
+ move_resist = MOVE_FORCE_VERY_STRONG
+ pull_force = MOVE_FORCE_VERY_STRONG
+ crusher_loot = /obj/item/crusher_trophy/goliath_tentacle
+ ai_controller = /datum/ai_controller/basic_controller/goliath
+ faction = list("mining")
+ butcher_results = list(
+ /obj/item/food/monstermeat/goliath = 2,
+ /obj/item/stack/sheet/animalhide/goliath_hide = 1,
+ /obj/item/stack/sheet/bone = 2,
+ )
+
+ /// Icon state to use when tentacles are available
+ var/tentacle_warning_state = "goliath2"
+ /// 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
+ /// 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,
+ )
+
+
+/mob/living/basic/mining/goliath/Initialize(mapload)
+ . = ..()
+
+ ADD_TRAIT(src, TRAIT_TENTACLE_IMMUNE, INNATE_TRAIT)
+
+ AddElement(/datum/element/ai_retaliate)
+ AddElement(/datum/element/basic_eating, heal_amt_ = 10, food_types_ = goliath_foods)
+ AddComponent(/datum/component/ai_target_timer)
+ AddComponent(/datum/component/footstep, FOOTSTEP_MOB_HEAVY)
+
+ tentacles = new(src)
+ tentacles.Grant(src)
+
+ 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)
+
+/// 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
+
+/// 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)
+ Shake(1, 0, 1.5 SECONDS)
+
+/mob/living/basic/mining/goliath/Destroy()
+ . = ..()
+ QDEL_NULL(tentacles)
+
+/mob/living/basic/mining/goliath/death(gibbed)
+ move_force = MOVE_FORCE_DEFAULT
+ move_resist = MOVE_RESIST_DEFAULT
+ pull_force = PULL_FORCE_DEFAULT
+ ..(gibbed)
+
+/mob/living/basic/mining/goliath/ancient
+ name = "ancient 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."
+ icon_state = "Goliath"
+ icon_living = "Goliath"
+ icon_dead = "Goliath_dead"
+ maxHealth = 400
+ health = 400
+ speed = 4
+ tentacle_warning_state = "Goliath_preattack"
+ butcher_results = list(
+ /obj/item/food/monstermeat/goliath = 2,
+ /obj/item/stack/sheet/bone = 2,
+ )
+ loot = list(/obj/item/stack/sheet/animalhide/goliath_hide)
+ crusher_loot = /obj/item/crusher_trophy/goliath_tentacle/ancient
+ crusher_drop_mod = 100 //These things are rare (1/100 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
+ /// 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/Life(seconds_per_tick, times_fired)
+ . = ..()
+ 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/temp_visual/goliath_tentacle(target_turf)
+
+/mob/living/basic/mining/goliath/ancient/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/proc/cache_nearby_turfs()
+ COOLDOWN_START(src, retarget_turfs_cooldown, 10 SECONDS)
+ LAZYCLEARLIST(tentacle_target_turfs)
+ for(var/turf/T in orange(4, loc))
+ if(isfloorturf(T))
+ LAZYADD(tentacle_target_turfs, T)
+
+/mob/living/basic/mining/goliath/space
+
+/mob/living/basic/mining/goliath/space/Process_Spacemove(movement_dir, continuous_move)
+ return TRUE
diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath_actions.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath_actions.dm
new file mode 100644
index 00000000000..83ea6f7c9ca
--- /dev/null
+++ b/code/modules/mob/living/basic/lavaland/goliath/goliath_actions.dm
@@ -0,0 +1,32 @@
+/// Place some grappling tentacles underfoot
+/datum/action/cooldown/mob_cooldown/goliath_tentacles
+ name = "Unleash Tentacles"
+ desc = "Unleash burrowed tentacles at a targeted location, grappling targets after a delay."
+ button_icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
+ button_icon_state = "Goliath_tentacle_wiggle"
+ background_icon_state = "bg_demon"
+ overlay_icon_state = "bg_demon_border"
+ cooldown_time = 12 SECONDS
+ shared_cooldown = NONE
+ /// Furthest range we can activate ability at
+ var/max_range = 7
+
+/datum/action/cooldown/mob_cooldown/goliath_tentacles/PreActivate(atom/target)
+ target = get_turf(target)
+ if(get_dist(owner, target) > max_range)
+ return FALSE
+ return ..()
+
+/datum/action/cooldown/mob_cooldown/goliath_tentacles/Activate(atom/target)
+ new /obj/effect/temp_visual/goliath_tentacle(target)
+ var/list/directions = GLOB.cardinal.Copy()
+ for(var/i in 1 to 3)
+ var/spawndir = pick_n_take(directions)
+ var/turf/adjacent_target = get_step(target, spawndir)
+ if(adjacent_target)
+ new /obj/effect/temp_visual/goliath_tentacle(adjacent_target)
+
+ if(isliving(target))
+ owner.visible_message("[owner] digs its tentacles under [target]!")
+ StartCooldown()
+ return TRUE
diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm
new file mode 100644
index 00000000000..81615341134
--- /dev/null
+++ b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm
@@ -0,0 +1,124 @@
+/// We won't use tentacles unless we have had the same target for this long
+#define MIN_TIME_TO_TENTACLE 3 SECONDS
+
+/datum/ai_controller/basic_controller/goliath
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_TARGET_MINIMUM_STAT = UNCONSCIOUS,
+ BB_AGGRO_RANGE = 5,
+ )
+
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/target_retaliate/check_faction,
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles,
+ /datum/ai_planning_subtree/attack_obstacle_in_path,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree/goliath,
+ /datum/ai_planning_subtree/find_food,
+ /datum/ai_planning_subtree/goliath_find_diggable_turf,
+ /datum/ai_planning_subtree/goliath_dig,
+ )
+
+/datum/ai_planning_subtree/basic_melee_attack_subtree/goliath
+ operational_datums = list(/datum/component/ai_target_timer)
+ melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/goliath
+
+/// Go for the tentacles if they're available
+/datum/ai_behavior/basic_melee_attack/goliath
+
+/datum/ai_behavior/basic_melee_attack/goliath/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key, health_ratio_key)
+ var/time_on_target = controller.blackboard[BB_BASIC_MOB_HAS_TARGET_TIME] || 0
+ if(time_on_target < MIN_TIME_TO_TENTACLE)
+ 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)))
+ var/datum/action/cooldown/using_action = controller.blackboard[BB_GOLIATH_TENTACLES]
+ if(using_action?.IsAvailable())
+ return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
+ return ..()
+
+/datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles
+ ability_key = BB_GOLIATH_TENTACLES
+ operational_datums = list(/datum/component/ai_target_timer)
+
+/datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles/select_behaviors(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)))
+ 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)
+ return // We need to spend some time acquiring our target first
+ return ..()
+
+/// If we got nothing better to do, find a turf we can search for tasty roots and such
+/datum/ai_planning_subtree/goliath_find_diggable_turf
+
+/datum/ai_planning_subtree/goliath_find_diggable_turf/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ controller.queue_behavior(/datum/ai_behavior/goliath_find_diggable_turf)
+
+/datum/ai_behavior/goliath_find_diggable_turf
+ action_cooldown = 2 SECONDS
+ /// Where do we store the target data
+ var/target_key = BB_GOLIATH_HOLE_TARGET
+ /// How far do we look for turfs?
+ var/scan_range = 3
+
+/datum/ai_behavior/goliath_find_diggable_turf/perform(seconds_per_tick, datum/ai_controller/controller)
+ var/turf/target_turf = controller.blackboard[target_key]
+ if(is_valid_turf(target_turf))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ var/mob/living/pawn = controller.pawn
+ var/list/nearby_turfs = RANGE_TURFS(scan_range, pawn)
+ var/turf/check_turf = pick(nearby_turfs) // This isn't an efficient search algorithm but we don't need it to be
+ if(!is_valid_turf(check_turf))
+ // Otherwise they won't perform idle wanderin
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+ controller.set_blackboard_key(target_key, check_turf)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+
+/// Return true if this is a turf we can dig
+/datum/ai_behavior/goliath_find_diggable_turf/proc/is_valid_turf(turf/check_turf)
+ var/turf/simulated/floor/plating/asteroid/asteroid_floor = check_turf
+ if(!istype(asteroid_floor))
+ return FALSE
+ return !asteroid_floor.dug
+
+/datum/ai_planning_subtree/goliath_dig
+ /// Where did we store the target data
+ var/target_key = BB_GOLIATH_HOLE_TARGET
+
+/datum/ai_planning_subtree/goliath_dig/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(!controller.blackboard_key_exists(target_key))
+ return
+ controller.queue_behavior(/datum/ai_behavior/goliath_dig, target_key)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+/// If we got nothing better to do, dig a little hole
+/datum/ai_behavior/goliath_dig
+ action_cooldown = 3 MINUTES
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+
+/datum/ai_behavior/goliath_dig/setup(datum/ai_controller/controller, target_key)
+ . = ..()
+ var/turf/target_turf = controller.blackboard[target_key]
+ if(QDELETED(target_turf))
+ return
+ set_movement_target(controller, target_turf)
+
+/datum/ai_behavior/goliath_dig/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+ var/turf/target_turf = controller.blackboard[target_key]
+ var/mob/living/basic/basic_mob = controller.pawn
+ if(!basic_mob.can_reach(target_turf))
+ return AI_BEHAVIOR_DELAY
+ basic_mob.melee_attack(target_turf)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+
+/datum/ai_behavior/goliath_dig/finish_action(datum/ai_controller/controller, succeeded, target_key)
+ . = ..()
+ controller.clear_blackboard_key(target_key)
+
+#undef MIN_TIME_TO_TENTACLE
diff --git a/code/modules/mob/living/basic/lavaland/goliath/tentacle.dm b/code/modules/mob/living/basic/lavaland/goliath/tentacle.dm
new file mode 100644
index 00000000000..69026831e83
--- /dev/null
+++ b/code/modules/mob/living/basic/lavaland/goliath/tentacle.dm
@@ -0,0 +1,100 @@
+/obj/effect/temp_visual/goliath_tentacle
+ name = "goliath tentacle"
+ icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
+ icon_state = "Goliath_tentacle_spawn"
+ layer = BELOW_MOB_LAYER
+ var/mob/living/spawner
+
+/obj/effect/temp_visual/goliath_tentacle/Initialize(mapload, mob/living/new_spawner)
+ . = ..()
+ for(var/obj/effect/temp_visual/goliath_tentacle/T in loc)
+ if(T != src)
+ return INITIALIZE_HINT_QDEL
+ if(!QDELETED(new_spawner))
+ spawner = new_spawner
+ if(ismineralturf(loc))
+ var/turf/simulated/mineral/M = loc
+ M.gets_drilled()
+ deltimer(timerid)
+ timerid = addtimer(CALLBACK(src, PROC_REF(tripanim)), 7, TIMER_STOPPABLE)
+
+/obj/effect/temp_visual/goliath_tentacle/original/Initialize(mapload, new_spawner)
+ . = ..()
+ var/list/directions = GLOB.cardinal.Copy()
+ for(var/i in 1 to 3)
+ var/spawndir = pick_n_take(directions)
+ var/turf/T = get_step(src, spawndir)
+ if(T)
+ new /obj/effect/temp_visual/goliath_tentacle(T, spawner)
+
+/obj/effect/temp_visual/goliath_tentacle/proc/tripanim()
+ icon_state = "Goliath_tentacle_wiggle"
+ deltimer(timerid)
+ timerid = addtimer(CALLBACK(src, PROC_REF(trip)), 3, TIMER_STOPPABLE)
+
+/obj/effect/temp_visual/goliath_tentacle/proc/trip()
+ var/latched = FALSE
+ for(var/mob/living/L in loc)
+ if((!QDELETED(spawner) && spawner.faction_check_mob(L)) || L.stat == DEAD)
+ continue
+ visible_message("[src] grabs hold of [L]!")
+ L.Stun(10 SECONDS)
+ L.adjustBruteLoss(rand(10,15))
+ latched = TRUE
+ if(!latched)
+ retract()
+ else
+ deltimer(timerid)
+ timerid = addtimer(CALLBACK(src, PROC_REF(retract)), 10, TIMER_STOPPABLE)
+
+/obj/effect/temp_visual/goliath_tentacle/proc/retract()
+ icon_state = "Goliath_tentacle_retract"
+ deltimer(timerid)
+ timerid = QDEL_IN(src, 7)
+
+/// 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
+ var/obj/effect/temp_visual/goliath_tentacle/tentacle
+
+/datum/status_effect/incapacitating/stun/goliath_tentacled/on_creation(mob/living/new_owner, set_duration, obj/effect/temp_visual/goliath_tentacle/tentacle)
+ . = ..()
+ if(!.)
+ return
+ src.tentacle = tentacle
+
+/datum/status_effect/incapacitating/stun/goliath_tentacled/on_apply()
+ . = ..()
+ RegisterSignal(owner, COMSIG_CARBON_PRE_MISC_HELP, PROC_REF(on_helped))
+ RegisterSignals(owner, SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE), PROC_REF(release))
+ RegisterSignals(tentacle, list(COMSIG_PARENT_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING), PROC_REF(on_tentacle_left))
+
+/datum/status_effect/incapacitating/stun/goliath_tentacled/on_remove()
+ . = ..()
+ UnregisterSignal(owner, list(COMSIG_CARBON_PRE_MISC_HELP, SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE)))
+ if(isnull(tentacle))
+ return
+ UnregisterSignal(tentacle, list(COMSIG_PARENT_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING))
+ 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 // COMSIG_CARBON_PRE_MISC_HELP
+ release()
+ source.visible_message("[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 // SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE)
+ 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 // COMSIG_PARENT_QDELETING + COMSIG_GOLIATH_TENTACLE_RETRACTING
+ UnregisterSignal(tentacle, list(COMSIG_PARENT_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING)) // No endless loops for us please
+ tentacle = null
+ release()
diff --git a/code/modules/mob/living/basic/mining/basilisk.dm b/code/modules/mob/living/basic/mining/basilisk.dm
index 05b8ad051d6..fb51260d6bb 100644
--- a/code/modules/mob/living/basic/mining/basilisk.dm
+++ b/code/modules/mob/living/basic/mining/basilisk.dm
@@ -130,6 +130,3 @@
var/mob/living/L = target
if(istype(L))
L.apply_status_effect(/datum/status_effect/freon/watcher)
-
-/mob/living/basic/mining/basilisk/watcher/tendril
- from_tendril = TRUE
diff --git a/code/modules/mob/living/basic/mining/hivelord.dm b/code/modules/mob/living/basic/mining/hivelord.dm
index 8d4ee88f9f5..4a3724bde02 100644
--- a/code/modules/mob/living/basic/mining/hivelord.dm
+++ b/code/modules/mob/living/basic/mining/hivelord.dm
@@ -183,9 +183,6 @@
crusher_drop_mod = 20
dwarf_mob = TRUE
-/mob/living/basic/mining/hivelord/legion/tendril
- from_tendril = TRUE
-
/mob/living/basic/mining/hivelord/legion/death(gibbed)
visible_message("The skulls on [src] wail in anger as they flee from their dying host!")
var/turf/T = get_turf(src)
@@ -193,7 +190,7 @@
if(stored_mob)
stored_mob.forceMove(get_turf(src))
stored_mob = null
- else if(from_tendril)
+ else if(HAS_TRAIT(src, TRAIT_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)
@@ -256,9 +253,6 @@
ai_controller = /datum/ai_controller/basic_controller/hivelord_brood/advanced_legion
can_infest_dead = TRUE
-/mob/living/basic/mining/hivelord/legion/advanced/tendril
- from_tendril = TRUE
-
// Big legion (billy)
/mob/living/basic/mining/big_legion
name = "big legion"
diff --git a/code/modules/mob/living/basic/mining/mining_mobs.dm b/code/modules/mob/living/basic/mining/mining_mobs.dm
index aa585c70222..efa770edc16 100644
--- a/code/modules/mob/living/basic/mining/mining_mobs.dm
+++ b/code/modules/mob/living/basic/mining/mining_mobs.dm
@@ -20,8 +20,6 @@
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
diff --git a/code/modules/mob/living/carbon/carbon_procs.dm b/code/modules/mob/living/carbon/carbon_procs.dm
index 04cdfde56c7..74cfb363469 100644
--- a/code/modules/mob/living/carbon/carbon_procs.dm
+++ b/code/modules/mob/living/carbon/carbon_procs.dm
@@ -247,6 +247,8 @@
/mob/living/carbon/proc/help_shake_act(mob/living/carbon/M)
if(health < HEALTH_THRESHOLD_CRIT)
return
+ if(SEND_SIGNAL(src, COMSIG_CARBON_PRE_MISC_HELP, M) & COMPONENT_BLOCK_MISC_HELP)
+ return
if(src == M && ishuman(src))
check_self_for_injuries()
return
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index 08e43832e26..491e6dcdab8 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -144,9 +144,11 @@ Difficulty: Medium
else
var/mob/living/basic/mining/hivelord/legion/A
if(enraged)
- A = new /mob/living/basic/mining/hivelord/legion/advanced/tendril(loc)
+ A = new /mob/living/basic/mining/hivelord/legion/advanced(loc)
+ ADD_TRAIT(A, TRAIT_FROM_TENDRIL, INNATE_TRAIT)
else
- A = new /mob/living/basic/mining/hivelord/legion/tendril(loc)
+ A = new /mob/living/basic/mining/hivelord/legion(loc)
+ ADD_TRAIT(A, TRAIT_FROM_TENDRIL, INNATE_TRAIT)
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
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm
deleted file mode 100644
index dfd0795b891..00000000000
--- a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm
+++ /dev/null
@@ -1,197 +0,0 @@
-//A slow but strong beast that tries to stun using its tentacles
-/mob/living/simple_animal/hostile/asteroid/goliath
- name = "goliath"
- desc = "A massive beast that uses long tentacles to ensnare its prey, threatening them is not advised under any conditions."
- icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
- icon_state = "Goliath"
- icon_living = "Goliath"
- icon_aggro = "Goliath_alert"
- icon_dead = "Goliath_dead"
- icon_gib = "syndicate_gib"
- mob_biotypes = MOB_ORGANIC | MOB_BEAST
- move_to_delay = 40
- ranged = TRUE
- ranged_cooldown_time = 120
- friendly = "wails at"
- speak_emote = list("bellows")
- speed = 3
- maxHealth = 300
- health = 300
- harm_intent_damage = 1 //Only the manliest of men can kill a Goliath with only their fists.
- obj_damage = 100
- melee_damage_lower = 25
- melee_damage_upper = 25
- attacktext = "pulverizes"
- attack_sound = 'sound/weapons/punch1.ogg'
- throw_message = "does nothing to the rocky hide of the"
- vision_range = 5
- move_force = MOVE_FORCE_VERY_STRONG
- move_resist = MOVE_FORCE_VERY_STRONG
- pull_force = MOVE_FORCE_VERY_STRONG
- var/pre_attack = FALSE
- var/pre_attack_icon = "Goliath_preattack"
- loot = list(/obj/item/stack/sheet/animalhide/goliath_hide)
- footstep_type = FOOTSTEP_MOB_HEAVY
- contains_xeno_organ = TRUE
- surgery_container = /datum/xenobiology_surgery_container/goliath
-
-/mob/living/simple_animal/hostile/asteroid/goliath/Life()
- . = ..()
- handle_preattack()
-
-/mob/living/simple_animal/hostile/asteroid/goliath/proc/handle_preattack()
- if(ranged_cooldown <= world.time + ranged_cooldown_time * 0.25 && !pre_attack)
- pre_attack++
- if(!pre_attack || stat || AIStatus == AI_IDLE)
- return
- icon_state = pre_attack_icon
-
-/mob/living/simple_animal/hostile/asteroid/goliath/revive()
- ..()
- anchored = TRUE
-
-/mob/living/simple_animal/hostile/asteroid/goliath/death(gibbed)
- move_force = MOVE_FORCE_DEFAULT
- move_resist = MOVE_RESIST_DEFAULT
- pull_force = PULL_FORCE_DEFAULT
- ..(gibbed)
-
-/mob/living/simple_animal/hostile/asteroid/goliath/OpenFire()
- var/tturf = get_turf(target)
- if(!isturf(tturf))
- return
- if(get_dist(src, target) <= 7)//Screen range check, so you can't get tentacle'd offscreen
- visible_message("[src] digs its tentacles under [target]!")
- new /obj/effect/temp_visual/goliath_tentacle/original(tturf, src)
- ranged_cooldown = world.time + ranged_cooldown_time
- icon_state = icon_aggro
- pre_attack = FALSE
-
-/mob/living/simple_animal/hostile/asteroid/goliath/adjustHealth(amount, updating_health = TRUE)
- ranged_cooldown -= 10
- handle_preattack()
- . = ..()
-
-/mob/living/simple_animal/hostile/asteroid/goliath/Aggro()
- vision_range = aggro_vision_range
- handle_preattack()
- if(icon_state != icon_aggro)
- icon_state = icon_aggro
-
-//Lavaland Goliath
-/mob/living/simple_animal/hostile/asteroid/goliath/beast
- desc = "A hulking, armor-plated beast with long tendrils arching from its back."
- icon_state = "goliath"
- icon_living = "goliath"
- icon_aggro = "goliath"
- icon_dead = "goliath_dead"
- throw_message = "does nothing to the tough hide of the"
- pre_attack_icon = "goliath2"
- crusher_loot = /obj/item/crusher_trophy/goliath_tentacle
- butcher_results = list(/obj/item/food/monstermeat/goliath = 2, /obj/item/stack/sheet/animalhide/goliath_hide = 1, /obj/item/stack/sheet/bone = 2)
- loot = list()
- stat_attack = UNCONSCIOUS
- robust_searching = TRUE
-
-
-/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient
- name = "ancient 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."
- icon_state = "Goliath"
- icon_living = "Goliath"
- icon_aggro = "Goliath_alert"
- icon_dead = "Goliath_dead"
- maxHealth = 400
- health = 400
- speed = 4
- pre_attack_icon = "Goliath_preattack"
- throw_message = "does nothing to the rocky hide of the"
- loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) //A throwback to the asteroid days
- butcher_results = list(/obj/item/food/monstermeat/goliath= 2, /obj/item/stack/sheet/bone = 2)
- crusher_loot = /obj/item/crusher_trophy/goliath_tentacle/ancient
- crusher_drop_mod = 100 //These things are rare (1/100 per spawner). You shouldn't have to hope for another stroke of luck to get it's trophy after finding it
- wander = FALSE
- var/list/cached_tentacle_turfs
- var/turf/last_location
- var/tentacle_recheck_cooldown = 100
-
-/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient/Life()
- . = ..()
- if(!.) // dead
- return
- if(target && isturf(loc))
- if(!LAZYLEN(cached_tentacle_turfs) || loc != last_location || tentacle_recheck_cooldown <= world.time)
- LAZYCLEARLIST(cached_tentacle_turfs)
- last_location = loc
- tentacle_recheck_cooldown = world.time + initial(tentacle_recheck_cooldown)
- for(var/turf/simulated/floor/T in orange(4, loc))
- LAZYADD(cached_tentacle_turfs, T)
- for(var/t in cached_tentacle_turfs)
- if(isfloorturf(t))
- if(prob(10))
- new /obj/effect/temp_visual/goliath_tentacle(t, src)
- else
- cached_tentacle_turfs -= t
-
-/mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril
- fromtendril = TRUE
-
-//Tentacles
-/obj/effect/temp_visual/goliath_tentacle
- name = "goliath tentacle"
- icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
- icon_state = "Goliath_tentacle_spawn"
- layer = BELOW_MOB_LAYER
- var/mob/living/spawner
-
-/obj/effect/temp_visual/goliath_tentacle/Initialize(mapload, mob/living/new_spawner)
- . = ..()
- for(var/obj/effect/temp_visual/goliath_tentacle/T in loc)
- if(T != src)
- return INITIALIZE_HINT_QDEL
- if(!QDELETED(new_spawner))
- spawner = new_spawner
- if(ismineralturf(loc))
- var/turf/simulated/mineral/M = loc
- M.gets_drilled()
- deltimer(timerid)
- timerid = addtimer(CALLBACK(src, PROC_REF(tripanim)), 7, TIMER_STOPPABLE)
-
-/obj/effect/temp_visual/goliath_tentacle/original/Initialize(mapload, new_spawner)
- . = ..()
- var/list/directions = GLOB.cardinal.Copy()
- for(var/i in 1 to 3)
- var/spawndir = pick_n_take(directions)
- var/turf/T = get_step(src, spawndir)
- if(T)
- new /obj/effect/temp_visual/goliath_tentacle(T, spawner)
-
-/obj/effect/temp_visual/goliath_tentacle/proc/tripanim()
- icon_state = "Goliath_tentacle_wiggle"
- deltimer(timerid)
- timerid = addtimer(CALLBACK(src, PROC_REF(trip)), 3, TIMER_STOPPABLE)
-
-/obj/effect/temp_visual/goliath_tentacle/proc/trip()
- var/latched = FALSE
- for(var/mob/living/L in loc)
- if((!QDELETED(spawner) && spawner.faction_check_mob(L)) || L.stat == DEAD)
- continue
- visible_message("[src] grabs hold of [L]!")
- L.Stun(10 SECONDS)
- L.adjustBruteLoss(rand(10,15))
- latched = TRUE
- if(!latched)
- retract()
- else
- deltimer(timerid)
- timerid = addtimer(CALLBACK(src, PROC_REF(retract)), 10, TIMER_STOPPABLE)
-
-/obj/effect/temp_visual/goliath_tentacle/proc/retract()
- icon_state = "Goliath_tentacle_retract"
- deltimer(timerid)
- timerid = QDEL_IN(src, 7)
-
-/mob/living/simple_animal/hostile/asteroid/goliath/space
-
-/mob/living/simple_animal/hostile/asteroid/goliath/space/Process_Spacemove(movement_dir, continuous_move)
- return TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm
index 3e447222baf..4ba267e3448 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm
@@ -14,7 +14,6 @@
a_intent = INTENT_HARM
var/crusher_loot
var/throw_message = "bounces off of"
- var/fromtendril = FALSE
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
mob_size = MOB_SIZE_LARGE
diff --git a/code/modules/procedural_mapping/mapGenerators/asteroid.dm b/code/modules/procedural_mapping/mapGenerators/asteroid.dm
index d8ddacf542e..4aea5157724 100644
--- a/code/modules/procedural_mapping/mapGenerators/asteroid.dm
+++ b/code/modules/procedural_mapping/mapGenerators/asteroid.dm
@@ -19,10 +19,11 @@
//Monsters
/datum/map_generator_module/splatter_layer/asteroid_monsters
spawnableTurfs = list()
- spawnableAtoms = list(/mob/living/basic/mining/basilisk = 10, \
- /mob/living/basic/mining/hivelord = 10, \
- /mob/living/simple_animal/hostile/asteroid/goliath = 10)
-
+ spawnableAtoms = list(
+ /mob/living/basic/mining/basilisk = 10,
+ /mob/living/basic/mining/hivelord = 10,
+ /mob/living/basic/mining/goliath/space = 10,
+ )
// GENERATORS
diff --git a/paradise.dme b/paradise.dme
index 572bb04357f..7fab737b109 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -526,6 +526,7 @@
#include "code\datums\cache\powermonitor.dm"
#include "code\datums\components\_component.dm"
#include "code\datums\components\action_item_overlay.dm"
+#include "code\datums\components\ai_has_target_timer.dm"
#include "code\datums\components\ai_retaliate_advanced.dm"
#include "code\datums\components\anti_magic.dm"
#include "code\datums\components\basic_mob_aggro_emote.dm"
@@ -2515,6 +2516,10 @@
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider_ai.dm"
#include "code\modules\mob\living\basic\hostile\gorilla\gorilla.dm"
#include "code\modules\mob\living\basic\hostile\gorilla\gorilla_emote.dm"
+#include "code\modules\mob\living\basic\lavaland\goliath\goliath.dm"
+#include "code\modules\mob\living\basic\lavaland\goliath\goliath_actions.dm"
+#include "code\modules\mob\living\basic\lavaland\goliath\goliath_ai.dm"
+#include "code\modules\mob\living\basic\lavaland\goliath\tentacle.dm"
#include "code\modules\mob\living\basic\minebots\minebot.dm"
#include "code\modules\mob\living\basic\minebots\minebot_abilities.dm"
#include "code\modules\mob\living\basic\minebots\minebot_ai.dm"
@@ -2737,7 +2742,6 @@
#include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\megafauna.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\abandoned_minebot.dm"
-#include "code\modules\mob\living\simple_animal\hostile\mining\goliath.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\mining.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\elites\elite.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\elites\goliath_broodmother.dm"
diff --git a/tools/ci/check_simplemob_additions.py b/tools/ci/check_simplemob_additions.py
index 106fd087029..e6b9c27e54a 100644
--- a/tools/ci/check_simplemob_additions.py
+++ b/tools/ci/check_simplemob_additions.py
@@ -67,11 +67,6 @@ BURNDOWN_LIST = {
"/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead",
"/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead/xenobiology",
"/mob/living/simple_animal/hostile/asteroid/elite/pandora",
- "/mob/living/simple_animal/hostile/asteroid/goliath",
- "/mob/living/simple_animal/hostile/asteroid/goliath/beast",
- "/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient",
- "/mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril",
- "/mob/living/simple_animal/hostile/asteroid/goliath/space",
"/mob/living/simple_animal/hostile/clockwork_construct",
"/mob/living/simple_animal/hostile/clockwork_construct/clockwork_marauder",
"/mob/living/simple_animal/hostile/clockwork_construct/clockwork_marauder/hostile",