From 60e287c0196306eeaf6a0bdd499b22478503ae8c Mon Sep 17 00:00:00 2001 From: tigercat2000 Date: Thu, 14 Sep 2017 13:45:06 -0700 Subject: [PATCH 1/3] VV Refactor Ports /tg/'s VV refactor. Most of this is just cleaning up and making it easier on the coders, but everyone else benefits too: - Mass mod no longer freezes the server (CHECK_TICK <3) - Attempting to change any of the step_* values or anything that will otherwise disable pixel movement will now prompt you and ask if you are sure you want to continue - You can actually enter |text| again in a box, rather than being forced to always use the multi-line input - Cookies! VV saves your search and scroll position for a given UID, rather convenient for debugging - The refresh button on /clients works finally - You can view lists in detail by clicking on them like any other reference, and edit them directly that way. This also means there is no hard limit on how many variables you can see in a list before it's truncated- it will just be hidden until you view the list in VV directly. - You can shuffle lists and clear duplicate/null entries. - Mildly reordered the stuff in the dropdown, inconvenient but necessary. --- code/__DEFINES/typeids.dm | 6 + code/__DEFINES/vv.dm | 21 + code/__HELPERS/lists.dm | 25 + code/__HELPERS/type2type.dm | 37 + code/__HELPERS/unique_ids.dm | 1 + code/__HELPERS/unsorted.dm | 94 +- code/datums/datumvars.dm | 883 +++++++++------- code/game/atoms.dm | 17 +- .../game/objects/items/weapons/power_cells.dm | 15 +- code/game/objects/objs.dm | 6 +- code/modules/admin/holder2.dm | 3 + code/modules/admin/verbs/massmodvar.dm | 523 ++++------ code/modules/admin/verbs/modifyvariables.dm | 948 ++++++++++-------- code/modules/mob/living/carbon/human/human.dm | 16 +- code/modules/mob/living/update_status.dm | 8 +- code/modules/mob/mob.dm | 28 + code/modules/projectiles/guns/energy.dm | 15 +- paradise.dme | 2 + 18 files changed, 1520 insertions(+), 1128 deletions(-) create mode 100644 code/__DEFINES/typeids.dm create mode 100644 code/__DEFINES/vv.dm diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm new file mode 100644 index 00000000000..4fd2c390fa7 --- /dev/null +++ b/code/__DEFINES/typeids.dm @@ -0,0 +1,6 @@ +//Byond type ids +#define TYPEID_NULL "0" +#define TYPEID_NORMAL_LIST "f" +//helper macros +#define GET_TYPEID(ref) ( ( (lentext(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, lentext(ref) - 6) ) ) +#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) \ No newline at end of file diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm new file mode 100644 index 00000000000..3acd09a1a81 --- /dev/null +++ b/code/__DEFINES/vv.dm @@ -0,0 +1,21 @@ +#define VV_NUM "Number" +#define VV_TEXT "Text" +#define VV_MESSAGE "Mutiline Text" +#define VV_ICON "Icon" +#define VV_ATOM_REFERENCE "Atom Reference" +#define VV_DATUM_REFERENCE "Datum Reference" +#define VV_MOB_REFERENCE "Mob Reference" +#define VV_CLIENT "Client" +#define VV_ATOM_TYPE "Atom Typepath" +#define VV_DATUM_TYPE "Datum Typepath" +#define VV_TYPE "Custom Typepath" +#define VV_MATRIX "Matrix" +#define VV_FILE "File" +#define VV_LIST "List" +#define VV_NEW_ATOM "New Atom" +#define VV_NEW_DATUM "New Datum" +#define VV_NEW_TYPE "New Custom Typepath" +#define VV_NEW_LIST "New List" +#define VV_NULL "NULL" +#define VV_RESTORE_DEFAULT "Restore to Default" +#define VV_MARKED_DATUM "Marked Datum" \ No newline at end of file diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 3556ef2d6f4..6ee5097a76e 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -654,3 +654,28 @@ proc/dd_sortedObjectList(list/incoming) // LAZYING PT 2: THE LAZENING #define LAZYREINITLIST(L) LAZYCLEARLIST(L); LAZYINITLIST(L); + + +//same, but returns nothing and acts on list in place +/proc/shuffle_inplace(list/L) + if(!L) + return + + for(var/i=1, i list("A dog said hi ","name]!" + // jointext() --> "A dog said hi name]!" + // splittext() --> list("A","dog","said","hi","name]!") + + t_string = replacetext(t_string, "\[", "\[ ")//Necessary to resolve "word[var_name]" scenarios + var/list/list_value = splittext(t_string, "\[") + var/intermediate_stage = jointext(list_value, null) + + list_value = splittext(intermediate_stage, " ") + for(var/value in list_value) + if(findtext(value, "]")) + value = splittext(value, "]") //"name]!" --> list("name","!") + for(var/A in value) + if(var_source.vars.Find(A)) + . += A \ No newline at end of file diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm index 9363df4252e..f6f9393d937 100644 --- a/code/__HELPERS/unique_ids.dm +++ b/code/__HELPERS/unique_ids.dm @@ -32,6 +32,7 @@ var/global/next_unique_datum_id = 1 unique_datum_id = "\ref[src]_[next_unique_datum_id++]" return unique_datum_id + /proc/locateUID(uid) if(!istext(uid)) return null diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 3900ba2ba33..892dd3f8107 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1730,53 +1730,81 @@ var/mob/dview/dview_mob = new closest_atom = A return closest_atom -/proc/pick_closest_path(value) - var/list/matches = get_fancy_list_of_types() - if(!isnull(value) && value!="") +/proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types()) + if(value == FALSE) //nothing should be calling us with a number, so this is safe + value = input("Enter type to find (blank for all, cancel to cancel)", "Search for type") as null|text + if(isnull(value)) + return + value = trim(value) + if(!isnull(value) && value != "") matches = filter_fancy_list(matches, value) - if(matches.len==0) + if(matches.len == 0) return var/chosen - if(matches.len==1) + if(matches.len == 1) chosen = matches[1] else - chosen = input("Select an atom type", "Spawn Atom", matches[1]) as null|anything in matches + chosen = input("Select a type", "Pick Type", matches[1]) as null|anything in matches if(!chosen) return chosen = matches[chosen] return chosen +/proc/make_types_fancy(var/list/types) + if(ispath(types)) + types = list(types) + . = list() + for(var/type in types) + var/typename = "[type]" + var/static/list/TYPES_SHORTCUTS = list( + /obj/effect/decal/cleanable = "CLEANABLE", + /obj/item/device/radio/headset = "HEADSET", + /obj/item/clothing/head/helmet/space = "SPESSHELMET", + /obj/item/weapon/book/manual = "MANUAL", + /obj/item/weapon/reagent_containers/food/drinks = "DRINK", //longest paths comes first + /obj/item/weapon/reagent_containers/food = "FOOD", + /obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS", + /obj/item/weapon = "WEAPON", + /obj/machinery/atmospherics = "ATMOS_MECH", + /obj/machinery/portable_atmospherics = "PORT_ATMOS", + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK", + /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", + /obj/item/organ = "ORGAN", + /obj/item = "ITEM", + /obj/machinery = "MACHINERY", + /obj/effect = "EFFECT", + /obj = "O", + /datum = "D", + /turf/simulated/floor = "FLOOR", + /turf/simulated/wall = "WALL", + /turf = "T", + /mob/living/carbon = "CARBON", + /mob/living/simple_animal = "SIMPLE", + /mob/living = "LIVING", + /mob = "M" + ) + for(var/tn in TYPES_SHORTCUTS) + if(copytext(typename, 1, length("[tn]/") + 1) == "[tn]/") + typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) + break + .[typename] = type -var/list/TYPES_SHORTCUTS = list( - /obj/effect/decal/cleanable = "CLEANABLE", - /obj/item/device/radio/headset = "HEADSET", - /obj/item/clothing/head/helmet/space = "SPESSHELMET", - /obj/item/weapon/book/manual = "MANUAL", - /obj/item/weapon/reagent_containers/food/drinks = "DRINK", //longest paths comes first - /obj/item/weapon/reagent_containers/food = "FOOD", - /obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS", - /obj/machinery/atmospherics = "ATMOS", - /obj/machinery/portable_atmospherics = "PORT_ATMOS", -// /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/missile_rack = "MECHA_MISSILE_RACK", - /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", -// /obj/item/organ/internal = "ORGAN_INT", -) -var/global/list/g_fancy_list_of_types = null -/proc/get_fancy_list_of_types() - if(isnull(g_fancy_list_of_types)) //init - var/list/temp = sortList(subtypesof(/atom) - typesof(/area) - /atom/movable) - g_fancy_list_of_types = new(temp.len) - for(var/type in temp) - var/typename = "[type]" - for(var/tn in TYPES_SHORTCUTS) - if(copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) - typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) - break - g_fancy_list_of_types[typename] = type - return g_fancy_list_of_types +/proc/get_fancy_list_of_atom_types() + var/static/list/pre_generated_list + if(!pre_generated_list) //init + pre_generated_list = make_types_fancy(typesof(/atom)) + return pre_generated_list + + +/proc/get_fancy_list_of_datum_types() + var/static/list/pre_generated_list + if(!pre_generated_list) //init + pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom))) + return pre_generated_list + /proc/filter_fancy_list(list/L, filter as text) var/list/matches = new diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 9b3d413e94d..e97de99feff 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -1,441 +1,517 @@ // reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0) /datum - var/var_edited = 0 //Warrenty void if seal is broken + var/var_edited = FALSE //Warranty void if seal is broken -/datum/proc/on_varedit(modified_var) //called whenever a var is edited - var_edited = 1 +/datum/proc/can_vv_get(var_name) + return TRUE + +/client/proc/can_vv_get(var_name) + return TRUE + +/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited + switch(var_name) + if("vars") + return FALSE + if("var_edited") + return FALSE + var_edited = TRUE + vars[var_name] = var_value + +/client/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited + switch(var_name) + if("vars") + return FALSE + if("var_edited") + return FALSE + var_edited = TRUE + vars[var_name] = var_value + +/datum/proc/vv_get_var(var_name) + switch(var_name) + if("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + +/client/proc/vv_get_var(var_name) + switch(var_name) + if("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + + +//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down +//add seperaters by doing . += "---" +/datum/proc/vv_get_dropdown() + . = list() + . += "---" + .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" + .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" + .["Jump to Object"] = "?_src_=vars;jump_to=[UID()]" + .["Delete"] = "?_src_=vars;delete=[UID()]" + . += "---" + +/client/proc/vv_get_dropdown() + . = list() + . += "---" + .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" + .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" + .["Delete"] = "?_src_=vars;delete=[UID()]" + . += "---" /client/proc/debug_variables(datum/D in world) set category = "Debug" set name = "View Variables" - //set src in world + var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. if(!is_admin(usr)) to_chat(usr, "You need to be an administrator to access this.") return + if(!D) + return + + + var/islist = islist(D) + var/isclient = isclient(D) + if(!islist && !isclient && !istype(D)) + return var/title = "" - var/list/body = list() + var/icon/sprite + var/hash + var/refid - if(!D) return - if(istype(D, /atom)) - var/atom/A = D - title = "[A.name] (\ref[A]) = [A.type]" - - #ifdef VARSICON - if(A.icon) - body += debug_variable("icon", new/icon(A.icon, A.icon_state, A.dir), 0) - #endif - - var/sprite - - if(istype(D,/atom)) - var/atom/AT = D - if(AT.icon && AT.icon_state) - sprite = 1 - - title = "[D] (\ref[D]) = [D.type]" - - body += {" "} - - body += "" - - body += "
" - - if(sprite) - body += "" - - body += "
[bicon(D, use_class=0)]" + if(!islist) + refid = "[D.UID()]" else - body += "
" + refid = "\ref[D]" - body += "
" + var/type = /list + if(!islist) + type = D.type - if(istype(D,/atom)) + if(isatom(D)) + var/atom/A = D + if(A.icon && A.icon_state) + sprite = new /icon(A.icon, A.icon_state) + hash = md5(A.icon) + hash = md5(hash + A.icon_state) + usr << browse_rsc(sprite, "vv[hash].png") + + + var/sprite_text + if(sprite) + sprite_text = "
" + + + var/list/atomsnowflake = list() + if(isatom(D)) var/atom/A = D if(isliving(A)) - body += "[D]" - if(A.dir) - body += "
<< [dir2text(A.dir)] >>" - var/mob/living/M = A - body += "
[M.ckey ? M.ckey : "No ckey"] / [M.real_name ? M.real_name : "No real name"]" - body += {" -
- BRUTE:[M.getBruteLoss()] - FIRE:[M.getFireLoss()] - TOXIN:[M.getToxLoss()] - OXY:[M.getOxyLoss()] - CLONE:[M.getCloneLoss()] - BRAIN:[M.getBrainLoss()] - - - + var/mob/living/L = A + atomsnowflake += "[L]" + if(L.dir) + atomsnowflake += "
<< [dir2text(L.dir)] >>" + atomsnowflake += {" +
[L.ckey ? L.ckey : "No ckey"] / [L.real_name ? L.real_name : "No real name"] +
+ BRUTE:[L.getBruteLoss()] + FIRE:[L.getFireLoss()] + TOXIN:[L.getToxLoss()] + OXY:[L.getOxyLoss()] + CLONE:[L.getCloneLoss()] + BRAIN:[L.getBrainLoss()] + STAMINA:[L.getStaminaLoss()] + "} else - body += "[D]" + atomsnowflake += "[A]" if(A.dir) - body += "
<< [dir2text(A.dir)] >>" + atomsnowflake += "
<< [dir2text(A.dir)] >>" else - body += "[D]" + atomsnowflake += "[D]" - body += "" - body += "
" - - var/formatted_type = text("[D.type]") + var/formatted_type = "[type]" if(length(formatted_type) > 25) var/middle_point = length(formatted_type) / 2 - var/splitpoint = findtext(formatted_type,"/",middle_point) + var/splitpoint = findtext(formatted_type, "/", middle_point) if(splitpoint) - formatted_type = "[copytext(formatted_type,1,splitpoint)]
[copytext(formatted_type,splitpoint)]" + formatted_type = "[copytext(formatted_type, 1, splitpoint)]
[copytext(formatted_type, splitpoint)]" else formatted_type = "Type too long" //No suitable splitpoint (/) found. - body += "
[formatted_type]" - if(src.holder && src.holder.marked_datum && src.holder.marked_datum == D) - body += "
Marked Object" + var/marked + if(holder.marked_datum && holder.marked_datum == D) + marked = "
Marked Object" - if(D.var_edited) - body += "
Var Edited" - if(istype(D, /atom)) + var/varedited_line = "" + if(isatom(D)) var/atom/A = D if(A.admin_spawned) - body += "
Admin Spawned" - - body += "
" - - body += "
Refresh" - - body += {"
-
" - - body += "

