[MIRROR] Station Trait: Spider Infestation [MDB IGNORE] (#19813)

* Station Trait: Spider Infestation (#73893)

## About The Pull Request

Hate having your cables eaten by mice? Nanotrasen have heard your
complaints and settled on a natural, _organic_, and eco-friendly
solution.

When this station trait is active, roundstart and event mouse spawns
have a chance to instead be replaced with duct spiders (both will exist,
it doesn't remove mice).
Duct spiders are largely harmless to humans, actively hunt other
maintenance creatures (such as mice), and have only one _tiny_ downside.

![image](https://user-images.githubusercontent.com/7483112/224345781-2627be98-67f2-4cab-ac40-c6c9b35ea909.png)

These mobs can also sometimes be spawned by a minor scrubber clog event.

As a side note, all spider basic mobs with AI (except Araneus) will now
try to automatically fill a small area around them with webs.

Also I made it so that mobs will ignore their random_walking behaviour
if they're engaged in a `do_after`, just in case.

## Why It's Good For The Game

Adds a little bit of variety to things which can slightly annoy you in
maintenance.
Spiders will automatically make places they live in look like spiders
live there.

## Changelog

🆑
add: A station trait which sometimes populates maintenance with small
spiders. You can wear them as a hat if you wanted to have a spider on
your head for some reason.
add: Spider mobs will automatically start webbing up their environment.
/🆑

* Station Trait: Spider Infestation

---------

Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
SkyratBot
2023-03-13 03:00:22 +01:00
committed by GitHub
parent f31dbb9ccb
commit 42dd487728
19 changed files with 297 additions and 26 deletions
@@ -73,6 +73,7 @@
var/datum/action/cooldown/lay_web/webbing = new web_type(src)
webbing.webbing_time *= web_speed
webbing.Grant(src)
ai_controller.blackboard[BB_SPIDER_WEB_ACTION] = WEAKREF(webbing)
/mob/living/basic/giant_spider/Login()
. = ..()
@@ -1,4 +1,4 @@
/// For now, essentially just a Simple Hostile but room for expansion
/// Attacks people it can see, spins webs if it can't see anything to attack.
/datum/ai_controller/basic_controller/giant_spider
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic(),
@@ -13,6 +13,8 @@
/datum/ai_planning_subtree/attack_obstacle_in_path,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/random_speech/insect, // Space spiders are taxonomically insects not arachnids, don't DM me
/datum/ai_planning_subtree/find_unwebbed_turf,
/datum/ai_planning_subtree/spin_web,
)
/// Giant spider which won't attack structures
@@ -21,9 +23,11 @@
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/find_unwebbed_turf,
/datum/ai_planning_subtree/spin_web,
)
/// Used by Araneus, who only attacks those who attack first
/// Used by Araneus, who only attacks those who attack first. He is house-trained and will not web up the HoS office.
/datum/ai_controller/basic_controller/giant_spider/retaliate
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/ignore_faction(),
@@ -35,3 +39,22 @@
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/random_speech/insect,
)
/// Retaliates, hunts other maintenance creatures, runs away from larger attackers, and spins webs.
/datum/ai_controller/basic_controller/giant_spider/pest
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/of_size/ours_or_smaller(), // Hunt mobs our size
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,
)
idle_behavior = /datum/idle_behavior/idle_random_walk
planning_subtrees = list(
/datum/ai_planning_subtree/target_retaliate/to_flee,
/datum/ai_planning_subtree/flee_target/from_flee_key,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/random_speech/insect,
/datum/ai_planning_subtree/find_unwebbed_turf,
/datum/ai_planning_subtree/spin_web,
)
@@ -55,7 +55,7 @@
if(do_after(owner, webbing_time, target = spider_turf, interaction_key = DOAFTER_SOURCE_SPIDER) && owner.loc == spider_turf)
plant_web(spider_turf, web)
else
owner.balloon_alert(owner, "interrupted!")
owner?.balloon_alert(owner, "interrupted!") // Null check because we might have been interrupted via being disintegrated
build_all_button_icons()
/// Creates a web in the current turf
@@ -0,0 +1,91 @@
/// Search for a nearby location to put webs on
/datum/ai_planning_subtree/find_unwebbed_turf
/datum/ai_planning_subtree/find_unwebbed_turf/SelectBehaviors(datum/ai_controller/controller, delta_time)
controller.queue_behavior(/datum/ai_behavior/find_unwebbed_turf)
/// Find an unwebbed nearby turf and store it
/datum/ai_behavior/find_unwebbed_turf
action_cooldown = 5 SECONDS
/// Where do we store the target data
var/target_key = BB_SPIDER_WEB_TARGET
/// How far do we look for unwebbed turfs?
var/scan_range = 3
/datum/ai_behavior/find_unwebbed_turf/perform(delta_time, datum/ai_controller/controller)
. = ..()
var/mob/living/spider = controller.pawn
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/atom/current_target = weak_target?.resolve()
if (current_target && !(locate(/obj/structure/spider/stickyweb) in current_target))
finish_action(controller, succeeded = FALSE) // Already got a target
return
controller.blackboard[target_key] = null
var/turf/our_turf = get_turf(spider)
if (is_valid_web_turf(our_turf))
controller.blackboard[target_key] = WEAKREF(our_turf)
finish_action(controller, succeeded = TRUE)
return
var/list/potential_turfs = list()
for(var/turf/turf_in_view in oview(scan_range, our_turf))
if (!is_valid_web_turf(turf_in_view))
continue
potential_turfs += turf_in_view
if (!length(potential_turfs))
finish_action(controller, succeeded = FALSE)
return
controller.blackboard[target_key] = WEAKREF(get_closest_atom(/turf/, potential_turfs, our_turf))
finish_action(controller, succeeded = TRUE)
/datum/ai_behavior/find_unwebbed_turf/proc/is_valid_web_turf(turf/target_turf, mob/living/spider)
if (locate(/obj/structure/spider/stickyweb) in target_turf)
return FALSE
return !target_turf.is_blocked_turf(source_atom = spider)
/// Run the spin web behaviour if we have an ability to use for it
/datum/ai_planning_subtree/spin_web
/// Key where the web spinning action is stored
var/action_key = BB_SPIDER_WEB_ACTION
/// Key where the target turf is stored
var/target_key = BB_SPIDER_WEB_TARGET
/datum/ai_planning_subtree/spin_web/SelectBehaviors(datum/ai_controller/controller, delta_time)
var/datum/weakref/weak_action = controller.blackboard[action_key]
var/datum/action/cooldown/using_action = weak_action?.resolve()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/turf/target_turf = weak_target?.resolve()
if (!using_action || !target_turf)
return
controller.queue_behavior(/datum/ai_behavior/spin_web, action_key, target_key)
return SUBTREE_RETURN_FINISH_PLANNING
/// Move to an unwebbed nearby turf and web it up
/datum/ai_behavior/spin_web
action_cooldown = 15 SECONDS // We don't want them doing this too quickly
required_distance = 0
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
/datum/ai_behavior/spin_web/setup(datum/ai_controller/controller, action_key, target_key)
var/datum/weakref/weak_action = controller.blackboard[action_key]
var/datum/action/cooldown/web_action = weak_action?.resolve()
var/datum/weakref/weak_target = controller.blackboard[target_key]
var/turf/target_turf = weak_target?.resolve()
if (!web_action || !target_turf)
return FALSE
set_movement_target(controller, target_turf)
return ..()
/datum/ai_behavior/spin_web/perform(delta_time, datum/ai_controller/controller, action_key, target_key)
. = ..()
var/datum/weakref/weak_action = controller.blackboard[action_key]
var/datum/action/cooldown/web_action = weak_action?.resolve()
finish_action(controller, succeeded = web_action?.Trigger(), action_key = action_key, target_key = target_key)
/datum/ai_behavior/spin_web/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key)
controller.blackboard[target_key] = null
return ..()
@@ -1,5 +1,5 @@
/**
* # Spider Hunter
* ### Spider Hunter
* A subtype of the giant spider which is faster, has toxin injection, but less health and damage.
* This spider is only slightly slower than a human.
*/
@@ -19,7 +19,7 @@
menu_description = "Fast spider variant specializing in catching running prey and toxin injection, but has less health and damage."
/**
* # Spider Nurse
* ### Spider Nurse
*
* A subtype of the giant spider which specializes in support skills.
* Nurses can place down webbing in a quarter of the time that other species can and can wrap other spiders' wounds, healing them.
@@ -56,7 +56,7 @@
)
/**
* # Tarantula
* ### Tarantula
*
* A subtype of the giant spider which specializes in pure strength and staying power.
* Is slowed down when not on webbing, but can lunge to throw off attackers and possibly to stun them.
@@ -97,7 +97,7 @@
charge.Trigger(target = atom_target)
/**
* # Spider Viper
* ### Spider Viper
*
* A subtype of the giant spider which specializes in speed and poison.
* Injects a deadlier toxin than other spiders, moves extremely fast, but has a limited amount of health.
@@ -120,7 +120,7 @@
menu_description = "Assassin spider variant with an unmatched speed and very deadly poison, but has very low amount of health and damage."
/**
* # Spider Broodmother
* ### Spider Broodmother
*
* A subtype of the giant spider which is the crux of a spider horde, and the way which it grows.
* Has very little offensive capabilities but can lay eggs at any time to create more basic spiders.
@@ -161,7 +161,7 @@
not_hivemind_talk.Grant(src)
/**
* # Giant Ice Spider
* ### Giant Ice Spider
*
* A subtype of the giant spider which is immune to temperature damage, unlike its normal counterpart.
* Currently unused in the game unless spawned by admins.
@@ -176,7 +176,7 @@
menu_description = "Versatile ice spider variant for frontline combat with high health and damage. Immune to temperature damage."
/**
* # Ice Nurse Spider
* ### Ice Nurse Spider
*
* A temperature-proof nurse spider. Also unused.
*/
@@ -190,7 +190,7 @@
menu_description = "Support ice spider variant specializing in healing their brethren and placing webbings very swiftly, but has very low amount of health and deals low damage. Immune to temperature damage."
/**
* # Ice Hunter Spider
* ### Ice Hunter Spider
*
* A temperature-proof hunter with chilling venom. Also unused.
*/
@@ -205,7 +205,7 @@
menu_description = "Fast ice spider variant specializing in catching running prey and frost oil injection, but has less health and damage. Immune to temperature damage."
/**
* # Scrawny Hunter Spider
* ### Scrawny Hunter Spider
*
* A hunter spider that trades damage for health, unable to smash enviroments.
* Used as a minor threat in abandoned places, such as areas in maintenance or a ruin.
@@ -221,7 +221,7 @@
ai_controller = /datum/ai_controller/basic_controller/giant_spider/weak
/**
* # Scrawny Tarantula
* ### Scrawny Tarantula
*
* A weaker version of the Tarantula, unable to smash enviroments.
* Used as a moderately strong but slow threat in abandoned places, such as areas in maintenance or a ruin.
@@ -237,7 +237,7 @@
ai_controller = /datum/ai_controller/basic_controller/giant_spider/weak
/**
* # Scrawny Nurse Spider
* ### Scrawny Nurse Spider
*
* A weaker version of the nurse spider with reduced health, unable to smash enviroments.
* Mainly used as a weak threat in abandoned places, such as areas in maintenance or a ruin.
@@ -252,7 +252,7 @@
ai_controller = /datum/ai_controller/basic_controller/giant_spider/weak
/**
* # Flesh Spider
* ### Flesh Spider
*
* A subtype of giant spider which only occurs from changelings.
* Has the base stats of a hunter, but they can heal themselves and spin webs faster.
@@ -291,7 +291,7 @@
return TRUE
/**
* # Viper Spider (Wizard)
* ### Viper Spider (Wizard)
*
* A spider form for wizards. Has the viper spider's extreme speed and strong venom, with additional health and vent crawling abilities.
*/
@@ -306,7 +306,7 @@
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
/**
* # Sergeant Araneus
* ### Sergeant Araneus
*
* This friendly arachnid hangs out in the HoS office on some space stations. Better trained than an average officer and does not attack except in self-defence.
*/
@@ -327,3 +327,42 @@
AddElement(/datum/element/pet_bonus, "chitters proudly!")
AddElement(/datum/element/ai_retaliate)
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
/**
* ### Duct Spider
*
* A less giant spider which lives in the maintenance ducts and makes them annoying to traverse.
*/
/mob/living/basic/giant_spider/maintenance
name = "duct spider"
desc = "Nanotrasen's imported solution to mice, comes with its own problems."
icon_state = "maint_spider"
icon_living = "maint_spider"
icon_dead = "maint_spider_dead"
can_be_held = TRUE
mob_size = MOB_SIZE_TINY
held_w_class = WEIGHT_CLASS_TINY
worn_slot_flags = ITEM_SLOT_HEAD
head_icon = 'icons/mob/clothing/head/pets_head.dmi'
density = FALSE
pass_flags = PASSTABLE|PASSGRILLE|PASSMOB
gold_core_spawnable = FRIENDLY_SPAWN
maxHealth = 10
health = 10
melee_damage_lower = 1
melee_damage_upper = 1
speed = 0
player_speed_modifier = 0
web_speed = 0.25
menu_description = "Fragile spider variant which is not good for much other than laying webs."
response_harm_continuous = "splats"
response_harm_simple = "splat"
ai_controller = /datum/ai_controller/basic_controller/giant_spider/pest
apply_spider_antag = FALSE
/mob/living/basic/giant_spider/maintenance/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
AddElement(/datum/element/web_walker, /datum/movespeed_modifier/duct_spider_web)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/tiny_mob_hunter)