TGUI update (from letterN branch) (#7244)

Co-authored-by: LetterN <24603524+LetterN@users.noreply.github.com>
This commit is contained in:
silicons
2025-10-04 01:45:41 -07:00
committed by GitHub
parent 49ad66297d
commit 10d2505338
1248 changed files with 28935 additions and 96122 deletions

View File

@@ -1,3 +1,5 @@
root = true
[*]
indent_style = tab
indent_size = 4

3
.vscode/launch.json vendored
View File

@@ -14,7 +14,8 @@
"request": "launch",
"name": "Launch DreamSeeker",
"preLaunchTask": "Build All",
"dmb": "${workspaceFolder}/${command:CurrentDMB}"
"dmb": "${workspaceFolder}/${command:CurrentDMB}",
"dreamDaemon": false
},
{
"type": "opendream",

22
.vscode/settings.json vendored
View File

@@ -1,8 +1,7 @@
{
"eslint.nodePath": "./tgui/.yarn/sdks",
"eslint.workingDirectories": [
"./tgui"
],
"eslint.workingDirectories": ["./tgui"],
"prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.cjs",
"typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.tsserver.experimental.enableProjectDiagnostics": true,
@@ -15,15 +14,20 @@
},
"files.eol": "\n",
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"editor.insertSpaces": false,
"git.branchProtection": ["master"],
"gitlens.advanced.blame.customArguments": ["-w"],
"tgstationTestExplorer.project.resultsType": "json",
"[javascript]": {
"editor.rulers": [80]
"[html][scss][css][json][jsonc][markdown][yaml]": {
"editor.rulers": [80],
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[typescript]": {
"editor.rulers": [80]
"workbench.editorAssociations": {
"*.dmi": "dmiEditor.dmiEditor"
},
"[scss]": {
"editor.rulers": [80]
"[javascript][typescript][typescriptreact][javascriptreact]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
}

View File

@@ -51,6 +51,7 @@
#include "code\__DEFINES\damage_organs.dm"
#include "code\__DEFINES\directional.dm"
#include "code\__DEFINES\dna.dm"
#include "code\__DEFINES\font_awesome_icons.dm"
#include "code\__DEFINES\fonts.dm"
#include "code\__DEFINES\frames.dm"
#include "code\__DEFINES\gamemode.dm"
@@ -405,6 +406,7 @@
#include "code\__HELPERS\heap.dm"
#include "code\__HELPERS\icon_smoothing.dm"
#include "code\__HELPERS\icons.dm"
#include "code\__HELPERS\maths.dm"
#include "code\__HELPERS\mobs.dm"
#include "code\__HELPERS\names.dm"
#include "code\__HELPERS\pass.dm"
@@ -730,6 +732,7 @@
#include "code\datums\category.dm"
#include "code\datums\changelog.dm"
#include "code\datums\character_profile.dm"
#include "code\datums\chat_payload.dm"
#include "code\datums\computerfiles.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datum-filters.dm"
@@ -1144,6 +1147,7 @@
#include "code\game\click\click-observer.dm"
#include "code\game\click\click-silicon-ai.dm"
#include "code\game\click\click-silicon-legacy.dm"
#include "code\game\click\click-silicon-pai.dm"
#include "code\game\click\click-silicon-robot.dm"
#include "code\game\click\click-silicon.dm"
#include "code\game\click\click.dm"
@@ -2485,6 +2489,7 @@
#include "code\modules\asset_cache\assets\fontawesome.dm"
#include "code\modules\asset_cache\assets\genetics.dm"
#include "code\modules\asset_cache\assets\headers.dm"
#include "code\modules\asset_cache\assets\icon_ref_map.dm"
#include "code\modules\asset_cache\assets\inventory.dm"
#include "code\modules\asset_cache\assets\jquery.dm"
#include "code\modules\asset_cache\assets\legacy_nanomaps.dm"
@@ -4410,7 +4415,6 @@
#include "code\modules\modular_computers\file_system\news_article.dm"
#include "code\modules\modular_computers\file_system\program.dm"
#include "code\modules\modular_computers\file_system\program_events.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\access_decrypter.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\hacked_camera.dm"
#include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm"
@@ -5396,20 +5400,14 @@
#include "code\modules\tgchat\to_chat.dm"
#include "code\modules\tgs\includes.dm"
#include "code\modules\tgs_impl\tgs_event_handler.dm"
#include "code\modules\tgui\client.dm"
#include "code\modules\tgui\datum.dm"
#include "code\modules\tgui\mob.dm"
#include "code\modules\tgui\module.dm"
#include "code\modules\tgui\states.dm"
#include "code\modules\tgui\status_composers.dm"
#include "code\modules\tgui\tgui-ext-client.dm"
#include "code\modules\tgui\tgui-ext-datum.dm"
#include "code\modules\tgui\tgui-ext-mob.dm"
#include "code\modules\tgui\tgui.dm"
#include "code\modules\tgui\tgui_window.dm"
#include "code\modules\tgui\legacy\modal_vr_legacy.dm"
#include "code\modules\tgui\modals\tgui_alert.dm"
#include "code\modules\tgui\modals\tgui_dynamic_input.dm"
#include "code\modules\tgui\modals\tgui_input_list.dm"
#include "code\modules\tgui\modals\tgui_input_number.dm"
#include "code\modules\tgui\modals\tgui_input_text.dm"
#include "code\modules\tgui\modules\_base.dm"
#include "code\modules\tgui\modules\alarm.dm"
#include "code\modules\tgui\modules\atmos_control.dm"
@@ -5447,6 +5445,13 @@
#include "code\modules\tgui\states\reverse_contained.dm"
#include "code\modules\tgui\states\self.dm"
#include "code\modules\tgui\states\zlevel.dm"
#include "code\modules\tgui_extensions\module.dm"
#include "code\modules\tgui_input\tgui_alert.dm"
#include "code\modules\tgui_input\tgui_dynamic_input.dm"
#include "code\modules\tgui_input\tgui_input_checkboxes.dm"
#include "code\modules\tgui_input\tgui_input_list.dm"
#include "code\modules\tgui_input\tgui_input_number.dm"
#include "code\modules\tgui_input\tgui_input_text.dm"
#include "code\modules\tgui_panel\audio.dm"
#include "code\modules\tgui_panel\external.dm"
#include "code\modules\tgui_panel\telemetry.dm"

View File

@@ -1,27 +1,62 @@
/*
* Holds procs to help with list operations
*/
/// Picks from the list, with some safeties, and returns the "default" arg if it fails
#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
/// Ensures L is initailized after this point
#define LAZYINITLIST(L) if (isnull(L)) L = list()
/// Ensures L is initialized and uses it as a rvalue
#define LAZYGETLIST(L) (isnull(L)? (L = list()) : L)
/// Sets a L back to null iff it is empty
/*
* ## Lazylists
*
* * What is a lazylist?
*
* True to its name a lazylist is a lazy instantiated list.
* It is a list that is only created when necessary (when it has elements) and is null when empty.
*
* * Why use a lazylist?
*
* Lazylists save memory - an empty list that is never used takes up more memory than just `null`.
*
* * When to use a lazylist?
*
* Lazylists are best used on hot types when making lists that are not always used.
*
* For example, if you were adding a list to all atoms that tracks the names of people who touched it,
* you would want to use a lazylist because most atoms will never be touched by anyone.
*
* * How do I use a lazylist?
*
* A lazylist is just a list you defined as `null` rather than `list()`.
* Then, you use the LAZY* macros to interact with it, which are essentially null-safe ways to interact with a list.
*
* Note that you probably should not be using these macros if your list is not a lazylist.
* This will obfuscate the code and make it a bit harder to read and debug.
*
* Generally speaking you shouldn't be checking if your lazylist is `null` yourself, the macros will do that for you.
* Remember that LAZYLEN (and by extension, length) will return 0 if the list is null.
*/
///Initialize the lazylist
#define LAZYINITLIST(L) if (!L) { L = list(); }
///If the provided list is empty, set it to null
#define UNSETEMPTY(L) if (L && !length(L)) L = null
/// Removes I from list L, and sets I to null if it is now empty
///If the provided key -> list is empty, remove it from the list
#define ASSOC_UNSETEMPTY(L, K) if (!length(L[K])) L -= K;
///Like LAZYCOPY - copies an input list if the list has entries, If it doesn't the assigned list is nulled
#define LAZYLISTDUPLICATE(L) (L ? L.Copy() : null )
///Remove an item from the list, set the list to null if empty
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
/// Adds I to L, initalizing L if necessary
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
/// Adds I to L, initalizing L if necessary, if I is not already in L
///Add an item to the list if not already present, if the list is null it will initialize it
#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I;
/// Calls L.Find(V) if L exists, otherwise evals to 0.
///Returns the key of the submitted item in the list
#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
/// Reads I from L safely - Works with both associative and traditional lists.
///returns L[I] if L exists and I is a valid index of L, runtimes if L is not a list
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
/// Turns LAZYINITLIST(L) L[K] = V into ... for associated lists
///Sets the item K to the value V, if the list is null it will initialize it
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
/// Reads the length of L, returning 0 if null
///Sets the length of a lazylist
#define LAZYSETLEN(L, V) if (!L) { L = list(); } L.len = V;
///Returns the length of the list
#define LAZYLEN(L) length(L)
///Sets a list to null
#define LAZYNULL(L) L = null
@@ -33,6 +68,8 @@
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }
///Accesses an associative list, returns null if nothing is found
#define LAZYACCESSASSOC(L, I, K) L ? L[I] ? L[I][K] ? L[I][K] : null : null : null
///Qdel every item in the list before setting the list to null
#define QDEL_LAZYLIST(L) for(var/I in L) qdel(I); L = null;
//These methods don't null the list
///Use LAZYLISTDUPLICATE instead if you want it to null with no entries
#define LAZYCOPY(L) (L ? L.Copy() : list() )
@@ -40,12 +77,33 @@
#define LAZYCLEARLIST(L) if(L) L.Cut()
///Returns the list if it's actually a valid list, otherwise will initialize it
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
#define SANITIZE_TO_LIST(L) ( islist(L) ? L : list(L) )
/// Performs an insertion on the given lazy list with the given key and value. If the value already exists, a new one will not be made.
#define LAZYORASSOCLIST(lazy_list, key, value) \
#define LAZYDISTINCTADDASSOC(lazy_list, key, value) \
LAZYINITLIST(lazy_list); \
LAZYINITLIST(lazy_list[key]); \
lazy_list[key] |= value;
/// Calls Insert on the lazy list if it exists, otherwise initializes it with the value
#define LAZYINSERT(lazylist, index, value) \
if (!lazylist) { \
lazylist = list(value); \
} else if (index == 0 && index > length(lazylist)) { \
lazylist += value; \
} else { \
lazylist.Insert(index, value); \
}
///Ensures the length of a list is at least I, prefilling it with V if needed. if V is a proc call, it is repeated for each new index so that list() can just make a new list for each item.
#define LISTASSERTLEN(L, I, V...) \
if (length(L) < I) { \
var/_OLD_LENGTH = length(L); \
L.len = I; \
/* Convert the optional argument to a if check */ \
for (var/_USELESS_VAR in list(V)) { \
for (var/_INDEX_TO_ASSIGN_TO in _OLD_LENGTH+1 to I) { \
L[_INDEX_TO_ASSIGN_TO] = V; \
} \
} \
}
#define reverseList(L) reverseRange(L.Copy())

View File

@@ -1,20 +1,26 @@
/**
*! Copyright (c) 2020 Aleksej Komarov
*! SPDX-License-Identifier: MIT
/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
/// How many chat payloads to keep in history
#define CHAT_RELIABILITY_HISTORY_SIZE 5
/// How many resends to allow before giving up
#define CHAT_RELIABILITY_MAX_RESENDS 3
//! ## Message Types
#define MESSAGE_TYPE_SYSTEM "system"
#define MESSAGE_TYPE_LOCALCHAT "localchat"
#define MESSAGE_TYPE_RADIO "radio"
#define MESSAGE_TYPE_ENTERTAINMENT "entertainment"
#define MESSAGE_TYPE_INFO "info"
#define MESSAGE_TYPE_WARNING "warning"
#define MESSAGE_TYPE_HELPFUL "helpful"
#define MESSAGE_TYPE_DEADCHAT "deadchat"
#define MESSAGE_TYPE_OOC "ooc"
#define MESSAGE_TYPE_ADMINPM "adminpm"
#define MESSAGE_TYPE_COMBAT "combat"
#define MESSAGE_TYPE_ADMINCHAT "adminchat"
#define MESSAGE_TYPE_PRAYER "prayer"
#define MESSAGE_TYPE_MODCHAT "modchat"
#define MESSAGE_TYPE_EVENTCHAT "eventchat"
#define MESSAGE_TYPE_ADMINLOG "adminlog"
@@ -28,7 +34,7 @@
text = "DEBUG: [msg]")
/// Used for debug messages to the player
#define debug_usr(msg) if (GLOB.Debug2&&usr) to_chat(usr, \
#define debug_usr(msg) if (GLOB.Debug2 && usr) to_chat(usr, \
type = MESSAGE_TYPE_DEBUG, \
text = "DEBUG: [msg]")
@@ -39,3 +45,15 @@
/// Used for debug messages to the server
#define debug_world_log(msg) if (GLOB.Debug2) log_world("DEBUG: [msg]")
/// Adds a generic box around whatever message you're sending in chat. Really makes things stand out.
#define boxed_message(str) ("<div class='boxed_message'>" + str + "</div>")
/// Adds a box around whatever message you're sending in chat. Can apply color and/or additional classes. Available colors: red, green, blue, purple. Use it like red_box
#define custom_boxed_message(classes, str) ("<div class='boxed_message " + classes + "'>" + str + "</div>")
/// Makes a fieldset with a neaty styled name. Can apply additional classes.
#define fieldset_block(title, content, classes) ("<fieldset class='fieldset " + classes + "'><legend class='fieldset_legend'>" + title + "</legend>" + content + "</fieldset>")
/// Makes a horizontal line with text in the middle
#define separator_hr(str) ("<div class='separator'>" + str + "</div>")
/// Emboldens runechat messages
#define RUNECHAT_BOLD(str) "+[str]+"
/// Helper which creates a chat message which may have a tooltip in some contexts, but not others.
#define conditional_tooltip(normal_text, tooltip_text, condition) ((condition) ? (SPAN_TOOLTIP(tooltip_text, normal_text)) : (normal_text))

File diff suppressed because it is too large Load Diff

View File

@@ -251,3 +251,6 @@
/// get highest magnitude of two numbers, magnitude is abs value
#define BIGGER_MAGNITUDE(a, b) ((abs(a) > abs(b))? a : b)
#define SI_COEFFICIENT "coefficient"
#define SI_UNIT "unit"

View File

@@ -33,6 +33,16 @@
/// Window is free and ready to receive data
#define TGUI_WINDOW_READY 2
/// Though not the maximum renderable ByondUis within tgui, this is the maximum that the server will manage per-UI
#define TGUI_MANAGED_BYONDUI_LIMIT 10
// These are defines instead of being inline, as they're being sent over
// from tgui-core, so can't be easily played with
#define TGUI_MANAGED_BYONDUI_TYPE_RENDER "renderByondUi"
#define TGUI_MANAGED_BYONDUI_TYPE_UNMOUNT "unmountByondUi"
#define TGUI_MANAGED_BYONDUI_PAYLOAD_ID "renderByondUi"
/// Get a window id based on the provided pool index
#define TGUI_WINDOW_ID(index) "tgui-window-[index]"
/// Get a pool index of the provided window id
@@ -46,6 +56,14 @@
"%7b%22type%22%3a%22[type]%22%2c%22payload%22%3a[url_encode(json_encode(payload))]%7d" \
)
/**
* Gets a ui_state that checks to see if the user has specific admin permissions.
*
* Arguments:
* * required_perms: Which admin permission flags to check the user for, such as [R_ADMIN]
*/
#define ADMIN_STATE(required_perms) (GLOB.admin_states["[required_perms]"] ||= new /datum/ui_state/admin_state(required_perms))
//* Legacy Modal Stuff
/// Max length for Modal Input

View File

@@ -485,6 +485,68 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
var/list/partial = splittext(iconData, "{")
return replacetext(copytext_char(partial[2], 3, -5), "\n", "") //if cleanup fails we want to still return the correct base64
///given a text string, returns whether it is a valid dmi icons folder path
/proc/is_valid_dmi_file(icon_path)
if(!istext(icon_path) || !length(icon_path))
return FALSE
var/is_in_icon_folder = findtextEx(icon_path, "icons/")
var/is_dmi_file = findtextEx(icon_path, ".dmi")
if(is_in_icon_folder && is_dmi_file)
return TRUE
return FALSE
/// given an icon object, dmi file path, or atom/image/mutable_appearance, attempts to find and return an associated dmi file path.
/// a weird quirk about dm is that /icon objects represent both compile-time or dynamic icons in the rsc,
/// but stringifying rsc references returns a dmi file path
/// ONLY if that icon represents a completely unchanged dmi file from when the game was compiled.
/// so if the given object is associated with an icon that was in the rsc when the game was compiled, this returns a path. otherwise it returns ""
/proc/get_icon_dmi_path(icon/icon)
/// the dmi file path we attempt to return if the given object argument is associated with a stringifiable icon
/// if successful, this looks like "icons/path/to/dmi_file.dmi"
var/icon_path = ""
if(isatom(icon) || istype(icon, /image) || istype(icon, /mutable_appearance))
var/atom/atom_icon = icon
icon = atom_icon.icon
//atom icons compiled in from 'icons/path/to/dmi_file.dmi' are weird and not really icon objects that you generate with icon().
//if they're unchanged dmi's then they're stringifiable to "icons/path/to/dmi_file.dmi"
if(isicon(icon) && isfile(icon))
//icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and aren't really /icon objects,
///but they pass both isicon() and isfile() checks. they're the easiest case since stringifying them gives us the path we want
var/icon_ref = ref(icon)
var/locate_icon_string = "[locate(icon_ref)]"
icon_path = locate_icon_string
else if(isicon(icon) && "[icon]" == "/icon")
// icon objects generated from icon() at runtime are icons, but they AREN'T files themselves, they represent icon files.
// if the files they represent are compile time dmi files in the rsc, then
// the rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi"
var/rsc_ref = fcopy_rsc(icon)
var/icon_ref = ref(rsc_ref)
var/icon_path_string = "[locate(icon_ref)]"
icon_path = icon_path_string
else if(istext(icon))
var/rsc_ref = fcopy_rsc(icon)
//if its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path
var/rsc_ref_ref = ref(rsc_ref)
var/rsc_ref_string = "[locate(rsc_ref_ref)]"
icon_path = rsc_ref_string
if(is_valid_dmi_file(icon_path))
return icon_path
return FALSE
/**
* generate an asset for the given icon or the icon of the given appearance for [thing], and send it to any clients in target.
* Arguments:

66
code/__HELPERS/maths.dm Normal file
View File

@@ -0,0 +1,66 @@
/**
* Formats a number into a list representing the si unit.
* Access the coefficient with [SI_COEFFICIENT], and access the unit with [SI_UNIT].
*
* Supports SI exponents between 1e-15 to 1e15, but properly handles numbers outside that range as well.
* Arguments:
* * value - The number to convert to text. Can be positive or negative.
* * unit - The base unit of the number, such as "Pa" or "W".
* * maxdecimals - Maximum amount of decimals to display for the final number. Defaults to 1.
* Returns: [SI_COEFFICIENT = si unit coefficient, SI_UNIT = prefixed si unit.]
*/
/proc/siunit_isolated(value, unit, maxdecimals=1)
var/static/list/prefixes = list("q","r","y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y","R","Q")
// We don't have prefixes beyond this point
// and this also captures value = 0 which you can't compute the logarithm for
// and also byond numbers are floats and doesn't have much precision beyond this point anyway
if(abs(value) < 1e-30)
. = list(SI_COEFFICIENT = 0, SI_UNIT = " [unit]")
return
var/exponent = clamp(log(10, abs(value)), -30, 30) // Calculate the exponent and clamp it so we don't go outside the prefix list bounds
var/divider = 10 ** (round(exponent / 3) * 3) // Rounds the exponent to nearest SI unit and power it back to the full form
var/coefficient = round(value / divider, 10 ** -maxdecimals) // Calculate the coefficient and round it to desired decimals
var/prefix_index = round(exponent / 3) + 11 // Calculate the index in the prefixes list for this exponent
// An edge case which happens if we round 999.9 to 0 decimals for example, which gets rounded to 1000
// In that case, we manually swap up to the next prefix if there is one available
if(coefficient >= 1000 && prefix_index < 21)
coefficient /= 1e3
prefix_index++
var/prefix = prefixes[prefix_index]
. = list(SI_COEFFICIENT = coefficient, SI_UNIT = " [prefix][unit]")
/**Format a power value in prefixed watts.
* Converts from energy if convert is true.
* Args:
* - power: The value of power to format.
* - convert: Whether to convert this from joules.
* - datum/controller/subsystem/scheduler: used in the conversion
* Returns: The string containing the formatted power.
*/
/proc/display_power(power, convert = TRUE, datum/controller/subsystem/scheduler = SSmachines)
power = convert ? energy_to_power(power, scheduler) : power
return siunit(power, "W", 3)
/**
* Format an energy value in prefixed joules.
* Arguments
*
* * units - the value t convert
*/
/proc/display_energy(units)
return siunit(units, "J", 3)
/**
* Converts the joule to the watt, assuming SSmachines tick rate.
* Arguments
*
* * joules - the value in joules to convert
* * datum/controller/subsystem/scheduler - the subsystem whos wait time is used in the conversion
*/
/proc/energy_to_power(joules, datum/controller/subsystem/scheduler = SSmachines)
return joules * (1 SECONDS) / scheduler.wait

View File

@@ -1,9 +1,9 @@
/**
* probably, because the matrix isn't sanitized for numbers, just that it's the right length of list.
*/
/proc/sanitize_probably_a_byond_color(what, default = "#ffffff")
/proc/sanitize_probably_a_byond_color(what, default = "#ffffffff")
if(istext(what))
return sanitize_hexcolor(what, 6, 1, default)
return sanitize_hexcolor(what, 8, 1, default)
if(islist(what))
var/list/cmatrix = what
switch(length(cmatrix))
@@ -13,6 +13,13 @@
return default
/proc/sanitize_hexcolor(color, desired_format=3, include_crunch=0, default)
// TODO: don't check this here, do `sanitize_rgba_hexcolor` or something
switch(desired_format)
if(3 to 4)
if(6 to 8)
else
CRASH("asked for a nonsensical format ([desired_format])")
var/crunch = include_crunch ? "#" : ""
if(!istext(color))
color = ""
@@ -20,8 +27,13 @@
var/start = 1 + (text2ascii(color, 1) == 35)
var/len = length(color)
var/char = ""
// RRGGBB -> RGB but awful
var/convert_to_shorthand = desired_format == 3 && length_char(color) > 3
// check if we want to convert from 6-character to 3-character
var/want_shorthand = FALSE
switch(desired_format)
if(3, 4)
if(length(color) > desired_format)
want_shorthand = TRUE
. = ""
var/i = start
@@ -37,10 +49,19 @@
else
break
i += length(char)
if(convert_to_shorthand && i <= len) //skip next one
if(want_shorthand && i <= len) //skip next one
i += length(color[i])
if(length_char(.) != desired_format)
var/resultant_length = length(.)
// special case: insert alpha if it's not there
var/has_alpha = resultant_length == 4 || resultant_length == 8
var/want_alpha = desired_format == 4 || desired_format == 8
if(!has_alpha && want_alpha)
. += want_shorthand ? "f" : "ff"
// check format is correct, if not, toss
if(resultant_length != desired_format)
if(default)
return default
return crunch + repeat_string(desired_format, "0")

View File

@@ -181,7 +181,7 @@
* Used to get a properly sanitized input, of max_length
* no_trim is self explanatory but it prevents the input from being trimed if you intend to parse newlines or whitespace.
*/
/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length = MAX_MESSAGE_LEN, no_trim = FALSE)
/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length = MAX_MESSAGE_LEN + 1, no_trim = FALSE)
var/name = input(user, message, title, default) as text|null
if(no_trim)
return copytext(html_encode(name), 1, max_length)
@@ -191,7 +191,7 @@
/**
* Used to get a properly sanitized multiline input, of max_length.
*/
/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length = MAX_MESSAGE_LEN, no_trim = FALSE)
/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length = MAX_MESSAGE_LEN + 1, no_trim = FALSE)
var/name = input(user, message, title, default) as message|null
if(isnull(name)) // Return null if canceled.
return null
@@ -611,3 +611,32 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
/proc/sanitize_css_class_name(name)
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
return replacetext(name, regex, "")
/**
* Formats a number to human readable form with the appropriate SI unit.
*
* Supports SI exponents between 1e-15 to 1e15, but properly handles numbers outside that range as well.
* Examples:
* * `siunit(1234, "Pa", 1)` -> `"1.2 kPa"`
* * `siunit(0.5345, "A", 0)` -> `"535 mA"`
* * `siunit(1000, "Pa", 4)` -> `"1 kPa"`
* Arguments:
* * value - The number to convert to text. Can be positive or negative.
* * unit - The base unit of the number, such as "Pa" or "W".
* * maxdecimals - Maximum amount of decimals to display for the final number. Defaults to 1.
* *
* * For pressure conversion, use proc/siunit_pressure() below
*/
/proc/siunit(value, unit, maxdecimals=1)
var/si_isolated = siunit_isolated(value, unit, maxdecimals)
return "[si_isolated[SI_COEFFICIENT]][si_isolated[SI_UNIT]]"
/** The game code never uses Pa, but kPa, since 1 Pa is too small to reasonably handle
* Thus, to ensure correct conversion from any kPa in game code, this value needs to be multiplied by 10e3 to get Pa, which the siunit() proc expects
* Args:
* * value_in_kpa - Value that should be converted to readable text in kPa
* * maxdecimals - maximum number of decimals that are displayed, defaults to 1 in proc/siunit()
*/
/proc/siunit_pressure(value_in_kpa, maxdecimals)
var/pressure_adj = value_in_kpa * 1000 //to adjust for using kPa instead of Pa
return siunit(pressure_adj, "Pa", maxdecimals)

