mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-01 12:31:32 +00:00
## About The Pull Request Implements https://hackmd.io/@tgstation/SkeUS7lSp , rewriting Dynamic from the ground-up - Dynamic configuration is now vastly streamlined, making it far far far easier to understand and edit - Threat is gone entirely; round chaos is now determined by dynamic tiers - There's 5 dynamic tiers, 0 to 4. - 0 is a pure greenshift. - Tiers are just picked via weight - "16% chance of getting a high chaos round". - Tiers have min pop ranges. "Tier 4 (high chaos) requires 25 pop to be selected". - Tier determines how much of every ruleset is picked. "Tier 4 (High Chaos) will pick 3-4 roundstart[1], 1-2 light, 1-2 heavy, and 2-3 latejoins". - The number of rulesets picked depends on how many people are in the server - this is also configurable[2]. As an example, a tier that demands "1-3" rulesets will not spawn 3 rulesets if population <= 40 and will not spawn 2 rulesets if population <= 25. - Tiers also determine time before light, heavy, and latejoin rulesets are picked, as well as the cooldown range between spawns. More chaotic tiers may send midrounds sooner or wait less time between sending them. - On the ruleset side of things, "requirements", "scaling", and "enemies" is gone. - You can configure a ruleset's min pop and weight flat, or per tier. - For example a ruleset like Obsession is weighted higher for tiers 1-2 and lower for tiers 3-4. - Rather than scaling up, roundstart rulesets can just be selected multiple times. - Rulesets also have `min_antag_cap` and `max_antag_cap`. `min_antag_cap` determines how many candidates are needed for it to run, and `max_antag_cap` determines how many candidates are selected. - Rulesets attempt to run every 2.5 minutes. [3] - Light rulesets will ALWAYS be picked before heavy rulesets. [4] - Light injection chance is no longer 100%, heavy injection chance formula has been simplified. - Chance simply scales based on number of dead players / total number off players, with a flag 50% chance if no antags exist. [5] [1] This does not guarantee you will actually GET 3-4 roundstart rulesets. If a roundstart ruleset is picked, and it ends up being unable to execute (such as "not enough candidates", that slot is effectively a wash.) This might be revisited. [2] Currently, this is a hard limit - below X pop, you WILL get a quarter or a half of the rulesets. This might be revisited to just be weighted - you are just MORE LIKELY to get a quarter or a half. [3] Little worried about accidentally frontloading everything so we'll see about this [4] This may be revisited but in most contexts it seems sensible. [5] This may also be revisited, I'm not 100% sure what the best / most simple way to tackle midround chances is. Other implementation details - The process of making rulesets has been streamlined as well. Many rulesets only amount to a definition and `assign_role`. - Dynamic.json -> Dynamic.toml - Dynamic event hijacked was ripped out entirely. - Most midround antag random events are now dynamic rulesets. Fugitives, Morphs, Slaughter Demons, etc. - The 1 weight slaughter demon event is gone. RIP in peace. - There is now a hidden midround event that simply adds +1 latejoin, +1 light, or +1 heavy ruleset. - `mind.special_role` is dead. Minds have a lazylist of special roles now but it's essentially only used for traitor panel. - Revs refactored almost entirely. Revs can now exist without a dynamic ruleset. - Cult refactored a tiny bit. - Antag datums cleaned up. - Pre round setup is less centralized on Dynamic. - Admins have a whole panel for interfacing with dynamic. It's pretty slapdash I'm sure someone could make a nicer looking one.   - Maybe some other things. ## Why It's Good For The Game See readme for more info. Will you see a massive change in how rounds play out? My hunch says rounds will spawn less rulesets on average, but it's ultimately to how it's configured ## Changelog 🆑 Melbert refactor: Dynamic rewritten entirely, report any strange rounds config: Dynamic config reworked, it's now a TOML file refactor: Refactored antag roles somewhat, report any oddities refactor: Refactored Revolution entirely, report any oddities del: Deleted most midround events that spawn antags - they use dynamic rulesets now add: Dynamic rulesets can now be false alarms add: Adds a random event that gives dynamic the ability to run another ruleset later admin: Adds a panel for messing around with dynamic admin: Adds a panel for chance for every dynamic ruleset to be selected admin: You can spawn revs without using dynamic now fix: Nuke team leaders get their fun title back /🆑
758 lines
30 KiB
Plaintext
758 lines
30 KiB
Plaintext
/datum/station_trait/carp_infestation
|
|
name = "Carp infestation"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 5
|
|
show_in_report = TRUE
|
|
report_message = "Dangerous fauna is present in the area of this station."
|
|
trait_to_give = STATION_TRAIT_CARP_INFESTATION
|
|
|
|
/datum/station_trait/distant_supply_lines
|
|
name = "Distant supply lines"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 3
|
|
show_in_report = TRUE
|
|
report_message = "Due to the distance to our normal supply lines, cargo orders are more expensive."
|
|
blacklist = list(/datum/station_trait/strong_supply_lines)
|
|
|
|
/datum/station_trait/distant_supply_lines/on_round_start()
|
|
SSeconomy.pack_price_modifier *= 1.2
|
|
|
|
///A negative trait that stops mail from arriving (or the inverse if on holiday). It also enables a specific shuttle loan situation.
|
|
/datum/station_trait/mail_blocked
|
|
name = "Postal workers strike"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
show_in_report = TRUE
|
|
report_message = "Due to an ongoing strike announced by the postal workers union, mail won't be delivered this shift."
|
|
|
|
/datum/station_trait/mail_blocked/on_round_start()
|
|
//This is either a holiday or Sunday... well then, let's flip the situation.
|
|
if(SSeconomy.mail_blocked)
|
|
name = "Postal system overtime"
|
|
report_message = "Despite being a day off, the postal system is working overtime today. Mail will be delivered this shift."
|
|
else
|
|
var/datum/round_event_control/shuttle_loan/our_event = locate() in SSevents.control
|
|
our_event.unavailable_situations -= /datum/shuttle_loan_situation/mail_strike
|
|
SSeconomy.mail_blocked = !SSeconomy.mail_blocked
|
|
|
|
/datum/station_trait/mail_blocked/hangover/revert()
|
|
var/datum/round_event_control/shuttle_loan/our_event = locate() in SSevents.control
|
|
our_event.unavailable_situations |= /datum/shuttle_loan_situation/mail_strike
|
|
SSeconomy.mail_blocked = !SSeconomy.mail_blocked
|
|
return ..()
|
|
|
|
/datum/station_trait/late_arrivals
|
|
name = "Late Arrivals"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
show_in_report = TRUE
|
|
report_message = "Sorry for that, we didn't expect to fly into that vomiting goose while bringing you to your new station."
|
|
trait_to_give = STATION_TRAIT_LATE_ARRIVALS
|
|
blacklist = list(/datum/station_trait/random_spawns, /datum/station_trait/hangover)
|
|
|
|
/datum/station_trait/random_spawns
|
|
name = "Drive-by landing"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
show_in_report = TRUE
|
|
report_message = "Sorry for that, we missed your station by a few miles, so we just launched you towards your station in pods. Hope you don't mind!"
|
|
trait_to_give = STATION_TRAIT_RANDOM_ARRIVALS
|
|
blacklist = list(/datum/station_trait/late_arrivals, /datum/station_trait/hangover)
|
|
|
|
/datum/station_trait/hangover
|
|
name = "Hangover"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
show_in_report = TRUE
|
|
report_message = "Ohh....Man....That mandatory office party from last shift...God that was awesome..I woke up in some random toilet 3 sectors away..."
|
|
trait_to_give = STATION_TRAIT_HANGOVER
|
|
blacklist = list(/datum/station_trait/late_arrivals, /datum/station_trait/random_spawns)
|
|
|
|
/datum/station_trait/hangover/New()
|
|
. = ..()
|
|
RegisterSignal(SSdcs, COMSIG_GLOB_JOB_AFTER_LATEJOIN_SPAWN, PROC_REF(on_job_after_spawn))
|
|
|
|
/datum/station_trait/hangover/revert()
|
|
for (var/obj/effect/landmark/start/hangover/hangover_spot in GLOB.start_landmarks_list)
|
|
QDEL_LIST(hangover_spot.hangover_debris)
|
|
|
|
return ..()
|
|
|
|
/datum/station_trait/hangover/proc/on_job_after_spawn(datum/source, datum/job/job, mob/living/spawned_mob)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!prob(35))
|
|
return
|
|
var/obj/item/hat = pick(
|
|
/obj/item/clothing/head/costume/sombrero/green,
|
|
/obj/item/clothing/head/fedora,
|
|
/obj/item/clothing/mask/balaclava,
|
|
/obj/item/clothing/head/costume/ushanka,
|
|
/obj/item/clothing/head/costume/cardborg,
|
|
/obj/item/clothing/head/costume/pirate,
|
|
/obj/item/clothing/head/cone,
|
|
)
|
|
hat = new hat(spawned_mob)
|
|
spawned_mob.equip_to_slot_or_del(hat, ITEM_SLOT_HEAD)
|
|
|
|
|
|
/datum/station_trait/blackout
|
|
name = "Blackout"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 3
|
|
show_in_report = TRUE
|
|
report_message = "Station lights seem to be damaged, be safe when starting your shift today."
|
|
|
|
/datum/station_trait/blackout/on_round_start()
|
|
. = ..()
|
|
for(var/obj/machinery/power/apc/apc as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/power/apc))
|
|
if(is_station_level(apc.z) && prob(60))
|
|
apc.overload_lighting()
|
|
|
|
/datum/station_trait/empty_maint
|
|
name = "Cleaned out maintenance"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 5
|
|
cost = STATION_TRAIT_COST_LOW //Most of maints is literal trash anyway
|
|
show_in_report = TRUE
|
|
report_message = "Our workers cleaned out most of the junk in the maintenace areas."
|
|
blacklist = list(/datum/station_trait/filled_maint)
|
|
trait_to_give = STATION_TRAIT_EMPTY_MAINT
|
|
|
|
// This station trait is checked when loot drops initialize, so it's too late
|
|
can_revert = FALSE
|
|
|
|
/datum/station_trait/overflow_job_bureaucracy
|
|
name = "Overflow bureaucracy mistake"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 5
|
|
show_in_report = TRUE
|
|
var/chosen_job_name
|
|
|
|
/datum/station_trait/overflow_job_bureaucracy/New()
|
|
. = ..()
|
|
RegisterSignal(SSjob, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(set_overflow_job_override))
|
|
|
|
/datum/station_trait/overflow_job_bureaucracy/get_report()
|
|
return "[name] - It seems for some reason we put out the wrong job-listing for the overflow role this shift...I hope you like [chosen_job_name]s."
|
|
|
|
/datum/station_trait/overflow_job_bureaucracy/proc/set_overflow_job_override(datum/source)
|
|
SIGNAL_HANDLER
|
|
var/datum/job/picked_job = pick(SSjob.get_valid_overflow_jobs())
|
|
chosen_job_name = LOWER_TEXT(picked_job.title) // like Chief Engineers vs like chief engineers
|
|
SSjob.set_overflow_role(picked_job.type)
|
|
|
|
/datum/station_trait/slow_shuttle
|
|
name = "Slow Shuttle"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 5
|
|
show_in_report = TRUE
|
|
report_message = "Due to distance to our supply station, the cargo shuttle will have a slower flight time to your cargo department."
|
|
blacklist = list(/datum/station_trait/quick_shuttle)
|
|
|
|
/datum/station_trait/slow_shuttle/on_round_start()
|
|
. = ..()
|
|
SSshuttle.supply.callTime *= 1.5
|
|
|
|
/datum/station_trait/bot_languages
|
|
name = "Bot Language Matrix Malfunction"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 4
|
|
cost = STATION_TRAIT_COST_LOW
|
|
show_in_report = TRUE
|
|
report_message = "Your station's friendly bots have had their language matrix fried due to an event, resulting in some strange and unfamiliar speech patterns."
|
|
trait_to_give = STATION_TRAIT_BOTS_GLITCHED
|
|
|
|
/datum/station_trait/bot_languages/New()
|
|
. = ..()
|
|
// What "caused" our robots to go haywire (fluff)
|
|
var/event_source = pick("an ion storm", "a syndicate hacking attempt", "a malfunction", "issues with your onboard AI", "an intern's mistakes", "budget cuts")
|
|
report_message = "Your station's friendly bots have had their language matrix fried due to [event_source], resulting in some strange and unfamiliar speech patterns."
|
|
|
|
/datum/station_trait/bot_languages/on_round_start()
|
|
. = ..()
|
|
// All bots that exist round start on station Z OR on the escape shuttle have their set language randomized.
|
|
for(var/mob/living/found_bot as anything in GLOB.bots_list)
|
|
found_bot.randomize_language_if_on_station()
|
|
|
|
/datum/station_trait/machine_languages
|
|
name = "Machine Language Matrix Malfunction"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
cost = STATION_TRAIT_COST_FULL
|
|
show_in_report = TRUE
|
|
report_message = "Your station's machines have had their language matrix fried due to an event, \
|
|
resulting in some strange and unfamiliar speech patterns."
|
|
trait_to_give = STATION_TRAIT_MACHINES_GLITCHED
|
|
|
|
/datum/station_trait/machine_languages/New()
|
|
. = ..()
|
|
// What "caused" our machines to go haywire (fluff)
|
|
var/event_source = pick("an ion storm", "a malfunction", "a software update", "a power surge", "a computer virus", "a subdued machine uprising", "a clown's prank")
|
|
report_message = "Your station's machinery have had their language matrix fried due to [event_source], resulting in some strange and unfamiliar speech patterns."
|
|
|
|
/datum/station_trait/revenge_of_pun_pun
|
|
name = "Revenge of Pun Pun"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
cost = STATION_TRAIT_COST_LOW
|
|
|
|
// Way too much is done on atoms SS to be reverted, and it'd look
|
|
// kinda clunky on round start. It's not impossible to make this work,
|
|
// but it's a project for...someone else.
|
|
can_revert = FALSE
|
|
|
|
var/static/list/weapon_types
|
|
|
|
/datum/station_trait/revenge_of_pun_pun/New()
|
|
if(!weapon_types)
|
|
weapon_types = list(
|
|
/obj/item/chair = 20,
|
|
/obj/item/tailclub = 10,
|
|
/obj/item/melee/baseball_bat = 10,
|
|
/obj/item/melee/chainofcommand/tailwhip = 10,
|
|
/obj/item/melee/chainofcommand/tailwhip/kitty = 10,
|
|
/obj/item/reagent_containers/cup/glass/bottle = 20,
|
|
/obj/item/reagent_containers/cup/glass/bottle/kong = 5,
|
|
/obj/item/switchblade/extended = 10,
|
|
/obj/item/sign/random = 10,
|
|
/obj/item/gun/ballistic/automatic/pistol = 1,
|
|
)
|
|
|
|
RegisterSignal(SSatoms, COMSIG_SUBSYSTEM_POST_INITIALIZE, PROC_REF(arm_monke))
|
|
|
|
/datum/station_trait/revenge_of_pun_pun/proc/arm_monke()
|
|
SIGNAL_HANDLER
|
|
var/mob/living/carbon/human/species/monkey/punpun/punpun = GLOB.the_one_and_only_punpun
|
|
if(!punpun)
|
|
return
|
|
var/weapon_type = pick_weight(weapon_types)
|
|
var/obj/item/weapon = new weapon_type
|
|
if(!punpun.put_in_l_hand(weapon) && !punpun.put_in_r_hand(weapon))
|
|
// Guess they did all this with whatever they have in their hands already
|
|
qdel(weapon)
|
|
weapon = punpun.get_active_held_item() || punpun.get_inactive_held_item()
|
|
|
|
weapon?.add_mob_blood(punpun)
|
|
punpun.add_mob_blood(punpun)
|
|
|
|
if(!isnull(punpun.ai_controller)) // In case punpun somehow lacks AI
|
|
QDEL_NULL(punpun.ai_controller)
|
|
|
|
new /datum/ai_controller/monkey/angry(punpun)
|
|
|
|
var/area/place = get_area(punpun)
|
|
|
|
var/list/area_open_turfs = list()
|
|
for(var/turf/location in place)
|
|
if(location.density)
|
|
continue
|
|
area_open_turfs += location
|
|
|
|
punpun.forceMove(pick(area_open_turfs))
|
|
|
|
for(var/i in 1 to rand(10, 40))
|
|
new /obj/effect/decal/cleanable/blood(pick(area_open_turfs))
|
|
|
|
var/list/blood_path = list()
|
|
for(var/i in 1 to 10) // Only 10 attempts
|
|
var/turf/destination = pick(area_open_turfs)
|
|
var/turf/next_step = get_step_to(punpun, destination)
|
|
for(var/k in 1 to 30) // Max 30 steps
|
|
if(!next_step)
|
|
break
|
|
blood_path += next_step
|
|
next_step = get_step_to(next_step, destination)
|
|
if(length(blood_path))
|
|
break
|
|
if(!length(blood_path))
|
|
CRASH("Unable to make a path from punpun")
|
|
|
|
var/turf/last_location
|
|
for(var/turf/location as anything in blood_path)
|
|
last_location = location
|
|
|
|
if(prob(80))
|
|
new /obj/effect/decal/cleanable/blood(location)
|
|
|
|
if(prob(50))
|
|
var/static/blood_types = list(
|
|
/obj/effect/decal/cleanable/blood/splatter,
|
|
/obj/effect/decal/cleanable/blood/gibs,
|
|
)
|
|
var/blood_type = pick(blood_types)
|
|
new blood_type(get_turf(pick(orange(location, 2))))
|
|
|
|
new /obj/effect/decal/cleanable/blood/gibs/torso(last_location)
|
|
|
|
// Abstract station trait used for traits that modify a random event in some way (their weight or max occurrences).
|
|
/datum/station_trait/random_event_weight_modifier
|
|
name = "Random Event Modifier"
|
|
report_message = "A random event has been modified this shift! Someone forgot to set this!"
|
|
show_in_report = TRUE
|
|
abstract_type = /datum/station_trait/random_event_weight_modifier
|
|
weight = 0
|
|
|
|
/// The path to the round_event_control that we modify.
|
|
var/datum/round_event_control/event_control_path
|
|
/// Multiplier applied to the weight of the event.
|
|
var/weight_multiplier = 1
|
|
/// Flat modifier added to the amount of max occurances the random event can have.
|
|
var/max_occurrences_modifier = 0
|
|
|
|
/datum/station_trait/random_event_weight_modifier/on_round_start()
|
|
. = ..()
|
|
var/datum/round_event_control/modified_event = locate(event_control_path) in SSevents.control
|
|
if(!modified_event)
|
|
CRASH("[type] could not find a round event controller to modify on round start (likely has an invalid event_control_path set)!")
|
|
|
|
modified_event.weight *= weight_multiplier
|
|
modified_event.max_occurrences += max_occurrences_modifier
|
|
|
|
/datum/station_trait/random_event_weight_modifier/ion_storms
|
|
name = "Ionic Stormfront"
|
|
report_message = "An ionic stormfront is passing over your station's system. Expect an increased likelihood of ion storms afflicting your station's silicon units."
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 3
|
|
event_control_path = /datum/round_event_control/ion_storm
|
|
weight_multiplier = 2
|
|
|
|
/datum/station_trait/random_event_weight_modifier/ion_storms/get_pulsar_message()
|
|
var/advisory_string = "Advisory Level: <b>ERROR</b></center><BR>"
|
|
advisory_string += scramble_message_replace_chars("Your sector's advisory level is ERROR. An electromagnetic field has stormed through nearby surveillance equipment, causing major data loss. Partial data was recovered and showed no credible threats to Nanotrasen assets within the Spinward Sector; however, the Department of Intelligence advises maintaining high alert against potential threats due to the lack of complete data.", 35)
|
|
return advisory_string
|
|
|
|
/datum/station_trait/random_event_weight_modifier/rad_storms
|
|
name = "Radiation Stormfront"
|
|
report_message = "A radioactive stormfront is passing through your station's system. Expect an increased likelihood of radiation storms passing over your station, as well the potential for multiple radiation storms to occur during your shift."
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
event_control_path = /datum/round_event_control/radiation_storm
|
|
weight_multiplier = 1.5
|
|
max_occurrences_modifier = 2
|
|
|
|
/datum/station_trait/random_event_weight_modifier/dust_storms
|
|
name = "Dust Stormfront"
|
|
report_message = "The space around your station is clouded by heavy pockets of space dust. Expect an increased likelihood of space dust storms damaging the station hull."
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 2
|
|
cost = STATION_TRAIT_COST_LOW
|
|
event_control_path = /datum/round_event_control/meteor_wave/dust_storm
|
|
weight_multiplier = 2
|
|
max_occurrences_modifier = 3
|
|
|
|
/datum/station_trait/cramped_escape_pods
|
|
name = "Cramped Escape Pods"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 5
|
|
show_in_report = TRUE
|
|
report_message = "Due to budget cuts, we have downsized your escape pods."
|
|
trait_to_give = STATION_TRAIT_SMALLER_PODS
|
|
blacklist = list(/datum/station_trait/luxury_escape_pods)
|
|
|
|
/datum/station_trait/revolutionary_trashing
|
|
name = "Post-Revolutionary Fervor"
|
|
show_in_report = TRUE
|
|
report_message = "Your station was recently reclaimed from a revolutionary commune. We couldn't clean up after them in time."
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
trait_to_give = STATION_TRAIT_REVOLUTIONARY_TRASHING
|
|
weight = 2
|
|
///The IDs of the graffiti designs that we will generate.
|
|
var/static/list/trash_talk = list(
|
|
"amyjon",
|
|
"antilizard",
|
|
"body",
|
|
"cyka",
|
|
"danger",
|
|
"electricdanger",
|
|
"face",
|
|
"guy",
|
|
"matt",
|
|
"peace",
|
|
"prolizard",
|
|
"radiation",
|
|
"revolution",
|
|
"shotgun",
|
|
"skull",
|
|
"splatter",
|
|
"star",
|
|
"stickman",
|
|
"toilet",
|
|
"toolbox",
|
|
"uboa",
|
|
)
|
|
|
|
/datum/station_trait/revolutionary_trashing/on_round_start()
|
|
. = ..()
|
|
|
|
INVOKE_ASYNC(src, PROC_REF(trash_this_place)) //Must be called asynchronously
|
|
|
|
/**
|
|
* "Trashes" the command areas of the station.
|
|
*
|
|
* Creates random graffiti and damages certain machinery/structures in the
|
|
* command areas of the station.
|
|
*/
|
|
|
|
/datum/station_trait/revolutionary_trashing/proc/trash_this_place()
|
|
for(var/area/station/command/area_to_trash in GLOB.areas)
|
|
for (var/list/zlevel_turfs as anything in area_to_trash.get_zlevel_turf_lists())
|
|
for (var/turf/current_turf as anything in zlevel_turfs)
|
|
if(isclosedturf(current_turf))
|
|
continue
|
|
if(prob(25))
|
|
var/obj/effect/decal/cleanable/crayon/created_art
|
|
created_art = new(current_turf, RANDOM_COLOUR, pick(trash_talk))
|
|
created_art.pixel_x = rand(-10, 10)
|
|
created_art.pixel_y = rand(-10, 10)
|
|
|
|
if(prob(0.01))
|
|
new /obj/effect/mob_spawn/corpse/human/assistant(current_turf)
|
|
continue
|
|
|
|
for(var/atom/current_thing as anything in current_turf.contents)
|
|
if(istype(current_thing, /obj/machinery/light) && prob(40))
|
|
var/obj/machinery/light/light_to_smash = current_thing
|
|
light_to_smash.break_light_tube(skip_sound_and_sparks = TRUE)
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/structure/window))
|
|
if(prob(15))
|
|
current_thing.take_damage(rand(30, 90))
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/structure/table) && prob(40))
|
|
current_thing.take_damage(100)
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/structure/chair) && prob(60))
|
|
current_thing.take_damage(150)
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/machinery/computer) && prob(30))
|
|
if(istype(current_thing, /obj/machinery/computer/communications))
|
|
continue //To prevent the shuttle from getting autocalled at the start of the round
|
|
current_thing.take_damage(160)
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/machinery/vending) && prob(45))
|
|
var/obj/machinery/vending/vendor_to_trash = current_thing
|
|
if(prob(50))
|
|
vendor_to_trash.tilt(get_turf(vendor_to_trash), 0) // crit effects can do some real weird shit, lets disable it
|
|
|
|
if(prob(50))
|
|
vendor_to_trash.take_damage(150)
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/structure/fireaxecabinet)) //A staple of revolutionary behavior
|
|
current_thing.take_damage(90)
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/item/bedsheet/captain))
|
|
new /obj/item/bedsheet/rev(current_thing.loc)
|
|
qdel(current_thing)
|
|
continue
|
|
|
|
if(istype(current_thing, /obj/item/bedsheet/captain/double))
|
|
new /obj/item/bedsheet/rev/double(current_thing.loc)
|
|
qdel(current_thing)
|
|
continue
|
|
|
|
CHECK_TICK
|
|
|
|
///Station traits that influence the space background and apply some unique effects!
|
|
/datum/station_trait/nebula
|
|
name = "Nebula"
|
|
abstract_type = /datum/station_trait/nebula
|
|
weight = 0
|
|
|
|
show_in_report = TRUE
|
|
|
|
///The parallax layer of the nebula
|
|
var/nebula_layer = /atom/movable/screen/parallax_layer/random/space_gas
|
|
///If set, gives the basic carp different colors
|
|
var/carp_color_override
|
|
|
|
/datum/station_trait/nebula/New()
|
|
. = ..()
|
|
|
|
SSparallax.swap_out_random_parallax_layer(nebula_layer)
|
|
|
|
//Color the carp in unique colors to better blend with the nebula
|
|
if(carp_color_override)
|
|
GLOB.carp_colors = carp_color_override
|
|
|
|
///Station nebula that incur some sort of effect if no shielding is created
|
|
/datum/station_trait/nebula/hostile
|
|
abstract_type = /datum/station_trait/nebula/hostile
|
|
trait_processes = TRUE
|
|
|
|
///Intensity of the nebula
|
|
VAR_PRIVATE/nebula_intensity = -1
|
|
///The max intensity of a nebula
|
|
VAR_PROTECTED/maximum_nebula_intensity = 2 HOURS
|
|
///How long it takes to go to the next nebula level/intensity
|
|
VAR_PROTECTED/intensity_increment_time = 30 MINUTES
|
|
///Objects that we use to calculate the current shielding level
|
|
var/list/shielding = list()
|
|
|
|
/datum/station_trait/nebula/hostile/process(seconds_per_tick)
|
|
calculate_nebula_strength()
|
|
|
|
apply_nebula_effect(nebula_intensity - get_shielding_level())
|
|
|
|
/datum/station_trait/nebula/hostile/on_round_start()
|
|
. = ..()
|
|
|
|
addtimer(CALLBACK(src, PROC_REF(send_instructions)), 30 SECONDS)
|
|
|
|
///Announce to the station what's going on and what they need to do
|
|
/datum/station_trait/nebula/hostile/proc/send_instructions()
|
|
return
|
|
|
|
///Calculate how strong we currently are
|
|
/datum/station_trait/nebula/hostile/proc/calculate_nebula_strength()
|
|
nebula_intensity = min(STATION_TIME_PASSED(), maximum_nebula_intensity) / intensity_increment_time
|
|
|
|
///Check how strong the stations shielding is
|
|
/datum/station_trait/nebula/hostile/proc/get_shielding_level()
|
|
var/shield_strength = 0
|
|
for(var/atom/movable/shielder as anything in shielding)
|
|
if(!is_station_level(shielder.z))
|
|
continue
|
|
var/datum/callback/callback = shielding[shielder]
|
|
shield_strength += callback.Invoke()
|
|
|
|
return shield_strength
|
|
|
|
///Add a shielding unit to ask for shielding
|
|
/datum/station_trait/nebula/hostile/proc/add_shielder(atom/movable/shielder, shielding_proc)
|
|
shielding[shielder] = CALLBACK(shielder, shielding_proc)
|
|
|
|
RegisterSignal(shielder, COMSIG_QDELETING, PROC_REF(remove_shielder))
|
|
|
|
///Remove a shielding unit from our tracking
|
|
/datum/station_trait/nebula/hostile/proc/remove_shielder(atom/movable/shielder)
|
|
SIGNAL_HANDLER
|
|
|
|
shielding.Remove(shielder)
|
|
|
|
///The station did not set up shielding, start creating effects
|
|
/datum/station_trait/nebula/hostile/proc/apply_nebula_effect(effect_strength = 0)
|
|
return
|
|
|
|
/proc/add_to_nebula_shielding(atom/movable/shielder, nebula_type, shielding_proc)
|
|
var/datum/station_trait/nebula/hostile/nebula = locate(nebula_type) in SSstation.station_traits
|
|
if(!nebula)
|
|
return FALSE
|
|
|
|
nebula.add_shielder(shielder, shielding_proc)
|
|
|
|
///The station will be inside a radioactive nebula! Space is radioactive and the station needs to start setting up nebula shielding
|
|
/datum/station_trait/nebula/hostile/radiation
|
|
name = "Radioactive Nebula"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
trait_flags = STATION_TRAIT_SPACE_BOUND //maybe when we can LOOK UP
|
|
weight = 1
|
|
show_in_report = TRUE
|
|
report_message = "This station is located inside a radioactive nebula. Setting up nebula shielding is top-priority."
|
|
trait_to_give = STATION_TRAIT_RADIOACTIVE_NEBULA
|
|
|
|
blacklist = list(/datum/station_trait/random_event_weight_modifier/rad_storms)
|
|
dynamic_threat_id = "Radioactive Nebula"
|
|
|
|
intensity_increment_time = 5 MINUTES
|
|
maximum_nebula_intensity = 1 HOURS + 40 MINUTES
|
|
|
|
nebula_layer = /atom/movable/screen/parallax_layer/random/space_gas/radioactive
|
|
carp_color_override = list(
|
|
COLOR_CARP_GREEN = 1,
|
|
COLOR_CARP_TEAL = 1,
|
|
COLOR_CARP_PALE_GREEN = 1,
|
|
COLOR_CARP_DARK_GREEN = 1,
|
|
)
|
|
///When are we going to send them a care package?
|
|
COOLDOWN_DECLARE(send_care_package_at)
|
|
///How long does the storm have to last for us to send a care package?
|
|
VAR_PROTECTED/send_care_package_time = 5 MINUTES
|
|
///The glow of 'fake' radioactive objects in space
|
|
var/nebula_radglow = "#66ff33"
|
|
/// Area's that are part of the radioactive nebula
|
|
var/radioactive_areas = /area/space
|
|
|
|
/datum/station_trait/nebula/hostile/radiation/New()
|
|
. = ..()
|
|
|
|
RegisterSignal(SSdcs, COMSIG_RULESET_BODY_GENERATED_FROM_GHOSTS, PROC_REF(on_spawned_mob))
|
|
|
|
for(var/area/target as anything in get_areas(radioactive_areas))
|
|
RegisterSignal(target, COMSIG_AREA_ENTERED, PROC_REF(on_entered))
|
|
RegisterSignal(target, COMSIG_AREA_EXITED, PROC_REF(on_exited))
|
|
|
|
/datum/station_trait/nebula/hostile/radiation/on_round_start()
|
|
. = ..()
|
|
|
|
//Let people order more nebula shielding
|
|
var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/engineering/rad_nebula_shielding_kit]
|
|
pack.special_enabled = TRUE
|
|
|
|
//Give robotics some radiation protection modules for modsuits
|
|
var/datum/supply_pack/supply_pack_modsuits = new /datum/supply_pack/engineering/rad_protection_modules()
|
|
send_supply_pod_to_area(supply_pack_modsuits.generate(null), /area/station/science/robotics, /obj/structure/closet/supplypod/centcompod)
|
|
|
|
//Send a nebula shielding unit to engineering
|
|
var/datum/supply_pack/supply_pack_shielding = new /datum/supply_pack/engineering/rad_nebula_shielding_kit()
|
|
if(!send_supply_pod_to_area(supply_pack_shielding.generate(null), /area/station/engineering/main, /obj/structure/closet/supplypod/centcompod))
|
|
//if engineering isn't valid, just send it to the bridge
|
|
send_supply_pod_to_area(supply_pack_shielding.generate(null), /area/station/command/bridge, /obj/structure/closet/supplypod/centcompod)
|
|
|
|
// Let medical know resistance is futile
|
|
if (/area/station/medical/virology in GLOB.areas_by_type)
|
|
send_fax_to_area(
|
|
new /obj/item/paper/fluff/radiation_nebula_virologist,
|
|
/area/station/medical/virology,
|
|
"NT Virology Department",
|
|
force = TRUE,
|
|
force_pod_type = /obj/structure/closet/supplypod/centcompod,
|
|
)
|
|
|
|
//Disables radstorms, they don't really make sense since we already have the nebula causing storms
|
|
var/datum/round_event_control/modified_event = locate(/datum/round_event_control/radiation_storm) in SSevents.control
|
|
modified_event.weight = 0
|
|
|
|
///They entered space? START BOMBING WITH RADS HAHAHAHA. old_area can be null for new objects
|
|
/datum/station_trait/nebula/hostile/radiation/proc/on_entered(area/space, atom/movable/enterer, area/old_area)
|
|
SIGNAL_HANDLER
|
|
|
|
// Old area was radioactive, so what's the point. nothing changes. nothing ever does. also make sure the subsystem is alive before we give it food
|
|
if (istype(old_area, radioactive_areas) || !SSradioactive_nebula.initialized)
|
|
return
|
|
|
|
SSradioactive_nebula.fake_irradiate(enterer)
|
|
|
|
///Called when an atom leaves space, so we can remove the radiation effect
|
|
/datum/station_trait/nebula/hostile/radiation/proc/on_exited(area/space, atom/movable/exiter, direction)
|
|
SIGNAL_HANDLER
|
|
|
|
SSradioactive_nebula.fake_unirradiate(exiter)
|
|
|
|
// The component handles its own removal
|
|
|
|
/// When a mob is spawned by dynamic, intercept and give it a little radiation shield. Only works for dynamic mobs!
|
|
/datum/station_trait/nebula/hostile/radiation/proc/on_spawned_mob(datum/source, mob/spawned_mob)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!istype(get_area(spawned_mob), radioactive_areas)) //only if you're spawned in the radioactive areas
|
|
return
|
|
|
|
if(!isliving(spawned_mob)) // Dynamic shouldn't spawn non-living but uhhhhhhh why not
|
|
return
|
|
|
|
var/mob/living/spawnee = spawned_mob
|
|
spawnee.apply_status_effect(/datum/status_effect/radiation_immunity/radnebula)
|
|
|
|
/datum/station_trait/nebula/hostile/radiation/apply_nebula_effect(effect_strength = 0)
|
|
//big bombad now
|
|
if(effect_strength > 0 && !SSmapping.is_planetary()) //admins can force this
|
|
if(!SSweather.get_weather_by_type(/datum/weather/rad_storm/nebula))
|
|
COOLDOWN_START(src, send_care_package_at, send_care_package_time)
|
|
SSweather.run_weather(/datum/weather/rad_storm/nebula)
|
|
|
|
//Send a care package to temporarily lift the storm!
|
|
if(COOLDOWN_FINISHED(src, send_care_package_at))
|
|
COOLDOWN_START(src, send_care_package_at, send_care_package_time)
|
|
var/obj/machinery/nebula_shielding/emergency/rad_shield = /obj/machinery/nebula_shielding/emergency/radiation
|
|
|
|
priority_announce(
|
|
{"Is everything okay there? We're getting high radiation readings from inside the station. \
|
|
We're sending an emergency shielding unit for now, it will last [initial(rad_shield.detonate_in) / (1 MINUTES)] minutes. \n\n\
|
|
Set up the nebula shielding. You can order construction kits at cargo if yours have been lost.
|
|
"}
|
|
)
|
|
|
|
addtimer(CALLBACK(src, PROC_REF(send_care_package)), 10 SECONDS)
|
|
return
|
|
|
|
//No storms, shielding is good!
|
|
var/datum/weather/weather = SSweather.get_weather_by_type(/datum/weather/rad_storm/nebula)
|
|
weather?.wind_down()
|
|
COOLDOWN_RESET(src, send_care_package_at)
|
|
|
|
///Send a care package because it is not going well
|
|
/datum/station_trait/nebula/hostile/radiation/proc/send_care_package()
|
|
new /obj/effect/pod_landingzone (get_safe_random_station_turf_equal_weight(), new /obj/structure/closet/supplypod/centcompod (), new /obj/machinery/nebula_shielding/emergency/radiation ())
|
|
|
|
/datum/station_trait/nebula/hostile/radiation/send_instructions()
|
|
var/obj/machinery/nebula_shielding/shielder = /obj/machinery/nebula_shielding/radiation
|
|
var/obj/machinery/gravity_generator/main/innate_shielding = /obj/machinery/gravity_generator/main
|
|
//How long do we have until the first shielding unit needs to be up?
|
|
var/deadline = "[(initial(innate_shielding.radioactive_nebula_shielding) * intensity_increment_time) / (1 MINUTES)] minute\s"
|
|
//For how long each shielding unit will protect for
|
|
var/shielder_time = "[(initial(shielder.shielding_strength) * intensity_increment_time) / (1 MINUTES)] minute\s"
|
|
//Max shielders, excluding the grav-gen to avoid confusion when that goes down
|
|
var/max_shielders = ((maximum_nebula_intensity / intensity_increment_time)) / initial(shielder.shielding_strength)
|
|
|
|
var/announcement = {"Your station has been constructed inside a radioactive nebula. \
|
|
Standard spacesuits will not protect against the nebula and using them is strongly discouraged. \n\n\
|
|
|
|
EXTREME IMPORTANCE: The station is falling deeper into the nebula, and the gravity generator's innate radiation shielding \
|
|
will not hold very long. Your engineering department has been supplied with all the necessary supplies to set up \
|
|
shields to protect against the nebula. Additional supply crates can be ordered at cargo. \n\n\
|
|
You have [deadline] before the nebula enters the station. \
|
|
Every shielding unit will provide an additional [shielder_time] of protection, fully protecting the station with [max_shielders] shielding units.
|
|
"}
|
|
|
|
priority_announce(announcement, sound = 'sound/announcer/notice/notice1.ogg')
|
|
|
|
//Set the display screens to the radiation alert
|
|
var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
|
|
if(!frequency)
|
|
return
|
|
|
|
var/datum/signal/signal = new
|
|
signal.data["command"] = "alert"
|
|
signal.data["picture_state"] = "radiation"
|
|
|
|
var/atom/movable/virtualspeaker/virtual_speaker = new(null)
|
|
frequency.post_signal(virtual_speaker, signal)
|
|
|
|
/datum/station_trait/nebula/hostile/radiation/get_decal_color(atom/thing_to_color, pattern)
|
|
if(istype(get_area(thing_to_color), /area/station/hallway)) //color hallways green
|
|
return COLOR_GREEN
|
|
|
|
///Starts a storm on roundstart
|
|
/datum/station_trait/storm
|
|
abstract_type = /datum/station_trait/storm
|
|
var/datum/weather/storm_type
|
|
|
|
/datum/station_trait/storm/on_round_start()
|
|
. = ..()
|
|
|
|
SSweather.run_weather(storm_type)
|
|
|
|
/// Calls down an eternal storm on planetary stations
|
|
/datum/station_trait/storm/foreverstorm
|
|
name = "Forever Storm"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
trait_flags = STATION_TRAIT_PLANETARY
|
|
weight = 3
|
|
show_in_report = TRUE
|
|
report_message = "It looks like the storm is not gonna calm down anytime soon, stay safe out there."
|
|
storm_type = /datum/weather/snow_storm/forever_storm
|
|
|
|
/datum/station_trait/storm/foreverstorm/get_pulsar_message()
|
|
var/advisory_string = "Advisory Level: <b>Ice Giant</b></center><BR>"
|
|
advisory_string += "The ongoing blizzard has interfered with our surveillance equipment, and we cannot provide an accurate threat summary at this time. We advise you to stay safe and avoid traversing the area around the station."
|
|
return advisory_string
|
|
|
|
/datum/station_trait/spiked_drinks
|
|
name = "Spiked Drinks"
|
|
trait_type = STATION_TRAIT_NEGATIVE
|
|
weight = 3
|
|
cost = STATION_TRAIT_COST_LOW
|
|
show_in_report = TRUE
|
|
report_message = "Due to a mishap at the Robust Softdrinks Megafactory, some drinks may contain traces of ethanol or psychoactive chemicals."
|
|
trait_to_give = STATION_TRAIT_SPIKED_DRINKS
|
|
|
|
#undef GLOW_NEBULA
|