Files
Aurora.3/code/modules/shieldgen/emergency_shield.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

386 lines
11 KiB
Plaintext

/obj/machinery/shield
name = "emergency energy shield"
desc = "An energy shield used to contain hull breaches."
icon = 'icons/effects/effects.dmi'
icon_state = "shield-old"
density = TRUE
opacity = FALSE
anchored = TRUE
unacidable = TRUE
atmos_canpass = CANPASS_NEVER
var/health = 75 //The shield can only take so much beating (prevents perma-prisons)
var/shield_generate_power = 2500 //how much power we use when regenerating
var/shield_idle_power = 500 //how much power we use when just being sustained.
/obj/machinery/shield/malfai
name = "emergency forcefield"
desc = "A forcefield which seems to be projected by the station's emergency atmosphere containment field."
health = 100
/obj/machinery/shield/malfai/New()
..()
desc = "A forcefield which seems to be projected by the [station_name(TRUE)]'s emergency atmosphere containment field."
/obj/machinery/shield/malfai/process()
health -= 0.5 // Slowly lose integrity over time
check_failure()
/obj/machinery/shield/proc/check_failure()
var/health_percentage = (health / initial(health)) * 100
switch(health_percentage)
if(-INFINITY to 25)
if(alpha != 150)
animate(src, 1 SECOND, alpha = 150)
if(26 to 50)
if(alpha != 175)
animate(src, 1 SECOND, alpha = 175)
if(51 to 75)
if(alpha != 210)
animate(src, 1 SECOND, alpha = 210)
if(76 to 90)
if(alpha != 230)
animate(src, 1 SECOND, alpha = 230)
if(91 to INFINITY)
if(alpha != initial(alpha))
animate(src, 1 SECOND, alpha = initial(alpha))
if(health <= 0)
visible_message(SPAN_NOTICE("\The [src] dissipates!"))
qdel(src)
return
/obj/machinery/shield/New()
src.set_dir(pick(1,2,3,4))
..()
update_nearby_tiles(need_rebuild=1)
/obj/machinery/shield/Destroy()
opacity = FALSE
density = FALSE
update_nearby_tiles()
return ..()
/obj/machinery/shield/CanPass(atom/movable/mover, turf/target, height, air_group)
if(mover?.movement_type & PHASING)
return TRUE
if(!height || air_group)
return FALSE
else
return ..()
/obj/machinery/shield/attackby(obj/item/attacking_item, mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(src, attacking_item)
//Calculate damage
var/aforce = attacking_item.force
if(attacking_item.damtype == DAMAGE_BRUTE || attacking_item.damtype == DAMAGE_BURN)
health -= aforce
//Play a fitting sound
playsound(src.loc, 'sound/effects/EMPulse.ogg', 75, 1)
check_failure()
..()
/obj/machinery/shield/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit)
. = ..()
if(. != BULLET_ACT_HIT)
return .
health -= hitting_projectile.get_structure_damage()
check_failure()
opacity = 1
spawn(20) if(src) opacity = FALSE
/obj/machinery/shield/ex_act(severity)
switch(severity)
if(1.0)
if (prob(75))
qdel(src)
if(2.0)
if (prob(50))
qdel(src)
if(3.0)
if (prob(25))
qdel(src)
return
/obj/machinery/shield/emp_act(severity)
. = ..()
switch(severity)
if(EMP_HEAVY)
qdel(src)
if(EMP_LIGHT)
if(prob(50))
qdel(src)
/obj/machinery/shield/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
//Let everyone know we've been hit!
visible_message(SPAN_NOTICE("<B>\[src] was hit by [hitting_atom].</B>"))
//Super realistic, resource-intensive, real-time damage calculations.
var/tforce = 0
if(ismob(hitting_atom))
tforce = 40
else if(isobj(hitting_atom))
var/obj/O = hitting_atom
tforce = O.throwforce
src.health -= tforce
//This seemed to be the best sound for hitting a force field.
playsound(src.loc, 'sound/effects/EMPulse.ogg', 100, 1)
check_failure()
//The shield becomes dense to absorb the blow.. purely asthetic.
opacity = TRUE
spawn(20) if(src) opacity = FALSE
..()
return
/obj/machinery/shieldgen
name = "emergency shield projector"
desc = "Used to seal minor hull breaches."
icon = 'icons/obj/machinery/shielding.dmi'
icon_state = "shieldoff"
density = TRUE
opacity = FALSE
anchored = FALSE
req_access = list(ACCESS_ENGINE)
var/health = 100
var/active = FALSE
// Malfunction causes parts of the shield to slowly dissapate
var/malfunction = FALSE
var/list/deployed_shields = list()
var/list/regenerating = list()
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 ..()
/obj/machinery/shieldgen/proc/shields_up()
if(active) return FALSE //If it's already turned on, how did this get called?
src.active = 1
update_icon()
create_shields()
var/shield_power_usage
for(var/obj/machinery/shield/shield_tile in deployed_shields)
shield_power_usage += shield_tile.shield_idle_power
change_power_consumption(shield_power_usage, POWER_USE_IDLE)
update_use_power(POWER_USE_IDLE)
/obj/machinery/shieldgen/proc/shields_down()
if(!active) return FALSE //If it's already off, how did this get called?
src.active = FALSE
update_icon()
collapse_shields()
update_use_power(POWER_USE_OFF)
/obj/machinery/shieldgen/proc/create_shields()
for(var/T in RANGE_TURFS(2, src))
var/turf/target_tile = T
if(locate(/obj/machinery/shield) in target_tile)
continue
var/obj/item/tape/engineering/E = locate() in target_tile
if(E?.shield_marker)
deploy_shield(target_tile)
else if(istype(target_tile,/turf/space) || istype(target_tile,/turf/simulated/open) || istype(target_tile,/turf/simulated/floor/exoplanet/asteroid/ash) || istype(target_tile,/turf/simulated/floor/airless))
if(malfunction && prob(33) || !malfunction)
deploy_shield(target_tile)
/obj/machinery/shieldgen/proc/deploy_shield(var/turf/T)
var/obj/machinery/shield/S = new /obj/machinery/shield(T)
deployed_shields += S
use_power_oneoff(S.shield_generate_power)
/obj/machinery/shieldgen/proc/collapse_shields()
for(var/obj/machinery/shield/shield_tile in deployed_shields)
qdel(shield_tile)
/obj/machinery/shieldgen/power_change()
..()
if(!active) return
if (stat & NOPOWER)
collapse_shields()
else
create_shields()
update_icon()
/obj/machinery/shieldgen/process()
if (!active || (stat & NOPOWER))
return
if(malfunction)
if(deployed_shields.len && prob(5))
qdel(pick(deployed_shields))
else
if (check_delay <= 0)
create_shields()
var/new_power_usage = 0
for(var/obj/machinery/shield/shield_tile in deployed_shields)
new_power_usage += shield_tile.shield_idle_power
if (new_power_usage != idle_power_usage)
change_power_consumption(new_power_usage, POWER_USE_IDLE)
use_power_oneoff(0)
check_delay = 60
else
check_delay--
/obj/machinery/shieldgen/proc/checkhp()
if(health <= 30)
src.malfunction = 1
if(health <= 0)
spawn(0)
explosion(get_turf(src.loc), 0, 0, 1, 0, 0, 0)
qdel(src)
update_icon()
return
/obj/machinery/shieldgen/ex_act(severity)
switch(severity)
if(1.0)
src.health -= 75
src.checkhp()
if(2.0)
src.health -= 30
if (prob(15))
src.malfunction = 1
src.checkhp()
if(3.0)
src.health -= 10
src.checkhp()
return
/obj/machinery/shieldgen/emp_act(severity)
. = ..()
switch(severity)
if(EMP_HEAVY)
src.health /= 2 //cut health in half
malfunction = TRUE
locked = pick(0,1)
if(EMP_LIGHT)
if(prob(50))
src.health *= 0.3 //chop off a third of the health
malfunction = TRUE
checkhp()
/obj/machinery/shieldgen/attack_hand(mob/user)
if(locked)
to_chat(user, SPAN_WARNING("The machine is locked!"))
return
if(panel_open)
to_chat(user, SPAN_WARNING("The panel must be closed before operating this machine."))
return
if (src.active)
user.visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] [user] deactivates the shield generator."), \
SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] You deactivate the shield generator."), \
"You hear heavy droning fade out.")
src.shields_down()
else
if(anchored)
user.visible_message(SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] [user] activate the shield generator."), \
SPAN_NOTICE("[icon2html(src, viewers(get_turf(src)))] You activate the shield generator."), \
"You hear heavy droning.")
src.shields_up()
else
to_chat(user, SPAN_WARNING("The device must first be secured to the floor."))
return
/obj/machinery/shieldgen/emag_act(var/remaining_charges, var/mob/user)
if(!malfunction)
malfunction = TRUE
update_icon()
return 1
/obj/machinery/shieldgen/attackby(obj/item/attacking_item, mob/user)
if(attacking_item.isscrewdriver())
attacking_item.play_tool_sound(get_turf(src), 50)
if(panel_open)
to_chat(user, SPAN_NOTICE("You close the panel."))
panel_open = FALSE
else
to_chat(user, SPAN_NOTICE("You open the panel and expose the wiring."))
panel_open = TRUE
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
if(attacking_item.use_tool(src, user, 30, volume = 50))
if (coil.use(1))
health = initial(health)
malfunction = FALSE
to_chat(user, SPAN_NOTICE("You repair the [src]!"))
update_icon()
else if(attacking_item.iswrench())
if(locked)
to_chat(user, "The bolts are covered, unlocking this would retract the covers.")
return
if(anchored)
attacking_item.play_tool_sound(get_turf(src), 100)
to_chat(user, SPAN_NOTICE("You unsecure the [src] from the floor!"))
if(active)
to_chat(user, SPAN_NOTICE("The [src] shuts off!"))
src.shields_down()
anchored = FALSE
else
if(istype(get_turf(src), /turf/space)) return //No wrenching these in space!
attacking_item.play_tool_sound(get_turf(src), 100)
to_chat(user, SPAN_NOTICE("You secure the [src] to the floor!"))
anchored = TRUE
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))
src.icon_state = malfunction ? "shieldonbr":"shieldon"
else
src.icon_state = malfunction ? "shieldoffbr":"shieldoff"
return