View File

@@ -15,6 +15,11 @@
#error Please consider downgrading to 514.1575 or lower.
#endif
// 516.1660 broke (x in vars), which breaks a lot of things.
#if (DM_VERSION == 516 && DM_BUILD == 1660)
#error This version of BYOND (516.1660) has a bug which prevents this codebase from loading properly. If possible, update your BYOND version. Otherwise, visit www.byond.com/download/build to download an older release.
#endif
// Keep savefile compatibilty at minimum supported level
#if DM_VERSION >= 515
/savefile/byond_version = MIN_COMPILER_VERSION
@@ -36,10 +41,16 @@
/// Call by name proc reference, checks if the proc is existing global proc
#define GLOBAL_PROC_REF(X) (/proc/##X)
#else
/// Call by name proc reference, checks if the proc exists on this type or as a global proc
/// Call by name proc references, checks if the proc exists on either this type () (AND ONLY THIS TYPE) or as a global proc.
#define PROC_REF(X) (nameof(.proc/##X))
/// Call by name proc reference, checks if the proc exists on given type or as a global proc
/// Call by name verb references, checks if the verb exists on either this type or as a global verb.
#define VERB_REF(X) (nameof(.verb/##X))
/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc
#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X))
/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb
#define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X))
/// Call by name proc reference, checks if the proc is existing global proc
#define GLOBAL_PROC_REF(X) (/proc/##X)
#endif

