mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into make-emotes-local
This commit is contained in:
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -2,6 +2,8 @@
|
||||
"recommendations": [
|
||||
"gbasood.byond-dm-language-support",
|
||||
"platymuus.dm-langclient",
|
||||
"EditorConfig.EditorConfig"
|
||||
"EditorConfig.EditorConfig",
|
||||
"arcanis.vscode-zipfs",
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
}
|
||||
|
||||
18
.vscode/settings.json
vendored
Normal file
18
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"eslint.nodePath": "tgui/.yarn/sdks",
|
||||
"eslint.workingDirectories": [
|
||||
"./tgui"
|
||||
],
|
||||
"search.exclude": {
|
||||
"tgui/.yarn": true,
|
||||
"tgui/.pnp.*": true
|
||||
},
|
||||
"workbench.editorAssociations": [
|
||||
{
|
||||
"filenamePattern": "*.dmi",
|
||||
"viewType": "imagePreview.previewEditor"
|
||||
}
|
||||
],
|
||||
"files.eol": "\n",
|
||||
"gitlens.advanced.blame.customArguments": ["-w"]
|
||||
}
|
||||
@@ -56600,10 +56600,6 @@
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/brig)
|
||||
"lxP" = (
|
||||
/mob/living/simple_animal/opossum,
|
||||
/turf/closed/wall,
|
||||
/area/science/xenobiology)
|
||||
"lzt" = (
|
||||
/obj/machinery/portable_atmospherics/pump,
|
||||
/obj/effect/turf_decal/stripes/line,
|
||||
@@ -107389,7 +107385,7 @@ bLe
|
||||
bMr
|
||||
bNr
|
||||
bIT
|
||||
lxP
|
||||
bJN
|
||||
bJN
|
||||
bJN
|
||||
bJN
|
||||
|
||||
@@ -162,3 +162,6 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
||||
REMOVE_TRAIT(x, TRAIT_KEEP_TOGETHER, KEEP_TOGETHER_ORIGINAL);\
|
||||
else if(!HAS_TRAIT(x, TRAIT_KEEP_TOGETHER))\
|
||||
x.appearance_flags &= ~KEEP_TOGETHER
|
||||
|
||||
/// 33554431 (2^24 - 1) is the maximum value our bitflags can reach.
|
||||
#define MAX_BITFLAG_DIGITS 8
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
#define TRAIT_THERMAL_VISION "thermal_vision"
|
||||
#define TRAIT_NO_TELEPORT "no-teleport" //you just can't
|
||||
#define TRAIT_NO_INTERNALS "no-internals"
|
||||
#define TRAIT_NO_ALCOHOL "alcohol_intolerance"
|
||||
#define TRAIT_TOXIC_ALCOHOL "alcohol_intolerance"
|
||||
#define TRAIT_MUTATION_STASIS "mutation_stasis" //Prevents processed genetics mutations from processing.
|
||||
#define TRAIT_FAST_PUMP "fast_pump"
|
||||
#define TRAIT_NO_PROCESS_FOOD "no-process-food" // You don't get benefits from nutriment, nor nutrition from reagent consumables
|
||||
|
||||
@@ -104,7 +104,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"TRAIT_AUTO_CATCH_ITEM" = TRAIT_AUTO_CATCH_ITEM,
|
||||
"TRAIT_FREESPRINT" = TRAIT_FREESPRINT,
|
||||
"TRAIT_NO_INTERNALS" = TRAIT_NO_INTERNALS,
|
||||
"TRAIT_NO_ALCOHOL" = TRAIT_NO_ALCOHOL,
|
||||
"TRAIT_TOXIC_ALCOHOL" = TRAIT_TOXIC_ALCOHOL,
|
||||
"TRAIT_MUTATION_STASIS" = TRAIT_MUTATION_STASIS,
|
||||
"TRAIT_HEAVY_SLEEPER" = TRAIT_HEAVY_SLEEPER,
|
||||
"TRAIT_LIGHT_STEP" = TRAIT_LIGHT_STEP,
|
||||
|
||||
@@ -391,6 +391,8 @@ Example config:
|
||||
for(var/T in storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
var/config_tag = initial(S.config_tag)
|
||||
if(!config_tag)
|
||||
continue
|
||||
var/probability = (config_tag in probabilities) ? probabilities[config_tag] : initial(S.weight)
|
||||
var/min_players = (config_tag in min_player_counts) ? min_player_counts[config_tag] : initial(S.min_players)
|
||||
if(probability <= 0)
|
||||
|
||||
@@ -6,6 +6,12 @@ SUBSYSTEM_DEF(input)
|
||||
priority = FIRE_PRIORITY_INPUT
|
||||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
|
||||
|
||||
/// KEEP THIS UP TO DATE!
|
||||
var/static/list/all_macrosets = list(
|
||||
SKIN_MACROSET_HOTKEYS,
|
||||
SKIN_MACROSET_CLASSIC_HOTKEYS,
|
||||
SKIN_MACROSET_CLASSIC_INPUT
|
||||
)
|
||||
/// Classic mode input focused macro set. Manually set because we can't define ANY or ANY+UP for classic.
|
||||
var/static/list/macroset_classic_input
|
||||
/// Classic mode map focused macro set. Manually set because it needs to be clientside and go to macroset_classic_input.
|
||||
@@ -51,11 +57,6 @@ SUBSYSTEM_DEF(input)
|
||||
// let's play the ascii game of A to Z (UPPERCASE)
|
||||
for(var/i in 65 to 90)
|
||||
classic_ctrl_override_keys += ascii2text(i)
|
||||
// let's play the game of clientside bind overrides!
|
||||
classic_ctrl_override_keys -= list("T", "O", "M", "L")
|
||||
macroset_classic_input["Ctrl+T"] = "say"
|
||||
macroset_classic_input["Ctrl+O"] = "ooc"
|
||||
macroset_classic_input["Ctrl+L"] = "looc"
|
||||
// let's play the list iteration game x2
|
||||
for(var/key in classic_ctrl_override_keys)
|
||||
// make sure to double double quote to ensure things are treated as a key combo instead of addition/semicolon logic.
|
||||
@@ -67,20 +68,6 @@ SUBSYSTEM_DEF(input)
|
||||
|
||||
// FINALLY, WE CAN DO SOMETHING MORE NORMAL FOR THE SNOWFLAKE-BUT-LESS KEYSET.
|
||||
|
||||
// HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when
|
||||
// conflicts occur with something like say indicator vs say), we're going to snowflake this anyways
|
||||
var/list/hard_binds = list(
|
||||
"O" = "ooc",
|
||||
"T" = "say",
|
||||
"L" = "looc",
|
||||
"M" = "me"
|
||||
)
|
||||
var/list/hard_bind_anti_collision = list()
|
||||
var/list/anti_collision_modifiers = list("Ctrl", "Alt", "Shift", "Ctrl+Alt", "Ctrl+Shift", "Alt+Shift", "Ctrl+Alt+Shift")
|
||||
for(var/key in hard_binds)
|
||||
for(var/modifier in anti_collision_modifiers)
|
||||
hard_bind_anti_collision["[modifier]+[key]"] = ".NONSENSICAL_VERB_THAT_DOES_NOTHING"
|
||||
|
||||
macroset_classic_hotkey = list(
|
||||
"Any" = "\"KeyDown \[\[*\]\]\"",
|
||||
"Any+UP" = "\"KeyUp \[\[*\]\]\"",
|
||||
@@ -89,9 +76,6 @@ SUBSYSTEM_DEF(input)
|
||||
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
|
||||
)
|
||||
|
||||
macroset_classic_hotkey |= hard_binds
|
||||
macroset_classic_hotkey |= hard_bind_anti_collision
|
||||
|
||||
// And finally, the modern set.
|
||||
macroset_hotkey = list(
|
||||
"Any" = "\"KeyDown \[\[*\]\]\"",
|
||||
@@ -101,16 +85,12 @@ SUBSYSTEM_DEF(input)
|
||||
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
|
||||
)
|
||||
|
||||
macroset_hotkey |= hard_binds
|
||||
macroset_hotkey |= hard_bind_anti_collision
|
||||
|
||||
// Badmins just wanna have fun ♪
|
||||
/datum/controller/subsystem/input/proc/refresh_client_macro_sets()
|
||||
var/list/clients = GLOB.clients
|
||||
for(var/i in 1 to clients.len)
|
||||
var/client/user = clients[i]
|
||||
user.set_macros()
|
||||
user.update_movement_keys()
|
||||
user.full_macro_assert()
|
||||
|
||||
/datum/controller/subsystem/input/fire()
|
||||
var/list/clients = GLOB.clients // Let's sing the list cache song
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
name = "Alcohol Intolerance"
|
||||
desc = "You take toxin damage from alcohol rather than getting drunk."
|
||||
value = 0
|
||||
mob_trait = TRAIT_NO_ALCOHOL
|
||||
mob_trait = TRAIT_TOXIC_ALCOHOL
|
||||
medical_record_text = "Patient's body does not react properly to ethyl alcohol."
|
||||
|
||||
/datum/quirk/alcohol_intolerance/add()
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
R = rank_names[new_rank]
|
||||
if(!R) //rank with that name doesn't exist yet - make it
|
||||
if(D)
|
||||
R = new(new_rank, D.rank.rights) //duplicate our previous admin_rank but with a new name
|
||||
R = new(new_rank, D.rank.rights, D.rank.exclude_rights, D.rank.can_edit_rights) //duplicate our previous admin_rank but with a new name
|
||||
else
|
||||
R = new(new_rank) //blank new admin_rank
|
||||
GLOB.admin_ranks += R
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
var/ntype = text2num(type)
|
||||
|
||||
//Add client links
|
||||
var/dat = ""
|
||||
var/list/dat = list()
|
||||
if(M.client)
|
||||
dat += "<center><p>Client</p></center>"
|
||||
dat += "<center>"
|
||||
@@ -46,22 +46,27 @@
|
||||
var/log_source = M.logging;
|
||||
if(source == LOGSRC_CLIENT && M.client) //if client doesn't exist just fall back to the mob log
|
||||
log_source = M.client.player_details.logging //should exist, if it doesn't that's a bug, don't check for it not existing
|
||||
|
||||
var/list/concatenated_logs = list()
|
||||
for(var/log_type in log_source)
|
||||
var/nlog_type = text2num(log_type)
|
||||
if(nlog_type & ntype)
|
||||
var/list/reversed = log_source[log_type]
|
||||
if(islist(reversed))
|
||||
reversed = reverseRange(reversed.Copy())
|
||||
for(var/entry in reversed)
|
||||
dat += "<font size=2px><b>[entry]</b><br>[reversed[entry]]</font><br>"
|
||||
dat += "<hr>"
|
||||
var/list/all_the_entrys = log_source[log_type]
|
||||
for(var/entry in all_the_entrys)
|
||||
concatenated_logs += "<b>[entry]</b><br>[all_the_entrys[entry]]"
|
||||
if(length(concatenated_logs))
|
||||
sortTim(concatenated_logs, cmp = /proc/cmp_text_dsc) //Sort by timestamp.
|
||||
dat += "<font size=2px>"
|
||||
dat += concatenated_logs.Join("<br>")
|
||||
dat += "</font>"
|
||||
|
||||
usr << browse(dat, "window=invidual_logging_[key_name(M)];size=600x480")
|
||||
var/datum/browser/popup = new(usr, "invidual_logging_[key_name(M)]", "Individual Logs", 600, 600)
|
||||
popup.set_content(dat.Join())
|
||||
popup.open()
|
||||
|
||||
/proc/individual_logging_panel_link(mob/M, log_type, log_src, label, selected_src, selected_type)
|
||||
var/slabel = label
|
||||
if(selected_type == log_type && selected_src == log_src)
|
||||
slabel = "<b>\[[label]\]</b>"
|
||||
|
||||
//This is necessary because num2text drops digits and rounds on big numbers. If more defines get added in the future it could break again.
|
||||
log_type = num2text(log_type, MAX_BITFLAG_DIGITS)
|
||||
return "<a href='?_src_=holder;[HrefToken()];individuallog=[REF(M)];log_type=[log_type];log_src=[log_src]'>[slabel]</a>"
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
var/list/stored_profiles = list() //list of datum/changelingprofile
|
||||
var/datum/changelingprofile/first_prof = null
|
||||
var/dna_max = 6 //How many extra DNA strands the changeling can store for transformation.
|
||||
var/absorbedcount = 0
|
||||
/// did we get succed by another changeling
|
||||
var/hostile_absorbed = FALSE
|
||||
@@ -252,12 +251,6 @@
|
||||
var/mob/living/carbon/user = owner.current
|
||||
if(!istype(user))
|
||||
return
|
||||
if(stored_profiles.len)
|
||||
var/datum/changelingprofile/prof = stored_profiles[1]
|
||||
if(prof.dna == user.dna && stored_profiles.len >= dna_max)//If our current DNA is the stalest, we gotta ditch it.
|
||||
if(verbose)
|
||||
to_chat(user, "<span class='warning'>We have reached our capacity to store genetic information! We must transform before absorbing more.</span>")
|
||||
return
|
||||
if(!target)
|
||||
return
|
||||
if(NO_DNA_COPY in target.dna.species.species_traits)
|
||||
@@ -317,9 +310,6 @@
|
||||
return prof
|
||||
|
||||
/datum/antagonist/changeling/proc/add_profile(datum/changelingprofile/prof)
|
||||
if(stored_profiles.len > dna_max)
|
||||
if(!push_out_profile())
|
||||
return
|
||||
|
||||
if(!first_prof)
|
||||
first_prof = prof
|
||||
@@ -340,19 +330,6 @@
|
||||
stored_profiles -= prof
|
||||
qdel(prof)
|
||||
|
||||
/datum/antagonist/changeling/proc/get_profile_to_remove()
|
||||
for(var/datum/changelingprofile/prof in stored_profiles)
|
||||
if(!prof.protected)
|
||||
return prof
|
||||
|
||||
/datum/antagonist/changeling/proc/push_out_profile()
|
||||
var/datum/changelingprofile/removeprofile = get_profile_to_remove()
|
||||
if(removeprofile)
|
||||
stored_profiles -= removeprofile
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/datum/antagonist/changeling/proc/create_initial_profile()
|
||||
var/mob/living/carbon/C = owner.current //only carbons have dna now, so we have to typecaste
|
||||
if(ishuman(C))
|
||||
|
||||
@@ -679,6 +679,7 @@
|
||||
item_state = "ling_gauntlets"
|
||||
desc = "Rough bone and chitin, pulsing with an abomination barely called \"life\". Good for punching people, not so much for firearms."
|
||||
transfer_prints = TRUE
|
||||
item_flags = DROPDEL // whoops
|
||||
body_parts_covered = ARMS|HANDS
|
||||
cold_protection = ARMS|HANDS
|
||||
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
|
||||
@@ -691,13 +692,13 @@
|
||||
var/slow_enhancement = 20
|
||||
var/slow_wound_enhancement = 20
|
||||
silent = TRUE
|
||||
inherited_trait = TRAIT_CHUNKYFINGERS // dummy thicc bone hands
|
||||
secondary_trait = TRAIT_MAULER // its only violence from here, bucko
|
||||
inherited_trait = TRAIT_CHUNKYFINGERS // how do you expect to shoot anyone with bone covered hands
|
||||
secondary_trait = TRAIT_MAULER // just punch them idiot
|
||||
var/fasthands = TRUE
|
||||
|
||||
/obj/item/clothing/gloves/fingerless/pugilist/cling/examine(mob/user)
|
||||
. = ..()
|
||||
. += "[src] are formed to allow for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"]."
|
||||
. += "[src] are shaped for [fasthands ? "fast, precise strikes" : "crippling, damaging blows"]."
|
||||
. += "Alt-click them to change between rapid strikes and strong blows."
|
||||
|
||||
/obj/item/clothing/gloves/fingerless/pugilist/cling/AltClick(mob/user)
|
||||
|
||||
@@ -136,6 +136,7 @@
|
||||
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
|
||||
// slightly better than normal cult robes
|
||||
armor = list("melee" = 50, "bullet" = 50, "laser" = 50,"energy" = 50, "bomb" = 35, "bio" = 20, "rad" = 0, "fire" = 20, "acid" = 20)
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
|
||||
/obj/item/reagent_containers/glass/beaker/eldritch
|
||||
name = "flask of eldritch essence"
|
||||
|
||||
@@ -89,6 +89,8 @@
|
||||
|
||||
var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen.
|
||||
|
||||
/// Last time they used fix macros
|
||||
var/last_macro_fix = 0
|
||||
/// Keys currently held
|
||||
var/list/keys_held = list()
|
||||
/// These next two vars are to apply movement for keypresses and releases made while move delayed.
|
||||
|
||||
@@ -269,7 +269,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
prefs = new /datum/preferences(src)
|
||||
GLOB.preferences_datums[ckey] = prefs
|
||||
|
||||
addtimer(CALLBACK(src, .proc/ensure_keys_set), 10) //prevents possible race conditions
|
||||
addtimer(CALLBACK(src, .proc/ensure_keys_set, prefs), 10) //prevents possible race conditions
|
||||
|
||||
prefs.last_ip = address //these are gonna be used for banning
|
||||
prefs.last_id = computer_id //these are gonna be used for banning
|
||||
@@ -443,7 +443,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them.
|
||||
to_chat(src, "<span class='warning'>Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.</span>")
|
||||
|
||||
|
||||
//This is down here because of the browse() calls in tooltip/New()
|
||||
if(!tooltips)
|
||||
tooltips = new /datum/tooltip(src)
|
||||
@@ -475,11 +474,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
fit_viewport()
|
||||
Master.UpdateTickRate()
|
||||
|
||||
/client/proc/ensure_keys_set()
|
||||
if(SSinput.initialized)
|
||||
set_macros()
|
||||
update_movement_keys(prefs)
|
||||
|
||||
//////////////
|
||||
//DISCONNECT//
|
||||
//////////////
|
||||
@@ -937,23 +931,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
y = clamp(y+change, min,max)
|
||||
view_size.setDefault("[x]x[y]")
|
||||
|
||||
/client/proc/update_movement_keys(datum/preferences/direct_prefs)
|
||||
var/datum/preferences/D = prefs || direct_prefs
|
||||
if(!D?.key_bindings)
|
||||
return
|
||||
movement_keys = list()
|
||||
for(var/key in D.key_bindings)
|
||||
for(var/kb_name in D.key_bindings[key])
|
||||
switch(kb_name)
|
||||
if("North")
|
||||
movement_keys[key] = NORTH
|
||||
if("East")
|
||||
movement_keys[key] = EAST
|
||||
if("West")
|
||||
movement_keys[key] = WEST
|
||||
if("South")
|
||||
movement_keys[key] = SOUTH
|
||||
|
||||
/client/proc/change_view(new_size)
|
||||
if (isnull(new_size))
|
||||
CRASH("change_view called without argument.")
|
||||
|
||||
@@ -53,7 +53,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
/// List with a key string associated to a list of keybindings. Unlike key_bindings, this one operates on raw key, allowing for binding a key that triggers regardless of if a modifier is depressed as long as the raw key is sent.
|
||||
var/list/modless_key_bindings = list()
|
||||
|
||||
|
||||
var/tgui_fancy = TRUE
|
||||
var/tgui_lock = TRUE
|
||||
var/windowflashing = TRUE
|
||||
@@ -219,7 +218,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
//we couldn't load character data so just randomize the character appearance + name
|
||||
random_character() //let's create a random character then - rather than a fat, bald and naked man.
|
||||
key_bindings = deepCopyList(GLOB.hotkey_keybinding_list_by_key) // give them default keybinds and update their movement keys
|
||||
C?.update_movement_keys(src)
|
||||
C?.ensure_keys_set(src)
|
||||
real_name = pref_species.random_name(gender,1)
|
||||
if(!loaded_preferences_successfully)
|
||||
save_preferences()
|
||||
@@ -952,7 +951,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/list/default_keys = hotkeys ? kb.hotkey_keys : kb.classic_keys
|
||||
if(LAZYLEN(default_keys))
|
||||
dat += "| Default: [default_keys.Join(", ")]"
|
||||
dat += "</span><span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
|
||||
dat += "</span>"
|
||||
if(!kb.special && !kb.clientside)
|
||||
dat += "<span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
|
||||
dat += "<br>"
|
||||
else
|
||||
var/bound_key = user_binds[kb.name][1]
|
||||
@@ -965,7 +966,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/list/default_keys = hotkeys ? kb.classic_keys : kb.hotkey_keys
|
||||
if(LAZYLEN(default_keys))
|
||||
dat += "| Default: [default_keys.Join(", ")]"
|
||||
dat += "</span><span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
|
||||
dat += "</span>"
|
||||
if(!kb.special && !kb.clientside)
|
||||
dat += "<span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
|
||||
dat += "<br>"
|
||||
|
||||
dat += "<br><br>"
|
||||
@@ -991,7 +994,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
#undef APPEARANCE_CATEGORY_COLUMN
|
||||
#undef MAX_MUTANT_ROWS
|
||||
|
||||
/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, old_key, independent = FALSE)
|
||||
/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, old_key, independent = FALSE, special = FALSE)
|
||||
var/HTML = {"
|
||||
<div id='focus' style="outline: 0;" tabindex=0>Keybinding: [kb.full_name]<br>[kb.description]<br><br><b>Press any key to change<br>Press ESC to clear</b></div>
|
||||
<script>
|
||||
@@ -1003,7 +1006,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var shift = e.shiftKey ? 1 : 0;
|
||||
var numpad = (95 < e.keyCode && e.keyCode < 112) ? 1 : 0;
|
||||
var escPressed = e.keyCode == 27 ? 1 : 0;
|
||||
var url = 'byond://?_src_=prefs;preference=keybindings_set;keybinding=[kb.name];old_key=[old_key];[independent?"independent=1":""];clear_key='+escPressed+';key='+e.key+';alt='+alt+';ctrl='+ctrl+';shift='+shift+';numpad='+numpad+';key_code='+e.keyCode;
|
||||
var url = 'byond://?_src_=prefs;preference=keybindings_set;keybinding=[kb.name];old_key=[old_key];[independent?"independent=1;":""][special?"special=1;":""]clear_key='+escPressed+';key='+e.key+';alt='+alt+';ctrl='+ctrl+';shift='+shift+';numpad='+numpad+';key_code='+e.keyCode;
|
||||
window.location=url;
|
||||
deedDone = true;
|
||||
}
|
||||
@@ -2354,11 +2357,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
|
||||
if("hotkeys")
|
||||
hotkeys = !hotkeys
|
||||
user.client.set_macros()
|
||||
user.client.ensure_keys_set(src)
|
||||
|
||||
if("keybindings_capture")
|
||||
var/datum/keybinding/kb = GLOB.keybindings_by_name[href_list["keybinding"]]
|
||||
CaptureKeybinding(user, kb, href_list["old_key"], text2num(href_list["independent"]))
|
||||
CaptureKeybinding(user, kb, href_list["old_key"], text2num(href_list["independent"]), kb.special || kb.clientside)
|
||||
return
|
||||
|
||||
if("keybindings_set")
|
||||
@@ -2378,9 +2381,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
else
|
||||
if(key_bindings[old_key])
|
||||
key_bindings[old_key] -= kb_name
|
||||
LAZYADD(key_bindings["Unbound"], kb_name)
|
||||
if(!length(key_bindings[old_key]))
|
||||
key_bindings -= old_key
|
||||
user << browse(null, "window=capturekeypress")
|
||||
if(href_list["special"]) // special keys need a full reset
|
||||
user.client.ensure_keys_set(src)
|
||||
save_preferences()
|
||||
ShowChoices(user)
|
||||
return
|
||||
@@ -2415,7 +2421,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
key_bindings -= old_key
|
||||
key_bindings[full_key] += list(kb_name)
|
||||
key_bindings[full_key] = sortList(key_bindings[full_key])
|
||||
user.client.update_movement_keys()
|
||||
if(href_list["special"]) // special keys need a full reset
|
||||
user.client.ensure_keys_set(src)
|
||||
user << browse(null, "window=capturekeypress")
|
||||
save_preferences()
|
||||
|
||||
@@ -2427,7 +2434,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
hotkeys = (choice == "Hotkey")
|
||||
key_bindings = (hotkeys) ? deepCopyList(GLOB.hotkey_keybinding_list_by_key) : deepCopyList(GLOB.classic_keybinding_list_by_key)
|
||||
modless_key_bindings = list()
|
||||
user.client.update_movement_keys()
|
||||
user.client.ensure_keys_set(src)
|
||||
|
||||
if("chat_on_map")
|
||||
chat_on_map = !chat_on_map
|
||||
@@ -2827,17 +2834,21 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
cached_holoform_icons[filter_type] = process_holoform_icon_filter(custom_holoform_icon, filter_type)
|
||||
return cached_holoform_icons[filter_type]
|
||||
|
||||
//Used in savefile update 32, can be removed once that is no longer relevant.
|
||||
/// Resets the client's keybindings. Asks them for which
|
||||
/datum/preferences/proc/force_reset_keybindings()
|
||||
var/choice = tgalert(parent.mob, "Your basic keybindings need to be reset, emotes will remain as before. Would you prefer 'hotkey' or 'classic' mode?", "Reset keybindings", "Hotkey", "Classic")
|
||||
hotkeys = (choice != "Classic")
|
||||
force_reset_keybindings_direct(hotkeys)
|
||||
|
||||
/// Does the actual reset
|
||||
/datum/preferences/proc/force_reset_keybindings_direct(hotkeys = TRUE)
|
||||
var/list/oldkeys = key_bindings
|
||||
key_bindings = (hotkeys) ? deepCopyList(GLOB.hotkey_keybinding_list_by_key) : deepCopyList(GLOB.classic_keybinding_list_by_key)
|
||||
|
||||
for(var/key in oldkeys)
|
||||
if(!key_bindings[key])
|
||||
key_bindings[key] = oldkeys[key]
|
||||
parent.update_movement_keys()
|
||||
parent?.ensure_keys_set(src)
|
||||
|
||||
/datum/preferences/proc/is_loadout_slot_available(slot)
|
||||
var/list/L
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// You do not need to raise this if you are adding new values that have sane defaults.
|
||||
// Only raise this value when changing the meaning/format/name/layout of an existing value
|
||||
// where you would want the updater procs below to run
|
||||
#define SAVEFILE_VERSION_MAX 43
|
||||
#define SAVEFILE_VERSION_MAX 46
|
||||
|
||||
/*
|
||||
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
||||
@@ -42,7 +42,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
//if your savefile is 3 months out of date, then 'tough shit'.
|
||||
|
||||
/datum/preferences/proc/update_preferences(current_version, savefile/S)
|
||||
if(current_version < 32) //If you remove this, remove force_reset_keybindings() too.
|
||||
if(current_version < 46) //If you remove this, remove force_reset_keybindings() too.
|
||||
force_reset_keybindings_direct(TRUE)
|
||||
addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
|
||||
|
||||
/datum/preferences/proc/update_character(current_version, savefile/S)
|
||||
|
||||
@@ -50,7 +50,7 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC")
|
||||
|
||||
msg = emoji_parse(msg)
|
||||
|
||||
mob.log_talk(msg,LOG_OOC, tag="(LOOC)")
|
||||
mob.log_talk(msg,LOG_OOC, tag="LOOC")
|
||||
|
||||
var/list/heard = get_hearers_in_view(7, get_top_level_mob(src.mob))
|
||||
for(var/mob/M in heard)
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
name = "long red forensics coat"
|
||||
icon_state = "forensics_red_long"
|
||||
item_state = "forensics_red_long"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE
|
||||
|
||||
/obj/item/clothing/suit/det_suit/forensicsblue
|
||||
name = "blue forensics coat"
|
||||
@@ -136,6 +137,7 @@
|
||||
name = "long blue forensics coat"
|
||||
icon_state = "forensics_blue_long"
|
||||
item_state = "forensics_blue_long"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE
|
||||
|
||||
|
||||
//Engineering
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
icon_state = "labcoat_tox"
|
||||
|
||||
// Departmental Jackets
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
|
||||
/obj/item/clothing/suit/toggle/labcoat/depjacket/sci
|
||||
name = "science jacket"
|
||||
|
||||
@@ -99,18 +99,21 @@
|
||||
desc = "A rather impractical, long coat."
|
||||
icon_state = "tailcoat"
|
||||
item_state = "tailcoat"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
|
||||
/obj/item/clothing/suit/vickyblack
|
||||
name = "black victorian coat"
|
||||
desc = "An overbearing black coat, it looks far older than you are."
|
||||
icon_state = "vickyblack"
|
||||
item_state = "vickyblack"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
|
||||
/obj/item/clothing/suit/vickyred
|
||||
name = "red victorian coat"
|
||||
desc = "An overbearing red coat, it looks far older than you are."
|
||||
icon_state = "vickyred"
|
||||
item_state = "vickyred"
|
||||
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
|
||||
|
||||
/obj/item/clothing/suit/apron/overalls
|
||||
name = "coveralls"
|
||||
|
||||
@@ -168,7 +168,6 @@
|
||||
desc = "A simple, inconspicuous harness replacement for a jumpsuit."
|
||||
icon_state = "gear_harness"
|
||||
item_state = "gear_harness"
|
||||
body_parts_covered = CHEST|GROIN
|
||||
can_adjust = FALSE
|
||||
|
||||
/obj/item/clothing/under/misc/durathread
|
||||
@@ -236,7 +235,7 @@
|
||||
icon_state = "tssuit"
|
||||
item_state = "r_suit"
|
||||
can_adjust = FALSE
|
||||
mutantrace_variation = USE_TAUR_CLIP_MASK
|
||||
mutantrace_variation = STYLE_DIGITIGRADE
|
||||
|
||||
/obj/item/clothing/under/misc/poly_shirt
|
||||
name = "polychromic button-up shirt"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "Spontaneous Brain Trauma"
|
||||
typepath = /datum/round_event/brain_trauma
|
||||
weight = 25
|
||||
min_players = 5
|
||||
|
||||
/datum/round_event_control/brain_trauma/canSpawnEvent(var/players_amt, var/gamemode)
|
||||
var/list/enemy_roles = list("Medical Doctor","Chief Medical Officer","Paramedic")
|
||||
|
||||
@@ -554,7 +554,7 @@ Since Ramadan is an entire month that lasts 29.5 days on average, the start and
|
||||
|
||||
/datum/holiday/xmas
|
||||
name = CHRISTMAS
|
||||
begin_day = 22
|
||||
begin_day = 10
|
||||
begin_month = DECEMBER
|
||||
end_day = 27
|
||||
drone_hat = /obj/item/clothing/head/santa
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define CATEGORY_MOVEMENT "MOVEMENT"
|
||||
#define CATEGORY_TARGETING "TARGETING"
|
||||
#define CATEGORY_COMBAT "COMBAT"
|
||||
#define CATEGORY_COMMUNICATION "COMMUNICATION"
|
||||
|
||||
#define WEIGHT_HIGHEST 0
|
||||
#define WEIGHT_ADMIN 10
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
var/category = CATEGORY_MISC
|
||||
var/weight = WEIGHT_LOWEST
|
||||
var/keybind_signal
|
||||
/// Is this a clientside verb trigger? If so, this should be set to the name of the verb.
|
||||
var/clientside
|
||||
/// Special - Needs to update special keys on update. clientside implis special.
|
||||
var/special = FALSE
|
||||
|
||||
/datum/keybinding/New()
|
||||
|
||||
|
||||
71
code/modules/keybindings/keybind/communication.dm
Normal file
71
code/modules/keybindings/keybind/communication.dm
Normal file
@@ -0,0 +1,71 @@
|
||||
/datum/keybinding/client/communication
|
||||
category = CATEGORY_COMMUNICATION
|
||||
|
||||
/datum/keybinding/client/communication/say
|
||||
hotkey_keys = list("CtrlT")
|
||||
name = "Say"
|
||||
full_name = "IC Say"
|
||||
clientside = "say"
|
||||
|
||||
/datum/keybinding/client/communication/ooc
|
||||
hotkey_keys = list("O")
|
||||
name = "OOC"
|
||||
full_name = "Out Of Character Say (OOC)"
|
||||
clientside = "ooc"
|
||||
|
||||
/datum/keybinding/client/communication/me
|
||||
hotkey_keys = list("CtrlM")
|
||||
name = "Me"
|
||||
full_name = "Me (emote)"
|
||||
clientside = "me"
|
||||
|
||||
//indicators
|
||||
/datum/keybinding/client/communication/say_with_indicator
|
||||
hotkey_keys = list("T")
|
||||
classic_keys = list()
|
||||
name = "say_with_indicator"
|
||||
full_name = "Say with Typing Indicator"
|
||||
|
||||
/datum/keybinding/client/communication/say_with_indicator/down(client/user)
|
||||
var/mob/M = user.mob
|
||||
M.say_typing_indicator()
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/client/communication/me_with_indicator
|
||||
hotkey_keys = list("M")
|
||||
classic_keys = list()
|
||||
name = "me_with_indicator"
|
||||
full_name = "Me (emote) with Typing Indicator"
|
||||
|
||||
/datum/keybinding/client/communication/me_with_indicator/down(client/user)
|
||||
var/mob/M = user.mob
|
||||
M.me_typing_indicator()
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/client/communication/subtle
|
||||
hotkey_keys = list("5")
|
||||
classic_keys = list()
|
||||
name = "Subtle"
|
||||
full_name = "Subtle Emote"
|
||||
clientside = "subtle"
|
||||
|
||||
/datum/keybinding/client/communication/subtler
|
||||
hotkey_keys = list("6")
|
||||
classic_keys = list()
|
||||
name = "Subtler"
|
||||
full_name = "Subtler Anti-Ghost Emote"
|
||||
clientside = "subtler-anti-ghost"
|
||||
|
||||
/datum/keybinding/client/communication/whisper
|
||||
hotkey_keys = list("Y")
|
||||
classic_keys = list()
|
||||
name = "Whisper"
|
||||
full_name = "Whisper"
|
||||
clientside = "whisper"
|
||||
|
||||
/datum/keybinding/client/communication/looc
|
||||
hotkey_keys = list("L")
|
||||
classic_keys = list()
|
||||
name = "LOOC"
|
||||
full_name = "Local Out of Character chat"
|
||||
clientside = "looc"
|
||||
@@ -76,59 +76,3 @@
|
||||
else
|
||||
user.mob.dropItemToGround(I)
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/mob/say_with_indicator
|
||||
hotkey_keys = list("CtrlT")
|
||||
classic_keys = list()
|
||||
name = "say_with_indicator"
|
||||
full_name = "Say with Typing Indicator"
|
||||
|
||||
/datum/keybinding/mob/say_with_indicator/down(client/user)
|
||||
var/mob/M = user.mob
|
||||
M.say_typing_indicator()
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/mob/me_with_indicator
|
||||
hotkey_keys = list("CtrlM")
|
||||
classic_keys = list()
|
||||
name = "me_with_indicator"
|
||||
full_name = "Me (emote) with Typing Indicator"
|
||||
|
||||
/datum/keybinding/mob/me_with_indicator/down(client/user)
|
||||
var/mob/M = user.mob
|
||||
M.me_typing_indicator()
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/living/subtle
|
||||
hotkey_keys = list("5")
|
||||
classic_keys = list()
|
||||
name = "subtle_emote"
|
||||
full_name = "Subtle Emote"
|
||||
|
||||
/datum/keybinding/living/subtle/down(client/user)
|
||||
var/mob/living/L = user.mob
|
||||
L.subtle()
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/living/subtler
|
||||
hotkey_keys = list("6")
|
||||
classic_keys = list()
|
||||
name = "subtler_emote"
|
||||
full_name = "Subtler Anti-Ghost Emote"
|
||||
|
||||
/datum/keybinding/living/subtler/down(client/user)
|
||||
var/mob/living/L = user.mob
|
||||
L.subtler()
|
||||
return TRUE
|
||||
|
||||
/datum/keybinding/mob/whisper
|
||||
hotkey_keys = list("Y")
|
||||
classic_keys = list()
|
||||
name = "whisper"
|
||||
full_name = "Whisper"
|
||||
|
||||
/datum/keybinding/mob/whisper/down(client/user)
|
||||
var/mob/M = user.mob
|
||||
M.whisper_keybind()
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/datum/keybinding/movement
|
||||
category = CATEGORY_MOVEMENT
|
||||
weight = WEIGHT_HIGHEST
|
||||
special = TRUE
|
||||
|
||||
/datum/keybinding/movement/north
|
||||
hotkey_keys = list("W", "North")
|
||||
|
||||
@@ -7,10 +7,28 @@
|
||||
/datum/proc/keyLoop(client/user) // Called once every frame
|
||||
//SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
/client/verb/fix_macros()
|
||||
set name = "Fix Keybindings"
|
||||
set desc = "Re-assert all your macros/keybindings."
|
||||
set category = "OOC"
|
||||
if(last_macro_fix > (world.time - 10 SECONDS))
|
||||
to_chat(src, "<span class='warning'>It's been too long since the last reset. Wait a while.</span>")
|
||||
return
|
||||
if(!SSinput.initialized)
|
||||
to_chat(src, "<span class='warning'>Input hasn't been initialized yet. Wait a while.</span>")
|
||||
return
|
||||
to_chat(src, "<span class='danger'>Force-reasserting all macros.</span>")
|
||||
last_macro_fix = world.time
|
||||
full_macro_assert()
|
||||
|
||||
// removes all the existing macros
|
||||
/client/proc/erase_all_macros()
|
||||
var/erase_output = ""
|
||||
var/list/macro_set = params2list(winget(src, "default.*", "command")) // The third arg doesnt matter here as we're just removing them all
|
||||
var/list/set_text = list()
|
||||
for(var/macroset in SSinput.all_macrosets)
|
||||
set_text += "[macroset].*"
|
||||
set_text = set_text.Join(";")
|
||||
var/list/macro_set = params2list(winget(src, "[set_text]", "command"))
|
||||
for(var/k in 1 to length(macro_set))
|
||||
var/list/split_name = splittext(macro_set[k], ".")
|
||||
var/macro_name = "[split_name[1]].[split_name[2]]" // [3] is "command"
|
||||
@@ -27,20 +45,74 @@
|
||||
winset(src, "[name]-[REF(key)]", "parent=[name];name=[key];command=[command]")
|
||||
|
||||
/client/proc/set_macros(datum/preferences/prefs_override = prefs)
|
||||
set waitfor = FALSE
|
||||
|
||||
keys_held.Cut()
|
||||
|
||||
erase_all_macros()
|
||||
|
||||
apply_macro_set(SKIN_MACROSET_HOTKEYS, SSinput.macroset_hotkey)
|
||||
apply_macro_set(SKIN_MACROSET_CLASSIC_HOTKEYS, SSinput.macroset_classic_hotkey)
|
||||
apply_macro_set(SKIN_MACROSET_CLASSIC_INPUT, SSinput.macroset_classic_input)
|
||||
|
||||
set_hotkeys_preference()
|
||||
|
||||
/client/proc/set_hotkeys_preference(datum/preferences/prefs_override = prefs)
|
||||
if(prefs_override.hotkeys)
|
||||
winset(src, null, "map.focus=true input.background-color=[COLOR_INPUT_DISABLED] mainwindow.macro=[SKIN_MACROSET_HOTKEYS]")
|
||||
else
|
||||
winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=[SKIN_MACROSET_CLASSIC_INPUT]")
|
||||
|
||||
/client/proc/ensure_keys_set(datum/preferences/prefs_override = prefs)
|
||||
if(SSinput.initialized)
|
||||
full_macro_assert(prefs_override)
|
||||
|
||||
/client/proc/full_macro_assert(datum/preferences/prefs_override = prefs)
|
||||
INVOKE_ASYNC(src, .proc/do_full_macro_assert, prefs_override) // winget sleeps.
|
||||
|
||||
/client/proc/do_full_macro_assert(datum/preferences/prefs_override = prefs)
|
||||
erase_all_macros()
|
||||
set_macros(prefs_override)
|
||||
update_special_keybinds(prefs_override)
|
||||
set_hotkeys_preference(prefs_override)
|
||||
|
||||
/client/proc/do_special_keybind(key, command, datum/preferences/prefs_override = prefs)
|
||||
var/alt = findtext(key, "Alt")
|
||||
if(alt)
|
||||
key = copytext(key, 1, alt) + copytext(key, alt + 3, 0)
|
||||
var/ctrl = findtext(key, "Ctrl")
|
||||
if(ctrl)
|
||||
key = copytext(key, 1, ctrl) + copytext(key, ctrl + 4, 0)
|
||||
var/shift = findtext(key, "Shift")
|
||||
if(shift)
|
||||
key = copytext(key, 1, shift) + copytext(key, shift + 5, 0)
|
||||
if(!alt && !ctrl && !shift && !prefs_override.hotkeys)
|
||||
return /// DO NOT.
|
||||
key = "[alt? "Alt+":""][ctrl? "Ctrl+":""][shift? "Shift+":""][key]"
|
||||
for(var/macroset in SSinput.all_macrosets)
|
||||
winset(src, "[macroset]-[REF(key)]", "parent=[macroset];name=[key];command=[command]")
|
||||
|
||||
/**
|
||||
* Updates the keybinds for special keys
|
||||
*
|
||||
* Handles adding macros for the keys that need it
|
||||
* And adding movement keys to the clients movement_keys list
|
||||
* At the time of writing this, communication(OOC, Say, IC) require macros
|
||||
* Arguments:
|
||||
* * direct_prefs - the preference we're going to get keybinds from
|
||||
*/
|
||||
/client/proc/update_special_keybinds(datum/preferences/direct_prefs)
|
||||
var/datum/preferences/D = direct_prefs || prefs
|
||||
if(!D?.key_bindings)
|
||||
return
|
||||
movement_keys = list()
|
||||
for(var/key in D.key_bindings)
|
||||
for(var/kb_name in D.key_bindings[key])
|
||||
switch(kb_name)
|
||||
if("North")
|
||||
movement_keys[key] = NORTH
|
||||
if("East")
|
||||
movement_keys[key] = EAST
|
||||
if("West")
|
||||
movement_keys[key] = WEST
|
||||
if("South")
|
||||
movement_keys[key] = SOUTH
|
||||
else
|
||||
var/datum/keybinding/KB = GLOB.keybindings_by_name[kb_name]
|
||||
if(!KB.clientside)
|
||||
continue
|
||||
do_special_keybind(key, KB.clientside, D)
|
||||
|
||||
@@ -258,6 +258,16 @@
|
||||
AM.setDir(current_dir)
|
||||
now_pushing = FALSE
|
||||
|
||||
// i wish to have a "friendly chat" with whoever made three tail variables instead of one
|
||||
/mob/proc/has_tail()
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/has_tail()
|
||||
if(!dna || !dna.features)
|
||||
return ..()
|
||||
var/list/L = dna.features // caches list because i refuse to type it out and because performance
|
||||
return (L["mam_tail"] && (L["mam_tail"] != "None")) || (L["tail_human"] && (L["tail_human"] != "None")) || (L["tail_lizard"] && (L["tail_lizard"] != "None"))
|
||||
|
||||
/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
|
||||
if(!AM || !src)
|
||||
return FALSE
|
||||
@@ -296,9 +306,12 @@
|
||||
|
||||
log_combat(src, M, "grabbed", addition="passive grab")
|
||||
if(!supress_message && !(iscarbon(AM) && HAS_TRAIT(src, TRAIT_STRONG_GRABBER)))
|
||||
visible_message("<span class='warning'>[src] has grabbed [M][(zone_selected == "l_arm" || zone_selected == "r_arm")? " by [M.p_their()] hands":" passively"]!</span>",
|
||||
"<span class='warning'>You have grabbed [M][(zone_selected == "l_arm" || zone_selected == "r_arm")? " by [M.p_their()] hands":" passively"]!</span>", target = M,
|
||||
target_message = "<span class='warning'>[src] has grabbed you[(zone_selected == "l_arm" || zone_selected == "r_arm")? " by your hands":" passively"]!</span>")
|
||||
if((zone_selected == BODY_ZONE_PRECISE_GROIN) && has_tail() && M.has_tail())
|
||||
visible_message("<span class='warning'>[src] coils [p_their()] tail with [AM], pulling [M.p_them()] along!</span>", "[src] has entwined [p_their()] tail with yours, pulling you along!")
|
||||
else
|
||||
visible_message("<span class='warning'>[src] has grabbed [M][(zone_selected == "l_arm" || zone_selected == "r_arm")? " by [M.p_their()] hands":" passively"]!</span>",
|
||||
"<span class='warning'>You have grabbed [M][(zone_selected == "l_arm" || zone_selected == "r_arm")? " by [M.p_their()] hands":" passively"]!</span>", target = M,
|
||||
target_message = "<span class='warning'>[src] has grabbed you[(zone_selected == "l_arm" || zone_selected == "r_arm")? " by your hands":" passively"]!</span>")
|
||||
if(!iscarbon(src))
|
||||
M.LAssailant = null
|
||||
else
|
||||
|
||||
@@ -496,8 +496,19 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
|
||||
colored_message = "<font color=[color]>[message]</font>"
|
||||
else
|
||||
colored_message = "<font color='[color]'>[message]</font>"
|
||||
|
||||
var/list/timestamped_message = list("[LAZYLEN(logging[smessage_type]) + 1]\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(src)] [loc_name(src)]" = colored_message)
|
||||
|
||||
//This makes readability a bit better for admins.
|
||||
switch(message_type)
|
||||
if(LOG_WHISPER)
|
||||
colored_message = "(WHISPER) [colored_message]"
|
||||
if(LOG_OOC)
|
||||
colored_message = "(OOC) [colored_message]"
|
||||
if(LOG_ASAY)
|
||||
colored_message = "(ASAY) [colored_message]"
|
||||
if(LOG_EMOTE)
|
||||
colored_message = "(EMOTE) [colored_message]"
|
||||
|
||||
var/list/timestamped_message = list("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(src)] [loc_name(src)]" = colored_message)
|
||||
|
||||
logging[smessage_type] += timestamped_message
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
if(GLOB.say_disabled) //This is here to try to identify lag problems
|
||||
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
|
||||
return
|
||||
|
||||
|
||||
if(length(message) > MAX_MESSAGE_LEN)
|
||||
to_chat(usr, message)
|
||||
to_chat(usr, "<span class='danger'>^^^----- The preceeding message has been DISCARDED for being over the maximum length of [MAX_MESSAGE_LEN]. It has NOT been sent! -----^^^</span>")
|
||||
|
||||
@@ -1373,9 +1373,9 @@
|
||||
// next: take from or charge to the cell, depending on how much is left
|
||||
if(cell && !shorted)
|
||||
if(cur_excess > 0)
|
||||
var/charging_cell = min(cur_excess, cell.maxcharge * GLOB.CHARGELEVEL)
|
||||
var/charging_cell = min(min(cur_excess*GLOB.CELLRATE, cell.maxcharge * GLOB.CHARGELEVEL), cell.maxcharge - cell.charge)
|
||||
cell.give(charging_cell)
|
||||
add_load(charging_cell)
|
||||
add_load(charging_cell/GLOB.CELLRATE)
|
||||
lastused_total += charging_cell
|
||||
longtermpower = min(10,longtermpower + 1)
|
||||
if(chargemode && !charging)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// stored_power += (pulse_strength-RAD_COLLECTOR_EFFICIENCY)*RAD_COLLECTOR_COEFFICIENT
|
||||
#define RAD_COLLECTOR_EFFICIENCY 80 // radiation needs to be over this amount to get power
|
||||
#define RAD_COLLECTOR_COEFFICIENT 100
|
||||
#define RAD_COLLECTOR_STORED_OUT 0.04 // (this*100)% of stored power outputted per tick. Doesn't actualy change output total, lower numbers just means collectors output for longer in absence of a source
|
||||
#define RAD_COLLECTOR_COEFFICIENT 125
|
||||
#define RAD_COLLECTOR_STORED_OUT 0.05 // (this*100)% of stored power outputted per tick. Doesn't actualy change output total, lower numbers just means collectors output for longer in absence of a source
|
||||
#define RAD_COLLECTOR_MINING_CONVERSION_RATE 0.00001 //This is gonna need a lot of tweaking to get right. This is the number used to calculate the conversion of watts to research points per process()
|
||||
#define RAD_COLLECTOR_OUTPUT min(stored_power, (stored_power*RAD_COLLECTOR_STORED_OUT)+1000) //Produces at least 1000 watts if it has more than that stored
|
||||
|
||||
|
||||
@@ -35,8 +35,18 @@ All effects don't start immediately, but rather get worse over time; the rate is
|
||||
91-100: Dangerously toxic - swift death
|
||||
*/
|
||||
|
||||
|
||||
/datum/reagent/consumable/ethanol/on_mob_add(mob/living/L, amount)
|
||||
. = ..()
|
||||
if(!iscarbon(L))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/C = L
|
||||
if(HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM))
|
||||
C.reagents.remove_reagent(type, amount, FALSE)
|
||||
|
||||
/datum/reagent/consumable/ethanol/on_mob_life(mob/living/carbon/C)
|
||||
if(HAS_TRAIT(C, TRAIT_NO_ALCOHOL))
|
||||
if(HAS_TRAIT(C, TRAIT_TOXIC_ALCOHOL))
|
||||
C.adjustToxLoss((boozepwr/25)*REM,forced = TRUE)
|
||||
else if(C.drunkenness < volume * boozepwr * ALCOHOL_THRESHOLD_MODIFIER)
|
||||
var/booze_power = boozepwr
|
||||
|
||||
@@ -71,21 +71,21 @@
|
||||
// Generate page html
|
||||
var/html = SStgui.basehtml
|
||||
html = replacetextEx(html, "\[tgui:windowId]", id)
|
||||
// Process inline assets
|
||||
var/inline_styles = ""
|
||||
var/inline_scripts = ""
|
||||
// Inject inline assets
|
||||
var/inline_assets_str = ""
|
||||
for(var/datum/asset/asset in inline_assets)
|
||||
var/mappings = asset.get_url_mappings()
|
||||
for(var/name in mappings)
|
||||
var/url = mappings[name]
|
||||
// Not urlencoding since asset strings are considered safe
|
||||
// Not encoding since asset strings are considered safe
|
||||
if(copytext(name, -4) == ".css")
|
||||
inline_styles += "<link rel=\"stylesheet\" type=\"text/css\" href=\"[url]\">\n"
|
||||
inline_assets_str += "Byond.loadCss('[url]', true);\n"
|
||||
else if(copytext(name, -3) == ".js")
|
||||
inline_scripts += "<script type=\"text/javascript\" defer src=\"[url]\"></script>\n"
|
||||
inline_assets_str += "Byond.loadJs('[url]', true);\n"
|
||||
asset.send(client)
|
||||
html = replacetextEx(html, "<!-- tgui:styles -->\n", inline_styles)
|
||||
html = replacetextEx(html, "<!-- tgui:scripts -->\n", inline_scripts)
|
||||
if(length(inline_assets_str))
|
||||
inline_assets_str = "<script>\n" + inline_assets_str + "</script>\n"
|
||||
html = replacetextEx(html, "<!-- tgui:assets -->\n", inline_assets_str)
|
||||
// Inject custom HTML
|
||||
html = replacetextEx(html, "<!-- tgui:html -->\n", inline_html)
|
||||
// Open the window
|
||||
|
||||
5
html/changelogs/AutoChangeLog-pr-13576.yml
Normal file
5
html/changelogs/AutoChangeLog-pr-13576.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
author: "Sonic121x"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "digi sprite uniform"
|
||||
- bugfix: "digi leg suit"
|
||||
6
html/changelogs/AutoChangeLog-pr-13685.yml
Normal file
6
html/changelogs/AutoChangeLog-pr-13685.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
author: "uomo91"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Fixed \"Show All\" tab in player panel logs being broken."
|
||||
- bugfix: "Whispers, OOC, and various other things display differently in logs, visually distinguishing them from say logs."
|
||||
- refactor: "Player panel logs will now show all logs chronologically, so you'll see commingled say and attack logs if you're on the \"Show All\" tab, etc..."
|
||||
4
html/changelogs/AutoChangeLog-pr-13686.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13686.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Arturlang"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Hopefully fixes whitescreen issues for TGUI UI's by giving assets more time to get to the client"
|
||||
4
html/changelogs/AutoChangeLog-pr-13687.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13687.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "silicons"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "tailed individuals can now target groin to intertwine tails on grab intent."
|
||||
5
html/changelogs/AutoChangeLog-pr-13689.yml
Normal file
5
html/changelogs/AutoChangeLog-pr-13689.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
author: "DeltaFire15"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Recharging APCs no longer use 0.2% of the power they should be using."
|
||||
- bugfix: "APCs no longer always use as much power as they can for their cell, even if it is full."
|
||||
4
html/changelogs/AutoChangeLog-pr-13695.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13695.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "shellspeed1"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "Lings now have infinite space for DNA."
|
||||
4
html/changelogs/AutoChangeLog-pr-13697.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13697.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Putnam3145"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "Spontaneous brain trauma now requires minimum 5 players"
|
||||
4
html/changelogs/AutoChangeLog-pr-13698.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13698.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "timothyteakettle"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "you can rebind communication hotkeys and they're the default now"
|
||||
4
html/changelogs/AutoChangeLog-pr-13702.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13702.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Putnam3145"
|
||||
delete-after: True
|
||||
changes:
|
||||
- tweak: "gear harness no longer magically covers up the body mechanically despite covering up nothing visually"
|
||||
4
html/changelogs/AutoChangeLog-pr-13703.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13703.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "Putnam3145"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Dynamic vote no longer shows the none-storyteller."
|
||||
6
html/changelogs/AutoChangeLog-pr-13705.yml
Normal file
6
html/changelogs/AutoChangeLog-pr-13705.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
author: "Putnam3145"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "Rad collectors now get 1.25x as much energy from radiation"
|
||||
- balance: "Rad collectors now put out 1.25x as much stored energy per tick"
|
||||
- balance: "Above two rad collector changes give a total 56.25% power output increase"
|
||||
5
html/changelogs/AutoChangeLog-pr-13707.yml
Normal file
5
html/changelogs/AutoChangeLog-pr-13707.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
author: "DeltaFire15"
|
||||
delete-after: True
|
||||
changes:
|
||||
- balance: "Robotpeople are now fully immune to the effects of alcohol (drunkness etc.)"
|
||||
- tweak: "Renames the alcohol intolerance trait in the code to make what it does more clear."
|
||||
4
html/changelogs/AutoChangeLog-pr-13715.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13715.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "MrJWhit"
|
||||
delete-after: True
|
||||
changes:
|
||||
- tweak: "Removes an opposum from the wall"
|
||||
4
html/changelogs/AutoChangeLog-pr-13721.yml
Normal file
4
html/changelogs/AutoChangeLog-pr-13721.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
author: "MrJWhit"
|
||||
delete-after: True
|
||||
changes:
|
||||
- tweak: "Increased christmas event from 22th to 27th to 10th to 27th"
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 284 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 310 KiB |
@@ -11,7 +11,7 @@
|
||||
mob_overlay_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
|
||||
icon_state = "hostrench"
|
||||
item_state = "hostrench"
|
||||
mutantrace_variation = NONE
|
||||
mutantrace_variation = STYLE_DIGITIGRADE
|
||||
body_parts_covered = CHEST|ARMS|LEGS
|
||||
|
||||
/obj/item/clothing/suit/hooded/cloak/david
|
||||
@@ -20,7 +20,7 @@
|
||||
desc = "Ever wanted to look like a badass without ANY effort? Try this nanotrasen brand red cloak, perfect for kids"
|
||||
hoodtype = /obj/item/clothing/head/hooded/cloakhood/david
|
||||
body_parts_covered = CHEST|GROIN|ARMS
|
||||
mutantrace_variation = NONE
|
||||
mutantrace_variation = STYLE_DIGITIGRADE
|
||||
|
||||
/obj/item/clothing/head/hooded/cloakhood/david
|
||||
name = "red cloak hood"
|
||||
|
||||
@@ -2298,6 +2298,7 @@
|
||||
#include "code\modules\keybindings\keybind\carbon.dm"
|
||||
#include "code\modules\keybindings\keybind\client.dm"
|
||||
#include "code\modules\keybindings\keybind\combat.dm"
|
||||
#include "code\modules\keybindings\keybind\communication.dm"
|
||||
#include "code\modules\keybindings\keybind\emote.dm"
|
||||
#include "code\modules\keybindings\keybind\human.dm"
|
||||
#include "code\modules\keybindings\keybind\living.dm"
|
||||
|
||||
20
tgui/.yarn/sdks/eslint/bin/eslint.js
vendored
Normal file
20
tgui/.yarn/sdks/eslint/bin/eslint.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve, dirname} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.js";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real eslint/bin/eslint.js your application uses
|
||||
module.exports = absRequire(`eslint/bin/eslint.js`);
|
||||
6
tgui/.yarn/sdks/eslint/package.json
vendored
Normal file
6
tgui/.yarn/sdks/eslint/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "eslint",
|
||||
"version": "7.4.0-pnpify",
|
||||
"main": "./lib/api.js",
|
||||
"type": "commonjs"
|
||||
}
|
||||
5
tgui/.yarn/sdks/integrations.yml
vendored
Normal file
5
tgui/.yarn/sdks/integrations.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# This file is automatically generated by PnPify.
|
||||
# Manual changes will be lost!
|
||||
|
||||
integrations:
|
||||
- vscode
|
||||
12
tgui/global.d.ts
vendored
12
tgui/global.d.ts
vendored
@@ -97,11 +97,21 @@ interface ByondType {
|
||||
winset(id: string, propName: string, propValue: any): void;
|
||||
|
||||
/**
|
||||
* Parses BYOND JSON
|
||||
* Parses BYOND JSON.
|
||||
*
|
||||
* Uses a special encoding to preverse Infinity and NaN.
|
||||
*/
|
||||
parseJson(text: string): any;
|
||||
|
||||
/**
|
||||
* Loads a stylesheet into the document.
|
||||
*/
|
||||
loadCss(url: string): void;
|
||||
|
||||
/**
|
||||
* Loads a script into the document.
|
||||
*/
|
||||
loadJs(url: string): void;
|
||||
}
|
||||
|
||||
declare const Byond: ByondType;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
@include meta.load-css('~tgui/styles/components/Dimmer.scss');
|
||||
@include meta.load-css('~tgui/styles/components/Divider.scss');
|
||||
@include meta.load-css('~tgui/styles/components/Dropdown.scss');
|
||||
@include meta.load-css('~tgui/styles/components/FatalError.scss');
|
||||
@include meta.load-css('~tgui/styles/components/Flex.scss');
|
||||
@include meta.load-css('~tgui/styles/components/Input.scss');
|
||||
@include meta.load-css('~tgui/styles/components/Knob.scss');
|
||||
|
||||
@@ -4,68 +4,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { loadCSS as fgLoadCss } from 'fg-loadcss';
|
||||
import { createLogger } from './logging';
|
||||
|
||||
const logger = createLogger('assets');
|
||||
|
||||
const EXCLUDED_PATTERNS = [/v4shim/i];
|
||||
const RETRY_ATTEMPTS = 5;
|
||||
const RETRY_INTERVAL = 3000;
|
||||
|
||||
const loadedStyleSheetByUrl = {};
|
||||
const loadedMappings = {};
|
||||
|
||||
export const loadStyleSheet = (url, attempt = 1) => {
|
||||
if (loadedStyleSheetByUrl[url]) {
|
||||
return;
|
||||
}
|
||||
loadedStyleSheetByUrl[url] = true;
|
||||
logger.log(`loading stylesheet '${url}'`);
|
||||
/** @type {HTMLLinkElement} */
|
||||
let node = fgLoadCss(url);
|
||||
node.addEventListener('load', () => {
|
||||
if (!isStyleSheetReallyLoaded(node, url)) {
|
||||
node.parentNode.removeChild(node);
|
||||
node = null;
|
||||
loadedStyleSheetByUrl[url] = null;
|
||||
if (attempt >= RETRY_ATTEMPTS) {
|
||||
logger.error(`Error: Failed to load the stylesheet `
|
||||
+ `'${url}' after ${RETRY_ATTEMPTS} attempts.\nIt was either `
|
||||
+ `not found, or you're trying to load an empty stylesheet `
|
||||
+ `that has no CSS rules in it.`);
|
||||
return;
|
||||
}
|
||||
setTimeout(() => loadStyleSheet(url, attempt + 1), RETRY_INTERVAL);
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the stylesheet was registered in the DOM
|
||||
* and is not empty.
|
||||
*/
|
||||
const isStyleSheetReallyLoaded = (node, url) => {
|
||||
// Method #1 (works on IE10+)
|
||||
const styleSheet = node.sheet;
|
||||
if (styleSheet) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
// Method #2
|
||||
const styleSheets = document.styleSheets;
|
||||
const len = styleSheets.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const styleSheet = styleSheets[i];
|
||||
if (styleSheet.href.includes(url)) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
}
|
||||
// All methods failed
|
||||
logger.warn(`Warning: stylesheet '${url}' was not found in the DOM`);
|
||||
return false;
|
||||
};
|
||||
|
||||
export const resolveAsset = name => (
|
||||
loadedMappings[name] || name
|
||||
);
|
||||
@@ -73,7 +14,7 @@ export const resolveAsset = name => (
|
||||
export const assetMiddleware = store => next => action => {
|
||||
const { type, payload } = action;
|
||||
if (type === 'asset/stylesheet') {
|
||||
loadStyleSheet(payload);
|
||||
Byond.loadCss(payload);
|
||||
return;
|
||||
}
|
||||
if (type === 'asset/mappings') {
|
||||
@@ -86,7 +27,10 @@ export const assetMiddleware = store => next => action => {
|
||||
const ext = name.split('.').pop();
|
||||
loadedMappings[name] = url;
|
||||
if (ext === 'css') {
|
||||
loadStyleSheet(url);
|
||||
Byond.loadCss(url);
|
||||
}
|
||||
if (ext === 'js') {
|
||||
Byond.loadJs(url);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -189,6 +189,9 @@ export const backendMiddleware = store => {
|
||||
|
||||
// Resume on incoming update
|
||||
if (type === 'backend/update' && suspended) {
|
||||
// Show the payload
|
||||
logger.log('backend/update', payload);
|
||||
// Signal renderer that we have resumed
|
||||
resumeRenderer();
|
||||
// Setup drag
|
||||
setupDrag();
|
||||
|
||||
@@ -24,7 +24,7 @@ export const debugMiddleware = store => {
|
||||
if (key.code === KEY_F12) {
|
||||
store.dispatch(toggleKitchenSink());
|
||||
}
|
||||
if (key.ctrl && key.shift && key.code === KEY_BACKSPACE) {
|
||||
if (key.ctrl && key.alt && key.code === KEY_BACKSPACE) {
|
||||
// NOTE: We need to call this in a timeout, because we need a clean
|
||||
// stack in order for this to be a fatal error.
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
"version": "4.2.0",
|
||||
"dependencies": {
|
||||
"common": "workspace:*",
|
||||
"dompurify": "^2.0.11",
|
||||
"fg-loadcss": "^2.1.0",
|
||||
"dompurify": "^2.0.12",
|
||||
"inferno": "^7.4.2",
|
||||
"inferno-vnode-flags": "^7.4.2",
|
||||
"marked": "^1.1.0",
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Aleksej Komarov
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
.FatalError {
|
||||
display: block !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 12px;
|
||||
font-size: 12px;
|
||||
font-family: Consolas, monospace;
|
||||
color: #ffffff;
|
||||
background-color: #0000dd;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.FatalError__logo {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
font-size: 10px;
|
||||
line-height: 8px;
|
||||
position: relative;
|
||||
margin-top: 12px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
animation:
|
||||
FatalError__rainbow 2s linear infinite alternate,
|
||||
FatalError__shadow 4s linear infinite alternate,
|
||||
FatalError__tfmX 3s infinite alternate,
|
||||
FatalError__tfmY 4s infinite alternate;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.FatalError__header {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.FatalError__stack {
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.FatalError__footer {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
@keyframes FatalError__rainbow {
|
||||
0% {
|
||||
color: #ff0;
|
||||
}
|
||||
50% {
|
||||
color: #0ff;
|
||||
}
|
||||
100% {
|
||||
color: #f0f;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FatalError__shadow {
|
||||
0% {
|
||||
left: -2px;
|
||||
text-shadow: 4px 0 #f0f;
|
||||
}
|
||||
50% {
|
||||
left: 0px;
|
||||
text-shadow: 0px 0 #0ff;
|
||||
}
|
||||
100% {
|
||||
left: 2px;
|
||||
text-shadow: -4px 0 #ff0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FatalError__tfmX {
|
||||
0% {
|
||||
left: 15px;
|
||||
}
|
||||
100% {
|
||||
left: -15px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FatalError__tfmY {
|
||||
100% {
|
||||
top: -15px;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@
|
||||
@include meta.load-css('./components/Dimmer.scss');
|
||||
@include meta.load-css('./components/Divider.scss');
|
||||
@include meta.load-css('./components/Dropdown.scss');
|
||||
@include meta.load-css('./components/FatalError.scss');
|
||||
@include meta.load-css('./components/Flex.scss');
|
||||
@include meta.load-css('./components/Input.scss');
|
||||
@include meta.load-css('./components/Knob.scss');
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -17,9 +17,7 @@ if (window.__windowId__ === '[' + 'tgui:windowId' + ']') {
|
||||
window.__windowId__ = null;
|
||||
}
|
||||
|
||||
// BYOND API object
|
||||
window.Byond = (function () {
|
||||
var Byond = {};
|
||||
(function () {
|
||||
// Utility functions
|
||||
var hasOwn = Object.prototype.hasOwnProperty;
|
||||
var assign = function (target) {
|
||||
@@ -33,6 +31,11 @@ window.Byond = (function () {
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
// BYOND API object
|
||||
// ------------------------------------------------------
|
||||
|
||||
var Byond = window.Byond = {};
|
||||
// Trident engine version
|
||||
var tridentVersion = (function () {
|
||||
var groups = navigator.userAgent.match(/Trident\/(\d+).+?;/i);
|
||||
@@ -170,7 +173,135 @@ window.Byond = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
return Byond;
|
||||
|
||||
// Asset loaders
|
||||
// ------------------------------------------------------
|
||||
|
||||
var RETRY_ATTEMPTS = 5;
|
||||
var RETRY_INTERVAL = 2000;
|
||||
|
||||
var loadedCssByUrl = {};
|
||||
var loadedJsByUrl = {};
|
||||
|
||||
var isStyleSheetLoaded = function (node, url) {
|
||||
// Method #1 (works on IE10+)
|
||||
var styleSheet = node.sheet;
|
||||
if (styleSheet) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
// Method #2
|
||||
var styleSheets = document.styleSheets;
|
||||
var len = styleSheets.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var styleSheet = styleSheets[i];
|
||||
if (styleSheet.href.indexOf(url) !== -1) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
}
|
||||
// All methods failed
|
||||
return false;
|
||||
};
|
||||
|
||||
var onDocumentBodyReady = function (done) {
|
||||
if (document.body) {
|
||||
return done();
|
||||
}
|
||||
setTimeout(function () {
|
||||
onDocumentBodyReady(done);
|
||||
});
|
||||
};
|
||||
|
||||
var getRefNode = function () {
|
||||
var refs = (document.body || document.getElementsByTagName('head')[0])
|
||||
.childNodes;
|
||||
return refs[refs.length - 1];
|
||||
};
|
||||
|
||||
// Based on fg-loadcss package
|
||||
// See: https://github.com/filamentgroup/loadCSS
|
||||
Byond.loadCss = function (url, sync, attempt) {
|
||||
if (loadedCssByUrl[url]) {
|
||||
return;
|
||||
}
|
||||
if (!attempt) {
|
||||
attempt = 1;
|
||||
}
|
||||
loadedCssByUrl[url] = true;
|
||||
// Inject the stylesheet
|
||||
var ref = getRefNode();
|
||||
/** @type {HTMLLinkElement} */
|
||||
var node = document.createElement('link');
|
||||
node.type = 'text/css';
|
||||
node.rel = 'stylesheet';
|
||||
node.href = url;
|
||||
// Temporarily set media to something inapplicable
|
||||
// to ensure it'll fetch without blocking render
|
||||
if (!sync) {
|
||||
node.media = 'only x';
|
||||
}
|
||||
onDocumentBodyReady(function () {
|
||||
ref.parentNode.insertBefore(node, ref.nextSibling);
|
||||
});
|
||||
// Listen for the load event
|
||||
node.onload = function () {
|
||||
node.onload = null;
|
||||
if (isStyleSheetLoaded(node, url)) {
|
||||
// Render the stylesheet
|
||||
node.media = 'all';
|
||||
return;
|
||||
}
|
||||
// Try again
|
||||
node.parentNode.removeChild(node);
|
||||
node = null;
|
||||
loadedCssByUrl[url] = null;
|
||||
if (attempt >= RETRY_ATTEMPTS) {
|
||||
throw new Error("Error: Failed to load the stylesheet "
|
||||
+ "'" + url + "' after " + RETRY_ATTEMPTS + " attempts.\n"
|
||||
+ "It was either not found, or you're trying to load "
|
||||
+ "an empty stylesheet that has no CSS rules in it.");
|
||||
}
|
||||
setTimeout(function () {
|
||||
Byond.loadCss(url, sync, attempt + 1);
|
||||
}, RETRY_INTERVAL);
|
||||
};
|
||||
};
|
||||
|
||||
Byond.loadJs = function (url, sync, attempt) {
|
||||
if (loadedJsByUrl[url]) {
|
||||
return;
|
||||
}
|
||||
if (!attempt) {
|
||||
attempt = 1;
|
||||
}
|
||||
loadedJsByUrl[url] = true;
|
||||
// Inject the stylesheet
|
||||
var ref = getRefNode();
|
||||
var node = document.createElement('script');
|
||||
node.type = 'text/javascript';
|
||||
if (sync) {
|
||||
node.defer = true;
|
||||
}
|
||||
else {
|
||||
node.async = true;
|
||||
}
|
||||
node.src = url;
|
||||
onDocumentBodyReady(function () {
|
||||
ref.parentNode.insertBefore(node, ref.nextSibling);
|
||||
});
|
||||
node.onerror = function () {
|
||||
node.onerror = null;
|
||||
node.parentNode.removeChild(node);
|
||||
node = null;
|
||||
loadedJsByUrl[url] = null;
|
||||
if (attempt >= RETRY_ATTEMPTS) {
|
||||
throw new Error("Error: Failed to load the script "
|
||||
+ "'" + url + "' after " + RETRY_ATTEMPTS + " attempts.");
|
||||
}
|
||||
setTimeout(function () {
|
||||
Byond.loadJs(url, sync, attempt + 1);
|
||||
}, RETRY_INTERVAL);
|
||||
};
|
||||
};
|
||||
})();
|
||||
|
||||
// Global error handling
|
||||
@@ -190,7 +321,7 @@ window.onerror = function (msg, url, line, col, error) {
|
||||
var errorRoot = document.getElementById('FatalError');
|
||||
var errorStack = document.getElementById('FatalError__stack');
|
||||
if (errorRoot) {
|
||||
errorRoot.className = 'FatalError';
|
||||
errorRoot.className = 'FatalError FatalError--visible';
|
||||
if (errorStack.textContent) {
|
||||
errorStack.textContent += '\n\n' + stack;
|
||||
}
|
||||
@@ -263,15 +394,97 @@ if (!Function.prototype.bind) (function () {
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Styles -->
|
||||
<!-- tgui:styles -->
|
||||
<style>
|
||||
.FatalError {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 12px;
|
||||
font-size: 12px;
|
||||
font-family: Consolas, monospace;
|
||||
color: #ffffff;
|
||||
background-color: #0000dd;
|
||||
z-index: 1000;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
<!-- Scripts -->
|
||||
<!-- tgui:scripts -->
|
||||
.FatalError--visible {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.FatalError__logo {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
position: relative;
|
||||
margin: 16px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
animation:
|
||||
FatalError__rainbow 2s linear infinite alternate,
|
||||
FatalError__shadow 4s linear infinite alternate,
|
||||
FatalError__tfmX 3s infinite alternate,
|
||||
FatalError__tfmY 4s infinite alternate;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.FatalError__header {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.FatalError__stack {
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.FatalError__footer {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
@keyframes FatalError__rainbow {
|
||||
0% { color: #ff0; }
|
||||
50% { color: #0ff; }
|
||||
100% { color: #f0f; }
|
||||
}
|
||||
|
||||
@keyframes FatalError__shadow {
|
||||
0% {
|
||||
left: -2px;
|
||||
text-shadow: 4px 0 #f0f;
|
||||
}
|
||||
50% {
|
||||
left: 0px;
|
||||
text-shadow: 0px 0 #0ff;
|
||||
}
|
||||
100% {
|
||||
left: 2px;
|
||||
text-shadow: -4px 0 #ff0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes FatalError__tfmX {
|
||||
0% { left: 15px; }
|
||||
100% { left: -15px; }
|
||||
}
|
||||
|
||||
@keyframes FatalError__tfmY {
|
||||
100% { top: -15px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Inline assets -->
|
||||
<!-- tgui:assets -->
|
||||
|
||||
<!-- Inline HTML -->
|
||||
<!-- tgui:html -->
|
||||
|
||||
@@ -279,36 +492,22 @@ if (!Function.prototype.bind) (function () {
|
||||
<div id="react-root"></div>
|
||||
|
||||
<!-- Fatal error container -->
|
||||
<div id="FatalError" style="display: none">
|
||||
<div id="FatalError" class="FatalError">
|
||||
<div class="FatalError__logo">
|
||||
_____ _____ _______ _____
|
||||
/\ \ /\ \ /::\ \ /\ \
|
||||
/::\____\ /::\ \ /::::\ \ /::\ \
|
||||
/::::| | \:::\ \ /::::::\ \ /::::\ \
|
||||
/:::::| | \:::\ \ /::::::::\ \ /::::::\ \
|
||||
/::::::| | \:::\ \ /:::/~~\:::\ \ /:::/\:::\ \
|
||||
/:::/|::| | \:::\ \ /:::/ \:::\ \ /:::/__\:::\ \
|
||||
/:::/ |::| | /::::\ \ /:::/ / \:::\ \ \:::\ \:::\ \
|
||||
/:::/ |::| | _____ /::::::\ \ /:::/____/ \:::\____\ ___\:::\ \:::\ \
|
||||
/:::/ |::| |/\ \ /:::/\:::\ \|:::| | |:::| |/\ \:::\ \:::\ \
|
||||
/:: / |::| /::\____\/:::/ \:::\____\:::|____| |:::| /::\ \:::\ \:::\____\
|
||||
\::/ /|::| /:::/ /:::/ \::/ /\:::\ \ /:::/ /\:::\ \:::\ \::/ /
|
||||
\/____/ |::| /:::/ /:::/ / \/____/ \:::\ \ /:::/ / \:::\ \:::\ \/____/
|
||||
|::|/:::/ /:::/ / \:::\ /:::/ / \:::\ \:::\ \
|
||||
|::::::/ /:::/ / \:::\__/:::/ / \:::\ \:::\____\
|
||||
|:::::/ /\::/ / \::::::::/ / \:::\ /:::/ /
|
||||
|::::/ / \/____/ \::::::/ / \:::\/:::/ /
|
||||
/:::/ / \::::/ / \::::::/ /
|
||||
/:::/ / \::/____/ \::::/ /
|
||||
\::/ / \::/ /
|
||||
\/____/ \/____/
|
||||
ooooo ooo . .oooooo. .oooooo..o
|
||||
`888b. `8' .o8 d8P' `Y8b d8P' `Y8
|
||||
8 `88b. 8 .o888oo 888 888 Y88bo.
|
||||
8 `88b. 8 888 888 888 `"Y8888o.
|
||||
8 `88b.8 888 888 888 `"Y88b
|
||||
8 `888 888 . `88b d88' oo .d8P
|
||||
o8o `8 "888" `Y8bood8P' 8""88888P'
|
||||
</div>
|
||||
<marquee class="FatalError__header">
|
||||
A fatal exception has occurred at 002B:C562F1B7 in TGUI.
|
||||
The current application will be terminated.
|
||||
Please remain calm. Get to the nearest NTNet workstation
|
||||
and send the copy of the following stack trace to:
|
||||
www.github.com/tgstation/tgstation. Thank you for your cooperation.
|
||||
www.github.com/Citadel-Station-13/Citadel-Station-13/issues. Thank you for your cooperation.
|
||||
</marquee>
|
||||
<div id="FatalError__stack" class="FatalError__stack"></div>
|
||||
<div class="FatalError__footer">
|
||||
|
||||
@@ -3062,10 +3062,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dompurify@npm:^2.0.11":
|
||||
version: 2.0.12
|
||||
resolution: "dompurify@npm:2.0.12"
|
||||
checksum: 72fe758306be02d95c7ac51cecaf9d887dea6291cd83012b1c251d21912545c18d6ccc7140b84da2278a6910517cd1e996258991c7e3dda9b6da34f0b6040b91
|
||||
"dompurify@npm:^2.0.12":
|
||||
version: 2.2.2
|
||||
resolution: "dompurify@npm:2.2.2"
|
||||
checksum: eab7b8763c56256bb4fb009e59b814f750fe0a4be40f996f037f136a40dbe8107820989c7b548b92263076fce7023ee31c58a11ad479cba16b4626b8d91559f1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -3637,13 +3637,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fg-loadcss@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "fg-loadcss@npm:2.1.0"
|
||||
checksum: 5672a437bd51ba418ffa3c304394a4f85d6b53ae2bbcec2ef54d6498642af4e2c11a9902a55bf95cd96e7e787483c2087f56569484a3120a4228d880ffec6309
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"figgy-pudding@npm:^3.5.1":
|
||||
version: 3.5.2
|
||||
resolution: "figgy-pudding@npm:3.5.2"
|
||||
@@ -8155,8 +8148,7 @@ fsevents@~2.1.2:
|
||||
resolution: "tgui@workspace:packages/tgui"
|
||||
dependencies:
|
||||
common: "workspace:*"
|
||||
dompurify: ^2.0.11
|
||||
fg-loadcss: ^2.1.0
|
||||
dompurify: ^2.0.12
|
||||
inferno: ^7.4.2
|
||||
inferno-vnode-flags: ^7.4.2
|
||||
marked: ^1.1.0
|
||||
|
||||
Reference in New Issue
Block a user