diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index fbc12c60bd8..76d1ef360c4 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -323,6 +323,7 @@ #define COLOR_CARP_PALE_GREEN "#7ef099" #define COLOR_CARP_SILVER "#fdfbf3" #define COLOR_CARP_DARK_BLUE "#3a384d" +#define COLOR_CARP_DARK_GREEN "#358102" #define COLOR_GNOME_RED_ONE "#f10b0b" #define COLOR_GNOME_RED_TWO "#bc5347" diff --git a/code/__DEFINES/dcs/signals/signals_area.dm b/code/__DEFINES/dcs/signals/signals_area.dm index df5104cd885..0dc275f70e1 100644 --- a/code/__DEFINES/dcs/signals/signals_area.dm +++ b/code/__DEFINES/dcs/signals/signals_area.dm @@ -12,6 +12,8 @@ #define COMSIG_ENTER_AREA "enter_area" ///from base of area/Exited(): (area). Sent to "area-sensitive" movables, see __DEFINES/traits.dm for info. #define COMSIG_EXIT_AREA "exit_area" +///from base of /datum/controller/subsystem/atoms/proc/InitAtom(): (atom/new_atom) +#define COMSIG_AREA_INITIALIZED_IN "area_initialized_in" // Alarm listener datum signals ///Sent when an alarm is fired and an alarm listener has tracked onto it (alarm, area/source_area) diff --git a/code/__DEFINES/radiation.dm b/code/__DEFINES/radiation.dm index bb345312ce5..2ab5860763a 100644 --- a/code/__DEFINES/radiation.dm +++ b/code/__DEFINES/radiation.dm @@ -42,6 +42,8 @@ Ask Mothblocks if they're around /// The minimum exposure time before uranium structures can irradiate #define URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME (3 SECONDS) +/// The minimum exposure time before the radioactive nebula can irradiate +#define NEBULA_RADIATION_MINIMUM_EXPOSURE_TIME (10 SECONDS) /// Return values of [proc/get_perceived_radiation_danger] // If you change these, update /datum/looping_sound/geiger as well. diff --git a/code/__DEFINES/station.dm b/code/__DEFINES/station.dm index 5b97a079c6e..3cb026e4f76 100644 --- a/code/__DEFINES/station.dm +++ b/code/__DEFINES/station.dm @@ -2,8 +2,15 @@ #define STATION_TRAIT_NEUTRAL 2 #define STATION_TRAIT_NEGATIVE 3 - +/// For traits that shouldn't be selected, like abstract types (wow) #define STATION_TRAIT_ABSTRACT (1<<0) +/// Only run on planet stations +#define STATION_TRAIT_PLANETARY (1<<1) +/// Only run on space stations +#define STATION_TRAIT_SPACE_BOUND (1<<2) + +/// Not restricted by space or planet, can always just happen +#define STATION_TRAIT_MAP_UNRESTRICTED STATION_TRAIT_PLANETARY | STATION_TRAIT_SPACE_BOUND /// The data file that future station traits forced by an admin are stored in #define FUTURE_STATION_TRAITS_FILE "data/future_station_traits.json" diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 64955e4a569..00a85d61995 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -4,6 +4,9 @@ ///displays the current time into the round, with a lot of extra code just there for ensuring it looks okay after an entire day passes #define ROUND_TIME(...) ( "[world.time - SSticker.round_start_time > MIDNIGHT_ROLLOVER ? "[round((world.time - SSticker.round_start_time)/MIDNIGHT_ROLLOVER)]:[worldtime2text()]" : worldtime2text()]" ) +///Returns the time that has passed since the game started +#define STATION_TIME_PASSED(...) (world.time - SSticker.round_start_time) + /// Define that just has the current in-universe year for use in whatever context you might want to display that in. (For example, 2022 -> 2562 given a 540 year offset) #define CURRENT_STATION_YEAR (GLOB.year_integer + STATION_YEAR_OFFSET) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index d7d134e00ac..e1a6fdaef76 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -1102,6 +1102,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define STATION_TRAIT_BIRTHDAY "station_trait_birthday" #define STATION_TRAIT_SPIDER_INFESTATION "station_trait_spider_infestation" #define STATION_TRAIT_REVOLUTIONARY_TRASHING "station_trait_revolutionary_trashing" +#define STATION_TRAIT_RADIOACTIVE_NEBULA "station_trait_radioactive_nebula" ///From the market_crash event #define MARKET_CRASH_EVENT_TRAIT "crashed_market_event" diff --git a/code/_globalvars/lists/color.dm b/code/_globalvars/lists/color.dm index 26c0d89b9f1..1a195798f46 100644 --- a/code/_globalvars/lists/color.dm +++ b/code/_globalvars/lists/color.dm @@ -5,3 +5,23 @@ GLOBAL_LIST_INIT(contrast_colors, list( COLOR_TAN_ORANGE, COLOR_VIOLET, )) + +/// Weighted list of colours a carp can be +/// Weighted list of usual carp colors +GLOBAL_LIST_INIT(carp_colors, list( + COLOR_CARP_PURPLE = 7, + COLOR_CARP_PINK = 7, + COLOR_CARP_GREEN = 7, + COLOR_CARP_GRAPE = 7, + COLOR_CARP_SWAMP = 7, + COLOR_CARP_TURQUOISE = 7, + COLOR_CARP_BROWN = 7, + COLOR_CARP_TEAL = 7, + COLOR_CARP_LIGHT_BLUE = 7, + COLOR_CARP_RUSTY = 7, + COLOR_CARP_RED = 7, + COLOR_CARP_YELLOW = 7, + COLOR_CARP_BLUE = 7, + COLOR_CARP_PALE_GREEN = 7, + COLOR_CARP_SILVER = 1, // The rare silver carp +)) diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 5e2bfb58ef6..9d639a703d9 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -155,10 +155,13 @@ SUBSYSTEM_DEF(atoms) BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT else SEND_SIGNAL(A, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE) - var/atom/movable/location = A.loc + var/atom/location = A.loc if(location) /// Sends a signal that the new atom `src`, has been created at `loc` SEND_SIGNAL(location, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, A, arguments[1]) + var/area/atom_area = get_area(location) + if(atom_area) + SEND_SIGNAL(atom_area, COMSIG_AREA_INITIALIZED_IN, A) if(created_atoms && from_template && ispath(the_type, /atom/movable))//we only want to populate the list with movables created_atoms += A.get_all_contents() diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm index 4cc105ce6c1..1a597de84fb 100644 --- a/code/controllers/subsystem/parallax.dm +++ b/code/controllers/subsystem/parallax.dm @@ -10,17 +10,16 @@ SUBSYSTEM_DEF(parallax) var/random_layer var/random_parallax_color - //These are cached per client so needs to be done asap so people joining at roundstart do not miss these. /datum/controller/subsystem/parallax/PreInit() . = ..() + if(prob(70)) //70% chance to pick a special extra layer random_layer = pick(/atom/movable/screen/parallax_layer/random/space_gas, /atom/movable/screen/parallax_layer/random/asteroids) random_parallax_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_SILVER, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)//Special color for random_layer1. Has to be done here so everyone sees the same color. planet_y_offset = rand(100, 160) planet_x_offset = rand(100, 160) - /datum/controller/subsystem/parallax/fire(resumed = FALSE) if (!resumed) src.currentrun = GLOB.clients.Copy() diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm index 21cd66c11a7..fc07b0acd28 100644 --- a/code/controllers/subsystem/processing/station.dm +++ b/code/controllers/subsystem/processing/station.dm @@ -58,6 +58,13 @@ PROCESSING_SUBSYSTEM_DEF(station) if(initial(trait_typepath.trait_flags) & STATION_TRAIT_ABSTRACT) continue //Dont add abstract ones to it + + if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_PLANETARY) && SSmapping.is_planetary()) // we're on a planet but we can't do planet ;_; + continue + + if(!(initial(trait_typepath.trait_flags) & STATION_TRAIT_SPACE_BOUND) && !SSmapping.is_planetary()) //we're in space but we can't do space ;_; + continue + selectable_traits_by_types[initial(trait_typepath.trait_type)][trait_typepath] = initial(trait_typepath.weight) var/positive_trait_count = pick(20;0, 5;1, 1;2) diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/weather.dm index 386cd48a6bc..b78beeed815 100644 --- a/code/controllers/subsystem/weather.dm +++ b/code/controllers/subsystem/weather.dm @@ -81,3 +81,7 @@ SUBSYSTEM_DEF(weather) A = W break return A + +///Returns an active storm by its type +/datum/controller/subsystem/weather/proc/get_weather_by_type(type) + return locate(type) in processing diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 99c3531d2ff..072b50c0d53 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -211,7 +211,7 @@ var/list/possible_symptoms = list() for(var/symp in SSdisease.list_symptoms) var/datum/symptom/S = new symp - if(S.naturally_occuring && S.level >= level_min && S.level <= level_max) + if(S.can_generate_randomly() && S.level >= level_min && S.level <= level_max) if(!HasSymptom(S)) possible_symptoms += S diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 5c56cb3ad6e..1cd4a5c7a28 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -654,3 +654,6 @@ if(L.heal_damage(heal_amt/parts.len, heal_amt/parts.len, BODYTYPE_ORGANIC)) M.update_damage_overlays() return 1 + +/datum/symptom/heal/radiation/can_generate_randomly() + return ..() && !HAS_TRAIT(SSstation, STATION_TRAIT_RADIOACTIVE_NEBULA) //because people can never really suffer enough diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 6b00d967192..28ba45f8de4 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -114,3 +114,7 @@ data["neutered"] = neutered data["threshold_desc"] = threshold_descs return data + +/// Check if we can generate randomly +/datum/symptom/proc/can_generate_randomly() + return naturally_occuring diff --git a/code/datums/elements/radioactive.dm b/code/datums/elements/radioactive.dm index e4e8059a7a4..c949692930c 100644 --- a/code/datums/elements/radioactive.dm +++ b/code/datums/elements/radioactive.dm @@ -1,20 +1,40 @@ #define DELAY_BETWEEN_RADIATION_PULSES (3 SECONDS) /// This atom will regularly pulse radiation. -/// As this is only applied on uranium objects for now, this defaults to uranium constants. /datum/element/radioactive - element_flags = ELEMENT_DETACH_ON_HOST_DESTROY + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY | ELEMENT_BESPOKE + argument_hash_start_idx = 2 + ///Range of our wave in tiles + var/range + ///Threshold for radioactive permeance + var/threshold + ///Chance the object is irradiated + var/chance + ///Minimum time needed in order to be irradiated + var/minimum_exposure_time var/list/radioactive_objects = list() /datum/element/radioactive/New() START_PROCESSING(SSdcs, src) -/datum/element/radioactive/Attach(datum/target) +/datum/element/radioactive/Attach(\ + datum/target, \ + range = 3, \ + threshold = RAD_LIGHT_INSULATION, \ + chance = URANIUM_IRRADIATION_CHANCE, \ + minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME,\ +) + . = ..() radioactive_objects[target] = world.time + src.range = range + src.threshold = threshold + src.chance = chance + src.minimum_exposure_time = minimum_exposure_time + /datum/element/radioactive/Detach(datum/source, ...) radioactive_objects -= source @@ -27,10 +47,10 @@ radiation_pulse( radioactive_object, - max_range = 3, - threshold = RAD_LIGHT_INSULATION, - chance = URANIUM_IRRADIATION_CHANCE, - minimum_exposure_time = URANIUM_RADIATION_MINIMUM_EXPOSURE_TIME, + max_range = range, + threshold = threshold, + chance = chance, + minimum_exposure_time = minimum_exposure_time, ) radioactive_objects[radioactive_object] = world.time diff --git a/code/datums/materials/_material.dm b/code/datums/materials/_material.dm index 5b07d0fec9f..d91884d972e 100644 --- a/code/datums/materials/_material.dm +++ b/code/datums/materials/_material.dm @@ -21,8 +21,8 @@ Simple datum which is instanced once per type and is used for every object of sa ///Base alpha of the material, is used for greyscale icons. var/alpha = 255 ///Starlight color of the material - ///This is the color of light it'll emit if its turf is transparent and over space - var/starlight_color = COLOR_STARLIGHT + ///This is the color of light it'll emit if its turf is transparent and over space. Defaults to COLOR_STARLIGHT if not set + var/starlight_color ///Bitflags that influence how SSmaterials handles this material. var/init_flags = MATERIAL_INIT_MAPLOAD ///Materials "Traits". its a map of key = category | Value = Bool. Used to define what it can be used for @@ -154,7 +154,11 @@ Simple datum which is instanced once per type and is used for every object of sa // We assume no parallax means no space means no light if(SSmapping.level_trait(on.z, ZTRAIT_NOPARALLAX)) return - on.set_light(2, 0.75, starlight_color) + on.set_light(2, 0.75, get_starlight_color()) + +///Gets the space color and possible changed color if space is different +/datum/material/proc/get_starlight_color() + return starlight_color || GLOB.starlight_color /datum/material/proc/get_greyscale_config_for(datum/greyscale_config/config_path) if(!config_path) diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm index d11a2c0e8d9..44061746eaf 100644 --- a/code/datums/materials/basemats.dm +++ b/code/datums/materials/basemats.dm @@ -18,7 +18,6 @@ desc = "Glass forged by melting sand." color = "#88cdf1" greyscale_colors = "#88cdf196" - starlight_color = COLOR_STARLIGHT alpha = 150 categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE) integrity_modifier = 0.1 diff --git a/code/datums/station_traits/_station_trait.dm b/code/datums/station_traits/_station_trait.dm index cb0de6afd4e..e42ce87f3e5 100644 --- a/code/datums/station_traits/_station_trait.dm +++ b/code/datums/station_traits/_station_trait.dm @@ -18,16 +18,23 @@ var/trait_to_give ///What traits are incompatible with this one? var/blacklist - ///Extra flags for station traits such as it being abstract - var/trait_flags + ///Extra flags for station traits such as it being abstract, planetary or space only + var/trait_flags = STATION_TRAIT_MAP_UNRESTRICTED /// Whether or not this trait can be reverted by an admin var/can_revert = TRUE + /// The ID that we look for in dynamic.json. Not synced with 'name' because I can already see this go wrong + var/dynamic_threat_id + /// If ran during dynamic, do we reduce the total threat? Will be overriden by config if set + var/threat_reduction = 0 /datum/station_trait/New() . = ..() RegisterSignal(SSticker, COMSIG_TICKER_ROUND_STARTING, PROC_REF(on_round_start)) + if(threat_reduction) + GLOB.dynamic_station_traits[src] = threat_reduction + if(trait_processes) START_PROCESSING(SSstation, src) if(trait_to_give) @@ -35,6 +42,7 @@ /datum/station_trait/Destroy() SSstation.station_traits -= src + GLOB.dynamic_station_traits.Remove(src) return ..() /// Proc ran when round starts. Use this for roundstart effects. @@ -55,3 +63,15 @@ REMOVE_TRAIT(SSstation, trait_to_give, STATION_TRAIT) qdel(src) + +///Called by decals if they can be colored, to see if we got some cool colors for them. Only takes the first station trait +/proc/request_station_colors(atom/thing_to_color, pattern = PATTERN_DEFAULT) + for(var/datum/station_trait/trait in SSstation.station_traits) + var/decal_color = trait.get_decal_color(thing_to_color, pattern) + if(decal_color) + return decal_color + return null + +///Return a color for the decals, if any +/datum/station_trait/proc/get_decal_color(thing_to_color, pattern) + return diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm index 4fc6a464a9c..922b05e2464 100644 --- a/code/datums/station_traits/negative_traits.dm +++ b/code/datums/station_traits/negative_traits.dm @@ -267,7 +267,7 @@ 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 - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 3 event_control_path = /datum/round_event_control/ion_storm weight_multiplier = 2 @@ -276,7 +276,7 @@ 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 - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 2 event_control_path = /datum/round_event_control/radiation_storm weight_multiplier = 1.5 @@ -286,7 +286,7 @@ name = "Dust Stormfront" report_message = "The space around your station is clouded by heavy pockets of space dust. Expect an increased likelyhood of space dust storms damaging the station hull." trait_type = STATION_TRAIT_NEGATIVE - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 2 event_control_path = /datum/round_event_control/meteor_wave/dust_storm weight_multiplier = 2 @@ -410,3 +410,255 @@ continue CHECK_TICK + +///Station traits that influence the space background and apply some unique effects! +/datum/station_trait/nebula + name = "Nebula" + trait_flags = STATION_TRAIT_ABSTRACT + weight = 0 + + show_in_report = TRUE + + ///The color of the "nebula" we send to the players client + var/nebula_color + ///The parallax layer of the nebula + var/nebula_layer = /atom/movable/screen/parallax_layer/random/space_gas + ///The color space 'glows' + var/space_light_color = COLOR_STARLIGHT + ///If set, gives the basic carp different colors + var/carp_color_override + +/datum/station_trait/nebula/New() + . = ..() + + ///We set the parallax layer to give a visual effect + SSparallax.random_layer = nebula_layer + SSparallax.random_parallax_color = nebula_color //give a unique color to tell the player somethings up + GLOB.starlight_color = space_light_color //color starlight in our nebula color + + //parallax is generated for quick-joiners before we can change it, so reset it for them :/ + for(var/client/client as anything in GLOB.clients) + client.parallax_layers_cached?.Cut() + client.mob?.hud_used?.update_parallax_pref(client.mob) + + //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 + trait_flags = STATION_TRAIT_ABSTRACT + 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 = INFINITE + ///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() / intensity_increment_time, maximum_nebula_intensity) + +///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) + +/// Name of the glow we use for the radioation effect outside +#define GLOW_NEBULA "glow_nebula" + +///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) + threat_reduction = 30 + dynamic_threat_id = "Radioactive Nebula" + + intensity_increment_time = 5 MINUTES + maximum_nebula_intensity = 1 HOURS + 40 MINUTES + + nebula_color = list(0,0,0,0, 0,2,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0) //very vibrant green + space_light_color = COLOR_VIBRANT_LIME + 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" + +/datum/station_trait/nebula/hostile/radiation/New() + . = ..() + + for(var/area/target as anything in get_areas(/area/space)) + RegisterSignals(target, list(COMSIG_AREA_ENTERED, COMSIG_AREA_INITIALIZED_IN), 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 isnt 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) + + //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 +/datum/station_trait/nebula/hostile/radiation/proc/on_entered(area/space, atom/movable/enterer) + SIGNAL_HANDLER + + if(!ismovable(enterer)) + return + + enterer.AddElement(/datum/element/radioactive, range = 0, minimum_exposure_time = NEBULA_RADIATION_MINIMUM_EXPOSURE_TIME) + //Don't actually make EVERY. SINGLE. THING. radioactive, just make them glow so people arent killed instantly + if(!SSradiation.can_irradiate_basic(enterer)) + //outline clashes too much with other outlines and creates pretty ugly lines + enterer.add_filter(GLOW_NEBULA, 2, list("type" = "drop_shadow", "color" = nebula_radglow, "size" = 2)) + +///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) + SIGNAL_HANDLER + + exiter.RemoveElement(/datum/element/radioactive, range = 0, minimum_exposure_time = NEBULA_RADIATION_MINIMUM_EXPOSURE_TIME) + exiter.remove_filter(GLOW_NEBULA) + +/datum/station_trait/nebula/hostile/radiation/apply_nebula_effect(effect_strength = 0) + //big bombad now + if(effect_strength > 0) + 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(), 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 untill 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/misc/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 diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm index 0fe10d9bce0..e63fcf399ad 100644 --- a/code/datums/station_traits/positive_traits.dm +++ b/code/datums/station_traits/positive_traits.dm @@ -161,49 +161,49 @@ /datum/station_trait/deathrattle_department/service name = "Deathrattled Service" - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 1 department_to_apply_to = DEPARTMENT_BITFLAG_SERVICE department_name = "Service" /datum/station_trait/deathrattle_department/cargo name = "Deathrattled Cargo" - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 1 department_to_apply_to = DEPARTMENT_BITFLAG_CARGO department_name = "Cargo" /datum/station_trait/deathrattle_department/engineering name = "Deathrattled Engineering" - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 1 department_to_apply_to = DEPARTMENT_BITFLAG_ENGINEERING department_name = "Engineering" /datum/station_trait/deathrattle_department/command name = "Deathrattled Command" - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 1 department_to_apply_to = DEPARTMENT_BITFLAG_COMMAND department_name = "Command" /datum/station_trait/deathrattle_department/science name = "Deathrattled Science" - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 1 department_to_apply_to = DEPARTMENT_BITFLAG_SCIENCE department_name = "Science" /datum/station_trait/deathrattle_department/security name = "Deathrattled Security" - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 1 department_to_apply_to = DEPARTMENT_BITFLAG_SECURITY department_name = "Security" /datum/station_trait/deathrattle_department/medical name = "Deathrattled Medical" - trait_flags = NONE + trait_flags = STATION_TRAIT_MAP_UNRESTRICTED weight = 1 department_to_apply_to = DEPARTMENT_BITFLAG_MEDICAL department_name = "Medical" diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index 5e736277f8f..e9cadf5687b 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -75,5 +75,38 @@ else signal.data["command"] = "shuttle" - var/atom/movable/virtualspeaker/virt = new(null) - frequency.post_signal(virt, signal) + var/atom/movable/virtualspeaker/virtual_speaker = new(null) + frequency.post_signal(virtual_speaker, signal) + +/// Used by the radioactive nebula when the station doesnt have enough shielding +/datum/weather/rad_storm/nebula + protected_areas = list(/area/shuttle) + + weather_overlay = "nebula_radstorm" + weather_duration_lower = 100 HOURS + weather_duration_upper = 100 HOURS + + end_message = null + + ///Chance we pulse a living during the storm + var/radiation_chance = 20 + +/datum/weather/rad_storm/nebula/weather_act(mob/living/living) + if(!prob(radiation_chance)) + return + + if(!SSradiation.can_irradiate_basic(living) || SSradiation.wearing_rad_protected_clothing(living)) + return + + radiation_pulse( + source = living, + max_range = 0, + threshold = RAD_LIGHT_INSULATION, + chance = URANIUM_IRRADIATION_CHANCE, + minimum_exposure_time = NEBULA_RADIATION_MINIMUM_EXPOSURE_TIME, + ) + +/datum/weather/rad_storm/nebula/status_alarm(active) + if(!active) //we stay on + return + ..() diff --git a/code/game/area/areas/misc.dm b/code/game/area/areas/misc.dm index f2c7ac62c15..c7273c75aac 100644 --- a/code/game/area/areas/misc.dm +++ b/code/game/area/areas/misc.dm @@ -23,9 +23,6 @@ /area/space/nearstation icon_state = "space_near" area_flags = UNIQUE_AREA | AREA_USES_STARLIGHT - static_lighting = TRUE - base_lighting_alpha = 0 - base_lighting_color = COLOR_WHITE /area/misc/start name = "start area" diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm index 863e24d454d..7e370257dbe 100644 --- a/code/game/gamemodes/dynamic/dynamic.dm +++ b/code/game/gamemodes/dynamic/dynamic.dm @@ -15,6 +15,8 @@ GLOBAL_VAR_INIT(dynamic_stacking_limit, 90) GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset) // Forced threat level, setting this to zero or higher forces the roundstart threat to the value. GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) +/// Modify the threat level for station traits before dynamic can be Initialized. List(instance = threat_reduction) +GLOBAL_LIST_EMPTY(dynamic_station_traits) /datum/game_mode/dynamic // Threat logging vars @@ -395,6 +397,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) var/relative_threat = LORENTZ_DISTRIBUTION(threat_curve_centre, threat_curve_width) threat_level = clamp(round(lorentz_to_amount(relative_threat), 0.1), 0, max_threat_level) + for(var/datum/station_trait/station_trait in GLOB.dynamic_station_traits) + threat_level = max(threat_level - GLOB.dynamic_station_traits[station_trait], 0) + log_dynamic("Threat reduced by [GLOB.dynamic_station_traits[station_trait]]. Source: [type].") + if (SSticker.totalPlayersReady < low_pop_player_threshold) threat_level = min(threat_level, LERP(low_pop_maximum_threat, max_threat_level, SSticker.totalPlayersReady / low_pop_player_threshold)) @@ -442,6 +448,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) continue vars[variable] = configuration["Dynamic"][variable] + configure_station_trait_costs() setup_parameters() setup_hijacking() setup_shown_threat() @@ -777,6 +784,26 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1) if(CONFIG_GET(flag/protect_assistant_from_antagonist)) ruleset.restricted_roles |= JOB_ASSISTANT +/// Get station traits and call for their config +/datum/game_mode/dynamic/proc/configure_station_trait_costs() + if(!CONFIG_GET(flag/dynamic_config_enabled)) + return + for(var/datum/station_trait/station_trait as anything in GLOB.dynamic_station_traits) + configure_station_trait(station_trait) + +/// Apply configuration for station trait costs +/datum/game_mode/dynamic/proc/configure_station_trait(datum/station_trait/station_trait) + var/list/station_trait_config = LAZYACCESSASSOC(configuration, "Station", station_trait.dynamic_threat_id) + var/cost = station_trait_config["cost"] + + if(isnull(cost)) //0 is valid so check for null specifically + return + + if(cost != GLOB.dynamic_station_traits[station_trait]) + log_dynamic("Config set [station_trait.dynamic_threat_id] cost from [station_trait.threat_reduction] to [cost]") + + GLOB.dynamic_station_traits[station_trait] = cost + /// Refund threat, but no more than threat_level. /datum/game_mode/dynamic/proc/refund_threat(regain) mid_round_budget = min(threat_level, mid_round_budget + regain) diff --git a/code/game/gamemodes/dynamic/readme.md b/code/game/gamemodes/dynamic/readme.md index e584ac4ee4a..d663e88a239 100644 --- a/code/game/gamemodes/dynamic/readme.md +++ b/code/game/gamemodes/dynamic/readme.md @@ -130,6 +130,11 @@ The format of this file is: "Latejoin": { /* Same as "Roundstart", but for `/datum/dynamic_ruleset/latejoin` instead. */ + }, + + "Station": { + /* Special threat reductions for dangerous station traits. Traits are selected before dynamic, so traits will always */ + /* reduce threat even if there's no threat for it available. Only "cost" can be modified */ } } ``` diff --git a/code/game/machinery/nebula_shielding.dm b/code/game/machinery/nebula_shielding.dm new file mode 100644 index 00000000000..091f97bb947 --- /dev/null +++ b/code/game/machinery/nebula_shielding.dm @@ -0,0 +1,129 @@ +///Machinery that block nebulas. This type is for convenience, you can set nebula shielding on other objects as well using add_to_nebula_shielding() +/obj/machinery/nebula_shielding + density = TRUE + + icon = 'icons/obj/machines/nebula_shielding.dmi' + pixel_x = -16 + + layer = FLY_LAYER + plane = ABOVE_GAME_PLANE + + ///Strength of the shield we apply + var/shielding_strength + ///The type of nebula that we shield against + var/nebula_type + ///How much power we use every time we block the nebula's effects + var/power_use_per_block = BASE_MACHINE_ACTIVE_CONSUMPTION * 2 + ///State we use when actively blocking a nebula + var/active_icon_state + +/obj/machinery/nebula_shielding/Initialize(mapload) + . = ..() + + add_to_nebula_shielding(src, nebula_type, PROC_REF(get_nebula_shielding)) + +///Nebula is asking us how strong we are. Return our shield strength is all is well +/obj/machinery/nebula_shielding/proc/get_nebula_shielding() + if(panel_open) + return + if(!powered()) + icon_state = initial(icon_state) + return + + use_power_from_net(power_use_per_block) + generate_reward() + icon_state = active_icon_state + return shielding_strength + +///Generate a resource for defending against the nebula +/obj/machinery/nebula_shielding/proc/generate_reward() + return + +///Short-lived nebula shielding sent by centcom in-case there hasn't been shielding for a while +/obj/machinery/nebula_shielding/emergency + density = TRUE + anchored = FALSE //so some handsome rogue could potentially move it off the station z-level + shielding_strength = 999 //should block the nebula completely + + ///How long we work untill we self-destruct + var/detonate_in = 10 MINUTES + +/obj/machinery/nebula_shielding/emergency/Initialize(mapload) + . = ..() + + addtimer(CALLBACK(src, PROC_REF(self_destruct)), detonate_in) + +///We don't live for very long, so self-destruct +/obj/machinery/nebula_shielding/emergency/proc/self_destruct() + explosion(src, light_impact_range = 5, flame_range = 3, explosion_cause = src) + qdel(src) + +/obj/machinery/nebula_shielding/emergency/examine(mob/user) + . = ..() + + . += span_notice("[p_They()] will block the nebula for [round(detonate_in / (1 MINUTES))] minute\s with a shield strength of [shielding_strength].") + +/obj/machinery/nebula_shielding/emergency/get_nebula_shielding() + return shielding_strength //no strings attached, we will always produce shielding + +/obj/machinery/nebula_shielding/emergency/generate_reward() + return //no reward for you + +///We shield against the radioactive nebula and passively generate tritium +/obj/machinery/nebula_shielding/radiation + name = "radioactive nebula shielder" + desc = "Generates a field around the station, protecting it from a radioactive nebula." + + icon_state = "radioactive_shielding" + active_icon_state = "radioactive_shielding_on" + + circuit = /obj/item/circuitboard/machine/radioactive_nebula_shielding + + nebula_type = /datum/station_trait/nebula/hostile/radiation + shielding_strength = 4 + +/obj/machinery/nebula_shielding/radiation/examine(mob/user) + . = ..() + + . += span_notice("Passively generates tritium. Provides [shielding_strength] levels of nebula shielding when active.") + +/obj/machinery/nebula_shielding/radiation/generate_reward() + var/turf/open/turf = get_turf(src) + if(isopenturf(turf)) + turf.atmos_spawn_air("[GAS_TRITIUM]=1;[TURF_TEMPERATURE(T20C)]") + +/obj/machinery/nebula_shielding/radiation/attackby(obj/item/item, mob/user, params) + if(default_deconstruction_screwdriver(user, initial(icon_state) + "_open", initial(icon_state), item)) + return + + if(default_deconstruction_crowbar(item)) + return + + return ..() + +///Emergency shielding so people aren't permanently in a radstorm if shit goes very wrong in engineering +/obj/machinery/nebula_shielding/emergency/radiation + name = "emergency nebula radiation shielder" + desc = "Generates a field around the station to protect it from a radioactive nebula." + + icon = 'icons/obj/machines/engine/other.dmi' + icon_state = "portgen1_1" + pixel_x = 0 + + nebula_type = /datum/station_trait/nebula/hostile/radiation + +/obj/machinery/nebula_shielding/emergency/radiation/self_destruct() + var/turf/open/turf = get_turf(src) + if(isopenturf(turf)) + turf.atmos_spawn_air("[GAS_TRITIUM]=50;[TURF_TEMPERATURE(T20C)]") //causes a small tritium fire when combined with the explosion + + ..() + +///Small explanation for engineering on how to set-up the radioactive nebula shielding +/obj/item/paper/fluff/radiation_nebula + name = "radioactive nebula shielding" + default_raw_text = {"EXTREME IMPORTANCE!!!!
+ Set up these radioactive nebula shielding units before the gravity generators native shielding is overwhelmed!
+ Shielding units passively generate tritium, so make sure to properly ventilate/isolate the area before setting up a shielding unit! + More circuit boards can be ordered through cargo. Consider setting up auxillary shielding units in-case of destruction, power loss or sabotage. + "} diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm index 1c74766ac69..2c6d01b5bc4 100644 --- a/code/game/objects/effects/decals/decal.dm +++ b/code/game/objects/effects/decals/decal.dm @@ -64,9 +64,10 @@ // If the tile uses holiday colors, apply them here if(use_holiday_colors) - var/current_holiday_color = request_holiday_colors(src, pattern) - if(current_holiday_color) - color = current_holiday_color + + var/custom_color = request_station_colors(src, pattern) || request_holiday_colors(src, pattern) + if(custom_color) + color = custom_color alpha = DECAL_ALPHA var/turf/T = loc diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 825e8bd9d9d..8af2c2bbfc6 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -1499,3 +1499,13 @@ greyscale_colors = CIRCUIT_COLOR_SCIENCE build_path = /obj/machinery/navbeacon req_components = list() + +/obj/item/circuitboard/machine/radioactive_nebula_shielding + name = "Radioactive Nebula Shielding" + greyscale_colors = CIRCUIT_COLOR_ENGINEERING + build_path = /obj/machinery/nebula_shielding/radiation + req_components = list( + /datum/stock_part/capacitor = 2, + /obj/item/mod/module/rad_protection = 1, + /obj/item/stack/sheet/plasteel = 2, + ) diff --git a/code/game/objects/items/storage/boxes/job_boxes.dm b/code/game/objects/items/storage/boxes/job_boxes.dm index abf63629000..118229146eb 100644 --- a/code/game/objects/items/storage/boxes/job_boxes.dm +++ b/code/game/objects/items/storage/boxes/job_boxes.dm @@ -42,6 +42,9 @@ new /obj/item/flashlight/flare(src) new /obj/item/radio/off(src) + if(HAS_TRAIT(SSstation, STATION_TRAIT_RADIOACTIVE_NEBULA)) + new /obj/item/storage/pill_bottle/potassiodide(src) + /obj/item/storage/box/survival/radio/PopulateContents() ..() // we want the survival stuff too. new /obj/item/radio/off(src) diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm index 57135bf0bda..2804ebfe00d 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm @@ -21,6 +21,10 @@ anchored = TRUE /obj/structure/closet/emcloset/Initialize(mapload) + if(HAS_TRAIT(SSstation, STATION_TRAIT_RADIOACTIVE_NEBULA) && prob(30)) + new /obj/structure/closet/radiation(loc) + return INITIALIZE_HINT_QDEL + . = ..() if (prob(1)) diff --git a/code/game/turfs/open/floor/glass.dm b/code/game/turfs/open/floor/glass.dm index beb1535e694..af2a0bec5bf 100644 --- a/code/game/turfs/open/floor/glass.dm +++ b/code/game/turfs/open/floor/glass.dm @@ -21,8 +21,8 @@ var/list/glow_stuff /// How much alpha to leave when cutting away emissive blockers var/alpha_to_leave = 255 - /// Color of starlight to use - var/starlight_color = COLOR_STARLIGHT + /// Color of starlight to use. Defaults to STARLIGHT_COLOR if not set + var/starlight_color /turf/open/floor/glass/broken_states() return list("glass-damaged1", "glass-damaged2", "glass-damaged3") @@ -51,7 +51,7 @@ return glow_stuff = partially_block_emissives(src, alpha_to_leave) - set_light(2, 0.75, starlight_color) + set_light(2, 0.75, starlight_color || GLOB.starlight_color) /turf/open/floor/glass/make_plating() return diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm index 024dcd80a89..04eedb6619f 100644 --- a/code/game/turfs/open/space/space.dm +++ b/code/game/turfs/open/space/space.dm @@ -1,3 +1,6 @@ +///The color of light space emits +GLOBAL_VAR_INIT(starlight_color, COLOR_STARLIGHT) + /turf/open/space icon = 'icons/turf/space.dmi' icon_state = "space" @@ -21,7 +24,6 @@ plane = PLANE_SPACE layer = SPACE_LAYER light_power = 0.75 - light_color = COLOR_STARLIGHT space_lit = TRUE bullet_bounce_sound = null vis_flags = VIS_INHERIT_ID //when this be added to vis_contents of something it be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf. @@ -54,6 +56,7 @@ stack_trace("Warning: [src]([type]) initialized multiple times!") flags_1 |= INITIALIZED_1 + light_color = GLOB.starlight_color // We make the assumption that the space plane will never be blacklisted, as an optimization if(SSmapping.max_plane_offset) diff --git a/code/modules/cargo/packs/_packs.dm b/code/modules/cargo/packs/_packs.dm index 479d2595c02..357f32f3cb4 100644 --- a/code/modules/cargo/packs/_packs.dm +++ b/code/modules/cargo/packs/_packs.dm @@ -79,6 +79,21 @@ /datum/supply_pack/proc/generate_supply_packs() return +///Easily send a supplypod to an area +/proc/send_supply_pod_to_area(contents, area_type, pod_type = /obj/structure/closet/supplypod) + var/list/areas = get_areas(area_type) + if(!LAZYLEN(areas)) + return FALSE + var/list/open_turfs = list() + for(var/turf/open/floor/found_turf in get_area_turfs(pick(areas), subtypes = TRUE)) + open_turfs += found_turf + + if(!length(open_turfs)) + return FALSE + + new /obj/effect/pod_landingzone (pick(open_turfs), new pod_type (), contents) + return TRUE + /** * Custom supply pack * The contents are given on New rather than being static diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm index 1c3ec6c813a..ef6e0a9e726 100644 --- a/code/modules/cargo/packs/engineering.dm +++ b/code/modules/cargo/packs/engineering.dm @@ -292,3 +292,25 @@ crate_name = "HFR crate" crate_type = /obj/structure/closet/crate/secure/engineering dangerous = TRUE + +/datum/supply_pack/engineering/rad_protection_modules + name = "Radiation Protection Modules" + desc = "Contains multiple radiation protections modules for MODsuits." + hidden = TRUE + contains = list(/obj/item/mod/module/rad_protection = 3) + crate_name = "modsuit radiation modules" + crate_type = /obj/structure/closet/crate/engineering + +/datum/supply_pack/engineering/rad_nebula_shielding_kit + name = "Radioactive Nebula Shielding" + desc = "Contains circuitboards and radiation modules for constructing radioactive nebula shielding." + cost = CARGO_CRATE_VALUE * 2 + + special = TRUE + contains = list( + /obj/item/mod/module/rad_protection = 5, + /obj/item/circuitboard/machine/radioactive_nebula_shielding = 5, + /obj/item/paper/fluff/radiation_nebula = 1, + ) + crate_name = "radioactive nebula shielding (IMPORTANT)" + crate_type = /obj/structure/closet/crate/engineering diff --git a/code/modules/cargo/packs/materials.dm b/code/modules/cargo/packs/materials.dm index a6fdd73e77f..68dacd730be 100644 --- a/code/modules/cargo/packs/materials.dm +++ b/code/modules/cargo/packs/materials.dm @@ -82,14 +82,6 @@ crate_name = "fuel tank crate" crate_type = /obj/structure/closet/crate/large -/datum/supply_pack/materials/hightank - name = "Large Water Tank Crate" - desc = "Contains a high-capacity water tank. Useful for botany or other service jobs." - cost = CARGO_CRATE_VALUE * 2.4 - contains = list(/obj/structure/reagent_dispensers/watertank/high) - crate_name = "high-capacity water tank crate" - crate_type = /obj/structure/closet/crate/large - /datum/supply_pack/materials/hightankfuel name = "Large Fuel Tank Crate" desc = "Contains a high-capacity fuel tank. Keep contents away from open flame." diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm index c11130dcbc1..f7e1ca2d868 100644 --- a/code/modules/lighting/lighting_area.dm +++ b/code/modules/lighting/lighting_area.dm @@ -64,7 +64,7 @@ lighting_effect.layer = LIGHTING_PRIMARY_LAYER lighting_effect.blend_mode = BLEND_ADD lighting_effect.alpha = base_lighting_alpha - lighting_effect.color = base_lighting_color + lighting_effect.color = (base_lighting_color == COLOR_STARLIGHT ? GLOB.starlight_color : base_lighting_color) lighting_effect.appearance_flags = RESET_TRANSFORM | RESET_ALPHA | RESET_COLOR lighting_effects += lighting_effect add_overlay(lighting_effects[1]) diff --git a/code/modules/lighting/static_lighting_area.dm b/code/modules/lighting/static_lighting_area.dm index dc8e9c48879..7dc6cc6c3d8 100644 --- a/code/modules/lighting/static_lighting_area.dm +++ b/code/modules/lighting/static_lighting_area.dm @@ -7,7 +7,7 @@ GLOBAL_LIST_INIT_TYPED(fullbright_overlays, /mutable_appearance, list(create_ful SET_PLANE_W_SCALAR(lighting_effect, LIGHTING_PLANE, offset) lighting_effect.layer = LIGHTING_PRIMARY_LAYER lighting_effect.blend_mode = BLEND_ADD - lighting_effect.color = COLOR_STARLIGHT + lighting_effect.color = GLOB.starlight_color return lighting_effect /area diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index f57b7b4f20d..6ce40d8c1a3 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -78,25 +78,6 @@ /obj/machinery/vending, /obj/structure/window, )) - /// Weighted list of colours a carp can be - /// Weighted list of usual carp colors - var/static/list/carp_colors = list( - COLOR_CARP_PURPLE = 7, - COLOR_CARP_PINK = 7, - COLOR_CARP_GREEN = 7, - COLOR_CARP_GRAPE = 7, - COLOR_CARP_SWAMP = 7, - COLOR_CARP_TURQUOISE = 7, - COLOR_CARP_BROWN = 7, - COLOR_CARP_TEAL = 7, - COLOR_CARP_LIGHT_BLUE = 7, - COLOR_CARP_RUSTY = 7, - COLOR_CARP_RED = 7, - COLOR_CARP_YELLOW = 7, - COLOR_CARP_BLUE = 7, - COLOR_CARP_PALE_GREEN = 7, - COLOR_CARP_SILVER = 1, // The rare silver carp - ) /mob/living/basic/carp/Initialize(mapload, mob/tamer) ADD_TRAIT(src, TRAIT_FREE_HYPERSPACE_MOVEMENT, INNATE_TRAIT) //Need to set before init cause if we init in hyperspace we get dragged before the trait can be added @@ -138,7 +119,7 @@ /mob/living/basic/carp/proc/apply_colour() if (!greyscale_config) return - set_greyscale(colors = list(pick_weight(carp_colors))) + set_greyscale(colors = list(pick_weight(GLOB.carp_colors))) /// Called when another mob has forged a bond of friendship with this one, passed the taming mob as 'tamer' /mob/living/basic/carp/proc/on_tamed(mob/tamer, feedback = TRUE) diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index c2621aac9ce..4d32559ce71 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -148,6 +148,9 @@ GLOBAL_LIST_EMPTY(gravity_generators) /// Audio for when the gravgen is on var/datum/looping_sound/gravgen/soundloop + ///Amount of shielding we offer against a radioactive nebula + var/radioactive_nebula_shielding = 4 + ///Station generator that spawns with gravity turned off. /obj/machinery/gravity_generator/main/off on = FALSE @@ -162,6 +165,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) enable() center_part.add_overlay("activated") + add_to_nebula_shielding(src, /datum/station_trait/nebula/hostile/radiation, PROC_REF(get_radioactive_nebula_shielding)) + /obj/machinery/gravity_generator/main/Destroy() // If we somehow get deleted, remove all of our other parts. investigate_log("was destroyed!", INVESTIGATE_GRAVITY) disable() @@ -467,6 +472,10 @@ GLOBAL_LIST_EMPTY(gravity_generators) for(var/obj/machinery/gravity_generator/part as anything in generator_parts) SET_PLANE(part, PLANE_TO_TRUE(part.plane), new_turf) +/// Returns the radioactive shielding (if there's a radioactive nebula). Called from a callback set in add_to_nebula_shielding() +/obj/machinery/gravity_generator/main/proc/get_radioactive_nebula_shielding() + return on ? radioactive_nebula_shielding : 0 + //prevents shuttles attempting to rotate this since it messes up sprites /obj/machinery/gravity_generator/main/shuttleRotate(rotation, params) params = NONE diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index f958414c1d3..88e205e8240 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1668,6 +1668,12 @@ ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 3500) +/datum/techweb_node/mod_advanced_engineering/New() + if(HAS_TRAIT(SSstation, STATION_TRAIT_RADIOACTIVE_NEBULA)) //we'll really need the rad protection modsuit module + starting_node = TRUE + + return ..() + /datum/techweb_node/mod_medical id = "mod_medical" display_name = "Medical Modular Suits" diff --git a/config/dynamic.json b/config/dynamic.json index 53f2eaa6560..a4a1eb7ebdb 100644 --- a/config/dynamic.json +++ b/config/dynamic.json @@ -154,5 +154,10 @@ "Stowaway Changeling": { "weight": 0 } + }, + + "Station": { + "Radioactive Nebula": { + } } } diff --git a/icons/effects/weather_effects.dmi b/icons/effects/weather_effects.dmi index 00083c464a2..fa21b2d12eb 100644 Binary files a/icons/effects/weather_effects.dmi and b/icons/effects/weather_effects.dmi differ diff --git a/icons/obj/machines/nebula_shielding.dmi b/icons/obj/machines/nebula_shielding.dmi new file mode 100644 index 00000000000..32aedcc1b7a Binary files /dev/null and b/icons/obj/machines/nebula_shielding.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 8a92d746feb..b0817d49741 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1626,6 +1626,7 @@ #include "code\game\machinery\medipen_refiller.dm" #include "code\game\machinery\modular_shield.dm" #include "code\game\machinery\navbeacon.dm" +#include "code\game\machinery\nebula_shielding.dm" #include "code\game\machinery\PDApainter.dm" #include "code\game\machinery\prisongate.dm" #include "code\game\machinery\prisonlabor.dm"