diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index cc3fccb2a01..4b64266f2b7 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -25,6 +25,11 @@ #define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds +// main_status var +#define APC_EXTERNAL_POWER_NOTCONNECTED 0 +#define APC_EXTERNAL_POWER_NOENERGY 1 +#define APC_EXTERNAL_POWER_GOOD 2 + // APC malf status #define APC_MALF_NOT_HACKED 1 #define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core. @@ -75,7 +80,7 @@ var/lastused_equip = 0 var/lastused_environ = 0 var/lastused_total = 0 - var/main_status = 0 + var/main_status = APC_EXTERNAL_POWER_NOTCONNECTED powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( var/malfhack = 0 //New var for my changes to AI malf. --NeoFite var/mob/living/silicon/ai/malfai = null //See above --NeoFite @@ -213,7 +218,7 @@ if(isarea(A)) area = A // no-op, keep the name - else if(isarea(A) && src.areastring == null) + else if(isarea(A) && !areastring) area = A name = "\improper [area.name] APC" else @@ -429,8 +434,8 @@ //attack with an item - open/close cover, insert cell, or (un)lock interface /obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) - if(issilicon(user) && get_dist(src,user)>1) - return src.attack_hand(user) + if(issilicon(user) && get_dist(src, user) > 1) + return attack_hand(user) else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside if(cell) @@ -445,7 +450,7 @@ W.forceMove(src) cell = W user.visible_message(\ - "[user.name] has inserted the power cell to [src.name]!",\ + "[user.name] has inserted the power cell to [name]!",\ "You insert the power cell.") chargecount = 0 update_icon() @@ -474,7 +479,7 @@ return user.visible_message("[user.name] adds cables to the APC frame.", \ "You start adding cables to the APC frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) if(do_after(user, 20, target = src)) if(C.get_amount() < 10 || !C) return @@ -499,10 +504,10 @@ user.visible_message("[user.name] inserts the power control board into [src].", \ "You start to insert the power control board into the frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) if(do_after(user, 10, target = src)) - if(has_electronics==0) - has_electronics = 1 + if(!has_electronics) + has_electronics = TRUE locked = FALSE to_chat(user, "You place the power control board inside the frame.") qdel(W) @@ -549,11 +554,11 @@ return to_chat(user, "You are trying to remove the power control board..." ) if(I.use_tool(src, user, 50, volume = I.tool_volume)) - if(has_electronics==1) - has_electronics = 0 + if(has_electronics) + has_electronics = FALSE if(stat & BROKEN) user.visible_message(\ - "[user.name] has broken the power control board inside [src.name]!", + "[user.name] has broken the power control board inside [name]!", "You break the charred power control board and remove the remains.", "You hear a crack.") return @@ -561,19 +566,19 @@ else if(emagged) // We emag board, not APC's frame emagged = FALSE user.visible_message( - "[user.name] has discarded emaged power control board from [src.name]!", - "You discarded shorten board.") + "[user.name] has discarded the shorted power control board from [name]!", + "You discarded the shorted board.") return else if(malfhack) // AI hacks board, not APC's frame user.visible_message(\ - "[user.name] has discarded strangely programmed power control board from [src.name]!", - "You discarded strangely programmed board.") + "[user.name] has discarded strangely the programmed power control board from [name]!", + "You discarded the strangely programmed board.") malfai = null malfhack = 0 return else user.visible_message(\ - "[user.name] has removed the power control board from [src.name]!", + "[user.name] has removed the power control board from [name]!", "You remove the power control board.") new /obj/item/apc_electronics(loc) return @@ -711,36 +716,29 @@ // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/attack_hand(mob/user) -// if(!can_use(user)) This already gets called in interact() and in topic() -// return if(!user) return - src.add_fingerprint(user) + add_fingerprint(user) - if(usr == user && opened && (!issilicon(user))) + if(usr == user && opened && !issilicon(user)) if(cell) - if(issilicon(user)) - cell.loc=src.loc // Drop it, whoops. - else - user.put_in_hands(cell) + user.put_in_hands(cell) cell.add_fingerprint(user) cell.update_icon() - - src.cell = null - user.visible_message("[user.name] removes the power cell from [src.name]!", "You remove the power cell.") -// to_chat(user, "You remove the power cell.") - charging = 0 - src.update_icon() + cell = null + user.visible_message("[user.name] removes [cell] from [src]!", "You remove the [cell].") + charging = FALSE + update_icon() return if(stat & (BROKEN|MAINT)) return - src.interact(user) + interact(user) /obj/machinery/power/apc/attack_ghost(mob/user) if(panel_open) wires.Interact(user) - return ui_interact(user) + return tgui_interact(user) /obj/machinery/power/apc/interact(mob/user) if(!user) @@ -749,7 +747,7 @@ if(panel_open) wires.Interact(user) - return ui_interact(user) + return tgui_interact(user) /obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf) @@ -770,22 +768,16 @@ else return APC_MALF_NOT_HACKED -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!user) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/apc/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - // the ui does not exist, so we'll create a new one - ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460) + ui = new(user, src, ui_key, "APC", name, 510, 460, master_ui, state) ui.open() - // Auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/power/apc/tgui_data(mob/user) + var/list/data = list() data["locked"] = is_locked(user) + data["normallyLocked"] = locked data["isOperating"] = operating data["externalPower"] = main_status data["powerCellStatus"] = cell ? cell.percent() : null @@ -862,7 +854,7 @@ var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user if( \ - src.aidisabled || \ + aidisabled || \ malfhack && istype(malfai) && \ ( \ (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ @@ -873,21 +865,21 @@ to_chat(user, "\The [src] has AI control disabled!") user << browse(null, "window=apc") user.unset_machine() - return 0 + return FALSE else - if((!in_range(src, user) || !istype(src.loc, /turf))) - return 0 + if((!in_range(src, user) || !istype(loc, /turf))) + return FALSE var/mob/living/carbon/human/H = user if(istype(H)) if(H.getBrainLoss() >= 60) for(var/mob/M in viewers(src, null)) to_chat(M, "[H] stares cluelessly at [src] and drools.") - return 0 + return FALSE else if(prob(H.getBrainLoss())) to_chat(user, "You momentarily forget how to use [src].") - return 0 - return 1 + return FALSE + return TRUE /obj/machinery/power/apc/proc/is_authenticated(mob/user as mob) if(user.can_admin_interact()) @@ -905,104 +897,59 @@ else return locked -/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1) - if(..()) - return 1 - - if(!can_use(usr, 1)) - return 1 - - if(href_list["lock"]) - if(!is_authenticated(usr)) - return - - coverlocked = !coverlocked - - else if(href_list["breaker"]) - if(!is_authenticated(usr)) - return - - toggle_breaker() - - else if(href_list["toggle_nightshift"]) - if(!is_authenticated(usr)) - return - - if(last_nightshift_switch > world.time + 100) // don't spam... - to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") - return - last_nightshift_switch = world.time - set_nightshift(!nightshift_lights) - - else if(href_list["cmode"]) - if(!is_authenticated(usr)) - return - - chargemode = !chargemode - if(!chargemode) - charging = 0 - update_icon() - - else if(href_list["eqp"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["eqp"]) - equipment = setsubsystem(val) - update_icon() - update() - - else if(href_list["lgt"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["lgt"]) - lighting = setsubsystem(val) - update_icon() - update() - - else if(href_list["env"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["env"]) - environ = setsubsystem(val) - update_icon() - update() - else if( href_list["close"] ) - SSnanoui.close_user_uis(usr, src) - - return 0 - else if(href_list["close2"]) - usr << browse(null, "window=apcwires") - - return 0 - - else if(href_list["overload"]) - if(issilicon(usr) && !aidisabled) - overload_lighting() - - else if(href_list["malfhack"]) - if(get_malf_status(usr)) - malfhack(usr) - - else if(href_list["occupyapc"]) - if(get_malf_status(usr)) - malfoccupy(usr) - - else if(href_list["deoccupyapc"]) - if(get_malf_status(usr)) - malfvacate() - - else if(href_list["toggleaccess"]) - if(istype(usr, /mob/living/silicon)) - if(emagged || aidisabled || (stat & (BROKEN|MAINT))) - to_chat(usr, "The APC does not respond to the command.") +/obj/machinery/power/apc/tgui_act(action, params) + if(..() || !can_use(usr, TRUE) || (locked && !usr.has_unlimited_silicon_privilege && (action != "toggle_nightshift") && !usr.can_admin_interact())) + return + . = TRUE + switch(action) + if("lock") + if(usr.has_unlimited_silicon_privilege) + if(emagged || stat & BROKEN) + to_chat(usr, "The APC does not respond to the command!") + return FALSE + else + locked = !locked + update_icon() else - locked = !locked + to_chat(usr, "Access Denied!") + return FALSE + if("cover") + coverlocked = !coverlocked + if("breaker") + toggle_breaker(usr) + if("toggle_nightshift") + if(last_nightshift_switch > world.time + 100) // don't spam... + to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") + return FALSE + last_nightshift_switch = world.time + set_nightshift(!nightshift_lights) + if("charge") + chargemode = !chargemode + if("channel") + if(params["eqp"]) + equipment = setsubsystem(text2num(params["eqp"])) update_icon() - - return 0 + update() + else if(params["lgt"]) + lighting = setsubsystem(text2num(params["lgt"])) + update_icon() + update() + else if(params["env"]) + environ = setsubsystem(text2num(params["env"])) + update_icon() + update() + if("overload") + if(usr.has_unlimited_silicon_privilege) + overload_lighting() + if("hack") + if(get_malf_status(usr)) + malfhack(usr) + if("occupy") + if(get_malf_status(usr)) + malfoccupy(usr) + if("deoccupy") + if(get_malf_status(usr)) + malfvacate() /obj/machinery/power/apc/proc/toggle_breaker() operating = !operating @@ -1033,7 +980,7 @@ if(!malf.can_shunt) to_chat(malf, "You cannot shunt!") return - if(!is_station_level(src.z)) + if(!is_station_level(z)) return occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) occupier.adjustOxyLoss(malf.getOxyLoss()) @@ -1080,21 +1027,21 @@ /obj/machinery/power/apc/proc/ion_act() //intended to be exactly the same as an AI malf attack - if(!src.malfhack && is_station_level(src.z)) + if(!malfhack && is_station_level(z)) if(prob(3)) - src.locked = 1 - if(src.cell.charge > 0) - src.cell.charge = 0 + locked = TRUE + if(cell.charge > 0) + cell.charge = 0 cell.corrupt() - src.malfhack = 1 + malfhack = TRUE update_icon() var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) + smoke.set_up(3, 0, loc) smoke.attach(src) smoke.start() do_sparks(3, 1, src) for(var/mob/M in viewers(src)) - M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) + M.show_message("The [name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) /obj/machinery/power/apc/surplus() @@ -1137,12 +1084,12 @@ var/excess = surplus() - if(!src.avail()) - main_status = 0 + if(!avail()) + main_status = APC_EXTERNAL_POWER_NOTCONNECTED else if(excess < 0) - main_status = 1 + main_status = APC_EXTERNAL_POWER_NOENERGY else - main_status = 2 + main_status = APC_EXTERNAL_POWER_GOOD if(debug) log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]") @@ -1257,7 +1204,7 @@ if(shock_mobs.len) var/mob/living/L = pick(shock_mobs) L.electrocute_act(rand(5, 25), "electrical arc") - playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1) + playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, TRUE) Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) else // no cell, switch everything off @@ -1383,3 +1330,7 @@ updateDialog() #undef APC_UPDATE_ICON_COOLDOWN + +#undef APC_EXTERNAL_POWER_NOTCONNECTED +#undef APC_EXTERNAL_POWER_NOENERGY +#undef APC_EXTERNAL_POWER_GOOD diff --git a/nano/templates/apc.tmpl b/nano/templates/apc.tmpl deleted file mode 100644 index 234074fe577..00000000000 --- a/nano/templates/apc.tmpl +++ /dev/null @@ -1,197 +0,0 @@ -
- {{if data.siliconUser}} -
- Interface Lock: -
-
- {{:helper.link('Engaged', 'lock', {'toggleaccess' : 1}, data.siliconLock ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'toggleaccess' : 1}, data.malfStatus >= 2 ? 'linkOff' : (data.siliconLock ? null : 'selected'))}} -
-
- {{else}} - {{if data.locked}} - Swipe an ID card to unlock this interface - {{else}} - Swipe an ID card to lock this interface - {{/if}} - {{/if}} -
- -
- -