View File

@@ -50,3 +50,10 @@
/datum/config_entry/number/hard_deletes_overrun_limit
default = 0
min_val = 0
/**
* Tgui ui_act payloads larger than 2kb are split into chunks a maximum of 1kb in size.
* This flag represents the maximum chunk count the server is willing to receive.
*/
/datum/config_entry/number/tgui_max_chunk_count
default = 32

View File

@@ -1,44 +1,98 @@
/**
*! Copyright (c) 2020 Aleksej Komarov
*! SPDX-License-Identifier: MIT
/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
SUBSYSTEM_DEF(chat)
name = "Chat"
subsystem_flags = SS_NO_INIT
wait = 0.25 // scale up to 40 fps
runlevels = RUNLEVELS_ALL
subsystem_flags = SS_TICKER|SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
init_stage = INIT_STAGE_LATE
var/list/payload_by_client = list()
/// Assosciates a ckey with a list of messages to send to them.
var/list/list/datum/chat_payload/client_to_payloads = list()
/// Associates a ckey with an assosciative list of their last CHAT_RELIABILITY_HISTORY_SIZE messages.
var/list/list/datum/chat_payload/client_to_reliability_history = list()
/// Assosciates a ckey with their next sequence number.
var/list/client_to_sequence_number = list()
/datum/controller/subsystem/chat/proc/generate_payload(client/target, message_data)
var/sequence = client_to_sequence_number[target.ckey]
client_to_sequence_number[target.ckey] += 1
var/datum/chat_payload/payload = new
payload.sequence = sequence
payload.content = message_data
if(!(target.ckey in client_to_reliability_history))
client_to_reliability_history[target.ckey] = list()
var/list/client_history = client_to_reliability_history[target.ckey]
client_history["[sequence]"] = payload
if(length(client_history) > CHAT_RELIABILITY_HISTORY_SIZE)
var/oldest = text2num(client_history[1])
for(var/index in 2 to length(client_history))
var/test = text2num(client_history[index])
if(test < oldest)
oldest = test
client_history -= "[oldest]"
return payload
/datum/controller/subsystem/chat/proc/send_payload_to_client(client/target, datum/chat_payload/payload)
target.tgui_panel.window.send_message("chat/message", payload.into_message())
SEND_TEXT(target, payload.get_content_as_html())
/datum/controller/subsystem/chat/fire()
for(var/key in payload_by_client)
var/client/client = key
var/payload = payload_by_client[key]
payload_by_client -= key
if(client)
// Send to tgchat
client.tgui_panel?.window.send_message("chat/message", payload)
// Send to old chat
for(var/message in payload)
SEND_TEXT(client, message_to_html(message))
for(var/ckey in client_to_payloads)
var/client/target = GLOB.directory[ckey]
if(isnull(target)) // verify client still exists
LAZYREMOVE(client_to_payloads, ckey)
continue
for(var/datum/chat_payload/payload as anything in client_to_payloads[ckey])
send_payload_to_client(target, payload)
LAZYREMOVE(client_to_payloads, ckey)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/chat/proc/queue(target, message)
if(islist(target))
for(var/_target in target)
var/client/client = CLIENT_FROM_VAR(_target)
if(client)
LAZYADD(payload_by_client[client], list(message))
return
var/client/client = CLIENT_FROM_VAR(target)
if(client)
LAZYADD(payload_by_client[client], list(message))
/datum/controller/subsystem/chat/proc/queue(queue_target, list/message_data)
var/list/targets = islist(queue_target) ? queue_target : list(queue_target)
for(var/target in targets)
var/client/client = CLIENT_FROM_VAR(target)
if(isnull(client))
continue
LAZYADDASSOCLIST(client_to_payloads, client.ckey, generate_payload(client, message_data))
/datum/controller/subsystem/chat/Recover()
payload_by_client = list()
initialized = SSchat.initialized
/datum/controller/subsystem/chat/proc/send_immediate(send_target, list/message_data)
var/list/targets = islist(send_target) ? send_target : list(send_target)
for(var/target in targets)
var/client/client = CLIENT_FROM_VAR(target)
if(isnull(client))
continue
send_payload_to_client(client, generate_payload(client, message_data))
/datum/controller/subsystem/chat/proc/handle_resend(client/client, sequence)
var/list/client_history = client_to_reliability_history[client.ckey]
sequence = "[sequence]"
if(isnull(client_history) || !(sequence in client_history))
return
var/datum/chat_payload/payload = client_history[sequence]
if(payload.resends > CHAT_RELIABILITY_MAX_RESENDS)
return // we tried but byond said no
payload.resends += 1
send_payload_to_client(client, client_history[sequence])
// SSblackbox.record_feedback(
// "nested tally",
// "chat_resend_byond_version",
// 1,
// list(
// "[client.byond_version]",
// "[client.byond_build]",
// ),
// )

View File

@@ -428,33 +428,25 @@
*/
/datum/controller/subsystem/mapping/proc/recalculate_z_stack()
validate_no_loops()
z_stack_lookup = list()
z_stack_lookup.len = world.maxz
var/list/left = list()
for(var/z in 1 to world.maxz)
// todo: stacks
// if(struct_by_z[z])
// var/datum/world_struct/struct = struct_by_z[z]
// z_stack_lookup[z] = struct.stack_lookup[struct.real_indices.Find(z)]
// else
// left += z
left += z
var/list/datum/map_level/bottoms = list()
z_stack_lookup = new /list(world.maxz)
// let's sing the bottom song
for(var/z in left)
var/list/datum/map_level/bottoms = list()
for(var/z in 1 to world.maxz)
if(cached_level_down[z])
continue
bottoms += ordered_levels[z]
for(var/datum/map_level/bottom as anything in bottoms)
// register us
var/list/stack = list(bottom.z_index)
z_stack_lookup[bottom.z_index] = stack
// let's sing the list manipulation song
var/datum/map_level/next = ordered_levels[cached_level_up[bottom.z_index]]
while(next)
stack += next.z_index
z_stack_lookup[next.z_index] = stack
next = ordered_levels[cached_level_up[next.z_index]]
var/datum/map_level/iterating = bottom
var/list/stack = list()
do
stack += iterating.z_index
z_stack_lookup[iterating.z_index] = stack
iterating = ordered_levels[cached_level_up[iterating.z_index]]
while(iterating)
for(var/i in 1 to world.maxz)
if(length(z_stack_lookup[i]) >= 1)
else
stack_trace("z-level [i] ([ordered_levels[i]?.name]) had no z-stack. did someone mess up their up/down configs?")
/**
* Ensures there's no up/down infinite loops
@@ -486,9 +478,5 @@
var/datum/map_level/level = ordered_levels[z]
level.link_above = null
level.link_below = null
// if(struct_by_z[z])
// var/datum/world_struct/struct = struct_by_z[z]
// struct.Deconstruct()
// qdel(struct)
stack_trace("WARNING: Up/Down loops found in zlevels [english_list(loops)]. This is not allowed and will cause both falling and zcopy to infinitely loop. All zlevels involved have been disconnected, and any structs involved have been destroyed.")
rebuild_verticality()

View File

@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(nanoui)
wait = 7
/// A list of current open /nanoui UIs, grouped by src_object and ui_key.
var/list/open_uis = list()
var/list/open_nano_uis = list()
/// A list of current open /nanoui UIs, not grouped, for use in processing.
var/list/processing_uis = list()
@@ -16,8 +16,8 @@ SUBSYSTEM_DEF(nanoui)
UI.process()
/datum/controller/subsystem/nanoui/Recover()
if(SSnanoui.open_uis)
open_uis |= SSnanoui.open_uis
if(SSnanoui.open_nano_uis)
open_nano_uis |= SSnanoui.open_nano_uis
if(SSnanoui.processing_uis)
processing_uis |= SSnanoui.processing_uis
@@ -63,10 +63,10 @@ SUBSYSTEM_DEF(nanoui)
*/
/datum/controller/subsystem/nanoui/proc/get_open_ui(mob/user, src_object, ui_key)
var/src_object_key = "\ref[src_object]"
if (!open_uis[src_object_key] || !open_uis[src_object_key][ui_key])
if (!open_nano_uis[src_object_key] || !open_nano_uis[src_object_key][ui_key])
return
for (var/datum/nanoui/ui as anything in open_uis[src_object_key][ui_key])
for (var/datum/nanoui/ui as anything in open_nano_uis[src_object_key][ui_key])
if (ui.user == user)
return ui
@@ -80,11 +80,11 @@ SUBSYSTEM_DEF(nanoui)
/datum/controller/subsystem/nanoui/proc/update_uis(src_object)
. = 0 // We're going to return the number of uis updated.
var/src_object_key = "\ref[src_object]"
if (!open_uis[src_object_key])
if (!open_nano_uis[src_object_key])
return
for (var/ui_key in open_uis[src_object_key])
for (var/datum/nanoui/ui as anything in open_uis[src_object_key][ui_key])
for (var/ui_key in open_nano_uis[src_object_key])
for (var/datum/nanoui/ui as anything in open_nano_uis[src_object_key][ui_key])
if(ui.src_object && ui.user && ui.src_object.nano_host())
ui.try_update(1)
.++
@@ -102,11 +102,11 @@ SUBSYSTEM_DEF(nanoui)
/datum/controller/subsystem/nanoui/proc/close_uis(src_object)
. = 0 // We're going to return the number of uis closed.
var/src_object_key = "\ref[src_object]"
if (!open_uis[src_object_key])
if (!open_nano_uis[src_object_key])
return
for (var/ui_key in open_uis[src_object_key])
for (var/datum/nanoui/ui as anything in open_uis[src_object_key][ui_key])
for (var/ui_key in open_nano_uis[src_object_key])
for (var/datum/nanoui/ui as anything in open_nano_uis[src_object_key][ui_key])
ui.close() // If it's missing src_object or user, we want to close it even more.
.++
@@ -121,10 +121,10 @@ SUBSYSTEM_DEF(nanoui)
*/
/datum/controller/subsystem/nanoui/proc/update_user_uis(mob/user, src_object, ui_key)
. = 0 // We're going to return the number of uis updated.
if (!length(user.open_uis))
if (!length(user.open_nano_uis))
return // has no open uis
for (var/datum/nanoui/ui as anything in user.open_uis)
for (var/datum/nanoui/ui as anything in user.open_nano_uis)
if ((isnull(src_object) || ui.src_object == src_object) && (isnull(ui_key) || ui.ui_key == ui_key))
ui.try_update(1)
.++
@@ -140,10 +140,10 @@ SUBSYSTEM_DEF(nanoui)
*/
/datum/controller/subsystem/nanoui/proc/close_user_uis(mob/user, src_object, ui_key)
. = 0 // We're going to return the number of uis closed.
if (!length(user.open_uis))
if (!length(user.open_nano_uis))
return // has no open uis
for (var/datum/nanoui/ui in user.open_uis)
for (var/datum/nanoui/ui in user.open_nano_uis)
if ((isnull(src_object) || ui.src_object == src_object) && (isnull(ui_key) || ui.ui_key == ui_key))
ui.close()
.++
@@ -158,9 +158,9 @@ SUBSYSTEM_DEF(nanoui)
*/
/datum/controller/subsystem/nanoui/proc/ui_opened(datum/nanoui/ui)
var/src_object_key = "\ref[ui.src_object]"
LAZYINITLIST(open_uis[src_object_key])
LAZYDISTINCTADD(open_uis[src_object_key][ui.ui_key], ui)
LAZYDISTINCTADD(ui.user.open_uis, ui)
LAZYINITLIST(open_nano_uis[src_object_key])
LAZYDISTINCTADD(open_nano_uis[src_object_key][ui.ui_key], ui)
LAZYDISTINCTADD(ui.user.open_nano_uis, ui)
processing_uis += ui
/**
@@ -173,18 +173,18 @@ SUBSYSTEM_DEF(nanoui)
*/
/datum/controller/subsystem/nanoui/proc/on_ui_closed(datum/nanoui/ui)
var/src_object_key = "\ref[ui.src_object]"
if (!open_uis[src_object_key] || !open_uis[src_object_key][ui.ui_key])
if (!open_nano_uis[src_object_key] || !open_nano_uis[src_object_key][ui.ui_key])
return FALSE // wasn't open
processing_uis -= ui
if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface)
LAZYREMOVE(ui.user.open_uis, ui)
LAZYREMOVE(ui.user.open_nano_uis, ui)
open_uis[src_object_key][ui.ui_key] -= ui
if(!length(open_uis[src_object_key][ui.ui_key]))
open_uis[src_object_key] -= ui.ui_key
if(!length(open_uis[src_object_key]))
open_uis -= src_object_key
open_nano_uis[src_object_key][ui.ui_key] -= ui
if(!length(open_nano_uis[src_object_key][ui.ui_key]))
open_nano_uis[src_object_key] -= ui.ui_key
if(!length(open_nano_uis[src_object_key]))
open_nano_uis -= src_object_key
return TRUE
@@ -209,13 +209,13 @@ SUBSYSTEM_DEF(nanoui)
* @return bool FALSE if no ui was removed, TRUE if removed successfully
*/
/datum/controller/subsystem/nanoui/proc/user_transferred(mob/oldMob, mob/newMob)
if (!oldMob || !oldMob.open_uis)
if (!oldMob || !oldMob.open_nano_uis)
return FALSE // has no open uis
LAZYINITLIST(newMob.open_uis)
for (var/datum/nanoui/ui in oldMob.open_uis)
LAZYINITLIST(newMob.open_nano_uis)
for (var/datum/nanoui/ui in oldMob.open_nano_uis)
ui.user = newMob
newMob.open_uis += ui
newMob.open_nano_uis += ui
oldMob.open_uis = null
oldMob.open_nano_uis = null
return TRUE // success

