mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-28 23:58:07 +01:00
[MIRROR] Converts Spiderlings from Structures to Basic Mobs [MDB IGNORE] (#20852)
* Converts Spiderlings from Structures to Basic Mobs (#75001) If I could've made this more atomic, I would have in a heartbeat, trust me. ## About The Pull Request Hey there. People were mocking us for having spiderlings still be a subtype of `/obj/structure`. I decided to take a lot of time to fix that. A lot of behavior it was implementing was just pseudo-mob stuff, so it was actually easier than it looked for the raw conversion. A lot of the footwork on spider stuff in the basic framework was already done previously by Jacquerel, so that was pretty nice. However, there are two new things that weren't introduced in the code that had to be put in. A) A component to handle growth and differentiation into a mob. This may have already existed, no clue. If it does (and it's NOT evolutionary_leap), let me know. B) AI Behavior to handle seeking out a vent, entering a vent, and then exiting out of a different vent. I may have gone a bit wacky on the code, but it certainly works as expected (spiderling goes in one vent, exits the other). Let me know if you can think of a way it can be better optimized, but it was deliberately written to be very failsafey in case shit goes yonkers. One fundamental difference between structure spiderlings and basic mob spiderlings (beyond the AI and not just a random prob() check for movement) is the fact that they had vent movement coded in... but we _really_ don't need stuff like that for our intents and purposes. If the range turns out to be too OP in the current framework, we can always change it up a bit, but also there's a _lot_ of vents we can end up in the station (my testing had one spiderling end up in the AI sat to get obliterated). ## Why It's Good For The Game Spiderlings aren't structures! They behave like a mob should! Players can possess spiderlings! They work seamlessly with differentiating into a giant spider! Better AI! More room for people to add into this very under-utilized buggers! ## Changelog 🆑 refactor: Spiderlings are now basic mobs, report any complete weirdness/deviation from known behavior. They should be a lot more intelligent now though. add: AI Spiderlings are super fragile, but they're also super fast, especially when they get into a vent. Once they're in circulation, they could end up everywhere! Maybe in the armory, maybe in a locked closet in maintenance. Be sure to be vigilant and splat them whenever you can to save the station from a whole lotta heartache! /🆑 --------- Co-authored-by: MrMelbert <51863163+MrMelbert@ users.noreply.github.com> * Converts Spiderlings from Structures to Basic Mobs --------- Co-authored-by: san7890 <the@san7890.com> Co-authored-by: MrMelbert <51863163+MrMelbert@ users.noreply.github.com>
This commit is contained in:
co-authored by
MrMelbert
san7890
parent
cf9fce9f8b
commit
7028b1d32e
@@ -237,6 +237,23 @@
|
||||
///How long have we spent with no target?
|
||||
#define BB_TARGETLESS_TIME "BB_targetless_time"
|
||||
|
||||
/// Key that holds a nearby vent that looks like it's a good place to hide
|
||||
#define BB_ENTRY_VENT_TARGET "BB_entry_vent_target"
|
||||
/// Key that holds a vent that we want to exit out of (when we're already in a pipenet)
|
||||
#define BB_EXIT_VENT_TARGET "BB_exit_vent_target"
|
||||
/// Do we plan on going inside a vent? Boolean.
|
||||
#define BB_CURRENTLY_TARGETTING_VENT "BB_currently_targetting_vent"
|
||||
/// How long should we wait before we try and enter a vent again?
|
||||
#define BB_VENTCRAWL_COOLDOWN "BB_ventcrawl_cooldown"
|
||||
/// The least amount of time (in seconds) we take to go through the vents.
|
||||
#define BB_LOWER_VENT_TIME_LIMIT "BB_lower_vent_time_limit"
|
||||
/// The most amount of time (in seconds) we take to go through the vents.
|
||||
#define BB_UPPER_VENT_TIME_LIMIT "BB_upper_vent_time_limit"
|
||||
/// How much time (in seconds) do we take until we completely go bust on vent pathing?
|
||||
#define BB_TIME_TO_GIVE_UP_ON_VENT_PATHING "BB_seconds_until_we_give_up_on_vent_pathing"
|
||||
/// The timer ID of the timer that makes us give up on vent pathing.
|
||||
#define BB_GIVE_UP_ON_VENT_PATHING_TIMER_ID "BB_give_up_on_vent_pathing_timer_id"
|
||||
|
||||
/// Is there something that scared us into being stationary? If so, hold the reference here
|
||||
#define BB_STATIONARY_CAUSE "BB_thing_that_made_us_stationary"
|
||||
///How long should we remain stationary for?
|
||||
|
||||
@@ -1001,6 +1001,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
/// Basically, without this, COMSIG_IN_RANGE_OF_IRRADIATION won't fire once the object is irradiated.
|
||||
#define TRAIT_BYPASS_EARLY_IRRADIATED_CHECK "radiation_bypass_early_irradiated_check"
|
||||
|
||||
/// Simple trait that just holds if we came into growth from a specific mob type. Should hold a REF(src) to the type of mob that caused the growth, not anything else.
|
||||
#define TRAIT_WAS_EVOLVED "was_evolved_from_the_mob_we_hold_a_textref_to"
|
||||
|
||||
// Traits to heal for
|
||||
|
||||
/// This mob heals from carp rifts.
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/// We hop into the vents through a vent outlet, and then crawl around a bit. Jolly good times.
|
||||
/// This also assumes that we are on the turf that the vent outlet is on. If it isn't, shit.
|
||||
|
||||
/// Warning: this was really snowflake code lifted from an obscure feature that likely has not been touched for over five years years.
|
||||
/// Something that isn't implemented is the ability to actually crawl through vents ourselves because I think that's just a waste of time for the same effect (instead of psuedo-teleportation, do REAL forceMoving)
|
||||
/// If you are seriously considering using this component, it would be a great idea to extend this proc to be more versatile/less overpowered - the mobs that currently implement this benefit the most
|
||||
/// since they are weak as shit with only five health. Up to you though, don't take what's written here as gospel.
|
||||
/datum/ai_behavior/crawl_through_vents
|
||||
action_cooldown = 10 SECONDS
|
||||
|
||||
/datum/ai_behavior/crawl_through_vents/setup(datum/ai_controller/controller, target_key)
|
||||
action_cooldown = controller.blackboard[BB_VENTCRAWL_COOLDOWN] || initial(action_cooldown)
|
||||
. = ..()
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/target = controller.blackboard[target_key] || controller.blackboard[BB_ENTRY_VENT_TARGET]
|
||||
return istype(target) && isliving(controller.pawn) // only mobs can vent crawl in the current framework
|
||||
|
||||
/datum/ai_behavior/crawl_through_vents/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
. = ..()
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent = controller.blackboard[target_key] || controller.blackboard[BB_ENTRY_VENT_TARGET]
|
||||
var/mob/living/cached_pawn = controller.pawn
|
||||
if(HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING) || !controller.blackboard[BB_CURRENTLY_TARGETTING_VENT] || !is_vent_valid(entry_vent))
|
||||
return
|
||||
|
||||
if(!cached_pawn.can_enter_vent(entry_vent, provide_feedback = FALSE)) // we're an AI we scoff at feedback
|
||||
finish_action(controller, FALSE, target_key) // "never enter a hole you can't get out of"
|
||||
return
|
||||
|
||||
var/vent_we_exit_out_of = calculate_exit_vent(controller, target_key)
|
||||
if(isnull(vent_we_exit_out_of)) // don't get into the vents if we can't get out of them, that's SILLY.
|
||||
finish_action(controller, FALSE, target_key)
|
||||
return
|
||||
|
||||
controller.set_blackboard_key(BB_CURRENTLY_TARGETTING_VENT, FALSE) // must be done here because we have a do_after sleep in handle_ventcrawl unfortunately and double dipping could lead to erroneous suicide pill calls.
|
||||
cached_pawn.handle_ventcrawl(entry_vent)
|
||||
if(!HAS_TRAIT(cached_pawn, TRAIT_MOVE_VENTCRAWLING)) //something failed and we ARE NOT IN THE VENT even though the earlier check said we were good to go! odd.
|
||||
finish_action(controller, FALSE, target_key)
|
||||
return
|
||||
|
||||
controller.set_blackboard_key(BB_EXIT_VENT_TARGET, vent_we_exit_out_of)
|
||||
|
||||
if(prob(50))
|
||||
cached_pawn.visible_message(
|
||||
span_warning("[src] scrambles into the ventilation ducts!"),
|
||||
span_hear("You hear something scampering through the ventilation ducts."),
|
||||
)
|
||||
|
||||
var/lower_vent_time_limit = controller.blackboard[BB_LOWER_VENT_TIME_LIMIT] // the least amount of time we spend in the vents
|
||||
var/upper_vent_time_limit = controller.blackboard[BB_UPPER_VENT_TIME_LIMIT] // the most amount of time we spend in the vents
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(exit_the_vents), controller), rand(lower_vent_time_limit, upper_vent_time_limit))
|
||||
controller.set_blackboard_key(BB_GIVE_UP_ON_VENT_PATHING_TIMER_ID, addtimer(CALLBACK(src, PROC_REF(suicide_pill), controller), controller.blackboard[BB_TIME_TO_GIVE_UP_ON_VENT_PATHING], TIMER_STOPPABLE))
|
||||
|
||||
/// Figure out an exit vent that we should head towards. If we don't have one, default to the entry vent. If they're all kaput, we die.
|
||||
/datum/ai_behavior/crawl_through_vents/proc/calculate_exit_vent(datum/ai_controller/controller, target_key)
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/returnable_vent
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/vent_we_entered_through = controller.blackboard[target_key] || controller.blackboard[BB_ENTRY_VENT_TARGET]
|
||||
|
||||
var/datum/pipeline/entry_vent_parent = vent_we_entered_through.parents[1]
|
||||
var/list/potential_exits = list()
|
||||
|
||||
for(var/obj/machinery/atmospherics/components/unary/vent_pump/vent in entry_vent_parent.other_atmos_machines)
|
||||
if(is_vent_valid(vent))
|
||||
potential_exits.Add(vent)
|
||||
|
||||
if(length(potential_exits))
|
||||
returnable_vent = pick(potential_exits)
|
||||
return returnable_vent
|
||||
|
||||
// if we're here, we're in "what the flarp" mode... okay maybe we can default to the vent we entered in.
|
||||
returnable_vent = vent_we_entered_through
|
||||
if(is_vent_valid(vent_we_entered_through))
|
||||
// AH WHAT THE FUCK. okay, maybe we're not inside the vents yet? let's return null and we can pick up on that based on the wider context of the proc that invokes it.
|
||||
return null
|
||||
|
||||
return returnable_vent // we return null in case something yonked between then and now so it's all good man
|
||||
|
||||
/// We've had enough horsing around in the vents, it's time to get out.
|
||||
/datum/ai_behavior/crawl_through_vents/proc/exit_the_vents(datum/ai_controller/controller, target_key)
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/emergency_vent // vent we will scramble to search for in case plan A is a bust (exit vent)
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/exit_vent = controller.blackboard[BB_EXIT_VENT_TARGET]
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
|
||||
if(!HAS_TRAIT(living_pawn, TRAIT_MOVE_VENTCRAWLING) && isturf(get_turf(living_pawn))) // we're out of the vents, so no need to do an exit
|
||||
finish_action(controller, TRUE, target_key) // assume that we got yeeted out somehow and call this so we can halt the suicide pill timer.
|
||||
return
|
||||
|
||||
living_pawn.forceMove(exit_vent)
|
||||
if(!living_pawn.can_enter_vent(exit_vent, provide_feedback = FALSE))
|
||||
// oh shit, something happened while we were waiting on that timer. let's figure out a different way to get out of here.
|
||||
emergency_vent = calculate_exit_vent(controller)
|
||||
if(isnull(emergency_vent))
|
||||
// it's joever. we cooked too hard.
|
||||
suicide_pill(controller, target_key)
|
||||
return
|
||||
|
||||
controller.set_blackboard_key(BB_EXIT_VENT_TARGET, emergency_vent) // assign and go again
|
||||
addtimer(CALLBACK(src, PROC_REF(exit_the_vents), controller), (rand(controller.blackboard[BB_LOWER_VENT_TIME_LIMIT], controller.blackboard[BB_UPPER_VENT_TIME_LIMIT]) / 2)) // we're in danger mode, so scurry out at half the time it would normally take.
|
||||
return
|
||||
|
||||
living_pawn.handle_ventcrawl(exit_vent)
|
||||
if(HAS_TRAIT(living_pawn, TRAIT_MOVE_VENTCRAWLING)) // how'd we fail? what the fuck
|
||||
stack_trace("We failed to exit the vents, even though we should have been fine? This is very weird.")
|
||||
suicide_pill() // all of the prior checks say we should have definitely made it through, but we didn't. dammit.
|
||||
return
|
||||
|
||||
finish_action(controller, TRUE, target_key) // we did it! we went into the vents and out of the vents. poggers.
|
||||
|
||||
/// Incredibly stripped down version of the overarching `can_enter_vent` proc on `/mob, just meant for rapid rechecking of a vent. Will be TRUE if not blocked, FALSE otherwise.
|
||||
/datum/ai_behavior/crawl_through_vents/proc/is_vent_valid(obj/machinery/atmospherics/components/unary/vent_pump/checkable)
|
||||
return !QDELETED(checkable) && !checkable.welded
|
||||
|
||||
/// Aw fuck, we may have been bested somehow. Regardless of what we do, we can't exit through a vent! Let's end our misery and prevent useless endless calculations.
|
||||
/datum/ai_behavior/crawl_through_vents/proc/suicide_pill(datum/ai_controller/controller, target_key)
|
||||
var/mob/living/living_pawn = controller.pawn
|
||||
|
||||
if(istype(living_pawn))
|
||||
finish_action(controller, FALSE, target_key)
|
||||
|
||||
if(isnull(living_pawn.client)) // only call death if we don't have a client because maybe their natural intelligence can pick up where our AI calculations have failed
|
||||
living_pawn.death(TRUE) // call gibbed as true because we are never coming back it is so fucking joever
|
||||
|
||||
return
|
||||
|
||||
if(QDELETED(living_pawn)) // we got deleted by some other means, just presume the action is a wash and get outta here
|
||||
return
|
||||
|
||||
qdel(living_pawn) // failover, we really should've been caught in the istype() but lets just bow out of existing at this point
|
||||
|
||||
|
||||
/datum/ai_behavior/crawl_through_vents/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||
. = ..()
|
||||
|
||||
deltimer(controller.blackboard[BB_GIVE_UP_ON_VENT_PATHING_TIMER_ID])
|
||||
controller.clear_blackboard_key(target_key)
|
||||
controller.clear_blackboard_key(BB_ENTRY_VENT_TARGET)
|
||||
controller.clear_blackboard_key(BB_EXIT_VENT_TARGET)
|
||||
controller.set_blackboard_key(BB_CURRENTLY_TARGETTING_VENT, FALSE) // just in case
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/// Opportunistically searches for and hides/scurries through vents.
|
||||
/datum/ai_planning_subtree/opportunistic_ventcrawler
|
||||
|
||||
/datum/ai_planning_subtree/opportunistic_ventcrawler/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
if(HAS_TRAIT(controller.pawn, TRAIT_MOVE_VENTCRAWLING))
|
||||
return SUBTREE_RETURN_FINISH_PLANNING // hold on let me cook
|
||||
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/target = controller.blackboard[BB_ENTRY_VENT_TARGET]
|
||||
|
||||
if(QDELETED(target))
|
||||
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_ENTRY_VENT_TARGET, /obj/machinery/atmospherics/components/unary/vent_pump) // keep looking otherwise they KILL US AND WE DIE
|
||||
return
|
||||
|
||||
if(get_turf(controller.pawn) != get_turf(target))
|
||||
controller.queue_behavior(/datum/ai_behavior/travel_towards, BB_ENTRY_VENT_TARGET)
|
||||
return
|
||||
|
||||
controller.set_blackboard_key(BB_CURRENTLY_TARGETTING_VENT, TRUE)
|
||||
controller.queue_behavior(/datum/ai_behavior/crawl_through_vents, BB_ENTRY_VENT_TARGET)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING // we are going into this vent... no distractions
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* ### Growth and Differentiation Component: Used to randomly "grow" a creature into a new entity over its lifespan.
|
||||
*
|
||||
* If we are passed a typepath, we will 100% grow into that type. However, if we are not passed a typepath, we will pick one from a subtype of the parent we were applied to!
|
||||
*
|
||||
* Used for spiderlings to turn them into giant spiders.
|
||||
*/
|
||||
|
||||
/datum/component/growth_and_differentiation
|
||||
/// What this mob turns into when fully grown.
|
||||
var/growth_path
|
||||
/// Failover for how much time we have until we fully grow. If passed as null, we eschew setting up the timer.
|
||||
/// Remember: We can grow earlier than this if the randomness rolls turn out to be in our favor though!
|
||||
var/growth_time
|
||||
/// Integer - Probability we grow per SPT_PROB
|
||||
var/growth_probability
|
||||
/// Integer - The lower bound for the percentage we have to grow before we can differentiate.
|
||||
var/lower_growth_value
|
||||
/// Integer - The upper bound for the percentage we have to grow before we can differentiate.
|
||||
var/upper_growth_value
|
||||
/// Optional callback for checks to see if we're okay to grow.
|
||||
var/datum/callback/optional_checks
|
||||
/// Optional callback in case we wish to override the default grow() behavior. Assume we supersede the change_mob_type() call if we have this set.
|
||||
var/datum/callback/optional_grow_behavior
|
||||
|
||||
/// ID for the failover timer.
|
||||
var/timer_id
|
||||
/// Percentage we have grown.
|
||||
var/percent_grown = 0
|
||||
/// Are we ready to grow? This is just in case we fail our checks and need to wait until the next tick.
|
||||
/// We only really need this because we have two competing systems, the timer and the probability-based growth. When one succeeds, this component is considered successful in growth,
|
||||
/// and will actively try to grow the mob (only barred by optional checks).
|
||||
var/ready_to_grow = FALSE
|
||||
|
||||
/datum/component/growth_and_differentiation/Initialize(growth_time, growth_path, growth_probability, lower_growth_value, upper_growth_value, optional_checks, optional_grow_behavior)
|
||||
if(!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.growth_path = growth_path
|
||||
src.growth_time = growth_time
|
||||
src.growth_probability = growth_probability
|
||||
src.lower_growth_value = lower_growth_value
|
||||
src.upper_growth_value = upper_growth_value
|
||||
src.optional_checks = optional_checks
|
||||
src.optional_grow_behavior = optional_grow_behavior
|
||||
|
||||
// If we haven't started the round, we can't do timer stuff. Let's wait in case we're mapped in or something.
|
||||
if(!SSticker.HasRoundStarted() && !isnull(growth_time))
|
||||
RegisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING, PROC_REF(comp_on_round_start))
|
||||
return
|
||||
|
||||
return setup_growth_tracking()
|
||||
|
||||
/datum/component/growth_and_differentiation/Destroy(force, silent)
|
||||
. = ..()
|
||||
deltimer(timer_id)
|
||||
|
||||
/datum/component/growth_and_differentiation/UnregisterFromParent()
|
||||
UnregisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING)
|
||||
|
||||
/// What we invoke when the round starts so we can set up our timer.
|
||||
/datum/component/growth_and_differentiation/proc/comp_on_round_start()
|
||||
SIGNAL_HANDLER
|
||||
setup_growth_tracking()
|
||||
UnregisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING)
|
||||
|
||||
/// Sets up the failover timer for certain growth.
|
||||
/datum/component/growth_and_differentiation/proc/setup_growth_tracking()
|
||||
var/did_we_add_at_least_one_thing = FALSE
|
||||
|
||||
if(!isnull(growth_time))
|
||||
timer_id = addtimer(CALLBACK(src, PROC_REF(grow), FALSE), growth_time, TIMER_STOPPABLE)
|
||||
if(!isnull(timer_id)) // realistically shouldn't happen considering how hardy addtimer() is but you can never be too sure
|
||||
did_we_add_at_least_one_thing = TRUE
|
||||
|
||||
if(!isnull(growth_probability))
|
||||
START_PROCESSING(SSdcs, src)
|
||||
did_we_add_at_least_one_thing = TRUE
|
||||
|
||||
if(!did_we_add_at_least_one_thing)
|
||||
stack_trace("Growth and Differentiation Component: Neither growth time nor probability were set! This component is useless!")
|
||||
return COMPONENT_INCOMPATIBLE // if we're invoked via COMSIG_TICKER_ROUND_STARTING this won't do anything (and shouldn't be invoked since we nullcheck growth_time before adding that signal anyways)
|
||||
|
||||
return null // just for explicitness's sake, if they ever change Component's Initialize to have more return values make sure this is the one for "Success!"
|
||||
|
||||
/datum/component/growth_and_differentiation/process(seconds_per_tick) // check the prob we were passed in, and if we're lucky, grow!
|
||||
if(ready_to_grow)
|
||||
INVOKE_ASYNC(src, PROC_REF(grow), FALSE)
|
||||
return
|
||||
|
||||
if(percent_grown >= 100)
|
||||
ready_to_grow = TRUE
|
||||
INVOKE_ASYNC(src, PROC_REF(grow), FALSE) // lets not waste any more of SSmobs time this tick.
|
||||
return
|
||||
|
||||
if(SPT_PROB(growth_probability, seconds_per_tick))
|
||||
percent_grown += rand(lower_growth_value, upper_growth_value)
|
||||
|
||||
/// Grows the mob into its new form.
|
||||
/datum/component/growth_and_differentiation/proc/grow(silent)
|
||||
if(!isnull(optional_checks) && !optional_checks.Invoke()) // we failed our checks somehow, but we're still ready to grow. Let's wait until next tick to see if our circumstances have changed.
|
||||
ready_to_grow = TRUE
|
||||
return
|
||||
|
||||
var/mob/living/old_mob = parent
|
||||
if (old_mob.stat == DEAD)
|
||||
qdel(src) // assume that we are priced out of growth once dead
|
||||
return
|
||||
|
||||
STOP_PROCESSING(SSdcs, src)
|
||||
|
||||
if(!isnull(optional_grow_behavior)) // basically growth_path is OK to be null but only if we have an optional grow behavior.
|
||||
optional_grow_behavior.Invoke()
|
||||
return
|
||||
|
||||
var/mob/living/new_mob = growth_path
|
||||
if(!istype(new_mob))
|
||||
CRASH("Growth and Differentiation Component: Growth path was not a mob type! If you wanted to do something special, please put it in the optional_grow_behavior callback instead!")
|
||||
|
||||
var/new_mob_name = initial(new_mob.name)
|
||||
|
||||
if(!silent)
|
||||
old_mob.visible_message(span_warning("[old_mob] grows into \a [new_mob_name]!"))
|
||||
|
||||
old_mob.change_mob_type(growth_path, old_mob.loc, new_name = new_mob_name, delete_old_mob = TRUE)
|
||||
@@ -101,132 +101,6 @@
|
||||
else if(isprojectile(mover))
|
||||
return prob(30)
|
||||
|
||||
/obj/structure/spider/spiderling
|
||||
name = "spiderling"
|
||||
desc = "It never stays still for long."
|
||||
icon_state = "spiderling"
|
||||
anchored = FALSE
|
||||
layer = PROJECTILE_HIT_THRESHHOLD_LAYER
|
||||
max_integrity = 3
|
||||
var/amount_grown = 0
|
||||
var/grow_as = null
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/entry_vent
|
||||
var/travelling_in_vent = 0
|
||||
var/directive = "" //Message from the mother
|
||||
var/list/faction = list(FACTION_SPIDER)
|
||||
|
||||
/obj/structure/spider/spiderling/Destroy()
|
||||
new/obj/item/food/spiderling(get_turf(src))
|
||||
. = ..()
|
||||
|
||||
/obj/structure/spider/spiderling/Initialize(mapload)
|
||||
. = ..()
|
||||
pixel_x = rand(6,-6)
|
||||
pixel_y = rand(6,-6)
|
||||
START_PROCESSING(SSobj, src)
|
||||
AddComponent(/datum/component/swarming)
|
||||
|
||||
/obj/structure/spider/spiderling/hunter
|
||||
grow_as = /mob/living/basic/giant_spider/hunter
|
||||
|
||||
/obj/structure/spider/spiderling/nurse
|
||||
grow_as = /mob/living/basic/giant_spider/nurse
|
||||
|
||||
/obj/structure/spider/spiderling/midwife
|
||||
grow_as = /mob/living/basic/giant_spider/midwife
|
||||
|
||||
/obj/structure/spider/spiderling/viper
|
||||
grow_as = /mob/living/basic/giant_spider/viper
|
||||
|
||||
/obj/structure/spider/spiderling/tarantula
|
||||
grow_as = /mob/living/basic/giant_spider/tarantula
|
||||
|
||||
/obj/structure/spider/spiderling/Bump(atom/user)
|
||||
if(istype(user, /obj/structure/table))
|
||||
forceMove(user.loc)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/structure/spider/spiderling/proc/cancel_vent_move()
|
||||
forceMove(entry_vent)
|
||||
entry_vent = null
|
||||
|
||||
/obj/structure/spider/spiderling/proc/vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent)
|
||||
if(QDELETED(exit_vent) || exit_vent.welded)
|
||||
cancel_vent_move()
|
||||
return
|
||||
|
||||
forceMove(exit_vent)
|
||||
var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
|
||||
addtimer(CALLBACK(src, PROC_REF(do_vent_move), exit_vent, travel_time), travel_time)
|
||||
|
||||
/obj/structure/spider/spiderling/proc/do_vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent, travel_time)
|
||||
if(QDELETED(exit_vent) || exit_vent.welded)
|
||||
cancel_vent_move()
|
||||
return
|
||||
|
||||
if(prob(50))
|
||||
audible_message(span_hear("You hear something scampering through the ventilation ducts."))
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(finish_vent_move), exit_vent), travel_time)
|
||||
|
||||
/obj/structure/spider/spiderling/proc/finish_vent_move(obj/machinery/atmospherics/components/unary/vent_pump/exit_vent)
|
||||
if(QDELETED(exit_vent) || exit_vent.welded)
|
||||
cancel_vent_move()
|
||||
return
|
||||
forceMove(exit_vent.loc)
|
||||
entry_vent = null
|
||||
|
||||
/obj/structure/spider/spiderling/process()
|
||||
if(travelling_in_vent)
|
||||
if(isturf(loc))
|
||||
travelling_in_vent = 0
|
||||
entry_vent = null
|
||||
else if(entry_vent)
|
||||
if(get_dist(src, entry_vent) <= 1)
|
||||
var/list/vents = list()
|
||||
var/datum/pipeline/entry_vent_parent = entry_vent.parents[1]
|
||||
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in entry_vent_parent.other_atmos_machines)
|
||||
vents.Add(temp_vent)
|
||||
if(!vents.len)
|
||||
entry_vent = null
|
||||
return
|
||||
var/obj/machinery/atmospherics/components/unary/vent_pump/exit_vent = pick(vents)
|
||||
if(prob(50))
|
||||
visible_message("<B>[src] scrambles into the ventilation ducts!</B>", \
|
||||
span_hear("You hear something scampering through the ventilation ducts."))
|
||||
|
||||
addtimer(CALLBACK(src, PROC_REF(vent_move), exit_vent), rand(20,60))
|
||||
|
||||
//=================
|
||||
|
||||
else if(prob(33))
|
||||
var/list/nearby = oview(10, src)
|
||||
if(nearby.len)
|
||||
var/target_atom = pick(nearby)
|
||||
SSmove_manager.move_to(src, target_atom)
|
||||
if(prob(40))
|
||||
src.visible_message(span_notice("\The [src] skitters[pick(" away"," around","")]."))
|
||||
else if(prob(10))
|
||||
//ventcrawl!
|
||||
for(var/obj/machinery/atmospherics/components/unary/vent_pump/v in view(7,src))
|
||||
if(!v.welded)
|
||||
entry_vent = v
|
||||
SSmove_manager.move_to(src, entry_vent, 1)
|
||||
break
|
||||
if(isturf(loc))
|
||||
amount_grown += rand(0,2)
|
||||
if(amount_grown >= 100)
|
||||
if(!grow_as)
|
||||
if(prob(3))
|
||||
grow_as = pick(/mob/living/basic/giant_spider/tarantula, /mob/living/basic/giant_spider/viper, /mob/living/basic/giant_spider/midwife)
|
||||
else
|
||||
grow_as = pick(/mob/living/basic/giant_spider, /mob/living/basic/giant_spider/hunter, /mob/living/basic/giant_spider/nurse)
|
||||
var/mob/living/basic/giant_spider/S = new grow_as(src.loc)
|
||||
S.faction = faction.Copy()
|
||||
S.directive = directive
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/spider/cocoon
|
||||
name = "cocoon"
|
||||
desc = "Something wrapped in silky spider web."
|
||||
|
||||
@@ -855,7 +855,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
/mob/living/simple_animal/butterfly,
|
||||
/mob/living/basic/cockroach,
|
||||
/obj/item/queen_bee,
|
||||
/obj/structure/spider/spiderling,
|
||||
/mob/living/basic/spiderling,
|
||||
/mob/living/simple_animal/hostile/ant,
|
||||
/obj/effect/decal/cleanable/ants,
|
||||
))
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
/obj/item/organ/internal/heart/gland/spiderman/activate()
|
||||
to_chat(owner, span_warning("You feel something crawling in your skin."))
|
||||
owner.faction |= FACTION_SPIDER
|
||||
var/obj/structure/spider/spiderling/S = new(owner.drop_location())
|
||||
var/mob/living/basic/spiderling/S = new(owner.drop_location())
|
||||
S.directive = "Protect your nest inside [owner.real_name]."
|
||||
|
||||
@@ -822,7 +822,7 @@
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] last altered a hydro tray's contents which spawned spiderlings.")
|
||||
user.log_message("last altered a hydro tray, which spiderlings spawned from.", LOG_GAME)
|
||||
visible_message(span_warning("The pests seem to behave oddly..."))
|
||||
spawn_atom_to_turf(/obj/structure/spider/spiderling/hunter, src, 3, FALSE)
|
||||
spawn_atom_to_turf(/mob/living/basic/spiderling/hunter, src, 3, FALSE)
|
||||
else if(myseed)
|
||||
visible_message(span_warning("The pests seem to behave oddly in [myseed.name] tray, but quickly settle down..."))
|
||||
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@
|
||||
if(!message)
|
||||
return
|
||||
var/my_message = span_spider("<b>Command from [user]:</b> [message]")
|
||||
for(var/mob/living/basic/giant_spider/spider as anything in GLOB.spidermobs)
|
||||
for(var/mob/living/basic/spider as anything in GLOB.spidermobs)
|
||||
to_chat(spider, my_message)
|
||||
for(var/ghost in GLOB.dead_mob_list)
|
||||
var/link = FOLLOW_LINK(ghost, user)
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* # Spiderlings
|
||||
*
|
||||
* Baby spiders that are generated through a variety of means (like botany for instance).
|
||||
* Able to vent-crawl and eventually grow into a full fledged giant spider.
|
||||
*
|
||||
*/
|
||||
/mob/living/basic/spiderling
|
||||
name = "spiderling"
|
||||
desc = "It never stays still for long."
|
||||
icon_state = "spiderling"
|
||||
icon_dead = "spiderling_dead"
|
||||
faction = list(FACTION_SPIDER)
|
||||
speed = 1
|
||||
move_resist = INFINITY // YOU CAN'T HANDLE ME LET ME BE FREE LET ME BE FREE LET ME BE FREE
|
||||
speak_emote = list("hisses")
|
||||
basic_mob_flags = FLAMMABLE_MOB | DEL_ON_DEATH
|
||||
mob_size = MOB_SIZE_TINY
|
||||
|
||||
unique_name = TRUE
|
||||
gold_core_spawnable = HOSTILE_SPAWN // because of what we grow into!
|
||||
|
||||
// we have _some_ bite
|
||||
melee_damage_lower = 2
|
||||
melee_damage_upper = 4
|
||||
|
||||
health = 5 // very low.
|
||||
maxHealth = 5
|
||||
unsuitable_cold_damage = 4
|
||||
unsuitable_heat_damage = 4
|
||||
death_message = "lets out a final hiss..."
|
||||
|
||||
ai_controller = /datum/ai_controller/basic_controller/spiderling
|
||||
|
||||
// VERY red, to fit the eyes
|
||||
lighting_cutoff_red = 22
|
||||
lighting_cutoff_green = 5
|
||||
lighting_cutoff_blue = 5
|
||||
|
||||
/// The mob we will grow into.
|
||||
var/mob/living/basic/giant_spider/grow_as = null
|
||||
/// The message that the mother left for our big strong selves.
|
||||
var/directive = ""
|
||||
/// Simple boolean that determines if we should apply the spider antag to the player if they possess this mob. TRUE by default since we're always going to evolve into a spider that will have an antagonistic role.
|
||||
var/apply_spider_antag = TRUE
|
||||
|
||||
/mob/living/basic/spiderling/Initialize(mapload)
|
||||
. = ..()
|
||||
// random placement since we're pretty small and to make the swarming component actually look like it's doing something when we have a buncha these fuckers
|
||||
pixel_x = rand(6,-6)
|
||||
pixel_y = rand(6,-6)
|
||||
|
||||
// the proc that handles passtable is nice but we should always be able to pass through table since we're so small so we can eschew adding that here
|
||||
pass_flags |= PASSTABLE
|
||||
ADD_TRAIT(src, TRAIT_PASSTABLE, INNATE_TRAIT)
|
||||
|
||||
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
|
||||
AddComponent(/datum/component/swarming)
|
||||
AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW, volume = 0.2) // they're small but you can hear 'em
|
||||
|
||||
// it's A-OKAY for grow_as to be null for the purposes of this component since we override that behavior anyhow.
|
||||
AddComponent(\
|
||||
/datum/component/growth_and_differentiation,\
|
||||
growth_time = 10 MINUTES,\
|
||||
growth_path = grow_as,\
|
||||
growth_probability = 25,\
|
||||
lower_growth_value = 1,\
|
||||
upper_growth_value = 2,\
|
||||
optional_checks = CALLBACK(src, PROC_REF(ready_to_grow)),\
|
||||
optional_grow_behavior = CALLBACK(src, PROC_REF(grow_into_giant_spider))\
|
||||
)
|
||||
|
||||
// keep in mind we have infinite range (the entire pipenet is our playground, it's just a matter of random choice as to where we end up) so lower and upper both have their gives and takes.
|
||||
// but, also remember the more time we aren't in a vent, the more susceptible we are to dying to anything and everything.
|
||||
// also remember we can't evolve if we're in a vent. lots to keep in mind when you set these variables.
|
||||
ai_controller.set_blackboard_key(BB_LOWER_VENT_TIME_LIMIT, rand(9, 11) SECONDS)
|
||||
ai_controller.set_blackboard_key(BB_UPPER_VENT_TIME_LIMIT, rand(12, 14) SECONDS)
|
||||
|
||||
/mob/living/basic/spiderling/Destroy()
|
||||
GLOB.spidermobs -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/spiderling/death(gibbed)
|
||||
if(isturf(get_turf(loc)) && (basic_mob_flags & DEL_ON_DEATH || gibbed))
|
||||
var/obj/item/food/spiderling/dead_spider = new(loc) // mmm yummy
|
||||
dead_spider.name = name
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/spiderling/Login() // this is only really here for admins dragging and dropping players into spiderlings, player control of spiderlings is otherwise unimplemented
|
||||
. = ..()
|
||||
if(!. || isnull(client))
|
||||
return FALSE
|
||||
basic_mob_flags &= ~DEL_ON_DEATH // we don't want to be deleted if we die while player controlled in case there's some revive schenanigans going on that can bring us back
|
||||
GLOB.spidermobs[src] = TRUE
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/player_spider_modifier, multiplicative_slowdown = -2) // let's pick up the tempo, we are meant to be fast after all
|
||||
if (apply_spider_antag)
|
||||
var/datum/antagonist/spider/spider_antag = new(directive)
|
||||
mind.add_antag_datum(spider_antag)
|
||||
|
||||
/mob/living/basic/spiderling/Logout()
|
||||
. = ..()
|
||||
remove_movespeed_modifier(/datum/movespeed_modifier/player_spider_modifier)
|
||||
|
||||
/mob/living/basic/spiderling/mob_negates_gravity() // in case our sisters want to give us a helping hand
|
||||
if(locate(/obj/structure/spider/stickyweb) in loc)
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/spiderling/start_pulling(atom/movable/pulled_atom, state, force = move_force, supress_message = FALSE) // we're TOO FUCKING SMALL
|
||||
return
|
||||
|
||||
/// Checks to see if we're ready to grow, primarily if we are on solid ground and not in a vent or something.
|
||||
/// The component will automagically grow us when we return TRUE and that threshold has been met.
|
||||
/mob/living/basic/spiderling/proc/ready_to_grow()
|
||||
if(isturf(loc))
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/// Actually grows the spiderling into a giant spider. We have to do a bunch of unique behavior that really can't be genericized, so we have to override the component in this manner.
|
||||
/mob/living/basic/spiderling/proc/grow_into_giant_spider()
|
||||
if(isnull(grow_as))
|
||||
if(prob(3))
|
||||
grow_as = pick(/mob/living/basic/giant_spider/tarantula, /mob/living/basic/giant_spider/viper, /mob/living/basic/giant_spider/midwife)
|
||||
else
|
||||
grow_as = pick(/mob/living/basic/giant_spider, /mob/living/basic/giant_spider/hunter, /mob/living/basic/giant_spider/nurse)
|
||||
|
||||
var/mob/living/basic/giant_spider/grown = change_mob_type(grow_as, get_turf(src), initial(grow_as.name))
|
||||
ADD_TRAIT(grown, TRAIT_WAS_EVOLVED, REF(src))
|
||||
grown.faction = faction.Copy()
|
||||
grown.directive = directive
|
||||
|
||||
qdel(src)
|
||||
|
||||
/// Opportunistically hops in and out of vents, if it can find one. We aren't interested in attacking due to how weak we are, we gotta be quick and hidey.
|
||||
/datum/ai_controller/basic_controller/spiderling
|
||||
blackboard = list(
|
||||
BB_FLEE_TARGETTING_DATUM = new /datum/targetting_datum/basic/of_size/larger, // Run away from mobs bigger than we are
|
||||
BB_BASIC_MOB_FLEEING = TRUE,
|
||||
BB_VENTCRAWL_COOLDOWN = 20 SECONDS, // enough time to get splatted while we're out in the open.
|
||||
BB_TIME_TO_GIVE_UP_ON_VENT_PATHING = 30 SECONDS,
|
||||
)
|
||||
|
||||
ai_traits = STOP_MOVING_WHEN_PULLED
|
||||
ai_movement = /datum/ai_movement/basic_avoidance
|
||||
idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking
|
||||
|
||||
// We understand that vents are nice little hidey holes through epigenetic inheritance, so we'll use them.
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/target_retaliate/to_flee,
|
||||
/datum/ai_planning_subtree/flee_target/from_flee_key,
|
||||
/datum/ai_planning_subtree/opportunistic_ventcrawler,
|
||||
/datum/ai_planning_subtree/random_speech/insect,
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
// This whole file is just a container for the spiderling subtypes that actually differentiate into different giant spiders. None of them are particularly special as of now.
|
||||
|
||||
/// Will differentiate into the base giant spider (known colloquially as the "guard" spider).
|
||||
/mob/living/basic/spiderling/guard
|
||||
grow_as = /mob/living/basic/giant_spider
|
||||
|
||||
/// Will differentiate into the "hunter" giant spider.
|
||||
/mob/living/basic/spiderling/hunter
|
||||
grow_as = /mob/living/basic/giant_spider/hunter
|
||||
|
||||
/// Will differentiate into the "nurse" giant spider.
|
||||
/mob/living/basic/spiderling/nurse
|
||||
grow_as = /mob/living/basic/giant_spider/nurse
|
||||
|
||||
/// Will differentiate into the "midwife" giant spider.
|
||||
/mob/living/basic/spiderling/midwife
|
||||
grow_as = /mob/living/basic/giant_spider/midwife
|
||||
|
||||
/// Will differentiate into the "viper" giant spider.
|
||||
/mob/living/basic/spiderling/viper
|
||||
grow_as = /mob/living/basic/giant_spider/viper
|
||||
|
||||
/// Will differentiate into the "tarantula" giant spider.
|
||||
/mob/living/basic/spiderling/tarantula
|
||||
grow_as = /mob/living/basic/giant_spider/tarantula
|
||||
@@ -52,10 +52,12 @@
|
||||
if(!can_enter_vent(ventcrawl_target))
|
||||
return
|
||||
|
||||
var/has_client = !isnull(client) // clientless mobs can do this too! this is just stored in case the client disconnects while we sleep in do_after.
|
||||
|
||||
//Handle the exit here
|
||||
if(HAS_TRAIT(src, TRAIT_MOVE_VENTCRAWLING) && istype(loc, /obj/machinery/atmospherics) && movement_type & VENTCRAWLING)
|
||||
visible_message(span_notice("[src] begins climbing out from the ventilation system...") ,span_notice("You begin climbing out from the ventilation system..."))
|
||||
if(!client)
|
||||
visible_message(span_notice("[src] begins climbing out from the ventilation system..."), span_notice("You begin climbing out from the ventilation system..."))
|
||||
if(has_client && isnull(client))
|
||||
return
|
||||
visible_message(span_notice("[src] scrambles out from the ventilation ducts!"),span_notice("You scramble out from the ventilation ducts."))
|
||||
forceMove(ventcrawl_target.loc)
|
||||
@@ -70,7 +72,7 @@
|
||||
visible_message(span_notice("[src] begins climbing into the ventilation system...") ,span_notice("You begin climbing into the ventilation system..."))
|
||||
if(!do_after(src, 2.5 SECONDS, target = ventcrawl_target, extra_checks = CALLBACK(src, PROC_REF(can_enter_vent), ventcrawl_target)))
|
||||
return
|
||||
if(!client)
|
||||
if(has_client && isnull(client))
|
||||
return
|
||||
ventcrawl_target.flick_overlay_static(image('icons/effects/vent_indicator.dmi', "insert", ABOVE_MOB_LAYER), 1 SECONDS)
|
||||
visible_message(span_notice("[src] scrambles into the ventilation ducts!"),span_notice("You climb into the ventilation ducts."))
|
||||
@@ -103,6 +105,8 @@
|
||||
* We move first and then call update. Dont flip this around
|
||||
*/
|
||||
/mob/living/proc/update_pipe_vision(full_refresh = FALSE)
|
||||
if(!isnull(ai_controller) && isnull(client)) // we don't care about pipe vision if we have an AI controller with no client (typically means we are clientless).
|
||||
return
|
||||
|
||||
// Take away all the pipe images if we're not doing anything with em
|
||||
if(isnull(client) || !HAS_TRAIT(src, TRAIT_MOVE_VENTCRAWLING) || !istype(loc, /obj/machinery/atmospherics) || !(movement_type & VENTCRAWLING))
|
||||
|
||||
@@ -2,7 +2,6 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
|
||||
/mob/living,
|
||||
/obj/structure/blob,
|
||||
/obj/effect/rune,
|
||||
/obj/structure/spider/spiderling,
|
||||
/obj/item/disk/nuclear,
|
||||
/obj/machinery/nuclearbomb,
|
||||
/obj/item/beacon,
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 928 KiB After Width: | Height: | Size: 851 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 222 KiB |
+13
-8
@@ -843,9 +843,11 @@
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\targetting.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\travel_towards.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_ai_behaviors\ventcrawling.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\attack_obstacle_in_path.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\find_food.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\flee_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\opportunistic_ventcrawler.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_nearest_target_to_flee.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_target.dm"
|
||||
@@ -1000,6 +1002,7 @@
|
||||
#include "code\datums\components\gps.dm"
|
||||
#include "code\datums\components\grillable.dm"
|
||||
#include "code\datums\components\ground_sinking.dm"
|
||||
#include "code\datums\components\growth_and_differentiation.dm"
|
||||
#include "code\datums\components\gunpoint.dm"
|
||||
#include "code\datums\components\hazard_area.dm"
|
||||
#include "code\datums\components\healing_touch.dm"
|
||||
@@ -4015,14 +4018,6 @@
|
||||
#include "code\modules\mob\living\basic\space_fauna\carp\carp_controllers.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\carp\magicarp.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\carp\megacarp.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\giant_spider.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\giant_spider_ai.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_subtrees.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_variants.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\hivemind.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\lay_eggs.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\web.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\wrap.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\chasing_spikes.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\meteor_eyeball.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\meteor_heart.dm"
|
||||
@@ -4031,6 +4026,16 @@
|
||||
#include "code\modules\mob\living\basic\space_fauna\netherworld\blankbody.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\netherworld\creature.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\netherworld\migo.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\giant_spider\giant_spider.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\giant_spider\giant_spider_ai.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\giant_spider\giant_spider_subtrees.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\giant_spider\giant_spider_variants.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\spider_abilities\hivemind.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\spider_abilities\lay_eggs.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\spider_abilities\web.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\spider_abilities\wrap.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\spiderlings\spiderling.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\spider\spiderlings\spiderling_subtypes.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\statue\statue.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\wumborian_fugu\fugu_gland.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\wumborian_fugu\inflation.dm"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/obj/structure/spider/spiderling/@SUBTYPES : /mob/living/basic/spiderling/@SUBTYPES{@OLD}
|
||||
Reference in New Issue
Block a user