Merge branch 'master' into fix-ghost-poll-chat-signup

This commit is contained in:
mochi
2020-09-29 15:08:16 +02:00
1219 changed files with 37220 additions and 29828 deletions
+28 -28
View File
@@ -1,31 +1,31 @@
SUBSYSTEM_DEF(alarms)
name = "Alarms"
init_order = INIT_ORDER_ALARMS // 2
offline_implications = "Alarms (Power, camera, fire, etc) will no longer be checked. No immediate action is needed."
var/datum/alarm_handler/atmosphere/atmosphere_alarm = new()
var/datum/alarm_handler/burglar/burglar_alarm = new()
var/datum/alarm_handler/camera/camera_alarm = new()
var/datum/alarm_handler/fire/fire_alarm = new()
var/datum/alarm_handler/motion/motion_alarm = new()
var/datum/alarm_handler/power/power_alarm = new()
var/list/datum/alarm/all_handlers
SUBSYSTEM_DEF(alarm)
name = "Alarm"
flags = SS_NO_INIT | SS_NO_FIRE
var/list/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list(), "Burglar" = list())
/datum/controller/subsystem/alarms/Initialize(start_timeofday)
all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
return ..()
/datum/controller/subsystem/alarm/proc/triggerAlarm(class, area/A, list/O, obj/alarmsource)
var/list/L = alarms[class]
for(var/I in L)
if(I == A.name)
var/list/alarm = L[I]
var/list/sources = alarm[3]
if(!(alarmsource.UID() in sources))
sources += alarmsource.UID()
return TRUE
L[A.name] = list(get_area_name(A, TRUE), O, list(alarmsource.UID()))
SEND_SIGNAL(SSalarm, COMSIG_TRIGGERED_ALARM, class, A, O, alarmsource)
return TRUE
/datum/controller/subsystem/alarms/fire()
for(var/datum/alarm_handler/AH in all_handlers)
AH.process()
/datum/controller/subsystem/alarm/proc/cancelAlarm(class, area/A, obj/origin)
var/list/L = alarms[class]
var/cleared = FALSE
for(var/I in L)
if(I == A.name)
var/list/alarm = L[I]
var/list/srcs = alarm[3]
srcs -= origin.UID()
if(!length(srcs))
cleared = TRUE
L -= I
/datum/controller/subsystem/alarms/proc/active_alarms()
var/list/all_alarms = new ()
for(var/datum/alarm_handler/AH in all_handlers)
var/list/alarms = AH.alarms
all_alarms += alarms
return all_alarms
/datum/controller/subsystem/alarms/proc/number_of_active_alarms()
var/list/alarms = active_alarms()
return alarms.len
SEND_SIGNAL(SSalarm, COMSIG_CANCELLED_ALARM, class, A, origin, cleared)
-67
View File
@@ -1,67 +0,0 @@
SUBSYSTEM_DEF(chat)
name = "Chat"
flags = SS_TICKER|SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
offline_implications = "Chat messages will no longer be cleanly queued. No immediate action is needed."
var/list/payload = list()
/datum/controller/subsystem/chat/fire()
for(var/i in payload)
var/client/C = i
if(C)
C << output(payload[C], "browseroutput:output")
payload -= C
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/chat/proc/queue(target, message, flag)
if(!target || !message)
return
if(!istext(message))
stack_trace("to_chat called with invalid input type")
return
if(target == world)
target = GLOB.clients
//Some macros remain in the string even after parsing and fuck up the eventual output
message = replacetext(message, "\improper", "")
message = replacetext(message, "\proper", "")
message += "<br>"
//url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
//Do the double-encoding here to save nanoseconds
var/twiceEncoded = url_encode(url_encode(message))
if(islist(target))
for(var/I in target)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
continue
if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
continue
payload[C] += twiceEncoded
else
var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file.
return
if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
return
payload[C] += twiceEncoded
+44
View File
@@ -0,0 +1,44 @@
/**
* # Cleanup Subsystem
*
* For now, all it does is periodically clean the supplied global lists of any null values they may contain.
*
* Why is this important?
*
* Sometimes, these lists can gain nulls due to errors.
* For example, when a dead player trasitions from the `dead_mob_list` to the `alive_mob_list`, a null value may get stuck in the dead mob list.
* This can cause issues when other code tries to do things with the values in the list, but are instead met with null values.
* These problems are incredibly hard to track down and fix, so this subsystem is a solution to that.
*/
SUBSYSTEM_DEF(cleanup)
name = "Null cleanup"
wait = 30 SECONDS
flags = SS_POST_FIRE_TIMING
priority = FIRE_PRIORITY_CLEANUP
init_order = INIT_ORDER_CLEANUP
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
offline_implications = "Certain global lists will no longer be cleared of nulls, which may result in runtimes. No immediate action is needed."
/// A list of global lists we want the subsystem to clean.
var/list/lists_to_clean
/datum/controller/subsystem/cleanup/Initialize(start_timeofday)
// If you want this subsystem to clean out nulls from a specific list, add it here.
lists_to_clean = list(
GLOB.clients = "clients",
GLOB.player_list = "player_list",
GLOB.mob_list = "mob_list",
GLOB.alive_mob_list = "alive_mob_list",
GLOB.dead_mob_list = "dead_mob_list",
GLOB.human_list = "human_list",
GLOB.carbon_list = "carbon_list"
)
return ..()
/datum/controller/subsystem/cleanup/fire(resumed)
for(var/L in lists_to_clean)
var/list/_list = L
var/prev_length = length(_list)
listclearnulls(_list)
if(length(_list) < prev_length)
stack_trace("Found a null value in GLOB.[lists_to_clean[_list]]!")
+4 -4
View File
@@ -40,7 +40,7 @@ SUBSYSTEM_DEF(events)
var/datum/event_container/EC = event_containers[i]
EC.process()
/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E)
/datum/controller/subsystem/events/proc/event_complete(datum/event/E)
if(!E.event_meta) // datum/event is used here and there for random reasons, maintaining "backwards compatibility"
log_debug("Event of '[E.type]' with missing meta-data has completed.")
return
@@ -65,11 +65,11 @@ SUBSYSTEM_DEF(events)
log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].")
/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay)
/datum/controller/subsystem/events/proc/delay_events(severity, delay)
var/datum/event_container/EC = event_containers[severity]
EC.next_event_time += delay
/datum/controller/subsystem/events/proc/Interact(var/mob/living/user)
/datum/controller/subsystem/events/proc/Interact(mob/living/user)
var/html = GetInteractWindow()
@@ -113,7 +113,7 @@ SUBSYSTEM_DEF(events)
html += "<td>[EM.name]</td>"
html += "<td><A align='right' href='?src=[UID()];set_weight=\ref[EM]'>[EM.weight]</A></td>"
html += "<td>[EM.min_weight]</td>"
html += "<td>[EM.max_weight]</td>"
html += "<td>[EM.max_weight == INFINITY ? "No max" : EM.max_weight]</td>"
html += "<td><A align='right' href='?src=[UID()];toggle_oneshot=\ref[EM]'>[EM.one_shot]</A></td>"
html += "<td><A align='right' href='?src=[UID()];toggle_enabled=\ref[EM]'>[EM.enabled]</A></td>"
html += "<td><span class='alert'>[EM.get_weight(number_active_with_role())]</span></td>"
+40 -38
View File
@@ -620,20 +620,32 @@ SUBSYSTEM_DEF(jobs)
var/mob/M = tgtcard.getPlayer()
for(var/datum/job/job in occupations)
if(tgtcard.assignment && tgtcard.assignment == job.title)
jobs_to_formats[job.title] = "disabled" // the job they already have is pre-selected
jobs_to_formats[job.title] = "green" // the job they already have is pre-selected
else if(tgtcard.assignment == "Demoted" || tgtcard.assignment == "Terminated")
jobs_to_formats[job.title] = "grey"
else if(!job.would_accept_job_transfer_from_player(M))
jobs_to_formats[job.title] = "linkDiscourage" // jobs which are karma-locked and not unlocked for this player are discouraged
jobs_to_formats[job.title] = "grey" // jobs which are karma-locked and not unlocked for this player are discouraged
else if((job.title in GLOB.command_positions) && istype(M) && M.client && job.available_in_playtime(M.client))
jobs_to_formats[job.title] = "linkDiscourage" // command jobs which are playtime-locked and not unlocked for this player are discouraged
jobs_to_formats[job.title] = "grey" // command jobs which are playtime-locked and not unlocked for this player are discouraged
else if(job.total_positions && !job.current_positions && job.title != "Civilian")
jobs_to_formats[job.title] = "linkEncourage" // jobs with nobody doing them at all are encouraged
jobs_to_formats[job.title] = "teal" // jobs with nobody doing them at all are encouraged
else if(job.total_positions >= 0 && job.current_positions >= job.total_positions)
jobs_to_formats[job.title] = "linkDiscourage" // jobs that are full (no free positions) are discouraged
jobs_to_formats[job.title] = "grey" // jobs that are full (no free positions) are discouraged
if(tgtcard.assignment == "Demoted" || tgtcard.assignment == "Terminated")
jobs_to_formats["Custom"] = "grey"
return jobs_to_formats
/datum/controller/subsystem/jobs/proc/log_job_transfer(transferee, oldvalue, newvalue, whodidit)
id_change_records["[id_change_counter]"] = list("transferee" = transferee, "oldvalue" = oldvalue, "newvalue" = newvalue, "whodidit" = whodidit, "timestamp" = station_time_timestamp())
/datum/controller/subsystem/jobs/proc/log_job_transfer(transferee, oldvalue, newvalue, whodidit, reason)
id_change_records["[id_change_counter]"] = list(
"transferee" = transferee,
"oldvalue" = oldvalue,
"newvalue" = newvalue,
"whodidit" = whodidit,
"timestamp" = station_time_timestamp(),
"reason" = reason
)
id_change_counter++
/datum/controller/subsystem/jobs/proc/slot_job_transfer(oldtitle, newtitle)
@@ -663,45 +675,35 @@ SUBSYSTEM_DEF(jobs)
return
var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
if(PM && PM.can_receive())
PM.notify("<b>Automated Notification: </b>\"[antext]\" (Unable to Reply)")
PM.notify("<b>Automated Notification: </b>\"[antext]\" (Unable to Reply)", 0) // the 0 means don't make the PDA flash
/datum/controller/subsystem/jobs/proc/notify_by_name(target_name, antext)
// Used to notify a specific crew member based on their real_name
if(!target_name || !antext)
return
var/obj/item/pda/target_pda
for(var/obj/item/pda/check_pda in GLOB.PDAs)
if(check_pda.owner == target_name)
target_pda = check_pda
break
if(!target_pda)
return
var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
if(PM && PM.can_receive())
PM.notify("<b>Automated Notification: </b>\"[antext]\" (Unable to Reply)", 0) // the 0 means don't make the PDA flash
/datum/controller/subsystem/jobs/proc/fetch_transfer_record_html(var/centcom)
var/record_html = "<TABLE border=\"1\">"
var/table_headers = list("Crewman", "Old Rank", "New Rank", "Authorized By", "Time")
var/hidden_fields = list("deletedby")
if(centcom)
table_headers += "<span class='bad'>Deleted By</span>"
record_html += "<TR>"
for(var/thisheader in table_headers)
record_html += "<TD><B>[thisheader]</B></TD>"
record_html += "</TR>"
var/visible_record_count = 0
/datum/controller/subsystem/jobs/proc/format_job_change_records(centcom)
var/list/formatted = list()
for(var/thisid in id_change_records)
var/thisrecord = id_change_records[thisid]
if(thisrecord["deletedby"] && !centcom)
continue
record_html += "<TR>"
var/list/newlist = list()
for(var/lkey in thisrecord)
if(lkey in hidden_fields)
if(centcom)
record_html += "<TD><span class='bad'>[thisrecord[lkey]]<span></TD>"
else
continue
else
record_html += "<TD>[thisrecord[lkey]]</TD>"
record_html += "</TR>"
visible_record_count++
newlist[lkey] = thisrecord[lkey]
formatted.Add(list(newlist))
return formatted
record_html += "</TABLE>"
if(!visible_record_count)
return "No records on file yet."
return record_html
/datum/controller/subsystem/jobs/proc/delete_log_records(sourceuser, delete_all)
. = 0
+1 -1
View File
@@ -34,7 +34,7 @@ SUBSYSTEM_DEF(parallax)
for (A; isloc(A.loc) && !isturf(A.loc); A = A.loc);
if(A != C.movingmob)
if(C.movingmob != null)
if(C.movingmob != null && C.movingmob.client_mobs_in_contents)
C.movingmob.client_mobs_in_contents -= C.mob
UNSETEMPTY(C.movingmob.client_mobs_in_contents)
LAZYINITLIST(A.client_mobs_in_contents)
@@ -0,0 +1,86 @@
PROCESSING_SUBSYSTEM_DEF(instruments)
name = "Instruments"
init_order = INIT_ORDER_INSTRUMENTS
wait = 1
flags = SS_TICKER|SS_BACKGROUND|SS_KEEP_TIMING
offline_implications = "Instruments will no longer play. No immediate action is needed."
/// 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
else
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]"]
+15 -8
View File
@@ -14,9 +14,9 @@ SUBSYSTEM_DEF(shuttle)
//emergency shuttle stuff
var/obj/docking_port/mobile/emergency/emergency
var/obj/docking_port/mobile/emergency/backup/backup_shuttle
var/emergencyCallTime = 6000 //time taken for emergency shuttle to reach the station when called (in deciseconds)
var/emergencyDockTime = 1800 //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
var/emergencyCallTime = SHUTTLE_CALLTIME //time taken for emergency shuttle to reach the station when called (in deciseconds)
var/emergencyDockTime = SHUTTLE_DOCKTIME //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
var/emergencyEscapeTime = SHUTTLE_ESCAPETIME //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
var/emergency_sec_level_time = 0 // time sec level was last raised to red or higher
var/area/emergencyLastCallLoc
var/emergencyNoEscape
@@ -31,7 +31,7 @@ SUBSYSTEM_DEF(shuttle)
var/points_per_intel = 250 //points gained per intel returned
var/points_per_plasma = 5 //points gained per plasma returned
var/points_per_design = 25 //points gained per research design returned
var/centcom_message = "" //Remarks from Centcom on how well you checked the last order.
var/centcom_message = null //Remarks from Centcom on how well you checked the last order.
var/list/discoveredPlants = list() //Typepaths for unusual plants we've already sent CentComm, associated with their potencies
var/list/techLevels = list()
var/list/researchDesigns = list()
@@ -61,6 +61,8 @@ SUBSYSTEM_DEF(shuttle)
supply_packs["[P.type]"] = P
initial_move()
centcom_message = "<center>---[station_time_timestamp()]---</center><br>Remember to stamp and send back the supply manifests.<hr>"
return ..()
/datum/controller/subsystem/shuttle/stat_entry(msg)
@@ -93,6 +95,11 @@ SUBSYSTEM_DEF(shuttle)
return S
WARNING("couldn't find dock with id: [id]")
/datum/controller/subsystem/shuttle/proc/secondsToRefuel()
var/elapsed = world.time - SSticker.round_start_time
var/remaining = round((config.shuttle_refuel_delay - elapsed) / 10)
return remaining > 0 ? remaining : 0
/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
if(!emergency)
WARNING("requestEvac(): There is no emergency shuttle, but the shuttle was called. Using the backup shuttle instead.")
@@ -107,7 +114,7 @@ SUBSYSTEM_DEF(shuttle)
return
emergency = backup_shuttle
if(world.time - SSticker.round_start_time < config.shuttle_refuel_delay)
if(secondsToRefuel())
to_chat(user, "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - SSticker.round_start_time) - config.shuttle_refuel_delay)/600))] minutes before trying again.")
return
@@ -131,7 +138,7 @@ SUBSYSTEM_DEF(shuttle)
call_reason = trim(html_encode(call_reason))
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH)
to_chat(user, "You must provide a reason.")
to_chat(user, "Reason is too short. [CALL_SHUTTLE_REASON_LENGTH] character minimum.")
return
var/area/signal_origin = get_area(user)
@@ -192,7 +199,7 @@ SUBSYSTEM_DEF(shuttle)
var/obj/machinery/computer/communications/C = thing
if(C.stat & BROKEN)
continue
else if(istype(thing, /datum/computer_file/program/comm) || istype(thing, /obj/item/circuitboard/communications))
else if(istype(thing, /obj/item/circuitboard/communications))
continue
var/turf/T = get_turf(thing)
@@ -247,7 +254,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/proc/generateSupplyOrder(packId, _orderedby, _orderedbyRank, _comment, _crates)
if(!packId)
return
var/datum/supply_packs/P = supply_packs["[packId]"]
var/datum/supply_packs/P = locateUID(packId)
if(!P)
return
+165
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) // 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 with 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
+2
View File
@@ -126,6 +126,8 @@ SUBSYSTEM_DEF(tgui)
* return int The number of UIs closed.
**/
/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
if(!src_object.unique_datum_id) // First check if the datum has an UID set
return 0
var/src_object_key = "[src_object.UID()]"
if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
return 0 // Couldn't find any UIs for this object.
+2
View File
@@ -65,6 +65,8 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds")
current_state = GAME_STATE_PREGAME
fire() // TG says this is a good idea
for(var/mob/new_player/N in GLOB.player_list)
N.new_player_panel_proc() // to enable the observe option
if(GAME_STATE_PREGAME)
if(!SSticker.ticker_going) // This has to be referenced like this, and I dont know why. If you dont put SSticker. it will break
return
@@ -25,4 +25,4 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets
message_mentorTicket(msg)
/datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T)
SStickets.newTicket(T.clientName, T.content, T.title)
SStickets.newTicket(get_client_by_ckey(T.client_ckey), T.content, T.title)
+16 -13
View File
@@ -93,7 +93,7 @@ SUBSYSTEM_DEF(tickets)
var/datum/ticket/T = new(title, passedContent, getTicketCounterAndInc())
allTickets += T
T.clientName = C
T.client_ckey = C.ckey
T.locationSent = C.mob.loc.name
T.mobControlled = C.mob
@@ -139,14 +139,16 @@ SUBSYSTEM_DEF(tickets)
/datum/controller/subsystem/tickets/proc/convert_ticket(datum/ticket/T)
T.ticketState = TICKET_CLOSED
var/client/C = usr.client
to_chat_safe(T.clientName, list("<span class='[span_class]'>[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.</span>",\
var/client/owner = get_client_by_ckey(T.client_ckey)
to_chat_safe(owner, list("<span class='[span_class]'>[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.</span>",\
"<span class='[span_class]'>Be sure to use the correct type of help next time!</span>"))
message_staff("<span class='[span_class]'>[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.</span>")
log_game("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.")
create_other_system_ticket(T)
/datum/controller/subsystem/tickets/proc/create_other_system_ticket(datum/ticket/T)
SSmentor_tickets.newTicket(T.clientName, T.content, T.title)
var/client/C = get_client_by_ckey(T.client_ckey)
SSmentor_tickets.newTicket(C, T.content, T.title)
/datum/controller/subsystem/tickets/proc/autoRespond(N)
if(!check_rights(rights_needed))
@@ -177,7 +179,7 @@ SUBSYSTEM_DEF(tickets)
sorted_responses += key
var/message_key = input("Select an autoresponse. This will mark the ticket as resolved.", "Autoresponse") as null|anything in sortTim(sorted_responses, /proc/cmp_text_asc) //use sortTim and cmp_text_asc to sort alphabetically
var/client/ticket_owner = get_client_by_ckey(T.client_ckey)
switch(message_key)
if(null) //they cancelled
T.staffAssigned = initial(T.staffAssigned) //if they cancel we dont need to hold this ticket anymore
@@ -189,18 +191,18 @@ SUBSYSTEM_DEF(tickets)
C.man_up(returnClient(N))
T.lastStaffResponse = "Autoresponse: [message_key]"
resolveTicket(N)
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>")
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
message_staff("[C] has auto responded to [ticket_owner]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>")
log_game("[C] has auto responded to [ticket_owner]\'s adminhelp with: [response_phrases[message_key]]")
if("Mentorhelp")
convert_ticket(T)
else
var/msg_sound = sound('sound/effects/adminhelp.ogg')
SEND_SOUND(returnClient(N), msg_sound)
to_chat_safe(returnClient(N), "<span class='[span_class]'>[key_name_hidden(C)] is autoresponding with: <span/> <span class='adminticketalt'>[response_phrases[message_key]]</span>")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key]
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key
message_staff("[C] has auto responded to [ticket_owner]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key
T.lastStaffResponse = "Autoresponse: [message_key]"
resolveTicket(N)
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
log_game("[C] has auto responded to [ticket_owner]\'s adminhelp with: [response_phrases[message_key]]")
//Set ticket state with key N to closed
/datum/controller/subsystem/tickets/proc/closeTicket(N)
@@ -214,7 +216,7 @@ SUBSYSTEM_DEF(tickets)
//Check if the user already has a ticket open and within the cooldown period.
/datum/controller/subsystem/tickets/proc/checkForOpenTicket(client/C)
for(var/datum/ticket/T in allTickets)
if(T.clientName == C && T.ticketState == TICKET_OPEN && (T.ticketCooldown > world.time))
if(T.client_ckey == C.ckey && T.ticketState == TICKET_OPEN && (T.ticketCooldown > world.time))
return T
return FALSE
@@ -222,7 +224,7 @@ SUBSYSTEM_DEF(tickets)
/datum/controller/subsystem/tickets/proc/checkForTicket(client/C)
var/list/tickets = list()
for(var/datum/ticket/T in allTickets)
if(T.clientName == C && (T.ticketState == TICKET_OPEN || T.ticketState == TICKET_STALE))
if(T.client_ckey == C.ckey && (T.ticketState == TICKET_OPEN || T.ticketState == TICKET_STALE))
tickets += T
if(tickets.len)
return tickets
@@ -231,7 +233,7 @@ SUBSYSTEM_DEF(tickets)
//return the client of a ticket number
/datum/controller/subsystem/tickets/proc/returnClient(N)
var/datum/ticket/T = allTickets[N]
return T.clientName
return get_client_by_ckey(T.client_ckey)
/datum/controller/subsystem/tickets/proc/assignStaffToTicket(client/C, N)
var/datum/ticket/T = allTickets[N]
@@ -244,7 +246,8 @@ SUBSYSTEM_DEF(tickets)
/datum/ticket
var/ticketNum // Ticket number
var/clientName // Client which opened the ticket
/// ckey of the client who opened the ticket
var/client_ckey
var/timeOpened // Time the ticket was opened
var/title //The initial message with links
var/list/content // content of the staff help
@@ -379,7 +382,7 @@ UI STUFF
dat += "<h2>Ticket #[T.ticketNum]</h2>"
dat += "<h3>[T.clientName] / [T.mobControlled] opened this [ticket_name] at [T.timeOpened] at location [T.locationSent]</h3>"
dat += "<h3>[T.client_ckey] / [T.mobControlled] opened this [ticket_name] at [T.timeOpened] at location [T.locationSent]</h3>"
dat += "<h4>Ticket Status: <font color='red'>[status]</font>"
dat += "<table style='width:950px; border: 3px solid;'>"
dat += "<tr><td>[T.title]</td></tr>"
+1 -4
View File
@@ -20,7 +20,7 @@
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
/client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI",
"Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires",
"Cameras", "Garbage", "Event", "Nano", "Vote", "Fires",
"Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input"))
set category = "Debug"
set name = "Debug Controller"
@@ -65,9 +65,6 @@
if("Event")
debug_variables(SSevents)
feedback_add_details("admin_verb","DEvent")
if("Alarm")
debug_variables(SSalarms)
feedback_add_details("admin_verb", "DAlarm")
if("Nano")
debug_variables(SSnanoui)
feedback_add_details("admin_verb","DNano")