From 9f250ce3c9e1cface5a6ef6740eb3b7ab3198c57 Mon Sep 17 00:00:00 2001 From: Atermonera Date: Sat, 21 Mar 2020 13:38:32 -0700 Subject: [PATCH 1/2] Emergency lighting --- code/modules/power/apc.dm | 12 +- code/modules/power/cells/power_cells.dm | 13 ++ code/modules/power/lighting.dm | 133 +++++++++++++++++- .../ShadowQuill - Emergency-Lighting.yml | 37 +++++ nano/templates/apc.tmpl | 13 ++ 5 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 html/changelogs/ShadowQuill - Emergency-Lighting.yml diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index d366f07632a..ac082d739bc 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -105,6 +105,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 @@ -795,6 +796,7 @@ "gridCheck" = grid_check, "coverLocked" = coverlocked, "siliconUser" = issilicon(user) || isobserver(user), //I add observer here so admins can have more control, even if it makes 'siliconUser' seem inaccurate. + "emergencyLights" = !emergency_lights, "powerChannels" = list( list( @@ -835,7 +837,7 @@ if (!ui) // the ui does not exist, so we'll create a new() one // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 520, data["siliconUser"] ? 465 : 440) + ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 520, data["siliconUser"] ? 490 : 465) // when the ui is first opened this is the data it will use ui.set_initial_data(data) // open the new ui window @@ -932,6 +934,14 @@ update_icon() update() + else if (href_list["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 + else if (href_list["breaker"]) toggle_breaker() diff --git a/code/modules/power/cells/power_cells.dm b/code/modules/power/cells/power_cells.dm index 3afb0d54507..3638c625f1b 100644 --- a/code/modules/power/cells/power_cells.dm +++ b/code/modules/power/cells/power_cells.dm @@ -132,3 +132,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/weapon/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/weapon/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 \ No newline at end of file diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index ba60c84d32d..4d30f268f12 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -9,7 +9,12 @@ #define LIGHT_BROKEN 2 #define LIGHT_BURNED 3 #define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb +<<<<<<< HEAD #define LIGHTING_POWER_FACTOR 2 //5W per luminosity * range //VOREStation Edit: why the fuck are lights eating so much power, 2W per thing +======= +#define LIGHTING_POWER_FACTOR 5 //5W per luminosity * range +#define LIGHT_EMERGENCY_POWER_USE 0.2 //How much power emergency lights will consume per tick +>>>>>>> 28f34f1... Merge pull request #6820 from Meghan-Rossi/Emergency-Lighting var/global/list/light_type_cache = list() /proc/get_light_type_instance(var/light_type) @@ -29,6 +34,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/weapon/cell/cell = null + + var/cell_connectors = TRUE /obj/machinery/light_construct/New(var/atom/newloc, var/newdir, var/building = 0, var/datum/frame/frame_types/frame_type, var/obj/machinery/light/fixture = null) ..(newloc) @@ -61,9 +70,44 @@ var/global/list/light_type_cache = list() to_chat(user, "It's wired.") if(3) to_chat(user, "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/weapon/W as obj, mob/user as mob) src.add_fingerprint(user) + if(istype(W, /obj/item/weapon/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) @@ -114,6 +158,10 @@ var/global/list/light_type_cache = list() var/obj/machinery/light/newlight = new fixture_type(src.loc, src) newlight.set_dir(src.dir) src.transfer_fingerprints_to(newlight) + if(cell) + newlight.cell = cell + cell.forceMove(newlight) + cell = null qdel(src) return ..() @@ -192,6 +240,16 @@ var/global/list/light_type_cache = list() 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/weapon/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 + /obj/machinery/light/flicker auto_flicker = TRUE @@ -208,6 +266,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,10 +286,14 @@ var/global/list/light_type_cache = list() /obj/machinery/light/flamp/New(atom/newloc, obj/machinery/light_construct/construct = null) ..(newloc, construct) - if(construct) + start_with_cell = FALSE lamp_shade = 0 update_icon() + else + if(start_with_cell && !no_emergency) + cell = new/obj/item/weapon/cell/emergency_light(src) + /obj/machinery/light/flamp/flicker auto_flicker = TRUE @@ -256,11 +324,14 @@ var/global/list/light_type_cache = list() ..(newloc) if(construct) + start_with_cell = FALSE status = LIGHT_EMPTY construct_type = construct.type construct.transfer_fingerprints_to(src) set_dir(construct.dir) else + if(start_with_cell && !no_emergency) + cell = new/obj/item/weapon/cell/emergency_light(src) var/obj/item/weapon/light/L = get_light_type_instance(light_type) update_from_bulb(L) if(prob(L.broken_chance)) @@ -274,6 +345,7 @@ var/global/list/light_type_cache = list() if(A) on = 0 // A.update_lights() + QDEL_NULL(cell) return ..() /obj/machinery/light/update_icon() @@ -369,6 +441,10 @@ var/global/list/light_type_cache = list() else use_power = 2 set_light(brightness_range, brightness_power, brightness_color) + else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off()) + use_power = 1 + emergency_mode = TRUE + START_PROCESSING(SSobj, src) else use_power = 1 set_light(0) @@ -408,6 +484,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) var/fitting = get_fitting_name() @@ -420,6 +499,8 @@ var/global/list/light_type_cache = list() to_chat(user, "[desc] The [fitting] is burnt out.") if(LIGHT_BROKEN) to_chat(user, "[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/weapon/light/L = light_type @@ -545,6 +626,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() @@ -558,6 +645,28 @@ 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 @@ -572,12 +681,17 @@ 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) - src.flicker(1) + no_emergency = !no_emergency + to_chat(user, "Emergency lights for this fixture have been [no_emergency ? "disabled" : "enabled"].") + update(FALSE) return +// 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 @@ -700,6 +814,17 @@ var/global/list/light_type_cache = list() // use power /obj/machinery/light/process() + 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. diff --git a/html/changelogs/ShadowQuill - Emergency-Lighting.yml b/html/changelogs/ShadowQuill - Emergency-Lighting.yml new file mode 100644 index 00000000000..b046710905b --- /dev/null +++ b/html/changelogs/ShadowQuill - Emergency-Lighting.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Shadow-Quill + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Adds emergency backup cells to most light fixtures" + - tweak: "AIs can turn emergency lighting on and off by clicking light fixtures. Flickering the lights has been moved to alt-click. \ No newline at end of file diff --git a/nano/templates/apc.tmpl b/nano/templates/apc.tmpl index b77b024c8f5..d99b1f87004 100644 --- a/nano/templates/apc.tmpl +++ b/nano/templates/apc.tmpl @@ -188,6 +188,19 @@ +
+
+ Emergency Lighting: +
+
+ {{if data.locked && !data.siliconUser}} + {{:data.emergencyLights ? "Enabled" : "Disabled"}} + {{else}} + {{:helper.link(data.emergencyLights ? 'Enabled' : 'Disabled', data.emergencyLights ? 'power' : 'close', {'emergency_lighting' : 1}, null)}} + {{/if}} +
+
+ {{if data.siliconUser}}

System Overrides

From 90e6e1fea241446e3e4e9cab7a4628d8d058d62c Mon Sep 17 00:00:00 2001 From: Novacat <35587478+Novacat@users.noreply.github.com> Date: Sat, 21 Mar 2020 16:47:52 -0400 Subject: [PATCH 2/2] Update lighting.dm --- code/modules/power/lighting.dm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 4d30f268f12..a281ff7b0f5 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -9,12 +9,8 @@ #define LIGHT_BROKEN 2 #define LIGHT_BURNED 3 #define LIGHT_BULB_TEMPERATURE 400 //K - used value for a 60W bulb -<<<<<<< HEAD #define LIGHTING_POWER_FACTOR 2 //5W per luminosity * range //VOREStation Edit: why the fuck are lights eating so much power, 2W per thing -======= -#define LIGHTING_POWER_FACTOR 5 //5W per luminosity * range #define LIGHT_EMERGENCY_POWER_USE 0.2 //How much power emergency lights will consume per tick ->>>>>>> 28f34f1... Merge pull request #6820 from Meghan-Rossi/Emergency-Lighting var/global/list/light_type_cache = list() /proc/get_light_type_instance(var/light_type)