Port goliaths to basic mobs, Round 2 (#30566)

* Port goliaths to basic mobs.

* linting whitespace

* more linting

* wtf

* Addresses reviews on prior PR, adjusts behavior for food searching

* Missing Comma

* Goliath aggro range

* Reduces goliath speed to 5 from 3

---------

Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Co-authored-by: warriorstar-orion <orion@snowfrost.garden>
This commit is contained in:
PollardTheDragon
2025-10-21 15:27:27 -04:00
committed by GitHub
parent 28f7414c64
commit dc3f8f6cc9
26 changed files with 541 additions and 235 deletions
@@ -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" = (
+8
View File
@@ -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"
+10
View File
@@ -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"
+2
View File
@@ -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"
+2
View File
@@ -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
),
@@ -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()
+1
View File
@@ -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("<span class='danger'>[L] [spawn_text] [P].</span>")
P.on_mob_spawn(L)
return L
+2 -2
View File
@@ -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
@@ -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)
@@ -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"
+4 -1
View File
@@ -71,7 +71,10 @@
visible_message("<span class='danger'>\A [spawned_mob.name] crawls out of \the [name]!</span>")
/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)
+1 -1
View File
@@ -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
@@ -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
@@ -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("<span class='warning'>[owner] digs its tentacles under [target]!</span>")
StartCooldown()
return TRUE
@@ -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
@@ -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("<span class='danger'>[src] grabs hold of [L]!</span>")
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("<span class='notice'>[helping] rips [source] from the tentacle's grasp!</span>")
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()
@@ -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
@@ -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("<span class='warning'>The skulls on [src] wail in anger as they flee from their dying host!</span>")
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"
@@ -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
@@ -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
@@ -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
@@ -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("<span class='warning'>[src] digs its tentacles under [target]!</span>")
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("<span class='danger'>[src] grabs hold of [L]!</span>")
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
@@ -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
@@ -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
+5 -1
View File
@@ -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"
-5
View File
@@ -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",