View File

@@ -1,6 +1,6 @@
/**
*! Copyright (c) 2020 Aleksej Komarov
*! SPDX-License-Identifier: MIT
/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
/**
@@ -20,43 +20,44 @@ SUBSYSTEM_DEF(tgui)
/// A list of UIs scheduled to process
var/list/current_run = list()
/// A list of open UIs
var/list/open_uis = list()
/// A list of open UIs, grouped by src_object.
var/list/open_uis_by_src = list()
var/list/all_uis = list()
/// The HTML base used for all UIs.
var/basehtml
/datum/controller/subsystem/tgui/PreInit(recovering)
basehtml = file2text('tgui/public/tgui.html')
// Inject inline polyfills
var/polyfill = file2text('tgui/public/tgui-polyfill.min.js')
polyfill = "<script>\n[polyfill]\n</script>"
basehtml = replacetextEx(basehtml, "<!-- tgui:inline-polyfill -->", polyfill)
// Inject inline helper functions
var/helpers = file2text('tgui/public/helpers.min.js')
helpers = "<script type='text/javascript'>\n[helpers]\n</script>"
basehtml = replacetextEx(basehtml, "<!-- tgui:helpers -->", helpers)
// Inject inline ntos-error styles
var/ntos_error = file2text('tgui/public/ntos-error.min.css')
ntos_error = "<style type='text/css'>\n[ntos_error]\n</style>"
basehtml = replacetextEx(basehtml, "<!-- tgui:ntos-error -->", ntos_error)
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "Nanotrasen (c) 2525-[GLOB.startup_year + 544]")
/datum/controller/subsystem/tgui/Shutdown()
close_all_uis()
/* //no tguistat
/datum/controller/subsystem/tgui/stat_entry(msg)
msg = "P:[length(open_uis)]"
return ..()
*/
/datum/controller/subsystem/tgui/stat_entry()
return ..() + " P:[length(open_uis)]"
return ..() + " P:[length(all_uis)]"
/datum/controller/subsystem/tgui/fire(resumed = FALSE)
if(!resumed)
src.current_run = open_uis.Copy()
src.current_run = all_uis.Copy()
// Cache for sanic speed (lists are references anyways)
var/list/current_run = src.current_run
while(current_run.len)
var/datum/tgui/ui = current_run[current_run.len]
current_run.len--
// TODO: Move user/src_object check to process()
if(ui && ui.user && ui.src_object)
ui.process()
if(ui?.user && ui.src_object)
ui.process(wait * 0.1)
else
open_uis.Remove(ui)
ui.close(0)
if(MC_TICK_CHECK)
return
@@ -67,7 +68,7 @@ SUBSYSTEM_DEF(tgui)
* Returns null if pool was exhausted.
*
* required user mob
* return datum/tgui
* return datum/tgui_window
*/
/datum/controller/subsystem/tgui/proc/request_pooled_window(mob/user)
if(!user.client)
@@ -127,8 +128,6 @@ SUBSYSTEM_DEF(tgui)
for(var/datum/tgui/ui in user.tgui_open_uis)
if(ui.window && ui.window.id == window_id)
ui.close(can_be_suspended = FALSE)
// Unset machine just to be sure.
user.unset_machine()
// Close window directly just to be sure.
user << browse(null, "window=[window_id]")
@@ -175,12 +174,10 @@ SUBSYSTEM_DEF(tgui)
* return datum/tgui The found UI.
*/
/datum/controller/subsystem/tgui/proc/get_open_ui(mob/user, datum/src_object)
RETURN_TYPE(/datum/tgui)
var/key = "[REF(src_object)]"
// No UIs opened for this src_object
if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
if(!LAZYLEN(src_object?.open_uis))
return null
for(var/datum/tgui/ui in open_uis_by_src[key])
for(var/datum/tgui/ui in src_object.open_uis)
// Make sure we have the right user
if(ui.user == user)
return ui
@@ -196,15 +193,14 @@ SUBSYSTEM_DEF(tgui)
* return int The number of UIs updated.
*/
/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object)
var/count = 0
var/key = "[REF(src_object)]"
// No UIs opened for this src_object
if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
return count
for(var/datum/tgui/ui in open_uis_by_src[key])
if(!LAZYLEN(src_object?.open_uis))
return 0
var/count = 0
for(var/datum/tgui/ui in src_object.open_uis)
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.process(force = 1)
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
INVOKE_ASYNC(ui, TYPE_PROC_REF(/datum/tgui, process), wait * 0.1, TRUE)
count++
return count
@@ -218,14 +214,13 @@ SUBSYSTEM_DEF(tgui)
* return int The number of UIs closed.
*/
/datum/controller/subsystem/tgui/proc/close_uis(datum/src_object)
var/count = 0
var/key = "[REF(src_object)]"
// No UIs opened for this src_object
if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
return count
for(var/datum/tgui/ui in open_uis_by_src[key])
if(!LAZYLEN(src_object?.open_uis))
return 0
var/count = 0
for(var/datum/tgui/ui in src_object.open_uis)
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.close()
count++
return count
@@ -239,12 +234,11 @@ SUBSYSTEM_DEF(tgui)
*/
/datum/controller/subsystem/tgui/proc/close_all_uis()
var/count = 0
for(var/key in open_uis_by_src)
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.close()
count++
for(var/datum/tgui/ui in all_uis)
// Check if UI is valid.
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.close()
count++
return count
/**
@@ -263,7 +257,7 @@ SUBSYSTEM_DEF(tgui)
return count
for(var/datum/tgui/ui in user.tgui_open_uis)
if(isnull(src_object) || ui.src_object == src_object)
ui.process(force = 1)
ui.process(wait * 0.1, force = 1)
count++
return count
@@ -295,13 +289,9 @@ SUBSYSTEM_DEF(tgui)
* required ui datum/tgui The UI to be added.
*/
/datum/controller/subsystem/tgui/proc/on_open(datum/tgui/ui)
var/key = "[REF(ui.src_object)]"
if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
open_uis_by_src[key] = list()
ui.user.tgui_open_uis |= ui
var/list/uis = open_uis_by_src[key]
uis |= ui
open_uis |= ui
ui.user?.tgui_open_uis |= ui
LAZYDISTINCTADD(ui.src_object.open_uis, ui)
all_uis |= ui
/**
* private
@@ -313,18 +303,14 @@ SUBSYSTEM_DEF(tgui)
* return bool If the UI was removed or not.
*/
/datum/controller/subsystem/tgui/proc/on_close(datum/tgui/ui)
var/key = "[REF(ui.src_object)]"
if(isnull(open_uis_by_src[key]) || !istype(open_uis_by_src[key], /list))
return FALSE
// Remove it from the list of processing UIs.
open_uis.Remove(ui)
all_uis -= ui
current_run -= ui
// If the user exists, remove it from them too.
if(ui.user)
ui.user.tgui_open_uis.Remove(ui)
var/list/uis = open_uis_by_src[key]
uis.Remove(ui)
if(length(uis) == 0)
open_uis_by_src.Remove(key)
ui.user.tgui_open_uis -= ui
if(ui.src_object)
LAZYREMOVE(ui.src_object.open_uis, ui)
return TRUE
/**
@@ -359,10 +345,7 @@ SUBSYSTEM_DEF(tgui)
for(var/datum/tgui/ui in source.tgui_open_uis)
// Inform the UIs of their new owner.
ui.user = target
target.tgui_open_uis.Add(ui)
source.on_ui_transfer(source, target, ui)
for(var/datum/module in ui.modules_processed)
module.on_ui_transfer(source, target, ui, TRUE)
target.tgui_open_uis += ui
// Clear the old list.
source.tgui_open_uis.Cut()
return TRUE

View File

@@ -268,7 +268,7 @@ SUBSYSTEM_DEF(vote)
return data
/datum/controller/subsystem/vote/ui_act(action, list/params, datum/tgui/ui)
/datum/controller/subsystem/vote/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -10,7 +10,7 @@
ui = new(user, src, "Changelog")
ui.open()
/datum/changelog/ui_act(action, list/params, datum/tgui/ui)
/datum/changelog/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return
@@ -22,14 +22,13 @@
changelog_item = new /datum/asset_pack/changelog_item(params["date"])
SSassets.register_asset_pack(changelog_item)
changelog_items[params["date"]] = changelog_item
ui.send_asset(changelog_item)
return TRUE
return ui.send_asset(changelog_item)
/datum/changelog/ui_static_data(mob/user, datum/tgui/ui)
/datum/changelog/ui_static_data()
var/list/data = list( "dates" = list() )
var/regex/ymlRegex = regex(@"\.yml", "g")
for(var/archive_file in flist("[global.config.directory]/../html/changelogs/archive/"))
for(var/archive_file in sortList(flist("html/changelogs/archive/")))
var/archive_date = ymlRegex.Replace(archive_file, "")
data["dates"] = list(archive_date) + data["dates"]

View File

@@ -0,0 +1,16 @@
/// Stores information about a chat payload
/datum/chat_payload
/// Sequence number of this payload
var/sequence = 0
/// Message we are sending
var/list/content
/// Resend count
var/resends = 0
/// Converts the chat payload into a JSON string
/datum/chat_payload/proc/into_message()
return "{\"sequence\":[sequence],\"content\":[json_encode(content)]}"
/// Returns an HTML-encoded message from our contents.
/datum/chat_payload/proc/get_content_as_html()
return message_to_html(content)

View File

@@ -373,7 +373,7 @@
data["crafting_recipes"] = crafting_recipes
return data
/datum/component/personal_crafting/ui_act(action, list/params, datum/tgui/ui)
/datum/component/personal_crafting/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return
switch(action)

View File

@@ -17,6 +17,10 @@
*/
var/gc_destroyed
/// Open uis owned by this datum
/// Lazy, since this case is semi rare
var/list/datum/tgui/open_uis
/// Active timers with this datum as the target
var/list/active_timers
/// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...))