Power Status

- -
-
- Main Breaker: -
-
- {{if data.locked && !data.siliconUser}} - {{if data.isOperating}} - On - {{else}} - Off - {{/if}} - {{else}} - {{:helper.link('On', 'power-off', {'breaker' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'breaker' : 1}, data.isOperating ? null : 'selected')}} - {{/if}} -
-
- -
-
- External Power: -
-
- {{if data.externalPower == 2}} - Good - {{else data.externalPower == 1}} - Low - {{else}} - None - {{/if}} -
-
- -
-
- Power Cell: -
- {{if data.powerCellStatus == null}} -
- Power cell removed. -
- {{else}} - - {{:helper.displayBar(data.powerCellStatus, 0, 100, (data.powerCellStatus >= 50) ? 'good' : (data.powerCellStatus >= 25) ? 'average' : 'bad')}} -
- {{:helper.smoothRound(data.powerCellStatus, 1)}}% -
- {{/if}} -
- - {{if data.powerCellStatus != null}} -
-
- Charge Mode: -
-
- {{if data.locked && !data.siliconUser}} - {{if data.chargeMode}} - Auto - {{else}} - Off - {{/if}} - {{else}} - {{:helper.link('Auto', 'refresh', {'cmode' : 1}, data.chargeMode ? 'selected' : null)}}{{:helper.link('Off', 'close', {'cmode' : 1}, data.chargeMode ? null : 'selected')}} - {{/if}} -   - {{if data.chargingStatus > 1}} - [Fully Charged] - {{else data.chargingStatus == 1}} - [Charging] - {{else}} - [Not Charging] - {{/if}} -
-
- {{/if}} - - -

