Alt-click lock toggle functionality, APC SFX upgrade (#21310)

Updated objects whose behavior when an ID was used on them was strictly
toggling lock/unlock status to use Alt-click functionality, with
associated pop-up messages for the clicking player. Mechanics hints for
all affected objects updated.

Fixed APC sprite's status lights for each power channel (general messy
alignment).

Updated APC sprite's lock status light to be brighter/clearer for better
visual feedback of lock status.

Added a lot of nice clicky beepy sounds to APCs. They all came from here
on TG, and they seem oooooold so I wasn't able to find credit. [So,
credit to TG for sounds/machines/terminal sounds until we track down an
actual
name!](https://github.com/tgstation/tgstation/tree/master/sound/machines/terminal)

Updated dmdocs as I passed by them.
This commit is contained in:
Batrachophreno
2025-10-03 15:56:32 +00:00
committed by GitHub
parent 41dedf13aa
commit f06bdc8b3c
34 changed files with 387 additions and 201 deletions
+37 -22
View File
@@ -147,7 +147,8 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
/obj/machinery/power/apc/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "An APC (Area Power Controller) regulates and supplies backup power for the area they are in."
. += "Their power channels are divided into 'environmental' (items that manipulate airflow and temperature), 'lighting' (lights), and 'equipment' (everything else that consumes power)."
. += "ALT-click the [src] to lock or unlock it (if you have the appropriate ID access)."
. += "APC power channels are divided into 'environmental' (items that manipulate airflow and temperature), 'lighting' (lights), and 'equipment' (everything else that consumes power)."
. += "Power consumption and backup power cell charge can be seen from the interface; further controls (turning a specific channel on, off or automatic, \
toggling the APC's ability to charge the backup cell, or toggling power for the entire area via master breaker) first requires the interface to be unlocked \
with an ID with Engineering access or by one of the ship's robots or AI."
@@ -373,7 +374,7 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
if(update & 2)
ClearOverlays()
if(!(stat & (BROKEN|MAINT)) && update_state & UPDATE_ALLGOOD)
AddOverlays(status_overlays_lock[locked+coverlocked+1])
AddOverlays(status_overlays_lock[locked+1])
AddOverlays(status_overlays_charging[charging+1])
AddOverlays(emissive_appearance(icon, "apc-cover-0"))
AddOverlays(emissive_appearance(icon, "apc-charge-0"))
@@ -585,26 +586,6 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]")
update_icon()
// ID CARD: Attempt to unlock the interface if you have sufficient access.
else if (attacking_item.GetID())
if(emagged)
to_chat(user, "The interface is broken.")
else if(opened != COVER_CLOSED)
to_chat(user, "You must close the cover to swipe an ID card.")
else if(panel_open)
to_chat(user, "You must close the wiring panel to swipe an ID card.")
else if(stat & (BROKEN|MAINT))
to_chat(user, "Nothing happens.")
else if(hacker)
to_chat(user, SPAN_WARNING("Access denied."))
else
if(allowed(usr) && !isWireCut(WIRE_IDSCAN))
locked = !locked
to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.")
update_icon()
else
to_chat(user, SPAN_WARNING("Access denied."))
// CABLE COIL: Install the power terminal (wire stuff on the floor in front of the APC).
else if (attacking_item.iscoil() && !terminal && opened != COVER_CLOSED && has_electronics != HAS_ELECTRONICS_SECURED)
var/turf/T = loc
@@ -798,6 +779,33 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
SPAN_DANGER("You hit the [name] with your [attacking_item.name]!"), \
"You hear a loud metallic bang")
/obj/machinery/power/apc/AltClick(mob/user)
if(Adjacent(user) || issilicon(user))
add_fingerprint(user)
if(emagged)
to_chat(user, "The interface is broken.")
else if(opened != COVER_CLOSED)
to_chat(user, "You must close the cover to swipe an ID card.")
else if(panel_open)
to_chat(user, "You must close the wiring panel to swipe an ID card.")
else if(stat & (BROKEN|MAINT))
to_chat(user, "Nothing happens.")
else if(hacker)
to_chat(user, SPAN_WARNING("Access denied."))
else
if(allowed(usr) && !isWireCut(WIRE_IDSCAN))
locked = !locked
if(locked)
playsound(src, 'sound/machines/terminal/terminal_button03.ogg', 35, FALSE)
else
playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE)
balloon_alert(user, locked ? "locked" : "unlocked")
update_icon()
else
to_chat(user, SPAN_WARNING("Access denied."))
playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE)
balloon_alert(user, "access denied!")
/obj/machinery/power/apc/emag_act(var/remaining_charges, var/mob/user)
if(emagged && !infected)
to_chat(user, SPAN_WARNING("You start sliding your cryptographic device into the charging slot. This will take a few seconds..."))
@@ -957,10 +965,12 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
area.power_light = (lighting > 1)
area.power_equip = (equipment > 1)
area.power_environ = (environ > 1)
else
area.power_light = FALSE
area.power_equip = FALSE
area.power_environ = FALSE
playsound(src.loc, 'sound/machines/terminal/terminal_off.ogg', 50, FALSE)
area.power_change()
/obj/machinery/power/apc/proc/isWireCut(var/wireIndex)
@@ -1070,6 +1080,7 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
if("Environment")
environ = setsubsystem(val)
intent_message(BUTTON_FLICK, 5)
playsound(src, 'sound/machines/terminal/terminal_select.ogg', 18, TRUE)
update_icon()
update()
. = TRUE
@@ -1090,6 +1101,10 @@ ABSTRACT_TYPE(/obj/machinery/power/apc)
/obj/machinery/power/apc/proc/toggle_breaker()
operating = !operating
if(operating)
playsound(src, 'sound/machines/terminal/terminal_on.ogg', 35, FALSE)
else
playsound(src, 'sound/machines/terminal/terminal_off.ogg', 35, FALSE)
update()
update_icon()
intent_message(BUTTON_FLICK)
+19 -10
View File
@@ -14,9 +14,10 @@
req_access = list(ACCESS_ENGINE_EQUIP)
obj_flags = OBJ_FLAG_ROTATABLE | OBJ_FLAG_SIGNALER
var/id
use_power = POWER_USE_OFF //uses powernet power, not APC power
active_power_usage = 30000 //30 kW laser. I guess that means 30 kJ per shot.
/// uses powernet power, not APC power
use_power = POWER_USE_OFF
/// 30 kW laser. I guess that means 30 kJ per shot.
active_power_usage = 30000
var/active = FALSE
var/powered = FALSE
@@ -29,8 +30,8 @@
var/shot_counter = 0
var/state = EMITTER_LOOSE
var/locked = FALSE
var/special_emitter = FALSE // special emitters notify admins if something happens to them, to prevent grief
/// special emitters notify admins if something happens to them, to prevent grief
var/special_emitter = FALSE
var/_wifi_id
var/datum/wifi/receiver/button/emitter/wifi_receiver
@@ -40,7 +41,7 @@
/obj/machinery/power/emitter/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "Standing next to \the [src] and examining it will let you see how many shots it has fired since last being turned on."
. += "Using an Engineering ID on \the [src] will toggle its control locks."
. += "ALT-click the [src] to lock or unlock it (if you have the appropriate ID access)."
. += "You can attach a signaler to \the [src] to remotely toggle it on and off (so long as its controls are not locked)."
/obj/machinery/power/emitter/assembly_hints(mob/user, distance, is_adjacent)
@@ -248,23 +249,31 @@
else
to_chat(user, SPAN_WARNING("You need more welding fuel to complete this task."))
return
..()
return
if(attacking_item.GetID())
/obj/machinery/power/emitter/AltClick(mob/user)
if(Adjacent(user))
add_fingerprint(user)
if(emagged)
to_chat(user, SPAN_WARNING("The lock seems to be broken."))
return
if(allowed(user))
if(active)
locked = !locked
to_chat(user, SPAN_NOTICE("The controls are now [locked ? "locked." : "unlocked."]"))
if(locked)
playsound(src, 'sound/machines/terminal/terminal_button03.ogg', 35, FALSE)
else
playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE)
balloon_alert(user, locked ? "locked" : "unlocked")
else
locked = FALSE //just in case it somehow gets locked
to_chat(user, SPAN_WARNING("The controls can only be locked when \the [src] is online."))
else
to_chat(user, SPAN_WARNING("Access denied."))
playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE)
balloon_alert(user, "access denied!")
return
..()
return
/obj/machinery/power/emitter/emag_act(remaining_charges, mob/user)
if(!emagged)
+28 -15
View File
@@ -155,15 +155,20 @@
req_access = list(ACCESS_ENGINE)
var/health = 100
var/active = FALSE
var/malfunction = FALSE //Malfunction causes parts of the shield to slowly dissapate
// Malfunction causes parts of the shield to slowly dissapate
var/malfunction = FALSE
var/list/deployed_shields = list()
var/list/regenerating = list()
var/is_open = FALSE //Whether or not the wires are exposed
panel_open = FALSE
var/locked = FALSE
var/check_delay = 60 //periodically recheck if we need to rebuild a shield
use_power = POWER_USE_OFF
idle_power_usage = 0
/obj/machinery/shieldgen/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "ALT-click the [src] to lock or unlock it (if you have the appropriate ID access)."
/obj/machinery/shieldgen/Destroy()
collapse_shields()
return ..()
@@ -289,7 +294,7 @@
if(locked)
to_chat(user, SPAN_WARNING("The machine is locked!"))
return
if(is_open)
if(panel_open)
to_chat(user, SPAN_WARNING("The panel must be closed before operating this machine."))
return
@@ -317,14 +322,14 @@
/obj/machinery/shieldgen/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.isscrewdriver())
attacking_item.play_tool_sound(get_turf(src), 50)
if(is_open)
if(panel_open)
to_chat(user, SPAN_NOTICE("You close the panel."))
is_open = FALSE
panel_open = FALSE
else
to_chat(user, SPAN_NOTICE("You open the panel and expose the wiring."))
is_open = TRUE
panel_open = TRUE
else if(attacking_item.iscoil() && malfunction && is_open)
else if(attacking_item.iscoil() && malfunction && panel_open)
var/obj/item/stack/cable_coil/coil = attacking_item
to_chat(user, SPAN_NOTICE("You begin to replace the wires."))
//if(do_after(user, min(60, round( ((maxhealth/health)*10)+(malfunction*10) ))) //Take longer to repair heavier damage
@@ -352,17 +357,25 @@
to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!"))
anchored = TRUE
else if(attacking_item.GetID())
if(src.allowed(user))
src.locked = !src.locked
to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]")
else
to_chat(user, SPAN_WARNING("Access denied."))
else
..()
/obj/machinery/shieldgen/AltClick(mob/user)
if(Adjacent(user))
add_fingerprint(user)
if(allowed(user))
locked = !locked
if(locked)
playsound(src, 'sound/machines/terminal/terminal_button03.ogg', 35, FALSE)
else
playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE)
balloon_alert(user, locked ? "locked" : "unlocked")
else
to_chat(user, SPAN_WARNING("Access denied."))
playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE)
balloon_alert(user, "access denied!")
return
/obj/machinery/shieldgen/update_icon()
if(active && !(stat & NOPOWER))
+16 -5
View File
@@ -23,7 +23,12 @@
///How much power is drawn from powernet. Increase this to allow the generator to sustain longer shields, at the cost of more power draw.
var/power_draw = 30 KILO WATTS
var/max_stored_power = 50 KILO WATTS
use_power = POWER_USE_OFF //Draws directly from power net. Does not use APC power.
/// Draws directly from power net. Does not use APC power.
use_power = POWER_USE_OFF
/obj/machinery/shieldwallgen/mechanics_hints(mob/user, distance, is_adjacent)
. += ..()
. += "ALT-click the [src] to lock or unlock it (if you have the appropriate ID access)."
/obj/machinery/shieldwallgen/update_icon()
if(power_state >= POWER_STARTING)
@@ -156,17 +161,23 @@
var/self_msg = wrenched ? "You secure the external reinforcing bolts to the floor." : "You unsecure the external reinforcing bolts."
user.visible_message(others_msg, SPAN_NOTICE(self_msg), SPAN_NOTICE("You hear a ratcheting noise."))
return
return ..()
if(attacking_item.GetID())
/obj/machinery/shieldwallgen/AltClick(mob/user)
if(Adjacent(user))
add_fingerprint(user)
if(allowed(user))
locked = !locked
var/msg = "The controls are now [locked ? "locked" : "unlocked"]."
to_chat(user, SPAN_NOTICE(msg))
if(locked)
playsound(src, 'sound/machines/terminal/terminal_button03.ogg', 35, FALSE)
else
playsound(src, 'sound/machines/terminal/terminal_button01.ogg', 35, FALSE)
balloon_alert(user, locked ? "locked" : "unlocked")
else
to_chat(user, SPAN_WARNING("Access denied."))
playsound(src, 'sound/machines/terminal/terminal_error.ogg', 25, FALSE)
balloon_alert(user, "access denied!")
return
return ..()
/obj/machinery/shieldwallgen/proc/alldir_cleanup()
for(var/dir in list(NORTH, SOUTH, EAST, WEST))