Files
Bubberstation/code/modules/admin/view_variables/modify_variables.dm
LemonInTheDark ded0ee6362 Bitflag Metainfo, Usable (Happy 90000th) (#90000)
## About The Pull Request

Makes wrapper procs to support working with bitflags as defined "things"
rather then EXCLUSIVELY numbers, mostly for getting random flags.

I've ~~also added a unit test to prevent bitfields from being double
defined~~ stolen some code form moth to make double definitions here a
compile error.

This does rely on people actually using the bitfield lists, essentially
upgrading them from breaking just admin debug to POTENTIALLY breaking
game logic (slightly). Would like input on this @tgstation/commit-access

## Why It's Good For The Game

More tools for feature coders to play with, more sane code
2025-03-19 19:54:03 +00:00

390 lines
12 KiB
Plaintext

GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "mob")) //Requires DEBUG
GLOBAL_PROTECT(VVlocked)
GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays")) //Requires DEBUG or FUN
GLOBAL_PROTECT(VVicon_edit_lock)
GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) //Requires DEBUG or SPAWN
GLOBAL_PROTECT(VVckey_edit)
GLOBAL_LIST_INIT(VVpixelmovement, list("bound_x", "bound_y", "step_x", "step_y", "step_size", "bound_height", "bound_width", "bounds")) //No editing ever.
GLOBAL_PROTECT(VVpixelmovement)
/client/proc/vv_parse_text(O, new_var)
if(O && findtext(new_var,"\["))
var/process_vars = tgui_alert(usr,"\[] detected in string, process as variables?","Process Variables?",list("Yes","No"))
if(process_vars == "Yes")
. = string2listofvars(new_var, O)
//do they want you to include subtypes?
//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes)
//TRUE = Yes subtypes
//NULL = User cancelled at the prompt or invalid type given
/client/proc/vv_subtype_prompt(type)
if (!ispath(type))
return
var/list/subtypes = subtypesof(type)
if (!subtypes || !subtypes.len)
return FALSE
if (subtypes?.len)
switch(tgui_alert(usr,"Strict object type detection?", "Type detection", list("Strictly this type","This type and subtypes", "Cancel")))
if("Strictly this type")
return FALSE
if("This type and subtypes")
return TRUE
else
return
/client/proc/vv_reference_list(type, subtypes)
. = list()
var/list/types = list(type)
if (subtypes)
types = typesof(type)
var/list/fancytypes = make_types_fancy(types)
for(var/fancytype in fancytypes) //swap the assoication
types[fancytypes[fancytype]] = fancytype
var/things = get_all_of_type(type, subtypes)
var/i = 0
for(var/thing in things)
var/datum/D = thing
i++
//try one of 3 methods to shorten the type text:
// fancy type,
// fancy type with the base type removed from the begaining,
// the type with the base type removed from the begaining
var/fancytype = types[D.type]
if (findtext(fancytype, types[type]))
fancytype = copytext(fancytype, length(types[type]) + 1)
var/shorttype = copytext("[D.type]", length("[type]") + 1)
if (length_char(shorttype) > length_char(fancytype))
shorttype = fancytype
if (!length(shorttype))
shorttype = "/"
.["[D]([shorttype])[REF(D)]#[i]"] = D
/client/proc/mod_list_add_ass(atom/O) //hehe
var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/class = L["class"]
if (!class)
return
var/var_value = L["value"]
if(class == VV_TEXT || class == VV_MESSAGE)
var/list/varsvars = vv_parse_text(O, var_value)
for(var/V in varsvars)
var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]")
return var_value
/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar)
var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/class = LL["class"]
if (!class)
return
var/var_value = LL["value"]
if(class == VV_TEXT || class == VV_MESSAGE)
var/list/varsvars = vv_parse_text(O, var_value)
for(var/V in varsvars)
var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]")
if (O)
L = L.Copy()
L += list(var_value) //var_value could be a list
switch(tgui_alert(usr,"Would you like to associate a value with the list entry?",,list("Yes","No")))
if("Yes")
L[var_value] = mod_list_add_ass(O) //hehe
if (O)
if (O.vv_edit_var(objectvar, L) == FALSE)
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE)
if(!check_rights(R_VAREDIT))
return
if(!istype(L, /list))
to_chat(src, "Not a List.", confidential = TRUE)
return
if(L.len > 1000)
var/confirm = tgui_alert(usr, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", list("Continue", "Abort"))
if(confirm != "Continue")
return
var/is_normal_list = IS_NORMAL_LIST(L)
var/list/names = list()
for (var/i in 1 to L.len)
var/key = L[i]
var/value
if (is_normal_list && !isnum(key))
value = L[key]
if (value == null)
value = "null"
names["#[i] [key] = [value]"] = i
if (!index)
var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)"
if(variable == null)
return
if(variable == "(ADD VAR)")
mod_list_add(L, O, original_name, objectvar)
return
if(variable == "(CLEAR NULLS)")
L = L.Copy()
list_clear_nulls(L)
if (!O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS")
return
if(variable == "(CLEAR DUPES)")
L = unique_list(L)
if (!O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES")
return
if(variable == "(SHUFFLE)")
L = shuffle(L)
if (!O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE")
message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE")
return
index = names[variable]
var/assoc_key
if (index == null)
return
var/assoc = 0
var/prompt = tgui_alert(usr, "Do you want to edit the key or its assigned value?", "Associated List", list("Key", "Assigned Value", "Cancel"))
if (prompt == "Cancel")
return
if (prompt == "Assigned Value")
assoc = 1
assoc_key = L[index]
var/default
var/variable
var/old_assoc_value //EXPERIMENTAL - Keep old associated value while modifying key, if any
if(is_normal_list)
if (assoc)
variable = L[assoc_key]
else
variable = L[index]
//EXPERIMENTAL - Keep old associated value while modifying key, if any
if(IS_VALID_ASSOC_KEY(variable))
var/found = L[variable]
if(!isnull(found))
old_assoc_value = found
//
default = vv_get_class(objectvar, variable)
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.", confidential = TRUE)
to_chat(src, "Variable contains: [variable]", confidential = TRUE)
if(default == VV_NUM)
var/dir_text = ""
var/tdir = variable
if(tdir > 0 && tdir < 16)
if(tdir & 1)
dir_text += "NORTH"
if(tdir & 2)
dir_text += "SOUTH"
if(tdir & 4)
dir_text += "EAST"
if(tdir & 8)
dir_text += "WEST"
if(dir_text)
to_chat(usr, "If a direction, direction is: [dir_text]", confidential = TRUE)
var/original_var = variable
if (O)
L = L.Copy()
var/class
if(autodetect_class)
if (default == VV_TEXT)
default = VV_MESSAGE
class = default
var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST"))
class = LL["class"]
if (!class)
return
var/new_var = LL["value"]
if(class == VV_MESSAGE)
class = VV_TEXT
switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet.
if(VV_LIST)
mod_list(variable, O, original_name, objectvar)
if("DELETE FROM LIST")
L.Cut(index, index+1)
if (O)
if (O.vv_edit_var(objectvar, L))
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
return
if(VV_TEXT)
var/list/varsvars = vv_parse_text(O, new_var)
for(var/V in varsvars)
new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]")
if(is_normal_list)
if(assoc)
L[assoc_key] = new_var
else
L[index] = new_var
if(!isnull(old_assoc_value) && IS_VALID_ASSOC_KEY(new_var))
L[new_var] = old_assoc_value
if (O)
if (O.vv_edit_var(objectvar, L) == FALSE)
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]")
message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]")
/proc/vv_varname_lockcheck(param_var_name)
if(param_var_name in GLOB.VVlocked)
if(!check_rights(R_DEBUG))
return FALSE
if(param_var_name in GLOB.VVckey_edit)
if(!check_rights(R_SPAWN|R_DEBUG))
return FALSE
if(param_var_name in GLOB.VVicon_edit_lock)
if(!check_rights(R_FUN|R_DEBUG))
return FALSE
return TRUE
/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0)
if(!check_rights(R_VAREDIT))
return
var/class
var/variable
var/var_value
if(param_var_name)
if(!(param_var_name in O.vars))
to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])", confidential = TRUE)
return
variable = param_var_name
else
var/list/names = list()
for (var/V in O.vars)
names += V
names = sort_list(names)
variable = input("Which var?","Var") as null|anything in names
if(!variable)
return
if(!O.can_vv_get(variable))
return
var_value = O.vars[variable]
if(!vv_varname_lockcheck(variable))
return
var/default = vv_get_class(variable, var_value)
if(isnull(default))
to_chat(src, "Unable to determine variable type.", confidential = TRUE)
else
to_chat(src, "Variable appears to be <b>[uppertext(default)]</b>.", confidential = TRUE)
to_chat(src, "Variable contains: [var_value]", confidential = TRUE)
if(default == VV_NUM)
var/dir_text = ""
if(var_value > 0 && var_value < 16)
if(var_value & 1)
dir_text += "NORTH"
if(var_value & 2)
dir_text += "SOUTH"
if(var_value & 4)
dir_text += "EAST"
if(var_value & 8)
dir_text += "WEST"
if(dir_text)
to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE)
if(autodetect_class && default != VV_NULL)
if (default == VV_TEXT)
default = VV_MESSAGE
class = default
var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST), var_name = variable)
class = value["class"]
if (!class)
return
var/var_new = value["value"]
if(class == VV_MESSAGE)
class = VV_TEXT
var/original_name = "[O]"
switch(class)
if(VV_LIST)
if(!islist(var_value))
mod_list(list(), O, original_name, variable)
mod_list(var_value, O, original_name, variable)
return
if(VV_RESTORE_DEFAULT)
var_new = initial(O.vars[variable])
if(VV_TEXT)
var/list/varsvars = vv_parse_text(O, var_new)
for(var/V in varsvars)
var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]")
if (O.vv_edit_var(variable, var_new) == FALSE)
to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
vv_update_display(O, "varedited", VV_MSG_EDITED)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args)
log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]")
log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]")
var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]"
message_admins(msg)
admin_ticket_log(O, msg)
return TRUE