Files
Aurora.3/code/modules/shieldgen/shieldwallgen.dm
T
BatrachophrenoandGitHub f06bdc8b3c 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.
2025-10-03 15:56:32 +00:00

327 lines
9.4 KiB
Plaintext

#define POWER_INACTIVE 0
#define POWER_STARTING 1
#define POWER_ACTIVE 2
/obj/machinery/shieldwallgen
name = "shield generator"
desc = "A portable shield generator, capable of casting a shield to another powered generator in range."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "Shield_Gen"
anchored = FALSE
density = TRUE
req_access = list(ACCESS_ENGINE_EQUIP)
var/power_state = FALSE
var/is_powered = FALSE
var/wrenched = FALSE
var/locked = TRUE
var/storedpower = 0
obj_flags = OBJ_FLAG_CONDUCTABLE
//There have to be at least two posts, so these are effectively doubled
///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
/// 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)
icon_state = "Shield_Gen +a"
else
icon_state = "Shield_Gen"
/obj/machinery/shieldwallgen/attack_hand(mob/user)
if(!wrenched)
to_chat(user, SPAN_WARNING("The shield generator needs to be firmly secured to the floor first."))
return TRUE
if(locked && !issilicon(user))
to_chat(user, SPAN_WARNING("The controls are locked!"))
return TRUE
if(!is_powered)
to_chat(user, SPAN_WARNING("The shield generator needs to be powered by wire underneath."))
return TRUE
if(power_state >= POWER_STARTING)
power_state = POWER_INACTIVE
user.visible_message("<b>[user]</b> turns \the [src] off.", SPAN_NOTICE("You turn off \the [src]."), SPAN_NOTICE("You hear a heavy droning fade out."))
alldir_cleanup()
else
power_state = POWER_STARTING
user.visible_message("<b>[user]</b> turns \the [src] on.", SPAN_NOTICE("You turn on \the [src]."), SPAN_NOTICE("You hear a heavy droning start up."))
update_icon()
add_fingerprint(user)
/obj/machinery/shieldwallgen/proc/power(seconds_per_tick = 1)
if(!anchored)
is_powered = FALSE
return FALSE
var/turf/T = loc
if(!istype(T))
is_powered = FALSE
return FALSE
var/obj/structure/cable/C = T.get_cable_node()
var/datum/powernet/PN
if(C)
PN = C.powernet // find the powernet of the connected cable
if(!PN)
is_powered = FALSE
return FALSE
var/shieldload = between(500, max_stored_power - storedpower, (power_draw*seconds_per_tick)) //what we try to draw
shieldload = PN.draw_power(shieldload) //what we actually get
storedpower += shieldload
//If we're still in the red, then there must not be enough available power to cover our load.
if(storedpower <= 0)
is_powered = FALSE
return FALSE
is_powered = TRUE // IVE GOT THE POWER!
return TRUE
/obj/machinery/shieldwallgen/process(seconds_per_tick)
power(seconds_per_tick)
if(is_powered)
storedpower -= (2500 * seconds_per_tick)
storedpower = clamp(storedpower, 0, max_stored_power)
if(power_state == POWER_STARTING)
if(!wrenched)
power_state = POWER_INACTIVE
return
addtimer(CALLBACK(src, PROC_REF(setup_field), 1), 1)
addtimer(CALLBACK(src, PROC_REF(setup_field), 2), 2)
addtimer(CALLBACK(src, PROC_REF(setup_field), 3), 4)
addtimer(CALLBACK(src, PROC_REF(setup_field), 4), 8)
power_state = POWER_ACTIVE
if(power_state >= POWER_STARTING)
if(!is_powered)
visible_message(SPAN_WARNING("\The [src] shuts down due to a lack of power."), SPAN_NOTICE("You hear a heavy droning fade out."))
power_state = POWER_INACTIVE
update_icon()
alldir_cleanup()
/obj/machinery/shieldwallgen/proc/setup_field(var/NSEW)
var/turf/T = loc
var/turf/T2 = loc
var/obj/machinery/shieldwallgen/G
var/steps = 0
var/oNSEW = 0
if(!NSEW)//Make sure its ran right
return
oNSEW = REVERSE_DIR(NSEW)
for(var/dist = 0, dist <= 9, dist++) // checks out to 8 tiles away for another generator
T = get_step(T2, NSEW)
T2 = T
steps += 1
if(locate(/obj/machinery/shieldwallgen) in T)
G = (locate(/obj/machinery/shieldwallgen) in T)
steps -= 1
if(!G.power_state)
return
G.cleanup(oNSEW)
break
if(isnull(G))
return
T2 = loc
for(var/dist = 0, dist < steps, dist++) // creates each field tile
var/field_dir = get_dir(T2, get_step(T2, NSEW))
T = get_step(T2, NSEW)
T2 = T
var/obj/shieldwall/CF = new /obj/shieldwall/(T, src, G) //(ref to this gen, ref to connected gen)
CF.set_dir(field_dir)
/obj/machinery/shieldwallgen/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.iswrench())
if(power_state)
to_chat(user, SPAN_WARNING("You cannot unsecure \the [src] while it's active."))
return
wrenched = !wrenched
anchored = wrenched
attacking_item.play_tool_sound(get_turf(src), 75)
add_fingerprint(user)
var/others_msg = wrenched ? "<b>[user]</b> secures the external reinforcing bolts to the floor." : "<b>[user]</b> unsecures the external reinforcing bolts."
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 ..()
/obj/machinery/shieldwallgen/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/shieldwallgen/proc/alldir_cleanup()
for(var/dir in list(NORTH, SOUTH, EAST, WEST))
cleanup(dir)
/obj/machinery/shieldwallgen/proc/cleanup(var/NSEW)
var/obj/shieldwall/F
var/obj/machinery/shieldwallgen/G
var/turf/T = loc
var/turf/T2 = loc
for(var/dist = 0, dist <= 9, dist++) // checks out to 8 tiles away for fields
T = get_step(T2, NSEW)
T2 = T
if(locate(/obj/shieldwall) in T)
F = (locate(/obj/shieldwall) in T)
qdel(F)
if(locate(/obj/machinery/shieldwallgen) in T)
G = (locate(/obj/machinery/shieldwallgen) in T)
if(!G.power_state)
break
/obj/machinery/shieldwallgen/Destroy()
alldir_cleanup()
return ..()
/obj/machinery/shieldwallgen/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
if(. != BULLET_ACT_HIT)
return .
storedpower -= 400 * hitting_projectile.get_structure_damage()
if(power_state >= POWER_STARTING)
visible_message(SPAN_WARNING("\The [src]'s shielding sparks as \the [hitting_projectile] hits it!"))
/obj/shieldwall
name = "energy shield"
desc = "An energy shield."
icon = 'icons/effects/effects.dmi'
icon_state = "shieldwall"
anchored = TRUE
density = TRUE
unacidable = TRUE
light_range = 3
light_color = LIGHT_COLOR_BLUE
var/needs_power = FALSE
var/power_state = POWER_STARTING
var/delay = 5
var/last_active
var/mob/U
var/obj/machinery/shieldwallgen/gen_primary
var/obj/machinery/shieldwallgen/gen_secondary
var/power_usage = 2500 //how much power it takes to sustain the shield
var/generate_power_usage = 7500 //how much power it takes to start up the shield
/obj/shieldwall/Initialize(mapload, var/obj/machinery/shieldwallgen/A, var/obj/machinery/shieldwallgen/B)
. = ..()
update_nearby_tiles()
gen_primary = A
gen_secondary = B
if(A?.power_state && B?.power_state)
needs_power = TRUE
A.storedpower -= generate_power_usage / 2
B.storedpower -= generate_power_usage / 2
START_PROCESSING(SSprocessing, src)
else
qdel(src) //need at least two generator posts
/obj/shieldwall/Destroy()
update_nearby_tiles()
STOP_PROCESSING(SSprocessing, src)
return ..()
/obj/shieldwall/attack_hand(mob/user)
to_chat(user, SPAN_WARNING("\The [src] pushes you back as you try to touch it."))
/obj/shieldwall/process()
if(needs_power)
if(!gen_primary || !gen_secondary)
qdel(src)
return
if(!gen_primary.power_state || !gen_secondary.power_state)
qdel(src)
return
gen_primary.storedpower -= power_usage / 2
gen_secondary.storedpower -= power_usage / 2
/obj/shieldwall/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
if(. != BULLET_ACT_HIT)
return .
if(needs_power)
var/obj/machinery/shieldwallgen/G
if(prob(50))
G = gen_primary
else
G = gen_secondary
visible_message(SPAN_WARNING("\The [src] wobbles precariously as \the [hitting_projectile] impacts it!"))
G.storedpower -= 400 * hitting_projectile.get_structure_damage()
/obj/shieldwall/ex_act(severity)
if(needs_power)
var/obj/machinery/shieldwallgen/G
switch(severity)
if(1.0) //big boom
if(prob(50))
G = gen_primary
else
G = gen_secondary
G.storedpower -= 120000
if(2.0) //medium boom
if(prob(50))
G = gen_primary
else
G = gen_secondary
G.storedpower -= 30000
if(3.0) //lil boom
if(prob(50))
G = gen_primary
else
G = gen_secondary
G.storedpower -= 12000
/obj/shieldwall/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0))
return TRUE
if(mover?.movement_type & PHASING)
return TRUE
if(istype(mover) && mover.pass_flags & PASSGLASS)
return prob(20)
else
if(istype(mover, /obj/projectile))
return prob(10)
else
return !density
#undef POWER_INACTIVE
#undef POWER_STARTING
#undef POWER_ACTIVE