diff --git a/code/__DEFINES/mob_defines.dm b/code/__DEFINES/mob_defines.dm
index 722ff1172e6..39e0016bb4c 100644
--- a/code/__DEFINES/mob_defines.dm
+++ b/code/__DEFINES/mob_defines.dm
@@ -255,7 +255,7 @@
#define iscaterpillar(A) (istype((A), /mob/living/basic/nian_caterpillar))
#define ishostile(A) (istype((A), /mob/living/simple_animal/hostile))
#define isretaliate(A) (istype((A), /mob/living/simple_animal/hostile/retaliate))
-#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider))
+#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider) || istype((A), /mob/living/basic/spiderling/terror_spiderling))
#define isslaughterdemon(A) (istype((A), /mob/living/simple_animal/demon/slaughter))
#define isdemon(A) (istype((A), /mob/living/simple_animal/demon))
#define iscat(A) (istype((A), /mob/living/simple_animal/pet/cat))
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_find_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_find_target.dm
index 67dfc11e0ec..2a56c9cb934 100644
--- a/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_find_target.dm
+++ b/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_find_target.dm
@@ -1,4 +1,5 @@
/datum/ai_planning_subtree/ventcrawl_find_target
+ var/crawl_behavior = /datum/ai_behavior/find_ventcrawl_target
/datum/ai_planning_subtree/ventcrawl_find_target/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
// We have a target already
@@ -11,7 +12,7 @@
else
vision_range = controller.blackboard[BB_VENT_SEARCH_RANGE]
// Find a target
- controller.queue_behavior(/datum/ai_behavior/find_ventcrawl_target, BB_VENTCRAWL_FINAL_TARGET, vision_range)
+ controller.queue_behavior(crawl_behavior, BB_VENTCRAWL_FINAL_TARGET, vision_range)
return SUBTREE_RETURN_FINISH_PLANNING
/datum/ai_behavior/find_ventcrawl_target
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_subtree.dm b/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_subtree.dm
index b590cf7067e..37f70dcfc60 100644
--- a/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_subtree.dm
+++ b/code/datums/ai/basic_mobs/basic_subtrees/ventcrawl_subtree.dm
@@ -1,4 +1,5 @@
/datum/ai_planning_subtree/ventcrawl
+ var/ventcrawl_finding_behavior = /datum/ai_behavior/find_and_set/ventcrawl
/datum/ai_planning_subtree/ventcrawl/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
// we're in a pipe
@@ -15,7 +16,7 @@
return SUBTREE_RETURN_FINISH_PLANNING
// We dont have an exit vent, but we have a target. Let's find an exit vent near it.
if(controller.blackboard_key_exists(BB_VENTCRAWL_FINAL_TARGET))
- controller.queue_behavior(/datum/ai_behavior/find_and_set/ventcrawl, BB_VENTCRAWL_EXIT, null, controller.blackboard[BB_VENT_SEARCH_RANGE])
+ controller.queue_behavior(ventcrawl_finding_behavior, BB_VENTCRAWL_EXIT, null, controller.blackboard[BB_VENT_SEARCH_RANGE])
return
// we dont have an set exit vent nor a final target... let's just get out of the pipe.
controller.queue_behavior(/datum/ai_behavior/find_and_set/pipenet_vent, BB_VENTCRAWL_EXIT, null, controller.blackboard[BB_VENT_SEARCH_RANGE])
@@ -34,12 +35,15 @@
// we're far-away, lets try to find a ventcrawl path
if(!controller.blackboard_key_exists(BB_VENTCRAWL_EXIT))
- controller.queue_behavior(/datum/ai_behavior/find_and_set/ventcrawl, BB_VENTCRAWL_EXIT, null, controller.blackboard[BB_VENT_SEARCH_RANGE], FALSE)
+ controller.queue_behavior(ventcrawl_finding_behavior, BB_VENTCRAWL_EXIT, null, controller.blackboard[BB_VENT_SEARCH_RANGE], FALSE)
return
// we found a path, lets take it!
controller.queue_behavior(/datum/ai_behavior/interact_with_vent, BB_VENTCRAWL_ENTRANCE)
return SUBTREE_RETURN_FINISH_PLANNING
+/datum/ai_behavior/find_and_set/ventcrawl
+ var/list/searched_entry_types = list(/obj/machinery/atmospherics/unary/vent_pump, /obj/machinery/atmospherics/unary/vent_scrubber)
+
/**
* This proc assumes that all ventcrawling creatures are omniscient.
*/
@@ -103,7 +107,7 @@
var/turf/current = popleft(open_set)
closed_set |= current
for(var/obj/machinery/atmospherics/atmos in current)
- if(is_type_in_list(atmos, GLOB.ventcrawl_machinery) && atmos.can_crawl_through())
+ if(is_type_in_list(atmos, searched_entry_types) && atmos.can_crawl_through())
vents |= atmos
for(var/dir in GLOB.cardinal)
@@ -166,6 +170,6 @@
controller.set_blackboard_key(BB_VENTCRAWL_IS_ENTERING, TRUE)
L.handle_ventcrawl(vent)
controller.set_blackboard_key(BB_VENTCRAWL_IS_ENTERING, FALSE)
- if(controller.pawn.loc != vent)
+ if(QDELETED(controller) || controller.pawn.loc != vent)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm
index 1ea43104dbb..e69656bf740 100644
--- a/code/game/gamemodes/miniantags/abduction/gland.dm
+++ b/code/game/gamemodes/miniantags/abduction/gland.dm
@@ -257,7 +257,7 @@
/obj/item/organ/internal/heart/gland/spiderman/trigger()
to_chat(owner, "You feel something crawling in your skin.")
owner.faction |= "spiders"
- var/obj/structure/spider/spiderling/S = new(owner.loc)
+ var/mob/living/basic/spiderling/S = new(owner.loc)
S.master_commander = owner
/obj/item/organ/internal/heart/gland/egg
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index b249101a70d..877fd464316 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -76,160 +76,13 @@
if(amount_grown >= 100)
var/num = rand(3, 12)
for(var/i in 1 to num)
- var/obj/structure/spider/spiderling/S = new /obj/structure/spider/spiderling(loc)
+ var/mob/living/basic/spiderling/S = new /mob/living/basic/spiderling(loc)
S.faction = faction.Copy()
S.master_commander = master_commander
if(player_spiders)
S.player_spiders = TRUE
qdel(src)
-/obj/structure/spider/spiderling
- name = "spiderling"
- desc = "It never stays still for long."
- icon_state = "spiderling"
- anchored = FALSE
- layer = 2.75
- max_integrity = 3
- var/amount_grown = 0
- var/grow_as = null
- var/obj/machinery/atmospherics/unary/vent_pump/entry_vent
- var/travelling_in_vent = FALSE
- var/player_spiders = FALSE
- var/list/faction = list("spiders")
- var/selecting_player = 0
-
-/obj/structure/spider/spiderling/Initialize(mapload)
- . = ..()
- pixel_x = rand(6,-6)
- pixel_y = rand(6,-6)
- START_PROCESSING(SSobj, src)
- AddComponent(/datum/component/swarming)
- AddComponent(/datum/component/event_tracker, EVENT_TERROR_SPIDERS)
- ADD_TRAIT(src, TRAIT_EDIBLE_BUG, "edible_bug") // Normally this is just used for mobs, but spiderlings are kind of that...
-
-/obj/structure/spider/spiderling/Destroy()
- STOP_PROCESSING(SSobj, src)
- // Cancel our movement.
- GLOB.move_manager.stop_looping(src)
- entry_vent = null
- if(amount_grown < 100)
- new /obj/effect/decal/cleanable/spiderling_remains(get_turf(src))
- return ..()
-
-/obj/structure/spider/spiderling/Bump(atom/user)
- if(istype(user, /obj/structure/table))
- loc = user.loc
- else
- ..()
-
-/obj/structure/spider/spiderling/process()
- if(travelling_in_vent)
- if(isturf(loc))
- travelling_in_vent = FALSE
- entry_vent = null
- else if(entry_vent)
- if(get_dist(src, entry_vent) <= 1)
- var/list/vents = list()
- for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in entry_vent.parent.other_atmosmch)
- vents.Add(temp_vent)
- if(!length(vents))
- entry_vent = null
- return
- var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = pick(vents)
- if(prob(50))
- visible_message("[src] scrambles into the ventilation ducts!", \
- "You hear something squeezing through the ventilation ducts.")
-
- spawn(rand(20,60))
- loc = exit_vent
- var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
- spawn(travel_time)
-
- if(!exit_vent || exit_vent.welded)
- loc = entry_vent
- entry_vent = null
- return
-
- if(prob(50))
- audible_message("You hear something squeezing through the ventilation ducts.")
- sleep(travel_time)
-
- if(!exit_vent || exit_vent.welded)
- loc = entry_vent
- entry_vent = null
- return
- loc = exit_vent.loc
- entry_vent = null
- var/area/new_area = get_area(loc)
- if(new_area)
- new_area.Entered(src)
- //=================
-
- else if(prob(33))
- if(random_skitter() && prob(40))
- visible_message("[src] skitters[pick(" away"," around","")].")
- else if(prob(10))
- //ventcrawl!
- for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src))
- if(!v.welded)
- entry_vent = v
- GLOB.move_manager.home_onto(src, entry_vent, 1, 10)
- break
- if(isturf(loc))
- amount_grown += rand(0,2)
- if(amount_grown >= 100)
- if(SSmobs.xenobiology_mobs > MAX_GOLD_CORE_MOBS && HAS_TRAIT(src, TRAIT_XENOBIO_SPAWNED))
- qdel(src)
- return
- if(!grow_as)
- grow_as = pick(typesof(/mob/living/basic/giant_spider) - list(/mob/living/basic/giant_spider/hunter/infestation_spider, /mob/living/basic/giant_spider/araneus))
- var/mob/living/basic/giant_spider/S = new grow_as(loc)
- S.faction = faction.Copy()
- S.master_commander = master_commander
- if(HAS_TRAIT(src, TRAIT_XENOBIO_SPAWNED))
- ADD_TRAIT(S, TRAIT_XENOBIO_SPAWNED, "xenobio")
- SSmobs.xenobiology_mobs++
- if(player_spiders && !selecting_player)
- selecting_player = 1
- spawn()
- var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_SENTIENT, TRUE, source = S)
-
- if(length(candidates) && !QDELETED(S))
- var/mob/C = pick(candidates)
- if(C)
- S.key = C.key
- dust_if_respawnable(C)
- if(S.master_commander)
- to_chat(S, "You are a spider who is loyal to [S.master_commander], obey [S.master_commander]'s every order and assist [S.master_commander.p_them()] in completing [S.master_commander.p_their()] goals at any cost.")
- qdel(src)
-
-/obj/structure/spider/spiderling/proc/random_skitter()
- var/list/available_turfs = list()
- for(var/turf/simulated/S in oview(10, src))
- // no !isspaceturf check needed since /turf/simulated is not a subtype of /turf/space
- if(S.density)
- continue
- available_turfs += S
- if(!length(available_turfs))
- return FALSE
- GLOB.move_manager.home_onto(src, pick(available_turfs), 1, 10)
- return TRUE
-
-/obj/structure/spider/spiderling/decompile_act(obj/item/matter_decompiler/C, mob/user)
- if(!isdrone(user))
- user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \
- "It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.")
- C.stored_comms["metal"] += 2
- C.stored_comms["glass"] += 1
- qdel(src)
- return TRUE
- return ..()
-
-/obj/effect/decal/cleanable/spiderling_remains
- name = "spiderling remains"
- desc = "Green squishy mess."
- icon_state = "greenshatter"
-
/obj/structure/spider/cocoon
name = "cocoon"
desc = "Something wrapped in silky spider web."
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index fe334c45d30..136423bd8cb 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -496,7 +496,7 @@
for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list)
if(is_station_level(E.z))
count_eggs += E.spiderling_number
- for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list)
+ for(var/mob/living/basic/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list)
if(!L.stillborn && is_station_level(L.z))
count_spiderlings += 1
count_infected = length(GLOB.ts_infected_list)
diff --git a/code/modules/events/infestation.dm b/code/modules/events/infestation.dm
index c908e831a11..e83031b49c9 100644
--- a/code/modules/events/infestation.dm
+++ b/code/modules/events/infestation.dm
@@ -64,7 +64,7 @@
max_number = 6
vermstring = "lizards"
if(VERM_SPIDERS)
- spawn_types = list(/obj/structure/spider/spiderling)
+ spawn_types = list(/mob/living/basic/spiderling)
max_number = 3
vermstring = "spiders"
var/amount_to_spawn = rand(2, max_number)
@@ -73,7 +73,7 @@
amount_to_spawn--
if(vermin == VERM_SPIDERS)
- var/obj/structure/spider/spiderling/S = new(T)
+ var/mob/living/basic/spiderling/S = new(T)
S.amount_grown = -1
else
var/spawn_type = pick(spawn_types)
diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm
index 3e846a8a6b1..1b687fc631c 100644
--- a/code/modules/events/spider_infestation.dm
+++ b/code/modules/events/spider_infestation.dm
@@ -19,7 +19,7 @@
var/list/vents = get_valid_vent_spawns(exclude_mobs_nearby = TRUE)
while(spawncount && length(vents))
var/obj/vent = pick_n_take(vents)
- var/obj/structure/spider/spiderling/S = new(vent.loc)
+ var/mob/living/basic/spiderling/S = new(vent.loc)
if(prob(66))
S.grow_as = /mob/living/basic/giant_spider/nurse
spawncount--
diff --git a/code/modules/hydroponics/hydroponics_tray.dm b/code/modules/hydroponics/hydroponics_tray.dm
index 42e38d2a97b..89553cf3949 100644
--- a/code/modules/hydroponics/hydroponics_tray.dm
+++ b/code/modules/hydroponics/hydroponics_tray.dm
@@ -500,7 +500,7 @@
log_game("[key_name(user)] caused spiderling pests to spawn in a hydro tray")
visible_message("The pests seem to behave oddly...")
for(var/i in 1 to 3)
- var/obj/structure/spider/spiderling/S = new(get_turf(src))
+ var/mob/living/basic/spiderling/S = new(get_turf(src))
S.grow_as = /mob/living/basic/giant_spider/hunter
else
to_chat(user, "The pests seem to behave oddly, but quickly settle down...")
diff --git a/code/modules/mob/living/basic/friendly/lizard.dm b/code/modules/mob/living/basic/friendly/lizard.dm
index 419246bc537..123fbe356fb 100644
--- a/code/modules/mob/living/basic/friendly/lizard.dm
+++ b/code/modules/mob/living/basic/friendly/lizard.dm
@@ -34,7 +34,7 @@
var/static/list/edibles = list(
/mob/living/basic/butterfly,
/mob/living/basic/cockroach,
- /obj/structure/spider/spiderling
+ /mob/living/basic/spiderling
)
/// Lizards start with a tail
var/has_tail = TRUE
diff --git a/code/modules/mob/living/basic/hostile/spiderlings.dm b/code/modules/mob/living/basic/hostile/spiderlings.dm
new file mode 100644
index 00000000000..b34294856e0
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/spiderlings.dm
@@ -0,0 +1,172 @@
+/mob/living/basic/spiderling
+ name = "spiderling"
+ desc = "It never stays still for long."
+ icon_state = "spiderling"
+ icon_dead = "spiderling_dead" // Doesn't exist, because they splat.
+ density = FALSE
+ speed = -0.75
+ move_resist = INFINITY // YOU CAN'T HANDLE ME LET ME BE FREE LET ME BE FREE LET ME BE FREE
+ speak_emote = list("hisses")
+ layer = 2.75
+ basic_mob_flags = FLAMMABLE_MOB | DEL_ON_DEATH
+ pass_flags = PASSTABLE | PASSGRILLE | PASSMOB | PASSDOOR
+ mob_size = MOB_SIZE_TINY
+ ventcrawler = TRUE
+ melee_damage_lower = 1
+ melee_damage_upper = 1
+ health = 5
+ maxHealth = 5
+ faction = list("spiders")
+ attack_verb_simple = "bite"
+ attack_verb_continuous = "bites"
+ attack_sound = 'sound/weapons/bite.ogg'
+ can_hide = TRUE
+ ai_controller = /datum/ai_controller/basic_controller/spiderling
+ /// How grown up is the spider?
+ var/amount_grown = 0
+ /// The mob the spiderling will grow into. null is giant spiders.
+ var/grow_as = null
+ /// Is the spider supposed to be player controlled?
+ var/player_spiders = FALSE
+ /// Are we selecting a player?
+ var/selecting_player = FALSE
+
+/mob/living/basic/spiderling/Initialize(mapload)
+ . = ..()
+ pixel_x = rand(6,-6)
+ pixel_y = rand(6,-6)
+ AddComponent(/datum/component/swarming)
+ AddElement(/datum/element/ai_retaliate)
+ ADD_TRAIT(src, TRAIT_EDIBLE_BUG, "edible_bug")
+
+/mob/living/basic/spiderling/Life(seconds, times_fired)
+ . = ..()
+ if(!isturf(loc))
+ return
+ amount_grown += rand(0,2)
+ if(amount_grown < 100)
+ return
+ if(SSmobs.xenobiology_mobs > MAX_GOLD_CORE_MOBS && HAS_TRAIT(src, TRAIT_XENOBIO_SPAWNED))
+ qdel(src)
+ return
+ if(!grow_as)
+ grow_as = pick(typesof(/mob/living/basic/giant_spider) - list(/mob/living/basic/giant_spider/hunter/infestation_spider, /mob/living/basic/giant_spider/araneus))
+ var/mob/living/basic/giant_spider/S = new grow_as(loc)
+ S.faction = faction.Copy()
+ S.master_commander = master_commander
+ if(HAS_TRAIT(src, TRAIT_XENOBIO_SPAWNED))
+ ADD_TRAIT(S, TRAIT_XENOBIO_SPAWNED, "xenobio")
+ SSmobs.xenobiology_mobs++
+ if(!player_spiders)
+ qdel(src)
+ return
+ if(selecting_player)
+ return
+ selecting_player = TRUE
+ spawn()
+ var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_SENTIENT, TRUE, source = S)
+ if(!length(candidates) || QDELETED(S))
+ return
+ var/mob/C = pick(candidates)
+ if(C)
+ S.key = C.key
+ dust_if_respawnable(C)
+ if(S.master_commander)
+ to_chat(S, "You are a spider who is loyal to [S.master_commander], obey [S.master_commander]'s every order and assist [S.master_commander.p_them()] in completing [S.master_commander.p_their()] goals at any cost.")
+ qdel(src)
+
+/mob/living/basic/spiderling/death(gibbed)
+ if(!istype(loc, /obj/machinery/atmospherics))
+ new /obj/effect/decal/cleanable/spiderling_remains(get_turf(src))
+ . = ..()
+
+/mob/living/basic/spiderling/decompile_act(obj/item/matter_decompiler/C, mob/user)
+ if(isdrone(user))
+ return ..()
+ user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \
+ "It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.")
+ C.stored_comms["metal"] += 2
+ C.stored_comms["glass"] += 1
+ qdel(src)
+ return TRUE
+
+/obj/effect/decal/cleanable/spiderling_remains
+ name = "spiderling remains"
+ desc = "Green squishy mess."
+ icon_state = "greenshatter"
+
+// --------------------------------------------------------------------------------
+// ----------------- TERROR SPIDERS: SPIDERLINGS (USED BY GREEN, WHITE, QUEEN AND MOTHER TYPES)
+// --------------------------------------------------------------------------------
+
+/mob/living/basic/spiderling/terror_spiderling
+ desc = "A fast-moving tiny spider, prone to making aggressive hissing sounds. Hope it doesn't grow up."
+ speed = -1
+ faction = list("terrorspiders")
+ var/stillborn = FALSE
+ var/mob/living/simple_animal/hostile/poison/terror_spider/queen/spider_myqueen = null
+ var/mob/living/simple_animal/hostile/poison/terror_spider/spider_mymother = null
+ var/list/enemies = list()
+ var/spider_awaymission = FALSE
+
+/mob/living/basic/spiderling/terror_spiderling/Initialize(mapload)
+ . = ..()
+ GLOB.ts_spiderling_list += src
+ if(is_away_level(z))
+ spider_awaymission = TRUE
+
+/mob/living/basic/spiderling/terror_spiderling/death(gibbed)
+ GLOB.ts_spiderling_list -= src
+ return ..()
+
+/mob/living/basic/spiderling/terror_spiderling/Life(seconds, times_fired)
+ . = ..()
+ var/turf/T = get_turf(src)
+ amount_grown += rand(0,2)
+ if(amount_grown >= 100)
+ if(spider_awaymission && !is_away_level(T.z))
+ stillborn = TRUE
+ if(stillborn)
+ if(amount_grown >= 300)
+ // Fake spiderlings stick around for awhile, just to be spooky.
+ qdel(src)
+ else
+ if(!grow_as)
+ grow_as = pick(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green)
+ var/mob/living/simple_animal/hostile/poison/terror_spider/S = new grow_as(T)
+ S.spider_myqueen = spider_myqueen
+ S.spider_mymother = spider_mymother
+ S.enemies = enemies
+ qdel(src)
+
+/datum/ai_controller/basic_controller/spiderling
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/of_size/larger, // Run away from mobs bigger than we are
+ BB_VENT_SEARCH_RANGE = 10,
+ BB_VENTCRAWL_DELAY = 1
+ )
+ ai_movement = /datum/ai_movement/jps
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+
+ // 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,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree/opportunistic, // Trapped! Bite the person trapping them.
+ /datum/ai_planning_subtree/simple_find_nearest_target_to_flee,
+ /datum/ai_planning_subtree/flee_target,
+ /datum/ai_planning_subtree/ventcrawl_find_target/spiderling,
+ /datum/ai_planning_subtree/ventcrawl/spiderling,
+ /datum/ai_planning_subtree/random_speech/insect,
+ )
+
+/datum/ai_planning_subtree/ventcrawl_find_target/spiderling
+ crawl_behavior = /datum/ai_behavior/find_ventcrawl_target/spiderling
+
+/datum/ai_behavior/find_ventcrawl_target/spiderling
+ action_cooldown = 20 SECONDS
+
+/datum/ai_planning_subtree/ventcrawl/spiderling
+ ventcrawl_finding_behavior = /datum/ai_behavior/find_and_set/ventcrawl/spiderling
+
+/datum/ai_behavior/find_and_set/ventcrawl/spiderling
+ searched_entry_types = list(/obj/machinery/atmospherics/unary/vent_pump)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress_terror.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress_terror.dm
index 90c09fa1dd6..50c40db56d7 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress_terror.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress_terror.dm
@@ -82,7 +82,7 @@
var/numlings = input("How many?") as null|anything in list(10, 20, 30, 40, 50)
var/sbpc = input("%chance to be stillborn?") as null|anything in list(0, 25, 50, 75, 100)
for(var/i=0, iThrough the hivemind, the raw power of [src] floods into your body, burning it from the inside out!")
for(var/obj/structure/spider/eggcluster/terror_eggcluster/T in GLOB.ts_egg_list)
qdel(T)
- for(var/obj/structure/spider/spiderling/terror_spiderling/T in GLOB.ts_spiderling_list)
+ for(var/mob/living/basic/spiderling/terror_spiderling/T in GLOB.ts_spiderling_list)
qdel(T)
to_chat(src, "All Terror Spiders, except yourself, will die off shortly.")
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm
index 3ff6a579a7a..f0e7c93a913 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm
@@ -57,7 +57,7 @@
spider_totals[E.spiderling_type] = E.spiderling_number
spider_totals["all"] += E.spiderling_number
for(var/thing in GLOB.ts_spiderling_list)
- var/obj/structure/spider/spiderling/terror_spiderling/L = thing
+ var/mob/living/basic/spiderling/terror_spiderling/L = thing
if(L.stillborn)
continue
if(check_mine && L.spider_myqueen != src)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm
index 8727ebd8fe0..57135749d75 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/mother.dm
@@ -97,10 +97,9 @@
/mob/living/simple_animal/hostile/poison/terror_spider/mother/proc/PickupSpiderlings()
// Mothers can pick up spiderlings, carrying them on their back and stopping them from wandering into trouble.
var/pickup_count = 0
- for(var/obj/structure/spider/spiderling/terror_spiderling/S in orange(2, src))
+ for(var/mob/living/basic/spiderling/terror_spiderling/S in orange(2, src))
var/turf/T = get_turf(S)
new /obj/effect/temp_visual/heal(T)
- S.movement_disabled = TRUE
S.forceMove(src)
pickup_count++
if(pickup_count)
@@ -111,10 +110,8 @@
/mob/living/simple_animal/hostile/poison/terror_spider/mother/proc/DropSpiderlings()
// Called when a mother dies.
var/turf/T = get_turf(src)
- for(var/obj/structure/spider/spiderling/terror_spiderling/S in src)
- S.movement_disabled = FALSE
+ for(var/mob/living/basic/spiderling/terror_spiderling/S in src)
S.forceMove(T)
- S.immediate_ventcrawl = TRUE
/mob/living/simple_animal/hostile/poison/terror_spider/mother/proc/IncubateEggs()
// Mothers can spend regen points to make existing eggs mature faster.
@@ -136,6 +133,6 @@
desc = "This web is coated in pheromones which prevent spiderlings from passing it."
/obj/structure/spider/terrorweb/mother/CanPass(atom/movable/mover, border_dir)
- if(istype(mover, /obj/structure/spider/spiderling/terror_spiderling))
+ if(istype(mover, /mob/living/basic/spiderling/terror_spiderling))
return FALSE
return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm
index a96be22ea2f..fc57aee0bc5 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen_terror.dm
@@ -112,7 +112,7 @@
to_chat(T, "\The psychic backlash from the death of [src] overwhelms you! You feel the life start to drain out of you...")
T.degenerate = TRUE
for(var/thing in GLOB.ts_spiderling_list)
- var/obj/structure/spider/spiderling/terror_spiderling/T = thing
+ var/mob/living/basic/spiderling/terror_spiderling/T = thing
if(T.spider_myqueen && T.spider_myqueen == src)
qdel(T)
return ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm
index 718874daf44..8cc352095dc 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm
@@ -1,171 +1,3 @@
-
-// --------------------------------------------------------------------------------
-// ----------------- TERROR SPIDERS: SPIDERLINGS (USED BY GREEN, WHITE, QUEEN AND MOTHER TYPES)
-// --------------------------------------------------------------------------------
-
-/obj/structure/spider/spiderling/terror_spiderling
- desc = "A fast-moving tiny spider, prone to making aggressive hissing sounds. Hope it doesn't grow up."
- var/stillborn = FALSE
- var/mob/living/simple_animal/hostile/poison/terror_spider/queen/spider_myqueen = null
- var/mob/living/simple_animal/hostile/poison/terror_spider/spider_mymother = null
- var/goto_mother = FALSE
- var/ventcrawl_chance = 30 // 30% every process(), assuming 33% wander does not trigger
- var/immediate_ventcrawl = TRUE
- var/list/enemies = list()
- var/spider_awaymission = FALSE
- var/frustration = 0
- var/debug_ai_choices = FALSE
- var/movement_disabled = FALSE
-
-/obj/structure/spider/spiderling/terror_spiderling/Initialize(mapload)
- . = ..()
- GLOB.ts_spiderling_list += src
- if(is_away_level(z))
- spider_awaymission = TRUE
- AddComponent(/datum/component/event_tracker, EVENT_TERROR_SPIDERS)
-
-/obj/structure/spider/spiderling/terror_spiderling/Destroy()
- GLOB.ts_spiderling_list -= src
- for(var/obj/structure/spider/spiderling/terror_spiderling/S in view(7, src))
- S.immediate_ventcrawl = TRUE
- return ..()
-
-/obj/structure/spider/spiderling/terror_spiderling/Bump(obj/O)
- if(istype(O, /obj/structure/table))
- forceMove(O.loc)
- . = ..()
-
-/obj/structure/spider/spiderling/terror_spiderling/proc/score_surroundings(atom/A = src)
- var/safety_score = 0
- var/turf/T = get_turf(A)
- for(var/mob/living/L in viewers(T))
- if(isterrorspider(L))
- if(L.stat == DEAD)
- safety_score--
- else
- safety_score++
- if(spider_mymother && L == spider_mymother)
- safety_score++
- else if(L.stat != DEAD)
- safety_score--
- if(debug_ai_choices)
- debug_visual(T, safety_score, A)
- return safety_score
-
-/obj/structure/spider/spiderling/terror_spiderling/proc/debug_visual(turf/T, score, atom/A)
- // This proc exists to help debug why spiderlings are making the ventcrawl AI choices they do.
- // It won't be called unless you set the spiderling's debug_ai_choices to true.
- if(debug_ai_choices && istype(T))
- if(A == src)
- if(score > 0)
- new /obj/effect/temp_visual/heart(T) // heart symbol, I am safe here, protected by a friendly spider
- else if(score == 0)
- new /obj/effect/temp_visual/heal(T) // white "+" symbol, I am neutral here
- else
- new /obj/effect/temp_visual/at_shield(T) // octagon symbol, I am unsafe here, I need to flee
- else
- if(score > 0)
- new /obj/effect/temp_visual/telekinesis(T) // blue sparks, this is a safe area, I want to go here
- else if(score == 0)
- new /obj/effect/temp_visual/revenant(T) // purple sparks, this is a neutral area, an acceptable choice
- else
- new /obj/effect/temp_visual/cult/sparks(T) // red sparks, this is an unsafe area, I won't go here unless fleeing something worse
-
-/obj/structure/spider/spiderling/terror_spiderling/process()
- var/turf/T = get_turf(src)
- amount_grown += rand(0,2)
- if(amount_grown >= 100)
- if(spider_awaymission && !is_away_level(T.z))
- stillborn = TRUE
- if(stillborn)
- if(amount_grown >= 300)
- // Fake spiderlings stick around for awhile, just to be spooky.
- qdel(src)
- else
- if(!grow_as)
- grow_as = pick(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green)
- var/mob/living/simple_animal/hostile/poison/terror_spider/S = new grow_as(T)
- S.spider_myqueen = spider_myqueen
- S.spider_mymother = spider_mymother
- S.enemies = enemies
- qdel(src)
- if(movement_disabled)
- return
- if(travelling_in_vent)
- if(isturf(loc))
- travelling_in_vent = FALSE
- entry_vent = null
- else if(entry_vent)
- if(get_dist(src, entry_vent) <= 1)
- frustration = 0
- var/list/vents = list()
- for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in entry_vent.parent.other_atmosmch)
- if(temp_vent.welded) // no point considering a vent we can't even use
- continue
- vents.Add(temp_vent)
- if(!length(vents))
- entry_vent = null
- return
- var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = pick(vents)
- if(spider_mymother && (goto_mother || prob(10)))
- for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(5, spider_mymother))
- if(!v.welded)
- exit_vent = v
- goto_mother = FALSE
- if(!stillborn)
- var/current_safety_score = score_surroundings(src)
- var/new_safety_score = score_surroundings(exit_vent)
- if(new_safety_score < current_safety_score)
- // Try to find an alternative.
- exit_vent = pick(vents)
- new_safety_score = score_surroundings(exit_vent)
- if(new_safety_score < current_safety_score)
- // No alternative safe vent could be found. Abort.
- entry_vent = null
- return
- var/original_location = loc
- spawn(rand(20,60))
- forceMove(exit_vent)
- var/travel_time = round(get_dist(loc, exit_vent.loc) / 2)
- spawn(travel_time)
- if(!exit_vent || exit_vent.welded)
- forceMove(original_location)
- entry_vent = null
- return
- if(prob(50))
- audible_message("You hear something squeezing through the ventilation ducts.")
- spawn(travel_time)
- if(!exit_vent || exit_vent.welded)
- forceMove(original_location)
- entry_vent = null
- return
- forceMove(exit_vent.loc)
- entry_vent = null
- var/area/new_area = get_area(loc)
- if(new_area)
- new_area.Entered(src)
- else
- frustration++
- GLOB.move_manager.move_to(src, entry_vent, 1)
- if(frustration > 2)
- entry_vent = null
- else if(prob(33))
- random_skitter()
- else if(immediate_ventcrawl || prob(ventcrawl_chance))
- immediate_ventcrawl = FALSE
- if(!stillborn && !goto_mother)
- var/safety_score = score_surroundings(src)
- if(safety_score > 0)
- // This area seems safe (friendly spiders present). Do not leave this area.
- return
- for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src))
- if(!v.welded)
- entry_vent = v
- GLOB.move_manager.move_to(src, entry_vent, 1)
- break
-
-
-
// --------------------------------------------------------------------------------
// ----------------- TERROR SPIDERS: EGGS (USED BY NURSE AND QUEEN TYPES) ---------
// --------------------------------------------------------------------------------
@@ -228,7 +60,7 @@
if(amount_grown >= 100)
var/num = spiderling_number
for(var/i=0, i[owner] bursts open! Holy fuck!")
owner.gib()
@@ -85,7 +85,7 @@
/obj/item/organ/internal/body_egg/terror_eggs/proc/hatch_egg()
var/infection_completed = FALSE
- var/obj/structure/spider/spiderling/terror_spiderling/S = new(get_turf(owner))
+ var/mob/living/basic/spiderling/terror_spiderling/S = new(get_turf(owner))
switch(eggs_hatched)
if(0) // 1st spiderling
S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/gray
@@ -98,7 +98,6 @@
if(4) // 5th
S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/green
infection_completed = TRUE
- S.immediate_ventcrawl = TRUE
eggs_hatched++
to_chat(owner, "A strange prickling sensation moves across your skin... then suddenly the whole world seems to spin around you!")
owner.Paralyse(20 SECONDS)
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 53ed7f1358c..42cbf73e3bd 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index 6d932d6f184..59cd7d6f592 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 5401a014f2d..4530a2b0d5d 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -2512,9 +2512,10 @@
#include "code\modules\mob\living\basic\hostile\killertomato.dm"
#include "code\modules\mob\living\basic\hostile\pirate.dm"
#include "code\modules\mob\living\basic\hostile\skeleton_mob.dm"
+#include "code\modules\mob\living\basic\hostile\spiderlings.dm"
+#include "code\modules\mob\living\basic\hostile\soviet.dm"
#include "code\modules\mob\living\basic\hostile\tree.dm"
#include "code\modules\mob\living\basic\hostile\viscerator.dm"
-#include "code\modules\mob\living\basic\hostile\soviet.dm"
#include "code\modules\mob\living\basic\hostile\vox_raiders.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_ai.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_drone.dm"