diff --git a/code/ATMOSPHERICS/components/binary_devices/station_boiler.dm b/code/ATMOSPHERICS/components/binary_devices/station_boiler.dm new file mode 100644 index 0000000000..13cf28ecb7 --- /dev/null +++ b/code/ATMOSPHERICS/components/binary_devices/station_boiler.dm @@ -0,0 +1,212 @@ +/obj/structure/stationboiler + name = "Station Boiler" + desc = "A large, phoron-infused wood powered, super boiler. Capable of keeping a entire colony heated up" + icon = 'icons/obj/machines/heat_boiler.dmi' + icon_state = "boiler_off" + anchored = TRUE + density = TRUE + pixel_x = -32 + layer = UNDER_JUNK_LAYER + + VAR_PRIVATE/is_active = FALSE // Check init for actual auto startup + VAR_PRIVATE/target_heat_temperature = T20C //The temperature we want the pipes to be heated to + VAR_PRIVATE/wood_per_process = 1SECOND + VAR_PRIVATE/list/stored_material = list( + MAT_LOG = 1 HOUR, //1 hour of mats free + MAT_WOODEN_STICK = 0, + MAT_WOOD = 0, + MAT_SIFWOOD = 0, + MAT_SIFLOG = 0, + MAT_HARDWOOD = 0, + MAT_HARDLOG = 0, + MAT_WOODEN_STICK = 0, + MAT_BIRCHWOOD = 0, + MAT_PINEWOOD = 0, + MAT_OAKWOOD = 0, + MAT_ACACIAWOOD = 0, + MAT_REDWOOD = 0, + ) + VAR_PRIVATE/storage_capacity = 4 HOURS + VAR_PRIVATE/datum/looping_sound/oven/boiler_loop + +/obj/structure/stationboiler/Initialize(mapload) + . = ..() + SSstationheater.boilers += src + update_icon() + START_PROCESSING(SSobj, src) + boiler_loop = new(list(src), FALSE) + set_state(TRUE) + +/obj/structure/stationboiler/Destroy() + QDEL_NULL(boiler_loop) + STOP_PROCESSING(SSobj, src) + SSstationheater.boilers -= src + . = ..() + +/obj/structure/stationboiler/process() + var/list/available_mats = list() + for(var/mat_check in stored_material) + if(stored_material[mat_check] <= 0) + continue + available_mats += mat_check + + if(!length(available_mats)) //Out of wood + set_state(FALSE) + return + + var/mat_drain = pick(available_mats) + stored_material[mat_drain] = max(stored_material[mat_drain] - wood_per_process, 0) + +/obj/structure/stationboiler/proc/set_state(activated) + if(activated == is_active) + return + is_active = activated + update_icon() + if(is_active) + set_light(4, 3, "#e9400dff") + boiler_loop.start(src) + return + set_light(0) + boiler_loop.stop(src) + +/obj/structure/stationboiler/update_icon() + if(is_active) + icon_state = "boiler_on" + return + icon_state = "boiler_off" + +/obj/structure/stationboiler/attack_ai(mob/user) + add_hiddenprint(user) + tgui_interact(user) + +/obj/structure/stationboiler/attack_hand(mob/user) + add_fingerprint(user) + tgui_interact(user) + +/obj/structure/stationboiler/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "StationBoiler", name) + ui.open() + +/obj/structure/stationboiler/tgui_act(action, params) + if(..()) + return TRUE + switch(action) + if("ejectMaterial") + var/matName = params["mat"] + if(!(matName in stored_material)) + return FALSE + eject_materials(matName) + return TRUE + + if("ignite") + try_ignite() + return TRUE + +/obj/structure/stationboiler/tgui_data(mob/user) + var/list/data = list() + var/list/materials_ui = list() + for(var/M in stored_material) + if(stored_material[M] > 0) + var/datum/material/matdata = get_material_by_name(M) + var/obj/item/stack/material/stack_type = matdata.stack_type + var/stack_units = initial(stack_type.perunit) + var/sheet_count = FLOOR(stored_material[M] / stack_units, 1) + materials_ui[++materials_ui.len] = list( + "name" = M, + "display" = material_display_name(M), + "qty" = "[sheet_count] [sheet_count > 1 ? matdata.sheet_plural_name : matdata.sheet_singular_name]", // Show the number of sheets + "can_eject" = (stored_material[M] >= stack_units)) + data["materials"] = materials_ui + + var/mat_store = get_total_stored_mats() + data["stored"] = mat_store + data["max"] = storage_capacity + data["timeleft"] = get_time_left() + + return data + +/obj/structure/stationboiler/proc/get_total_stored_mats() + var/mat_store = 0 + for(var/mat_check in stored_material) + mat_store += stored_material[mat_check] + return mat_store + +/obj/structure/stationboiler/proc/get_time_left() + var/org_ticks = get_total_stored_mats()/wood_per_process + var/second = org_ticks % 60 + var/minute = FLOOR((org_ticks / 60), 1) % 60 + var/hours = FLOOR((org_ticks / 3600), 1) + if(second < 10) + second = "0[second]" + if(minute < 10) + minute = "0[minute]" + return "[hours]:[minute]:[second]" + +/obj/structure/stationboiler/ex_act(severity) + return + +/obj/structure/stationboiler/proc/try_ignite() + if(stored_material[MAT_LOG] >= wood_per_process) + set_state(TRUE) + +/obj/structure/stationboiler/proc/is_heating() + return is_active + +/obj/structure/stationboiler/proc/is_on_same_planet(obj/machinery/stationboiler_radiator/rad) + var/turf/rad_turf = get_turf(rad) + var/turf/our_turf = get_turf(src) + return AreConnectedZLevels(rad_turf.z,our_turf.z) + +/obj/structure/stationboiler/proc/try_load_materials(mob/user, obj/item/stack/material/S) + if(!istype(S)) + return FALSE + + // Can we even put the material in? + if(!(S.material.name in stored_material)) + to_chat(user, span_warning("\The [src] doesn't accept [material_display_name(S.material)]!")) + return TRUE + + // Check if there is room left to store a sheet of the material + var/remaining_stack_space = FLOOR((storage_capacity - get_total_stored_mats()) / S.perunit, 1) + if(remaining_stack_space < 1) + to_chat(user, span_warning("\The [src] cannot hold more [S.name].")) + return TRUE + + // Put the material in, convert the sheet into material units + var/mat_name = S.material.name + var/inserting_sheets = S.get_amount() + inserting_sheets = min(inserting_sheets, remaining_stack_space) + stored_material[mat_name] += inserting_sheets * S.perunit + S.use(inserting_sheets) + user.visible_message("\The [user] inserts [mat_name] into \the [src].", span_notice("You insert [inserting_sheets] [mat_name]\s into \the [src].")) + return TRUE + +/obj/structure/stationboiler/attackby(obj/item/W as obj, mob/user as mob) + add_fingerprint(user) + if(try_load_materials(user, W)) + return + else + to_chat(user, span_notice("You cannot insert this item into \the [src]!")) + return + +// 0 amount = 0 means ejecting a full stack; -1 means eject everything +/obj/structure/stationboiler/proc/eject_materials(material_name) + var/datum/material/matdata = get_material_by_name(material_name) + var/obj/item/stack/material/stack_type = matdata.stack_type + var/stack_units = initial(stack_type.perunit) + + var/sheets_loaded = FLOOR(stored_material[material_name] / stack_units, 1) + if(sheets_loaded < 1) + return + var/amount = sheets_loaded + + // Drop stacks of sheets till empty + while(amount >= 1) + var/obj/item/stack/material/S = new stack_type(get_turf(src)) + var/export_amount = min(amount, S.max_amount) + S.set_amount(export_amount) + S.update_icon() + stored_material[material_name] -= stack_units * export_amount + amount -= export_amount diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index 986f060e7d..ce1b5bcb80 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -59,6 +59,7 @@ What is the naming convention for planes or layers? // Turf Planes #define PLATING_PLANE -44 // Plating + #define FLOOR_HEATER_LAYER 2.03 // Under objects, even when planeswapped #define DISPOSAL_LAYER 2.1 // Under objects, even when planeswapped #define PIPES_LAYER 2.2 // Under objects, even when planeswapped #define WIRES_LAYER 2.3 // Under objects, even when planeswapped diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 8923b2391b..68cd6816be 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -120,11 +120,14 @@ #define AREA_BLOCK_GHOST_SIGHT 0x2000 // If an area blocks sight for ghosts #define AREA_BLOCK_INSTANT_BUILDING 0x4000 // If an area blocks the usage of instant building creation items/mechanics such as shelter capsules #define AREA_ALWAYS_HAS_GRAVITY 0x8000 // If an area should always have gravity, even during events that would otherwise remove it. -// The 0x800000 is blocked by INITIALIZED, do NOT use it! - +#define AREA_CRYOPLANET_SHIELDED 0x10000 // If an area is protected from SScryoplanet temperature shifts +// UNUSED 0x20000 +// UNUSED 0x40000 +// UNUSED 0x80000 #define PHASE_SHIELDED 0x100000 // A less rough way to prevent phase shifting without blocking access //VOREStation Note: Not implemented on VS. Used downstream. #define AREA_LIMIT_DARK_RESPITE 0x200000 // Shadekin will die normally in those areas //VOREStation Note: Not implemented on VS. Used downstream. #define AREA_ALLOW_CLOCKOUT 0x400000 // The PDA timeclock app can only be used in these areas //VOREStation Note: Not implemented on VS. Used downstream. +// The 0x800000 is blocked by INITIALIZED, do NOT use it! // OnTopic return values #define TOPIC_NOACTION 0 diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 966d9c5862..1e57930173 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -131,6 +131,8 @@ #define FIRE_PRIORITY_CHARSETUP 25 #define FIRE_PRIORITY_AIRFLOW 30 #define FIRE_PRIORITY_AIR 35 +#define FIRE_PRIORITY_STATIONBOILER 36 +#define FIRE_PRIORITY_CRYOPLANETS 37 #define FIRE_PRIORITY_BURNING 40 #define FIRE_PRIORITY_OBJ 40 #define FIRE_PRIORITY_PROCESS 45 diff --git a/code/controllers/subsystems/air.dm b/code/controllers/subsystems/air.dm index 518f412517..c8d049489b 100644 --- a/code/controllers/subsystems/air.dm +++ b/code/controllers/subsystems/air.dm @@ -363,6 +363,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun // Reset all the lists zones.Cut() + SScryoplanets.cryo_zones.Cut() edges.Cut() tiles_to_update.Cut() zones_to_update.Cut() @@ -381,10 +382,12 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun zones.Add(z) z.name = "Zone [next_id++]" mark_zone_update(z) + SScryoplanets.cryo_zones.Add(z) /datum/controller/subsystem/air/proc/remove_zone(datum/zone/z) zones.Remove(z) zones_to_update.Remove(z) + SScryoplanets.cryo_zones.Remove(z) /datum/controller/subsystem/air/proc/air_blocked(turf/A, turf/B) #ifdef ZASDBG diff --git a/code/controllers/subsystems/cryoplanets.dm b/code/controllers/subsystems/cryoplanets.dm new file mode 100644 index 0000000000..7f78c40206 --- /dev/null +++ b/code/controllers/subsystems/cryoplanets.dm @@ -0,0 +1,97 @@ +#define THERMAL_ENERGY_CHANGE 12000 + +SUBSYSTEM_DEF(cryoplanets) + name = "Cryogenic Planets" + wait = 8 SECOND + runlevels = RUNLEVEL_GAME + priority = FIRE_PRIORITY_STATIONBOILER + flags = SS_BACKGROUND // Extremely low priority + + dependencies = list( + /datum/controller/subsystem/planets, + /datum/controller/subsystem/air + ) + + var/list/cryo_zones = list() + VAR_PRIVATE/list/current_run = list() + +/datum/controller/subsystem/cryoplanets/Initialize() + for(var/datum/planet/check in SSplanets.planets) + if(check.cryogenic_temp_shift) + return SS_INIT_SUCCESS + flags |= SS_NO_FIRE + cryo_zones.Cut() // Populated by Zone/add_zone() which is done by SSair before this. Clear those hardrefs if we are not gonna use them! + return SS_INIT_NO_NEED // No cryoplanets to deal with! + +/datum/controller/subsystem/cryoplanets/stat_entry(msg) + msg = " Cr: [length(current_run)] | Zs: [length(cryo_zones)]" + . = ..() + +/datum/controller/subsystem/cryoplanets/fire(resumed) + if(!resumed) + current_run = cryo_zones.Copy() + + while(length(current_run)) + var/datum/zone/zone = current_run[length(current_run)] + current_run.len-- + if(QDELETED(zone) || zone.invalid) + cryo_zones.Remove(zone) + continue + + var/turf/T = pick(zone.contents) + if(!SScryoplanets.is_station_temp_change_turf(T)) + cryo_zones.Remove(zone) + continue + + equalize_temperature_to_planet(T, zone, THERMAL_ENERGY_CHANGE) + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/cryoplanets/proc/equalize_temperature_to_planet(turf/T, datum/zone/zone, max_thermal_change) + var/datum/gas_mixture/currentAir = zone.air + if(!currentAir) + return + + // Get the temperature energy needed to shift the zones' temp toward our goal... Maxes out at THERMAL_ENERGY_CHANGE per subsystem tick + var/datum/planet/P = SSplanets.z_to_planet[T.z] + var/target_temp = P.weather_holder.temperature + var/neededEnergy = currentAir.get_thermal_energy_change(target_temp) + if(neededEnergy > 0) + neededEnergy = min(neededEnergy, max_thermal_change) + else + neededEnergy = max(neededEnergy, -max_thermal_change) + + // Check if this would be affected by the station mainboiler's radiators. + var/area/check_area = get_area(T) // If we have an active radiator in the area, then there is no point in starting a temperature war.... + if(check_area && length(SSstationheater.radiators) && target_temp < SSstationheater.target_heat_temperature) + var/obj/structure/stationboiler/current_heater = SSstationheater.get_current_boiler() + if(current_heater?.is_heating()) // If the current boiler isn't on... It won't matter. + for(var/obj/machinery/stationboiler_radiator/radiator in SSstationheater.radiators) + CHECK_TICK + if(get_area(radiator) != check_area) + continue + return // Area had an active radiator, we'll drop out! + //testing("Energy: [neededEnergy]") + currentAir.add_thermal_energy(neededEnergy) + +/datum/controller/subsystem/cryoplanets/proc/is_station_temp_change_turf(turf/T) + if(!issimulatedturf(T)) // What are you even doing? + return FALSE + + if(T.is_outdoors()) // Radiators can't help you outside + return FALSE + + if(T.z > length(SSplanets.z_to_planet)) // Not even on a planet + return FALSE + + var/datum/planet/P = SSplanets.z_to_planet[T.z] + if(!P || !P.cryogenic_temp_shift) // Not on a temp shifting planet + return FALSE + + var/area/area_check = get_area(T) //Do not freeze dorms + if(!area_check || (area_check.flags & AREA_CRYOPLANET_SHIELDED)) + return FALSE + + return TRUE + +#undef THERMAL_ENERGY_CHANGE diff --git a/code/controllers/subsystems/stationheater.dm b/code/controllers/subsystems/stationheater.dm new file mode 100644 index 0000000000..53b8ad8957 --- /dev/null +++ b/code/controllers/subsystems/stationheater.dm @@ -0,0 +1,77 @@ +#define THERMAL_ENERGY_CHANGE 12000 + +SUBSYSTEM_DEF(stationheater) + name = "Station Boiler" + wait = 8 SECOND + runlevels = RUNLEVEL_GAME + priority = FIRE_PRIORITY_CRYOPLANETS + flags = SS_BACKGROUND // Extremely low priority + + dependencies = list( + /datum/controller/subsystem/planets, + /datum/controller/subsystem/air + ) + + var/list/boilers = list() + var/list/radiators = list() + VAR_PRIVATE/list/current_run = list() + VAR_PRIVATE/datum/weakref/current_loop_heater + + // Radiator power + var/static/target_heat_temperature = T20C //The temperature the radiator wants the room to reach + +/datum/controller/subsystem/stationheater/Initialize() + for(var/datum/planet/check in SSplanets.planets) + if(check.cryogenic_temp_shift) + return SS_INIT_SUCCESS + flags |= SS_NO_FIRE + return SS_INIT_NO_NEED // No cryoplanets to deal with! + +/datum/controller/subsystem/stationheater/stat_entry(msg) + msg = " Cr: [length(current_run)] | Br: [length(boilers)] | Rs: [length(radiators)]" + . = ..() + +/datum/controller/subsystem/stationheater/fire(resumed) + if(!length(boilers)) // No boilers? No point, but keep running incase we get one + return + + // Get current run of all radiators on station + if(!resumed) + current_run = radiators.Copy() + current_loop_heater = WEAKREF(pick(boilers)) + + // Process the current run until all raditors are done heating + var/obj/structure/stationboiler/current_heater = get_current_boiler() + while(length(current_run)) + var/obj/machinery/stationboiler_radiator/rad = current_run[length(current_run)] + current_run.len-- + if(!QDELETED(rad)) + handle_radiate(rad, current_heater) + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/stationheater/proc/get_current_boiler() + return current_loop_heater?.resolve() + +/datum/controller/subsystem/stationheater/proc/handle_radiate(obj/machinery/stationboiler_radiator/radiator, obj/structure/stationboiler/assigned_boiler) + // Check if this boiler is OUR boiler..... And by that just see if it's in the same zlevel complex. + if(!assigned_boiler?.is_on_same_planet(radiator)) + return + + // Update radiator icon if the main boiler is functioning or not + radiator.set_state(assigned_boiler?.is_heating()) + radiator.update_icon() + + // Remove us if we are in an invalid radiator turf + var/turf/radiator_turf = get_turf(radiator) + if(!SScryoplanets.is_station_temp_change_turf(radiator_turf)) + return + + // If the temp is colder than the radiator, begin heating! + var/datum/gas_mixture/environment = radiator_turf.return_air() + var/neededEnergy = environment.get_thermal_energy_change(target_heat_temperature) + if(neededEnergy > 0) + neededEnergy = min(neededEnergy, THERMAL_ENERGY_CHANGE) + environment.add_thermal_energy(neededEnergy) + +#undef THERMAL_ENERGY_CHANGE diff --git a/code/game/area/asteroid_areas.dm b/code/game/area/asteroid_areas.dm index c25e0e3c15..dceaf48225 100644 --- a/code/game/area/asteroid_areas.dm +++ b/code/game/area/asteroid_areas.dm @@ -3,7 +3,7 @@ /area/mine icon_state = "mining" sound_env = ASTEROID - flags = AREA_FLAG_IS_NOT_PERSISTENT + flags = AREA_FLAG_IS_NOT_PERSISTENT | AREA_CRYOPLANET_SHIELDED /area/mine/explored name = "Mine" diff --git a/code/game/machinery/atmoalter/station_boiler_radiator.dm b/code/game/machinery/atmoalter/station_boiler_radiator.dm new file mode 100644 index 0000000000..7ec62a3a54 --- /dev/null +++ b/code/game/machinery/atmoalter/station_boiler_radiator.dm @@ -0,0 +1,40 @@ +/// A floor radiator made to work with the station_boiler. If none are on the map then they are purely asthetic and safe to map. +/obj/machinery/stationboiler_radiator + name = "Station Radiator" + desc = "A radiator pipe connected to the station boiler, used to keep the rooms warm" + icon = 'icons/obj/machines/floor_radiator.dmi' + + plane = PLATING_PLANE + layer = FLOOR_HEATER_LAYER + + icon_state = "off" + use_power = USE_POWER_OFF + bullet_vulnerability = 0 //Invincible machine + anchored = TRUE + density = FALSE + VAR_PRIVATE/actively_radiating = FALSE + +/obj/machinery/stationboiler_radiator/Initialize(mapload) + . = ..() + SSstationheater.radiators += src + +/obj/machinery/stationboiler_radiator/Destroy() + SSstationheater.radiators -= src + . = ..() + +/obj/machinery/stationboiler_radiator/proc/set_state(activate) + if(actively_radiating == activate) + return + actively_radiating = activate + update_icon() + set_light(1, 0.9, "#e9400dff", l_on = actively_radiating) + +/obj/machinery/stationboiler_radiator/update_icon() + icon_state = actively_radiating ? "on" : "off" + +// TODO - Make a viable sane construction method for these +/obj/machinery/stationboiler_radiator/ex_act(severity) + return //Invincible machine + +/obj/machinery/stationboiler_radiator/fall_apart(severity = 3, scatter = TRUE) + return //Invincible machine diff --git a/code/game/objects/items/devices/communicator/UI_tgui.dm b/code/game/objects/items/devices/communicator/UI_tgui.dm index 0e32a8b05d..5bc39ccc81 100644 --- a/code/game/objects/items/devices/communicator/UI_tgui.dm +++ b/code/game/objects/items/devices/communicator/UI_tgui.dm @@ -266,7 +266,7 @@ "Low" = planet.weather_holder.current_weather.temp_low - T0C, "WindDir" = planet.weather_holder.wind_dir ? dir2text(planet.weather_holder.wind_dir) : "None", "WindSpeed" = planet.weather_holder.wind_speed ? "[planet.weather_holder.wind_speed > 2 ? "Severe" : "Normal"]" : "None", - "Forecast" = english_list(planet.weather_holder.forecast, and_text = "→", comma_text = "→", final_comma_text = "→") // Unicode RIGHTWARDS ARROW. + "Forecast" = english_list(list("\[[planet.weather_holder.current_weather?.name]\]") + planet.weather_holder.get_forecast_data(), and_text = "→", comma_text = "→", final_comma_text = "→") // Unicode RIGHTWARDS ARROW. ) weather.Add(list(W)) diff --git a/code/modules/planet/planet.dm b/code/modules/planet/planet.dm index a286d65f9a..d8ec041aab 100644 --- a/code/modules/planet/planet.dm +++ b/code/modules/planet/planet.dm @@ -28,6 +28,8 @@ var/moon_name = null // Purely for flavor. Null means no moon exists. var/moon_phase = null // Set if above is defined. + var/cryogenic_temp_shift = FALSE // If true, ALL areas that are not AREA_CRYOPLANET_SHIELDED will be brought toward the current weather's temperature. Causing indoor station areas to slowly match the planet's environment. + /datum/planet/New() ..() weather_holder = new(src) diff --git a/code/modules/planet/weather.dm b/code/modules/planet/weather.dm index e2c0b9bfc3..b603b22da8 100644 --- a/code/modules/planet/weather.dm +++ b/code/modules/planet/weather.dm @@ -9,7 +9,7 @@ var/list/roundstart_weather_chances = list() // Assoc list of weather identifiers and their odds of being picked to happen at roundstart. var/next_weather_shift = null // world.time when the weather subsystem will advance the forecast. var/imminent_weather_shift = null // world.time when weather will shift towards pre-set imminent weather type. - var/list/forecast = list() // A list of what the weather will be in the future. This allows it to be pre-determined and planned around. + VAR_PROTECTED/list/forecast = list() // A list of what the weather will be in the future. This allows it to be pre-determined and planned around. // Holds the weather icon, using vis_contents. Documentation says an /atom/movable is required for placing inside another atom's vis_contents. var/atom/movable/weather_visuals/visuals = null @@ -132,7 +132,8 @@ forecast.Cut() build_forecast() - +/datum/weather_holder/proc/get_forecast_data() + return forecast /datum/weather_holder/proc/update_icon_effects() visuals.icon_state = current_weather.icon_state diff --git a/icons/obj/machines/floor_radiator.dmi b/icons/obj/machines/floor_radiator.dmi new file mode 100644 index 0000000000..33fe4854c4 Binary files /dev/null and b/icons/obj/machines/floor_radiator.dmi differ diff --git a/icons/obj/machines/heat_boiler.dmi b/icons/obj/machines/heat_boiler.dmi new file mode 100644 index 0000000000..b1734c872d Binary files /dev/null and b/icons/obj/machines/heat_boiler.dmi differ diff --git a/maps/gateway_vr/lucky_7.dm b/maps/gateway_vr/lucky_7.dm index c379b7a54e..e353fb1760 100644 --- a/maps/gateway_vr/lucky_7.dm +++ b/maps/gateway_vr/lucky_7.dm @@ -97,7 +97,7 @@ /area/awaymission/lucky7/dorms name = "\improper Gateway - Dorms" icon_state = "crew_quarters" - flags = RAD_SHIELDED | BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_BLOCK_INSTANT_BUILDING + flags = RAD_SHIELDED | BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_BLOCK_INSTANT_BUILDING | AREA_CRYOPLANET_SHIELDED /area/awaymission/lucky7/dorms/one name = "\improper Gateway - Dorm 1" diff --git a/maps/groundbase/groundbase_areas.dm b/maps/groundbase/groundbase_areas.dm index 3b997ead07..38820fe2ef 100644 --- a/maps/groundbase/groundbase_areas.dm +++ b/maps/groundbase/groundbase_areas.dm @@ -463,7 +463,7 @@ holomap_color = HOLOMAP_AREACOLOR_DORMS icon_state = "grawhisqu" ambience = AMBIENCE_GENERIC - flags = RAD_SHIELDED | BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING + flags = RAD_SHIELDED | BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_CRYOPLANET_SHIELDED /area/groundbase/dorms/bathroom name = "Dormitory Bathroom" diff --git a/maps/redgate/casino_canal/casino_canal.dm b/maps/redgate/casino_canal/casino_canal.dm index a9c237fde3..f32c469dae 100644 --- a/maps/redgate/casino_canal/casino_canal.dm +++ b/maps/redgate/casino_canal/casino_canal.dm @@ -201,7 +201,7 @@ /area/redgate/underwater_casino/indoors/lower/dorms name = UNDERWATER_CASINO_NAME + " - Dorms" - flags = RAD_SHIELDED | BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_BLOCK_INSTANT_BUILDING + flags = RAD_SHIELDED | BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_BLOCK_INSTANT_BUILDING | AREA_CRYOPLANET_SHIELDED /area/redgate/underwater_casino/indoors/lower/dorms/dorm1 name = UNDERWATER_CASINO_NAME + " - Dormitory One" diff --git a/maps/stellar_delight/stellar_delight_areas.dm b/maps/stellar_delight/stellar_delight_areas.dm index 904b029cb1..0d1b69b224 100644 --- a/maps/stellar_delight/stellar_delight_areas.dm +++ b/maps/stellar_delight/stellar_delight_areas.dm @@ -126,7 +126,7 @@ /area/stellardelight/deck1/dorms name = "Dormitory" sound_env = SMALL_SOFTFLOOR - flags = RAD_SHIELDED| BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_BLOCK_INSTANT_BUILDING + flags = RAD_SHIELDED| BLUE_SHIELDED | AREA_FLAG_IS_NOT_PERSISTENT | AREA_FORBID_EVENTS | AREA_FORBID_SINGULO | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_BLOCK_INSTANT_BUILDING | AREA_CRYOPLANET_SHIELDED /area/stellardelight/deck1/dorms/dorm1 name = "Dormitory One" diff --git a/maps/virtual_reality/constructVR.dm b/maps/virtual_reality/constructVR.dm index 96b23ea387..e36664b38b 100644 --- a/maps/virtual_reality/constructVR.dm +++ b/maps/virtual_reality/constructVR.dm @@ -162,7 +162,7 @@ /area/vr/powered/mall/dorms name = "VR Mall Dorms" icon_state = "Sleep" - flags = RAD_SHIELDED | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_LIMIT_DARK_RESPITE + flags = RAD_SHIELDED | AREA_SOUNDPROOF | AREA_ALLOW_LARGE_SIZE | AREA_BLOCK_SUIT_SENSORS | AREA_BLOCK_TRACKING | AREA_LIMIT_DARK_RESPITE | AREA_CRYOPLANET_SHIELDED /area/vr/powered/mall/dorms/dorms1 diff --git a/tgui/packages/tgui/interfaces/StationBoiler.tsx b/tgui/packages/tgui/interfaces/StationBoiler.tsx new file mode 100644 index 0000000000..9493b301b3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/StationBoiler.tsx @@ -0,0 +1,72 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { + Button, + LabeledList, + ProgressBar, + Section, +} from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import { capitalize } from 'tgui-core/string'; + +type Data = { + materials: MaterialStack[]; + stored: number; + max: number; + percent: number; + timeleft: string; +}; + +type MaterialStack = { + name: string; + display: string; + qty: string; + can_eject: BooleanLike; +}; + +export const StationBoiler = (props) => { + const { act, data } = useBackend(); + const { materials, timeleft, stored, max } = data; + return ( + + +
act('ignite')}>Ignite} + fill + scrollable + > + + + + {timeleft} + + + + {materials.map((material) => ( + + act('ejectMaterial', { + mat: material.name, + }) + } + > + Eject + + } + > + {material.qty} + + ))} + +
+
+
+ ); +}; diff --git a/vorestation.dme b/vorestation.dme index e9f63993b7..387e847e5b 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -532,6 +532,7 @@ #include "code\ATMOSPHERICS\components\binary_devices\passive_gate_vr.dm" #include "code\ATMOSPHERICS\components\binary_devices\pipeturbine.dm" #include "code\ATMOSPHERICS\components\binary_devices\pump.dm" +#include "code\ATMOSPHERICS\components\binary_devices\station_boiler.dm" #include "code\ATMOSPHERICS\components\binary_devices\volume_pump.dm" #include "code\ATMOSPHERICS\components\omni_devices\_omni_extras.dm" #include "code\ATMOSPHERICS\components\omni_devices\filter.dm" @@ -596,6 +597,7 @@ #include "code\controllers\subsystems\chemistry.dm" #include "code\controllers\subsystems\circuits.dm" #include "code\controllers\subsystems\communications.dm" +#include "code\controllers\subsystems\cryoplanets.dm" #include "code\controllers\subsystems\dbcore.dm" #include "code\controllers\subsystems\dcs.dm" #include "code\controllers\subsystems\early_assets.dm" @@ -644,6 +646,7 @@ #include "code\controllers\subsystems\sqlite.dm" #include "code\controllers\subsystems\SSvote.dm" #include "code\controllers\subsystems\starmover.dm" +#include "code\controllers\subsystems\stationheater.dm" #include "code\controllers\subsystems\statpanel.dm" #include "code\controllers\subsystems\sun.dm" #include "code\controllers\subsystems\supply.dm" @@ -1391,6 +1394,7 @@ #include "code\game\machinery\atmoalter\pump.dm" #include "code\game\machinery\atmoalter\pump_vr.dm" #include "code\game\machinery\atmoalter\scrubber.dm" +#include "code\game\machinery\atmoalter\station_boiler_radiator.dm" #include "code\game\machinery\camera\camera.dm" #include "code\game\machinery\camera\camera_assembly.dm" #include "code\game\machinery\camera\motion.dm"