View File

@@ -10,30 +10,32 @@
var/proper_name = "Unknown"
/// The total number of wires that our holder atom has.
var/wire_count = NONE
/// A list of all wires. For a list of valid wires defines that can go here, see `code/__DEFINES/wires.dm`
var/list/wires
/// A list of all cut wires. The same values that can go into `wires` will get added and removed from this list.
var/list/cut_wires
/// An associative list with the wire color as the key, and the wire define as the value.
var/list/colors
/// An associative list of signalers attached to the wires. The wire color is the key, and the signaler object reference is the value.
var/list/assemblies
/datum/wires/New(atom/_holder)
/// List of all wires.
var/list/wires = list()
/// A list of all cut wires. The same values that can go into `wires` will get added and removed from this list.
var/list/cut_wires = list()
/// Dictionary of colours to wire.
var/list/colors = list()
/// List of attached assemblies.
var/list/assemblies = list()
/datum/wires/New(atom/holder)
..()
if(!istype(_holder, holder_type))
if(!istype(holder, holder_type))
CRASH("Our holder is null/the wrong type!")
holder = _holder
cut_wires = list()
colors = list()
assemblies = list()
src.holder = holder
// Add in the appropriate amount of dud wires.
var/wire_len = length(wires)
if(wire_len < wire_count) // If the amount of "real" wires is less than the total we're suppose to have...
add_duds(wire_count - wire_len) // Add in the appropriate amount of duds to reach `wire_count`.
// TODO: compile flag this, this is effectively a runtime sanity lint for gc. this shouldn't be needed, and should only be on when
// debugging GC issues.
RegisterSignal(holder, COMSIG_PARENT_QDELETING, PROC_REF(on_holder_qdel))
// If the randomize is true, we need to generate a new set of wires and ignore any wire color directories.
if(randomize)
randomize()
@@ -50,6 +52,19 @@
detach_assembly(color)
return ..()
/datum/wires/proc/on_holder_qdel(atom/source, force)
SIGNAL_HANDLER
// don't just silently qdel self this isn't a catch-all it's supposed to yell at you if you forget to delete
addtimer(CALLBACK(src, PROC_REF(still_not_qdeled), REF(source), source.type), 0)
/datum/wires/proc/still_not_qdeled(source_ref, source_type)
if(QDELETED(src))
CRASH("why is the qdel leak timer still firing after self-del?")
else
stack_trace("qdel leak timer fired on wire datum associated to [source_ref] ([source_type]). this means it didn't delete its wires immediately.")
qdel(src)
/**
* Randomly generates a new set of wires. and corresponding colors from the given pool. Assigns the information as an associative list, to `colors`.
*
@@ -89,7 +104,7 @@
/datum/wires/ui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Wires", "[proper_name] wires")
ui = new(user, src, "Wires", "[holder.name] wires")
ui.open()
/datum/wires/ui_state()
@@ -123,13 +138,13 @@
color_name = LIST_COLOR_RENAME[color]
wires_list += list(list(
"seen_color" = replaced_color, // The color of the wire that the mob will see. This will be the same as `color` if the user is NOT colorblind.
"color_name" = color_name, // The wire's name. This will be the same as `color` if the user is NOT colorblind.
"color" = color, // The "real" color of the wire. No replacements.
"shownColor" = color_name,
"wire" = can_see_wire_info(user) && !is_dud_color(color) ? get_wire(color) : null, // Wire define information like "Contraband" or "Door Bolts".
"cut" = is_color_cut(color), // Whether the wire is cut or not. Used to display "cut" or "mend".
"attached" = is_attached(color) // Whether or not a signaler is attached to this wire.
))
data["wires"] = wires_list
// Get the information shown at the bottom of wire TGUI window, such as "The red light is blinking", etc.
@@ -148,62 +163,55 @@
break
data["status"] = status
data["proper_name"] = (proper_name != "Unknown") ? proper_name : null
return data
/datum/wires/ui_act(action, list/params, datum/tgui/ui)
/datum/wires/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return
var/mob/user = usr
if(!interactable(user))
if(. || !interactable(usr))
return
var/target_wire = params["wire"]
var/mob/user = usr
var/obj/item/I = user.get_active_held_item()
var/color = lowertext(params["wire"])
holder.add_hiddenprint(user)
switch(action)
// Toggles the cut/mend status.
// Toggles the cut/mend status
if("cut")
// if(!I.is_wirecutter() && !user.can_admin_interact())
if(!istype(I) || !I.is_wirecutter())
to_chat(user, "<span class='error'>You need wirecutters!</span>")
return
playsound(holder, I.tool_sound, 20, 1)
cut_color(color)
return TRUE
if(I?.is_wirecutter() || isAdminGhostAI(usr))
cut_color(target_wire)
. = TRUE
else
to_chat(user, SPAN_WARNING("You need wirecutters!"))
// Pulse a wire.
if("pulse")
// if(!I.is_multitool() && !user.can_admin_interact())
if(!istype(I) || !I.is_multitool())
to_chat(user, "<span class='error'>You need a multitool!</span>")
return
if(I?.is_multitool() || isAdminGhostAI(usr))
if(I && holder)
playsound(holder, I.tool_sound, 20, 1)
pulse_color(target_wire)
playsound(holder, 'sound/weapons/empty.ogg', 20, 1)
pulse_color(color)
// If they pulse the electrify wire, call interactable() and try to shock them.
if(get_wire(color) == WIRE_ELECTRIFY)
interactable(user)
return TRUE
// If they pulse the electrify wire, call interactable() and try to shock them.
if(get_wire(target_wire) == WIRE_ELECTRIFY)
interactable(user)
. = TRUE
else
to_chat(user, SPAN_WARNING("You need a multitool!"))
// Attach a signaler to a wire.
if("attach")
if(is_attached(color))
var/obj/item/O = detach_assembly(color)
if(O)
user.put_in_hands(O)
if(is_attached(target_wire))
I = detach_assembly(target_wire)
if(I)
user.put_in_hands(I)
return TRUE
if(!istype(I, /obj/item/assembly/signaler))
if(!isassembly(I))
to_chat(user, "<span class='error'>You need a remote signaller!</span>")
return
if(user.attempt_void_item_for_installation(I))
attach_assembly(color, I)
attach_assembly(target_wire, I)
return TRUE
/**
@@ -215,12 +223,18 @@
* * user - the mob who is interacting with the wires.
*/
/datum/wires/proc/can_see_wire_info(mob/user)
// TODO: Reimplement this if we ever get Advanced Admin Interaction.
// if(user.can_admin_interact())
// return TRUE
// Admin ghost can see a purpose of each wire.
if(isAdminGhostAI(user))
return TRUE
// Same for anyone with an abductor multitool or clockwork one.
var/obj/item/I = user.get_active_held_item()
if(istype(I, /obj/item/multitool/alien) || istype(I, /obj/item/multitool/clockwork))
return TRUE
// Station blueprints do that too, but only if the wires are not randomized.
if(!randomize && istype(I, /obj/item/blueprints))
return TRUE
return FALSE
/**

View File

@@ -0,0 +1,5 @@
//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2025 Citadel Station Developers *//
/mob/living/silicon/pai/silicon_control_interaction_allowed(atom/target, datum/event_args/actor/actor, datum/event_args/actor/clickchain/clickchain, clickchain_flags)
return FALSE

