diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 0e0bd4ffaa..3739ce006e 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -69,3 +69,7 @@ #ifdef TRAVISTESTING #define TESTING #endif + +// A reasonable number of maximum overlays an object needs +// If you think you need more, rethink it +#define MAX_ATOM_OVERLAYS 100 diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 9050bcb5f0..eaa8214b7d 100755 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -287,6 +287,21 @@ speed = 1.4 layer = 3 +/obj/screen/parallax_layer/random + blend_mode = BLEND_OVERLAY + speed = 3 + layer = 3 + +/obj/screen/parallax_layer/random/space_gas + icon_state = "space_gas" + +/obj/screen/parallax_layer/random/space_gas/Initialize(mapload, view) + . = ..() + src.add_atom_colour(SSparallax.random_parallax_color, ADMIN_COLOUR_PRIORITY) + +/obj/screen/parallax_layer/random/asteroids + icon_state = "asteroids" + /obj/screen/parallax_layer/planet icon_state = "planet" blend_mode = BLEND_OVERLAY @@ -295,11 +310,11 @@ layer = 30 /obj/screen/parallax_layer/planet/update_status(mob/M) - var/turf/T = get_turf(M) - if(is_station_level(T.z)) - invisibility = 0 - else - invisibility = INVISIBILITY_ABSTRACT + var/client/C = M.client + var/turf/posobj = get_turf(C.eye) + if(!posobj) + return + invisibility = is_station_level(posobj.z) ? 0 : INVISIBILITY_ABSTRACT /obj/screen/parallax_layer/planet/update_o() - return //Shit wont move + return //Shit won't move diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index b42a1e6b7e..74b60783c3 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -48,9 +48,16 @@ SUBSYSTEM_DEF(overlays) for (var/thing in queue) count++ if(thing) - STAT_START_STOPWATCH var/atom/A = thing + if(A.overlays.len >= MAX_ATOM_OVERLAYS) + //Break it real GOOD + stack_trace("Too many overlays on [A.type] - [A.overlays.len], refusing to update and cutting") + A.overlays.Cut() + continue + STAT_START_STOPWATCH COMPILE_OVERLAYS(A) + UNSETEMPTY(A.add_overlays) + UNSETEMPTY(A.remove_overlays) STAT_STOP_STOPWATCH STAT_LOG_ENTRY(stats, A.type) if(mc_check) @@ -117,9 +124,8 @@ SUBSYSTEM_DEF(overlays) #define QUEUE_FOR_COMPILE flags_1 |= OVERLAY_QUEUED_1; SSoverlays.queue += src; /atom/proc/cut_overlays() LAZYINITLIST(remove_overlays) - LAZYINITLIST(add_overlays) remove_overlays = overlays.Copy() - add_overlays.Cut() + add_overlays = null //If not already queued for work and there are overlays to remove if(NOT_QUEUED_ALREADY && remove_overlays.len) @@ -129,7 +135,7 @@ SUBSYSTEM_DEF(overlays) if(!overlays) return overlays = build_appearance_list(overlays) - LAZYINITLIST(add_overlays) //always initialized after this point + LAZYINITLIST(add_overlays) LAZYINITLIST(remove_overlays) var/a_len = add_overlays.len var/r_len = remove_overlays.len @@ -140,8 +146,9 @@ SUBSYSTEM_DEF(overlays) var/fr_len = remove_overlays.len //If not already queued and there is work to be done - if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len)) + if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len )) QUEUE_FOR_COMPILE + UNSETEMPTY(add_overlays) /atom/proc/add_overlay(list/overlays) if(!overlays) diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm index 64299fda38..7acb779dcc 100644 --- a/code/controllers/subsystem/parallax.dm +++ b/code/controllers/subsystem/parallax.dm @@ -7,13 +7,21 @@ SUBSYSTEM_DEF(parallax) var/list/currentrun var/planet_x_offset = 128 var/planet_y_offset = 128 + var/random_layer + var/random_parallax_color -/datum/controller/subsystem/parallax/Initialize(timeofday) + +//These are cached per client so needs to be done asap so people joining at roundstart do not miss these. +/datum/controller/subsystem/parallax/PreInit() . = ..() + if(prob(70)) //70% chance to pick a special extra layer + random_layer = pick(/obj/screen/parallax_layer/random/space_gas, /obj/screen/parallax_layer/random/asteroids) + random_parallax_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_SILVER, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)//Special color for random_layer1. Has to be done here so everyone sees the same color. planet_y_offset = rand(100, 160) planet_x_offset = rand(100, 160) -/datum/controller/subsystem/parallax/fire(resumed = 0) + +/datum/controller/subsystem/parallax/fire(resumed = FALSE) if (!resumed) src.currentrun = GLOB.clients.Copy() @@ -21,24 +29,27 @@ SUBSYSTEM_DEF(parallax) var/list/currentrun = src.currentrun while(length(currentrun)) - var/client/C = currentrun[currentrun.len] + var/client/processing_client = currentrun[currentrun.len] currentrun.len-- - if (!C || !C.eye) + if (QDELETED(processing_client) || !processing_client.eye) if (MC_TICK_CHECK) return continue - var/atom/movable/A = C.eye - if(!istype(A)) - continue - for (A; isloc(A.loc) && !isturf(A.loc); A = A.loc); - if(A != C.movingmob) - if(C.movingmob != null) - C.movingmob.client_mobs_in_contents -= C.mob - UNSETEMPTY(C.movingmob.client_mobs_in_contents) - LAZYINITLIST(A.client_mobs_in_contents) - A.client_mobs_in_contents += C.mob - C.movingmob = A + var/atom/movable/movable_eye = processing_client.eye + if(!istype(movable_eye)) + continue + + for (movable_eye; isloc(movable_eye.loc) && !isturf(movable_eye.loc); movable_eye = movable_eye.loc); + + if(movable_eye == processing_client.movingmob) + if (MC_TICK_CHECK) + return + continue + if(!isnull(processing_client.movingmob)) + LAZYREMOVE(processing_client.movingmob.client_mobs_in_contents, processing_client.mob) + LAZYADD(movable_eye.client_mobs_in_contents, processing_client.mob) + processing_client.movingmob = movable_eye if (MC_TICK_CHECK) return currentrun = null diff --git a/code/controllers/subsystem/pathfinder.dm b/code/controllers/subsystem/pathfinder.dm index 8e1cf946ae..ccbea79306 100644 --- a/code/controllers/subsystem/pathfinder.dm +++ b/code/controllers/subsystem/pathfinder.dm @@ -18,7 +18,7 @@ SUBSYSTEM_DEF(pathfinder) var/free var/list/flow -/datum/flowcache/New(var/n) +/datum/flowcache/New(n) . = ..() lcount = n run = 0 diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index e39242aac3..4fcc7828d5 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -43,6 +43,7 @@ SUBSYSTEM_DEF(persistence) LoadAntagReputation() LoadRandomizedRecipes() LoadPanicBunker() + LoadPaintings() return ..() /datum/controller/subsystem/persistence/proc/LoadSatchels() diff --git a/code/controllers/subsystem/profiler.dm b/code/controllers/subsystem/profiler.dm index 81fa77dc6c..7533e9663f 100644 --- a/code/controllers/subsystem/profiler.dm +++ b/code/controllers/subsystem/profiler.dm @@ -18,7 +18,7 @@ SUBSYSTEM_DEF(profiler) if(CONFIG_GET(flag/auto_profile)) StartProfiling() else - StopProfiling() //Stop the early start from world/New + StopProfiling() //Stop the early start profiler return ..() /datum/controller/subsystem/profiler/fire() @@ -31,12 +31,23 @@ SUBSYSTEM_DEF(profiler) return ..() /datum/controller/subsystem/profiler/proc/StartProfiling() +#if DM_BUILD < 1506 + stack_trace("Auto profiling unsupported on this byond version") + CONFIG_SET(flag/auto_profile, FALSE) +#else world.Profile(PROFILE_START) +#endif /datum/controller/subsystem/profiler/proc/StopProfiling() +#if DM_BUILD >= 1506 world.Profile(PROFILE_STOP) +#endif /datum/controller/subsystem/profiler/proc/DumpFile() +#if DM_BUILD < 1506 + stack_trace("Auto profiling unsupported on this byond version") + CONFIG_SET(flag/auto_profile, FALSE) +#else var/timer = TICK_USAGE_REAL var/current_profile_data = world.Profile(PROFILE_REFRESH,format="json") fetch_cost = MC_AVERAGE(fetch_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) @@ -49,3 +60,4 @@ SUBSYSTEM_DEF(profiler) timer = TICK_USAGE_REAL WRITE_FILE(json_file, current_profile_data) write_cost = MC_AVERAGE(write_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) +#endif diff --git a/code/controllers/subsystem/radiation.dm b/code/controllers/subsystem/radiation.dm index f29fe72e80..2d764d84dd 100644 --- a/code/controllers/subsystem/radiation.dm +++ b/code/controllers/subsystem/radiation.dm @@ -1,6 +1,7 @@ PROCESSING_SUBSYSTEM_DEF(radiation) name = "Radiation" flags = SS_NO_INIT | SS_BACKGROUND + wait = 1 SECONDS var/list/warned_atoms = list() @@ -13,5 +14,5 @@ PROCESSING_SUBSYSTEM_DEF(radiation) warned_atoms[ref] = TRUE var/atom/master = contamination.parent SSblackbox.record_feedback("tally", "contaminated", 1, master.type) - var/msg = "has become contamintaed with enough radiation to contaminate other objects. || Source: [contamination.source] || Strength: [contamination.strength]" + var/msg = "has become contaminated with enough radiation to contaminate other objects. || Source: [contamination.source] || Strength: [contamination.strength]" master.investigate_log(msg, INVESTIGATE_RADIATION) diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm index 5e7c5e6545..fa9ba3c472 100644 --- a/code/controllers/subsystem/sounds.dm +++ b/code/controllers/subsystem/sounds.dm @@ -5,34 +5,48 @@ SUBSYSTEM_DEF(sounds) flags = SS_NO_FIRE init_order = INIT_ORDER_SOUNDS var/static/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/static/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 = list() + /// 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 = list() - /// List of all available channels with associations set to TRUE for fast lookups/allocation. - var/list/available_channels + 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 ..() /datum/controller/subsystem/sounds/proc/setup_available_channels() - available_channels = list() + channel_list = list() + reserved_channels = list() + using_channels = list() + using_channels_by_datum = list() for(var/i in 1 to using_channels_max) - available_channels[num2text(i)] = TRUE + channel_list += i + channel_random_low = 1 + channel_reserve_high = length(channel_list) /// Removes a channel from using list. /datum/controller/subsystem/sounds/proc/free_sound_channel(channel) - channel = num2text(channel) - var/using = using_channels[channel] - using_channels -= channel - if(using) + 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 - available_channels[channel] = TRUE + free_channel(channel) /// Frees all the channels a datum is using. /datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D) @@ -40,8 +54,8 @@ SUBSYSTEM_DEF(sounds) if(!L) return for(var/channel in L) - using_channels -= channel - available_channels[channel] = TRUE + using_channels -= num2text(channel) + free_channel(channel) using_channels_by_datum -= D /// Frees all datumless channels @@ -50,42 +64,72 @@ SUBSYSTEM_DEF(sounds) /// 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() - var/channel = random_available_channel_text() - if(!channel) //oh no.. + . = reserve_channel() + if(!.) //oh no.. return FALSE - available_channels -= channel - using_channels[channel] = DATUMLESS + var/text_channel = num2text(.) + using_channels[text_channel] = DATUMLESS LAZYINITLIST(using_channels_by_datum[DATUMLESS]) - using_channels_by_datum[DATUMLESS] += channel - return text2num(channel) + using_channels_by_datum[DATUMLESS] += . /// Reserves a channel for a datum. Automatic cleanup only when the datum is deleted. Returns an integer for channel. /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.") - var/channel = random_available_channel_text() - if(!channel) + .= reserve_channel() + if(!.) return FALSE - available_channels -= channel - using_channels[channel] = D + var/text_channel = num2text(.) + using_channels[text_channel] = D LAZYINITLIST(using_channels_by_datum[D]) - using_channels_by_datum[D] += channel - return text2num(channel) + 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() - return pick(available_channels) + 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() - return text2num(pick(available_channels)) - -/// If a channel is available -/datum/controller/subsystem/sounds/proc/is_channel_available(channel) - return available_channels[num2text(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(available_channels) + return length(channel_list) - random_channels_min #undef DATUMLESS diff --git a/code/controllers/subsystem/spacedrift.dm b/code/controllers/subsystem/spacedrift.dm index c3261df304..c1c7e2a872 100644 --- a/code/controllers/subsystem/spacedrift.dm +++ b/code/controllers/subsystem/spacedrift.dm @@ -13,7 +13,7 @@ SUBSYSTEM_DEF(spacedrift) return ..() -/datum/controller/subsystem/spacedrift/fire(resumed = 0) +/datum/controller/subsystem/spacedrift/fire(resumed = FALSE) if (!resumed) src.currentrun = processing.Copy() @@ -47,6 +47,7 @@ SUBSYSTEM_DEF(spacedrift) var/old_dir = AM.dir var/old_loc = AM.loc AM.inertia_moving = TRUE + AM.set_glide_size(DELAY_TO_GLIDE_SIZE(AM.inertia_move_delay)) step(AM, AM.inertia_dir) AM.inertia_moving = FALSE AM.inertia_next_move = world.time + AM.inertia_move_delay diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index 13e9ff50a2..d0d5579611 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -203,3 +203,4 @@ SUBSYSTEM_DEF(statpanels) set hidden = TRUE statbrowser_ready = TRUE + init_verbs()