Port Baystruments PR from tgstation

This commit is contained in:
mochi
2020-07-17 15:18:17 +02:00
parent a7e4c8af42
commit 24e8cd66ac
269 changed files with 2253 additions and 717 deletions

View File

@@ -0,0 +1,86 @@
PROCESSING_SUBSYSTEM_DEF(instruments)
name = "Instruments"
init_order = INIT_ORDER_INSTRUMENTS
wait = 0.5
flags = SS_KEEP_TIMING
offline_implications = "Instruments will no longer play. No immediate action is needed."
stat_tag = "Instruments"
/// List of all instrument data, associative id = datum
var/list/datum/instrument/instrument_data
/// List of all song datums.
var/list/datum/song/songs
/// Max lines in songs
var/musician_maxlines = 600
/// Max characters per line in songs
var/musician_maxlinechars = 300
/// Deciseconds between hearchecks. Too high and instruments seem to lag when people are moving around in terms of who can hear it. Too low and the server lags from this.
var/musician_hearcheck_mindelay = 5
/// Maximum instrument channels total instruments are allowed to use. This is so you don't have instruments deadlocking all sound channels.
var/max_instrument_channels = MAX_INSTRUMENT_CHANNELS
/// Current number of channels allocated for instruments
var/current_instrument_channels = 0
/// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer.
var/list/synthesizer_instrument_ids
/datum/controller/subsystem/processing/instruments/Initialize()
initialize_instrument_data()
synthesizer_instrument_ids = get_allowed_instrument_ids()
return ..()
/**
* Initializes all instrument datums
*/
/datum/controller/subsystem/processing/instruments/proc/initialize_instrument_data()
instrument_data = list()
for(var/path in subtypesof(/datum/instrument))
var/datum/instrument/I = path
if(initial(I.abstract_type) == path)
continue
I = new path
I.Initialize()
if(!I.id)
qdel(I)
continue
instrument_data[I.id] = I
CHECK_TICK
/**
* Reserves a sound channel for a given instrument datum
*
* Arguments:
* * I - The instrument datum
*/
/datum/controller/subsystem/processing/instruments/proc/reserve_instrument_channel(datum/instrument/I)
if(current_instrument_channels > max_instrument_channels)
return
. = SSsounds.reserve_sound_channel(I)
if(!isnull(.))
current_instrument_channels++
/**
* Called when a datum/song is created
*
* Arguments:
* * S - The created datum/song
*/
/datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S)
LAZYADD(songs, S)
/**
* Called when a datum/song is deleted
*
* Arguments:
* * S - The deleted datum/song
*/
/datum/controller/subsystem/processing/instruments/proc/on_song_del(datum/song/S)
LAZYREMOVE(songs, S)
/**
* Returns the instrument datum at the given ID or path
*
* Arguments:
* * id_or_path - The ID or path of the instrument
*/
/datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path)
return instrument_data["[id_or_path]"]

View File