View File

@@ -84,9 +84,8 @@
t += "Dispenser [(disabled ? "deactivated" : "activated")] - <A href='?src=\ref[src];toggleOn=1'>[(disabled ? "Enable" : "Disable")]?</a><br>\n"
t += "Uses Left: [uses]. <A href='?src=\ref[src];toggleUse=1'>Activate the dispenser?</A><br>\n"
user << browse(t, "window=computer;size=575x450")
user << browse(HTML_SKELETON(t), "window=computer;size=575x450")
onclose(user, "computer")
return
/obj/machinery/ai_slipper/Topic(href, href_list)
..()

View File

@@ -179,7 +179,7 @@
else
..(signal)
/obj/machinery/computer/general_air_control/large_tank_control/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/general_air_control/large_tank_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
@@ -280,7 +280,7 @@
else
..(signal)
/obj/machinery/computer/general_air_control/supermatter_core/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/general_air_control/supermatter_core/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
@@ -395,7 +395,7 @@
else
..(signal)
/obj/machinery/computer/general_air_control/fuel_injection/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/general_air_control/fuel_injection/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -188,7 +188,7 @@
return static_data
/obj/machinery/biogenerator/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/biogenerator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -133,7 +133,7 @@
return data
/obj/machinery/bomb_tester/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/bomb_tester/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -57,7 +57,7 @@
/obj/machinery/computer/operating/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "OperatingComputer", "Patient Monitor")
ui = new(user, src, "computers/OperatingComputer", "Patient Monitor")
ui.open()
/obj/machinery/computer/operating/ui_data(mob/user, datum/tgui/ui)
@@ -135,7 +135,7 @@
return data
/obj/machinery/computer/operating/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/operating/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))