Power Channels

- - {{for data.powerChannels}} -
-
- {{:value.title}}: -
-
- {{:helper.smoothRound(value.powerLoad)}} W -
-
-    - {{if value.status <= 1}} - Off - {{else value.status >= 2}} - On - {{/if}} - {{if data.locked}} - {{if value.status == 1 || value.status == 3}} -   Auto - {{else}} -   Manual - {{/if}} - {{/if}} -
- {{if !data.locked || data.siliconUser}} -
- {{:helper.link('Auto', 'refresh', value.topicParams.auto, (value.status == 1 || value.status == 3) ? 'selected' : null)}} - {{:helper.link('On', 'power-off', value.topicParams.on, (value.status == 2) ? 'selected' : null)}} - {{:helper.link('Off', 'close', value.topicParams.off, (value.status == 0) ? 'selected' : null)}} -
- {{/if}} -
- {{/for}} - -
-
- Total Load: -
-
- {{:helper.smoothRound(data.totalLoad)}} W -
-
- -
 
- -
-
- Cover Lock: -
-
- {{if data.locked && !data.siliconUser}} - {{if data.coverLocked}} - Engaged - {{else}} - Disengaged - {{/if}} - {{else}} - {{:helper.link('Engaged', 'lock', {'lock' : 1}, data.coverLocked ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, data.coverLocked ? null : 'selected')}} - {{/if}} -
-
- - {{if data.siliconUser}} -

