Goliath basic mob (#76754)

## About The Pull Request

Converts Goliaths to the basic mob framework and gives them some new
moves because I can't leave things well enough alone.
I am planning on touching all the lavaland fauna and then maybe even the
icebox ones if I haven't got bored. The Golaith is the first because it
is iconic.

https://www.youtube.com/watch?v=JNcKvMwT4-Q
Here's me getting killed by one as a demonstration. Despite my poor
performance I would contend that they aren't a _lot_ more dangerous, but
they are a little more dangerous.

The chief difference here is that they have two new attacks which they
will only use in response to being attacked.
If fired at from range, they will target the attacker with a line of
tentacles (it doesn't track you, so is easily sidestepped).
If attacked in melee, they will surround _themselves_ with tentacles, on
a longer cooldown.

Something else you may notice in this video: I discovered that basic
mobs are actually _too smart_ to be Lavaland fauna.
Typically (unlike their old form) a mob on our new AI system is smart
enough to attack someone _the moment they come into range_ rather than
only checking on predictable ticks, which would make using the Crusher
an essentially unviable prospect.
To counteract this, Goliaths now have a delayed attack component which
gives you a visual warning and short duration to get out of range before
they swing at you. I will probably put this on all mining fauna that get
reworked, it wouldn't be a terrible thing to put on other mobs to be
honest.

Other changes: The goliath stun is now a status effect with _buckles_
you to the tentacle as if grabbed, as well as its previous effects.
While this seems purely worse, any nearby helpers can now help-click on
you to instantly remove the debuff.
Experiencing the effect of a Lobstrosity Rush Gland makes you immune to
being grabbed by tentacles and an implanted one will automatically
trigger and free you if you are hit, and the explosive effect of
Brimdust also causes the tentacle to retract (although you'd need to
take damage for this to happen). Using the tools of the land, you can
make these creatures less threatening.

The ability for a Goliath to chain-apply the ability has now also been
reduced, it won't refresh its duration if you are hit when already
buckled.

When not occupied hounding miners, Goliaths will intermittently dig up
the asteroid sand and eat any worms that this produces.
I also made some new sprites for riding a Goliath because they've been
broken since the Lavaland mob update and also kind of were ugly before
then anyway:

![image](https://github.com/tgstation/tgstation/assets/7483112/90580403-d82f-4c29-b3e1-6c462e01edda)

Other code changes:
- I made an element which only lets an attached object move every x
seconds. This is because Goliaths are far too slow to use the speed
system (the glide just looks bugged as hell) but one thing I am invested
in when converting these is to make sure that they share the same
behaviour when player or AI controlled. This is disabled while you're
riding them because it was interminably slow.
- The Goliath tentacle trail uses a supertype object now shared with the
Meteor Heart which did something kind of similar.

## Why It's Good For The Game

It begins the process of moving one of our larger subsets of NPCs onto
the newer framework for NPC behaviour.
It adds a little bit more life to an iconic but slightly uninteresting
foe which mostly just walked at you slowly.
This PR contains a few components I expect to apply more widely to other
mobs in the future.

## Changelog

🆑
refactor: Goliaths now use the Basic Mob framework, please report any
unusual behaviour.
add: Goliaths learned a couple of new attacks which they will use in
self-defence.
balance: Help-clicking a miner grabbed by Goliath tentacles will
immediately free them, as will the effect of several items you can
scavenge from around Lavaland.
image: New sprites for the Goliath saddle.
/🆑
This commit is contained in:
Jacquerel
2023-07-12 18:52:02 -06:00
committed by GitHub
parent e696352def
commit 16cecf864d
48 changed files with 929 additions and 337 deletions
@@ -496,7 +496,7 @@
/turf/open/floor/carpet/blue/lavaland,
/area/ruin/unpowered)
"Fg" = (
/mob/living/simple_animal/hostile/asteroid/goliath/beast,
/mob/living/basic/mining/goliath,
/obj/effect/decal/cleanable/blood/old,
/turf/open/misc/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
+1 -1
View File
@@ -4376,7 +4376,7 @@
/turf/open/floor/iron/smooth_edge,
/area/mine/laborcamp/quarters)
"zS" = (
/mob/living/simple_animal/hostile/asteroid/goliath/beast,
/mob/living/basic/mining/goliath,
/turf/open/misc/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"zU" = (
+1 -1
View File
@@ -22,7 +22,7 @@
/turf/open/misc/asteroid,
/area/shuttle/escape/meteor)
"f" = (
/mob/living/simple_animal/hostile/asteroid/goliath,
/mob/living/basic/mining/goliath/ancient,
/turf/open/misc/asteroid,
/area/shuttle/escape/meteor)
"g" = (
+5
View File
@@ -309,3 +309,8 @@
//Festivus AI keys
/// Key where we store the charging apc ability
#define BB_FESTIVE_APC "BB_festive_apc"
/// 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"
+11
View File
@@ -1,7 +1,18 @@
#define BASIC_MOB_MAX_STAMINALOSS 200
///Basic mob flags
/// Delete mob upon death
#define DEL_ON_DEATH (1<<0)
/// Rotate mob 180 degrees while it is dead
#define FLIP_ON_DEATH (1<<1)
/// Mob remains dense while dead
#define REMAIN_DENSE_WHILE_DEAD (1<<2)
/// Mob can be set on fire
#define FLAMMABLE_MOB (1<<3)
/// Mob never takes damage from unarmed attacks
#define IMMUNE_TO_FISTS (1<<4)
/// Temporary trait applied when an attack forecast animation has completed
#define TRAIT_BASIC_ATTACK_FORECAST "trait_basic_attack_forecast"
#define INTERACTION_BASIC_ATTACK_FORCEAST "interaction_basic_attack_forecast"
@@ -0,0 +1,6 @@
/// Fired by a mob which has been grabbed by a goliath
#define COMSIG_GOLIATH_TENTACLED_GRABBED "comsig_goliath_tentacle_grabbed"
/// Fired by a goliath tentacle which is returning to the earth
#define COMSIG_GOLIATH_TENTACLE_RETRACTING "comsig_goliath_tentacle_retracting"
/// Fired by a mob which has triggered a brimdust explosion from itself (not the mobs that get hit)
#define COMSIG_BRIMDUST_EXPLOSION "comsig_brimdust_explosion"
+3
View File
@@ -614,6 +614,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_VOIDSTORM_IMMUNE "voidstorm_immune"
#define TRAIT_WEATHER_IMMUNE "weather_immune" //Immune to ALL weather effects.
/// Cannot be grabbed by goliath tentacles
#define TRAIT_TENTACLE_IMMUNE "tentacle_immune"
//non-mob traits
/// Used for limb-based paralysis, where replacing the limb will fix it.
#define TRAIT_PARALYSIS "paralysis"
-2
View File
@@ -50,8 +50,6 @@ multiple modular subtrees with behaviors
// Movement related things here
///Reference to the movement datum we use. Is a type on initialize but becomes a ref afterwards.
var/datum/ai_movement/ai_movement = /datum/ai_movement/dumb
///Cooldown until next movement
COOLDOWN_DECLARE(movement_cooldown)
///Delay between movements. This is on the controller so we can keep the movement datum singleton
var/movement_delay = 0.1 SECONDS
+3
View File
@@ -8,6 +8,9 @@
/datum/ai_behavior/find_and_set/perform(seconds_per_tick, datum/ai_controller/controller, set_key, locate_path, search_range)
. = ..()
var/atom/current_target = controller.blackboard[set_key]
if (!QDELETED(current_target))
return
var/find_this_thing = search_tactic(controller, locate_path, search_range)
if(find_this_thing)
controller.set_blackboard_key(set_key, find_this_thing)
@@ -0,0 +1,82 @@
/**
* Delays outgoing attacks which are directed at mobs to give players time to get out of the way
*/
/datum/component/basic_mob_attack_telegraph
/// Time to wait before attack can complete
var/telegraph_duration
/// Overlay which we display over targets
var/mutable_appearance/target_overlay
/// Our current target, if we have one
var/mob/living/current_target
/// Callback executed when we start aiming at something
var/datum/callback/on_began_forecast
/datum/component/basic_mob_attack_telegraph/Initialize(
telegraph_icon = 'icons/mob/telegraphing/telegraph.dmi',
telegraph_state = ATTACK_EFFECT_BITE,
telegraph_duration = 0.3 SECONDS,
datum/callback/on_began_forecast,
)
. = ..()
if (!isbasicmob(parent))
return ELEMENT_INCOMPATIBLE
target_overlay = mutable_appearance(telegraph_icon, telegraph_state)
src.telegraph_duration = telegraph_duration
src.on_began_forecast = on_began_forecast
/datum/component/basic_mob_attack_telegraph/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_attack))
/datum/component/basic_mob_attack_telegraph/UnregisterFromParent()
if (current_target)
forget_target(current_target)
QDEL_NULL(target_overlay)
REMOVE_TRAIT(parent, TRAIT_BASIC_ATTACK_FORECAST, REF(src))
UnregisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET)
return ..()
/// When we attempt to attack, check if it is allowed
/datum/component/basic_mob_attack_telegraph/proc/on_attack(mob/living/basic/source, atom/target)
SIGNAL_HANDLER
if (!isliving(target))
return
if (HAS_TRAIT_FROM(source, TRAIT_BASIC_ATTACK_FORECAST, REF(src)))
REMOVE_TRAIT(source, TRAIT_BASIC_ATTACK_FORECAST, REF(src))
return
if (!DOING_INTERACTION(source, INTERACTION_BASIC_ATTACK_FORCEAST))
INVOKE_ASYNC(src, PROC_REF(delayed_attack), source, target)
return COMPONENT_HOSTILE_NO_ATTACK
/// Perform an attack after a delay
/datum/component/basic_mob_attack_telegraph/proc/delayed_attack(mob/living/basic/source, mob/living/target)
current_target = target
target.add_overlay(target_overlay)
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(forget_target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(target_moved))
on_began_forecast?.Invoke(target)
if (!do_after(source, delay = telegraph_duration, target = source, interaction_key = INTERACTION_BASIC_ATTACK_FORCEAST))
forget_target(target)
return
if (!current_target) // They got out of the way :(
return
ADD_TRAIT(source, TRAIT_BASIC_ATTACK_FORECAST, REF(src))
source.melee_attack(current_target)
forget_target(current_target)
/// The guy we're trying to attack moved, is he still in range?
/datum/component/basic_mob_attack_telegraph/proc/target_moved(mob/living/target)
SIGNAL_HANDLER
if (in_range(parent, target))
return
forget_target(target)
/// The guy we're trying to attack isn't a valid target any more
/datum/component/basic_mob_attack_telegraph/proc/forget_target(atom/target)
SIGNAL_HANDLER
current_target = null
target.cut_overlay(target_overlay)
UnregisterSignal(target, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED))
+55
View File
@@ -0,0 +1,55 @@
/**
* Automatically triggers a linked ability at a target who attacks us.
* The ability might not necessarily be on our mob.
* Make sure that /datum/element/relay_attackers is also present or you'll never receive the triggering signal.
*/
/datum/component/revenge_ability
dupe_mode = COMPONENT_DUPE_ALLOWED
/// The ability to use when we are attacked
var/datum/action/cooldown/ability
/// Optional datum for validating targets
var/datum/targetting_datum/targetting
/// Trigger only if target is at least this far away
var/min_range
/// Trigger only if target is at least this close
var/max_range
/// Target the ability at ourself instead of at the offender
var/target_self
/datum/component/revenge_ability/Initialize(datum/action/cooldown/ability, datum/targetting_datum/targetting, min_range = 0, max_range = INFINITY, target_self = FALSE)
. = ..()
if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
src.ability = ability
src.targetting = targetting
src.min_range = min_range
src.max_range = max_range
src.target_self = target_self
RegisterSignal(ability, COMSIG_QDELETING, PROC_REF(ability_destroyed))
/datum/component/revenge_ability/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked))
/datum/component/revenge_ability/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_ATOM_WAS_ATTACKED)
if (ability)
UnregisterSignal(ability, COMSIG_QDELETING)
return ..()
/// If we were attacked, get revenge
/datum/component/revenge_ability/proc/on_attacked(mob/living/victim, atom/attacker)
SIGNAL_HANDLER
var/atom/ability_user = ability.owner
var/distance = get_dist(ability_user, attacker)
if (distance < min_range || distance > max_range)
return
if (targetting && !targetting.can_attack(victim, attacker))
return
INVOKE_ASYNC(ability, TYPE_PROC_REF(/datum/action/cooldown, InterceptClickOn), ability_user, null, (target_self) ? ability_user : attacker)
/// For whatever reason we lost our linked ability so we can drop this behaviour
/datum/component/revenge_ability/proc/ability_destroyed(datum/source)
SIGNAL_HANDLER
qdel(src)
+5
View File
@@ -200,6 +200,11 @@
return FALSE
riding_offsets["[index]"] = offsets
/datum/component/riding/proc/set_vehicle_offsets(list/offsets)
if(!islist(offsets))
return FALSE
directional_vehicle_offsets = offsets
/**
* This proc is used to see if we have the appropriate key to drive this atom, if such a key is needed. Returns FALSE if we don't have what we need to drive.
*
+14 -2
View File
@@ -378,12 +378,24 @@
/datum/component/riding/creature/goliath
keytype = /obj/item/key/lasso
vehicle_move_delay = 4
/datum/component/riding/creature/goliath/Initialize(mob/living/riding_mob, force, ride_check_flags, potion_boost)
. = ..()
var/mob/living/basic/mining/goliath/goliath = parent
goliath.RemoveElement(/datum/element/move_cooldown, move_delay = goliath.movement_delay)
/datum/component/riding/creature/goliath/Destroy(force, silent)
var/mob/living/basic/mining/goliath/goliath = parent
goliath.AddElement(/datum/element/move_cooldown, move_delay = goliath.movement_delay)
return ..()
/datum/component/riding/creature/goliath/handle_specials()
. = ..()
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 8), TEXT_SOUTH = list(0, 8), TEXT_EAST = list(-2, 8), TEXT_WEST = list(2, 8)))
set_vehicle_offsets(list(TEXT_NORTH = list(-12, 0), TEXT_SOUTH = list(-12, 0), TEXT_EAST = list(-12, 0), TEXT_WEST = list(-12, 0)))
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 12), TEXT_SOUTH = list(0, 12), TEXT_EAST = list(-4, 12), TEXT_WEST = list(3, 12)))
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(NORTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(EAST, OBJ_LAYER)
set_vehicle_dir_layer(WEST, OBJ_LAYER)
+1 -1
View File
@@ -65,7 +65,7 @@
/// Remove weakrefs to atoms which have been killed or deleted without us picking it up somehow
/datum/component/spawner/proc/validate_references()
for (var/datum/weakref/weak_thing as anything in spawned_things)
var/atom/previously_spawned = weak_thing.resolve()
var/atom/previously_spawned = weak_thing?.resolve()
if (!previously_spawned)
spawned_things -= weak_thing
continue
+43
View File
@@ -0,0 +1,43 @@
/**
* Allows something to move only every x interval
* For instances where they'd otherwise need to move so slowly that the glide would look stupid
* Does not differentiate between voluntary and involuntary movement so this is beneficial in some niche circumstances
*/
/datum/element/move_cooldown
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
/// Time to wait between moves
var/move_delay
/// List of refs to atoms vs when they can next move
var/list/next_move_cache = list()
/datum/element/move_cooldown/Attach(datum/target, move_delay = 1 SECONDS)
. = ..()
if (!ismovable(target))
return ELEMENT_INCOMPATIBLE
src.move_delay = move_delay
next_move_cache[REF(target)] = 0
RegisterSignal(target, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(tried_move))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
/datum/element/move_cooldown/Detach(datum/source)
UnregisterSignal(source, list(COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED))
next_move_cache -= REF(source)
return ..()
/// Called when something we're tracking tries to move, check if it's allowed
/datum/element/move_cooldown/proc/tried_move(atom/movable/source, new_loc)
SIGNAL_HANDLER
if (source.pulledby || source.throwing || !isturf(new_loc) || !isturf(source.loc))
return
if (world.time <= next_move_cache[REF(source)])
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
/// Called when we moved successfully, start the cooldown
/datum/element/move_cooldown/proc/on_moved(atom/movable/source, old_loc, direction, forced)
SIGNAL_HANDLER
if (forced || source.pulledby || source.throwing || !isturf(old_loc) || !isturf(source.loc))
return // Can't really eliminate involuntary movement but we'll try
if (!(source.movement_type & FLYING) && !source.has_gravity())
return // Skip newtonian movement if it's not under control
next_move_cache[REF(source)] = world.time + move_delay
+6 -1
View File
@@ -51,7 +51,12 @@
/datum/map_generator/cave_generator/New()
. = ..()
if(!weighted_mob_spawn_list)
weighted_mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goldgrub = 1, /mob/living/simple_animal/hostile/asteroid/goliath = 5, /mob/living/simple_animal/hostile/asteroid/basilisk = 4, /mob/living/simple_animal/hostile/asteroid/hivelord = 3)
weighted_mob_spawn_list = list(
/mob/living/basic/mining/goliath/ancient = 5,
/mob/living/simple_animal/hostile/asteroid/basilisk = 4,
/mob/living/simple_animal/hostile/asteroid/hivelord = 3,
/mob/living/simple_animal/hostile/asteroid/goldgrub = 1,
)
mob_spawn_list = expand_weights(weighted_mob_spawn_list)
mob_spawn_no_mega_list = expand_weights(weighted_mob_spawn_list - SPAWN_MEGAFAUNA)
if(!weighted_megafauna_spawn_list)
@@ -3,7 +3,7 @@
weighted_closed_turf_types = list(/turf/closed/mineral/random/volcanic = 1)
weighted_mob_spawn_list = list(
/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50,
/mob/living/basic/mining/goliath/random = 50,
/obj/structure/spawner/lavaland/goliath = 3,
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40,
/obj/structure/spawner/lavaland = 2,
+1 -1
View File
@@ -255,6 +255,7 @@
/mob/living/basic/cow/wisdom,
/mob/living/basic/giant_spider,
/mob/living/basic/giant_spider/hunter,
/mob/living/basic/mining/goliath,
/mob/living/basic/headslug,
/mob/living/basic/killer_tomato,
/mob/living/basic/lizard,
@@ -269,7 +270,6 @@
/mob/living/basic/stickman/dog,
/mob/living/simple_animal/crab,
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher,
/mob/living/simple_animal/hostile/asteroid/goliath/beast,
/mob/living/simple_animal/hostile/bear,
/mob/living/simple_animal/hostile/blob/blobbernaut/independent,
/mob/living/simple_animal/hostile/gorilla,
@@ -16,7 +16,7 @@
"a tendril hand can easily dig through basalt and obliterate hostile fauna, but your glove-wearing days are behind you...",
)
input_obj_or_mob = list(
/mob/living/simple_animal/hostile/asteroid/goliath,
/mob/living/basic/mining/goliath,
)
output_organs = list(
/obj/item/organ/internal/brain/goliath,
@@ -0,0 +1,46 @@
/// An invisible effect which chases a target, spawning spikes every so often.
/obj/effect/temp_visual/effect_trail
name = "effect trail"
desc = "An invisible effect, how did you examine this?"
icon = 'icons/mob/silicon/cameramob.dmi'
icon_state = "marker"
duration = 15 SECONDS
invisibility = INVISIBILITY_ABSTRACT
/// Typepath of our spawned effect
var/spawned_effect
/// How often do we spawn our other effect?
var/spawn_interval = 0.5 SECONDS
/// Speed at which we chase target
var/move_speed = 3
/// What are we chasing?
var/atom/target
/// Stop spawning if we have this many effects already
var/max_spawned = 20
/// Do we home in after we started moving?
var/homing = TRUE
/// Handles chasing the target
var/datum/move_loop/movement
/obj/effect/temp_visual/effect_trail/Initialize(mapload, atom/target)
. = ..()
if (!target)
return INITIALIZE_HINT_QDEL
AddElement(/datum/element/floor_loving)
AddComponent(/datum/component/spawner, spawn_types = list(spawned_effect), max_spawned = max_spawned, spawn_time = spawn_interval)
src.target = target
movement = SSmove_manager.move_towards(src, chasing = target, delay = move_speed, home = homing, timeout = duration, flags = MOVEMENT_LOOP_START_FAST)
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(on_target_invalid))
if (isliving(target))
RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(on_target_invalid))
/// Destroy ourselves if the target is no longer valid
/obj/effect/temp_visual/effect_trail/proc/on_target_invalid()
SIGNAL_HANDLER
target = null
qdel(src)
/obj/effect/temp_visual/effect_trail/Destroy()
QDEL_NULL(movement)
return ..()
@@ -178,7 +178,7 @@ GLOBAL_LIST_INIT(leather_recipes, list ( \
new/datum/stack_recipe("muzzle", /obj/item/clothing/mask/muzzle, 2, check_density = FALSE, category = CAT_ENTERTAINMENT), \
new/datum/stack_recipe("basketball", /obj/item/toy/basketball, 20, check_density = FALSE, category = CAT_ENTERTAINMENT), \
new/datum/stack_recipe("baseball", /obj/item/toy/beach_ball/baseball, 3, check_density = FALSE, category = CAT_ENTERTAINMENT), \
new/datum/stack_recipe("saddle", /obj/item/saddle, 5, check_density = FALSE, category = CAT_EQUIPMENT), \
new/datum/stack_recipe("saddle", /obj/item/goliath_saddle, 5, check_density = FALSE, category = CAT_EQUIPMENT), \
new/datum/stack_recipe("leather shoes", /obj/item/clothing/shoes/laceup, 2, check_density = FALSE, category = CAT_CLOTHING), \
new/datum/stack_recipe("cowboy boots", /obj/item/clothing/shoes/cowboy, 2, check_density = FALSE, category = CAT_CLOTHING), \
new/datum/stack_recipe("botany gloves", /obj/item/clothing/gloves/botanic_leather, 3, check_density = FALSE, category = CAT_CLOTHING), \
@@ -20,7 +20,7 @@
/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/simple_animal/hostile/asteroid/hivelord/legion/tendril)
+25 -4
View File
@@ -53,7 +53,18 @@
max_integrity = 200
max_mobs = 15
spawn_time = 15 SECONDS
mob_types = list(/mob/living/simple_animal/hostile/retaliate/clown, /mob/living/simple_animal/hostile/retaliate/clown/fleshclown, /mob/living/simple_animal/hostile/retaliate/clown/clownhulk, /mob/living/simple_animal/hostile/retaliate/clown/longface, /mob/living/simple_animal/hostile/retaliate/clown/clownhulk/chlown, /mob/living/simple_animal/hostile/retaliate/clown/clownhulk/honcmunculus, /mob/living/simple_animal/hostile/retaliate/clown/mutant/glutton, /mob/living/simple_animal/hostile/retaliate/clown/banana, /mob/living/simple_animal/hostile/retaliate/clown/honkling, /mob/living/simple_animal/hostile/retaliate/clown/lube)
mob_types = list(
/mob/living/simple_animal/hostile/retaliate/clown,
/mob/living/simple_animal/hostile/retaliate/clown/banana,
/mob/living/simple_animal/hostile/retaliate/clown/clownhulk,
/mob/living/simple_animal/hostile/retaliate/clown/clownhulk/chlown,
/mob/living/simple_animal/hostile/retaliate/clown/clownhulk/honcmunculus,
/mob/living/simple_animal/hostile/retaliate/clown/fleshclown,
/mob/living/simple_animal/hostile/retaliate/clown/mutant/glutton,
/mob/living/simple_animal/hostile/retaliate/clown/honkling,
/mob/living/simple_animal/hostile/retaliate/clown/longface,
/mob/living/simple_animal/hostile/retaliate/clown/lube,
)
spawn_text = "climbs out of"
faction = list(FACTION_CLOWN)
@@ -65,7 +76,13 @@
max_mobs = 3
icon = 'icons/mob/simple/lavaland/nest.dmi'
spawn_text = "crawls out of"
mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/hivelord, /mob/living/simple_animal/hostile/asteroid/basilisk, /mob/living/basic/wumborian_fugu)
mob_types = list(
/mob/living/basic/mining/goliath/ancient,
/mob/living/basic/wumborian_fugu,
/mob/living/simple_animal/hostile/asteroid/basilisk,
/mob/living/simple_animal/hostile/asteroid/goldgrub,
/mob/living/simple_animal/hostile/asteroid/hivelord,
)
faction = list(FACTION_MINING)
/obj/structure/spawner/mining/goldgrub
@@ -76,7 +93,7 @@
/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/ancient)
/obj/structure/spawner/mining/hivelord
name = "hivelord den"
@@ -102,7 +119,11 @@
max_mobs = 15
icon = 'icons/mob/simple/lavaland/nest.dmi'
spawn_text = "crawls through"
mob_types = list(/mob/living/basic/migo, /mob/living/basic/creature, /mob/living/basic/blankbody)
mob_types = list(
/mob/living/basic/blankbody,
/mob/living/basic/creature,
/mob/living/basic/migo,
)
faction = list(FACTION_NETHER)
/obj/structure/spawner/nether/Initialize(mapload)
@@ -171,6 +171,7 @@
var/armor = target.run_armor_check(attack_flag = BOMB)
target.apply_damage(damage_dealt, damagetype = BURN, blocked = armor, spread_damage = TRUE)
SEND_SIGNAL(owner, COMSIG_BRIMDUST_EXPLOSION)
add_stacks(-1)
/// Slowdown applied when you are detonated on the space station
@@ -21,6 +21,14 @@
if (owner.health <= HEALTH_DANGER_ZONE)
trigger_organ_action()
/obj/item/organ/internal/monster_core/rush_gland/on_insert(mob/living/carbon/organ_owner)
. = ..()
RegisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED, PROC_REF(trigger_organ_action))
/obj/item/organ/internal/monster_core/rush_gland/on_remove(mob/living/carbon/organ_owner, special)
. = ..()
UnregisterSignal(organ_owner, COMSIG_GOLIATH_TENTACLED_GRABBED)
/obj/item/organ/internal/monster_core/rush_gland/on_triggered_internal()
owner.apply_status_effect(/datum/status_effect/lobster_rush/extended)
@@ -44,14 +52,14 @@
. = ..()
RegisterSignal(owner, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_move))
RegisterSignal(owner, COMSIG_MOVABLE_BUMP, PROC_REF(on_bump))
ADD_TRAIT(owner, TRAIT_IGNORESLOWDOWN, TRAIT_STATUS_EFFECT(id))
owner.add_traits(list(TRAIT_IGNORESLOWDOWN, TRAIT_TENTACLE_IMMUNE), TRAIT_STATUS_EFFECT(id))
owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/lobster_rush)
to_chat(owner, span_notice("You feel your blood pumping!"))
/datum/status_effect/lobster_rush/on_remove()
. = ..()
UnregisterSignal(owner, list(COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_BUMP))
REMOVE_TRAIT(owner, TRAIT_IGNORESLOWDOWN, TRAIT_STATUS_EFFECT(id))
owner.remove_traits(list(TRAIT_IGNORESLOWDOWN, TRAIT_TENTACLE_IMMUNE), TRAIT_STATUS_EFFECT(id))
owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/lobster_rush)
to_chat(owner, span_notice("Your pulse returns to normal."))
@@ -38,7 +38,7 @@
to_chat(user, span_danger("You [response_harm_simple] [src]!"))
playsound(loc, attacked_sound, 25, TRUE, -1)
var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
var/damage = rand(active_arm.unarmed_damage_low, active_arm.unarmed_damage_high)
var/damage = (basic_mob_flags & IMMUNE_TO_FISTS) ? 0 : rand(active_arm.unarmed_damage_low, active_arm.unarmed_damage_high)
attack_threshold_check(damage)
log_combat(user, src, "attacked")
@@ -5,7 +5,6 @@
icon_state = "bileworm"
icon_living = "bileworm"
icon_dead = "bileworm_dead"
mob_size = MOB_SIZE_LARGE
mob_biotypes = MOB_BUG
maxHealth = 100
health = 100
@@ -0,0 +1,236 @@
/// Slow moving mob which attempts to immobilise its target
/mob/living/basic/mining/goliath
name = "goliath"
desc = "A hulking, armor-plated beast with long tendrils arching from its back."
icon = 'icons/mob/simple/lavaland/lavaland_monsters_wide.dmi'
icon_state = "goliath"
icon_living = "goliath"
icon_dead = "goliath_dead"
pixel_x = -12
base_pixel_x = -12
gender = MALE // Female ones are the bipedal elites
basic_mob_flags = IMMUNE_TO_FISTS
mob_biotypes = MOB_ORGANIC|MOB_BEAST
maxHealth = 300
health = 300
friendly_verb_continuous = "wails at"
friendly_verb_simple = "wail at"
speak_emote = list("bellows")
obj_damage = 100
melee_damage_lower = 25
melee_damage_upper = 25
attack_sound = 'sound/weapons/punch1.ogg'
attack_verb_continuous = "pulverizes"
attack_verb_simple = "pulverize"
throw_blocked_message = "does nothing to the tough hide of"
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
ai_controller = /datum/ai_controller/basic_controller/goliath
crusher_loot = /obj/item/crusher_trophy/goliath_tentacle
butcher_results = list(/obj/item/food/meat/slab/goliath = 2, /obj/item/stack/sheet/bone = 2)
guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/goliath_hide = 1)
/// Goliath can only take a step in intervals of this
var/movement_delay = 4 SECONDS
/// Icon state to use when tentacles are available
var/tentacle_warning_state = "goliath_preattack"
/// Can this kind of goliath be tamed?
var/tameable = TRUE
/// Has this particular goliath been tamed?
var/tamed = FALSE
/// Can someone ride us around like a horse?
var/saddled = FALSE
/// Slight cooldown to prevent double-dipping if we use both abilities at once
COOLDOWN_DECLARE(ability_animation_cooldown)
/// Our base tentacles ability
var/datum/action/cooldown/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, /obj/item/food/bait/worm)
/mob/living/basic/mining/goliath/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_TENTACLE_IMMUNE, INNATE_TRAIT)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY)
AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = goliath_foods)
AddElement(/datum/element/move_cooldown, move_delay = movement_delay)
AddComponent(/datum/component/basic_mob_attack_telegraph)
AddComponentFrom(INNATE_TRAIT, /datum/component/shovel_hands)
if (tameable)
AddComponent(\
/datum/component/tameable,\
food_types = list(/obj/item/food/grown/ash_flora),\
tame_chance = 10,\
bonus_tame_chance = 5,\
after_tame = CALLBACK(src, PROC_REF(tamed)),\
)
tentacles = new (src)
tentacles.Grant(src)
var/datum/action/cooldown/tentacle_burst/melee_tentacles = new (src)
melee_tentacles.Grant(src)
AddComponent(/datum/component/revenge_ability, melee_tentacles, targetting = ai_controller.blackboard[BB_TARGETTING_DATUM], max_range = 1, target_self = TRUE)
var/datum/action/cooldown/tentacle_grasp/ranged_tentacles = new (src)
ranged_tentacles.Grant(src)
AddComponent(/datum/component/revenge_ability, ranged_tentacles, targetting = ai_controller.blackboard[BB_TARGETTING_DATUM], min_range = 2, max_range = 9)
tentacles_ready()
RegisterSignal(src, COMSIG_MOB_ABILITY_FINISHED, PROC_REF(used_ability))
ai_controller.set_blackboard_key(BB_BASIC_FOODS, goliath_foods)
ai_controller.set_blackboard_key(BB_GOLIATH_TENTACLES, tentacles)
/mob/living/basic/mining/goliath/Destroy()
QDEL_NULL(tentacles)
return ..()
/mob/living/basic/mining/goliath/examine(mob/user)
. = ..()
if (saddled)
. += span_info("Someone appears to have attached a saddle to this one.")
/mob/living/basic/mining/goliath/revive(full_heal_flags, excess_healing, force_grab_ghost)
. = ..()
if (!.)
return
move_force = initial(move_force)
move_resist = initial(move_resist)
pull_force = initial(pull_force)
/mob/living/basic/mining/goliath/death(gibbed)
move_force = MOVE_FORCE_DEFAULT
move_resist = MOVE_RESIST_DEFAULT
pull_force = PULL_FORCE_DEFAULT
return ..()
// Goliaths can summon tentacles more frequently as they take damage, scary.
/mob/living/basic/mining/goliath/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, bare_wound_bonus, sharpness, attack_direction, attacking_item)
. = ..()
if (!.)
return
if (damage <= 0)
return
if (tentacles.cooldown_time > 1 SECONDS)
tentacles.cooldown_time -= 1 SECONDS
/mob/living/basic/mining/goliath/attackby(obj/item/attacking_item, mob/living/user, params)
if (!istype(attacking_item, /obj/item/goliath_saddle))
return ..()
if (!tameable)
balloon_alert(user, "doesn't fit!")
return
if (saddled)
balloon_alert(user, "already saddled!")
return
if (!tamed)
balloon_alert(user, "too rowdy!")
return
balloon_alert(user, "affixing saddle...")
if (!do_after(user, delay = 5.5 SECONDS, target = src))
return
balloon_alert(user, "ready to ride")
qdel(attacking_item)
saddled = TRUE
buckle_lying = 0
add_overlay("goliath_saddled")
AddElement(/datum/element/ridable, /datum/component/riding/creature/goliath)
/// When we use an ability, activate some kind of visual tell
/mob/living/basic/mining/goliath/proc/used_ability(mob/living/source, datum/action/cooldown/ability)
SIGNAL_HANDLER
if (stat == DEAD || ability.IsAvailable())
return // We died or the action failed for some reason like being out of range
if (istype(ability, /datum/action/cooldown/goliath_tentacles))
if (ability.cooldown_time <= 2 SECONDS)
return
icon_state = icon_living
addtimer(CALLBACK(src, PROC_REF(tentacles_ready)), ability.cooldown_time - 2 SECONDS, TIMER_DELETE_ME)
return
if (!COOLDOWN_FINISHED(src, ability_animation_cooldown))
return
COOLDOWN_START(src, ability_animation_cooldown, 2 SECONDS)
playsound(src, 'sound/magic/demon_attack1.ogg', vol = 50, vary = TRUE)
Shake(1, 0, 1.5 SECONDS)
/// Called slightly before tentacles ability comes off cooldown, as a warning
/mob/living/basic/mining/goliath/proc/tentacles_ready()
if (stat == DEAD)
return
icon_state = tentacle_warning_state
/// Get ready for mounting
/mob/living/basic/mining/goliath/proc/tamed()
tamed = TRUE
// Copy entire faction rather than just placing user into faction, to avoid tentacle peril on station
/mob/living/basic/mining/goliath/befriend(mob/living/new_friend)
. = ..()
faction = new_friend.faction.Copy()
/// Goliath which sometimes replaces itself with a rare variant
/mob/living/basic/mining/goliath/random
/mob/living/basic/mining/goliath/random/Initialize(mapload)
. = ..()
if(!prob(1))
return
new /mob/living/basic/mining/goliath/ancient/immortal(loc)
return INITIALIZE_HINT_QDEL
/// Legacy Goliath mob with different sprites, largely the same behaviour
/mob/living/basic/mining/goliath/ancient
name = "ancient goliath"
desc = "A massive beast that uses long tentacles to ensnare its prey, threatening them is not advised under any conditions."
icon = 'icons/mob/simple/lavaland/lavaland_monsters_wide.dmi'
icon_state = "ancient_goliath"
icon_living = "ancient_goliath"
icon_dead = "ancient_goliath_dead"
tentacle_warning_state = "ancient_goliath_preattack"
tameable = FALSE
/// Rare Goliath variant which occasionally replaces the normal mining mob, releases shitloads of tentacles
/mob/living/basic/mining/goliath/ancient/immortal
name = "immortal goliath"
desc = "Goliaths are biologically immortal, and rare specimens have survived for centuries. \
This one is clearly ancient, and its tentacles constantly churn the earth around it."
maxHealth = 400
health = 400
crusher_drop_chance = 30 // Wow a whole 5% more likely, how generous
/// Don't re-check nearby turfs for this long
COOLDOWN_DECLARE(retarget_turfs_cooldown)
/// List of places we might spawn a tentacle, if we're alive
var/list/tentacle_target_turfs
/mob/living/basic/mining/goliath/ancient/immortal/Life(seconds_per_tick, 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/goliath_tentacle(target_turf)
/mob/living/basic/mining/goliath/ancient/immortal/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
. = ..()
if (loc == old_loc || stat == DEAD || !isturf(loc))
return
cache_nearby_turfs()
/// Store nearby turfs in our list so we can pop them out later
/mob/living/basic/mining/goliath/ancient/immortal/proc/cache_nearby_turfs()
COOLDOWN_START(src, retarget_turfs_cooldown, 10 SECONDS)
LAZYCLEARLIST(tentacle_target_turfs)
for(var/turf/open/floor in orange(4, loc))
LAZYADD(tentacle_target_turfs, floor)
/// Use this to ride a goliath
/obj/item/goliath_saddle
name = "goliath saddle"
desc = "This rough saddle will give you a serviceable seat upon a goliath! Provided you can get one to stand still."
icon = 'icons/obj/mining.dmi'
icon_state = "goliath_saddle"
@@ -0,0 +1,85 @@
/// Place some grappling tentacles underfoot
/datum/action/cooldown/goliath_tentacles
name = "Unleash Tentacles"
desc = "Unleash burrowed tentacles at a targetted location, grappling targets after a delay."
button_icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
button_icon_state = "goliath_tentacle_wiggle"
background_icon_state = "bg_demon"
overlay_icon_state = "bg_demon_border"
click_to_activate = TRUE
cooldown_time = 12 SECONDS
melee_cooldown_time = 0
check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED
/// Furthest range we can activate ability at
var/max_range = 7
/datum/action/cooldown/goliath_tentacles/PreActivate(atom/target)
target = get_turf(target)
if (get_dist(owner, target) > max_range)
return FALSE
return ..()
/datum/action/cooldown/goliath_tentacles/Activate(atom/target)
. = ..()
new /obj/effect/goliath_tentacle(target)
var/list/directions = GLOB.cardinals.Copy()
for(var/i in 1 to 3)
var/spawndir = pick_n_take(directions)
var/turf/adjacent_target = get_step(target, spawndir)
if(adjacent_target)
new /obj/effect/goliath_tentacle(adjacent_target)
if (isliving(target))
owner.visible_message(span_warning("[owner] digs its tentacles under [target]!"))
return TRUE
/// Place grappling tentacles around you to grab attackers
/datum/action/cooldown/tentacle_burst
name = "Tentacle Burst"
desc = "Unleash burrowed tentacles in an area around you, grappling targets after a delay."
button_icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
button_icon_state = "goliath_tentacle_wiggle"
background_icon_state = "bg_demon"
overlay_icon_state = "bg_demon_border"
cooldown_time = 24 SECONDS
melee_cooldown_time = 0
check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED
/datum/action/cooldown/tentacle_burst/Activate(atom/target)
. = ..()
var/list/directions = GLOB.alldirs.Copy()
for (var/dir in directions)
var/turf/adjacent_target = get_step(target, dir)
if(adjacent_target)
new /obj/effect/goliath_tentacle(adjacent_target)
owner.visible_message(span_warning("[owner] unleashes tentacles from the ground around it!"))
return TRUE
/// Summon a line of tentacles towards the target
/datum/action/cooldown/tentacle_grasp
name = "Tentacle Grasp"
desc = "Unleash burrowed tentacles in a line towards a targetted location, grappling targets after a delay."
button_icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
button_icon_state = "goliath_tentacle_wiggle"
background_icon_state = "bg_demon"
overlay_icon_state = "bg_demon_border"
click_to_activate = TRUE
cooldown_time = 12 SECONDS
melee_cooldown_time = 0
check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED
/datum/action/cooldown/tentacle_grasp/Activate(atom/target)
. = ..()
new /obj/effect/temp_visual/effect_trail/burrowed_tentacle(owner.loc, target)
if (isliving(target))
owner.visible_message(span_warning("[owner] reaches for [target] with its tentacles!"))
return TRUE
/// An invisible effect which chases a target, spawning tentacles every so often.
/obj/effect/temp_visual/effect_trail/burrowed_tentacle
name = "burrowed_tentacle"
duration = 2 SECONDS
move_speed = 2
homing = FALSE
spawn_interval = 0.1 SECONDS
spawned_effect = /obj/effect/goliath_tentacle
@@ -0,0 +1,115 @@
/datum/ai_controller/basic_controller/goliath
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items/goliath,
)
ai_movement = /datum/ai_movement/basic_avoidance
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/target_retaliate,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/find_food,
/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/goliath_find_diggable_turf,
/datum/ai_planning_subtree/goliath_dig,
)
/datum/targetting_datum/basic/allow_items/goliath
stat_attack = HARD_CRIT
/datum/ai_planning_subtree/basic_melee_attack_subtree/goliath
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, targetting_datum_key, hiding_location_key, health_ratio_key)
var/mob/living/target = controller.blackboard[target_key]
// Interrupt attack chain to use tentacles, unless the target is already tentacled
if (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())
finish_action(controller, succeeded = FALSE)
return
return ..()
/datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles
ability_key = BB_GOLIATH_TENTACLES
/datum/ai_planning_subtree/targeted_mob_ability/goliath_tentacles/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/mob/living/target = controller.blackboard[target_key]
if (!isliving(target) || 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
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/SelectBehaviors(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))
finish_action(controller, succeeded = FALSE)
return
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))
finish_action(controller, succeeded = FALSE) // Otherwise they won't perform idle wanderin
return
controller.set_blackboard_key(target_key, check_turf)
finish_action(controller, succeeded = TRUE)
/// Return true if this is a turf we can dig
/datum/ai_behavior/goliath_find_diggable_turf/proc/is_valid_turf(turf/check_turf)
if (!isasteroidturf(check_turf))
return FALSE
var/turf/open/misc/asteroid/floor = check_turf
return !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/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
var/turf/target_turf = controller.blackboard[target_key]
if (QDELETED(target_turf))
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 = 1 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
basic_mob.melee_attack(target_turf)
finish_action(controller, succeeded = TRUE)
/datum/ai_behavior/goliath_dig/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
@@ -0,0 +1,120 @@
/// A tentacle which grabs you if you don't get away from it
/obj/effect/goliath_tentacle
name = "goliath tentacle"
icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
icon_state = "goliath_tentacle_spawn"
layer = BELOW_MOB_LAYER
plane = GAME_PLANE
anchored = TRUE
/// Timer for our current action stage
var/action_timer
/// Time in which to grab people
var/grapple_time = 10 SECONDS
/// Lower bound of damage to inflict
var/min_damage = 10
/// Upper bound of damage to inflict
var/max_damage = 15
/obj/effect/goliath_tentacle/Initialize(mapload)
. = ..()
if (!isopenturf(loc) || isspaceturf(loc) || isopenspaceturf(loc))
return INITIALIZE_HINT_QDEL
for (var/obj/effect/goliath_tentacle/tentacle in loc)
if (tentacle != src)
return INITIALIZE_HINT_QDEL
if (ismineralturf(loc))
var/turf/closed/mineral/floor = loc
floor.gets_drilled()
deltimer(action_timer)
action_timer = addtimer(CALLBACK(src, PROC_REF(animate_grab)), 0.7 SECONDS, TIMER_STOPPABLE)
/obj/effect/goliath_tentacle/Destroy()
deltimer(action_timer)
return ..()
/// Change to next icon state and set up grapple
/obj/effect/goliath_tentacle/proc/animate_grab()
icon_state = "goliath_tentacle_wiggle"
deltimer(action_timer)
addtimer(CALLBACK(src, PROC_REF(grab)), 0.3 SECONDS, TIMER_STOPPABLE)
/// Grab everyone we share space with. If it's nobody, go home.
/obj/effect/goliath_tentacle/proc/grab()
for (var/mob/living/victim in loc)
if (victim.stat == DEAD || HAS_TRAIT(victim, TRAIT_TENTACLE_IMMUNE))
continue
balloon_alert(victim, "grabbed")
visible_message(span_danger("[src] grabs hold of [victim]!"))
victim.adjustBruteLoss(rand(min_damage, max_damage))
if (victim.apply_status_effect(/datum/status_effect/incapacitating/stun/goliath_tentacled, grapple_time, src))
buckle_mob(victim, TRUE)
SEND_SIGNAL(victim, COMSIG_GOLIATH_TENTACLED_GRABBED)
if (!has_buckled_mobs())
retract()
return
deltimer(action_timer)
action_timer = addtimer(CALLBACK(src, PROC_REF(retract)), grapple_time, TIMER_STOPPABLE)
/// Play exit animation.
/obj/effect/goliath_tentacle/proc/retract()
if (icon_state == "goliath_tentacle_retract")
return // Already retracting
SEND_SIGNAL(src, COMSIG_GOLIATH_TENTACLE_RETRACTING)
unbuckle_all_mobs(force = TRUE)
icon_state = "goliath_tentacle_retract"
deltimer(action_timer)
action_timer = QDEL_IN_STOPPABLE(src, 0.7 SECONDS)
/obj/effect/goliath_tentacle/attack_hand(mob/living/user, list/modifiers)
. = ..()
if (. || !has_buckled_mobs())
return
retract()
return TRUE
/// Goliath tentacle stun with special removal conditions
/datum/status_effect/incapacitating/stun/goliath_tentacled
id = "goliath_tentacled"
duration = 10 SECONDS
/// The tentacle that is tenderly holding us close
var/obj/effect/goliath_tentacle/tentacle
/datum/status_effect/incapacitating/stun/goliath_tentacled/on_creation(mob/living/new_owner, set_duration, obj/effect/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, list(SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE), COMSIG_BRIMDUST_EXPLOSION), PROC_REF(release))
RegisterSignals(tentacle, list(COMSIG_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING), PROC_REF(on_tentacle_left))
/datum/status_effect/incapacitating/stun/goliath_tentacled/on_remove()
. = ..()
UnregisterSignal(owner, list(COMSIG_CARBON_PRE_MISC_HELP, SIGNAL_ADDTRAIT(TRAIT_TENTACLE_IMMUNE), COMSIG_BRIMDUST_EXPLOSION))
if (isnull(tentacle))
return
UnregisterSignal(tentacle, list(COMSIG_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
release()
source.visible_message(span_notice("[helping] rips [source] from the tentacle's grasp!"))
return COMPONENT_BLOCK_MISC_HELP
/// Something happened to make the tentacle let go
/datum/status_effect/incapacitating/stun/goliath_tentacled/proc/release()
SIGNAL_HANDLER
owner.remove_status_effect(/datum/status_effect/incapacitating/stun/goliath_tentacled)
/// Something happened to our associated tentacle
/datum/status_effect/incapacitating/stun/goliath_tentacled/proc/on_tentacle_left()
SIGNAL_HANDLER
UnregisterSignal(tentacle, list(COMSIG_QDELETING, COMSIG_GOLIATH_TENTACLE_RETRACTING)) // No endless loops for us please
tentacle = null
release()
@@ -1,6 +1,7 @@
///prototype for mining mobs
/mob/living/basic/mining
combat_mode = TRUE
mob_size = MOB_SIZE_LARGE
faction = list(FACTION_MINING)
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
@@ -40,6 +40,7 @@
/mob/living/basic/cat_butcherer/Initialize(mapload)
. = ..()
apply_dynamic_human_appearance(src, mob_spawn_path = /obj/effect/mob_spawn/corpse/human/cat_butcher, l_hand = /obj/item/circular_saw, bloody_slots = ITEM_SLOT_GLOVES|ITEM_SLOT_OCLOTHING)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/death_drops, drop_on_death)
RegisterSignal(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(after_attack))
@@ -12,7 +12,7 @@
/datum/action/cooldown/chasing_spikes/Activate(atom/target)
. = ..()
playsound(owner, 'sound/magic/demon_attack1.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
var/obj/effect/temp_visual/spike_chaser/chaser = new(get_turf(owner), target)
var/obj/effect/temp_visual/effect_trail/spike_chaser/chaser = new(get_turf(owner), target)
LAZYADD(active_chasers, WEAKREF(chaser))
RegisterSignal(chaser, COMSIG_QDELETING, PROC_REF(on_chaser_destroyed))
@@ -27,42 +27,9 @@
return ..()
/// An invisible effect which chases a target, spawning spikes every so often.
/obj/effect/temp_visual/spike_chaser
/obj/effect/temp_visual/effect_trail/spike_chaser
name = "spike chaser"
desc = "An invisible effect, how did you examine this?"
icon = 'icons/mob/silicon/cameramob.dmi'
icon_state = "marker"
duration = 15 SECONDS
invisibility = INVISIBILITY_ABSTRACT
/// Speed at which we chase target
var/move_speed = 3
/// What are we chasing?
var/datum/weakref/target
/// Handles chasing the target
var/datum/move_loop/movement
/obj/effect/temp_visual/spike_chaser/Initialize(mapload, atom/target)
. = ..()
if (!target)
return INITIALIZE_HINT_QDEL
AddElement(/datum/element/floor_loving)
AddComponent(/datum/component/spawner, spawn_types = list(/obj/effect/temp_visual/emerging_ground_spike), spawn_time = 0.5 SECONDS)
src.target = WEAKREF(target)
movement = SSmove_manager.move_towards(src, chasing = target, delay = move_speed, home = TRUE, timeout = duration, flags = MOVEMENT_LOOP_START_FAST)
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(on_target_invalid))
if (isliving(target))
RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(on_target_invalid))
/// Destroy ourselves if the target is no longer valid
/obj/effect/temp_visual/spike_chaser/proc/on_target_invalid()
SIGNAL_HANDLER
qdel(src)
/obj/effect/temp_visual/spike_chaser/Destroy()
QDEL_NULL(movement)
return ..()
spawned_effect = /obj/effect/temp_visual/emerging_ground_spike
/// A spike comes out of the ground, dealing damage after a short delay
/obj/effect/temp_visual/emerging_ground_spike
+1 -1
View File
@@ -1390,6 +1390,7 @@
/mob/living/basic/cow,
/mob/living/basic/giant_spider,
/mob/living/basic/giant_spider/hunter,
/mob/living/basic/mining/goliath,
/mob/living/basic/headslug,
/mob/living/basic/killer_tomato,
/mob/living/basic/lizard,
@@ -1404,7 +1405,6 @@
/mob/living/basic/stickman/dog,
/mob/living/simple_animal/crab,
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher,
/mob/living/simple_animal/hostile/asteroid/goliath/beast,
/mob/living/simple_animal/hostile/bear,
/mob/living/simple_animal/hostile/blob/blobbernaut/independent,
/mob/living/simple_animal/hostile/gorilla,
@@ -53,6 +53,10 @@
var/rand_tent = 0
var/list/mob/living/simple_animal/hostile/asteroid/elite/broodmother_child/children_list = list()
/mob/living/simple_animal/hostile/asteroid/elite/broodmother/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_TENTACLE_IMMUNE, INNATE_TRAIT)
/datum/action/innate/elite_attack/tentacle_patch
name = "Tentacle Patch"
button_icon_state = "tentacle_patch"
@@ -112,7 +116,7 @@
var/tentacle_loc = spiral_range_turfs(5, get_turf(src))
for(var/i in 1 to tentacle_amount)
var/turf/t = pick_n_take(tentacle_loc)
new /obj/effect/temp_visual/goliath_tentacle/broodmother(t, src)
new /obj/effect/goliath_tentacle/broodmother(t, src)
/mob/living/simple_animal/hostile/asteroid/elite/broodmother/proc/tentacle_patch(target)
ranged_cooldown = world.time + 15
@@ -120,7 +124,7 @@
if(!isturf(tturf))
return
visible_message(span_warning("[src] digs its tentacles under [target]!"))
new /obj/effect/temp_visual/goliath_tentacle/broodmother/patch(tturf, src)
new /obj/effect/goliath_tentacle/broodmother/patch(tturf, src)
/mob/living/simple_animal/hostile/asteroid/elite/broodmother/proc/spawn_children(target)
ranged_cooldown = world.time + 40
@@ -188,6 +192,10 @@
status_flags = CANPUSH
var/mob/living/simple_animal/hostile/asteroid/elite/broodmother/mother = null
/mob/living/simple_animal/hostile/asteroid/elite/broodmother_child/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_TENTACLE_IMMUNE, INNATE_TRAIT)
/mob/living/simple_animal/hostile/asteroid/elite/broodmother_child/OpenFire(target)
ranged_cooldown = world.time + 40
var/tturf = get_turf(target)
@@ -195,7 +203,7 @@
return
if(get_dist(src, target) <= 7)//Screen range check, so it can't attack people off-screen
visible_message(span_warning("[src] digs one of its tentacles under [target]!"))
new /obj/effect/temp_visual/goliath_tentacle/broodmother(tturf, src)
new /obj/effect/goliath_tentacle/broodmother(tturf, src)
/mob/living/simple_animal/hostile/asteroid/elite/broodmother_child/death()
. = ..()
@@ -205,35 +213,24 @@
explosion(src, flame_range = 3, adminlog = FALSE)
gib()
//Tentacles have less stun time compared to regular variant, to balance being able to use them much more often. Also, 10 more damage.
/obj/effect/temp_visual/goliath_tentacle/broodmother/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_danger("[src] grabs hold of [L]!"))
L.Stun(10)
L.adjustBruteLoss(rand(30,35))
latched = TRUE
if(!latched)
retract()
else
deltimer(timerid)
timerid = addtimer(CALLBACK(src, PROC_REF(retract)), 10, TIMER_STOPPABLE)
/obj/effect/goliath_tentacle/broodmother
grapple_time = 1 SECONDS
min_damage = 30
max_damage = 35
/obj/effect/temp_visual/goliath_tentacle/broodmother/patch/Initialize(mapload, new_spawner)
/obj/effect/goliath_tentacle/broodmother/patch/Initialize(mapload, new_spawner)
. = ..()
INVOKE_ASYNC(src, PROC_REF(createpatch))
/obj/effect/temp_visual/goliath_tentacle/broodmother/patch/proc/createpatch()
/obj/effect/goliath_tentacle/broodmother/patch/proc/createpatch()
var/tentacle_locs = spiral_range_turfs(1, get_turf(src))
for(var/T in tentacle_locs)
new /obj/effect/temp_visual/goliath_tentacle/broodmother(T, spawner)
new /obj/effect/goliath_tentacle/broodmother(T)
var/list/directions = GLOB.cardinals.Copy()
for(var/i in directions)
var/turf/T = get_step(get_turf(src), i)
T = get_step(T, i)
new /obj/effect/temp_visual/goliath_tentacle/broodmother(T, spawner)
new /obj/effect/goliath_tentacle/broodmother(T)
// Broodmother's loot: Broodmother Tongue
/obj/item/crusher_trophy/broodmother_tongue
@@ -251,7 +248,7 @@
/obj/item/crusher_trophy/broodmother_tongue/on_mark_detonation(mob/living/target, mob/living/user)
if(prob(bonus_value) && target.stat != DEAD)
new /obj/effect/temp_visual/goliath_tentacle/broodmother/patch(get_turf(target), user)
new /obj/effect/goliath_tentacle/broodmother/patch(get_turf(target), user)
/obj/item/crusher_trophy/broodmother_tongue/attack_self(mob/user)
if(!isliving(user))
@@ -1,243 +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/simple/lavaland/lavaland_monsters_wide.dmi'
icon_state = "ancient_goliath"
icon_living = "ancient_goliath"
icon_aggro = "ancient_goliath_alert"
icon_dead = "ancient_goliath_dead"
icon_gib = "syndicate_gib"
pixel_x = -12
base_pixel_x = -12
mob_biotypes = MOB_ORGANIC|MOB_BEAST
mouse_opacity = MOUSE_OPACITY_ICON
move_to_delay = 40
ranged = 1
ranged_cooldown_time = 120
friendly_verb_continuous = "wails at"
friendly_verb_simple = "wail at"
speak_emote = list("bellows")
speed = 3
maxHealth = 300
health = 300
harm_intent_damage = 0
obj_damage = 100
melee_damage_lower = 25
melee_damage_upper = 25
attack_verb_continuous = "pulverizes"
attack_verb_simple = "pulverize"
attack_sound = 'sound/weapons/punch1.ogg'
throw_message = "does nothing to the rocky hide of the"
vision_range = 5
aggro_vision_range = 9
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
gender = MALE//lavaland elite goliath says that i'''' 't s female and i ''t s stronger because of sexual dimorphism, so normal goliaths should be male
var/pre_attack = 0
var/pre_attack_icon = "ancient_goliath_preattack"
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide)
footstep_type = FOOTSTEP_MOB_HEAVY
/mob/living/simple_animal/hostile/asteroid/goliath/Life(seconds_per_tick = SSMOBS_DT, times_fired)
. = ..()
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(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE)//who the fuck anchors mobs
. = ..()
if(!.)
return
move_force = initial(move_force)
move_resist = initial(move_resist)
pull_force = initial(pull_force)
/mob/living/simple_animal/hostile/asteroid/goliath/death(gibbed)
move_force = MOVE_FORCE_DEFAULT
move_resist = MOVE_RESIST_DEFAULT
pull_force = PULL_FORCE_DEFAULT
return ..()
/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_warning("[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 = 0
/mob/living/simple_animal/hostile/asteroid/goliath/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
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
name = "goliath"
desc = "A hulking, armor-plated beast with long tendrils arching from its back."
icon = 'icons/mob/simple/lavaland/lavaland_monsters_wide.dmi'
icon_state = "goliath"
icon_living = "goliath"
icon_aggro = "goliath"
icon_dead = "goliath_dead"
throw_message = "does nothing to the tough hide of the"
pre_attack_icon = "goliath_preattack"
crusher_loot = /obj/item/crusher_trophy/goliath_tentacle
butcher_results = list(/obj/item/food/meat/slab/goliath = 2, /obj/item/stack/sheet/bone = 2)
guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/goliath_hide = 1)
loot = list()
stat_attack = HARD_CRIT
robust_searching = 1
var/can_saddle = FALSE
var/saddled = FALSE
/mob/living/simple_animal/hostile/asteroid/goliath/beast/Initialize(mapload)
. = ..()
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/ash_flora), tame_chance = 10, bonus_tame_chance = 5, after_tame = CALLBACK(src, PROC_REF(tamed)))
/mob/living/simple_animal/hostile/asteroid/goliath/beast/attackby(obj/item/O, mob/user, params)
if(!istype(O, /obj/item/saddle) || saddled)
return ..()
if(can_saddle && do_after(user,55,target=src))
user.visible_message(span_notice("You manage to put [O] on [src], you can now ride [p_them()]."))
qdel(O)
saddled = TRUE
buckle_lying = 0
add_overlay("goliath_saddled")
AddElement(/datum/element/ridable, /datum/component/riding/creature/goliath)
else
user.visible_message(span_warning("[src] is rocking around! You can't put the saddle on!"))
..()
/mob/living/simple_animal/hostile/asteroid/goliath/beast/proc/tamed(mob/living/tamer)
can_saddle = TRUE
/mob/living/simple_animal/hostile/asteroid/goliath/beast/random/Initialize(mapload)
. = ..()
if(prob(1))
new /mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient(loc)
return INITIALIZE_HINT_QDEL
/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 = "ancient_goliath"
icon_living = "ancient_goliath"
icon_aggro = "ancient_goliath_alert"
icon_dead = "ancient_goliath_dead"
maxHealth = 400
health = 400
speed = 4
pre_attack_icon = "ancient_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/meat/slab/goliath = 2, /obj/item/stack/sheet/bone = 2)
guaranteed_butcher_results = list()
crusher_drop_mod = 30
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(seconds_per_tick = SSMOBS_DT, times_fired)
. = ..()
if(!.) // dead
return
if(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/open/T in orange(4, loc))
LAZYADD(cached_tentacle_turfs, T)
for(var/t in cached_tentacle_turfs)
if(isopenturf(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
from_spawner = TRUE
//tentacles
/obj/effect/temp_visual/goliath_tentacle
name = "goliath tentacle"
icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
icon_state = "goliath_tentacle_spawn"
layer = BELOW_MOB_LAYER
plane = GAME_PLANE
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/closed/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.cardinals.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_danger("[src] grabs hold of [L]!"))
L.Stun(100)
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_STOPPABLE(src, 7)
/obj/item/saddle
name = "saddle"
desc = "This saddle will solve all your problems with being killed by lava beasts!"
icon = 'icons/obj/mining.dmi'
icon_state = "goliath_saddle"
@@ -40,7 +40,7 @@
///dead goliath spawner
/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/simple/lavaland/lavaland_monsters_wide.dmi'
icon_state = "goliath_dead_helper"
pixel_x = -12
@@ -19,9 +19,11 @@
//Monsters
/datum/map_generator_module/splatter_layer/asteroid_monsters
spawnableTurfs = list()
spawnableAtoms = list(/mob/living/simple_animal/hostile/asteroid/basilisk = 10, \
/mob/living/simple_animal/hostile/asteroid/hivelord = 10, \
/mob/living/simple_animal/hostile/asteroid/goliath = 10)
spawnableAtoms = list(
/mob/living/basic/mining/goliath/ancient = 10,
/mob/living/simple_animal/hostile/asteroid/basilisk = 10,
/mob/living/simple_animal/hostile/asteroid/hivelord = 10,
)
// GENERATORS
@@ -10,7 +10,7 @@
/datum/map_generator_module/splatter_layer/lavaland_monsters
spawnableTurfs = list()
spawnableAtoms = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast = 10,
spawnableAtoms = list(/mob/living/basic/mining/goliath = 10,
/mob/living/simple_animal/hostile/asteroid/hivelord/legion = 10,
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher = 10)
@@ -73,11 +73,6 @@
/mob/living/simple_animal/hostile/asteroid/elite/legionnairehead,
/mob/living/simple_animal/hostile/asteroid/elite/pandora,
/mob/living/simple_animal/hostile/asteroid/goldgrub,
/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/random,
/mob/living/simple_animal/hostile/asteroid/goliath/beast/tendril,
/mob/living/simple_animal/hostile/asteroid/gutlunch,
/mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch,
/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck,
+1 -1
View File
@@ -76,7 +76,7 @@
/obj/item/key/lasso
name = "bone lasso"
desc = "Perfect for taming all kinds of supernatural beasts! (Warning: only perfect for taming one kind of supernatural beast.)"
desc = "The perfect tool for directing a Goliath! If only it made them move any faster..."
force = 12
icon_state = "lasso"
inhand_icon_state = "chain"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 57 KiB

+9 -1
View File
@@ -291,6 +291,7 @@
#include "code\__DEFINES\dcs\signals\signals_light_eater.dm"
#include "code\__DEFINES\dcs\signals\signals_medical.dm"
#include "code\__DEFINES\dcs\signals\signals_mind.dm"
#include "code\__DEFINES\dcs\signals\signals_mining.dm"
#include "code\__DEFINES\dcs\signals\signals_mod.dm"
#include "code\__DEFINES\dcs\signals\signals_moveloop.dm"
#include "code\__DEFINES\dcs\signals\signals_movetype.dm"
@@ -876,6 +877,7 @@
#include "code\datums\components\attached_sticker.dm"
#include "code\datums\components\aura_healing.dm"
#include "code\datums\components\bakeable.dm"
#include "code\datums\components\basic_mob_attack_telegraph.dm"
#include "code\datums\components\beetlejuice.dm"
#include "code\datums\components\blood_walk.dm"
#include "code\datums\components\bloodysoles.dm"
@@ -988,6 +990,7 @@
#include "code\datums\components\religious_tool.dm"
#include "code\datums\components\remote_materials.dm"
#include "code\datums\components\rename.dm"
#include "code\datums\components\revenge_ability.dm"
#include "code\datums\components\rot.dm"
#include "code\datums\components\rotation.dm"
#include "code\datums\components\scope.dm"
@@ -1237,6 +1240,7 @@
#include "code\datums\elements\light_eater.dm"
#include "code\datums\elements\loomable.dm"
#include "code\datums\elements\mob_killed_tally.dm"
#include "code\datums\elements\move_cooldown.dm"
#include "code\datums\elements\movement_turf_changer.dm"
#include "code\datums\elements\movetype_handler.dm"
#include "code\datums\elements\nerfed_pulling.dm"
@@ -1840,6 +1844,7 @@
#include "code\game\objects\effects\spawners\random\trash.dm"
#include "code\game\objects\effects\spawners\random\vending.dm"
#include "code\game\objects\effects\temporary_visuals\cult.dm"
#include "code\game\objects\effects\temporary_visuals\effect_trail.dm"
#include "code\game\objects\effects\temporary_visuals\miscellaneous.dm"
#include "code\game\objects\effects\temporary_visuals\temporary_visual.dm"
#include "code\game\objects\effects\temporary_visuals\projectiles\impact.dm"
@@ -4030,6 +4035,10 @@
#include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_instrument.dm"
#include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_loot.dm"
#include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_vileworm.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\pets\dog.dm"
#include "code\modules\mob\living\basic\pets\fox.dm"
#include "code\modules\mob\living\basic\pets\pet.dm"
@@ -4334,7 +4343,6 @@
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\brimdemon.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\curse_blob.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\goldgrub.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\goliath.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\gutlunch.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\hivelord.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\ice_demon.dm"
@@ -0,0 +1,5 @@
/mob/living/simple_animal/hostile/asteroid/goliath/beast : /mob/living/basic/mining/goliath{@OLD}
/mob/living/simple_animal/hostile/asteroid/goliath/beast/random : /mob/living/basic/mining/goliath/random{@OLD}
/mob/living/simple_animal/hostile/asteroid/goliath : /mob/living/basic/mining/goliath/ancient{@OLD}
/mob/living/simple_animal/hostile/asteroid/goliath/beast/ancient : /mob/living/basic/mining/goliath/ancient/immortal{@OLD}
/obj/item/saddle : /obj/item/goliath_saddle{@OLD}