mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-28 07:38:05 +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
@@ -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
|
||||
Reference in New Issue
Block a user