From dfaee1de903c96c3d21b0193746a21d49a5a22a9 Mon Sep 17 00:00:00 2001 From: Zandario Date: Mon, 21 Mar 2022 23:25:46 -0500 Subject: [PATCH] Ports Nightshift from Paradise (#3817) * Ports Nightshift and converts tick time to ds time * Moves Nightshift config to the new system and ports the forgotten APC TGUI updates * Attempting to fix your god damn tome's init. * Lets do station time the paradise way. Also think I fixed some apc misfires? * Rerecompiles tgui.bundle.js --- code/__DEFINES/controllers/_subsystems.dm | 6 +- code/__DEFINES/lighting.dm | 1 + code/__HELPERS/time.dm | 39 ++-- code/_globalvars/misc.dm | 2 + .../configuration/entries/game_options.dm | 3 + .../configuration_old/configuration.dm | 1 + code/controllers/subsystems/game_master.dm | 4 +- code/controllers/subsystems/nightshift.dm | 63 +++++++ code/controllers/subsystems/planets.dm | 14 +- code/controllers/subsystems/transfer.dm | 6 +- code/game/gamemodes/cult/hell_universe.dm | 2 +- code/game/gamemodes/cult/ritual.dm | 4 +- .../endgame/supermatter_cascade/universe.dm | 2 +- code/game/gamemodes/events.dm | 6 +- code/game/gamemodes/events/power_failure.dm | 4 +- code/game/gamemodes/game_mode_latespawn.dm | 2 +- .../newmalf_ability_trees/HELPERS.dm | 2 +- .../newmalf_ability_trees/tree_networking.dm | 4 +- code/game/machinery/exonet_node.dm | 2 +- code/game/machinery/newscaster.dm | 2 +- code/game/world.dm | 2 +- code/modules/admin/verbs/debug.dm | 4 +- code/modules/events/ion_storm.dm | 4 +- .../integrated_electronics/subtypes/time.dm | 8 +- .../programs/engineering/rcon_console.dm | 2 +- code/modules/power/apc.dm | 136 ++++++-------- code/modules/power/cells/power_cells.dm | 13 ++ code/modules/power/lighting.dm | 172 +++++++++++++++++- .../security levels/security levels.dm | 8 +- .../xenoarcheaology/effects/cellcharge.dm | 38 +++- .../xenoarcheaology/effects/celldrain.dm | 36 +++- config/entries/game_options.txt | 3 + maps/~map_system/maps.dm | 60 ++++-- tgui/packages/tgui/interfaces/APC.js | 36 ++++ tgui/public/tgui.bundle.css | 8 +- tgui/public/tgui.bundle.js | 2 +- vorestation.dme | 1 + 37 files changed, 534 insertions(+), 168 deletions(-) create mode 100644 code/controllers/subsystems/nightshift.dm diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm index 5f94b6508e5..542b47f644f 100644 --- a/code/__DEFINES/controllers/_subsystems.dm +++ b/code/__DEFINES/controllers/_subsystems.dm @@ -118,7 +118,8 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define INIT_ORDER_PLANETS -2 #define INIT_ORDER_ASSETS -4 #define INIT_ORDER_HOLOMAPS -5 -#define INIT_ORDER_OVERLAY -6 +#define INIT_ORDER_NIGHTSHIFT -6 +#define INIT_ORDER_OVERLAY -7 #define INIT_ORDER_EVENTS -8 #define INIT_ORDER_OVERMAPS -9 #define INIT_ORDER_TICKER -10 @@ -133,6 +134,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G // Subsystem fire priority, from lowest to highest priority // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) #define FIRE_PRIORITY_SHUTTLES 5 +#define FIRE_PRIORITY_NIGHTSHIFT 6 #define FIRE_PRIORITY_ORBIT 8 #define FIRE_PRIORITY_VOTE 9 #define FIRE_PRIORITY_AI 10 @@ -140,7 +142,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define FIRE_PRIORITY_VIS 10 #define FIRE_PRIORITY_SERVER_MAINT 10 #define FIRE_PRIORITY_GARBAGE 15 -#define FIRE_PRIORITY_CHARSETUP 25 +#define FIRE_PRIORITY_CHARSETUP 25 #define FIRE_PRIORITY_SPACEDRIFT 25 #define FIRE_PRIORITY_AIRFLOW 30 #define FIRE_PRIORITY_AIR 35 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 4a935bc60f5..05296165da3 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -61,6 +61,7 @@ #define LIGHT_COLOR_INCANDESCENT_TUBE "#E0EFF0" #define LIGHT_COLOR_INCANDESCENT_BULB "#FFFEB8" #define LIGHT_COLOR_INCANDESCENT_FLASHLIGHT "#FFCC66" +#define LIGHT_COLOR_NIGHTSHIFT "#EFCC86" #define LIGHT_RANGE_FIRE 3 //How many tiles standard fires glow. diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index 062a4e2ce39..1529de95005 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -18,25 +18,25 @@ return wtime + (time_offset + wusage) * world.tick_lag -var/roundstart_hour +GLOBAL_VAR_INIT(roundstart_hour, pick(2,7,12,17)) var/station_date = "" var/next_station_date_change = 1 DAY -#define duration2stationtime(time) time2text(station_time_in_ticks + time, "hh:mm") -#define worldtime2stationtime(time) time2text(roundstart_hour HOURS + time, "hh:mm") -#define round_duration_in_ticks (SSticker.round_start_time ? world.time - SSticker.round_start_time : 0) -#define station_time_in_ticks (roundstart_hour HOURS + round_duration_in_ticks) +#define duration2stationtime(time) time2text(station_time_in_ds + time, "hh:mm") +#define worldtime2stationtime(time) time2text(GLOB.roundstart_hour HOURS + time, "hh:mm") +#define round_duration_in_ds (SSticker.round_start_time ? world.time - SSticker.round_start_time : 0) +#define station_time_in_ds (GLOB.roundstart_hour HOURS + round_duration_in_ds) /proc/stationtime2text() - return time2text(station_time_in_ticks, "hh:mm") + return time2text(station_time_in_ds + GLOB.timezoneOffset, "hh:mm") /proc/stationdate2text() var/update_time = FALSE - if(station_time_in_ticks > next_station_date_change) + if(station_time_in_ds > next_station_date_change) next_station_date_change += 1 DAY update_time = TRUE if(!station_date || update_time) - var/extra_days = round(station_time_in_ticks / (1 DAY)) DAYS + var/extra_days = round(station_time_in_ds / (1 DAY)) DAYS var/timeofday = world.timeofday + extra_days station_date = num2text((text2num(time2text(timeofday, "YYYY"))+544)) + "-" + time2text(timeofday, "MM-DD") return station_date @@ -47,11 +47,28 @@ var/next_station_date_change = 1 DAY var/time_portion = time2text(world.timeofday, "hh:mm:ss") return "[date_portion]T[time_portion]" +/proc/get_timezone_offset() + var/midnight_gmt_here = text2num(time2text(0,"hh")) * 36000 + if(midnight_gmt_here > 12 HOURS) + return 24 HOURS - midnight_gmt_here + else + return midnight_gmt_here + /proc/gameTimestamp(format = "hh:mm:ss", wtime=null) if(!wtime) wtime = world.time return time2text(wtime - GLOB.timezoneOffset, format) +/* This is used for displaying the "station time" equivelent of a world.time value + Calling it with no args will give you the current time, but you can specify a world.time-based value as an argument + - You can use this, for example, to do "This will expire at [station_time_at(world.time + 500)]" to display a "station time" expiration date + which is much more useful for a player)*/ +/proc/station_time(time=world.time, display_only=FALSE) + return ((((time - SSticker.round_start_time)) + GLOB.gametime_offset) % 864000) - (display_only ? GLOB.timezoneOffset : 0) + +/proc/station_time_timestamp(format = "hh:mm:ss", time=world.time) + return time2text(station_time(time, TRUE), format) + /* Returns 1 if it is the selected month and day */ proc/isDay(var/month, var/day) if(isnum(month) && isnum(day)) @@ -73,7 +90,7 @@ var/last_round_duration = 0 if(last_round_duration && world.time < next_duration_update) return last_round_duration - var/mills = round_duration_in_ticks // 1/10 of a second, not real milliseconds but whatever + var/mills = round_duration_in_ds // 1/10 of a second, not real milliseconds but whatever //var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for refrence.. or something var/mins = round((mills % 36000) / 600) var/hours = round(mills / 36000) @@ -85,10 +102,6 @@ var/last_round_duration = 0 next_duration_update = world.time + 1 MINUTES return last_round_duration -/hook/startup/proc/set_roundstart_hour() - roundstart_hour = pick(2,7,12,17) - return 1 - /var/midnight_rollovers = 0 /var/rollovercheck_last_timeofday = 0 /proc/update_midnight_rollover() diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index cff2ff865bc..9b1b658f69f 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -1,5 +1,7 @@ GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks. +GLOBAL_VAR_INIT(gametime_offset, 432000) // 12:00 in seconds + GLOBAL_VAR_INIT(TAB, "    ") GLOBAL_VAR_INIT(Debug2, FALSE) diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 3ef6ac6d2f9..a2ea4e9afd2 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -8,3 +8,6 @@ /datum/config_entry/flag/allow_holidays config_entry_value = TRUE + +/datum/config_entry/flag/nightshifts_enabled + config_entry_value = TRUE diff --git a/code/controllers/configuration_old/configuration.dm b/code/controllers/configuration_old/configuration.dm index c6d15d99178..06c67c7d9c6 100644 --- a/code/controllers/configuration_old/configuration.dm +++ b/code/controllers/configuration_old/configuration.dm @@ -268,6 +268,7 @@ var/list/gamemode_cache = list() + /datum/configuration_legacy/New() var/list/L = subtypesof(/datum/game_mode) for (var/T in L) diff --git a/code/controllers/subsystems/game_master.dm b/code/controllers/subsystems/game_master.dm index 02b2d721827..a33f48511f5 100644 --- a/code/controllers/subsystems/game_master.dm +++ b/code/controllers/subsystems/game_master.dm @@ -58,7 +58,7 @@ SUBSYSTEM_DEF(gamemaster) if(ignore_time_restrictions) return TRUE // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. - var/mills = round_duration_in_ticks + var/mills = round_duration_in_ds var/mins = round((mills % 36000) / 600) var/hours = round(mills / 36000) @@ -153,5 +153,3 @@ SUBSYSTEM_DEF(gamemaster) return best_actions else log_debug("Game Master failed to find a suitable event, something very wrong is going on.") - - diff --git a/code/controllers/subsystems/nightshift.dm b/code/controllers/subsystems/nightshift.dm new file mode 100644 index 00000000000..ba06a984d93 --- /dev/null +++ b/code/controllers/subsystems/nightshift.dm @@ -0,0 +1,63 @@ +SUBSYSTEM_DEF(nightshift) + name = "Night Shift" + init_order = INIT_ORDER_NIGHTSHIFT + priority = FIRE_PRIORITY_NIGHTSHIFT + wait = 60 SECONDS + flags = SS_NO_TICK_CHECK + + var/nightshift_active = FALSE + var/nightshift_start_time = 702000 //7:30 PM, station time + var/nightshift_end_time = 270000 //7:30 AM, station time + var/nightshift_first_check = 30 SECONDS + + var/high_security_mode = FALSE + +/datum/controller/subsystem/nightshift/Initialize() + if (!CONFIG_GET(flag/nightshifts_enabled)) + can_fire = FALSE + //if(CONFIG_GET(flag/randomized_start_time_enabled)) + //GLOB.gametime_offset = rand(0, 23) HOURS + return ..() + +/datum/controller/subsystem/nightshift/fire(resumed = FALSE) + if(world.time - SSticker.round_start_time < nightshift_first_check) + return + check_nightshift() + +/datum/controller/subsystem/nightshift/proc/announce(message) + var/announce_z + if(GLOB.using_map.station_levels.len) + announce_z = pick(GLOB.using_map.station_levels) + priority_announcement.Announce(message, new_title = "Automated Lighting System Announcement", new_sound = 'sound/misc/notice2.ogg', zlevel = announce_z) + +/datum/controller/subsystem/nightshift/proc/check_nightshift(check_canfire=FALSE) //This is called from elsewhere, like setting the alert levels + if(check_canfire && !can_fire) + return + var/emergency = security_level > SEC_LEVEL_GREEN + var/announcing = TRUE + var/time = station_time() + var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time) + if(high_security_mode != emergency) + high_security_mode = emergency + if(night_time) + announcing = FALSE + if(!emergency) + announce("Restoring night lighting configuration to normal operation.") + else + announce("Disabling night lighting: Station is in a state of emergency.") + if(emergency) + night_time = FALSE + if(nightshift_active != night_time) + update_nightshift(night_time, announcing) + +/datum/controller/subsystem/nightshift/proc/update_nightshift(active, announce = TRUE) + nightshift_active = active + if(announce) + if(active) + announce("Good evening, crew. To reduce power consumption and stimulate the circadian rhythms of some species, all of the lights aboard the station have been dimmed for the night.") + else + announce("Good morning, crew. As it is now day time, all of the lights aboard the station have been restored to their former brightness.") + for(var/obj/machinery/power/apc/apc in GLOB.apcs) + if(apc.z in GLOB.using_map.station_levels) + apc.set_nightshift(active, TRUE) + CHECK_TICK diff --git a/code/controllers/subsystems/planets.dm b/code/controllers/subsystems/planets.dm index 33a27494556..70dd00954e7 100644 --- a/code/controllers/subsystems/planets.dm +++ b/code/controllers/subsystems/planets.dm @@ -6,16 +6,16 @@ SUBSYSTEM_DEF(planets) flags = SS_BACKGROUND runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - var/list/new_outdoor_turfs = list() - var/list/new_outdoor_walls = list() + var/static/list/new_outdoor_turfs = list() + var/static/list/new_outdoor_walls = list() - var/list/planets = list() - var/list/z_to_planet = list() + var/static/list/planets = list() + var/static/list/z_to_planet = list() - var/list/currentrun = list() + var/static/list/currentrun = list() - var/list/needs_sun_update = list() - var/list/needs_temp_update = list() + var/static/list/needs_sun_update = list() + var/static/list/needs_temp_update = list() /datum/controller/subsystem/planets/Initialize(timeofday) admin_notice("Initializing planetary weather.", R_DEBUG) diff --git a/code/controllers/subsystems/transfer.dm b/code/controllers/subsystems/transfer.dm index f22d38c61ea..11861331eee 100644 --- a/code/controllers/subsystems/transfer.dm +++ b/code/controllers/subsystems/transfer.dm @@ -18,15 +18,15 @@ SUBSYSTEM_DEF(transfer) /datum/controller/subsystem/transfer/fire(resumed) currenttick = currenttick + 1 //VOREStation Edit START -/* if (round_duration_in_ticks >= shift_last_vote - 2 MINUTES) +/* if (round_duration_in_ds >= shift_last_vote - 2 MINUTES) shift_last_vote = 999999999999 //Setting to a stupidly high number since it'll be not used again. to_world("Warning: This upcoming round-extend vote will be your ONLY extend vote. Wrap up your scenes in the next 60 minutes if the round is extended.") //VOREStation Edit */ - if (round_duration_in_ticks >= shift_hard_end - 1 MINUTE) + if (round_duration_in_ds >= shift_hard_end - 1 MINUTE) init_shift_change(null, 1) shift_hard_end = timerbuffer + config_legacy.vote_autotransfer_interval //If shuttle somehow gets recalled, let's force it to call again next time a vote would occur. timerbuffer = timerbuffer + config_legacy.vote_autotransfer_interval //Just to make sure a vote doesn't occur immediately afterwords. - else if (round_duration_in_ticks >= timerbuffer - 1 MINUTE) + else if (round_duration_in_ds >= timerbuffer - 1 MINUTE) SSvote.autotransfer() //VOREStation Edit END timerbuffer = timerbuffer + config_legacy.vote_autotransfer_interval diff --git a/code/game/gamemodes/cult/hell_universe.dm b/code/game/gamemodes/cult/hell_universe.dm index dcdcd1a4a58..0f62f649777 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -78,7 +78,7 @@ In short: alm.ex_act(2) /datum/universal_state/hell/proc/APCSet() - for (var/obj/machinery/power/apc/APC in machines) + for (var/obj/machinery/power/apc/APC in GLOB.apcs) if (!(APC.stat & BROKEN) && !APC.is_critical) APC.emagged = 1 APC.queue_icon_update() diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index 58a3812ef35..307e5cf5b20 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -283,8 +283,8 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," "} -/obj/item/book/tome/Initialize() - ..() +/obj/item/book/tome/Initialize(mapload) + . = ..() if(!cultwords["travel"]) runerandom() for(var/V in cultwords) diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm index ee9da38f0fd..93f8b6f34d7 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -107,7 +107,7 @@ The access requirements on the Asteroid Shuttles' consoles have now been revoked alm.ex_act(2) /datum/universal_state/supermatter_cascade/proc/APCSet() - for (var/obj/machinery/power/apc/APC in machines) + for (var/obj/machinery/power/apc/APC in GLOB.apcs) if (!(APC.stat & BROKEN) && !APC.is_critical) APC.chargemode = 0 if(APC.cell) diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm index f3c1e9fc6b4..8119b6460f7 100644 --- a/code/game/gamemodes/events.dm +++ b/code/game/gamemodes/events.dm @@ -135,7 +135,7 @@ var/hadevent = 0 apc.overload_lighting() else - for(var/obj/machinery/power/apc/apc in machines) + for(var/obj/machinery/power/apc/apc in GLOB.apcs) apc.overload_lighting() return @@ -261,14 +261,14 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is spawn(0) to_chat(world, "Started processing APCs") - for (var/obj/machinery/power/apc/APC in machines) + for (var/obj/machinery/power/apc/apc in GLOB.apcs) if(APC.z in station_levels) APC.ion_act() apcnum++ to_chat(world, "Finished processing APCs. Processed: [apcnum]") spawn(0) to_chat(world, "Started processing SMES") - for (var/obj/machinery/power/smes/SMES in machines) + for (var/obj/machinery/power/smes/SMES in GLOB.smeses) if(SMES.z in station_levels) SMES.ion_act() smesnum++ diff --git a/code/game/gamemodes/events/power_failure.dm b/code/game/gamemodes/events/power_failure.dm index e4c7cc9a8d9..eac3e23df4e 100644 --- a/code/game/gamemodes/events/power_failure.dm +++ b/code/game/gamemodes/events/power_failure.dm @@ -20,7 +20,7 @@ S.power_change() - for(var/obj/machinery/power/apc/C in machines) + for(var/obj/machinery/power/apc/C in GLOB.apcs) if(!C.is_critical && C.cell && (C.z in GLOB.using_map.station_levels)) C.cell.charge = 0 @@ -29,7 +29,7 @@ if(announce) command_announcement.Announce("Power has been restored to [station_name()]. We apologize for the inconvenience.", "Power Systems Nominal", new_sound = 'sound/AI/poweron.ogg') - for(var/obj/machinery/power/apc/C in machines) + for(var/obj/machinery/power/apc/C in GLOB.apcs) if(C.cell && (C.z in GLOB.using_map.station_levels)) C.cell.charge = C.cell.maxcharge for(var/obj/machinery/power/smes/S in machines) diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm index 67580077aaf..28ec49d21f8 100644 --- a/code/game/gamemodes/game_mode_latespawn.dm +++ b/code/game/gamemodes/game_mode_latespawn.dm @@ -34,7 +34,7 @@ if(SSemergencyshuttle.shuttle && (SSemergencyshuttle.shuttle.moving_status == SHUTTLE_WARMUP || SSemergencyshuttle.shuttle.moving_status == SHUTTLE_INTRANSIT)) return // Don't do anything if the shuttle's coming. - var/mills = round_duration_in_ticks + var/mills = round_duration_in_ds var/mins = round((mills % 36000) / 600) var/hours = round(mills / 36000) diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm index c8cf7e36092..662838e1399 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/HELPERS.dm @@ -168,7 +168,7 @@ // Description: Returns a list of all unhacked APCs /proc/get_unhacked_apcs(var/mob/living/silicon/ai/user) var/list/H = list() - for(var/obj/machinery/power/apc/A in machines) + for(var/obj/machinery/power/apc/A in GLOB.apcs) if(A.hacker && A.hacker == user) continue H.Add(A) diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm index c073b800100..9a5ceb78f7c 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm @@ -147,7 +147,7 @@ to_chat(user, "You already started the system override sequence.") return var/list/remaining_apcs = list() - for(var/obj/machinery/power/apc/A in machines) + for(var/obj/machinery/power/apc/A in GLOB.apcs) if(!(A.z in GLOB.using_map.station_levels)) // Only station APCs continue if(A.hacker == user || A.aidisabled) // This one is already hacked, or AI control is disabled on it. @@ -193,7 +193,7 @@ to_chat(user, "## REACHABLE APC SYSTEMS OVERTAKEN. BYPASSING PRIMARY FIREWALL.") sleep(300) // Hack all APCs, including those built during hack sequence. - for(var/obj/machinery/power/apc/A in machines) + for(var/obj/machinery/power/apc/A in GLOB.apcs) if((!A.hacker || (A.hacker != src)) && !A.aidisabled && (A.z in GLOB.using_map.station_levels)) A.ai_hack(src) diff --git a/code/game/machinery/exonet_node.dm b/code/game/machinery/exonet_node.dm index 4cddac6aed0..742e2bfd0b3 100644 --- a/code/game/machinery/exonet_node.dm +++ b/code/game/machinery/exonet_node.dm @@ -184,7 +184,7 @@ // Description: This writes to the logs list, so that people can see what people are doing on the Exonet ingame. Note that this is not an admin logging function. // Communicators are already logged seperately. /obj/machinery/exonet_node/proc/write_log(var/origin_address, var/target_address, var/data_type, var/content) - //var/timestamp = time2text(station_time_in_ticks, "hh:mm:ss") + //var/timestamp = time2text(station_time_in_ds, "hh:mm:ss") var/timestamp = "[stationdate2text()] [stationtime2text()]" var/msg = "[timestamp] | FROM [origin_address] TO [target_address] | TYPE: [data_type] | CONTENT: [content]" logs.Add(msg) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 5bf2c8a5823..2286314b99b 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -79,7 +79,7 @@ newMsg.body = msg newMsg.time_stamp = "[stationtime2text()]" newMsg.is_admin_message = adminMessage - newMsg.post_time = round_duration_in_ticks // Should be almost universally unique + newMsg.post_time = round_duration_in_ds // Should be almost universally unique if(message_type) newMsg.message_type = message_type if(photo) diff --git a/code/game/world.dm b/code/game/world.dm index a4b2475fbeb..7cfde37b250 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -55,7 +55,7 @@ GLOBAL_LIST(topic_status_cache) // if(config && config_legacy.log_runtime) // log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM-DD-(hh-mm-ss)")]-runtime.log") - GLOB.timezoneOffset = text2num(time2text(0,"hh")) * 36000 + GLOB.timezoneOffset = get_timezone_offset() callHook("startup") //Emergency Fix diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 53fa80e8085..186396094b9 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -376,8 +376,8 @@ if(!(A.type in areas_all)) areas_all.Add(A.type) - for(var/obj/machinery/power/apc/APC in machines) - var/area/A = get_area(APC) + for(var/obj/machinery/power/apc/apc in GLOB.apcs) + var/area/A = get_area(apc) if(A && !(A.type in areas_with_APC)) areas_with_APC.Add(A.type) diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index bb5d9f8420a..52378ffae8d 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -197,14 +197,14 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is spawn(0) to_chat(world, "Started processing APCs") - for (var/obj/machinery/power/apc/APC in machines) + for (var/obj/machinery/power/apc/apc in GLOB.apcs) if(APC.z in station_levels) APC.ion_act() apcnum++ to_chat(world, "Finished processing APCs. Processed: [apcnum]") spawn(0) to_chat(world, "Started processing SMES") - for (var/obj/machinery/power/smes/SMES in machines) + for (var/obj/machinery/power/smes/SMES in GLOB.smeses) if(SMES.z in station_levels) SMES.ion_act() smesnum++ diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm index 8de8a473899..5c8b916f2ab 100644 --- a/code/modules/integrated_electronics/subtypes/time.dm +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -86,10 +86,10 @@ power_draw_per_use = 4 /obj/item/integrated_circuit/time/clock/do_work() - set_pin_data(IC_OUTPUT, 1, time2text(station_time_in_ticks, "hh:mm:ss") ) - set_pin_data(IC_OUTPUT, 2, text2num(time2text(station_time_in_ticks, "hh") ) ) - set_pin_data(IC_OUTPUT, 3, text2num(time2text(station_time_in_ticks, "mm") ) ) - set_pin_data(IC_OUTPUT, 4, text2num(time2text(station_time_in_ticks, "ss") ) ) + set_pin_data(IC_OUTPUT, 1, time2text(station_time_in_ds, "hh:mm:ss") ) + set_pin_data(IC_OUTPUT, 2, text2num(time2text(station_time_in_ds, "hh") ) ) + set_pin_data(IC_OUTPUT, 3, text2num(time2text(station_time_in_ds, "mm") ) ) + set_pin_data(IC_OUTPUT, 4, text2num(time2text(station_time_in_ds, "ss") ) ) push_data() activate_pin(2) diff --git a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm index 2220bdb7081..c60855aee23 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm @@ -122,7 +122,7 @@ // Description: Refreshes local list of known devices. /datum/nano_module/rcon/proc/FindDevices() known_SMESs = new /list() - for(var/obj/machinery/power/smes/buildable/SMES in machines) + for(var/obj/machinery/power/smes/buildable/SMES in GLOB.smeses) if(SMES.RCon_tag && (SMES.RCon_tag != "NO_TAG") && SMES.RCon) known_SMESs.Add(SMES) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 6f497b752ae..2883cd03118 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1,3 +1,5 @@ +GLOBAL_LIST_EMPTY(apcs) + #define CRITICAL_APC_EMP_PROTECTION 10 // EMP effect duration is divided by this number if the APC has "critical" flag //update_state #define UPDATE_CELL_IN 1 @@ -34,10 +36,14 @@ // controls power to devices in that area // may be opened to change power cell // three different channels (lighting/equipment/environ) - may each be set to on, off, or auto -#define POWERCHAN_OFF 0 -#define POWERCHAN_OFF_AUTO 1 -#define POWERCHAN_ON 2 -#define POWERCHAN_ON_AUTO 3 +#define POWERCHAN_OFF 0 // Power channel is off and will stay that way dammit +#define POWERCHAN_OFF_AUTO 1 // Power channel is off until power rises above a threshold +#define POWERCHAN_ON 2 // Power channel is on until there is no power +#define POWERCHAN_ON_AUTO 3 // Power channel is on until power drops below a threshold + +#define NIGHTSHIFT_AUTO 1 +#define NIGHTSHIFT_NEVER 2 +#define NIGHTSHIFT_ALWAYS 3 //NOTE: STUFF STOLEN FROM AIRLOCK.DM thx @@ -104,6 +110,7 @@ var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment var/longtermpower = 10 var/datum/wires/apc/wires = null + var/emergency_lights = FALSE var/update_state = -1 var/update_overlay = -1 var/is_critical = 0 @@ -118,6 +125,10 @@ var/global/list/status_overlays_environ var/alarms_hidden = FALSE //If power alarms from this APC are visible on consoles + var/nightshift_lights = FALSE + var/nightshift_setting = NIGHTSHIFT_AUTO + var/last_nightshift_switch = 0 + /obj/machinery/power/apc/updateDialog() if (stat & (BROKEN|MAINT)) return @@ -159,6 +170,7 @@ /obj/machinery/power/apc/Initialize(mapload, ndir, building = FALSE) . = ..() wires = new(src) + GLOB.apcs += src // offset 24 pixels in direction of dir // this allows the APC to be embedded in a wall, yet still inside an area @@ -179,6 +191,7 @@ src.update_icon() /obj/machinery/power/apc/Destroy() + GLOB.apcs -= src src.update() area.apc = null area.power_light = 0 @@ -805,6 +818,9 @@ "gridCheck" = grid_check, "coverLocked" = coverlocked, "siliconUser" = issilicon(user) || (isobserver(user) && is_admin(user)), //I add observer here so admins can have more control, even if it makes 'siliconUser' seem inaccurate. + "emergencyLights" = !emergency_lights, + "nightshiftLights" = nightshift_lights, + "nightshiftSetting" = nightshift_setting, "powerChannels" = list( list( @@ -873,6 +889,13 @@ coverlocked = !coverlocked if("breaker") toggle_breaker() + if("nightshift") + if(last_nightshift_switch > world.time - 10 SECONDS) // don't spam... + to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") + return 0 + last_nightshift_switch = world.time + nightshift_setting = params["nightshift"] + update_nightshift() if("charge") chargemode = !chargemode if(!chargemode) @@ -895,6 +918,13 @@ failure_timer = 0 update_icon() update() + if("emergency_lighting") + emergency_lights = !emergency_lights + for(var/obj/machinery/light/L in area) + if(!initial(L.no_emergency)) //If there was an override set on creation, keep that override + L.no_emergency = emergency_lights + INVOKE_ASYNC(L, /obj/machinery/light/.proc/update, FALSE) + CHECK_TICK if("overload") if(locked_exception) // Reusing for simplicity! overload_lighting() @@ -923,7 +953,7 @@ return 0 if(IsAdminGhost(user)) //This is to allow nanoUI interaction by ghost admins. return TRUE - if (user.stat) + if(user.stat) return 0 if(inoperable()) return 0 @@ -936,7 +966,7 @@ to_chat(user,"You must stand to use [src]!") return 0 autoflag = 5 - if (istype(user, /mob/living/silicon)) + if(istype(user, /mob/living/silicon)) var/permit = 0 // Malfunction variable. If AI hacks APC it can control it even without AI control wire. var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user @@ -948,83 +978,17 @@ if(aidisabled && !permit) if(!loud) - to_chat(user,"\The AI control for [src] has been disabled!") + to_chat(user, "\The AI control for [src] has been disabled!") return 0 else - if (!in_range(src, user) || !istype(src.loc, /turf)) + if(!in_range(src, user) || !istype(loc, /turf)) return 0 var/mob/living/carbon/human/H = user - if (istype(H) && prob(H.getBrainLoss())) - to_chat(user,"You momentarily forget how to use [src].") + if(istype(H) && prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") return 0 return 1 -/obj/machinery/power/apc/Topic(href, href_list) - if(..()) - return 1 - - if(!can_use(usr, 1)) - return 1 - - if(locked && !issilicon(usr) ) - if(isobserver(usr) ) - var/mob/observer/dead/O = usr //Added to allow admin nanoUI interactions. - if(!O.can_admin_interact() ) //NanoUI /should/ make this not needed, but better safe than sorry. - to_chat(usr,"Try as you might, your ghostly fingers can't press the buttons.") - return 1 - else - to_chat(usr,"You must unlock the panel to use this!") - return 1 - - if (href_list["lock"]) - coverlocked = !coverlocked - - else if (href_list["reboot"]) - failure_timer = 0 - update_icon() - update() - - else if (href_list["breaker"]) - toggle_breaker() - - else if (href_list["cmode"]) - chargemode = !chargemode - if(!chargemode) - charging = 0 - update_icon() - - else if (href_list["eqp"]) - var/val = text2num(href_list["eqp"]) - equipment = setsubsystem(val) - update_icon() - update() - - else if (href_list["lgt"]) - var/val = text2num(href_list["lgt"]) - lighting = setsubsystem(val) - update_icon() - update() - - else if (href_list["env"]) - var/val = text2num(href_list["env"]) - environ = setsubsystem(val) - update_icon() - update() - - else if (href_list["overload"]) - if(istype(usr, /mob/living/silicon)) - src.overload_lighting() - - else if (href_list["toggleaccess"]) - if(istype(usr, /mob/living/silicon)) - if(emagged || (stat & (BROKEN|MAINT))) - to_chat(usr,"The APC does not respond to the command.") - return - locked = !locked - update_icon() - - return 0 - /obj/machinery/power/apc/proc/toggle_breaker() operating = !operating src.update() @@ -1421,4 +1385,24 @@ obj/machinery/power/apc/proc/autoset(var/cur_state, var/on) name = "[area.name] APC" update() +/obj/machinery/power/apc/proc/set_nightshift(on, var/automated) + set waitfor = FALSE + if(automated && istype(area, /area/shuttle)) + return + nightshift_lights = on + update_nightshift() + +/obj/machinery/power/apc/proc/update_nightshift() + var/new_state = nightshift_lights + + switch(nightshift_setting) + if(NIGHTSHIFT_NEVER) + new_state = FALSE + if(NIGHTSHIFT_ALWAYS) + new_state = TRUE + + for(var/obj/machinery/light/L in area) + L.nightshift_mode(new_state) + CHECK_TICK + #undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/modules/power/cells/power_cells.dm b/code/modules/power/cells/power_cells.dm index 2ae8b2649f2..72e1c1fc8f1 100644 --- a/code/modules/power/cells/power_cells.dm +++ b/code/modules/power/cells/power_cells.dm @@ -125,3 +125,16 @@ overlays.Cut() target.nutrition += amount user.custom_emote(message = "connects \the [src] to [user == target ? "their" : "[target]'s"] charging port, expending it.") + +/obj/item/cell/emergency_light + name = "miniature power cell" + desc = "A tiny power cell with a very low power capacity. Used in light fixtures to power them in the event of an outage." + maxcharge = 120 //Emergency lights use 0.2 W per tick, meaning ~10 minutes of emergency power from a cell + matter = list("glass" = 20) + w_class = ITEMSIZE_TINY + +/obj/item/cell/emergency_light/Initialize() + . = ..() + var/area/A = get_area(src) + if(!A.lightswitch || !A.light_power) + charge = 0 //For naturally depowered areas, we start with no power diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 3587106ea74..04aaeafc831 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -10,6 +10,7 @@ #define LIGHT_BURNED 3 #define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb #define LIGHTING_POWER_FACTOR 2 //5W per luminosity * range //VOREStation Edit: why the fuck are lights eating so much power, 2W per thing +#define LIGHT_EMERGENCY_POWER_USE 0.2 //How much power emergency lights will consume per tick var/global/list/light_type_cache = list() /proc/get_light_type_instance(var/light_type) @@ -29,6 +30,10 @@ var/global/list/light_type_cache = list() var/stage = 1 var/fixture_type = /obj/machinery/light var/sheets_refunded = 2 + var/obj/machinery/light/newlight = null + var/obj/item/cell/cell = null + + var/cell_connectors = TRUE /obj/machinery/light_construct/Initialize(mapload, newdir, building = FALSE, datum/frame/frame_types/frame_type, obj/machinery/light/fixture) . = ..(mapload, newdir) @@ -61,8 +66,45 @@ var/global/list/light_type_cache = list() if(3) . += "The casing is closed." + if(cell_connectors) + if(cell) + to_chat(user, "You see [cell] inside the casing.") + else + to_chat(user, "The casing has no power cell for backup power.") + else + to_chat(user, "This casing doesn't support power cells for backup power.") + +/obj/machinery/light_construct/attack_hand(mob/user) + . = ..() + if(.) + return . // obj/machinery/attack_hand returns 1 if user can't use the machine + if(cell) + user.visible_message("[user] removes [cell] from [src]!","You remove [cell].") + user.put_in_hands(cell) + cell.update_icon() + cell = null + /obj/machinery/light_construct/attackby(obj/item/W as obj, mob/user as mob) src.add_fingerprint(user) + if(istype(W, /obj/item/cell/emergency_light)) + if(!cell_connectors) + to_chat(user, "This [name] can't support a power cell!") + return + if(!user.unEquip(W)) + to_chat(user, "[W] is stuck to your hand!") + return + if(cell) + to_chat(user, "There is a power cell already installed!") + else if(user.drop_from_inventory(W)) + user.visible_message("[user] hooks up [W] to [src].", \ + "You add [W] to [src].") + playsound(src, 'sound/machines/click.ogg', 50, TRUE) + W.forceMove(src) + cell = W + add_fingerprint(user) + return + + if (W.is_wrench()) if (src.stage == 1) playsound(src, W.usesound, 75, 1) @@ -113,6 +155,10 @@ var/global/list/light_type_cache = list() var/obj/machinery/light/newlight = new fixture_type(src.loc, src) newlight.setDir(src.dir) src.transfer_fingerprints_to(newlight) + if(cell) + newlight.cell = cell + cell.forceMove(newlight) + cell = null qdel(src) return ..() @@ -183,17 +229,34 @@ var/global/list/light_type_cache = list() // this is used to calc the probability the light burns out var/rigged = 0 // true if rigged to explode - //VOREStation Edit Start var/needsound = FALSE // Flag to prevent playing turn-on sound multiple times, and from playing at roundstart var/shows_alerts = TRUE // Flag for if this fixture should show alerts. Make sure icon states exist! var/current_alert = null // Which alert are we showing right now? - //VOREStation Edit End var/auto_flicker = FALSE // If true, will constantly flicker, so long as someone is around to see it (otherwise its a waste of CPU). + var/obj/item/cell/emergency_light/cell + var/start_with_cell = TRUE // if true, this fixture generates a very weak cell at roundstart + + var/emergency_mode = FALSE // if true, the light is in emergency mode + var/no_emergency = FALSE // if true, this light cannot ever have an emergency mode + var/bulb_emergency_brightness_mul = 0.25 // multiplier for this light's base brightness in emergency power mode + var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode + var/bulb_emergency_pow_mul = 0.75 // the multiplier for determining the light's power in emergency mode + var/bulb_emergency_pow_min = 0.5 // the minimum value for the light's power in emergency mode + + var/nightshift_enabled = FALSE + var/nightshift_allowed = TRUE + var/brightness_range_ns + var/brightness_power_ns + var/brightness_color_ns + /obj/machinery/light/flicker auto_flicker = TRUE +/obj/machinery/light/no_nightshift + nightshift_allowed = FALSE + // the smaller bulb light fixture /obj/machinery/light/small @@ -207,6 +270,12 @@ var/global/list/light_type_cache = list() /obj/machinery/light/small/flicker auto_flicker = TRUE +/obj/machinery/light/poi + start_with_cell = FALSE + +/obj/machinery/light/small/poi + start_with_cell = FALSE + /obj/machinery/light/flamp icon = 'icons/obj/lighting.dmi' //VOREStation Edit icon_state = "flamp1" @@ -222,8 +291,12 @@ var/global/list/light_type_cache = list() /obj/machinery/light/flamp/Initialize(mapload, obj/machinery/light_construct/construct) . = ..() if(construct) + start_with_cell = FALSE lamp_shade = 0 update_icon() + else + if(start_with_cell && !no_emergency) + cell = new/obj/item/cell/emergency_light(src) /obj/machinery/light/flamp/flicker auto_flicker = TRUE @@ -254,11 +327,14 @@ var/global/list/light_type_cache = list() . = ..(mapload) if(construct) + start_with_cell = FALSE status = LIGHT_EMPTY construct_type = construct.type construct.transfer_fingerprints_to(src) setDir(construct.dir) else + if(start_with_cell && !no_emergency) + cell = new/obj/item/cell/emergency_light(src) var/obj/item/light/L = get_light_type_instance(light_type) update_from_bulb(L) if(prob(L.broken_chance)) @@ -272,6 +348,7 @@ var/global/list/light_type_cache = list() if(A) on = 0 // A.update_lights() + QDEL_NULL(cell) return ..() /obj/machinery/light/update_icon() @@ -348,7 +425,10 @@ var/global/list/light_type_cache = list() //VOREStation Edit End if(on) - if(light_range != brightness_range || light_power != brightness_power || light_color != brightness_color) + var/correct_range = nightshift_enabled ? brightness_range_ns : brightness_range + var/correct_power = nightshift_enabled ? brightness_power_ns : brightness_power + var/correct_color = nightshift_enabled ? brightness_color_ns : brightness_color + if(light_range != correct_range || light_power != correct_power || light_color != correct_color) if(!auto_flicker) switchcount++ if(rigged) @@ -366,13 +446,24 @@ var/global/list/light_type_cache = list() set_light(0) else update_use_power(USE_POWER_ACTIVE) - set_light(brightness_range, brightness_power, brightness_color) + set_light(correct_range, correct_power, correct_color) + else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off()) + update_use_power(USE_POWER_IDLE) + emergency_mode = TRUE + START_PROCESSING(SSobj, src) else update_use_power(USE_POWER_IDLE) set_light(0) active_power_usage = ((light_range * light_power) * LIGHTING_POWER_FACTOR) +/obj/machinery/light/proc/nightshift_mode(var/state) + if(!nightshift_allowed) + return + + if(state != nightshift_enabled) + nightshift_enabled = state + update(FALSE) /obj/machinery/light/attack_generic(var/mob/user, var/damage) if(!damage) @@ -406,6 +497,9 @@ var/global/list/light_type_cache = list() on = (s && status == LIGHT_OK) update() +/obj/machinery/light/get_cell() + return cell + // examine verb /obj/machinery/light/examine(mob/user) . = ..() @@ -419,6 +513,8 @@ var/global/list/light_type_cache = list() . += "[desc] The [fitting] is burnt out." if(LIGHT_BROKEN) . += "[desc] The [fitting] has been smashed." + if(cell) + to_chat(user, "Its backup power charge meter reads [round((cell.charge / cell.maxcharge) * 100, 0.1)]%.") /obj/machinery/light/proc/get_fitting_name() var/obj/item/light/L = light_type @@ -428,10 +524,15 @@ var/global/list/light_type_cache = list() status = L.status switchcount = L.switchcount rigged = L.rigged + brightness_range = L.brightness_range brightness_power = L.brightness_power brightness_color = L.brightness_color + brightness_range_ns = L.nightshift_range + brightness_power_ns = L.nightshift_power + brightness_color_ns = L.nightshift_color + // attack with item - insert light (if right type), otherwise try to break the light /obj/machinery/light/proc/insert_bulb(obj/item/light/L) @@ -544,6 +645,12 @@ var/global/list/light_type_cache = list() ..() +// returns if the light has power /but/ is manually turned off +// if a light is turned off, it won't activate emergency power +/obj/machinery/light/proc/turned_off() + var/area/A = get_area(src) + return !A.lightswitch && A.power_light || flickering + // returns whether this light has power // true if area has power and lightswitch is on /obj/machinery/light/proc/has_power() @@ -557,6 +664,29 @@ var/global/list/light_type_cache = list() else return A && A.lightswitch && (!A.requires_power || A.power_light) +// returns whether this light has emergency power +// can also return if it has access to a certain amount of that power +/obj/machinery/light/proc/has_emergency_power(pwr) + if(no_emergency || !cell) + return FALSE + if(pwr ? cell.charge >= pwr : cell.charge) + return status == LIGHT_OK + +// attempts to use power from the installed emergency cell, returns true if it does and false if it doesn't +/obj/machinery/light/proc/use_emergency_power(pwr = LIGHT_EMERGENCY_POWER_USE) + if(turned_off()) + return FALSE + if(!has_emergency_power(pwr)) + return FALSE + if(cell.charge > 300) //it's meant to handle 120 W, ya doofus + visible_message("[src] short-circuits from too powerful of a power cell!") + status = LIGHT_BURNED + return FALSE + cell.use(pwr) + set_light(brightness_range * bulb_emergency_brightness_mul, max(bulb_emergency_pow_min, bulb_emergency_pow_mul * (cell.charge / cell.maxcharge)), bulb_emergency_colour) + return TRUE + + /obj/machinery/light/proc/flicker(var/amount = rand(10, 20)) if(flickering) return flickering = 1 @@ -571,11 +701,20 @@ var/global/list/light_type_cache = list() update(0) flickering = 0 -// ai attack - make lights flicker, because why not - +// ai attack - turn on/off emergency lighting for a specific fixture /obj/machinery/light/attack_ai(mob/user) + no_emergency = !no_emergency + to_chat(user, "Emergency lights for this fixture have been [no_emergency ? "disabled" : "enabled"].") + update(FALSE) + +// ai alt click - Make light flicker. Very important for atmosphere. +/obj/machinery/light/AIAltClick(mob/user) flicker(1) +/obj/machinery/light/flamp/attack_ai(mob/user) + attack_hand() + return + // attack with hand - remove tube/bulb // if hands aren't protected and the light is on, burn the player /obj/machinery/light/attack_hand(mob/user) @@ -694,6 +833,17 @@ var/global/list/light_type_cache = list() // use power /obj/machinery/light/process(delta_time) + if(!cell) + return PROCESS_KILL + if(has_power()) + emergency_mode = FALSE + update(FALSE) + if(cell.charge == cell.maxcharge) + return PROCESS_KILL + cell.charge = min(cell.maxcharge, cell.charge + LIGHT_EMERGENCY_POWER_USE*2) //Recharge emergency power automatically while not using it + if(emergency_mode && !use_emergency_power(LIGHT_EMERGENCY_POWER_USE)) + update(FALSE) //Disables emergency mode and sets the color to normal + if(auto_flicker && !flickering) if(check_for_player_proximity(src, radius = 12, ignore_ghosts = FALSE, ignore_afk = TRUE)) seton(TRUE) // Lights must be on to flicker. @@ -745,6 +895,10 @@ var/global/list/light_type_cache = list() drop_sound = 'sound/items/drop/glass.ogg' pickup_sound = 'sound/items/pickup/glass.ogg' + var/nightshift_range = 8 + var/nightshift_power = 0.7 + var/nightshift_color = LIGHT_COLOR_NIGHTSHIFT + /obj/item/light/tube name = "light tube" desc = "A replacement light tube." @@ -755,6 +909,9 @@ var/global/list/light_type_cache = list() brightness_range = 12 // luminosity when on, also used in power calculation //VOREStation Edit brightness_power = 1 + nightshift_range = 7 + nightshift_power = 0.5 + /obj/item/light/tube/large w_class = ITEMSIZE_SMALL name = "large light tube" @@ -820,6 +977,9 @@ var/global/list/light_type_cache = list() brightness_power = 1 brightness_color = LIGHT_COLOR_INCANDESCENT_BULB + nightshift_range = 3 + nightshift_power = 0.45 + /obj/item/light/throw_impact(atom/hit_atom) ..() shatter() diff --git a/code/modules/security levels/security levels.dm b/code/modules/security levels/security levels.dm index c859628a189..d58d7f347ba 100644 --- a/code/modules/security levels/security levels.dm +++ b/code/modules/security levels/security levels.dm @@ -64,10 +64,6 @@ else security_announcement_down.Announce("[config_legacy.alert_desc_red_downto]", "Attention! Code red!", new_sound = 'sound/misc/voyalert.ogg') security_level = SEC_LEVEL_RED - /* - At the time of commit, setting status displays didn't work properly - var/obj/machinery/computer/communications/CC = locate(/obj/machinery/computer/communications,world) - if(CC) - CC.post_status("alert", "redalert")*/ if(SEC_LEVEL_DELTA) security_announcement_up.Announce("[config_legacy.alert_desc_delta]", "Attention! Delta alert level reached!", new_sound = 'sound/effects/alert_levels/deltaklaxon.ogg') security_level = SEC_LEVEL_DELTA @@ -85,6 +81,10 @@ GLOB.lore_atc.reroute_traffic(TRUE) // Tell them fuck off we're busy. else GLOB.lore_atc.reroute_traffic(FALSE) + + spawn() + SSnightshift.check_nightshift() + admin_chat_message(message = "Security level is now: [uppertext(get_security_level())]", color = "#CC2222") //VOREStation Add /proc/get_security_level() diff --git a/code/modules/xenoarcheaology/effects/cellcharge.dm b/code/modules/xenoarcheaology/effects/cellcharge.dm index 1838dbcb2e2..62ec7db0650 100644 --- a/code/modules/xenoarcheaology/effects/cellcharge.dm +++ b/code/modules/xenoarcheaology/effects/cellcharge.dm @@ -6,7 +6,7 @@ /datum/artifact_effect/cellcharge/DoEffectTouch(var/mob/user) if(user) - if(istype(user, /mob/living/silicon/robot)) + if(isrobot(user)) var/mob/living/silicon/robot/R = user for (var/obj/item/cell/D in R.contents) D.charge += rand() * 100 + 50 @@ -16,12 +16,24 @@ /datum/artifact_effect/cellcharge/DoEffectAura() if(holder) var/turf/T = get_turf(holder) - for (var/obj/machinery/power/apc/C in range(200, T)) + for (var/obj/machinery/power/apc/C in GLOB.apcs) + if(T.z != C.z) + continue + if(get_dist(T, C) > 200) + continue for (var/obj/item/cell/B in C.contents) B.charge += 25 - for (var/obj/machinery/power/smes/S in range (src.effectrange,src)) + for (var/obj/machinery/power/smes/S in GLOB.smeses) + if(T.z != S.z) + continue + if(get_dist(T, S) > src.effectrange) + continue S.charge += 25 - for (var/mob/living/silicon/robot/M in range(50, T)) + for (var/mob/living/silicon/robot/M in silicon_mob_list) + if(T.z != M.z) + continue + if(get_dist(T, M) > 50) + continue for (var/obj/item/cell/D in M.contents) D.charge += 25 if(world.time - last_message > 200) @@ -32,12 +44,24 @@ /datum/artifact_effect/cellcharge/DoEffectPulse() if(holder) var/turf/T = get_turf(holder) - for (var/obj/machinery/power/apc/C in range(200, T)) + for (var/obj/machinery/power/apc/C in GLOB.apcs) + if(T.z != C.z) + continue + if(get_dist(T, C) > 200) + continue for (var/obj/item/cell/B in C.contents) B.charge += rand() * 100 - for (var/obj/machinery/power/smes/S in range (src.effectrange,src)) + for (var/obj/machinery/power/smes/S in GLOB.smeses) + if(T.z != S.z) + continue + if(get_dist(T, S) > src.effectrange) + continue S.charge += 250 - for (var/mob/living/silicon/robot/M in range(100, T)) + for (var/mob/living/silicon/robot/M in silicon_mob_list) + if(T.z != M.z) + continue + if(get_dist(T, M) > 100) + continue for (var/obj/item/cell/D in M.contents) D.charge += rand() * 100 if(world.time - last_message > 200) diff --git a/code/modules/xenoarcheaology/effects/celldrain.dm b/code/modules/xenoarcheaology/effects/celldrain.dm index d04445cc0f7..ed7016da88f 100644 --- a/code/modules/xenoarcheaology/effects/celldrain.dm +++ b/code/modules/xenoarcheaology/effects/celldrain.dm @@ -18,12 +18,24 @@ /datum/artifact_effect/celldrain/DoEffectAura() if(holder) var/turf/T = get_turf(holder) - for (var/obj/machinery/power/apc/C in range(200, T)) + for (var/obj/machinery/power/apc/C in GLOB.apcs) + if(T.z != C.z) + continue + if(get_dist(T, C) > 200) + continue for (var/obj/item/cell/B in C.contents) B.charge = max(B.charge - 50,0) - for (var/obj/machinery/power/smes/S in range (src.effectrange,src)) + for (var/obj/machinery/power/smes/S in GLOB.smeses) + if(T.z != S.z) + continue + if(get_dist(T, S) > src.effectrange) + continue S.charge = max(S.charge - 100,0) - for (var/mob/living/silicon/robot/M in range(50, T)) + for (var/mob/living/silicon/robot/M in silicon_mob_list) + if(T.z != M.z) + continue + if(get_dist(T, M) > 50) + continue for (var/obj/item/cell/D in M.contents) D.charge = max(D.charge - 50,0) if(world.time - last_message > 200) @@ -34,12 +46,24 @@ /datum/artifact_effect/celldrain/DoEffectPulse() if(holder) var/turf/T = get_turf(holder) - for (var/obj/machinery/power/apc/C in range(200, T)) + for (var/obj/machinery/power/apc/C in GLOB.apcs) + if(T.z != C.z) + continue + if(get_dist(T, C) > 200) + continue for (var/obj/item/cell/B in C.contents) B.charge = max(B.charge - rand() * 150,0) - for (var/obj/machinery/power/smes/S in range (src.effectrange,src)) + for (var/obj/machinery/power/smes/S in GLOB.smeses) + if(T.z != S.z) + continue + if(get_dist(T, S) > src.effectrange) + continue S.charge = max(S.charge - 250,0) - for (var/mob/living/silicon/robot/M in range(100, T)) + for (var/mob/living/silicon/robot/M in silicon_mob_list) + if(T.z != M.z) + continue + if(get_dist(T, M) > 100) + continue for (var/obj/item/cell/D in M.contents) D.charge = max(D.charge - rand() * 150,0) if(world.time - last_message > 200) diff --git a/config/entries/game_options.txt b/config/entries/game_options.txt index 1c33cd363f1..fe29f7247c8 100644 --- a/config/entries/game_options.txt +++ b/config/entries/game_options.txt @@ -14,3 +14,6 @@ ENGINE_SUBMAP EngineSubmap_RUST 40 ENGINE_SUBMAP EngineSubmap_Tesla 20 ## Singulo ENGINE_SUBMAP EngineSubmap_Singulo 20 + +## Comment this out if you don't want to use the 'nightshift lighting' subsystem to adjust lights based on ingame time. +NIGHTSHIFTS_ENABLED diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm index a4d69a03b17..e05ac23ecb2 100644 --- a/maps/~map_system/maps.dm +++ b/maps/~map_system/maps.dm @@ -23,21 +23,26 @@ var/list/all_maps = list() var/full_name = "Unnamed Map" var/path - var/list/zlevels = list() var/zlevel_datum_type // If populated, all subtypes of this type will be instantiated and used to populate the *_levels lists. - - var/list/station_levels = list() // Z-levels the station exists on - var/list/admin_levels = list() // Z-levels for admin functionality (Centcom, shuttle transit, etc) - var/list/contact_levels = list() // Z-levels that can be contacted from the station, for eg announcements - var/list/player_levels = list() // Z-levels a character can typically reach - var/list/sealed_levels = list() // Z-levels that don't allow random transit at edge - var/list/xenoarch_exempt_levels = list() // Z-levels exempt from xenoarch finds and digsites spawning. - var/list/empty_levels = null // Empty Z-levels that may be used for various things (currently used by bluespace jump) - - var/list/map_levels // Z-levels available to various consoles, such as the crew monitor (when that gets coded in). Defaults to station_levels if unset. var/list/base_turf_by_z = list() // Custom base turf by Z-level. Defaults to world.turf for unlisted Z-levels + // Automatically populated lists made static for faster lookups + var/list/zlevels = list() + var/list/station_levels = list() // Z-levels the station exists on + var/list/admin_levels = list() // Z-levels for admin functionality (Centcom, shuttle transit, etc) + var/list/contact_levels = list() // Z-levels that can be contacted from the station, for eg announcements + var/list/player_levels = list() // Z-levels a character can typically reach + var/list/sealed_levels = list() // Z-levels that don't allow random transit at edge + var/list/xenoarch_exempt_levels = list() //Z-levels exempt from xenoarch finds and digsites spawning. + var/list/empty_levels = null // Empty Z-levels that may be used for various things (currently used by bluespace jump) + // End Static Lists + + // Z-levels available to various consoles, such as the crew monitor. Defaults to station_levels if unset. + var/list/map_levels + + // E-mail TLDs to use for NTnet modular computer e-mail addresses var/list/usable_email_tlds = list("freemail.nt") + // This list contains the z-level numbers which can be accessed via space travel and the percentile chances to get there. var/list/accessible_z_levels = list() @@ -138,6 +143,39 @@ var/list/all_maps = list() if(!allowed_jobs || !allowed_jobs.len) allowed_jobs = subtypesof(/datum/job) +// Gets the current time on a current zlevel, and returns a time datum +/datum/map/proc/get_zlevel_time(var/z) + if(!z) + z = 1 + var/datum/planet/P = z <= SSplanets.z_to_planet.len ? SSplanets.z_to_planet[z] : null + // We found a planet tied to that zlevel, give them the time + if(P?.current_time) + return P.current_time + + // We have to invent a time + else + var/datum/time/T = new (station_time_in_ds) + return T + +// Returns a boolean for if it's night or not on a particular zlevel +/datum/map/proc/get_night(var/z) + if(!z) + z = 1 + var/datum/time/now = get_zlevel_time(z) + var/percent = now.seconds_stored / now.seconds_in_day //practically all of these are in DS + testing("get_night is [percent] through the day on [z]") + + // First quarter, last quarter + if(percent < 0.25 || percent > 0.75) + return TRUE + // Second quarter, third quarter + else + return FALSE + +// Boolean for if we should use SSnightshift night hours +/datum/map/proc/get_nightshift() + return get_night(1) //Defaults to z1, customize however you want on your own maps + /datum/map/proc/setup_map() return diff --git a/tgui/packages/tgui/interfaces/APC.js b/tgui/packages/tgui/interfaces/APC.js index 38969c8bf3b..8dcc357f224 100644 --- a/tgui/packages/tgui/interfaces/APC.js +++ b/tgui/packages/tgui/interfaces/APC.js @@ -193,6 +193,42 @@ const ApcContent = (props, context) => { disabled={locked} onClick={() => act('cover')} /> )} /> + +