Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Port] The big spider project #182

Merged
merged 10 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions code/__DEFINES/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,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"

///List of mobs who have damaged us
#define BB_BASIC_MOB_RETALIATE_LIST "BB_basic_mob_shitlist"

Expand Down
3 changes: 2 additions & 1 deletion code/__DEFINES/span.dm
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@
#define span_small(str) ("<span class='small'>" + str + "</span>")
#define span_smallnotice(str) ("<span class='smallnotice'>" + str + "</span>")
#define span_smallnoticeital(str) ("<span class='smallnoticeital'>" + str + "</span>")
#define span_spider(str) ("<span class='spider'>" + str + "</span>")
#define span_spiderbroodmother(str) ("<span class='spiderbroodmother'>" + str + "</span>")
#define span_spiderscout(str) ("<span class='spiderscout'>" + str + "</span>")
#define span_suicide(str) ("<span class='suicide'>" + str + "</span>")
#define span_suppradio(str) ("<span class='suppradio'>" + str + "</span>")
#define span_syndradio(str) ("<span class='syndradio'>" + str + "</span>")
Expand Down
11 changes: 8 additions & 3 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_RESEARCH_SCANNER "research_scanner"
/// Can weave webs into cloth
#define TRAIT_WEB_WEAVER "web_weaver"
/// Can navigate the web without getting stuck
#define TRAIT_WEB_SURFER "web_surfer"
/// A web is being spun on this turf presently
#define TRAIT_SPINNING_WEB_TURF "spinning_web_turf"
#define TRAIT_ABDUCTOR_TRAINING "abductor-training"
#define TRAIT_ABDUCTOR_SCIENTIST_TRAINING "abductor-scientist-training"
#define TRAIT_SURGEON "surgeon"
Expand Down Expand Up @@ -461,9 +465,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_ORBITING_FORBIDDEN "orbiting_forbidden"
/// Trait applied to mob/living to mark that spiders should not gain further enriched eggs from eating their corpse.
#define TRAIT_SPIDER_CONSUMED "spider_consumed"
/// Whether we're sneaking, from the alien sneak ability.
/// Maybe worth generalizing into a general "is sneaky" / "is stealth" trait in the future.
#define TRAIT_ALIEN_SNEAK "sneaking_alien"
/// Whether we're sneaking, from the creature sneak ability.
#define TRAIT_SNEAK "sneaking_creatures"

/// Item still allows you to examine items while blind and actively held.
#define TRAIT_BLIND_TOOL "blind_tool"
Expand Down Expand Up @@ -1027,6 +1030,8 @@ 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.
Expand Down
1 change: 1 addition & 0 deletions code/_globalvars/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NOMOBSWAP" = TRAIT_NOMOBSWAP,
"TRAIT_XRAY_VISION" = TRAIT_XRAY_VISION,
"TRAIT_WEB_WEAVER" = TRAIT_WEB_WEAVER,
"TRAIT_WEB_SURFER" = TRAIT_WEB_SURFER,
"TRAIT_THERMAL_VISION" = TRAIT_THERMAL_VISION,
"TRAIT_ABDUCTOR_TRAINING" = TRAIT_ABDUCTOR_TRAINING,
"TRAIT_ABDUCTOR_SCIENTIST_TRAINING" = TRAIT_ABDUCTOR_SCIENTIST_TRAINING,
Expand Down
1 change: 1 addition & 0 deletions code/datums/actions/mobs/charge.dm
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
cooldown_time = 6 SECONDS
charge_delay = 1.5 SECONDS
charge_distance = 4
melee_cooldown_time = 0
/// How long to shake for before charging
var/shake_duration = 1 SECONDS
/// Intensity of shaking animation
Expand Down
32 changes: 32 additions & 0 deletions code/datums/actions/mobs/sneak.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/datum/action/cooldown/sneak
name = "Sneak"
desc = "Sneak into the enviorment."
button_icon = 'icons/mob/actions/actions_animal.dmi'
button_icon_state = "sniper_zoom"
background_icon_state = "bg_alien"
overlay_icon_state = "bg_alien_border"
check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED | AB_CHECK_INCAPACITATED
/// The alpha we go to when sneaking.
var/sneak_alpha = 75

/datum/action/cooldown/sneak/Remove(mob/living/remove_from)
if(HAS_TRAIT(remove_from, TRAIT_SNEAK))
remove_from.alpha = initial(remove_from.alpha)
REMOVE_TRAIT(remove_from, TRAIT_SNEAK, name)

return ..()

/datum/action/cooldown/sneak/Activate(atom/target)
if(HAS_TRAIT(owner, TRAIT_SNEAK))
// It's safest to go to the initial alpha of the mob.
// Otherwise we get permanent invisbility exploits.
owner.alpha = initial(owner.alpha)
to_chat(owner, span_noticealien("You reveal yourself!"))
REMOVE_TRAIT(owner, TRAIT_SNEAK, name)

else
owner.alpha = sneak_alpha
to_chat(owner, span_noticealien("You blend into the enviorment..."))
ADD_TRAIT(owner, TRAIT_SNEAK, name)

return TRUE
137 changes: 137 additions & 0 deletions code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/// 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
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading