mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-01-25 17:02:04 +00:00
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:
@@ -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
|
||||
|
||||
147
code/modules/mob/living/basic/lavaland/goliath/goliath.dm
Normal file
147
code/modules/mob/living/basic/lavaland/goliath/goliath.dm
Normal file
@@ -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
|
||||
124
code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm
Normal file
124
code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm
Normal file
@@ -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
|
||||
100
code/modules/mob/living/basic/lavaland/goliath/tentacle.dm
Normal file
100
code/modules/mob/living/basic/lavaland/goliath/tentacle.dm
Normal file
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user