View File

@@ -80,7 +80,7 @@
return data
/obj/machinery/computer/aifixer/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/aifixer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
if(!occupier)

View File

@@ -184,7 +184,7 @@
return data
/obj/machinery/computer/arcade/clawmachine/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/arcade/clawmachine/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return

View File

@@ -185,7 +185,7 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events())
static_data["settlermoods"] = settlermoods
return static_data
/obj/machinery/computer/arcade/orion_trail/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/arcade/orion_trail/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -58,7 +58,7 @@ var/global/list/minor_air_alarms = list()
/obj/machinery/computer/atmos_alert/proc/on_alarm_update()
update_icon()
/obj/machinery/computer/atmos_alert/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/atmos_alert/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -21,13 +21,22 @@
QDEL_NULL(tgui_cardmod)
return ..()
/obj/machinery/computer/card/drop_products(method, atom/where)
. = ..()
if(authing)
drop_product(authing, where)
authing = null
if(editing)
drop_product(editing, where)
editing = null
/obj/machinery/computer/card/ui_route(action, list/params, datum/tgui/ui, id)
. = ..()
if(.)
return
switch(id)
if("modify")
return tgui_cardmod.ui_act(action, params, ui, new /datum/event_args/actor(usr))
return tgui_cardmod.ui_act(action, params, ui, ui.state, new /datum/event_args/actor(usr))
/**
* for later use: authorized to change slots
@@ -140,7 +149,7 @@
.["authed_cardmod"] = authed_for_edit()
.["authed_slotmod"] = authed_for_slotmod()
/obj/machinery/computer/card/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/card/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -162,7 +162,7 @@
RETURN_TYPE(/list)
return isnull(giver)? list() : giver.access
/obj/machinery/computer/guestpass/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/guestpass/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return TRUE

View File

@@ -202,7 +202,7 @@
data["modal"] = ui_modal_data(src)
return data
/obj/machinery/computer/med_data/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/med_data/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -51,7 +51,7 @@
.["scan_ready"] = !on_cooldown()
.["network"] = network_key || ""
/obj/machinery/computer/bioscan/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/bioscan/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -64,7 +64,7 @@
return list("locked" = !screen, "chemImplants" = chemImplants, "trackImplants" = trackImplants)
/obj/machinery/computer/prisoner/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/prisoner/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -150,7 +150,7 @@
data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user))
return data
/obj/machinery/computer/robotics/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/robotics/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
. = FALSE

View File

@@ -185,7 +185,7 @@
data["modal"] = ui_modal_data(src)
return data
/obj/machinery/computer/secure_data/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/secure_data/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -134,7 +134,7 @@
data["modal"] = ui_modal_data(src)
return data
/obj/machinery/computer/skills/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/skills/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -111,7 +111,7 @@
return data
/obj/machinery/computer/timeclock/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/timeclock/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -677,7 +677,7 @@ About the new airlock wires panel:
..(user)
return
/obj/machinery/door/airlock/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/door/airlock/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
if(!user_allowed(usr))

View File

@@ -25,7 +25,7 @@
.["req_access"] = conf_req_access || list()
.["req_one_access"] = conf_req_one_access || list()
/obj/item/airlock_electronics/ui_act(action, list/params, datum/tgui/ui)
/obj/item/airlock_electronics/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -228,7 +228,7 @@
break
return data
/obj/machinery/door_timer/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/door_timer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return

View File

@@ -29,7 +29,7 @@
"internalTemplateName" = "DockingConsoleMulti",
)
/obj/machinery/embedded_controller/radio/docking_port_multi/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/embedded_controller/radio/docking_port_multi/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
return //Apparently we swallow all input (this is corrected legacy code)

View File

@@ -30,7 +30,7 @@
. = ..()
stack_trace("WARNING: Embedded controller [src] ([type]) had Topic() called unexpectedly. Please report this.")
/obj/machinery/embedded_controller/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/embedded_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
if(LAZYLEN(valid_actions))

View File

@@ -134,7 +134,7 @@ GLOBAL_LIST_EMPTY(exonet_nodes)
// Proc: ui_act()
// Parameters: 2 (standard ui_act arguments)
// Description: Responds to button presses on the TGUI interface.
/obj/machinery/exonet_node/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/exonet_node/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -176,7 +176,7 @@
else
.["item"] = null
/obj/machinery/gear_painter/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/gear_painter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -477,7 +477,7 @@ GLOBAL_VAR_INIT(holopad_connectivity_rebuild_queued, FALSE)
ringing[++ringing.len] = holocall.ui_caller_id_source()
.["ringing"] = ringing
/obj/machinery/holopad/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/holopad/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -58,7 +58,7 @@
data["beakerAttached"] = reagent_container ? TRUE : FALSE
return data
/obj/machinery/iv_drip/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/iv_drip/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -160,7 +160,7 @@
if (panel_open)
add_overlay("panel_open")
/obj/machinery/media/jukebox/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/media/jukebox/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
@@ -241,7 +241,7 @@
ui = new(user, src, "Jukebox", "RetroBox - Space Style")
ui.open()
/obj/machinery/media/jukebox/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/media/jukebox/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -41,7 +41,7 @@ GLOBAL_LIST_EMPTY(bioscan_antenna_list)
if(!network_mutable)
return ..()
. = TRUE
var/new_network = default_input_text(e_args.initiator, "What do you want to set the network key to?", "Modify Network", network_key)
var/new_network = tgui_input_text(e_args.initiator, "What do you want to set the network key to?", "Modify Network", network_key)
if(!e_args.performer.Reachability(src) || isnull(new_network))
return
e_args.visible_feedback(

View File

@@ -59,7 +59,7 @@
return data
/obj/machinery/pipedispenser/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/pipedispenser/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -61,11 +61,11 @@
drop_product(inserted_id, where)
inserted_id = null
/obj/machinery/point_redemption_vendor/using_item_on(obj/item/using, datum/event_args/actor/clickchain/e_args, clickchain_flags)
/obj/machinery/point_redemption_vendor/using_item_on(obj/item/using, datum/event_args/actor/clickchain/clickchain, clickchain_flags)
. = ..()
if(. & CLICKCHAIN_FLAGS_INTERACT_ABORT)
return
if(handle_id_insertion(using, e_args))
if(handle_id_insertion(using, clickchain))
// in either case they wouldn't do anything because the id is either not on them anymore
// or is not going to touch the machine
return CLICKCHAIN_DID_SOMETHING | CLICKCHAIN_DO_NOT_PROPAGATE
@@ -91,7 +91,7 @@
inserted_id = maybe_id
return TRUE
/obj/machinery/point_redemption_vendor/ui_act(action, list/params, datum/tgui/ui, datum/event_args/actor/e_args)
/obj/machinery/point_redemption_vendor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return
@@ -99,16 +99,16 @@
switch(action)
if("idcard")
if(inserted_id)
e_args.visible_feedback(
actor.visible_feedback(
target = src,
range = MESSAGE_RANGE_INVENTORY_SOFT,
visible = SPAN_NOTICE("[e_args.performer] retrieves [inserted_id] from [src]."),
visible = SPAN_NOTICE("[actor.performer] retrieves [inserted_id] from [src]."),
)
e_args.performer.put_in_hands_or_drop(inserted_id)
actor.performer.put_in_hands_or_drop(inserted_id)
inserted_id = null
else if(!inserted_id)
var/obj/item/active_held = e_args.performer.get_active_held_item()
handle_id_insertion(active_held, e_args)
var/obj/item/active_held = actor.performer.get_active_held_item()
handle_id_insertion(active_held, actor)
return TRUE
if("vend")
if(!inserted_id)

View File

@@ -45,7 +45,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense)
ui_interact(user)
return TRUE
/obj/machinery/pointdefense_control/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/pointdefense_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -131,7 +131,7 @@
data["occupied"] = FALSE
return data
/obj/machinery/suit_storage_unit/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/suit_storage_unit/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..() || isUV || isbroken)
return TRUE

View File

@@ -539,7 +539,7 @@
overmap_range = clamp(new_range, overmap_range_min, overmap_range_max)
update_idle_power_usage(initial(idle_power_usage)**(overmap_range+1))
/obj/machinery/telecomms/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/telecomms/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -73,7 +73,7 @@
ui = new(user, src, "TelecommsLogBrowser", name)
ui.open()
/obj/machinery/computer/telecomms/server/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/telecomms/server/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -61,7 +61,7 @@
ui = new(user, src, "TelecommsMachineBrowser", name)
ui.open()
/obj/machinery/computer/telecomms/monitor/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/telecomms/monitor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -107,7 +107,7 @@
data["valid_destinations"] = generate_telebeacon_list()
return data
/obj/machinery/computer/teleporter/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/computer/teleporter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -310,7 +310,7 @@
// Proc: tgui-act()
// Parameters: 4 (standard ui_act arguments)
// Description: Responds to UI button presses.
/obj/item/communicator/ui_act(action, list/params, datum/tgui/ui)
/obj/item/communicator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -388,7 +388,7 @@
"name" = sig.gps_tag
))
/obj/item/gps/ui_act(action, list/params, datum/tgui/ui)
/obj/item/gps/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -33,6 +33,7 @@
var/color_overlay = null
tool_speed = 1
tool_behaviour = TOOL_MULTITOOL
tool_sound = 'sound/weapons/empty.ogg'
/obj/item/multitool/Initialize(mapload)
. = ..()

View File

@@ -187,7 +187,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list(
return data
/obj/item/radio/ui_act(action, list/params, datum/tgui/ui)
/obj/item/radio/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -94,7 +94,7 @@
data["valve"] = valve_open
return data
/obj/item/transfer_valve/ui_act(action, list/params, datum/tgui/ui)
/obj/item/transfer_valve/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return
. = TRUE

View File

@@ -35,7 +35,7 @@
return data
/obj/item/plant_analyzer/ui_act(action, list/params, datum/tgui/ui)
/obj/item/plant_analyzer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -156,7 +156,7 @@
. = ..()
.["amount"] = get_amount()
/obj/item/stack/ui_act(action, list/params, datum/tgui/ui)
/obj/item/stack/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -156,7 +156,7 @@
data["init_directions"] = init_directions
return data
/obj/item/pipe_dispenser/ui_act(action, list/params, datum/tgui/ui)
/obj/item/pipe_dispenser/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -275,7 +275,7 @@ var/list/global/tank_gauge_cache = list()
return .
/obj/item/tank/ui_act(action, list/params, datum/tgui/ui)
/obj/item/tank/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
switch(action)

View File

@@ -476,12 +476,12 @@
if(obj_storage.allow_outbound_mass_transfer && obj_storage.allow_clickdrag_mass_transfer && isobj(over))
var/obj/object = over
if(object.obj_storage.allow_inbound_mass_transfer)
obj_storage.interacted_mass_transfer(new /datum/event_args/actor(user), object.obj_storage)
obj_storage.auto_handle_interacted_mass_transfer(new /datum/event_args/actor(user), object.obj_storage)
return CLICKCHAIN_DO_NOT_PROPAGATE
// clickdrag to ground mass dumping
if(obj_storage.allow_quick_empty_via_clickdrag && obj_storage.allow_quick_empty && isturf(over))
var/turf/turf = over
obj_storage.interacted_mass_dumping(new /datum/event_args/actor(user), turf)
obj_storage.auto_handle_interacted_mass_dumping(new /datum/event_args/actor(user), turf)
return CLICKCHAIN_DO_NOT_PROPAGATE
return ..()

View File

@@ -76,7 +76,7 @@
data["items"] += list(content_data)
return data
/obj/structure/noticeboard/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
/obj/structure/noticeboard/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -106,7 +106,7 @@ GLOBAL_DATUM(character_directory, /datum/character_directory)
return data
/datum/character_directory/ui_act(action, list/params, datum/tgui/ui)
/datum/character_directory/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -379,10 +379,10 @@
if(findtext(browser_options, "devtools"))
winset(src, null, list("browser-options" = "-devtools"))
to_chat(src, SPAN_NOTICE("You can now right click to use inspect on browsers."))
to_chat(src, SPAN_NOTICE("You can no longer right click to use inspect on browsers."))
else
winset(src, null, list("browser-options" = "+devtools"))
to_chat(src, SPAN_NOTICE("You can no longer right click to use inspect on browsers."))
to_chat(src, SPAN_NOTICE("You can now right click to use inspect on browsers."))
/client/proc/cmd_admin_clear_mobs()
@@ -790,10 +790,12 @@
input_NIF = input("Pick the NIF type","Quick NIF") in show_NIFs
var/chosen_NIF = NIFs[capitalize(input_NIF)]
var/obj/item/nif/created
if(chosen_NIF)
new chosen_NIF(H)
created = new chosen_NIF(H)
else
new /obj/item/nif(H)
created = new /obj/item/nif(H)
created.install_done = world.time
log_and_message_admins("[key_name(src)] Quick NIF'd [H.real_name] with a [input_NIF].")
feedback_add_details("admin_verb","QNIF") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -76,7 +76,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/color_matrix_proxy_view)
return ..()
/datum/color_matrix_editor/ui_state()
return GLOB.admin_state
return ADMIN_STATE(NONE)
/datum/color_matrix_editor/ui_static_data(mob/user, datum/tgui/ui)
var/list/data = list()
@@ -96,7 +96,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/color_matrix_proxy_view)
ui = new(user, src, "ColorMatrixEditor")
ui.open()
/datum/color_matrix_editor/ui_act(action, list/params, datum/tgui/ui)
/datum/color_matrix_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -4,8 +4,8 @@
/datum/filter_editor/New(atom/target)
src.target = target
/datum/filter_editor/ui_state()
return GLOB.admin_state
/datum/filter_editor/ui_state(mob/user)
return ADMIN_STATE(R_VAREDIT)
/datum/filter_editor/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -24,7 +24,7 @@
data["target_filter_data"] = target.filter_data
return data
/datum/filter_editor/ui_act(action, list/params, datum/tgui/ui)
/datum/filter_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -98,7 +98,7 @@
immediate += /datum/asset_pack/spritesheet/crayons
return ..()
/obj/item/pen/crayon/ui_act(action, list/params, datum/tgui/ui)
/obj/item/pen/crayon/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -176,7 +176,7 @@
return data
/obj/item/assembly/infra/ui_act(action, list/params, datum/tgui/ui)
/obj/item/assembly/infra/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -118,7 +118,7 @@
return data
/obj/item/assembly/prox_sensor/ui_act(action, list/params, datum/tgui/ui)
/obj/item/assembly/prox_sensor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -49,7 +49,7 @@
data["maxFrequency"] = MAX_FREE_FREQ
return data
/obj/item/assembly/signaler/ui_act(action, list/params, datum/tgui/ui)
/obj/item/assembly/signaler/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -84,7 +84,7 @@
data["loop"] = loop
return data
/obj/item/assembly/timer/ui_act(action, list/params, datum/tgui/ui)
/obj/item/assembly/timer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -1,11 +1,10 @@
/datum/asset_pack/simple/fontawesome
assets = list(
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
"fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
"fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
"fa-regular-400.ttf" = 'html/font-awesome/webfonts/fa-regular-400.ttf',
"fa-solid-900.ttf" = 'html/font-awesome/webfonts/fa-solid-900.ttf',
"fa-v4compatibility.ttf" = 'html/font-awesome/webfonts/fa-v4compatibility.ttf',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css',
"font-awesome.css" = 'html/font-awesome/css/all.min.css',
"font-awesome.css" = 'html/font-awesome/css/all.min.css'
)
do_not_separate = TRUE
do_not_mangle = TRUE

View File

@@ -0,0 +1,26 @@
/datum/asset_pack/json/icon_ref_map
name = "icon_ref_map"
/datum/asset_pack/json/icon_ref_map/generate()
var/list/data = list() //"icons/obj/drinks.dmi" => "[0xc000020]"
//var/start = "0xc000000"
var/value = 0
while(TRUE)
value += 1
var/ref = "\[0xc[num2text(value,6,16)]\]"
var/mystery_meat = locate(ref)
if(isicon(mystery_meat))
if(!isfile(mystery_meat)) // Ignore the runtime icons for now
continue
var/path = get_icon_dmi_path(mystery_meat) //Try to get the icon path
if(path)
data[path] = ref
else if(mystery_meat)
continue; //Some other non-icon resource, ogg/json/whatever
else //Out of resources end this, could also try to end this earlier as soon as runtime generated icons appear but eh
break;
return data

View File

@@ -556,7 +556,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/air_alarm, 26)
/obj/machinery/air_alarm/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/ui_state/state)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "AirAlarm", name, parent_ui)
ui = new(user, src, "AirAlarm", name, parent_ui = parent_ui)
if(state)
ui.set_state(state)
ui.open()
@@ -611,7 +611,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/air_alarm, 26)
data["temperatureTLV"] = tlv_temperature
push_ui_data(data = data)
/obj/machinery/air_alarm/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/air_alarm/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -207,7 +207,7 @@
return data
/obj/machinery/atmospherics/component/binary/algae_farm/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/binary/algae_farm/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -185,7 +185,7 @@
/obj/machinery/atmospherics/component/binary/heat_pump/ui_state()
return GLOB.physical_state
/obj/machinery/atmospherics/component/binary/heat_pump/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/binary/heat_pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE
switch(action)

View File

@@ -157,7 +157,7 @@
return
ui_interact(user)
/obj/machinery/atmospherics/component/binary/massive_gas_pump/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/binary/massive_gas_pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -181,7 +181,7 @@
return
ui_interact(user)
/obj/machinery/atmospherics/component/binary/massive_heat_pump/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/binary/massive_heat_pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -199,7 +199,7 @@
return data
/obj/machinery/atmospherics/component/binary/passive_gate/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/binary/passive_gate/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -239,7 +239,7 @@ Thus, the two variables affect pump operation are set in New():
return
ui_interact(user)
/obj/machinery/atmospherics/component/binary/pump/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/binary/pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -91,7 +91,7 @@
.["powerSetting"] = power_setting
.["powerUsage"] = power_current
/obj/machinery/atmospherics/component/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -150,7 +150,7 @@
else
return null
/obj/machinery/atmospherics/component/quaternary/atmos_filter/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/quaternary/atmos_filter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -163,7 +163,7 @@
return data
/obj/machinery/atmospherics/component/quaternary/mixer/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/quaternary/mixer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -80,7 +80,7 @@
. = ..()
.["atmosContext"] = global.gas_data.tgui_gas_context()
/obj/machinery/atmospherics/component/trinary/filter/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/trinary/filter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -100,7 +100,7 @@
return
ui_interact(user)
/obj/machinery/atmospherics/component/trinary/mixer/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/trinary/mixer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -76,7 +76,7 @@
.["maxRate"] = air1.volume
.["rate"] = flow_setting
/obj/machinery/atmospherics/component/trinary/molar_filter/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/trinary/molar_filter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
. = ..()
if(.)
return

View File

@@ -88,7 +88,7 @@
return data
/obj/machinery/atmospherics/component/unary/freezer/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/unary/freezer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

View File

@@ -98,7 +98,7 @@
return data
/obj/machinery/atmospherics/component/unary/heater/ui_act(action, list/params, datum/tgui/ui)
/obj/machinery/atmospherics/component/unary/heater/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state, datum/event_args/actor/actor)
if(..())
return TRUE

Some files were not shown because too many files have changed in this diff Show More