Merge pull request #15634 from deathride58/jukeboxrefresh

Jukebox refresh - Jukebox queue, working volume knob, emagging, preliminary multiz support, and more!
This commit is contained in:
Lin
2022-06-01 21:18:02 +00:00
committed by GitHub
20 changed files with 391 additions and 175 deletions
+2 -1
View File
@@ -54656,6 +54656,7 @@
/obj/structure/sign/poster/contraband/robust_softdrinks{
pixel_y = 32
},
/obj/machinery/vending/snack/orange,
/turf/open/floor/wood,
/area/service/bar)
"fZm" = (
@@ -57444,10 +57445,10 @@
/turf/open/floor/plasteel/dark,
/area/security/prison/cells)
"kgB" = (
/obj/machinery/vending/snack/orange,
/obj/machinery/light{
dir = 1
},
/obj/machinery/jukebox,
/turf/open/floor/wood,
/area/service/bar)
"khb" = (
+1
View File
@@ -5011,6 +5011,7 @@
dir = 1
},
/obj/machinery/light,
/obj/machinery/jukebox,
/turf/open/floor/plasteel,
/area/service/bar)
"nb" = (
+9 -1
View File
@@ -65631,6 +65631,14 @@
},
/turf/open/floor/plasteel/dark,
/area/service/chapel/main)
"mWS" = (
/obj/effect/turf_decal/tile/bar,
/obj/effect/turf_decal/tile/bar{
dir = 1
},
/obj/machinery/jukebox,
/turf/open/floor/plasteel,
/area/service/bar)
"mXi" = (
/turf/open/floor/wood,
/area/command/heads_quarters/captain/private)
@@ -119802,7 +119810,7 @@ teZ
teZ
wbZ
hCA
msI
mWS
pzY
kPZ
upO
@@ -13604,14 +13604,7 @@
/turf/open/floor/plasteel,
/area/service/bar/atrium)
"ayN" = (
/obj/item/storage/fancy/cigarettes/cigars{
pixel_y = 6
},
/obj/item/storage/fancy/cigarettes/cigars/cohiba{
pixel_y = 3
},
/obj/item/storage/fancy/cigarettes/cigars/havana,
/obj/structure/table/reinforced,
/obj/machinery/jukebox,
/turf/open/floor/plasteel/dark/corner{
dir = 4
},
@@ -13622,6 +13615,13 @@
pixel_x = 32
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/item/storage/fancy/cigarettes/cigars/havana,
/obj/item/storage/fancy/cigarettes/cigars/cohiba{
pixel_y = 3
},
/obj/item/storage/fancy/cigarettes/cigars{
pixel_y = 6
},
/turf/open/floor/plasteel/dark/side{
dir = 1
},
@@ -84771,7 +84771,7 @@ awp
awX
axR
ayN
aue
asg
blj
aCb
aCe
+2 -1
View File
@@ -42,6 +42,7 @@
#define LOG_MECHA (1 << 17)
#define LOG_SHUTTLE (1 << 18)
#define LOG_VICTIM (1 << 19)
#define LOG_ECON (1 << 20)
//Individual logging panel pages
#define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK | LOG_VICTIM)
@@ -50,7 +51,7 @@
#define INDIVIDUAL_COMMS_LOG (LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS)
#define INDIVIDUAL_OOC_LOG (LOG_OOC | LOG_ADMIN)
#define INDIVIDUAL_OWNERSHIP_LOG (LOG_OWNERSHIP)
#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_VICTIM)
#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_VICTIM | LOG_ECON)
#define LOGSRC_CLIENT "Client"
#define LOGSRC_MOB "Mob"
+4
View File
@@ -161,6 +161,10 @@
if (CONFIG_GET(flag/log_telecomms))
WRITE_LOG(GLOB.world_telecomms_log, "TCOMMS: [text]")
/proc/log_econ(text)
if (CONFIG_GET(flag/log_econ))
WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]")
/proc/log_chat(text)
if (CONFIG_GET(flag/log_pda))
//same thing here
+1 -1
View File
@@ -548,7 +548,7 @@
parts += "There were [station_vault] credits collected by crew this shift.<br>"
if(total_players > 0)
parts += "An average of [station_vault/total_players] credits were collected.<br>"
// log_econ("Roundend credit total: [station_vault] credits. Average Credits: [station_vault/total_players]")
log_econ("Roundend credit total: [station_vault] credits. Average Credits: [station_vault/total_players]")
if(mr_moneybags)
parts += "The most affluent crew member at shift end was <b>[mr_moneybags.account_holder] with [mr_moneybags.account_balance]</b> cr!</div>"
else
+2
View File
@@ -49,6 +49,8 @@ GLOBAL_VAR(tgui_log)
GLOBAL_PROTECT(tgui_log)
GLOBAL_VAR(world_shuttle_log)
GLOBAL_PROTECT(world_shuttle_log)
GLOBAL_VAR(world_econ_log)
GLOBAL_PROTECT(world_econ_log)
GLOBAL_VAR(perf_log)
GLOBAL_PROTECT(perf_log)
@@ -79,6 +79,10 @@
/datum/config_entry/flag/log_telecomms
config_entry_value = TRUE
/// log economy
/datum/config_entry/flag/log_econ
config_entry_value = TRUE
/// log certain expliotable parrots and other such fun things in a JSON file of twitter valid phrases.
/datum/config_entry/flag/log_twitter
config_entry_value = TRUE
+90 -23
View File
@@ -1,3 +1,19 @@
//As a brief warning to all those who dare tread upon these grounds:
//The bulk of this code here was written years ago, back in the days of 512.
//We were incredibly drunk back then. And nowadays, we've found that being drunk is a hard requirement for working with this code.
//So if you're here to make changes? Brandish a glass. There are many sins here, but it's exactly as engineered as it needs to be.
//We physically won't be able to tell you what half of this code does. The only thing that'll help you here is the ballmer peak.
//Bottoms up, friend. And be sure to drink responsibly. Be sure to fetch some water, too; it eases the hangover. - Bhijn & Myr
// Jukelist indices
#define JUKE_TRACK 1
#define JUKE_CHANNEL 2
#define JUKE_BOX 3
#define JUKE_FALLOFF 4
#define JUKE_SOUND 5
SUBSYSTEM_DEF(jukeboxes)
name = "Jukeboxes"
wait = 5
@@ -25,27 +41,41 @@ SUBSYSTEM_DEF(jukeboxes)
var/channeltoreserve = pick(freejukeboxchannels)
if(!channeltoreserve)
return FALSE
var/sound/song_to_init = sound(T.song_path)
freejukeboxchannels -= channeltoreserve
var/list/youvegotafreejukebox = list(T, channeltoreserve, jukebox, jukefalloff)
var/list/youvegotafreejukebox = list(T, channeltoreserve, jukebox, jukefalloff, song_to_init)
song_to_init.status = SOUND_MUTE
song_to_init.environment = 7
song_to_init.channel = channeltoreserve
song_to_init.volume = 1
song_to_init.falloff = jukefalloff
song_to_init.echo = list(0, null, -10000, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, null, null)
activejukeboxes.len++
activejukeboxes[activejukeboxes.len] = youvegotafreejukebox
//Due to changes in later versions of 512, SOUND_UPDATE no longer properly plays audio when a file is defined in the sound datum. As such, we are now required to init the audio before we can actually do anything with it.
//Downsides to this? This means that you can *only* hear the jukebox audio if you were present on the server when it started playing, and it means that it's now impossible to add loops to the jukebox track list.
var/sound/song_to_init = sound(T.song_path)
song_to_init.status = SOUND_MUTE
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS))
continue
M.playsound_local(M, null, 100, channel = youvegotafreejukebox[2], S = song_to_init)
SEND_SOUND(M, song_to_init)
return activejukeboxes.len
//Updates jukebox by transferring to different object or modifying falloff.
/datum/controller/subsystem/jukeboxes/proc/updatejukebox(IDtoupdate, obj/jukebox, jukefalloff)
if(islist(activejukeboxes[IDtoupdate]))
if(istype(jukebox))
activejukeboxes[IDtoupdate][JUKE_BOX] = jukebox
if(!isnull(jukefalloff))
activejukeboxes[IDtoupdate][JUKE_FALLOFF] = jukefalloff
/datum/controller/subsystem/jukeboxes/proc/removejukebox(IDtoremove)
if(islist(activejukeboxes[IDtoremove]))
var/jukechannel = activejukeboxes[IDtoremove][2]
var/jukechannel = activejukeboxes[IDtoremove][JUKE_CHANNEL]
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
@@ -85,37 +115,74 @@ SUBSYSTEM_DEF(jukeboxes)
if(!jukeinfo.len)
stack_trace("Active jukebox without any associated metadata.")
continue
var/datum/track/juketrack = jukeinfo[1]
var/datum/track/juketrack = jukeinfo[JUKE_TRACK]
if(!istype(juketrack))
stack_trace("Invalid jukebox track datum.")
continue
var/obj/jukebox = jukeinfo[3]
var/obj/jukebox = jukeinfo[JUKE_BOX]
if(!istype(jukebox))
stack_trace("Nonexistant or invalid object associated with jukebox.")
continue
var/sound/song_played = sound(juketrack.song_path)
var/list/audible_zlevels = get_multiz_accessible_levels(jukebox.z) //TODO - for multiz refresh, this should use the cached zlevel connections var in SSMapping. For now this is fine!
var/sound/song_played = jukeinfo[JUKE_SOUND]
var/turf/currentturf = get_turf(jukebox)
var/area/currentarea = get_area(jukebox)
var/list/hearerscache = hearers(7, jukebox)
var/targetfalloff = jukeinfo[JUKE_FALLOFF]
var/mixes = ((targetfalloff*250)-750)
var/inrange
var/pressure_factor
song_played.falloff = jukeinfo[4]
var/datum/gas_mixture/source_env = (istype(currentturf) ? currentturf.return_air() : null)
var/datum/gas_mixture/hearer_env //We init this var outside of the mob loop for the sake of performance
var/turf/hearerturf //ditto
var/source_pressure = (istype(source_env) ? source_env.return_pressure() : 0)
song_played.falloff = targetfalloff
song_played.volume = min((targetfalloff * 50), 100)
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS) || !M.can_hear())
M.stop_sound_channel(jukeinfo[2])
if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS))
M.stop_sound_channel(jukeinfo[JUKE_CHANNEL])
continue
var/inrange = FALSE
if(jukebox.z == M.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master
song_played.status = SOUND_UPDATE
if(get_area(M) == currentarea)
inrange = TRUE
else if(M in hearerscache)
inrange = TRUE
else
song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame.
M.playsound_local(currentturf, null, 100, channel = jukeinfo[2], S = song_played, envwet = (inrange ? -250 : 0), envdry = (inrange ? 0 : -10000))
inrange = FALSE
song_played.status = SOUND_MUTE | SOUND_UPDATE
if(source_pressure)
hearerturf = get_turf(M)
hearer_env = (istype(hearerturf) ? hearerturf.return_air() : null)
if(istype(hearer_env))
pressure_factor = min(source_pressure, hearer_env.return_pressure())
if(pressure_factor && targetfalloff && M.can_hear() && (M.z in audible_zlevels))
if(get_area(M) == currentarea)
inrange = TRUE
else if(M in hearerscache)
inrange = TRUE
song_played.x = (currentturf.x - M.x) * SOUND_DEFAULT_DISTANCE_MULTIPLIER
song_played.z = (currentturf.y - M.y) * SOUND_DEFAULT_DISTANCE_MULTIPLIER
song_played.y = (((currentturf.z - M.z) * 10 * SOUND_DEFAULT_DISTANCE_MULTIPLIER) + ((currentturf.z < M.z) ? -5 : 5))
if(pressure_factor < ONE_ATMOSPHERE)
song_played.volume = (min((targetfalloff * 50), 100) * max((pressure_factor - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 1))
song_played.echo[1] = (inrange ? 0 : -10000)
song_played.echo[3] = (inrange ? mixes : max(mixes, 0))
song_played.status = SOUND_UPDATE
SEND_SOUND(M, song_played)
CHECK_TICK
return
#undef JUKE_TRACK
#undef JUKE_CHANNEL
#undef JUKE_BOX
#undef JUKE_FALLOFF
#undef JUKE_SOUND
+2
View File
@@ -1192,6 +1192,8 @@
log_mecha(log_text)
if(LOG_SHUTTLE)
log_shuttle(log_text)
if(LOG_ECON)
log_econ(log_text)
else
stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).")
log_game(log_text)
+30
View File
@@ -312,6 +312,36 @@ Class Procs:
return TRUE // If we passed all of those checks, woohoo! We can interact with this machine.
/obj/machinery/proc/can_transact(obj/item/card/id/thecard, allowdepartment, silent)
if(!istype(thecard))
if(!silent)
say("No card found.")
return FALSE
else if (!thecard.registered_account)
if(!silent)
say("No account found.")
return FALSE
else if(!allowdepartment && !thecard.registered_account.account_job)
if(!silent)
say("Departmental accounts have been blacklisted from personal expenses due to embezzlement.")
return FALSE
return TRUE
/obj/machinery/proc/attempt_transact(obj/item/card/id/thecard, transaction_cost)
if(!istype(thecard))
return FALSE
var/datum/bank_account/account = thecard.registered_account
if(!istype(account))
return FALSE
if(transaction_cost)
if(!account.adjust_money(-transaction_cost))
return FALSE
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
if(D)
D.adjust_money(transaction_cost)
return TRUE
/obj/machinery/proc/check_nap_violations()
if(!SSeconomy.full_ancap)
return TRUE
+103 -41
View File
@@ -6,17 +6,22 @@
verb_say = "states"
density = TRUE
req_one_access = list(ACCESS_BAR, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_ENGINE, ACCESS_CARGO, ACCESS_THEATRE)
payment_department = ACCOUNT_SRV
var/active = FALSE
var/list/rangers = list()
var/stop = 0
var/volume = 70
var/datum/track/selection = null
var/queuecost = PRICE_CHEAP //Set to -1 to make this jukebox require access for queueing
var/datum/track/playing = null
var/datum/track/selectedtrack = null
var/list/queuedplaylist = list()
var/queuecooldown //This var exists solely to prevent accidental repeats of John Mulaney's 'What's New Pussycat?' incident. Intentional, however......
/obj/machinery/jukebox/disco
name = "radiant dance machine mark IV"
desc = "The first three prototypes were discontinued after mass casualty incidents."
icon_state = "disco"
req_access = list(ACCESS_ENGINE)
req_one_access = list(ACCESS_ENGINE)
anchored = FALSE
var/list/spotlights = list()
var/list/sparkles = list()
@@ -24,7 +29,7 @@
/obj/machinery/jukebox/disco/indestructible
name = "radiant dance machine mark V"
desc = "Now redesigned with data gathered from the extensive disco and plasma research."
req_access = null
req_one_access = null
anchored = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
flags_1 = NODECONSTRUCT_1
@@ -46,6 +51,16 @@
return
return ..()
/obj/machinery/jukebox/emag_act(mob/user)
. = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
queuecost = PRICE_FREE
req_one_access = null
to_chat(user, "<span class='notice'>You've bypassed [src]'s audio volume limiter, and enabled free play.</span>")
return TRUE
/obj/machinery/jukebox/update_icon_state()
if(active)
icon_state = "[initial(icon_state)]-active"
@@ -56,7 +71,7 @@
if(!anchored)
to_chat(user,"<span class='warning'>This device must be anchored by a wrench!</span>")
return UI_CLOSE
if(!allowed(user) && !isobserver(user))
if((queuecost < 0 && !allowed(user)) && !isobserver(user))
to_chat(user,"<span class='warning'>Error: Access Denied.</span>")
user.playsound_local(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
return UI_CLOSE
@@ -77,18 +92,21 @@
data["active"] = active
data["songs"] = list()
for(var/datum/track/S in SSjukeboxes.songs)
var/list/track_data = list(
name = S.song_name
)
var/list/track_data = list(name = S.song_name)
data["songs"] += list(track_data)
data["queued_tracks"] = list()
for(var/datum/track/S in queuedplaylist)
var/list/track_data = list(name = S.song_name)
data["queued_tracks"] += list(track_data)
data["track_selected"] = null
data["track_length"] = null
data["track_beat"] = null
if(selection)
data["track_selected"] = selection.song_name
data["track_length"] = DisplayTimeText(selection.song_length)
data["track_beat"] = selection.song_beat
if(playing)
data["track_selected"] = playing.song_name
data["track_length"] = DisplayTimeText(playing.song_length)
data["volume"] = volume
data["is_emagged"] = (obj_flags & EMAGGED)
data["cost_for_play"] = queuecost
data["has_access"] = allowed(user)
return data
/obj/machinery/jukebox/ui_act(action, list/params)
@@ -100,57 +118,91 @@
if("toggle")
if(QDELETED(src))
return
if(!active)
if(stop > world.time)
to_chat(usr, "<span class='warning'>Error: The device is still resetting from the last activation, it will be ready again in [DisplayTimeText(stop-world.time)].</span>")
playsound(src, 'sound/misc/compiler-failure.ogg', 50, TRUE)
return
if(!allowed(usr))
return
if(!active && !playing)
activate_music()
START_PROCESSING(SSobj, src)
return TRUE
else
stop = 0
return TRUE
if("select_track")
if(active)
to_chat(usr, "<span class='warning'>Error: You cannot change the song until the current one is over.</span>")
return TRUE
if("add_to_queue")
if(QDELETED(src))
return
if(world.time < queuecooldown)
return
if(!istype(selectedtrack, /datum/track))
return
if(!allowed(usr) && queuecost)
var/obj/item/card/id/C
if(isliving(usr))
var/mob/living/L = usr
C = L.get_idcard(TRUE)
if(!can_transact(C))
queuecooldown = world.time + (1 SECONDS)
playsound(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
return
if(!attempt_transact(C, queuecost))
say("Insufficient funds.")
queuecooldown = world.time + (1 SECONDS)
playsound(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
return
to_chat(usr, "<span class='notice'>You spend [queuecost] credits to queue [selectedtrack.song_name].</span>")
log_econ("[queuecost] credits were inserted into [src] by [key_name(usr)] (ID: [C.registered_name]) to queue [selectedtrack.song_name].")
queuedplaylist += selectedtrack
if(active)
say("[selectedtrack.song_name] has been added to the queue.")
else if(!playing)
activate_music()
playsound(src, 'sound/machines/ping.ogg', 50, TRUE)
queuecooldown = world.time + (3 SECONDS)
return TRUE
if("select_track")
var/list/available = list()
for(var/datum/track/S in SSjukeboxes.songs)
available[S.song_name] = S
var/selected = params["track"]
if(QDELETED(src) || !selected || !istype(available[selected], /datum/track))
return
selection = available[selected]
selectedtrack = available[selected]
return TRUE
if("set_volume")
if(!allowed(usr))
return
var/new_volume = params["volume"]
if(new_volume == "reset")
volume = initial(volume)
return TRUE
else if(new_volume == "min")
volume = 0
return TRUE
else if(new_volume == "max")
volume = 100
return TRUE
volume = ((obj_flags & EMAGGED) ? 210 : 100)
else if(text2num(new_volume) != null)
volume = text2num(new_volume)
return TRUE
volume = clamp(0, text2num(new_volume), ((obj_flags & EMAGGED) ? 210 : 100))
var/wherejuke = SSjukeboxes.findjukeboxindex(src)
if(wherejuke)
SSjukeboxes.updatejukebox(wherejuke, jukefalloff = volume/35)
return TRUE
/obj/machinery/jukebox/proc/activate_music()
var/jukeboxslottotake = SSjukeboxes.addjukebox(src, selection, 2)
if(playing || !queuedplaylist.len)
return FALSE
playing = queuedplaylist[1]
var/jukeboxslottotake = SSjukeboxes.addjukebox(src, playing, volume/35)
if(jukeboxslottotake)
active = TRUE
update_icon()
START_PROCESSING(SSobj, src)
stop = world.time + selection.song_length
stop = world.time + playing.song_length
queuedplaylist.Cut(1, 2)
say("Now playing: [playing.song_name]")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, TRUE)
return TRUE
else
return FALSE
/obj/machinery/jukebox/disco/activate_music()
..()
. = ..()
if(!.)
return
dance_setup()
lights_spin()
@@ -243,7 +295,7 @@
S.pixel_y = 7
S.forceMove(get_turf(src))
sleep(7)
if(selection.song_name == "Engineering's Ultimate High-Energy Hustle")
if(playing.song_name == "Engineering's Ultimate High-Energy Hustle")
sleep(280)
for(var/obj/reveal in sparkles)
reveal.alpha = 255
@@ -299,7 +351,7 @@
continue
if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up
INVOKE_ASYNC(src, .proc/hierofunk)
sleep(selection.song_beat)
sleep(playing.song_beat)
#undef DISCO_INFENO_RANGE
@@ -431,6 +483,7 @@
return
SSjukeboxes.removejukebox(position)
STOP_PROCESSING(SSobj, src)
playing = null
rangers = list()
/obj/machinery/jukebox/disco/dance_over()
@@ -439,12 +492,21 @@
QDEL_LIST(sparkles)
/obj/machinery/jukebox/process()
if(active && world.time >= stop)
active = FALSE
dance_over()
playsound(src,'sound/machines/terminal_off.ogg',50,1)
update_icon()
stop = world.time + 100
if(active)
if(world.time >= stop)
active = FALSE
dance_over()
if(stop && queuedplaylist.len)
activate_music()
else
playsound(src,'sound/machines/terminal_off.ogg',50,1)
update_icon()
playing = null
stop = 0
else if(volume > 140) // BOOM BOOM BOOM BOOM
for(var/mob/living/carbon/C in hearers(round(volume/35), src)) // I WANT YOU IN MY ROOM
if(istype(C)) // LETS SPEND THE NIGHT TOGETHER
C.adjustEarDamage(max((((volume/35) - sqrt(get_dist(C, src) * 4)) - C.get_ear_protection())*0.1, 0)) // FROM NOW UNTIL FOREVER
/obj/machinery/jukebox/disco/process()
+5 -2
View File
@@ -111,12 +111,15 @@ distance_multiplier - Can be used to multiply the distance at which the sound is
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance,
falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = SOUND_DEFAULT_DISTANCE_MULTIPLIER, envwet = -10000, envdry = 0)
if(!client || !can_hear())
if(!client)
return
if(!S)
S = sound(get_sfx(soundin))
if(!can_hear() && !(S.status & SOUND_UPDATE)) //This is primarily to make sure sound updates still go through when a spaceman's deaf
return
S.wait = 0 //No queue
S.channel = channel || SSsounds.random_available_channel()
S.volume = vol
@@ -173,7 +176,7 @@ distance_multiplier - Can be used to multiply the distance at which the sound is
var/dz = turf_source.y - T.y // Hearing from infront/behind
S.z = dz * distance_multiplier
var/dy = (turf_source.z - T.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords.
S.y = dy + 1
S.y = dy + distance_multiplier
S.falloff = isnull(max_distance)? FALLOFF_SOUNDS : max_distance //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant.
+1
View File
@@ -117,6 +117,7 @@ GLOBAL_LIST(topic_status_cache)
GLOB.world_asset_log = "[GLOB.log_directory]/asset.log"
GLOB.world_attack_log = "[GLOB.log_directory]/attack.log"
GLOB.world_victim_log = "[GLOB.log_directory]/victim.log"
GLOB.world_econ_log = "[GLOB.log_directory]/econ.log"
GLOB.world_pda_log = "[GLOB.log_directory]/pda.log"
GLOB.world_telecomms_log = "[GLOB.log_directory]/telecomms.log"
GLOB.world_manifest_log = "[GLOB.log_directory]/manifest.log"
+1 -1
View File
@@ -103,7 +103,7 @@
return
account.adjust_money(-deposit_value) //The money vanishes, not paid to any accounts.
SSblackbox.record_feedback("amount", "BEPIS_credits_spent", deposit_value)
//log_econ("[deposit_value] credits were inserted into [src] by [account.account_holder]")
log_econ("[deposit_value] credits were inserted into [src] by [key_name(usr)] (account: [account.account_holder])")
banked_cash += deposit_value
use_power(1000 * power_saver)
say("Cash deposit successful. There is [banked_cash] in the chamber.")
+9 -4
View File
@@ -18,10 +18,9 @@
// to hear anything.
var/deaf = 0
// `ear_damage` measures long term damage to the ears, if too high,
// `damage` in this case measures long term damage to the ears, if too high,
// the person will not have either `deaf` or `ear_damage` decrease
// without external aid (earmuffs, drugs)
var/ear_damage = 0
//Resistance against loud noises
var/bang_protect = 0
@@ -44,16 +43,22 @@
/obj/item/organ/ears/proc/restoreEars()
deaf = 0
ear_damage = 0
damage = 0
prev_damage = 0
organ_flags &= ~ORGAN_FAILING
var/mob/living/carbon/C = owner
if(iscarbon(owner) && HAS_TRAIT(C, TRAIT_DEAF))
deaf = 1
var/mess = check_damage_thresholds()
if(mess && owner)
to_chat(owner, mess)
/obj/item/organ/ears/proc/adjustEarDamage(ddmg, ddeaf)
ear_damage = max(ear_damage + (ddmg*damage_multiplier), 0)
if(owner.status_flags & GODMODE)
return
setOrganDamage(max(damage + (ddmg*damage_multiplier), 0))
deaf = max(deaf + (ddeaf*damage_multiplier), 0)
/obj/item/organ/ears/proc/minimumDeafTicks(value)
+5 -19
View File
@@ -864,21 +864,10 @@ GLOBAL_LIST_EMPTY(vending_products)
if(isliving(usr))
var/mob/living/L = usr
C = L.get_idcard(TRUE)
if(!C)
say("No card found.")
if(!can_transact(C))
flick(icon_deny,src)
vend_ready = TRUE
return
else if (!C.registered_account)
say("No account found.")
flick(icon_deny,src)
vend_ready = TRUE
return
else if(!C.registered_account.account_job)
say("Departmental accounts have been blacklisted from personal expenses due to embezzlement.")
flick(icon_deny, src)
vend_ready = TRUE
return
// else if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR))
// say("You are not of legal age to purchase [R.name].")
// if(!(usr in GLOB.narcd_underages))
@@ -901,16 +890,13 @@ GLOBAL_LIST_EMPTY(vending_products)
price_to_use = 0 // it's free shut up
if(coin_records.Find(R) || hidden_records.Find(R))
price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price
if(price_to_use && !account.adjust_money(-price_to_use))
if(price_to_use && !attempt_transact(C, price_to_use))
say("You do not possess the funds to purchase [R.name].")
flick(icon_deny,src)
vend_ready = TRUE
return
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
if(D)
D.adjust_money(price_to_use)
SSblackbox.record_feedback("amount", "vending_spent", price_to_use)
// log_econ("[price_to_use] credits were inserted into [src] by [D.account_holder] to buy [R].")
SSblackbox.record_feedback("amount", "vending_spent", price_to_use)
log_econ("[price_to_use] credits were inserted into [src] by [key_name(usr)] (account: [account.account_holder]) to buy [R].")
if(last_shopper != REF(usr) || purchase_message_cooldown < world.time)
say("Thank you for shopping with [src]!")
purchase_message_cooldown = world.time + 5 SECONDS
@@ -1169,7 +1155,7 @@ GLOBAL_LIST_EMPTY(vending_products)
if(owner)
owner.adjust_money(S.custom_price)
SSblackbox.record_feedback("amount", "vending_spent", S.custom_price)
// log_econ("[S.custom_price] credits were spent on [src] buying a [S] by [owner.account_holder], owned by [private_a.account_holder].")
log_econ("[S.custom_price] credits were spent on [src] buying a [S] by [key_name(usr)] (account: [owner.account_holder]), owned by [private_a.account_holder].")
vending_machine_input[N] = max(vending_machine_input[N] - 1, 0)
S.forceMove(drop_location())
loaded_items--
+3
View File
@@ -61,6 +61,9 @@ LOG_JOB_DEBUG
## Log shuttle related actions
LOG_SHUTTLE
## Log economy-related actions
LOG_ECON
## log all world.Topic() calls
LOG_WORLD_TOPIC
+108 -72
View File
@@ -1,7 +1,7 @@
import { sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { useBackend } from '../backend';
import { Box, Button, Dropdown, Section, Knob, LabeledControls, LabeledList } from '../components';
import { Box, Button, Dropdown, Section, Knob, LabeledControls, LabeledList, Stack, Tabs } from '../components';
import { Window } from '../layouts';
export const Jukebox = (props, context) => {
@@ -10,98 +10,134 @@ export const Jukebox = (props, context) => {
active,
track_selected,
track_length,
track_beat,
volume,
is_emagged,
cost_for_play,
has_access,
} = data;
const songs = flow([
sortBy(
song => song.name),
])(data.songs || []);
const queued_tracks = data.queued_tracks || [];
return (
<Window
width={370}
height={313}>
width={420}
height={480}>
<Window.Content>
<Section
title="Song Player"
title="Machine Controls"
buttons={(
<Button
icon={active ? 'pause' : 'play'}
content={active ? 'Stop' : 'Play'}
selected={active}
disabled={!has_access}
onClick={() => act('toggle')} />
)}>
<Stack>
<Stack.Item>
<LabeledList>
<LabeledList.Item label="Current Track">
{track_selected ? track_selected : "No Track Selected"}
</LabeledList.Item>
<LabeledList.Item label="Track Length">
{track_selected ? track_length : "No Track Selected"}
</LabeledList.Item>
</LabeledList>
</Stack.Item>
<Stack.Item>
<LabeledControls justify="center">
<LabeledControls.Item label="Volume">
<Box position="relative">
<Knob
size={2.4}
color={volume > 140 ? 'red' : 'green'}
value={volume}
unit="%"
minValue={0}
maxValue={is_emagged ? 210 : 100}
step={1}
stepPixelSize={1}
disabled={!has_access}
onDrag={(e, value) => act('set_volume', {
volume: value,
})} />
<Button
fluid
position="absolute"
top="67px"
right="62px"
color="transparent"
icon="fast-backward"
disabled={!has_access}
onClick={() => act('set_volume', {
volume: "min",
})} />
<Button
fluid
position="absolute"
top="67px"
right="-8px"
color="transparent"
icon="fast-forward"
disabled={!has_access}
onClick={() => act('set_volume', {
volume: "max",
})} />
<Button
fluid
position="absolute"
top="67px"
right="84px"
color="transparent"
icon="undo"
disabled={!has_access}
onClick={() => act('set_volume', {
volume: "reset",
})} />
</Box>
</LabeledControls.Item>
</LabeledControls>
</Stack.Item>
</Stack>
<LabeledList>
<LabeledList.Item label="Track Selected">
<Dropdown
overflow-y="scroll"
width="240px"
options={songs.map(song => song.name)}
disabled={active}
selected={track_selected || "Select a Track"}
onSelected={value => act('select_track', {
track: value,
})} />
</LabeledList.Item>
<LabeledList.Item label="Track Length">
{track_selected ? track_length : "No Track Selected"}
</LabeledList.Item>
<LabeledList.Item label="Track Beat">
{track_selected ? track_beat : "No Track Selected"}
{track_beat === 1 ? " beat" : " beats"}
<LabeledList.Item label="Cost to Queue">
{cost_for_play} CR {has_access ? "(Cost waived)" : ""}
</LabeledList.Item>
</LabeledList>
</Section>
<Section title="Machine Settings">
<LabeledControls justify="center">
<LabeledControls.Item label="Volume">
<Box position="relative">
<Knob
size={3.2}
color={volume >= 50 ? 'red' : 'green'}
value={volume}
unit="%"
minValue={0}
maxValue={100}
step={1}
stepPixelSize={1}
disabled={active}
onDrag={(e, value) => act('set_volume', {
volume: value,
})} />
<Button
fluid
position="absolute"
top="-2px"
right="-22px"
color="transparent"
icon="fast-backward"
onClick={() => act('set_volume', {
volume: "min",
})} />
<Button
fluid
position="absolute"
top="16px"
right="-22px"
color="transparent"
icon="fast-forward"
onClick={() => act('set_volume', {
volume: "max",
})} />
<Button
fluid
position="absolute"
top="34px"
right="-22px"
color="transparent"
icon="undo"
onClick={() => act('set_volume', {
volume: "reset",
})} />
</Box>
</LabeledControls.Item>
</LabeledControls>
<Section fill vertical>
<Stack>
<Stack.Item grow>
<Dropdown
width="100%"
overflow-y="scroll"
options={songs.map(song => song.name)}
selected="Select a Track"
onSelected={value => act('select_track', {
track: value,
})} />
</Stack.Item>
<Stack.Item>
<Button
icon="play"
content="Queue"
onClick={() => act('add_to_queue')}
/>
</Stack.Item>
</Stack>
<Section fill vertical>
<Tabs vertical>
{queued_tracks.map(song => (
<Tabs.Tab
key={song.name}
>
{song.name}
</Tabs.Tab>
))}
</Tabs>
</Section>
</Section>
</Window.Content>
</Window>