Files
44f6846306 Adds structural weakpoints that can spawn on station (#94839)
## About The Pull Request

This PR was originally described to me by @AnturK and I implemented a
few elements of it to the best of my ability, then I promptly forgot
about it for the last 2 months.

This PR adds in randomly spawning structural weakpoints, using much of
the same mechanics of spawning in hidden satchels. If identified using a
T-ray scanner, they can be fixed using a welder by crew. More
excitingly, if a weakpoint is subject to an explosion, it will propagate
a crack, using a random line of turfs, then calling `ex_act()` on each
one with a small callback chain. Finally, cracks will form additional
weakpoints if caused this way, though are almost certainly going to be
noticeable, unlike before where they can spawn undertile.

With this addition, I've added a negative station trait, that will
increase the number of structural weakpoints that will spawn in a given
round. In a normal round, 3 weakpoints will spawn on the station, where
with the station trait, 4-8 weakpoints will spawn across the map.

Weakpoints come in 1 flavor practically, with a larger subtype being
spawnable by admins. The vars for weakpoints also happen to be fully
var-edit-able for admin purposes, including the explosion intensity that
can cause the weakpoint to trigger, the number of sub-cracks, if
additional weakpoints can spawn, etc.

You may also seal weakpoints using sticky tape.

## Why It's Good For The Game

Weakpoints feel like an interesting modifier to a map, where if caught
early can prevent a bad situation becoming worse. Otherwise, weakpoints
could very quickly cause chaos on the map, but despite their
description, aren't capable of causing **too** much destruction that
they can cause a shuttle call unless they're being manipulated by
traitors or antagonists. Though low odds, the idea of a player
welder-bombing a door or an IED going off related to the current
situation in a round, being exacerbated by pure chance due to being near
an IED seems really, REALLY funny.

## Changelog

🆑
add: Stations can now spawn with Structural weakpoints. If they're
subject to an explosion, they'll crack open a tear in the station's
turfs. If you spot one, make sure to weld it or seal it with sticky tape
to fix one. Or not!
add: Adds a negative station trait that greatly increases the number of
weakpoints that can spawn on station.
/🆑

---------

Co-authored-by: san7890 <the@san7890.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
2026-02-06 16:12:31 -05:00

134 lines
4.6 KiB
Plaintext

#define PROB_SPIDER_REPLACEMENT 50
SUBSYSTEM_DEF(minor_mapping)
name = "Minor Mapping"
dependencies = list(
/datum/controller/subsystem/mapping,
/datum/controller/subsystem/atoms,
)
flags = SS_NO_FIRE
///a list of vermin we pick from to spawn.
var/list/vermin_chances = list(
/mob/living/basic/mouse = 72,
/mob/living/basic/snail = 16,
/mob/living/basic/stoat = 10,
/mob/living/basic/regal_rat/controlled = 2,
)
/datum/controller/subsystem/minor_mapping/Initialize()
// This whole subsystem just introduces a lot of odd confounding variables into unit test situations,
// so let's just not bother with doing an initialize here.
#if defined(MAP_TEST) || defined(UNIT_TESTS)
return SS_INIT_NO_NEED
#else
trigger_migration(CONFIG_GET(number/mice_roundstart))
place_satchels(satchel_amount = 2)
var/weakpoint_spawns = 3
if(HAS_TRAIT(SSstation, STATION_TRAIT_SPAWN_WEAKPOINTS))
weakpoint_spawns = rand(4,8)
weakpoint_spawns += SSmapping.current_map.bonus_weakpoints //This will add 0 by default, or additional on large maps where it's included in the config.
place_weakpoints(weakpoint_spawns)
return SS_INIT_SUCCESS
#endif
/// Spawns some critters on exposed wires, usually but not always mice
/datum/controller/subsystem/minor_mapping/proc/trigger_migration(to_spawn=10)
var/list/exposed_wires = find_exposed_wires()
var/turf/open/proposed_turf
while((to_spawn > 0) && exposed_wires.len)
proposed_turf = pick_n_take(exposed_wires)
if (!valid_mouse_turf(proposed_turf))
continue
to_spawn--
var/picked_path
if(HAS_TRAIT(SSstation, STATION_TRAIT_SPIDER_INFESTATION) && prob(PROB_SPIDER_REPLACEMENT))
picked_path = /mob/living/basic/spider/maintenance
else
picked_path = pick_weight(vermin_chances)
new picked_path(proposed_turf)
/// Returns true if a mouse won't die if spawned on this turf
/datum/controller/subsystem/minor_mapping/proc/valid_mouse_turf(turf/open/proposed_turf)
if(!istype(proposed_turf))
return FALSE
var/datum/gas_mixture/turf/turf_gasmix = proposed_turf.air
var/turf_temperature = proposed_turf.temperature
return turf_gasmix.has_gas(/datum/gas/oxygen, 5) && turf_temperature < NPC_DEFAULT_MAX_TEMP && turf_temperature > NPC_DEFAULT_MIN_TEMP
/datum/controller/subsystem/minor_mapping/proc/place_satchels(satchel_amount)
var/list/turfs = find_satchel_suitable_turfs()
///List of areas where satchels should not be placed.
var/list/blacklisted_area_types = list(
/area/station/holodeck,
/area/space/nearstation,
/area/station/solars,
)
while(turfs.len && satchel_amount > 0)
var/turf/turf = pick_n_take(turfs)
if(is_type_in_list(get_area(turf), blacklisted_area_types))
continue
var/obj/item/storage/backpack/satchel/flat/flat_satchel = new(turf)
SEND_SIGNAL(flat_satchel, COMSIG_OBJ_HIDE, turf.underfloor_accessibility)
satchel_amount--
/proc/find_exposed_wires()
var/list/exposed_wires = list()
var/list/all_turfs
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
all_turfs += Z_TURFS(z)
for(var/turf/open/floor/plating/T in all_turfs)
if(T.is_blocked_turf())
continue
//dont include multiz cables in the list because repairing them sucks
var/cable = locate(/obj/structure/cable) in T
if(cable && !istype(cable, /obj/structure/cable/multilayer/multiz))
exposed_wires += T
return shuffle(exposed_wires)
/proc/find_satchel_suitable_turfs()
var/list/suitable = list()
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
for(var/turf/detected_turf as anything in Z_TURFS(z))
if(isfloorturf(detected_turf) && detected_turf.underfloor_accessibility == UNDERFLOOR_HIDDEN)
suitable += detected_turf
return shuffle(suitable)
/// This behaves nearly the same as spawning underfloot satchels, but instead spawns weakpoints.
/datum/controller/subsystem/minor_mapping/proc/place_weakpoints(weakpoint_amount)
if(weakpoint_amount == 0)
return
var/list/turfs = find_satchel_suitable_turfs()
///List of areas where weakpoints should not be placed.
var/list/blacklisted_area_types = get_blacklist_areas() + /area/station/maintenance
while(turfs.len && weakpoint_amount > 0)
var/turf/turf = pick_n_take(turfs)
if(is_type_in_list(get_area(turf), blacklisted_area_types))
continue
var/obj/effect/weakpoint/new_point = new(turf)
SEND_SIGNAL(new_point, COMSIG_OBJ_HIDE, turf.underfloor_accessibility)
weakpoint_amount--
/**
* Areas for minor_mapping procs to avoid. Mutate or adjust based on use case.
*/
/datum/controller/subsystem/minor_mapping/proc/get_blacklist_areas()
var/list/blacklist_areas = list(
/area/station/holodeck,
/area/space/nearstation,
/area/station/solars,
)
return blacklist_areas
#undef PROB_SPIDER_REPLACEMENT