@@ -0,0 +1,165 @@
#define DATUMLESS "NO_DATUM"
SUBSYSTEM_DEF(sounds)
name = "Sounds"
init_order = INIT_ORDER_SOUNDS
flags = SS_NO_FIRE
offline_implications = "Sounds may not play correctly. Shuttle call recommended."
var/using_channels_max = CHANNEL_HIGHEST_AVAILABLE // BYOND max channels
/// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
var/random_channels_min = 50
// Hey uh these two needs to be initialized fast because the whole "things get deleted before init" thing.
/// Assoc list, "[channel]" = either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel
var/list/using_channels
/// Assoc list datum = list(channel1, channel2, ...) for what channels something reserved.
var/list/using_channels_by_datum
// Special datastructure for fast channel management
/// List of all channels as numbers
var/list/channel_list
/// Associative list of all reserved channels associated to their position. "[channel_number]" = index as number
var/list/reserved_channels
/// lower iteration position - Incremented and looped to get "random" sound channels for normal sounds. The channel at this index is returned when asking for a random channel.
var/channel_random_low
/// higher reserve position - decremented and incremented to reserve sound channels, anything above this is reserved. The channel at this index is the highest unreserved channel.
var/channel_reserve_high
/datum/controller/subsystem/sounds/Initialize()
setup_available_channels()
return ..()
/**
* Sets up all available sound channels
*/
/datum/controller/subsystem/sounds/proc/setup_available_channels()
channel_list = list()
reserved_channels = list()
using_channels = list()
using_channels_by_datum = list()
for(var/i in 1 to using_channels_max)
channel_list += i
channel_random_low = 1
channel_reserve_high = length(channel_list)
/**
* Removes a channel from using list
*
* Arguments:
* * channel - The channel number
*/
/datum/controller/subsystem/sounds/proc/free_sound_channel(channel)
var/text_channel = num2text(channel)
var/using = using_channels[text_channel]
using_channels -= text_channel
if(using != TRUE) // datum channel
using_channels_by_datum[using] -= channel
if(!length(using_channels_by_datum[using]))
using_channels_by_datum -= using
free_channel(channel)
/**
* Frees all the channels a datum is using
*
* Arguments:
* * D - The datum
*/
/datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D)
var/list/L = using_channels_by_datum[D]
if(!L)
return
for(var/channel in L)
using_channels -= num2text(channel)
free_channel(channel)
using_channels_by_datum -= D
/**
* Frees all datumless channels
*/
/datum/controller/subsystem/sounds/proc/free_datumless_channels()
free_datum_channels(DATUMLESS)
/**
* NO AUTOMATIC CLEANUP - If you use this, you better manually free it later!
*
* Returns an integer for channel
*/
/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless()
. = reserve_channel()
if(!.) // oh no..
return FALSE
var/text_channel = num2text(.)
using_channels[text_channel] = DATUMLESS
LAZYADD(using_channels_by_datum[DATUMLESS], .)
/**
* Reserves a channel for a datum. Automatic cleanup only when the datum is deleted.
*
* Returns an integer for channel
* Arguments:
* * D - The datum
*/
/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D)
if(!D) // i don't like typechecks but someone will fuck it up
CRASH("Attempted to reserve sound channel without datum using the managed proc.")
. = reserve_channel()
if(!.)
return FALSE
var/text_channel = num2text(.)
using_channels[text_channel] = D
LAZYADD(using_channels_by_datum[D], .)
/**
* Reserves a channel and updates the datastructure. Private proc.
*/
/datum/controller/subsystem/sounds/proc/reserve_channel()
PRIVATE_PROC(TRUE)
if(channel_reserve_high <= random_channels_min) // out of channels
return
var/channel = channel_list[channel_reserve_high]
reserved_channels[num2text(channel)] = channel_reserve_high--
return channel
/**
* Frees a channel and updates the datastructure. Private proc.
*/
/datum/controller/subsystem/sounds/proc/free_channel(number)
PRIVATE_PROC(TRUE)
var/text_channel = num2text(number)
var/index = reserved_channels[text_channel]
if(!index)
CRASH("Attempted to (internally) free a channel that wasn't reserved.")
reserved_channels -= text_channel
// push reserve index up, which makes it now on a channel that is reserved
channel_reserve_high++
// swap the reserved channel wtih the unreserved channel so the reserve index is now on an unoccupied channel and the freed channel is next to be used.
channel_list.Swap(channel_reserve_high, index)
// now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index
// get it, and update position.
var/text_reserved = num2text(channel_list[index])
if(!reserved_channels[text_reserved]) // if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
return
reserved_channels[text_reserved] = index
/**
* Random available channel, returns text
*/
/datum/controller/subsystem/sounds/proc/random_available_channel_text()
if(channel_random_low > channel_reserve_high)
channel_random_low = 1
. = "[channel_list[channel_random_low++]]"
/**
* Random available channel, returns number
*/
/datum/controller/subsystem/sounds/proc/random_available_channel()
if(channel_random_low > channel_reserve_high)
channel_random_low = 1
. = channel_list[channel_random_low++]
/**
* How many channels we have left
*/
/datum/controller/subsystem/sounds/proc/available_channels_left()
return length(channel_list) - random_channels_min
#undef DATUMLESS