Converts spiderlings to basic mobs (#30571)

* Converts spiderlings to basic mobs

* Address code reviews, add terror_spiderlings to isterrorspider()

* Update code/modules/mob/living/basic/hostile/spiderlings.dm

Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>

* Update code/modules/mob/living/basic/hostile/spiderlings.dm

Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>

* Breaks up ancient spiderling life into something more modern

* Fixed the brimming station of spiders

---------

Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
This commit is contained in:
PollardTheDragon
2025-11-15 18:36:15 -05:00
committed by GitHub
parent 00cc58395d
commit c6875a50d0
21 changed files with 204 additions and 345 deletions
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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--
+1 -1
View File
@@ -500,7 +500,7 @@
log_game("[key_name(user)] caused spiderling pests to spawn in a hydro tray")
visible_message("<span class='warning'>The pests seem to behave oddly...</span>")
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, "<span class='warning'>The pests seem to behave oddly, but quickly settle down...</span>")
@@ -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
@@ -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, "<span class='biggerdanger'>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.</span>")
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("<span class='notice'>[user] sucks [src] into its decompiler. There's a horrible crunching noise.</span>", \
"<span class='warning'>It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.</span>")
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)
@@ -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, i<numlings, i++)
var/obj/structure/spider/spiderling/terror_spiderling/S = new /obj/structure/spider/spiderling/terror_spiderling(get_turf(src))
var/mob/living/basic/spiderling/terror_spiderling/S = new /mob/living/basic/spiderling/terror_spiderling(get_turf(src))
S.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, \
@@ -103,7 +103,7 @@
to_chat(T, "<span class='userdanger'>Through the hivemind, the raw power of [src] floods into your body, burning it from the inside out!</span>")
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, "<span class='userdanger'>All Terror Spiders, except yourself, will die off shortly.</span>")
@@ -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)
@@ -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 ..()
@@ -112,7 +112,7 @@
to_chat(T, "<span class='userdanger'>\The psychic backlash from the death of [src] overwhelms you! You feel the life start to drain out of you...</span>")
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 ..()
@@ -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("<span class='notice'>You hear something squeezing through the ventilation ducts.</span>")
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<num, i++)
var/obj/structure/spider/spiderling/terror_spiderling/S = new /obj/structure/spider/spiderling/terror_spiderling(get_turf(src))
var/mob/living/basic/spiderling/terror_spiderling/S = new /mob/living/basic/spiderling/terror_spiderling(get_turf(src))
if(spiderling_type)
S.grow_as = spiderling_type
S.spider_myqueen = spider_myqueen
+1 -1
View File
@@ -22,7 +22,7 @@
"living creatures" = list(
/mob/living,
/obj/structure/blob,
/obj/structure/spider/spiderling,
/mob/living/basic/spiderling,
/obj/machinery/clonepod,
/obj/item/paicard),
"classified nuclear weaponry" = list(
+2 -3
View File
@@ -31,7 +31,7 @@
owner.emote("scream")
var/spiders = rand(3,5)
for(var/i in 1 to spiders)
new/obj/structure/spider/spiderling(get_turf(owner))
new/mob/living/basic/spiderling(get_turf(owner))
owner.visible_message("<span class='danger'>[owner] bursts open! Holy fuck!</span>")
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, "<span class='warning'>A strange prickling sensation moves across your skin... then suddenly the whole world seems to spin around you!</span>")
owner.Paralyse(20 SECONDS)