System Overrides

- -
- {{:helper.link('Overload Lighting Circuit', 'lightbulb-o', {'overload' : 1})}} - {{if data.malfStatus == 1}} - {{:helper.link('Override Programming', 'file-text', {'malfhack' : 1})}} - {{else data.malfStatus == 2}} - {{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1})}} - {{else data.malfStatus == 3}} - {{:helper.link('Return to Main Core', 'arrow-left', {'deoccupyapc' : 1})}} - {{else data.malfStatus == 4}} - {{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1}, 'linkOff')}} - {{/if}} -
- {{/if}} - -
-
- Night Shift Lighting: -
-
- {{if data.locked}} - {{if data.nightshiftLights}} - On - {{else}} - Off - {{/if}} - {{else}} - {{:helper.link('Enabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? 'selected' : null)}}{{:helper.link('Disabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? null : 'selected')}} - {{/if}} -
-
- -
diff --git a/tgui/packages/tgui/interfaces/APC.js b/tgui/packages/tgui/interfaces/APC.js new file mode 100644 index 00000000000..652188a3073 --- /dev/null +++ b/tgui/packages/tgui/interfaces/APC.js @@ -0,0 +1,195 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components'; +import { Window } from '../layouts'; +import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; + +export const APC = (props, context) => { + return ( + + + + + + ); +}; + +const powerStatusMap = { + 2: { + color: 'good', + externalPowerText: 'External Power', + chargingText: 'Fully Charged', + }, + 1: { + color: 'average', + externalPowerText: 'Low External Power', + chargingText: 'Charging', + }, + 0: { + color: 'bad', + externalPowerText: 'No External Power', + chargingText: 'Not Charging', + }, +}; + +const malfMap = { + 1: { + icon: 'terminal', + content: 'Override Programming', + action: 'hack', + }, + 2: { + icon: 'caret-square-down', + content: 'Shunt Core Process', + action: 'occupy', + }, + 3: { + icon: 'caret-square-left', + content: 'Return to Main Core', + action: 'deoccupy', + }, + 4: { + icon: 'caret-square-down', + content: 'Shunt Core Process', + action: 'occupy', + }, +}; + +const ApcContent = (props, context) => { + const { act, data } = useBackend(context); + const locked = data.locked && !data.siliconUser; + const normallyLocked = data.normallyLocked; + const externalPowerStatus = powerStatusMap[data.externalPower] + || powerStatusMap[0]; + const chargingStatus = powerStatusMap[data.chargingStatus] + || powerStatusMap[0]; + const channelArray = data.powerChannels || []; + const malfStatus = malfMap[data.malfStatus] || malfMap[0]; + const adjustedCellChange = data.powerCellStatus / 100; + + return ( + + +
+ + act('breaker')} /> + )}> + [ {externalPowerStatus.externalPowerText} ] + + + + + act('charge')} /> + )}> + [ {chargingStatus.chargingText} ] + + +
+
+ + {channelArray.map(channel => { + const { topicParams } = channel; + return ( + + = 2 ? 'good' : 'bad'}> + {channel.status >= 2 ? 'On' : 'Off'} + +
+
+ {!!data.malfStatus && ( +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js b/tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js new file mode 100644 index 00000000000..652c981da27 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/InterfaceLockNoticeBox.js @@ -0,0 +1,60 @@ +import { useBackend } from '../../backend'; +import { Button, Flex, NoticeBox } from '../../components'; + +/** + * This component by expects the following fields to be returned + * from ui_data: + * + * - siliconUser: boolean + * - locked: boolean + * - normallyLocked: boolean + * + * And expects the following ui_act action to be implemented: + * + * - lock - for toggling the lock as a silicon user. + * + * All props can be redefined if you want custom behavior, but + * it's preferred to stick to defaults. + */ +export const InterfaceLockNoticeBox = (props, context) => { + const { act, data } = useBackend(context); + const { + siliconUser = data.siliconUser, + locked = data.locked, + normallyLocked = data.normallyLocked, + onLockStatusChange = () => act('lock'), + accessText = 'an ID card', + } = props; + // For silicon users + if (siliconUser) { + return ( + + + + Interface lock status: + + + +