" - - body += "E - Edit, tries to determine the variable type by itself.
" - body += "C - Change, asks you for the var type first.
" - body += "M - Mass modify: changes this variable for all objects of this type.

" - - body += "
Search:

" - - body += "
    " var/list/names = list() - for(var/V in D.vars) - names += V + if(!islist) + for(var/V in D.vars) + names += V - names = sortList(names) - for(var/V in names) - body += debug_variable(V, D.vars[V], 0, D) + sleep(1) // Without a sleep here, VV sometimes disconnects clients - body += "
" - var/html = "" - if(title) - html += "[title]" - html += {""} - html += "" - html += body.Join("") + var/list/variable_html = list() + if(islist) + var/list/L = D + for(var/i in 1 to L.len) + var/key = L[i] + var/value + if(IS_NORMAL_LIST(L) && !isnum(key)) + value = L[key] + variable_html += debug_variable(i, value, 0, D) + else + names = sortList(names) + for(var/V in names) + if(D.can_vv_get(V)) + variable_html += D.vv_get_var(V) - html += {" + var/html = {" + + + [title] + + + + +
+ + + + + +
+ + + + +
+ [sprite_text] +
+ [atomsnowflake.Join()] +
+
+
+ [formatted_type] + [marked] + [varedited_line] +
+
+
+ Refresh +
+ +
+
+
+
+
+ + E - Edit, tries to determine the variable type by itself.
+ C - Change, asks you for the var type first.
+ M - Mass modify: changes this variable for all objects of this type.
+
+
+ + + + + +
+
+ Search: +
+
+ +
+
+
    + [variable_html.Join()] +
+ + "} - html += "" - - usr << browse(html, "window=variables[D.UID()];size=475x650") - - return - -/client/proc/debug_variable(name, value, level, var/datum/DA = null) - var/list/html = list() + usr << browse(html, "window=variables[refid];size=475x650") +#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) +/proc/debug_variable(name, value, level, var/datum/DA = null, sanitize = TRUE) + var/header if(DA) - html += "
  • (E) (C) (M) " + if(islist(DA)) + var/index = name + if(value) + name = DA[name] // name is really the index until this line + else + value = DA[name] + header = "
  • (E) (C) (-) " + else + header = "
  • (E) (C) (M) " else - html += "
  • " + header = "
  • " + var/item if(isnull(value)) - html += "[name] = null" + item = "[VV_HTML_ENCODE(name)] = null" else if(istext(value)) - html += "[name] = \"[value]\"" + item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" else if(isicon(value)) #ifdef VARSICON - html += "[name] = /icon ([value]) [bicon(value, use_class=0)]" + item = "[name] = /icon ([value]) [bicon(value, use_class=0)]" #else - html += "[name] = /icon ([value])" + item = "[name] = /icon ([value])" #endif - + else if(istype(value, /image)) var/image/I = value #ifdef VARSICON - html += "[name] \ref[value] = /image ([value]) [bicon(value, use_class=0)]" + item = "[name] \ref[value] = /image ([value]) [bicon(value, use_class=0)]" #else - html += "[name] \ref[value] = /image ([value])" - #endif - + item = "[name] \ref[value] = /image ([value])" + #endif + else if(isfile(value)) - html += "[name] = '[value]'" + item = "[VV_HTML_ENCODE(name)] = '[value]'" else if(istype(value, /datum)) var/datum/D = value - html += "[name] \ref[value] = [D.type]" + item = "[VV_HTML_ENCODE(name)] \ref[value] = [D.type]" else if(istype(value, /client)) var/client/C = value - html += "[name] \ref[value] = [C] [C.type]" + item = "[VV_HTML_ENCODE(name)] \ref[value] = [C] [C.type]" // - else if(istype(value, /list)) + else if(islist(value)) var/list/L = value - html += "[name] = /list ([L.len])" + var/list/items = list() - if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > 500)) - // not sure if this is completely right... - if(0) //(L.vars.len > 0) - html += "
      " - html += "
    " - else - html += "
      " - var/index = 1 - for(var/entry in L) - if(istext(entry)) - html += debug_variable(entry, L[entry], level + 1) - else - html += debug_variable(index, L[index], level + 1) - index++ - html += "
    " + if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > (IS_NORMAL_LIST(L) ? 50 : 150))) + for(var/i in 1 to L.len) + var/key = L[i] + var/val + if(IS_NORMAL_LIST(L) && !isnum(key)) + val = L[key] + if(!val) + val = key + key = i + + items += debug_variable(key, val, level + 1, sanitize = sanitize) + + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " + + else + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" else - html += "[name] = [value]" - /* - // Bitfield stuff - if(round(value)==value) // Require integers. - var/idx=0 - var/bit=0 - var/bv=0 - html += "
    " - for(var/block=0;block<8;block++) - html += " " - for(var/i=0;i<4;i++) - idx=(block*4)+i - to_chat(bit=1, idx) - bv=value & bit - html += "[bv?1:0]" - html += "" - html += "
    " - */ - html += "
  • " + item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" - return html.Join("") + return "[header][item]" + +#undef VV_HTML_ENCODE /client/proc/view_var_Topic(href, href_list, hsrc) //This should all be moved over to datum/admins/Topic() or something ~Carn if(!check_rights(R_ADMIN|R_MOD)) return + if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings + return + // Correct and warn about any VV topic links that aren't using UIDs for(var/paramname in href_list) if(findtext(href_list[paramname], "]_")) @@ -516,6 +592,7 @@ body cmd_mass_modify_object_variables(A, href_list["varnamemass"]) + else if(href_list["mob_player_panel"]) if(!check_rights(R_ADMIN|R_MOD)) return @@ -1099,6 +1176,7 @@ body if("oxygen")L.adjustOxyLoss(amount) if("brain") L.adjustBrainLoss(amount) if("clone") L.adjustCloneLoss(amount) + if("stamina") L.adjustStaminaLoss(amount) else to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") return @@ -1110,8 +1188,123 @@ body if(href_list["datumrefresh"]) var/datum/DAT = locateUID(href_list["datumrefresh"]) - if(!istype(DAT, /datum)) + if(!istype(DAT, /datum) && !isclient(DAT)) return src.debug_variables(DAT) return + +/client/proc/view_var_Topic_list(href, href_list, hsrc) + if(href_list["VarsList"]) + debug_variables(locate(href_list["VarsList"])) + return TRUE + + if(href_list["listedit"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listedit"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) + return TRUE + + if(href_list["listchange"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listchange"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) + return TRUE + + if(href_list["listremove"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listremove"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + var/variable = L[index] + var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") + if(prompt != "Yes") + return + L.Cut(index, index+1) + log_to_dd("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") + log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") + message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") + return TRUE + + if(href_list["listadd"]) + var/list/L = locate(href_list["listadd"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + mod_list_add(L, null, "list", "contents") + return TRUE + + if(href_list["listdupes"]) + var/list/L = locate(href_list["listdupes"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + uniqueList_inplace(L) + log_to_dd("### ListVarEdit by [src]: /list contents: CLEAR DUPES") + log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") + return TRUE + + if(href_list["listnulls"]) + var/list/L = locate(href_list["listnulls"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + listclearnulls(L) + log_to_dd("### ListVarEdit by [src]: /list contents: CLEAR NULLS") + log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") + return TRUE + + if(href_list["listlen"]) + var/list/L = locate(href_list["listlen"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + var/value = vv_get_value(VV_NUM) + if(value["class"] != VV_NUM) + return TRUE + + L.len = value["value"] + log_to_dd("### ListVarEdit by [src]: /list len: [L.len]") + log_admin("[key_name(src)] modified list's len: [L.len]") + message_admins("[key_name_admin(src)] modified list's len: [L.len]") + return TRUE + + if(href_list["listshuffle"]) + var/list/L = locate(href_list["listshuffle"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + shuffle_inplace(L) + log_to_dd("### ListVarEdit by [src]: /list contents: SHUFFLE") + log_admin("[key_name(src)] modified list's contents: SHUFFLE") + message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") + return TRUE + + if(href_list["listrefresh"]) + debug_variables(locate(href_list["listrefresh"])) + return TRUE \ No newline at end of file diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 8c12e452d1d..22cadfaa659 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -616,7 +616,20 @@ var/list/blood_splatter_icons = list() /atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) return -/atom/on_varedit(modified_var) +/atom/vv_edit_var(var_name, var_value) if(!Debug2) admin_spawned = TRUE - ..() + . = ..() + switch(var_name) + if("light_power", "light_range", "light_color") + update_light() + + +/atom/vv_get_dropdown() + . = ..() + var/turf/curturf = get_turf(src) + if(curturf) + .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" + .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" + .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" + .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" \ No newline at end of file diff --git a/code/game/objects/items/weapons/power_cells.dm b/code/game/objects/items/weapons/power_cells.dm index d291329f354..8996f97fbea 100644 --- a/code/game/objects/items/weapons/power_cells.dm +++ b/code/game/objects/items/weapons/power_cells.dm @@ -28,13 +28,14 @@ processing_objects.Remove(src) return ..() -/obj/item/weapon/stock_parts/cell/on_varedit(modified_var) - if(modified_var == "self_recharge") - if(self_recharge) - processing_objects.Add(src) - else - processing_objects.Remove(src) - ..() +/obj/item/weapon/stock_parts/cell/vv_edit_var(var_name, var_value) + switch(var_name) + if("self_recharge") + if(var_value) + processing_objects.Add(src) + else + processing_objects.Remove(src) + . = ..() /obj/item/weapon/stock_parts/cell/suicide_act(mob/user) to_chat(viewers(user), "[user] is licking the electrodes of the [src.name]! It looks like \he's trying to commit suicide.") diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 197b742f307..60f9538fde0 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -310,4 +310,8 @@ a { Item.fire_act() //Set them on fire, too /obj/proc/on_mob_move(dir, mob/user) - return \ No newline at end of file + return + +/obj/vv_get_dropdown() + . = ..() + .["Delete all of type"] = "?_src_=vars;delall=[UID()]" diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index de22d5ea098..d2d0a6c7ab5 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -100,3 +100,6 @@ you will have to do something like if(client.holder.rights & R_ADMIN) yourself. return 0 return 1 return 0 + +/datum/admins/vv_edit_var(var_name, var_value) + return FALSE // no admin abuse \ No newline at end of file diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/verbs/massmodvar.dm index c7b2d6d537e..084057498bb 100644 --- a/code/modules/admin/verbs/massmodvar.dm +++ b/code/modules/admin/verbs/massmodvar.dm @@ -22,360 +22,251 @@ src.massmodify_variables(A, var_name, method) feedback_add_details("admin_verb","MEV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/massmodify_variables(var/atom/O, var/var_name = "", var/method = 0) - if(!check_rights(R_VAREDIT)) return - - var/list/locked = list("vars", "key", "ckey", "client") - - for(var/p in forbidden_varedit_object_types) - if( istype(O,p) ) - to_chat(usr, "It is forbidden to edit this object's variables.") - return - - var/list/names = list() - for(var/V in O.vars) - names += V - - names = sortList(names) +/client/proc/massmodify_variables(datum/O, var_name = "", method = 0) + if(!check_rights(R_VAREDIT)) + return + if(!istype(O)) + return var/variable = "" - if(!var_name) - variable = input("Which var?","Var") as null|anything in names + var/list/names = list() + for(var/V in O.vars) + names += V + + names = sortList(names) + + variable = input("Which var?", "Var") as null|anything in names else variable = var_name - if(!variable) return + if(!variable || !O.can_vv_get(variable)) + return var/default var/var_value = O.vars[variable] - var/dir - if(variable == "holder" || (variable in locked)) - if(!check_rights(R_DEBUG)) return + if(variable in VVckey_edit) + to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.") + return + if(variable in VVlocked) + if(!check_rights(R_DEBUG)) + return + if(variable in VVicon_edit_lock) + if(!check_rights(R_EVENT | R_DEBUG)) + return + if(variable in VVpixelmovement) + if(!check_rights(R_DEBUG)) + return + var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if(prompt != "Continue") + return - if(isnull(var_value)) - to_chat(usr, "Unable to determine variable type.") - - else if(isnum(var_value)) - to_chat(usr, "Variable appears to be NUM.") - default = "num" - dir = 1 - - else if(istext(var_value)) - to_chat(usr, "Variable appears to be TEXT.") - default = "text" - - else if(isloc(var_value)) - to_chat(usr, "Variable appears to be REFERENCE.") - default = "reference" - - else if(isicon(var_value)) - to_chat(usr, "Variable appears to be ICON.") - var_value = "[bicon(var_value)]" - default = "icon" - - else if(istype(var_value,/atom) || istype(var_value,/datum)) - to_chat(usr, "Variable appears to be TYPE.") - default = "type" - - else if(istype(var_value,/list)) - to_chat(usr, "Variable appears to be LIST.") - default = "list" - - else if(istype(var_value,/client)) - to_chat(usr, "Variable appears to be CLIENT.") - default = "cancel" + default = vv_get_class(var_value) + if(isnull(default)) + to_chat(src, "Unable to determine variable type.") else - to_chat(usr, "Variable appears to be FILE.") - default = "file" + to_chat(src, "Variable appears to be [uppertext(default)].") - to_chat(usr, "Variable contains: [var_value]") - if(dir) - switch(var_value) - if(1) - dir = "NORTH" - if(2) - dir = "SOUTH" - if(4) - dir = "EAST" - if(8) - dir = "WEST" - if(5) - dir = "NORTHEAST" - if(6) - dir = "SOUTHEAST" - if(9) - dir = "NORTHWEST" - if(10) - dir = "SOUTHWEST" - else - dir = null - if(dir) - to_chat(usr, "If a direction, direction is: [dir]") + to_chat(src, "Variable contains: [var_value]") - var/class = input("What kind of variable?","Variable Type",default) as null|anything in list("text", - "num","type","icon","file","edit referenced object","restore to default") + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" - if(!class) + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + var/value = vv_get_value(default_class = default) + var/new_value = value["value"] + var/class = value["class"] + + if(!class || !new_value == null && class != VV_NULL) return - var/original_name + if(class == VV_MESSAGE) + class = VV_TEXT - if(!istype(O, /atom)) - original_name = "\ref[O] ([O])" - else - original_name = O:name + if(value["type"]) + class = VV_NEW_TYPE + + var/original_name = "[O]" + + var/rejected = 0 + var/accepted = 0 switch(class) + if(VV_RESTORE_DEFAULT) + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK - if("restore to default") - O.vars[variable] = initial(O.vars[variable]) - if(method) - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if( istype(M , O.type) ) - M.vars[variable] = O.vars[variable] - M.on_varedit(variable) + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, new_value) + var/pre_processing = new_value + var/unique + if(varsvars && varsvars.len) + unique = alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", "Unique", "Same") + if(unique == "Unique") + unique = TRUE + else + unique = FALSE + for(var/V in varsvars) + new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]") - else if(istype(O, /obj)) - for(var/obj/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - A.on_varedit(variable) + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(unique) + new_value = pre_processing + for(var/V in varsvars) + new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]") - else if(istype(O, /turf)) - for(var/turf/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - A.on_varedit(variable) + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK + if(VV_NEW_TYPE) + var/many = alert(src, "Create only one [value["type"]] and assign each or a new one for each thing", "How Many", "One", "Many", "Cancel") + if(many == "Cancel") + return + if(many == "Many") + many = TRUE else - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if(M.type == O.type) - M.vars[variable] = O.vars[variable] - M.on_varedit(variable) + many = FALSE - else if(istype(O, /obj)) - for(var/obj/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - A.on_varedit(variable) + var/type = value["type"] + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(many && !new_value) + new_value = new type() - else if(istype(O, /turf)) - for(var/turf/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - A.on_varedit(variable) + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + new_value = null + CHECK_TICK - if("edit referenced object") - return .(O.vars[variable]) + else + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK - if("text") - var/new_value = input("Enter new text:","Text",O.vars[variable]) as message|null - if(new_value == null) return - O.vars[variable] = new_value - if(method) - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if( istype(M , O.type) ) - M.vars[variable] = O.vars[variable] + var/count = rejected+accepted + if(!count) + to_chat(src, "No objects found") + return + if(!accepted) + to_chat(src, "Every object rejected your edit") + return + if(rejected) + to_chat(src, "[rejected] out of [count] objects rejected your edit") - else if(istype(O, /obj)) - for(var/obj/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] + log_to_dd("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])") + log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") + message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") - else if(istype(O, /turf)) - for(var/turf/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - else - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if(M.type == O.type) - M.vars[variable] = O.vars[variable] +/proc/get_all_of_type(var/T, subtypes = TRUE) + var/list/typecache = list() + typecache[T] = 1 + if(subtypes) + typecache = typecacheof(typecache) + . = list() + if(ispath(T, /mob)) + for(var/mob/thing in mob_list) + if(typecache[thing.type]) + . += thing + CHECK_TICK - else if(istype(O, /obj)) - for(var/obj/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] + else if(ispath(T, /obj/machinery/door)) + for(var/obj/machinery/door/thing in airlocks) + if(typecache[thing.type]) + . += thing + CHECK_TICK - else if(istype(O, /turf)) - for(var/turf/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] + else if(ispath(T, /obj/machinery)) + for(var/obj/machinery/thing in machines) + if(typecache[thing.type]) + . += thing + CHECK_TICK - if("num") - var/new_value = input("Enter new number:","Num",\ - O.vars[variable]) as num|null - if(new_value == null) return + else if(ispath(T, /obj)) + for(var/obj/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK - if(variable=="light_range") - O.set_light(new_value) - else - O.vars[variable] = new_value + else if(ispath(T, /atom/movable)) + for(var/atom/movable/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK - if(method) - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if( istype(M , O.type) ) - if(variable=="light_range") - M.set_light(new_value) - else - M.vars[variable] = O.vars[variable] + else if(ispath(T, /turf)) + for(var/turf/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK - else if(istype(O, /obj)) - for(var/obj/A in world) - if( istype(A , O.type) ) - if(variable=="light_range") - A.set_light(new_value) - else - A.vars[variable] = O.vars[variable] + else if(ispath(T, /atom)) + for(var/atom/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK - else if(istype(O, /turf)) - for(var/turf/A in world) - if( istype(A , O.type) ) - if(variable=="light_range") - A.set_light(new_value) - else - A.vars[variable] = O.vars[variable] + else if(ispath(T, /client)) + for(var/client/thing in clients) + if(typecache[thing.type]) + . += thing + CHECK_TICK - else - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if(M.type == O.type) - if(variable=="light_range") - M.set_light(new_value) - else - M.vars[variable] = O.vars[variable] + else if(ispath(T, /datum)) + for(var/datum/thing) + if(typecache[thing.type]) + . += thing + CHECK_TICK - else if(istype(O, /obj)) - for(var/obj/A in world) - if(A.type == O.type) - if(variable=="light_range") - A.set_light(new_value) - else - A.vars[variable] = O.vars[variable] - - else if(istype(O, /turf)) - for(var/turf/A in world) - if(A.type == O.type) - if(variable=="light_range") - A.set_light(new_value) - else - A.vars[variable] = O.vars[variable] - - if("type") - var/new_value - new_value = input("Enter type:","Type",O.vars[variable]) as null|anything in typesof(/obj,/mob,/area,/turf) - if(new_value == null) return - O.vars[variable] = new_value - if(method) - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if( istype(M , O.type) ) - M.vars[variable] = O.vars[variable] - - else if(istype(O, /obj)) - for(var/obj/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - - else if(istype(O, /turf)) - for(var/turf/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - else - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if(M.type == O.type) - M.vars[variable] = O.vars[variable] - - else if(istype(O, /obj)) - for(var/obj/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - - else if(istype(O, /turf)) - for(var/turf/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - - if("file") - var/new_value = input("Pick file:","File",O.vars[variable]) as null|file - if(new_value == null) return - O.vars[variable] = new_value - - if(method) - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if( istype(M , O.type) ) - M.vars[variable] = O.vars[variable] - - else if(istype(O.type, /obj)) - for(var/obj/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - - else if(istype(O.type, /turf)) - for(var/turf/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - else - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if(M.type == O.type) - M.vars[variable] = O.vars[variable] - - else if(istype(O.type, /obj)) - for(var/obj/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - - else if(istype(O.type, /turf)) - for(var/turf/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - - if("icon") - var/new_value = input("Pick icon:","Icon",O.vars[variable]) as null|icon - if(new_value == null) return - O.vars[variable] = new_value - if(method) - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if( istype(M , O.type) ) - M.vars[variable] = O.vars[variable] - - else if(istype(O, /obj)) - for(var/obj/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - - else if(istype(O, /turf)) - for(var/turf/A in world) - if( istype(A , O.type) ) - A.vars[variable] = O.vars[variable] - - else - if(istype(O, /mob)) - for(var/mob/M in mob_list) - if(M.type == O.type) - M.vars[variable] = O.vars[variable] - - else if(istype(O, /obj)) - for(var/obj/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - - else if(istype(O, /turf)) - for(var/turf/A in world) - if(A.type == O.type) - A.vars[variable] = O.vars[variable] - - log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]]") - message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]]", 1) \ No newline at end of file + else + for(var/datum/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK \ No newline at end of file diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index cb50d2e6391..b94e319a848 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -4,14 +4,10 @@ var/list/forbidden_varedit_object_types = list( /datum/feedback_variable //Prevents people messing with feedback gathering ) -/* -/client/proc/cmd_modify_object_variables(obj/O as obj|mob|turf|area in world) - set category = "Debug" - set name = "Edit Variables" - set desc="(target) Edit a target item's variables" - src.modify_variables(O) - feedback_add_details("admin_verb","EDITV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -*/ +var/list/VVlocked = list("vars", "var_edited", "client", "firemut", "ishulk", "telekinesis", "xray", "ka", "virus", "viruses", "cuffed", "last_eaten", "unlock_content") // R_DEBUG +var/list/VVicon_edit_lock = list("icon", "icon_state", "overlays", "underlays", "resize") // R_EVENT | R_DEBUG +var/list/VVckey_edit = list("key", "ckey") // R_EVENT | R_DEBUG +var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width", "bound_x", "bound_y") // R_DEBUG + warning /proc/datum_is_forbidden(type) for(var/p in forbidden_varedit_object_types) @@ -20,275 +16,511 @@ var/list/forbidden_varedit_object_types = list( return FALSE return TRUE +/client/proc/vv_get_class(var/var_value) + if(isnull(var_value)) + . = VV_NULL -/client/proc/cmd_modify_ticker_variables() - set category = "Debug" - set name = "Edit Ticker Variables" + else if(isnum(var_value)) + . = VV_NUM - if(ticker == null) - to_chat(src, "Game hasn't started yet.") + else if(istext(var_value)) + if(findtext(var_value, "\n")) + . = VV_MESSAGE + else + . = VV_TEXT + + else if(isicon(var_value)) + . = VV_ICON + + else if(ismob(var_value)) + . = VV_MOB_REFERENCE + + else if(isloc(var_value)) + . = VV_ATOM_REFERENCE + + else if(istype(var_value, /matrix)) + . = VV_MATRIX + + else if(istype(var_value,/client)) + . = VV_CLIENT + + else if(istype(var_value, /datum)) + . = VV_DATUM_REFERENCE + + else if(ispath(var_value)) + if(ispath(var_value, /atom)) + . = VV_ATOM_TYPE + else if(ispath(var_value, /datum)) + . = VV_DATUM_TYPE + else + . = VV_TYPE + + else if(islist(var_value)) + . = VV_LIST + + else if(isfile(var_value)) + . = VV_FILE else - src.modify_variables(ticker) - feedback_add_details("admin_verb","ETV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + . = VV_NULL -/client/proc/mod_list_add_ass() //haha +/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes) + . = list("class" = class, "value" = null) + if(!class) + if(!classes) + classes = list( + VV_NUM, + VV_TEXT, + VV_MESSAGE, + VV_ICON, + VV_ATOM_REFERENCE, + VV_DATUM_REFERENCE, + VV_MOB_REFERENCE, + VV_CLIENT, + VV_ATOM_TYPE, + VV_DATUM_TYPE, + VV_TYPE, + VV_MATRIX, + VV_FILE, + VV_NEW_ATOM, + VV_NEW_DATUM, + VV_NEW_TYPE, + VV_NEW_LIST, + VV_NULL, + VV_RESTORE_DEFAULT + ) - var/class = "text" - var/list/allowed_types = list("text", "num","type", "type from text","reference","mob reference", "icon","file","list","edit referenced object","restore to default") - if(src.holder && src.holder.marked_datum) - allowed_types += "marked datum ([holder.marked_datum.type])" - class = input("What kind of variable?","Variable Type") as null|anything in allowed_types + if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) + classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" + if(restricted_classes) + classes -= restricted_classes + + if(extra_classes) + classes += extra_classes + + .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes + if(holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") + .["class"] = VV_MARKED_DATUM + + + switch(.["class"]) + if(VV_TEXT) + .["value"] = input("Enter new text:", "Text", current_value) as null|text + if(.["value"] == null) + .["class"] = null + return + if(VV_MESSAGE) + .["value"] = input("Enter new text:", "Text", current_value) as null|message + if(.["value"] == null) + .["class"] = null + return + + + if(VV_NUM) + .["value"] = input("Enter new number:", "Num", current_value) as null|num + if(.["value"] == null) + .["class"] = null + return + + + if(VV_ATOM_TYPE) + .["value"] = pick_closest_path(FALSE) + if(.["value"] == null) + .["class"] = null + return + + if(VV_DATUM_TYPE) + .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(.["value"] == null) + .["class"] = null + return + + if(VV_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["value"] = type + + if(VV_MATRIX) + .["value"] = text2matrix(input("Enter a, b, c, d, e, and f, seperated by a space.", "Matrix", "1 0 0 0 1 0") as null|text) + if(.["value"] == null) + .["class"] = null + return + + + if(VV_ATOM_REFERENCE) + var/type = pick_closest_path(FALSE) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_DATUM_REFERENCE) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_MOB_REFERENCE) + var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + + + if(VV_CLIENT) + .["value"] = input("Select reference:", "Reference", current_value) as null|anything in clients + if(.["value"] == null) + .["class"] = null + return + + + if(VV_FILE) + .["value"] = input("Pick file:", "File") as null|file + if(.["value"] == null) + .["class"] = null + return + + + if(VV_ICON) + .["value"] = input("Pick icon:", "Icon") as null|icon + if(.["value"] == null) + .["class"] = null + return + + + if(VV_MARKED_DATUM) + .["value"] = holder.marked_datum + if(.["value"] == null) + .["class"] = null + return + + if(VV_NEW_ATOM) + var/type = pick_closest_path(FALSE) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + if(VV_NEW_DATUM) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + if(VV_NEW_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + + if(VV_NEW_LIST) + .["value"] = list() + .["type"] = /list + +/client/proc/vv_parse_text(O, new_var) + if(O && findtext(new_var, "\[")) + var/process_vars = alert(usr, "\[] detected in string, process as variables?", "Process Variables?", "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(var/type) + if(!ispath(type)) + return + var/list/subtypes = subtypesof(type) + if(!subtypes || !subtypes.len) + return FALSE + if(subtypes && subtypes.len) + switch(alert("Strict object type detection?", "Type detection", "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, lentext(types[type])+1) + var/shorttype = copytext("[D.type]", lentext("[type]")+1) + if(lentext(shorttype) > lentext(fancytype)) + shorttype = fancytype + if(!lentext(shorttype)) + shorttype = "/" + + .["[D]([shorttype])\ref[D]#[i]"] = D + +/client/proc/mod_list_add_ass(atom/O) //haha + 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(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])") - class = "marked datum" - - var/var_value = null - - switch(class) - - if("text") - var_value = input("Enter new text:","Text") as null|message - - if("num") - var_value = input("Enter new number:","Num") as null|num - - if("type") - var_value = input("Enter type:","Type") as null|anything in typesof(/obj,/mob,/area,/turf) - - if("type from text") - var/type_text = input("Enter type:", "Type") as null|message - var_value = text2path(type_text) - if(!var_value) - to_chat(src, "[type_text] is not a valid path!") - - if("reference") - var_value = input("Select reference:","Reference") as null|mob|obj|turf|area in world - - if("mob reference") - var_value = input("Select reference:","Reference") as null|mob in world - - if("file") - var_value = input("Pick file:","File") as null|file - - if("icon") - var_value = input("Pick icon:","Icon") as null|icon - - if("marked datum") - var_value = holder.marked_datum - - if(!var_value) return + 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(var/list/L) - - var/class = "text" - var/list/allowed_types = list("text", "num","type", "type from text","reference","mob reference", "icon","file","list","edit referenced object","restore to default") - if(src.holder && src.holder.marked_datum) - allowed_types += "marked datum ([holder.marked_datum.type])" - class = input("What kind of variable?","Variable Type") as null|anything in allowed_types - +/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(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])") - class = "marked datum" + 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]]") - var/var_value = null + if(O) + L = L.Copy() - switch(class) + L += var_value - if("text") - var_value = input("Enter new text:","Text") as message|null - - if("num") - var_value = input("Enter new number:","Num") as num - - if("type") - var_value = input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) - - if("type from text") - var/type_text = input("Enter type:", "Type") as null|message - var_value = text2path(type_text) - if(!var_value) - to_chat(src, "[type_text] is not a valid path!") - - if("reference") - var_value = input("Select reference:","Reference") as mob|obj|turf|area in world - - if("mob reference") - var_value = input("Select reference:","Reference") as mob in world - - if("file") - var_value = input("Pick file:","File") as file - - if("icon") - var_value = input("Pick icon:","Icon") as icon - - if("marked datum") - var_value = holder.marked_datum - - if(!var_value) return - - switch(alert("Would you like to associate a var with the list entry?",,"Yes","No")) + switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) if("Yes") - L += var_value - L[var_value] = mod_list_add_ass() //haha - if("No") - L += var_value + 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.") + return + log_to_dd("### 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(var/list/L) - if(!check_rights(R_VAREDIT)) return - - if(!istype(L,/list)) +/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.") + return + if(L.len > 1000) var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort") if(confirm != "Continue") return - var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "viruses", "cuffed", "ka", "last_eaten", "urine", "poo", "icon", "icon_state", "step_x", "step_y") - var/assoc = 0 - if(L.len > 0) - var/a = L[1] - if(istext(a) && L[a] != null) - assoc = 1 //This is pretty weak test but i can't think of anything else - to_chat(usr, "List appears to be associative.") - var/list/names = null - if(!assoc) - names = sortList(L) + var/list/names = list() + for(var/i in 1 to L.len) + var/key = L[i] + var/value + if(IS_NORMAL_LIST(L) && !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() + listclearnulls(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_to_dd("### 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 = uniqueList(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_to_dd("### 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.") + return + log_to_dd("### 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/variable var/assoc_key - if(assoc) - variable = input("Which var?","Var") as null|anything in L + "(ADD VAR)" - else - variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" - - if(variable == "(ADD VAR)") - mod_list_add(L) + if(index == null) return - - if(assoc) - assoc_key = variable - variable = L[assoc_key] - - if(!assoc && !variable || assoc && !assoc_key) + var/assoc = 0 + var/prompt = alert(src, "Do you want to edit the key or it's assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") + if(prompt == "Cancel") return - + if(prompt == "Assigned Value") + assoc = 1 + assoc_key = L[index] var/default + var/variable + if(assoc) + variable = L[assoc_key] + else + variable = L[index] - var/dir + default = vv_get_class(variable) - if(variable in locked) - if(!check_rights(R_DEBUG)) return + to_chat(src, "Variable appears to be [uppertext(default)].") - default = variable_to_type(variable) + to_chat(src, "Variable contains: [L[index]]") - to_chat(usr, "Variable contains: [variable]") - if(default == "num") - dir = dir2text(variable) + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" - if(dir) - to_chat(usr, "If a direction, direction is: [dir]") - - var/class = "text" - var/list/allowed_types = list("text", "num","type", "type from text", "reference","mob reference", "icon","file","list","edit referenced object","restore to default","DELETE FROM LIST") - if(src.holder && src.holder.marked_datum) - allowed_types += "marked datum ([holder.marked_datum.type])" - - class = input("What kind of variable?","Variable Type",default) as null|anything in allowed_types + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + var/original_var + if(assoc) + original_var = L[assoc_key] + else + original_var = L[index] + 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(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])") - class = "marked datum" + 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("list") - mod_list(variable) - - if("restore to default") - if(assoc) - L[assoc_key] = initial(variable) - else - L[L.Find(variable)]=initial(variable) - - if("edit referenced object") - modify_variables(variable) + if(VV_LIST) + mod_list(variable, O, original_name, objectvar) if("DELETE FROM LIST") - L -= variable + L.Cut(index, index+1) + if(O) + if(O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_to_dd("### 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("text") - if(assoc) - L[assoc_key] = input("Enter new text:","Text") as text - else - L[L.Find(variable)] = input("Enter new text:","Text") as text - - if("num") - if(assoc) - L[assoc_key] = input("Enter new number:","Num") as num - else - L[L.Find(variable)] = input("Enter new number:","Num") as num - - if("type") - if(assoc) - L[assoc_key] = input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) - else - L[L.Find(variable)] = input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) - - if("reference") - if(assoc) - L[assoc_key] = input("Select reference:","Reference") as mob|obj|turf|area in world - else - L[L.Find(variable)] = input("Select reference:","Reference") as mob|obj|turf|area in world - - if("mob reference") - if(assoc) - L[assoc_key] = input("Select reference:","Reference") as mob in world - else - L[L.Find(variable)] = input("Select reference:","Reference") as mob in world - - if("file") - if(assoc) - L[assoc_key] = input("Pick file:","File") as file - else - L[L.Find(variable)] = input("Pick file:","File") as file - - if("icon") - if(assoc) - L[assoc_key] = input("Pick icon:","Icon") as icon - else - L[L.Find(variable)] = input("Pick icon:","Icon") as icon - - if("marked datum") - if(assoc) - L[assoc_key] = holder.marked_datum - else - L[L.Find(variable)] = holder.marked_datum + 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]]") -/client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0) - if(!check_rights(R_VAREDIT)) return - - var/list/locked = list("vars", "key", "ckey", "client", "firemut", "ishulk", "telekinesis", "xray", "virus", "cuffed", "ka", "last_eaten", "icon", "icon_state") - - for(var/p in forbidden_varedit_object_types) - if( istype(O,p) ) - to_chat(usr, "It is forbidden to edit this object's variables.") + if(assoc) + L[assoc_key] = new_var + else + L[index] = new_var + if(O) + if(O.vv_edit_var(objectvar, L) == FALSE) + to_chat(src, "Your edit was rejected by the object.") return + log_to_dd("### 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]") - if(istype(O, /client) && (param_var_name == "ckey" || param_var_name == "key")) - to_chat(usr, "You cannot edit ckeys on client objects.") +/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) + if(!check_rights(R_VAREDIT)) return var/class @@ -297,24 +529,11 @@ var/list/forbidden_varedit_object_types = list( 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 atom ([O])") + to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") return - - if(param_var_name == "holder" || (param_var_name in locked)) - if(!check_rights(R_DEBUG)) return - variable = param_var_name - var_value = O.vars[variable] - - if(autodetect_class) - class = variable_to_type(var_value) - if(!class) - autodetect_class = null - else if(class == "num") - dir = 1 else - var/list/names = list() for(var/V in O.vars) names += V @@ -322,187 +541,92 @@ var/list/forbidden_varedit_object_types = list( names = sortList(names) variable = input("Which var?","Var") as null|anything in names - if(!variable) return - var_value = O.vars[variable] - - if(variable == "holder" || (variable in locked)) - if(!check_rights(R_DEBUG)) return - - if(!autodetect_class) - - var/dir - var/default - default = variable_to_type(var_value) - if(default == "num") - dir = 1 - else if(default == "icon") - var_value = "[bicon(var_value)]" - - to_chat(usr, "Variable contains: [var_value]") - if(dir) - dir = dir2text(var_value) - if(dir) - to_chat(usr, "If a direction, direction is: [dir]") - - var/list/allowed_types = list("text", "num","type","reference","mob reference", "path", "matrix", "icon","file","list","edit referenced object","restore to default") - if(src.holder && src.holder.marked_datum) - allowed_types += "marked datum ([holder.marked_datum.type])" - - class = input("What kind of variable?","Variable Type",default) as null|anything in allowed_types - - if(!class) + if(!variable) return - var/original_name + if(!O.can_vv_get(variable)) + return - if(!istype(O, /atom)) - original_name = "\ref[O] ([O])" + var_value = O.vars[variable] + + if(variable in VVlocked) + if(!check_rights(R_DEBUG)) + return + if(variable in VVckey_edit) + if(!check_rights(R_EVENT | R_DEBUG)) + return + if(variable in VVicon_edit_lock) + if(!check_rights(R_EVENT | R_DEBUG)) + return + if(variable in VVpixelmovement) + if(!check_rights(R_DEBUG)) + return + var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if(prompt != "Continue") + return + + + var/default = vv_get_class(var_value) + + if(isnull(default)) + to_chat(src, "Unable to determine variable type.") else - original_name = O:name + to_chat(src, "Variable appears to be [uppertext(default)].") - if(holder.marked_datum && class == "marked datum ([holder.marked_datum.type])") - class = "marked datum" + to_chat(src, "Variable contains: [var_value]") + + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + 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)) + class = value["class"] + + if(!class) + return + var/var_new = value["value"] + + if(class == VV_MESSAGE) + class = VV_TEXT + + var/original_name = "[O]" - var/var_as_text = null switch(class) + if(VV_LIST) + if(!islist(var_value)) + mod_list(list(), O, original_name, variable) - if("list") - mod_list(O.vars[variable]) + mod_list(var_value, O, original_name, variable) return - if("restore to default") - O.vars[variable] = initial(O.vars[variable]) + if(VV_RESTORE_DEFAULT) + var_new = initial(O.vars[variable]) - if("edit referenced object") - return .(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("text") - var/var_new = input("Enter new text:","Text",O.vars[variable]) as null|message - if(var_new==null) return - O.vars[variable] = var_new - - if("num") - if(variable=="light_range") - var/var_new = input("Enter new number:","Num",O.vars[variable]) as null|num - if(var_new == null) return - O.set_light(var_new) - else if(variable=="stat") // ow, but I guess I'm glad you're trying to prevent at least one kind of inconsistent state...? This is the VARIABLE EDITOR, I'm not sure we need to worry...? - var/var_new = input("Enter new number:","Num",O.vars[variable]) as null|num - if(var_new == null) return - if((O.vars[variable] == DEAD) && (var_new < DEAD))//Bringing the dead back to life - dead_mob_list -= O - living_mob_list += O - if((O.vars[variable] < DEAD) && (var_new == DEAD))//Kill he - living_mob_list -= O - dead_mob_list += O - O.vars[variable] = var_new - else - var/var_new = input("Enter new number:","Num",O.vars[variable]) as null|num - if(var_new==null) return - O.vars[variable] = var_new - - if("type") - var/var_new = input("Enter type:","Type",O.vars[variable]) as null|anything in typesof(/obj,/mob,/area,/turf) - if(var_new==null) return - O.vars[variable] = var_new - - if("path") - var/path_text = input("Enter path:", "Path",O.vars[variable]) as null|text - var/var_new = text2path(path_text) - if(!var_new && path_text != null) // So aborting doesn't bother the VVer - to_chat(usr, "[path_text] does not appear to be a valid path.") - return - O.vars[variable] = var_new - - if("matrix") - var/matrix_text = input("Enter a, b, c, d, e, and f, separated by a space.", "Matrix", "1 0 0 0 1 0") as null|text - var/var_new = text2matrix(matrix_text) - if(!var_new && matrix_text != null) - to_chat(usr, "[matrix_text] is not a valid matrix string.") - return - O.vars[variable] = var_new - var_as_text = "matrix([matrix_text])" - - if("reference") - var/var_new = input("Select reference:","Reference",O.vars[variable]) as null|mob|obj|turf|area in world - if(var_new==null) return - O.vars[variable] = var_new - - if("mob reference") - var/var_new = input("Select reference:","Reference",O.vars[variable]) as null|mob in world - if(var_new==null) return - O.vars[variable] = var_new - - if("file") - var/var_new = input("Pick file:","File",O.vars[variable]) as null|file - if(var_new==null) return - O.vars[variable] = var_new - - if("icon") - var/var_new = input("Pick icon:","Icon",O.vars[variable]) as null|icon - if(var_new==null) return - O.vars[variable] = var_new - - if("marked datum") - O.vars[variable] = holder.marked_datum - - if(var_as_text == null) - var_as_text = "[O.vars[variable]]" - O.on_varedit(variable) - log_to_dd("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_as_text]")]") - log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_as_text]") - message_admins("[key_name_admin(src)] modified [original_name]'s [variable] to [var_as_text]", 1) - -// Let's get this all in one place. -// You'll need to take care of setting dir or iconizing the variable yourself once you've called this -/proc/variable_to_type(var/variable) - var/class - if(isnull(variable)) - to_chat(usr, "Unable to determine variable type.") - class = null - else if(isnum(variable)) - to_chat(usr, "Variable appears to be NUM.") - class = "num" - - else if(istext(variable)) - to_chat(usr, "Variable appears to be TEXT.") - class = "text" - - else if(isloc(variable)) - to_chat(usr, "Variable appears to be REFERENCE.") - class = "reference" - - else if(isicon(variable)) - to_chat(usr, "Variable appears to be ICON.") - variable = "[bicon(variable)]" - class = "icon" - - else if(istype(variable,/matrix)) - to_chat(usr, "Variable appears to be MATRIX") - class = "matrix" - - else if(istype(variable,/atom) || istype(variable,/datum)) - to_chat(usr, "Variable appears to be TYPE.") - class = "type" - - else if(istype(variable,/list)) - to_chat(usr, "Variable appears to be LIST.") - class = "list" - - else if(istype(variable,/client)) - to_chat(usr, "Variable appears to be CLIENT.") - class = "cancel" - - else if(ispath(variable)) - to_chat(usr, "Variable appears to be PATH.") - class = "path" - - else if(isfile(variable)) - to_chat(usr, "Variable appears to be FILE.") - class = "file" - - else - to_chat(usr, "Variable type is UNKNOWN.") - class = null - - return class + if(O.vv_edit_var(variable, var_new) == FALSE) + to_chat(src, "Your edit was rejected by the object.") + return + log_to_dd("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_new]")]") + log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_new]") + var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] to [var_new]" + message_admins(msg) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 82c478ce0da..6a223324268 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -2033,7 +2033,21 @@ ..() -mob/living/carbon/human/get_taste_sensitivity() + +/mob/living/carbon/human/vv_get_dropdown() + . = ..() + . += "---" + .["Set Species"] = "?_src_=vars;setspecies=[UID()]" + .["Make AI"] = "?_src_=vars;makeai=[UID()]" + .["Make Mask of Nar'sie"] = "?_src_=vars;makemask=[UID()]" + .["Make cyborg"] = "?_src_=vars;makerobot=[UID()]" + .["Make monkey"] = "?_src_=vars;makemonkey=[UID()]" + .["Make alien"] = "?_src_=vars;makealien=[UID()]" + .["Make slime"] = "?_src_=vars;makeslime=[UID()]" + .["Make superhero"] = "?_src_=vars;makesuper=[UID()]" + . += "---" + +/mob/living/carbon/human/get_taste_sensitivity() if(species) return species.taste_sensitivity else diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm index 9466806a78b..f7498392c02 100644 --- a/code/modules/mob/living/update_status.dm +++ b/code/modules/mob/living/update_status.dm @@ -97,8 +97,9 @@ /mob/living/proc/update_stamina() return -/mob/living/on_varedit(modified_var) - switch(modified_var) +/mob/living/vv_edit_var(var_name, var_value) + . = ..() + switch(var_name) if("weakened") SetWeakened(weakened) if("stunned") @@ -120,5 +121,4 @@ if("maxHealth") updatehealth() if("resize") - update_transform() - ..() \ No newline at end of file + update_transform() \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 03bc97c2d73..85028040cdc 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1237,3 +1237,31 @@ var/list/slot_equipment_priority = list( \ attack_log += new_log last_log = world.timeofday + +/mob/vv_get_dropdown() + . = ..() + .["Show player panel"] = "?_src_=vars;mob_player_panel=[UID()]" + + .["Give Spell"] = "?_src_=vars;give_spell=[UID()]" + .["Give Disease"] = "?_src_=vars;give_disease=[UID()]" + .["Toggle Godmode"] = "?_src_=vars;godmode=[UID()]" + .["Toggle Build Mode"] = "?_src_=vars;build_mode=[UID()]" + + .["Make 2spooky"] = "?_src_=vars;make_skeleton=[UID()]" + + .["Assume Direct Control"] = "?_src_=vars;direct_control=[UID()]" + .["Offer Control to Ghosts"] = "?_src_=vars;offer_control=[UID()]" + .["Drop Everything"] = "?_src_=vars;drop_everything=[UID()]" + + .["Regenerate Icons"] = "?_src_=vars;regenerateicons=[UID()]" + .["Add Language"] = "?_src_=vars;addlanguage=[UID()]" + .["Remove Language"] = "?_src_=vars;remlanguage=[UID()]" + .["Add Organ"] = "?_src_=vars;addorgan=[UID()]" + .["Remove Organ"] = "?_src_=vars;remorgan=[UID()]" + + .["Fix NanoUI"] = "?_src_=vars;fix_nano=[UID()]" + + .["Add Verb"] = "?_src_=vars;addverb=[UID()]" + .["Remove Verb"] = "?_src_=vars;remverb=[UID()]" + + .["Gib"] = "?_src_=vars;gib=[UID()]" \ No newline at end of file diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 7b848dca5c7..1d05f415f51 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -163,13 +163,14 @@ playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) return (OXYLOSS) -/obj/item/weapon/gun/energy/on_varedit(modified_var) - if(modified_var == "selfcharge") - if(selfcharge) - processing_objects.Add(src) - else - processing_objects.Remove(src) - ..() +/obj/item/weapon/gun/energy/vv_edit_var(var_name, var_value) + switch(var_name) + if("selfcharge") + if(var_value) + processing_objects.Add(src) + else + processing_objects.Remove(src) + . = ..() /obj/item/weapon/gun/energy/proc/robocharge() if(isrobot(loc)) diff --git a/paradise.dme b/paradise.dme index dd05488bf3c..fb3c89d71b0 100644 --- a/paradise.dme +++ b/paradise.dme @@ -52,6 +52,8 @@ #include "code\__DEFINES\sound.dm" #include "code\__DEFINES\stat.dm" #include "code\__DEFINES\tick.dm" +#include "code\__DEFINES\typeids.dm" +#include "code\__DEFINES\vv.dm" #include "code\__DEFINES\zlevel.dm" #include "code\__HELPERS\_string_lists.dm" #include "code\__HELPERS\AnimationLibrary.dm" From 17e2dfdbc2a49be09f79530bddeb38de525b8cb2 Mon Sep 17 00:00:00 2001 From: tigercat2000 Date: Thu, 14 Sep 2017 14:12:19 -0700 Subject: [PATCH 2/3] Adjusted vv_edit_var to use if(! syntax consistently --- code/modules/admin/verbs/modifyvariables.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index b94e319a848..3ffdc455045 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -353,7 +353,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width if("Yes") L[var_value] = mod_list_add_ass(O) //hehe if(O) - if(O.vv_edit_var(objectvar, L) == FALSE) + if(!O.vv_edit_var(objectvar, L)) to_chat(src, "Your edit was rejected by the object.") return log_to_dd("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") @@ -493,7 +493,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width if("DELETE FROM LIST") L.Cut(index, index+1) if(O) - if(O.vv_edit_var(objectvar, L)) + if(!O.vv_edit_var(objectvar, L)) to_chat(src, "Your edit was rejected by the object.") return log_to_dd("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") @@ -512,7 +512,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width else L[index] = new_var if(O) - if(O.vv_edit_var(objectvar, L) == FALSE) + if(!O.vv_edit_var(objectvar, L)) to_chat(src, "Your edit was rejected by the object.") return log_to_dd("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") @@ -623,7 +623,7 @@ var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width for(var/V in varsvars) var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]") - if(O.vv_edit_var(variable, var_new) == FALSE) + if(!O.vv_edit_var(variable, var_new)) to_chat(src, "Your edit was rejected by the object.") return log_to_dd("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_new]")]") From ec159814e2b3b875ee3fe47b192c972a6b9310c9 Mon Sep 17 00:00:00 2001 From: tigercat2000 Date: Fri, 15 Sep 2017 17:27:33 -0700 Subject: [PATCH 3/3] Add vv_can_delete, kill last vestiges of 'forbidden objects' --- code/datums/datumvars.dm | 12 +++++++++++ code/modules/admin/holder2.dm | 5 ++++- code/modules/admin/verbs/SDQL2/SDQL_2.dm | 23 ++++++++------------ code/modules/admin/verbs/modifyvariables.dm | 14 ------------ code/modules/admin/verbs/randomverbs.dm | 24 ++++++++++++++------- code/modules/research/message_server.dm | 7 ++++++ 6 files changed, 48 insertions(+), 37 deletions(-) diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index e97de99feff..b7ac60b84d7 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -39,6 +39,8 @@ return debug_variable(var_name, list(), 0, src) return debug_variable(var_name, vars[var_name], 0, src) +/datum/proc/can_vv_delete() + return TRUE //please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down //add seperaters by doing . += "---" @@ -722,6 +724,16 @@ to_chat(M, "There were no ghosts willing to take control.") message_admins("No ghosts were willing to take control of [key_name_admin(M)])") + else if(href_list["delete"]) + if(!check_rights(R_DEBUG, 0)) + return + + var/datum/D = locateUID(href_list["delete"]) + if(!D) + to_chat(usr, "Unable to locate item!") + admin_delete(D) + href_list["datumrefresh"] = href_list["delete"] + else if(href_list["delall"]) if(!check_rights(R_DEBUG|R_SERVER)) return diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index d2d0a6c7ab5..f3715eaab31 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -102,4 +102,7 @@ you will have to do something like if(client.holder.rights & R_ADMIN) yourself. return 0 /datum/admins/vv_edit_var(var_name, var_value) - return FALSE // no admin abuse \ No newline at end of file + return FALSE // no admin abuse + +/datum/admins/can_vv_delete() + return FALSE // don't break shit either \ No newline at end of file diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index 7b111e8ed20..859ff3ecf2b 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -89,8 +89,12 @@ if("delete") for(var/d in objs) - if(!datum_is_forbidden(d)) - qdel(d) + if(istype(d, /datum)) + var/datum/D = d + if(!D.can_vv_delete()) + to_chat(usr, "[D] rejected your deletion") + continue + qdel(d) if("select") var/text = "" @@ -118,9 +122,6 @@ if("set" in query_tree) var/list/set_list = query_tree["set"] for(var/d in objs) - // Forbid explicitly modifying an admin datum's vars - if(datum_is_forbidden(d)) - return for(var/list/sets in set_list) var/datum/temp = d var/i = 0 @@ -128,11 +129,10 @@ if(++i == sets.len) if(istype(temp, /turf) && (v == "x" || v == "y" || v == "z")) continue - if(!datum_is_forbidden(temp.vars[v])) - return - temp.vars[v] = SDQL_expression(d, set_list[sets]) + if(!temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))) + to_chat(usr, "[temp] rejected your varedit.") break - if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client)) && !datum_is_forbidden(temp.vars[v])) + if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client))) temp = temp.vars[v] else break @@ -440,11 +440,6 @@ for(var/arg in arguments) new_args[++new_args.len] = SDQL_expression(source, arg) - for(var/p in forbidden_varedit_object_types) - if(istype(object, p)) - to_chat(usr, "It is forbidden to run this object's procs.") - return - if(object == world) // Global proc. procname = "/proc/[procname]" return call(procname)(arglist(new_args)) diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index 3ffdc455045..0ad266ecaa1 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -1,21 +1,7 @@ -var/list/forbidden_varedit_object_types = list( - /datum/admins, //Admins editing their own admin-power object? Yup, sounds like a good idea. - /obj/machinery/blackbox_recorder, //Prevents people messing with feedback gathering - /datum/feedback_variable //Prevents people messing with feedback gathering - ) - var/list/VVlocked = list("vars", "var_edited", "client", "firemut", "ishulk", "telekinesis", "xray", "ka", "virus", "viruses", "cuffed", "last_eaten", "unlock_content") // R_DEBUG var/list/VVicon_edit_lock = list("icon", "icon_state", "overlays", "underlays", "resize") // R_EVENT | R_DEBUG var/list/VVckey_edit = list("key", "ckey") // R_EVENT | R_DEBUG var/list/VVpixelmovement = list("step_x", "step_y", "bound_height", "bound_width", "bound_x", "bound_y") // R_DEBUG + warning - -/proc/datum_is_forbidden(type) - for(var/p in forbidden_varedit_object_types) - if(istype(type, p)) - to_chat(usr, "It is forbidden to tamper with this object.") - return FALSE - return TRUE - /client/proc/vv_get_class(var/var_value) if(isnull(var_value)) . = VV_NULL diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index fdfbdfba6b2..ccae0db2988 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -588,22 +588,30 @@ Traitors and the like can also be revived with the previous role mostly intact. feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/cmd_admin_delete(atom/O as obj|mob|turf in view()) +/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view()) set category = "Admin" set name = "Delete" if(!check_rights(R_ADMIN)) return - if(alert(src, "Are you sure you want to delete:\n[O]\nat ([O.x], [O.y], [O.z])?", "Confirmation", "Yes", "No") == "Yes") - log_admin("[key_name(usr)] deleted [O] at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] deleted [O] at ([O.x],[O.y],[O.z])", 1) + admin_delete(A) + +/client/proc/admin_delete(datum/D) + if(istype(D) && !D.can_vv_delete()) + to_chat(src, "[D] rejected your deletion") + return + var/atom/A = D + var/coords = istype(A) ? "at ([A.x], [A.y], [A.z])" : "" + if(alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") + log_admin("[key_name(usr)] deleted [D][coords]") + message_admins("[key_name_admin(usr)] deleted [D][coords]", 1) feedback_add_details("admin_verb","DEL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(istype(O, /turf)) - var/turf/T = O + if(isturf(D)) + var/turf/T = D T.ChangeTurf(/turf/space) - return - qdel(O) + else + qdel(D) /client/proc/cmd_admin_list_open_jobs() set category = "Admin" diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index 52fa9596d92..86d507d34de 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -140,6 +140,9 @@ var/global/list/obj/machinery/message_server/message_servers = list() variable = param_variable value = param_value +/datum/feedback_variable/vv_edit_var(var_name, var_value) + return FALSE // come on guys don't break the stats + /datum/feedback_variable/proc/inc(var/num = 1) if(isnum(value)) value += num @@ -189,6 +192,7 @@ var/global/list/obj/machinery/message_server/message_servers = list() var/obj/machinery/blackbox_recorder/blackbox +//TODO: kill whoever designed this cancer /obj/machinery/blackbox_recorder icon = 'icons/obj/stationobjs.dmi' icon_state = "blackbox" @@ -314,6 +318,9 @@ var/obj/machinery/blackbox_recorder/blackbox var/DBQuery/query_insert = dbcon.NewQuery(sql) query_insert.Execute() +/obj/machinery/blackbox_recorder/vv_edit_var(var_name, var_value) + return FALSE // don't fuck with the stupid blackbox shit + proc/feedback_set(var/variable,var/value) if(!blackbox) return