mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-24 21:40:55 +01:00
Bun, Inferno->React migration (#22529)
Re-creation of https://github.com/Aurorastation/Aurora.3/pull/21046 to skip merge conflict hell. Brings us modern TGUI. **ALTERNATE TITLE: TGUI HELLSCAPE PR ABANDON ALL HOPE YE WHO ENTER HERE** - [x] Migrate build tools (javascript -> typescript, bun for package management). - [x] Upgrade all TGUI dependencies and associated root files to TG-congruent versions (axios, babel, dompurify, eslint, highlight, marked, prettier, sass, source-map, stacktrace-parser, typescript). - [x] InfernoJS -> React migrations - [x] React cleanup and polish (migrate all remaining .js files to appropriate .ts or .tsx filetype, all remaining hooks, linting, error corrections, etc.) - [ ] Test all remaining TGUI interfaces
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
*
|
||||
* required user mob The mob interacting with the UI.
|
||||
*
|
||||
* return list Statuic Data to be sent to the UI.
|
||||
* return list Static Data to be sent to the UI.
|
||||
*/
|
||||
/datum/proc/ui_static_data(mob/user)
|
||||
return list()
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
GLOBAL_DATUM_INIT(follow_menu, /datum/tgui_module/follow_menu, new)
|
||||
|
||||
/datum/tgui_module/follow_menu/ui_interact(var/mob/user, var/datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "FollowMenu", "Follow Menu", 800, 400)
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_module/follow_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
var/mob/abstract/ghost/ghost = usr
|
||||
if(!istype(ghost))
|
||||
return
|
||||
|
||||
if(action == "follow_target")
|
||||
if(!ghost.ManualFollow(locate(params["follow_target"]) in GLOB.mob_list))
|
||||
to_chat(ghost, SPAN_WARNING("Failed to follow selected target. Refreshing mob list..."))
|
||||
return TRUE
|
||||
|
||||
if(action == "refresh")
|
||||
ui.send_full_update()
|
||||
return TRUE
|
||||
|
||||
/datum/tgui_module/follow_menu/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
// Don't worry about this is_mod check being for storytellers as well - all it does is highlight antags in red.
|
||||
var/is_mod = check_rights(R_MOD|R_ADMIN, 0, user) || isstoryteller(user)
|
||||
data["is_mod"] = is_mod
|
||||
|
||||
var/list/ghosts = list()
|
||||
var/list/categories = list()
|
||||
|
||||
var/list/names = list()
|
||||
var/list/namecounts = list()
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
var/category
|
||||
var/name = M.name
|
||||
if(name in names)
|
||||
namecounts[name]++
|
||||
name = "[name] ([namecounts[name]])"
|
||||
else
|
||||
names.Add(name)
|
||||
namecounts[name] = 1
|
||||
if(M.real_name && M.real_name != M.name)
|
||||
name += " \[[M.real_name]\]"
|
||||
if(!M.mind)
|
||||
if(M.stat == DEAD)
|
||||
category = "Dead NPCs"
|
||||
else
|
||||
category = "NPCs"
|
||||
else if(M.stat == DEAD)
|
||||
if(isobserver(M))
|
||||
name += " \[Ghost\]"
|
||||
category = "Observer"
|
||||
else if(isstoryteller(M))
|
||||
name += "\[Storyteller\]"
|
||||
category = "Storytellers"
|
||||
else
|
||||
name += " \[Dead\]"
|
||||
category = "Dead"
|
||||
else
|
||||
if(player_is_obvious_antag(M.mind))
|
||||
category = M.mind.special_role
|
||||
else if(isobserver(user))
|
||||
var/mob/abstract/ghost/observer/O = user
|
||||
if(O.antagHUD && player_is_antag(M.mind))
|
||||
category = M.mind.special_role
|
||||
if(!category)
|
||||
category = "Alive"
|
||||
var/special_char = is_special_character(M)
|
||||
if(!name)
|
||||
continue
|
||||
|
||||
ghosts += list(list("name" = name, "ref" = REF(M), "category" = category, "special_character" = special_char))
|
||||
categories |= category
|
||||
|
||||
data["categories"] = categories
|
||||
data["ghosts"] = ghosts
|
||||
return data
|
||||
|
||||
/datum/tgui_module/follow_menu/proc/sort_categories(var/list/menu_info)
|
||||
var/static/list/ordered_categories = list("Alive", "Dead", "Observer", "NPCs", "Dead NPCs")
|
||||
var/list/sorted_menu_info = list()
|
||||
|
||||
// antags first
|
||||
for(var/category in menu_info)
|
||||
if(!(category in ordered_categories))
|
||||
sorted_menu_info[category] = menu_info[category]
|
||||
|
||||
// set categories in the order people probably want to look at them
|
||||
for(var/ordered_category in ordered_categories)
|
||||
if(menu_info[ordered_category])
|
||||
sorted_menu_info[ordered_category] = menu_info[ordered_category]
|
||||
|
||||
return sorted_menu_info
|
||||
@@ -0,0 +1,411 @@
|
||||
/datum/tgui_module/appearance_changer
|
||||
var/name = "Appearance Changer"
|
||||
var/flags = APPEARANCE_ALL_HAIR
|
||||
|
||||
var/datum/ui_state/ui_state
|
||||
var/datum/state_object
|
||||
|
||||
var/datum/weakref/target_human
|
||||
var/datum/weakref/target_id //Mostly for ghostroles and plastic surgery machines. If there is an ID, update it when we close the UI to ensure the correct info is imprinted.
|
||||
var/change_id = FALSE //Prevents runtimes if target_id is null
|
||||
var/list/valid_species = list()
|
||||
var/list/valid_genders = list()
|
||||
var/list/valid_pronouns = list()
|
||||
var/list/valid_hairstyles = list()
|
||||
var/list/valid_gradient_styles = list()
|
||||
var/list/valid_facial_hairstyles = list()
|
||||
var/list/valid_cultures = list()
|
||||
var/list/valid_origins = list()
|
||||
var/list/valid_citizenships = list()
|
||||
var/list/valid_accents = list()
|
||||
var/list/valid_languages = list()
|
||||
var/list/valid_prosthetics = list()
|
||||
var/list/valid_limbs = list()
|
||||
var/list/valid_organs = list()
|
||||
|
||||
var/check_whitelist
|
||||
var/list/whitelist
|
||||
var/list/blacklist
|
||||
|
||||
var/list/culture_map = list()
|
||||
var/list/origin_map = list()
|
||||
var/list/culture_restrictions = list()
|
||||
var/list/origin_restrictions = list()
|
||||
|
||||
/datum/tgui_module/appearance_changer/New(var/mob/living/carbon/human/H, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list(), var/list/culture_restriction = list(), var/list/origin_restriction = list(), var/datum/ui_state/set_ui_state = GLOB.always_state, var/datum/set_state_object = null, var/update_id)
|
||||
..()
|
||||
ui_state = set_ui_state
|
||||
state_object = set_state_object
|
||||
target_human = WEAKREF(H)
|
||||
if(istype(H) && update_id)
|
||||
var/obj/item/card/id/idcard = H.GetIdCard()
|
||||
if(idcard)
|
||||
target_id = WEAKREF(idcard)
|
||||
change_id = TRUE
|
||||
src.check_whitelist = check_species_whitelist
|
||||
src.whitelist = species_whitelist
|
||||
src.blacklist = species_blacklist
|
||||
culture_restrictions = culture_restriction
|
||||
origin_restrictions = culture_restriction
|
||||
generate_data(check_whitelist, whitelist, blacklist)
|
||||
|
||||
/datum/tgui_module/appearance_changer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
return FALSE
|
||||
|
||||
switch(action)
|
||||
if("race")
|
||||
if(can_change(APPEARANCE_RACE) && (params["race"] in valid_species))
|
||||
if(owner.change_species(params["race"]))
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("gender")
|
||||
if(can_change(APPEARANCE_GENDER))
|
||||
if(owner.change_gender(params["gender"], TRUE))
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("pronoun")
|
||||
if(can_change(APPEARANCE_GENDER))
|
||||
owner.pronouns = params["pronouns"]
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("skin_tone")
|
||||
if(can_change_skin_tone())
|
||||
var/new_s_tone = input(usr, "Choose your character's skin-tone:\n(Light [owner.species.lower_skin_tone_bound] - [owner.species.upper_skin_tone_bound] Dark)", "Skin Tone", -owner.s_tone + 35) as num|null
|
||||
if(isnum(new_s_tone))
|
||||
new_s_tone = 35 - clamp(round(new_s_tone), owner.species.lower_skin_tone_bound, owner.species.upper_skin_tone_bound)
|
||||
. = owner.change_skin_tone(new_s_tone)
|
||||
if("skin_color")
|
||||
if(can_change_skin_color())
|
||||
var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as color|null
|
||||
if(new_skin)
|
||||
var/r_skin = hex2num(copytext(new_skin, 2, 4))
|
||||
var/g_skin = hex2num(copytext(new_skin, 4, 6))
|
||||
var/b_skin = hex2num(copytext(new_skin, 6, 8))
|
||||
if(owner.change_skin_color(r_skin, g_skin, b_skin))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("skin_preset")
|
||||
if(can_change_skin_preset())
|
||||
var/new_preset = input(usr, "Choose your character's body color preset:", "Character Preference", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as null|anything in owner.species.character_color_presets
|
||||
if(new_preset)
|
||||
new_preset = owner.species.character_color_presets[new_preset]
|
||||
var/r_skin = GetRedPart(new_preset)
|
||||
var/g_skin = GetGreenPart(new_preset)
|
||||
var/b_skin = GetBluePart(new_preset)
|
||||
if(owner.change_skin_color(r_skin, g_skin, b_skin))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("hair")
|
||||
if(can_change(APPEARANCE_HAIR) && (params["hair"] in valid_hairstyles))
|
||||
if(owner.change_hair(params["hair"]))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("hair_color")
|
||||
if(can_change(APPEARANCE_HAIR_COLOR))
|
||||
var/new_hair = input("Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair)) as color|null
|
||||
if(new_hair)
|
||||
var/r_hair = hex2num(copytext(new_hair, 2, 4))
|
||||
var/g_hair = hex2num(copytext(new_hair, 4, 6))
|
||||
var/b_hair = hex2num(copytext(new_hair, 6, 8))
|
||||
if(owner.change_hair_color(r_hair, g_hair, b_hair))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("gradient")
|
||||
if(can_change(APPEARANCE_HAIR) && (params["gradient"] in valid_gradient_styles))
|
||||
if(owner.change_gradient(params["gradient"]))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("gradient_color")
|
||||
if(can_change(APPEARANCE_HAIR_COLOR))
|
||||
var/new_gradient = input("Please select gradient color.", "Gradient Color", rgb(owner.r_grad, owner.g_grad, owner.b_grad)) as color|null
|
||||
if(new_gradient)
|
||||
var/r_grad = hex2num(copytext(new_gradient, 2, 4))
|
||||
var/g_grad = hex2num(copytext(new_gradient, 4, 6))
|
||||
var/b_grad = hex2num(copytext(new_gradient, 6, 8))
|
||||
if(owner.change_gradient_color(r_grad, g_grad, b_grad))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("facial_hair")
|
||||
if(can_change(APPEARANCE_FACIAL_HAIR) && (params["facial_hair"] in valid_facial_hairstyles))
|
||||
if(owner.change_facial_hair(params["facial_hair"]))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("facial_hair_color")
|
||||
if(can_change(APPEARANCE_FACIAL_HAIR_COLOR))
|
||||
var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial)) as color|null
|
||||
if(new_facial)
|
||||
var/r_facial = hex2num(copytext(new_facial, 2, 4))
|
||||
var/g_facial = hex2num(copytext(new_facial, 4, 6))
|
||||
var/b_facial = hex2num(copytext(new_facial, 6, 8))
|
||||
if(owner.change_facial_hair_color(r_facial, g_facial, b_facial))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("eye_color")
|
||||
if(can_change(APPEARANCE_EYE_COLOR))
|
||||
var/new_eyes = input("Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) as color|null
|
||||
if(new_eyes)
|
||||
var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
|
||||
var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
|
||||
var/b_eyes = hex2num(copytext(new_eyes, 6, 8))
|
||||
if(owner.change_eye_color(r_eyes, g_eyes, b_eyes))
|
||||
update_dna()
|
||||
. = TRUE
|
||||
if("culture")
|
||||
if(can_change(APPEARANCE_CULTURE))
|
||||
var/new_culture_id = params["culture"]
|
||||
if(new_culture_id in valid_cultures)
|
||||
var/singleton/origin_item/culture/new_culture = culture_map[new_culture_id]
|
||||
owner.set_culture(new_culture)
|
||||
if(!(owner.origin in new_culture.possible_origins))
|
||||
owner.set_origin(GET_SINGLETON(pick(new_culture.possible_origins)))
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("origin")
|
||||
if(can_change(APPEARANCE_CULTURE))
|
||||
var/new_origin_id = params["origin"]
|
||||
if(new_origin_id in valid_origins)
|
||||
var/singleton/origin_item/origin/new_origin = origin_map[new_origin_id]
|
||||
owner.set_origin(new_origin)
|
||||
if(!(owner.accent in new_origin.possible_accents))
|
||||
owner.accent = new_origin.possible_accents[1]
|
||||
if(!(owner.religion in new_origin.possible_religions))
|
||||
owner.religion = new_origin.possible_religions[1]
|
||||
if(!(owner.citizenship in new_origin.possible_religions))
|
||||
owner.citizenship = new_origin.possible_citizenships[1]
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("citizenship")
|
||||
if(can_change(APPEARANCE_CULTURE))
|
||||
var/new_citizenship = params["citizenship"]
|
||||
if(new_citizenship in valid_citizenships)
|
||||
owner.citizenship = new_citizenship
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("accent")
|
||||
if(can_change(APPEARANCE_CULTURE))
|
||||
if(owner.set_accent(params["accent"]))
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("language")
|
||||
if(can_change(APPEARANCE_LANGUAGE) && (params["language"] in valid_languages))
|
||||
if(owner.add_or_remove_language(params["language"]))
|
||||
clear_and_generate_data()
|
||||
. = TRUE
|
||||
if("speech_bubble")
|
||||
if(can_change(APPEARANCE_RACE) && (params["speech_bubble"] in owner.species.possible_speech_bubble_types))
|
||||
owner.speech_bubble_type = params["speech_bubble"]
|
||||
. = TRUE
|
||||
if("set_height")
|
||||
owner.height = clamp(params["height"], owner.species.height_min, owner.species.height_max)
|
||||
if("prosthetics")
|
||||
if(can_change(APPEARANCE_PROSTHETICS))
|
||||
var/limb = tgui_input_list(owner, "Select a Limb", "Limb Modifications", valid_limbs)
|
||||
limb = reverse_parse_zone(limb) //turn it back into a proper organ tag
|
||||
if(limb)
|
||||
var/company = tgui_input_list(owner, "Select a Limb Type", "Limb Modifications", owner.species.possible_external_organs_modifications) //For amputated or nymph we just pass it straight into change_limb
|
||||
if(company == "Prosthesis")
|
||||
company = tgui_input_list(owner, "Select a Manufacturer", "Limb Modifications", valid_prosthetics)
|
||||
owner.change_limb(limb, company)
|
||||
. = TRUE
|
||||
if("organs")
|
||||
if(can_change(APPEARANCE_PROSTHETICS))
|
||||
var/organ = tgui_input_list(owner, "Select an Organ", "Organ Modification", valid_organs)
|
||||
var/obj/item/organ/internal/O = owner.internal_organs_by_name[organ]
|
||||
var/modification = tgui_input_list(owner, "Select a Modification", "Organ Modification", O.possible_modifications)
|
||||
owner.change_organ(organ, modification)
|
||||
|
||||
if("organ_preset")
|
||||
if(can_change(APPEARANCE_PROSTHETICS))
|
||||
var/organ = tgui_input_list(owner, "Select an Organ", "Organ Presets", valid_organs)
|
||||
var/obj/item/organ/internal/machine/O = owner.internal_organs_by_name[organ]
|
||||
var/list/possible_presets = list()
|
||||
for(var/P in O.organ_presets)
|
||||
var/singleton/synthetic_organ_preset/preset = GET_SINGLETON(P)
|
||||
possible_presets[preset.name] = preset
|
||||
var/singleton/synthetic_organ_preset/chosen_preset = tgui_input_list(owner, "Select a Preset", "Organ Presets", possible_presets)
|
||||
if(chosen_preset)
|
||||
O.apply_preset_data(chosen_preset)
|
||||
|
||||
/datum/tgui_module/appearance_changer/ui_interact(var/mob/user, var/datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "AppearanceChanger", "Appearance Changer", 800, 450)
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_module/appearance_changer/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
to_chat(user, SPAN_WARNING("We couldn't find you, closing."))
|
||||
SStgui.close_uis(src)
|
||||
return
|
||||
|
||||
data["owner_species"] = owner.species.name
|
||||
data["change_race"] = can_change(APPEARANCE_RACE)
|
||||
data["valid_species"] = valid_species
|
||||
data["valid_speech_bubbles"] = owner.species.possible_speech_bubble_types
|
||||
data["owner_speech_bubble"] = owner.speech_bubble_type
|
||||
data["height_max"] = owner.species.height_max
|
||||
data["height_min"] = owner.species.height_min
|
||||
data["owner_height"] = owner.height
|
||||
data["change_prosthetics"] = can_change(APPEARANCE_PROSTHETICS)
|
||||
|
||||
data["owner_gender"] = owner.gender
|
||||
data["owner_pronouns"] = owner.pronouns
|
||||
data["change_gender"] = can_change(APPEARANCE_GENDER)
|
||||
data["valid_genders"] = valid_genders
|
||||
data["valid_pronouns"] = valid_pronouns
|
||||
|
||||
data["change_culture"] = can_change(APPEARANCE_CULTURE)
|
||||
data["owner_culture"] = owner.culture.name
|
||||
data["valid_cultures"] = valid_cultures
|
||||
data["owner_origin"] = owner.origin.name
|
||||
data["valid_origins"] = valid_origins
|
||||
data["owner_citizenship"] = owner.citizenship
|
||||
data["valid_citizenships"] = valid_citizenships
|
||||
data["owner_accent"] = owner.accent
|
||||
data["valid_accents"] = valid_accents
|
||||
|
||||
var/list/owner_languages = list()
|
||||
for(var/datum/language/L in owner.languages)
|
||||
owner_languages += L.name
|
||||
data["owner_languages"] = owner_languages
|
||||
data["change_language"] = can_change(APPEARANCE_LANGUAGE)
|
||||
data["valid_languages"] = valid_languages
|
||||
|
||||
data["change_skin_tone"] = can_change_skin_tone()
|
||||
data["change_skin_color"] = can_change_skin_color()
|
||||
data["change_skin_preset"] = can_change_skin_preset()
|
||||
data["change_eye_color"] = can_change(APPEARANCE_EYE_COLOR)
|
||||
|
||||
data["change_hair"] = can_change(APPEARANCE_HAIR)
|
||||
data["owner_hair_style"] = owner.h_style
|
||||
data["valid_hair_styles"] = valid_hairstyles
|
||||
|
||||
data["change_gradient"] = can_change(APPEARANCE_HAIR)
|
||||
data["owner_gradient_style"] = owner.g_style
|
||||
data["valid_gradient_styles"] = valid_gradient_styles
|
||||
|
||||
data["change_facial_hair"] = can_change(APPEARANCE_FACIAL_HAIR)
|
||||
data["owner_facial_hair_style"] = owner.f_style
|
||||
data["valid_facial_hair_styles"] = valid_facial_hairstyles
|
||||
|
||||
data["change_hair_color"] = can_change(APPEARANCE_HAIR_COLOR)
|
||||
data["change_gradient_color"] = can_change(APPEARANCE_HAIR_COLOR)
|
||||
data["change_facial_hair_color"] = can_change(APPEARANCE_FACIAL_HAIR_COLOR)
|
||||
|
||||
return data
|
||||
|
||||
/datum/tgui_module/appearance_changer/ui_close(mob/user)
|
||||
. = ..()
|
||||
if(change_id)
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
var/obj/item/card/id/I = target_id.resolve()
|
||||
if(!istype(I) || !(istype(owner)))
|
||||
return FALSE
|
||||
owner.set_id_info(I)
|
||||
|
||||
/datum/tgui_module/appearance_changer/proc/update_dna()
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
return FALSE
|
||||
if(owner && (flags & APPEARANCE_UPDATE_DNA))
|
||||
owner.update_dna()
|
||||
|
||||
/datum/tgui_module/appearance_changer/proc/can_change(var/flag)
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
return FALSE
|
||||
return owner && (flags & flag)
|
||||
|
||||
/datum/tgui_module/appearance_changer/proc/can_change_skin_tone()
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
return FALSE
|
||||
return owner && (flags & APPEARANCE_SKIN) && owner.species.appearance_flags & HAS_SKIN_TONE
|
||||
|
||||
/datum/tgui_module/appearance_changer/proc/can_change_skin_color()
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
return FALSE
|
||||
return owner && (flags & APPEARANCE_SKIN) && owner.species.appearance_flags & HAS_SKIN_COLOR
|
||||
|
||||
/datum/tgui_module/appearance_changer/proc/can_change_skin_preset()
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
return FALSE
|
||||
return owner && (flags & APPEARANCE_SKIN) && owner.species.appearance_flags & HAS_SKIN_PRESET
|
||||
|
||||
/datum/tgui_module/appearance_changer/proc/clear_and_generate_data()
|
||||
// Making the assumption that the available species remain constant
|
||||
valid_genders = list()
|
||||
valid_pronouns = list()
|
||||
valid_hairstyles = list()
|
||||
valid_gradient_styles = list()
|
||||
valid_facial_hairstyles = list()
|
||||
valid_cultures = list()
|
||||
valid_origins = list()
|
||||
valid_citizenships = list()
|
||||
valid_accents = list()
|
||||
valid_languages = list()
|
||||
valid_prosthetics = list()
|
||||
valid_limbs = list()
|
||||
valid_organs = list()
|
||||
generate_data()
|
||||
|
||||
/datum/tgui_module/appearance_changer/proc/generate_data()
|
||||
var/mob/living/carbon/human/owner = target_human.resolve()
|
||||
if(!istype(owner))
|
||||
return FALSE
|
||||
if(!length(valid_species))
|
||||
valid_species = owner.generate_valid_species(check_whitelist, whitelist, blacklist)
|
||||
if(!length(valid_genders) && length(owner.species.default_genders))
|
||||
valid_genders = owner.species.default_genders.Copy()
|
||||
if(!length(valid_pronouns) && length(owner.species.selectable_pronouns))
|
||||
valid_pronouns = owner.species.selectable_pronouns.Copy()
|
||||
if(!length(valid_hairstyles) || !length(valid_facial_hairstyles))
|
||||
valid_hairstyles = owner.generate_valid_hairstyles(check_gender = 1)
|
||||
if(!length(valid_gradient_styles))
|
||||
valid_gradient_styles = owner.generate_valid_gradients()
|
||||
if(!length(valid_facial_hairstyles))
|
||||
valid_facial_hairstyles = owner.generate_valid_facial_hairstyles()
|
||||
if(!length(valid_cultures))
|
||||
for(var/culture in owner.species.possible_cultures)
|
||||
var/singleton/origin_item/culture/CI = GET_SINGLETON(culture)
|
||||
if(length(culture_restrictions))
|
||||
if(!(CI.type in culture_restrictions))
|
||||
continue
|
||||
valid_cultures += CI.name
|
||||
culture_map[CI.name] = CI
|
||||
if(length(culture_restrictions))
|
||||
for(var/culture in culture_restrictions)
|
||||
var/singleton/origin_item/culture/CL = GET_SINGLETON(culture)
|
||||
for(var/origin in CL.possible_origins)
|
||||
var/singleton/origin_item/origin/OI = GET_SINGLETON(origin)
|
||||
valid_origins += OI.name
|
||||
origin_map[OI.name] = OI
|
||||
var/singleton/origin_item/culture/OC = owner.culture
|
||||
for(var/origin in OC.possible_origins)
|
||||
var/singleton/origin_item/origin/OI = GET_SINGLETON(origin)
|
||||
if(length(origin_restrictions))
|
||||
if(!(OI.type in origin_restrictions))
|
||||
continue
|
||||
valid_origins += OI.name
|
||||
origin_map[OI.name] = OI
|
||||
valid_citizenships = owner.origin.possible_citizenships
|
||||
valid_accents = owner.origin.possible_accents
|
||||
if(!length(valid_languages))
|
||||
valid_languages = owner.generate_valid_languages()
|
||||
if(!length(valid_prosthetics))
|
||||
valid_prosthetics = owner.generate_valid_prosthetics()
|
||||
if(!length(valid_limbs))
|
||||
valid_limbs = owner.generate_valid_limbs()
|
||||
if(!length(valid_organs))
|
||||
valid_organs = owner.species.alterable_internal_organs
|
||||
@@ -0,0 +1,148 @@
|
||||
/datum/tgui_module/narrate_panel/ui_interact(var/mob/user, var/datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "NarratePanel", "Narrate Panel", 800, 400)
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_module/narrate_panel/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["narrate_styles"] = list("danger", "notice", "warning", "alien", "cult")
|
||||
data["narrate_locations"] = list("View", "Range", "Z-Level", "Global")
|
||||
data["narrate_filters"] = list("None", "Skrell-like Psi-sensitives", "Human-like Psi-sensitives", "Silicons", "Silicons + Implants", "Hivenet")
|
||||
return data
|
||||
|
||||
/datum/tgui_module/narrate_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
var/mob/abstract/ghost/narrate_user = ui.user
|
||||
var/is_admin = check_rights(R_ADMIN, user = ui.user)
|
||||
if(!istype(narrate_user) && !is_admin)
|
||||
return FALSE
|
||||
|
||||
if(action == "narrate")
|
||||
var/narrate_text = sanitizeSafe(params["narrate_text"])
|
||||
if(!length(narrate_text))
|
||||
to_chat(narrate_user, SPAN_WARNING("No text was supplied!"))
|
||||
return FALSE
|
||||
|
||||
var/narrate_style = params["narrate_style"]
|
||||
if(!narrate_style)
|
||||
to_chat(narrate_user, SPAN_WARNING("No text style was supplied!"))
|
||||
return FALSE
|
||||
|
||||
var/narrate_size = text2num(params["narrate_size"])
|
||||
if(!isnum(narrate_size))
|
||||
to_chat(narrate_user, SPAN_WARNING("No text size was supplied!"))
|
||||
return FALSE
|
||||
|
||||
var/narrate_range = text2num(params["narrate_range"])
|
||||
if(!isnum(narrate_range))
|
||||
to_chat(narrate_user, SPAN_WARNING("No narrate range was supplied!"))
|
||||
return FALSE
|
||||
|
||||
var/narrate_location = params["narrate_location"]
|
||||
if(!narrate_location)
|
||||
to_chat(narrate_user, SPAN_WARNING("No narrate location was supplied!"))
|
||||
return FALSE
|
||||
|
||||
var/list/mobs_to_message = list()
|
||||
switch(narrate_location)
|
||||
if("View")
|
||||
for(var/mob/M in get_hearers_in_view(narrate_range, narrate_user))
|
||||
mobs_to_message |= M
|
||||
|
||||
if("Range")
|
||||
for(var/mob/M in get_hearers_in_range(narrate_range, narrate_user))
|
||||
mobs_to_message |= M
|
||||
|
||||
if("Z-Level")
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(GET_Z(M) == narrate_user.z)
|
||||
mobs_to_message |= M
|
||||
|
||||
if("Global")
|
||||
mobs_to_message = GLOB.player_list.Copy()
|
||||
|
||||
// Skip the switch if no filter was selected.
|
||||
if (params["narrate_filter"] == "None")
|
||||
for(var/mob/actor in mobs_to_message)
|
||||
to_chat(actor, "<font size=[narrate_size]><span class='[narrate_style]'>[narrate_text]</span></font>")
|
||||
return
|
||||
|
||||
// Create a copy of the message receivers. We have to iterate the copy of the list in order to safely remove entries from the list of message receivers.
|
||||
// Otherwise we'll get memory exceptions.
|
||||
var/list/filtered_list = mobs_to_message.Copy()
|
||||
var/narrate_filter = params["narrate_filter"]
|
||||
switch(narrate_filter)
|
||||
if ("Skrell-like Psi-sensitives")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// Keep observers in the list, which includes ghosts, aghosts, and storytellers.
|
||||
if (isghost(filteree))
|
||||
continue
|
||||
|
||||
// This will check for anyone who is capable of receiving and interpreting telepathic messages.
|
||||
// It will include Skrell who don't have a mindshield or the Low Psi-sensitivity trait.
|
||||
// It will also include characters who have Psi-receivers, the High Psi-sensitivity trait, or are under the effects of Psycho-nootropic drugs.
|
||||
if (IS_TELEPATHY_BLOCKED(filteree, PSI_RANK_SENSITIVE))
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
if ("Human-like Psi-sensitives")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// Keep observers in the list, which includes ghosts, aghosts, and storytellers.
|
||||
if (isghost(filteree))
|
||||
continue
|
||||
|
||||
// This will check for anyone who has a Zona Bovina capable of hearing psionics at all.
|
||||
if (IS_TELEPATHY_BLOCKED(filteree, 0))
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
if ("Silicons")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// Keep observers in the list, which includes ghosts, aghosts, and storytellers.
|
||||
if (isghost(filteree))
|
||||
continue
|
||||
|
||||
// List will include Borgs, Robots, Shipbounds, pAIs, and IPCs.
|
||||
if(issilicon(filteree) || isipc(filteree) || ispAI(filteree))
|
||||
continue
|
||||
|
||||
// Remove anyone not made of silicon.
|
||||
mobs_to_message.Remove(filteree)
|
||||
if ("Silicons + Implants")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// Keep observers in the list, which includes ghosts, aghosts, and storytellers.
|
||||
if (isghost(filteree))
|
||||
continue
|
||||
|
||||
// List will include Borgs, Robots, Shipbounds, pAIs, and IPCs.
|
||||
if(issilicon(filteree) || isipc(filteree) || ispAI(filteree))
|
||||
continue
|
||||
|
||||
if (!ishuman(filteree))
|
||||
// The mob in question can't have a brain at all, filter it early.
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
|
||||
var/mob/living/carbon/human/brain_haver = filteree
|
||||
// Check for silicon brain implants.
|
||||
var/obj/item/organ/internal/brain = brain_haver.internal_organs_by_name[BP_BRAIN]
|
||||
if (brain && ((brain.status & ORGAN_ASSISTED) || (brain.status & ORGAN_ROBOT)))
|
||||
continue
|
||||
|
||||
// Remove anyone not made of silicon or doesn't have a cranial implant.
|
||||
mobs_to_message.Remove(filteree)
|
||||
if ("Hivenet")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// Keep observers in the list, which includes ghosts, aghosts, and storytellers.
|
||||
if (isghost(filteree))
|
||||
continue
|
||||
|
||||
// Filter anyone who doesn't have the Vaurca language at all.
|
||||
if (!(GLOB.all_languages[LANGUAGE_VAURCA] in filteree.languages))
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
|
||||
for(var/mob/actor in mobs_to_message)
|
||||
to_chat(actor, "<font size=[narrate_size]><span class='[narrate_style]'>[narrate_text]</span></font>")
|
||||
@@ -70,7 +70,7 @@
|
||||
// Disable UIs if unconscious.
|
||||
else if(stat)
|
||||
return UI_DISABLED
|
||||
// Update UIs if incapicitated but concious.
|
||||
// Update UIs if incapicitated but conscious.
|
||||
else if(incapacitated())
|
||||
return UI_UPDATE
|
||||
return UI_INTERACTIVE
|
||||
@@ -129,6 +129,3 @@
|
||||
|
||||
/datum/ui_state/proc/href_list(mob/user)
|
||||
return list()
|
||||
|
||||
/datum/proc/nano_container()
|
||||
return src
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new)
|
||||
|
||||
/datum/ui_state/default/can_use_topic(src_object, mob/user)
|
||||
return user.default_can_use_topic(src_object) // Call the individual mob-overridden procs.
|
||||
return user?.default_can_use_topic(src_object) // Call the individual mob-overridden procs.
|
||||
|
||||
/mob/proc/default_can_use_topic(src_object)
|
||||
return UI_CLOSE // Don't allow interaction by default.
|
||||
@@ -41,8 +41,11 @@ GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new)
|
||||
if(. < UI_INTERACTIVE)
|
||||
return
|
||||
|
||||
if(src_object == src)
|
||||
return UI_INTERACTIVE
|
||||
|
||||
// The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled.
|
||||
if(!control_disabled)
|
||||
if(!control_disabled && can_see(src_object))
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
|
||||
|
||||
@@ -83,6 +83,9 @@
|
||||
return UI_DISABLED // Otherwise they can keep the UI open.
|
||||
|
||||
/mob/living/silicon/ai/get_ui_access(atom/source)
|
||||
if(source == src)
|
||||
return UI_INTERACTIVE
|
||||
|
||||
// The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled.
|
||||
if(!control_disabled && can_see(source))
|
||||
return UI_INTERACTIVE
|
||||
@@ -113,3 +116,11 @@
|
||||
return UI_CLOSE
|
||||
|
||||
return UI_INTERACTIVE
|
||||
|
||||
/// Return UI_INTERACTIVE if the user is inside the target atom, whether they can see it or not.
|
||||
/// Return UI_CLOSE otherwise.
|
||||
/proc/ui_status_user_inside(mob/user, atom/target)
|
||||
if(target.contains(user))
|
||||
return UI_INTERACTIVE
|
||||
|
||||
return UI_CLOSE
|
||||
|
||||
+35
-51
@@ -37,11 +37,9 @@
|
||||
var/datum/ui_state/state = null
|
||||
/// Rate limit client refreshes to prevent DoS.
|
||||
COOLDOWN_DECLARE(refresh_cooldown)
|
||||
/// Are byond mouse events beyond the window passed in to the ui
|
||||
var/mouse_hooked = FALSE
|
||||
|
||||
/// Any partial packets that we have received from TGUI, waiting to be sent
|
||||
var/partial_packets
|
||||
/// The id of any ByondUi elements that we have opened
|
||||
var/list/open_byondui_elements
|
||||
|
||||
/**
|
||||
* public
|
||||
@@ -58,9 +56,6 @@
|
||||
* return datum/tgui The requested UI.
|
||||
*/
|
||||
/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
|
||||
log_tgui(user,
|
||||
"new [interface] fancy [user?.client?.prefs.tgui_fancy]",
|
||||
src_object = src_object)
|
||||
src.user = user
|
||||
src.src_object = src_object
|
||||
src.window_key = "[REF(src_object)]-main"
|
||||
@@ -85,7 +80,7 @@
|
||||
* return bool - TRUE if a new pooled window is opened, FALSE in all other situations including if a new pooled window didn't open because one already exists.
|
||||
*/
|
||||
/datum/tgui/proc/open()
|
||||
if(!user?.client)
|
||||
if(user && !user.client)
|
||||
return FALSE
|
||||
if(window)
|
||||
return FALSE
|
||||
@@ -100,7 +95,6 @@
|
||||
if(!window.is_ready())
|
||||
window.initialize(
|
||||
strict_mode = TRUE,
|
||||
fancy = user.client.prefs.tgui_fancy,
|
||||
assets = list(
|
||||
get_asset_datum(/datum/asset/simple/tgui),
|
||||
))
|
||||
@@ -110,8 +104,6 @@
|
||||
window.send_message("update", get_payload(
|
||||
with_data = TRUE,
|
||||
with_static_data = TRUE))
|
||||
if(mouse_hooked)
|
||||
window.set_mouse_macro()
|
||||
SStgui.on_open(src)
|
||||
|
||||
return TRUE
|
||||
@@ -146,11 +138,27 @@
|
||||
window.release_lock()
|
||||
window.close(can_be_suspended)
|
||||
src_object.ui_close(user)
|
||||
SEND_SIGNAL(src, COMSIG_TGUI_CLOSE, user)
|
||||
SStgui.on_close(src)
|
||||
|
||||
if(user.client)
|
||||
terminate_byondui_elements()
|
||||
|
||||
state = null
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Closes all ByondUI elements, left dangling by a forceful TGUI exit,
|
||||
* such as via Alt+F4, or terminating the process
|
||||
*
|
||||
*/
|
||||
/datum/tgui/proc/terminate_byondui_elements()
|
||||
set waitfor = FALSE
|
||||
|
||||
for(var/byondui_element in open_byondui_elements)
|
||||
winset(user.client, byondui_element, list("parent" = ""))
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
@@ -161,18 +169,6 @@
|
||||
/datum/tgui/proc/set_autoupdate(autoupdate)
|
||||
src.autoupdate = autoupdate
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Enable/disable passing through byond mouse events to the window
|
||||
*
|
||||
* required value bool Enable/disable hooking.
|
||||
*/
|
||||
/datum/tgui/proc/set_mouse_hook(value)
|
||||
src.mouse_hooked = value
|
||||
//Handle unhooking/hooking on already open windows ?
|
||||
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
@@ -248,12 +244,13 @@
|
||||
json_data["config"] = list(
|
||||
"title" = title,
|
||||
"status" = status,
|
||||
"interface" = interface,
|
||||
"interface" = list(
|
||||
"name" = interface,
|
||||
),
|
||||
"refreshing" = refreshing,
|
||||
"window" = list(
|
||||
"key" = window_key,
|
||||
"size" = window_size,
|
||||
"fancy" = user.client.prefs.tgui_fancy,
|
||||
"locked" = user.client.prefs.tgui_lock,
|
||||
"scale" = user.client.prefs.ui_scale,
|
||||
),
|
||||
@@ -329,31 +326,6 @@
|
||||
// Pass act type messages to ui_act
|
||||
if(type && copytext(type, 1, 5) == "act/")
|
||||
var/act_type = copytext(type, 5)
|
||||
|
||||
var/id = href_list["packetId"]
|
||||
if(!isnull(id))
|
||||
id = text2num(id)
|
||||
|
||||
var/total = text2num(href_list["totalPackets"])
|
||||
|
||||
if(total > MAX_MESSAGE_CHUNKS)
|
||||
return
|
||||
|
||||
if(id == 1)
|
||||
partial_packets = new /list(total)
|
||||
|
||||
partial_packets[id] = href_list["packet"]
|
||||
|
||||
if(id != total)
|
||||
return
|
||||
|
||||
var/assembled_payload = ""
|
||||
for(var/packet in partial_packets)
|
||||
assembled_payload += packet
|
||||
|
||||
payload = json_decode(assembled_payload)
|
||||
partial_packets = null
|
||||
|
||||
log_tgui(user, "Action: [act_type] [href_list["payload"]]",
|
||||
window = window,
|
||||
src_object = src_object)
|
||||
@@ -381,6 +353,18 @@
|
||||
LAZYINITLIST(src_object.tgui_shared_states)
|
||||
src_object.tgui_shared_states[href_list["key"]] = href_list["value"]
|
||||
SStgui.update_uis(src_object)
|
||||
if(TGUI_MANAGED_BYONDUI_TYPE_RENDER)
|
||||
var/byond_ui_id = payload[TGUI_MANAGED_BYONDUI_PAYLOAD_ID]
|
||||
if(!byond_ui_id || LAZYLEN(open_byondui_elements) > TGUI_MANAGED_BYONDUI_LIMIT)
|
||||
return
|
||||
|
||||
LAZYOR(open_byondui_elements, byond_ui_id)
|
||||
if(TGUI_MANAGED_BYONDUI_TYPE_UNMOUNT)
|
||||
var/byond_ui_id = payload[TGUI_MANAGED_BYONDUI_PAYLOAD_ID]
|
||||
if(!byond_ui_id)
|
||||
return
|
||||
|
||||
LAZYREMOVE(open_byondui_elements, byond_ui_id)
|
||||
|
||||
/// Wrapper for behavior to potentially wait until the next tick if the server is overloaded
|
||||
/datum/tgui/proc/on_act_message(act_type, payload, state)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
var/is_browser = FALSE
|
||||
var/status = TGUI_WINDOW_CLOSED
|
||||
var/locked = FALSE
|
||||
var/visible = FALSE
|
||||
var/datum/tgui/locked_by
|
||||
var/datum/subscriber_object
|
||||
var/subscriber_delegate
|
||||
@@ -19,12 +20,12 @@
|
||||
var/sent_assets = list()
|
||||
// Vars passed to initialize proc (and saved for later)
|
||||
var/initial_strict_mode
|
||||
var/initial_fancy
|
||||
var/initial_assets
|
||||
var/initial_inline_html
|
||||
var/initial_inline_js
|
||||
var/initial_inline_css
|
||||
var/mouse_event_macro_set = FALSE
|
||||
|
||||
var/list/oversized_payloads = list()
|
||||
|
||||
/**
|
||||
* public
|
||||
@@ -50,7 +51,6 @@
|
||||
* will be put into the queue until the window finishes loading.
|
||||
*
|
||||
* optional strict_mode bool - Enables strict error handling and BSOD.
|
||||
* optional fancy bool - If TRUE and if this is NOT a panel, will hide the window titlebar.
|
||||
* optional assets list - List of assets to load during initialization.
|
||||
* optional inline_html string - Custom HTML to inject.
|
||||
* optional inline_js string - Custom JS to inject.
|
||||
@@ -58,7 +58,6 @@
|
||||
*/
|
||||
/datum/tgui_window/proc/initialize(
|
||||
strict_mode = FALSE,
|
||||
fancy = FALSE,
|
||||
assets = list(),
|
||||
inline_html = "",
|
||||
inline_js = "",
|
||||
@@ -68,7 +67,6 @@
|
||||
window = src)
|
||||
if(!client)
|
||||
return
|
||||
src.initial_fancy = fancy
|
||||
src.initial_assets = assets
|
||||
src.initial_inline_html = inline_html
|
||||
src.initial_inline_js = inline_js
|
||||
@@ -76,16 +74,12 @@
|
||||
status = TGUI_WINDOW_LOADING
|
||||
fatally_errored = FALSE
|
||||
// Build window options
|
||||
var/options = "file=[id].html;can_minimize=0;auto_format=0;"
|
||||
// Remove titlebar and resize handles for a fancy window
|
||||
if(fancy)
|
||||
options += "titlebar=0;can_resize=0;"
|
||||
else
|
||||
options += "titlebar=1;can_resize=1;"
|
||||
var/options = "file=[id].html;can_minimize=0;auto_format=0;titlebar=0;can_resize=0;"
|
||||
// Generate page html
|
||||
var/html = SStgui.basehtml
|
||||
html = replacetextEx(html, "\[tgui:windowId]", id)
|
||||
html = replacetextEx(html, "\[tgui:strictMode]", strict_mode)
|
||||
html = replacetextEx(html, "\[tgui:storagecdn]", GLOB.config.storage_cdn_iframe)
|
||||
// Inject assets
|
||||
var/inline_assets_str = ""
|
||||
for(var/datum/asset/asset in assets)
|
||||
@@ -128,7 +122,6 @@
|
||||
/datum/tgui_window/proc/reinitialize()
|
||||
initialize(
|
||||
strict_mode = initial_strict_mode,
|
||||
fancy = initial_fancy,
|
||||
assets = initial_assets,
|
||||
inline_html = initial_inline_html,
|
||||
inline_js = initial_inline_js,
|
||||
@@ -220,12 +213,11 @@
|
||||
/datum/tgui_window/proc/close(can_be_suspended = TRUE)
|
||||
if(!client)
|
||||
return
|
||||
if(mouse_event_macro_set)
|
||||
remove_mouse_macro()
|
||||
if(can_be_suspended && can_be_suspended())
|
||||
log_tgui(client,
|
||||
context = "[id]/close (suspending)",
|
||||
window = src)
|
||||
visible = FALSE
|
||||
status = TGUI_WINDOW_READY
|
||||
send_message("suspend")
|
||||
return
|
||||
@@ -233,6 +225,7 @@
|
||||
context = "[id]/close",
|
||||
window = src)
|
||||
release_lock()
|
||||
visible = FALSE
|
||||
status = TGUI_WINDOW_CLOSED
|
||||
message_queue = null
|
||||
// Do not close the window to give user some time
|
||||
@@ -298,9 +291,6 @@
|
||||
return
|
||||
sent_assets |= list(asset)
|
||||
. = asset.send(client)
|
||||
if(istype(asset, /datum/asset/spritesheet))
|
||||
var/datum/asset/spritesheet/spritesheet = asset
|
||||
send_message("asset/stylesheet", spritesheet.css_filename())
|
||||
send_raw_message(asset.get_serialized_url_mappings())
|
||||
|
||||
/**
|
||||
@@ -364,6 +354,11 @@
|
||||
switch(type)
|
||||
if("ping")
|
||||
send_message("ping/reply", payload)
|
||||
if("ping/set")
|
||||
client?.avgping = payload["ping"]
|
||||
if("visible")
|
||||
visible = TRUE
|
||||
SEND_SIGNAL(src, COMSIG_TGUI_WINDOW_VISIBLE, client)
|
||||
if("suspend")
|
||||
close(can_be_suspended = TRUE)
|
||||
if("close")
|
||||
@@ -372,40 +367,48 @@
|
||||
client << link(href_list["url"])
|
||||
if("cacheReloaded")
|
||||
reinitialize()
|
||||
if("chat/resend")
|
||||
SSchat.handle_resend(client, payload)
|
||||
if("oversizedPayloadRequest")
|
||||
var/payload_id = payload["id"]
|
||||
var/chunk_count = payload["chunkCount"]
|
||||
var/permit_payload = chunk_count <= 32
|
||||
if(permit_payload)
|
||||
create_oversized_payload(payload_id, payload["type"], chunk_count)
|
||||
send_message("oversizePayloadResponse", list("allow" = permit_payload, "id" = payload_id))
|
||||
if("payloadChunk")
|
||||
var/payload_id = payload["id"]
|
||||
append_payload_chunk(payload_id, payload["chunk"])
|
||||
send_message("acknowledgePayloadChunk", list("id" = payload_id))
|
||||
|
||||
/datum/tgui_window/proc/set_mouse_macro()
|
||||
if(mouse_event_macro_set)
|
||||
/datum/tgui_window/vv_edit_var(var_name, var_value)
|
||||
return var_name != NAMEOF(src, id) && ..()
|
||||
|
||||
/datum/tgui_window/proc/create_oversized_payload(payload_id, message_type, chunk_count)
|
||||
if(oversized_payloads[payload_id])
|
||||
stack_trace("Attempted to create oversized tgui payload with duplicate ID.")
|
||||
return
|
||||
|
||||
var/list/byondToTguiEventMap = list(
|
||||
"MouseDown" = "byond/mousedown",
|
||||
"MouseUp" = "byond/mouseup"
|
||||
oversized_payloads[payload_id] = list(
|
||||
"type" = message_type,
|
||||
"count" = chunk_count,
|
||||
"chunks" = list(),
|
||||
"timeout" = addtimer(CALLBACK(src, PROC_REF(remove_oversized_payload), payload_id), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
|
||||
)
|
||||
|
||||
for(var/mouseMacro in byondToTguiEventMap)
|
||||
var/command_template = ".output CONTROL PAYLOAD"
|
||||
var/event_message = TGUI_CREATE_MESSAGE(byondToTguiEventMap[mouseMacro], null)
|
||||
var target_control = is_browser \
|
||||
? "[id]:update" \
|
||||
: "[id].browser:update"
|
||||
var/with_id = replacetext(command_template, "CONTROL", target_control)
|
||||
var/full_command = replacetext(with_id, "PAYLOAD", event_message)
|
||||
/datum/tgui_window/proc/append_payload_chunk(payload_id, chunk)
|
||||
var/list/payload = oversized_payloads[payload_id]
|
||||
if(!payload)
|
||||
return
|
||||
var/list/chunks = payload["chunks"]
|
||||
chunks += chunk
|
||||
if(length(chunks) >= payload["count"])
|
||||
deltimer(payload["timeout"])
|
||||
var/message_type = payload["type"]
|
||||
var/final_payload = chunks.Join()
|
||||
remove_oversized_payload(payload_id)
|
||||
on_message(message_type, json_decode(final_payload), list("type" = message_type, "payload" = final_payload, "tgui" = TRUE, "window_id" = id))
|
||||
else
|
||||
payload["timeout"] = addtimer(CALLBACK(src, PROC_REF(remove_oversized_payload), payload_id), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
|
||||
|
||||
var/list/params = list()
|
||||
params["parent"] = "default" //Technically this is external to tgui but whatever
|
||||
params["name"] = mouseMacro
|
||||
params["command"] = full_command
|
||||
|
||||
winset(client, "[mouseMacro]Window[id]Macro", params)
|
||||
mouse_event_macro_set = TRUE
|
||||
|
||||
/datum/tgui_window/proc/remove_mouse_macro()
|
||||
if(!mouse_event_macro_set)
|
||||
stack_trace("Unsetting mouse macro on tgui window that has none")
|
||||
var/list/byondToTguiEventMap = list(
|
||||
"MouseDown" = "byond/mousedown",
|
||||
"MouseUp" = "byond/mouseup"
|
||||
)
|
||||
for(var/mouseMacro in byondToTguiEventMap)
|
||||
winset(client, null, "[mouseMacro]Window[id]Macro.parent=null")
|
||||
mouse_event_macro_set = FALSE
|
||||
/datum/tgui_window/proc/remove_oversized_payload(payload_id)
|
||||
oversized_payloads -= payload_id
|
||||
|
||||
Reference in New Issue
Block a user