Upports fridge, tcomm, and shield_gen soundloops (#19405)

* Soundloops

* emp fix

* Update tools.dm

* Update tools.dm

* hum
This commit is contained in:
Cameron Lennox
2026-04-23 22:58:32 -04:00
committed by GitHub
parent a881fe3bd6
commit a5739a5a99
19 changed files with 214 additions and 9 deletions
+7 -3
View File
@@ -16,6 +16,7 @@
direct (bool) If true plays directly to provided atoms instead of from them
opacity_check (bool) If true, things behind walls/opaque things won't hear the sounds.
pref_check (type) If set to a /datum/client_preference type, will check if the hearer has that preference active before playing it to them.
volume_chan (type) If set to a specific volume channel via the incoming argument, we tell the playsound proc to modulate volume based on that channel
exclusive (bool) If true, only one of this sound is allowed to play. Relies on if started is true or not. If true, it will not start another loop until it is false.
*/
/datum/looping_sound
@@ -34,6 +35,7 @@
var/opacity_check
var/pref_check
var/exclusive
var/falloff
var/volume_chan
var/timerid
@@ -57,6 +59,8 @@
return ..()
/datum/looping_sound/proc/start(atom/add_thing, skip_start_sound = FALSE)
if(QDELETED(src))
return
if(add_thing)
output_atoms |= add_thing
if(timerid)
@@ -82,7 +86,7 @@
started = FALSE
/datum/looping_sound/proc/sound_loop(starttime)
if(max_loops && world.time >= starttime + mid_length * max_loops)
if(QDELETED(src) || (max_loops && world.time >= starttime + mid_length * max_loops))
stop()
return
if(!chance || prob(chance))
@@ -98,7 +102,7 @@
if(direct)
S.channel = SSsounds.random_available_channel()
S.volume = volume
for(var/i in 1 to atoms_cache.len)
for(var/i in 1 to atoms_cache?.len)
var/atom/thing = atoms_cache[i]
if(direct)
if(ismob(thing))
@@ -107,7 +111,7 @@
continue
SEND_SOUND(thing, S)
else
playsound(thing, S, volume, vary, extra_range, ignore_walls = !opacity_check, preference = pref_check)
playsound(thing, S, volume, vary, extra_range, falloff = falloff, ignore_walls = !opacity_check, preference = pref_check, volume_channel = volume_chan)
/datum/looping_sound/proc/get_sound(starttime, _mid_sounds)
if(!_mid_sounds)
@@ -118,6 +118,40 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/looping_sound/fridge
mid_sounds = list('sound/machines/kitchen/fridge/fridge_loop.ogg' = 1)
mid_length = 60
volume = 10
extra_range = -1 // Short-range
pref_check = /datum/preference/toggle/fridge_hum
volume_chan = VOLUME_CHANNEL_MACHINERY_IDLE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/looping_sound/tcomms
start_sound = 'sound/machines/tcomms/tcomms_pulse.ogg'
mid_sounds = list('sound/machines/tcomms/tcomms_01.ogg' = 1)
mid_length = 20
end_sound = 'sound/machines/tcomms/tcomms_pulse.ogg'
volume = 40
extra_range = -5 // Short-range
falloff = 0.1 // Harsh
volume_chan = VOLUME_CHANNEL_MACHINERY_IDLE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/looping_sound/shield_generator
start_sound = 'sound/machines/shield_hum/shield_generator_whir.ogg'
mid_sounds = list('sound/machines/shield_hum/shield_generator_hum2.ogg', 'sound/machines/shield_hum/shield_generator_hum3.ogg')
mid_length = 60
end_sound = 'sound/machines/shield_hum/shield_generator_whir.ogg'
volume = 40
volume_chan = VOLUME_CHANNEL_MACHINERY
exclusive = TRUE
extra_range = 10
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/looping_sound/lathe_print
mid_sounds = list('sound/machines/lathe/lathe_print.ogg')
mid_length = 2 SECONDS
+27
View File
@@ -17,16 +17,36 @@
var/list/logs = list() // Gets written to by exonet's send_message() function.
circuit = /obj/item/circuitboard/telecomms/exonet_node
var/datum/looping_sound/tcomms/soundloop
var/noisy = TRUE
// Proc: New()
// Parameters: None
// Description: Adds components to the machine for deconstruction.
/obj/machinery/exonet_node/Initialize(mapload)
soundloop = new(list(src), FALSE)
if(prob(60)) // 60% chance to change the midloop
if(prob(40))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_02.ogg' = 1)
soundloop.mid_length = 40
else if(prob(20))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_03.ogg' = 1)
soundloop.mid_length = 10
else
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_04.ogg' = 1)
soundloop.mid_length = 30
soundloop.start()
. = ..()
default_apply_parts()
if(mapload)
desc = "This machine is one of many, many nodes inside [using_map.starsys_name]'s section of the Exonet, connecting the [using_map.station_short] to the rest of the system, at least \
electronically."
/obj/machinery/exonet_node/Destroy()
QDEL_NULL(soundloop)
return ..()
// Proc: update_icon()
// Parameters: None
// Description: Self explanatory.
@@ -49,12 +69,19 @@
if(stat & (BROKEN|NOPOWER|EMPED))
on = 0
update_idle_power_usage(0)
soundloop.stop()
noisy = FALSE
else
on = 1
update_idle_power_usage(2500)
else
on = 0
update_idle_power_usage(0)
soundloop.stop()
noisy = FALSE
if(!noisy && on)
soundloop.start()
noisy = TRUE
update_icon()
// Proc: emp_act(severity, recursive)
+24 -1
View File
@@ -12,6 +12,9 @@
var/toggle = 1 // If we /should/ be active or not,
var/list/internal_PDAs = list() // Assoc list of PDAs inside of this, with the department name being the index,
var/datum/looping_sound/tcomms/soundloop
var/noisy = TRUE
/obj/machinery/pda_multicaster/Initialize(mapload)
. = ..()
internal_PDAs = list("command" = new /obj/item/pda/multicaster/command(src),
@@ -19,10 +22,23 @@
"engineering" = new /obj/item/pda/multicaster/engineering(src),
"medical" = new /obj/item/pda/multicaster/medical(src),
"research" = new /obj/item/pda/multicaster/research(src),
"exploration" = new /obj/item/pda/multicaster/exploration(src), //VOREStation Add,
"exploration" = new /obj/item/pda/multicaster/exploration(src),
"cargo" = new /obj/item/pda/multicaster/cargo(src),
"civilian" = new /obj/item/pda/multicaster/civilian(src))
soundloop = new(list(src), FALSE)
if(prob(60)) // 60% chance to change the midloop
if(prob(40))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_02.ogg' = 1)
soundloop.mid_length = 40
else if(prob(20))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_03.ogg' = 1)
soundloop.mid_length = 10
else
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_04.ogg' = 1)
soundloop.mid_length = 30
soundloop.start() // Have to do this here bc it starts on
/obj/machinery/pda_multicaster/prebuilt/Initialize(mapload)
. = ..()
default_apply_parts()
@@ -30,6 +46,7 @@
/obj/machinery/pda_multicaster/Destroy()
for(var/atom/movable/AM in contents)
qdel(AM)
QDEL_NULL(soundloop)
. = ..()
/obj/machinery/pda_multicaster/update_icon()
@@ -73,14 +90,20 @@
on = 0
update_PDAs(1) // 1 being to turn off.
update_idle_power_usage(0)
soundloop.stop()
noisy = FALSE
else
on = 1
update_PDAs(0)
update_idle_power_usage(750)
soundloop.start()
noisy = TRUE
else
on = 0
update_PDAs(1)
update_idle_power_usage(0)
soundloop.stop()
noisy = FALSE
update_icon()
/obj/machinery/pda_multicaster/process()
@@ -36,6 +36,8 @@
var/hide = 0 // Is it a hidden machine?
var/listening_level = 0 // 0 = auto set in New() - this is the z level that the machine is listening to.
var/datum/looping_sound/tcomms/soundloop
var/noisy = TRUE
/obj/machinery/telecomms/proc/relay_information(datum/signal/signal, filter, copysig, amount = 20)
// relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending
@@ -139,12 +141,25 @@
else
for(var/obj/machinery/telecomms/T in GLOB.telecomms_list)
add_link(T)
soundloop = new(list(src), FALSE)
if(prob(60)) // 60% chance to change the midloop
if(prob(40))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_02.ogg' = 1)
soundloop.mid_length = 40
else if(prob(20))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_03.ogg' = 1)
soundloop.mid_length = 10
else
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_04.ogg' = 1)
soundloop.mid_length = 30
soundloop.start()
/obj/machinery/telecomms/Destroy()
GLOB.telecomms_list -= src
for(var/obj/machinery/telecomms/comm in GLOB.telecomms_list)
comm.links -= src
links = list()
QDEL_NULL(soundloop)
. = ..()
// Used in auto linking
@@ -167,11 +182,18 @@
if(toggled)
if(stat & (BROKEN|NOPOWER|EMPED) || integrity <= 0) // if powered, on. if not powered, off. if too damaged, off
on = 0
on = FALSE
soundloop.stop()
noisy = FALSE
else
on = 1
on = TRUE
else
on = 0
on = FALSE
soundloop.stop()
noisy = FALSE
if(!noisy)
soundloop.start()
noisy = TRUE
/obj/machinery/telecomms/process()
update_power()
@@ -192,6 +214,7 @@
if(prob(100/severity))
if(!(stat & EMPED))
stat |= EMPED
playsound(src, 'sound/machines/tcomms/tcomms_pulse.ogg', 70, 1, 30)
var/duration = (300 * 10)/severity
spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot.
stat &= ~EMPED
@@ -269,3 +269,8 @@
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "sound_instruments"
savefile_identifier = PREFERENCE_PLAYER
/datum/preference/toggle/fridge_hum
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "SOUND_FRIDGEHUM"
savefile_identifier = PREFERENCE_PLAYER
@@ -24,6 +24,9 @@
var/persistent = null // Path of persistence datum used to track contents
circuit = /obj/item/circuitboard/smartfridge //This one is meant to be uncraftable, however.
var/datum/looping_sound/fridge/soundloop
var/playing_sound = FALSE
/obj/machinery/smartfridge/secure
is_secure = 1
@@ -35,6 +38,8 @@
set_wires(new /datum/wires/smartfridge/secure(src))
else
set_wires(new /datum/wires/smartfridge(src))
soundloop = new(list(src), FALSE)
update_icon()
default_apply_parts()
@@ -45,6 +50,7 @@
wires = null
if(persistent)
SSpersistence.forget_value(src, persistent)
QDEL_NULL(soundloop)
return ..()
/obj/machinery/smartfridge/proc/accept_check(obj/item/O)
@@ -52,7 +58,12 @@
/obj/machinery/smartfridge/process()
if(stat & (BROKEN|NOPOWER))
soundloop.stop()
playing_sound = FALSE
return
if(!playing_sound && !stat)
soundloop.start()
playing_sound = TRUE
if(src.seconds_electrified > 0)
src.seconds_electrified--
if(src.shoot_inventory && prob(2))
@@ -63,6 +74,12 @@
..()
if(old_stat != stat)
update_icon()
if(stat & (NOPOWER | BROKEN))
soundloop.stop()
playing_sound = FALSE
else
soundloop.start()
playing_sound = TRUE
/obj/machinery/smartfridge/update_icon()
cut_overlays()
@@ -262,9 +262,18 @@
/obj/item/integrated_electronics,
/obj/item/tool/crowbar,
/obj/item/tool/screwdriver,
/obj/item/multitool
/obj/item/multitool,
/obj/item/integrated_electronics/wirer,
/obj/item/integrated_electronics/debugger,
/obj/item/integrated_electronics/detailer,
)
//Emp'ing this one bag causes a recursion loop of over 700 emp_act's,
//Which is enough to trigger byond's recursion level protection
/obj/item/storage/bag/circuits/basic/Initialize(mapload)
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF)
. = ..()
/obj/item/storage/bag/circuits/basic/Initialize(mapload)
new /obj/item/storage/bag/circuits/mini/arithmetic(src)
new /obj/item/storage/bag/circuits/mini/trig(src)
@@ -283,6 +292,9 @@
new /obj/item/multitool(src)
new /obj/item/tool/screwdriver(src)
new /obj/item/tool/crowbar(src)
new /obj/item/integrated_electronics/wirer(src)
new /obj/item/integrated_electronics/debugger(src)
new /obj/item/integrated_electronics/detailer(src)
make_exact_fit()
. = ..()
@@ -307,6 +319,7 @@
new /obj/item/electronic_assembly/drone(src)
new /obj/item/integrated_electronics/wirer(src)
new /obj/item/integrated_electronics/debugger(src)
new /obj/item/integrated_electronics/detailer(src)
new /obj/item/tool/crowbar(src)
make_exact_fit()
. = ..()
@@ -19,6 +19,9 @@
var/dos_capacity = 500 // Amount of DoS "packets" in buffer required to crash the relay
var/dos_dissipate = 1 // Amount of DoS "packets" dissipated over time.
var/datum/looping_sound/tcomms/soundloop
var/noisy = TRUE
// TODO: Implement more logic here. For now it's only a placeholder.
/obj/machinery/ntnet_relay/operable()
@@ -33,8 +36,13 @@
/obj/machinery/ntnet_relay/update_icon()
if(operable())
icon_state = initial(icon_state)
if(!noisy)
soundloop.start()
noisy = TRUE
else
icon_state = "[initial(icon_state)]_off"
soundloop.stop()
noisy = FALSE
/obj/machinery/ntnet_relay/process()
if(operable())
@@ -106,6 +114,18 @@
GLOB.ntnet_global.relays.Add(src)
NTNet = GLOB.ntnet_global
GLOB.ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]")
soundloop = new(list(src), FALSE)
if(prob(60)) // 60% chance to change the midloop
if(prob(40))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_02.ogg' = 1)
soundloop.mid_length = 40
else if(prob(20))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_03.ogg' = 1)
soundloop.mid_length = 10
else
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_04.ogg' = 1)
soundloop.mid_length = 30
soundloop.start() // Have to do this here bc it starts on
/obj/machinery/ntnet_relay/Destroy()
if(GLOB.ntnet_global)
@@ -115,6 +135,7 @@
for(var/datum/computer_file/program/ntnet_dos/D in dos_sources)
D.target = null
D.error = "Connection to quantum relay severed"
QDEL_NULL(soundloop)
. = ..()
/obj/machinery/ntnet_relay/attackby(var/obj/item/W as obj, var/mob/user as mob)
+21 -1
View File
@@ -69,7 +69,21 @@
//Messages having theese tokens will be rejected by server. Case sensitive
var/spamfilter_limit = MESSAGE_SERVER_DEFAULT_SPAM_LIMIT //Maximal amount of tokens
var/datum/looping_sound/tcomms/soundloop
var/noisy = FALSE
/obj/machinery/message_server/Initialize(mapload)
soundloop = new(list(src), FALSE)
if(prob(60)) // 60% chance to change the midloop
if(prob(40))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_02.ogg' = 1)
soundloop.mid_length = 40
else if(prob(20))
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_03.ogg' = 1)
soundloop.mid_length = 10
else
soundloop.mid_sounds = list('sound/machines/tcomms/tcomms_04.ogg' = 1)
soundloop.mid_length = 30
. = ..()
GLOB.message_servers += src
decryptkey = GenerateKey()
@@ -77,6 +91,7 @@
/obj/machinery/message_server/Destroy()
GLOB.message_servers -= src
QDEL_NULL(soundloop)
return ..()
/obj/machinery/message_server/examine(mob/user, distance, infix, suffix)
@@ -96,7 +111,12 @@
// decryptkey = generateKey()
if(active && (stat & (BROKEN|NOPOWER)))
active = 0
soundloop.stop()
noisy = FALSE
return
if(!noisy && active)
soundloop.start()
noisy = TRUE
update_icon()
return
@@ -366,7 +386,7 @@ GLOBAL_DATUM(blackbox, /obj/machinery/blackbox_recorder)
query_insert.Execute()
qdel(query_insert)
// Sanitize inputs to avoid SQL injection attacks
// Sanitize inputs to avoid SQL injection attacks. This is not secure. Basic filters like this are pretty easy to bypass. Use the format for arguments used in the above.
/proc/sql_sanitize_text(var/text)
text = replacetext(text, "'", "''")
text = replacetext(text, ";", "")
+18
View File
@@ -25,6 +25,7 @@
use_power = USE_POWER_OFF //doesn't use APC power
interact_offline = TRUE // don't check stat & NOPOWER|BROKEN for our UI. We check BROKEN ourselves.
var/id //for button usage
var/datum/looping_sound/shield_generator/shield_hum
/obj/machinery/shield_gen/advanced
name = "advanced bubble shield generator"
@@ -41,11 +42,13 @@
if(get_dir(cap, src) == cap.dir)
capacitors |= cap
cap.owned_gen = src
shield_hum = new(list(src), FALSE)
. = ..()
AddElement(/datum/element/climbable)
/obj/machinery/shield_gen/Destroy()
QDEL_LIST_NULL(field)
QDEL_NULL(shield_hum)
return ..()
/obj/machinery/shield_gen/emag_act(var/remaining_charges, var/mob/user)
@@ -243,6 +246,7 @@
to_chat(M, "[icon2html(src, M.client)] You hear heavy droning start up.")
for(var/obj/effect/energy_field/E in field) // Update the icons here to ensure all the shields have been made already.
E.update_icon()
shield_hum.start()
else
for(var/obj/effect/energy_field/D in field)
field.Remove(D)
@@ -251,11 +255,25 @@
for(var/mob/M in view(5,src))
to_chat(M, "[icon2html(src, M.client)] You hear heavy droning fade out.")
shield_hum.stop()
/obj/machinery/shield_gen/proc/fill_diffused()
if(active)
var/list/covered_turfs = get_shielded_turfs()
var/turf/T = get_turf(src)
var/obj/effect/energy_field/E
if(T in covered_turfs)
covered_turfs.Remove(T)
for(var/turf/O in covered_turfs)
if(locate(/obj/effect/energy_field, O) || locate(/obj/machinery/pointdefense, orange(2, O)))
continue
E = new(O, src)
/obj/machinery/shield_gen/update_icon()
if(stat & BROKEN)
icon_state = "broke"
set_light(0)
shield_hum.stop()
else
if (src.active)
icon_state = "generator1"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.