/")
-
- var/sprite_text
- if(sprite)
- sprite_text = ""
- var/list/atomsnowflake = list()
-
- if(istype(D, /atom))
- var/atom/A = D
- if(isliving(A))
- atomsnowflake += "[D] "
- atomsnowflake += "<< [dir2text(A.dir) || A.dir] >> "
- var/mob/living/M = A
-
- atomsnowflake += {"
- [M.ckey || "No ckey"] / [M.real_name || "No real name"]
-
- BRUTE:[M.getBruteLoss()]
- FIRE:[M.getFireLoss()]
- TOXIN:[M.getToxLoss()]
- OXY:[M.getOxyLoss()]
- CLONE:[M.getCloneLoss()]
- BRAIN:[M.getOrganLoss(ORGAN_SLOT_BRAIN)]
- STAMINA:[M.getStaminaLoss()]
-
- "}
- else
- atomsnowflake += "[D] "
- atomsnowflake += "<< [dir2text(A.dir) || A.dir] >> "
- else if("name" in D.vars)
- atomsnowflake += "[D] "
- else
- atomsnowflake += "[formatted_type] "
- formatted_type = null
-
- var/marked
- if(holder && holder.marked_datum && holder.marked_datum == D)
- marked = VV_MSG_MARKED
- var/varedited_line = ""
- if(!islist && (D.datum_flags & DF_VAR_EDITED))
- varedited_line = VV_MSG_EDITED
- var/deleted_line
- if(!islist && D.gc_destroyed)
- deleted_line = VV_MSG_DELETED
-
- var/list/dropdownoptions = list()
- if (islist)
- dropdownoptions = list(
- "---",
- "Add Item" = "?_src_=vars;[HrefToken()];listadd=[refid]",
- "Remove Nulls" = "?_src_=vars;[HrefToken()];listnulls=[refid]",
- "Remove Dupes" = "?_src_=vars;[HrefToken()];listdupes=[refid]",
- "Set len" = "?_src_=vars;[HrefToken()];listlen=[refid]",
- "Shuffle" = "?_src_=vars;[HrefToken()];listshuffle=[refid]",
- "Show VV To Player" = "?_src_=vars;[HrefToken()];expose=[refid]"
- )
- else
- dropdownoptions = D.vv_get_dropdown()
- var/list/dropdownoptions_html = list()
-
- for (var/name in dropdownoptions)
- var/link = dropdownoptions[name]
- if (link)
- dropdownoptions_html += "[name] "
- else
- dropdownoptions_html += "[name] "
-
- var/list/names = list()
- if (!islist)
- for (var/V in D.vars)
- names += V
- sleep(1)//For some reason, without this sleep, VVing will cause client to disconnect on certain objects.
-
- 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)
-
- var/html = {"
-
-
- [title]
-
-
-
-
-
-
-
-
-
-
-
- [sprite_text]
-
- [atomsnowflake.Join()]
-
-
-
-
-
- [formatted_type]
- [marked]
- [varedited_line]
- [deleted_line]
-
-
-
-
-
-
-
-
-
-
- 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.
-
-
-
-
-
- [variable_html.Join()]
-
-
-
-
-"}
- src << browse(html, "window=variables[refid];size=475x650")
-
-
-/client/proc/vv_update_display(datum/D, span, content)
- src << output("[span]:[content]", "variables[REF(D)].browser:replace_span")
-
-
-#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
-/proc/debug_variable(name, value, level, datum/DA = null, sanitize = TRUE)
- var/header
- if(DA)
- 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
- header = " "
-
- var/item
- if (isnull(value))
- item = "[VV_HTML_ENCODE(name)] = null "
-
- else if (istext(value))
- item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\" "
-
- else if (isicon(value))
- #ifdef VARSICON
- var/icon/I = new/icon(value)
- var/rnd = rand(1,10000)
- var/rname = "tmp[REF(I)][rnd].png"
- usr << browse_rsc(I, rname)
- item = "[VV_HTML_ENCODE(name)] = ([value] ) "
- #else
- item = "[VV_HTML_ENCODE(name)] = /icon ([value] )"
- #endif
-
- else if (isfile(value))
- item = "[VV_HTML_ENCODE(name)] = '[value]' "
-
- else if (istype(value, /datum))
- var/datum/D = value
- if ("[D]" != "[D.type]") //if the thing as a name var, lets use it.
- item = "[VV_HTML_ENCODE(name)] [REF(value)] = [D] [D.type]"
- else
- item = "[VV_HTML_ENCODE(name)] [REF(value)] = [D.type]"
-
- else if (islist(value))
- var/list/L = value
- var/list/items = list()
-
- if (L.len > 0 && !(name == "underlays" || name == "overlays" || 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 (isnull(val)) // we still want to display non-null false values, such as 0 or ""
- val = key
- key = i
-
- items += debug_variable(key, val, level + 1, sanitize = sanitize)
-
- item = "[VV_HTML_ENCODE(name)] = /list ([L.len]) "
- else
- item = "[VV_HTML_ENCODE(name)] = /list ([L.len]) "
-
- else if (name in GLOB.bitfields)
- var/list/flags = list()
- for (var/i in GLOB.bitfields[name])
- if (value & GLOB.bitfields[name][i])
- flags += i
- item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(jointext(flags, ", "))]"
- else
- item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)] "
-
- return "[header][item] "
-
-#undef VV_HTML_ENCODE
-
-/client/proc/view_var_Topic(href, href_list, hsrc)
- if( (usr.client != src) || !src.holder || !holder.CheckAdminHref(href, href_list))
- return
- if(href_list["Vars"])
- debug_variables(locate(href_list["Vars"]))
-
- else if(href_list["datumrefresh"])
- var/datum/DAT = locate(href_list["datumrefresh"])
- if(!DAT) //can't be an istype() because /client etc aren't datums
- return
- src.debug_variables(DAT)
-
- else if(href_list["mob_player_panel"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["mob_player_panel"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.holder.show_player_panel(M)
-
- else if(href_list["godmode"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["godmode"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.cmd_admin_godmode(M)
-
- else if(href_list["mark_object"])
- if(!check_rights(NONE))
- return
-
- var/datum/D = locate(href_list["mark_object"])
- if(!istype(D))
- to_chat(usr, "This can only be done to instances of type /datum")
- return
-
- if(holder.marked_datum)
- vv_update_display(holder.marked_datum, "marked", "")
- holder.marked_datum = D
- vv_update_display(D, "marked", VV_MSG_MARKED)
-
- else if(href_list["proc_call"])
- if(!check_rights(NONE))
- return
-
- var/T = locate(href_list["proc_call"])
-
- if(T)
- callproc_datum(T)
-
- else if(href_list["delete"])
- if(!check_rights(R_DEBUG, 0))
- return
-
- var/datum/D = locate(href_list["delete"])
- if(!istype(D))
- to_chat(usr, "Unable to locate item!")
- admin_delete(D)
- if (isturf(D)) // show the turf that took its place
- debug_variables(D)
-
- else if(href_list["osay"])
- if(!check_rights(R_FUN, 0))
- return
- usr.client.object_say(locate(href_list["osay"]))
-
- else if(href_list["regenerateicons"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["regenerateicons"]) in GLOB.mob_list
- if(!ismob(M))
- to_chat(usr, "This can only be done to instances of type /mob")
- return
- M.regenerate_icons()
- else if(href_list["expose"])
- if(!check_rights(R_ADMIN, FALSE))
- return
- var/thing = locate(href_list["expose"])
- if (!thing)
- return
- var/value = vv_get_value(VV_CLIENT)
- if (value["class"] != VV_CLIENT)
- return
- var/client/C = value["value"]
- if (!C)
- return
- var/prompt = alert("Do you want to grant [C] access to view this VV window? (they will not be able to edit or change anything nor open nested vv windows unless they themselves are an admin)", "Confirm", "Yes", "No")
- if (prompt != "Yes" || !usr.client)
- return
- message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a VV window ")
- log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [thing]")
- to_chat(C, "[usr.client.holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
- C.debug_variables(thing)
- else if(href_list["traitmod"])
- if(!check_rights(NONE))
- return
- var/datum/A = locate(href_list["traitmod"])
- if(!A)
- return
- holder.modify_traits(A)
-
-
-//Needs +VAREDIT past this point
-
- else if(check_rights(R_VAREDIT))
-
-
- //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records).
-
- if(href_list["rename"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["rename"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN)
- if( !new_name || !M )
- return
-
- message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].")
- M.fully_replace_character_name(M.real_name,new_name)
- vv_update_display(M, "name", new_name)
- vv_update_display(M, "real_name", M.real_name || "No real name")
-
- else if(href_list["varnameedit"] && href_list["datumedit"])
- if(!check_rights(NONE))
- return
-
- var/datum/D = locate(href_list["datumedit"])
- if(!istype(D, /datum))
- to_chat(usr, "This can only be used on datums")
- return
-
- if (!modify_variables(D, href_list["varnameedit"], 1))
- return
- switch(href_list["varnameedit"])
- if("name")
- vv_update_display(D, "name", "[D]")
- if("dir")
- var/atom/A = D
- if(istype(A))
- vv_update_display(D, "dir", dir2text(A.dir) || A.dir)
- if("ckey")
- var/mob/living/L = D
- if(istype(L))
- vv_update_display(D, "ckey", L.ckey || "No ckey")
- if("real_name")
- var/mob/living/L = D
- if(istype(L))
- vv_update_display(D, "real_name", L.real_name || "No real name")
-
- else if(href_list["varnamechange"] && href_list["datumchange"])
- if(!check_rights(NONE))
- return
-
- var/D = locate(href_list["datumchange"])
- if(!istype(D, /datum))
- to_chat(usr, "This can only be used on datums")
- return
-
- modify_variables(D, href_list["varnamechange"], 0)
-
- else if(href_list["varnamemass"] && href_list["datummass"])
- if(!check_rights(NONE))
- return
-
- var/datum/D = locate(href_list["datummass"])
- if(!istype(D))
- to_chat(usr, "This can only be used on instances of type /datum")
- return
-
- cmd_mass_modify_object_variables(D, href_list["varnamemass"])
-
- else if(href_list["listedit"] && href_list["index"])
- var/index = text2num(href_list["index"])
- if (!index)
- return
-
- 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)
-
- else if(href_list["listchange"] && href_list["index"])
- var/index = text2num(href_list["index"])
- if (!index)
- return
-
- 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)
-
- else if(href_list["listremove"] && href_list["index"])
- var/index = text2num(href_list["index"])
- if (!index)
- return
-
- 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_world("### 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]")
-
- else 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
-
- mod_list_add(L, null, "list", "contents")
-
- else 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
-
- uniqueList_inplace(L)
- log_world("### 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")
-
- else 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
-
- listclearnulls(L)
- log_world("### 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")
-
- else 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
- var/value = vv_get_value(VV_NUM)
- if (value["class"] != VV_NUM)
- return
-
- L.len = value["value"]
- log_world("### 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]")
-
- else 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
-
- shuffle_inplace(L)
- log_world("### 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")
-
- else if(href_list["give_spell"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["give_spell"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.give_spell(M)
-
- else if(href_list["remove_spell"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["remove_spell"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- remove_spell(M)
-
- else if(href_list["give_disease"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["give_disease"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.give_disease(M)
-
- else if(href_list["gib"])
- if(!check_rights(R_FUN))
- return
-
- var/mob/M = locate(href_list["gib"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.cmd_admin_gib(M)
-
- else if(href_list["build_mode"])
- if(!check_rights(R_BUILD))
- return
-
- var/mob/M = locate(href_list["build_mode"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- togglebuildmode(M)
-
- else if(href_list["drop_everything"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["drop_everything"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- if(usr.client)
- usr.client.cmd_admin_drop_everything(M)
-
- else if(href_list["direct_control"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["direct_control"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- if(usr.client)
- usr.client.cmd_assume_direct_control(M)
-
- else if(href_list["offer_control"])
- if(!check_rights(NONE))
- return
-
- var/mob/M = locate(href_list["offer_control"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
- offer_control(M)
-
- else if (href_list["modarmor"])
- if(!check_rights(NONE))
- return
-
- var/obj/O = locate(href_list["modarmor"])
- if(!istype(O))
- to_chat(usr, "This can only be used on instances of type /obj")
- return
-
- var/list/pickerlist = list()
- var/list/armorlist = O.armor.getList()
-
- for (var/i in armorlist)
- pickerlist += list(list("value" = armorlist[i], "name" = i))
-
- var/list/result = presentpicker(usr, "Modify armor", "Modify armor: [O]", Button1="Save", Button2 = "Cancel", Timeout=FALSE, inputtype = "text", values = pickerlist)
-
- if (islist(result))
- if (result["button"] == 2) // If the user pressed the cancel button
- return
- // text2num conveniently returns a null on invalid values
- O.armor = O.armor.setRating(melee = text2num(result["values"]["melee"]),\
- bullet = text2num(result["values"]["bullet"]),\
- laser = text2num(result["values"]["laser"]),\
- energy = text2num(result["values"]["energy"]),\
- bomb = text2num(result["values"]["bomb"]),\
- bio = text2num(result["values"]["bio"]),\
- rad = text2num(result["values"]["rad"]),\
- fire = text2num(result["values"]["fire"]),\
- acid = text2num(result["values"]["acid"]))
- log_admin("[key_name(usr)] modified the armor on [O] ([O.type]) to melee: [O.armor.melee], bullet: [O.armor.bullet], laser: [O.armor.laser], energy: [O.armor.energy], bomb: [O.armor.bomb], bio: [O.armor.bio], rad: [O.armor.rad], fire: [O.armor.fire], acid: [O.armor.acid]")
- message_admins("[key_name_admin(usr)] modified the armor on [O] ([O.type]) to melee: [O.armor.melee], bullet: [O.armor.bullet], laser: [O.armor.laser], energy: [O.armor.energy], bomb: [O.armor.bomb], bio: [O.armor.bio], rad: [O.armor.rad], fire: [O.armor.fire], acid: [O.armor.acid] ")
- else
- return
-
- else if(href_list["delall"])
- if(!check_rights(R_DEBUG|R_SERVER))
- return
-
- var/obj/O = locate(href_list["delall"])
- if(!isobj(O))
- to_chat(usr, "This can only be used on instances of type /obj")
- return
-
- var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel")
- if(action_type == "Cancel" || !action_type)
- return
-
- if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes")
- return
-
- if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes")
- return
-
- var/O_type = O.type
- switch(action_type)
- if("Strict type")
- var/i = 0
- for(var/obj/Obj in world)
- if(Obj.type == O_type)
- i++
- qdel(Obj)
- CHECK_TICK
- if(!i)
- to_chat(usr, "No objects of this type exist")
- return
- log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ")
- message_admins("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ")
- if("Type and subtypes")
- var/i = 0
- for(var/obj/Obj in world)
- if(istype(Obj,O_type))
- i++
- qdel(Obj)
- CHECK_TICK
- if(!i)
- to_chat(usr, "No objects of this type exist")
- return
- log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ")
- message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ")
-
- else if(href_list["addreagent"])
- if(!check_rights(NONE))
- return
-
- var/atom/A = locate(href_list["addreagent"])
-
- if(!A.reagents)
- var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num
- if(amount)
- A.create_reagents(amount)
-
- if(A.reagents)
- var/chosen_id
- switch(alert(usr, "Choose a method.", "Add Reagents", "Search", "Choose from a list", "I'm feeling lucky"))
- if("Search")
- var/valid_id
- while(!valid_id)
- chosen_id = input(usr, "Enter the ID of the reagent you want to add.", "Search reagents") as null|text
- if(isnull(chosen_id)) //Get me out of here!
- break
- if (!ispath(text2path(chosen_id)))
- chosen_id = pick_closest_path(chosen_id, make_types_fancy(subtypesof(/datum/reagent)))
- if (ispath(chosen_id))
- valid_id = TRUE
- else
- valid_id = TRUE
- if(!valid_id)
- to_chat(usr, "A reagent with that ID doesn't exist! ")
- if("Choose from a list")
- chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in subtypesof(/datum/reagent)
- if("I'm feeling lucky")
- chosen_id = pick(subtypesof(/datum/reagent))
- if(chosen_id)
- var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num
- if(amount)
- A.reagents.add_reagent(chosen_id, amount)
- log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]")
- message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A] ")
-
- else if(href_list["explode"])
- if(!check_rights(R_FUN))
- return
-
- var/atom/A = locate(href_list["explode"])
- if(!isobj(A) && !ismob(A) && !isturf(A))
- to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf")
- return
-
- src.cmd_admin_explosion(A)
-
- else if(href_list["emp"])
- if(!check_rights(R_FUN))
- return
-
- var/atom/A = locate(href_list["emp"])
- if(!isobj(A) && !ismob(A) && !isturf(A))
- to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf")
- return
-
- src.cmd_admin_emp(A)
-
- else if(href_list["modtransform"])
- if(!check_rights(R_DEBUG))
- return
-
- var/atom/A = locate(href_list["modtransform"])
- if(!istype(A))
- to_chat(usr, "This can only be done to atoms.")
- return
-
- var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate")
- var/matrix/M = A.transform
- switch(result)
- if("Scale")
- var/x = input(usr, "Choose x mod","Transform Mod") as null|num
- var/y = input(usr, "Choose y mod","Transform Mod") as null|num
- if(!isnull(x) && !isnull(y))
- A.transform = M.Scale(x,y)
- if("Translate")
- var/x = input(usr, "Choose x mod","Transform Mod") as null|num
- var/y = input(usr, "Choose y mod","Transform Mod") as null|num
- if(!isnull(x) && !isnull(y))
- A.transform = M.Translate(x,y)
- if("Rotate")
- var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num
- if(!isnull(angle))
- A.transform = M.Turn(angle)
-
- else if(href_list["rotatedatum"])
- if(!check_rights(NONE))
- return
-
- var/atom/A = locate(href_list["rotatedatum"])
- if(!istype(A))
- to_chat(usr, "This can only be done to instances of type /atom")
- return
-
- switch(href_list["rotatedir"])
- if("right")
- A.setDir(turn(A.dir, -45))
- if("left")
- A.setDir(turn(A.dir, 45))
- vv_update_display(A, "dir", dir2text(A.dir))
-
- else if(href_list["editorgans"])
- if(!check_rights(NONE))
- return
-
- var/mob/living/carbon/C = locate(href_list["editorgans"]) in GLOB.mob_list
- if(!istype(C))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
- return
-
- manipulate_organs(C)
-
- else if(href_list["givemartialart"])
- if(!check_rights(NONE))
- return
-
- var/mob/living/carbon/C = locate(href_list["givemartialart"]) in GLOB.carbon_list
- if(!istype(C))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
- return
-
- var/list/artpaths = subtypesof(/datum/martial_art)
- var/list/artnames = list()
- for(var/i in artpaths)
- var/datum/martial_art/M = i
- artnames[initial(M.name)] = M
-
- var/result = input(usr, "Choose the martial art to teach","JUDO CHOP") as null|anything in artnames
- if(!usr)
- return
- if(QDELETED(C))
- to_chat(usr, "Mob doesn't exist anymore")
- return
-
- if(result)
- var/chosenart = artnames[result]
- var/datum/martial_art/MA = new chosenart
- MA.teach(C)
- log_admin("[key_name(usr)] has taught [MA] to [key_name(C)].")
- message_admins("[key_name_admin(usr)] has taught [MA] to [key_name_admin(C)]. ")
-
- else if(href_list["givetrauma"])
- if(!check_rights(NONE))
- return
-
- var/mob/living/carbon/C = locate(href_list["givetrauma"]) in GLOB.mob_list
- if(!istype(C))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
- return
-
- var/list/traumas = subtypesof(/datum/brain_trauma)
- var/result = input(usr, "Choose the brain trauma to apply","Traumatize") as null|anything in traumas
- if(!usr)
- return
- if(QDELETED(C))
- to_chat(usr, "Mob doesn't exist anymore")
- return
-
- if(!result)
- return
-
- var/datum/brain_trauma/BT = C.gain_trauma(result)
- if(BT)
- log_admin("[key_name(usr)] has traumatized [key_name(C)] with [BT.name]")
- message_admins("[key_name_admin(usr)] has traumatized [key_name_admin(C)] with [BT.name]. ")
-
- else if(href_list["curetraumas"])
- if(!check_rights(NONE))
- return
-
- var/mob/living/carbon/C = locate(href_list["curetraumas"]) in GLOB.mob_list
- if(!istype(C))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
- return
-
- C.cure_all_traumas(TRAUMA_RESILIENCE_ABSOLUTE)
- log_admin("[key_name(usr)] has cured all traumas from [key_name(C)].")
- message_admins("[key_name_admin(usr)] has cured all traumas from [key_name_admin(C)]. ")
-
- else if(href_list["hallucinate"])
- if(!check_rights(NONE))
- return
-
- var/mob/living/carbon/C = locate(href_list["hallucinate"]) in GLOB.mob_list
- if(!istype(C))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
- return
-
- var/list/hallucinations = subtypesof(/datum/hallucination)
- var/result = input(usr, "Choose the hallucination to apply","Send Hallucination") as null|anything in hallucinations
- if(!usr)
- return
- if(QDELETED(C))
- to_chat(usr, "Mob doesn't exist anymore")
- return
-
- if(result)
- new result(C, TRUE)
-
- else if(href_list["makehuman"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list
- if(!istype(Mo))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey")
- return
-
- if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
- return
- if(!Mo)
- to_chat(usr, "Mob doesn't exist anymore")
- return
- holder.Topic(href, list("humanone"=href_list["makehuman"]))
-
- else if(href_list["makemonkey"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["makemonkey"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
- return
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
- holder.Topic(href, list("monkeyone"=href_list["makemonkey"]))
-
- else if(href_list["makerobot"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["makerobot"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
- return
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
- holder.Topic(href, list("makerobot"=href_list["makerobot"]))
-
- else if(href_list["makealien"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["makealien"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
- return
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
- holder.Topic(href, list("makealien"=href_list["makealien"]))
-
- else if(href_list["makeslime"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["makeslime"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
- return
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
- holder.Topic(href, list("makeslime"=href_list["makeslime"]))
-
- else if(href_list["makeai"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/H = locate(href_list["makeai"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
- return
-
- if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
- return
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
- holder.Topic(href, list("makeai"=href_list["makeai"]))
-
- else if(href_list["setspecies"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["setspecies"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list
-
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
-
- if(result)
- var/newtype = GLOB.species_list[result]
- admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [H] to [result]")
- H.set_species(newtype)
-
- else if(href_list["editbodypart"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/C = locate(href_list["editbodypart"]) in GLOB.mob_list
- if(!istype(C))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon")
- return
-
- var/edit_action = input(usr, "What would you like to do?","Modify Body Part") as null|anything in list("add","remove", "augment")
- if(!edit_action)
- return
- var/list/limb_list = list()
- if(edit_action == "remove" || edit_action == "augment")
- for(var/obj/item/bodypart/B in C.bodyparts)
- limb_list += B.body_zone
- if(edit_action == "remove")
- limb_list -= BODY_ZONE_CHEST
- else
- limb_list = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
- for(var/obj/item/bodypart/B in C.bodyparts)
- limb_list -= B.body_zone
-
- var/result = input(usr, "Please choose which body part to [edit_action]","[capitalize(edit_action)] Body Part") as null|anything in limb_list
-
- if(!C)
- to_chat(usr, "Mob doesn't exist anymore")
- return
-
- if(result)
- var/obj/item/bodypart/BP = C.get_bodypart(result)
- switch(edit_action)
- if("remove")
- if(BP)
- BP.drop_limb()
- else
- to_chat(usr, "[C] doesn't have such bodypart.")
- if("add")
- if(BP)
- to_chat(usr, "[C] already has such bodypart.")
- else
- if(!C.regenerate_limb(result))
- to_chat(usr, "[C] cannot have such bodypart.")
- if("augment")
- if(ishuman(C))
- if(BP)
- BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE)
- else
- to_chat(usr, "[C] doesn't have such bodypart.")
- else
- to_chat(usr, "Only humans can be augmented.")
- admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [C]")
-
-
- else if(href_list["purrbation"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["purrbation"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
- return
- if(!ishumanbasic(H))
- to_chat(usr, "This can only be done to the basic human species at the moment.")
- return
-
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
-
- var/success = purrbation_toggle(H)
- if(success)
- to_chat(usr, "Put [H] on purrbation.")
- log_admin("[key_name(usr)] has put [key_name(H)] on purrbation.")
- var/msg = "[key_name_admin(usr)] has put [key_name(H)] on purrbation. "
- message_admins(msg)
- admin_ticket_log(H, msg)
-
- else
- to_chat(usr, "Removed [H] from purrbation.")
- log_admin("[key_name(usr)] has removed [key_name(H)] from purrbation.")
- var/msg = "[key_name_admin(usr)] has removed [key_name(H)] from purrbation. "
- message_admins(msg)
- admin_ticket_log(H, msg)
-
- else if(href_list["adjustDamage"] && href_list["mobToDamage"])
- if(!check_rights(NONE))
- return
-
- var/mob/living/L = locate(href_list["mobToDamage"]) in GLOB.mob_list
- if(!istype(L))
- return
-
- var/Text = href_list["adjustDamage"]
-
- var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num
-
- if(!L)
- to_chat(usr, "Mob doesn't exist anymore")
- return
-
- var/newamt
- switch(Text)
- if("brute")
- L.adjustBruteLoss(amount)
- newamt = L.getBruteLoss()
- if("fire")
- L.adjustFireLoss(amount)
- newamt = L.getFireLoss()
- if("toxin")
- L.adjustToxLoss(amount)
- newamt = L.getToxLoss()
- if("oxygen")
- L.adjustOxyLoss(amount)
- newamt = L.getOxyLoss()
- if("brain")
- L.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount)
- newamt = L.getOrganLoss(ORGAN_SLOT_BRAIN)
- if("clone")
- L.adjustCloneLoss(amount)
- newamt = L.getCloneLoss()
- if("stamina")
- L.adjustStaminaLoss(amount)
- newamt = L.getStaminaLoss()
- else
- to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
- return
-
- if(amount != 0)
- var/log_msg = "[key_name(usr)] dealt [amount] amount of [Text] damage to [key_name(L)]"
- message_admins("[key_name(usr)] dealt [amount] amount of [Text] damage to [ADMIN_LOOKUPFLW(L)]")
- log_admin(log_msg)
- admin_ticket_log(L, "[log_msg] ")
- vv_update_display(L, Text, "[newamt]")
- else if(href_list["copyoutfit"])
- if(!check_rights(R_SPAWN))
- return
- var/mob/living/carbon/human/H = locate(href_list["copyoutfit"]) in GLOB.carbon_list
- if(istype(H))
- H.copy_outfit()
- else if(href_list["modquirks"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["modquirks"]) in GLOB.mob_list
- if(!istype(H))
- to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
- return
-
- var/list/options = list("Clear"="Clear")
- for(var/x in subtypesof(/datum/quirk))
- var/datum/quirk/T = x
- var/qname = initial(T.name)
- options[H.has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T
-
- var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options
- if(result)
- if(result == "Clear")
- for(var/datum/quirk/q in H.roundstart_quirks)
- H.remove_quirk(q.type)
- else
- var/T = options[result]
- if(H.has_quirk(T))
- H.remove_quirk(T)
- else
- H.add_quirk(T,TRUE)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 9df649067ef..7f38ccca38f 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -862,14 +862,79 @@
*/
/atom/vv_get_dropdown()
. = ..()
- . += "---"
- var/turf/curturf = get_turf(src)
- if (curturf)
- .["Jump to"] = "?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]"
- .["Modify Transform"] = "?_src_=vars;[HrefToken()];modtransform=[REF(src)]"
- .["Add reagent"] = "?_src_=vars;[HrefToken()];addreagent=[REF(src)]"
- .["Trigger EM pulse"] = "?_src_=vars;[HrefToken()];emp=[REF(src)]"
- .["Trigger explosion"] = "?_src_=vars;[HrefToken()];explode=[REF(src)]"
+ VV_DROPDOWN_OPTION("", "---------")
+ if(!ismovableatom(src))
+ var/turf/curturf = get_turf(src)
+ if(curturf)
+ . += "Jump To "
+ VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRANSFORM, "Modify Transform")
+ VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent")
+ VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
+ VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
+
+/atom/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_ADD_REAGENT] && check_rights(R_VAREDIT))
+ if(!reagents)
+ var/amount = input(usr, "Specify the reagent size of [src]", "Set Reagent Size", 50) as num
+ if(amount)
+ create_reagents(amount)
+
+ if(reagents)
+ var/chosen_id
+ switch(alert(usr, "Choose a method.", "Add Reagents", "Search", "Choose from a list", "I'm feeling lucky"))
+ if("Search")
+ var/valid_id
+ while(!valid_id)
+ chosen_id = input(usr, "Enter the ID of the reagent you want to add.", "Search reagents") as null|text
+ if(isnull(chosen_id)) //Get me out of here!
+ break
+ if (!ispath(text2path(chosen_id)))
+ chosen_id = pick_closest_path(chosen_id, make_types_fancy(subtypesof(/datum/reagent)))
+ if (ispath(chosen_id))
+ valid_id = TRUE
+ else
+ valid_id = TRUE
+ if(!valid_id)
+ to_chat(usr, "A reagent with that ID doesn't exist! ")
+ if("Choose from a list")
+ chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in subtypesof(/datum/reagent)
+ if("I'm feeling lucky")
+ chosen_id = pick(subtypesof(/datum/reagent))
+ if(chosen_id)
+ var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", reagents.maximum_volume) as num
+ if(amount)
+ reagents.add_reagent(chosen_id, amount)
+ log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to [src]")
+ message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to [src] ")
+ if(href_list[VV_HK_TRIGGER_EXPLOSION] && check_rights(R_FUN))
+ usr.client.cmd_admin_explosion(src)
+ if(href_list[VV_HK_TRIGGER_EMP] && check_rights(R_FUN))
+ usr.client.cmd_admin_emp(src)
+ if(href_list[VV_HK_MODIFY_TRANSFORM] && check_rights(R_VAREDIT))
+ var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate")
+ var/matrix/M = transform
+ switch(result)
+ if("Scale")
+ var/x = input(usr, "Choose x mod","Transform Mod") as null|num
+ var/y = input(usr, "Choose y mod","Transform Mod") as null|num
+ if(!isnull(x) && !isnull(y))
+ transform = M.Scale(x,y)
+ if("Translate")
+ var/x = input(usr, "Choose x mod","Transform Mod") as null|num
+ var/y = input(usr, "Choose y mod","Transform Mod") as null|num
+ if(!isnull(x) && !isnull(y))
+ transform = M.Translate(x,y)
+ if("Rotate")
+ var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num
+ if(!isnull(angle))
+ transform = M.Turn(angle)
+
+/atom/vv_get_header()
+ . = ..()
+ var/refid = REF(src)
+ . += "[src] "
+ . += "<< [dir2text(dir) || dir] >> "
///Where atoms should drop if taken from this atom
/atom/proc/drop_location()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 190f9e7ae21..fd8e1dec9b1 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -742,9 +742,8 @@
/atom/movable/vv_get_dropdown()
. = ..()
- . -= "Jump to"
- .["Follow"] = "?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(src)]"
- .["Get"] = "?_src_=holder;[HrefToken()];admingetmovable=[REF(src)]"
+ . += "Follow "
+ . += "Get "
/atom/movable/proc/ex_check(ex_id)
if(!ex_id)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index ff88513e489..54e82999149 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -214,9 +214,78 @@
/obj/vv_get_dropdown()
. = ..()
- .["Delete all of type"] = "?_src_=vars;[HrefToken()];delall=[REF(src)]"
- .["Osay"] = "?_src_=vars;[HrefToken()];osay[REF(src)]"
- .["Modify armor values"] = "?_src_=vars;[HrefToken()];modarmor=[REF(src)]"
+ VV_DROPDOWN_OPTION("", "---")
+ VV_DROPDOWN_OPTION(VV_HK_MASS_DEL_TYPE, "Delete all of type")
+ VV_DROPDOWN_OPTION(VV_HK_OSAY, "Object Say")
+ VV_DROPDOWN_OPTION(VV_HK_ARMOR_MOD, "Modify armor values")
+
+/obj/vv_do_topic(list/href_list)
+ if(!(. = ..()))
+ return
+ if(href_list[VV_HK_OSAY])
+ if(check_rights(R_FUN, FALSE))
+ usr.client.object_say(src)
+ if(href_list[VV_HK_ARMOR_MOD])
+ var/list/pickerlist = list()
+ var/list/armorlist = armor.getList()
+
+ for (var/i in armorlist)
+ pickerlist += list(list("value" = armorlist[i], "name" = i))
+
+ var/list/result = presentpicker(usr, "Modify armor", "Modify armor: [src]", Button1="Save", Button2 = "Cancel", Timeout=FALSE, inputtype = "text", values = pickerlist)
+
+ if (islist(result))
+ if (result["button"] != 2) // If the user pressed the cancel button
+ // text2num conveniently returns a null on invalid values
+ armor = armor.setRating(melee = text2num(result["values"]["melee"]),\
+ bullet = text2num(result["values"]["bullet"]),\
+ laser = text2num(result["values"]["laser"]),\
+ energy = text2num(result["values"]["energy"]),\
+ bomb = text2num(result["values"]["bomb"]),\
+ bio = text2num(result["values"]["bio"]),\
+ rad = text2num(result["values"]["rad"]),\
+ fire = text2num(result["values"]["fire"]),\
+ acid = text2num(result["values"]["acid"]))
+ log_admin("[key_name(usr)] modified the armor on [src] ([type]) to melee: [armor.melee], bullet: [armor.bullet], laser: [armor.laser], energy: [armor.energy], bomb: [armor.bomb], bio: [armor.bio], rad: [armor.rad], fire: [armor.fire], acid: [armor.acid]")
+ message_admins("[key_name_admin(usr)] modified the armor on [src] ([type]) to melee: [armor.melee], bullet: [armor.bullet], laser: [armor.laser], energy: [armor.energy], bomb: [armor.bomb], bio: [armor.bio], rad: [armor.rad], fire: [armor.fire], acid: [armor.acid] ")
+ if(href_list[VV_HK_MASS_DEL_TYPE])
+ if(check_rights(R_DEBUG|R_SERVER))
+ var/action_type = alert("Strict type ([type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel")
+ if(action_type == "Cancel" || !action_type)
+ return
+
+ if(alert("Are you really sure you want to delete all objects of type [type]?",,"Yes","No") != "Yes")
+ return
+
+ if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes")
+ return
+
+ var/O_type = type
+ switch(action_type)
+ if("Strict type")
+ var/i = 0
+ for(var/obj/Obj in world)
+ if(Obj.type == O_type)
+ i++
+ qdel(Obj)
+ CHECK_TICK
+ if(!i)
+ to_chat(usr, "No objects of this type exist")
+ return
+ log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ")
+ message_admins("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ")
+ if("Type and subtypes")
+ var/i = 0
+ for(var/obj/Obj in world)
+ if(istype(Obj,O_type))
+ i++
+ qdel(Obj)
+ CHECK_TICK
+ if(!i)
+ to_chat(usr, "No objects of this type exist")
+ return
+ log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ")
+ message_admins("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ")
/obj/examine(mob/user)
. = ..()
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 8d0021e9c66..8d242832222 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -17,7 +17,8 @@ GLOBAL_PROTECT(admin_verbs_default)
/client/proc/reestablish_db_connection, /*reattempt a connection to the database*/
/client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
- /client/proc/stop_sounds
+ /client/proc/stop_sounds,
+ /client/proc/mark_datum_mapview
)
GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
GLOBAL_PROTECT(admin_verbs_admin)
diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm
new file mode 100644
index 00000000000..2142a5e57c7
--- /dev/null
+++ b/code/modules/admin/callproc/callproc.dm
@@ -0,0 +1,207 @@
+/client/proc/callproc()
+ set category = "Debug"
+ set name = "Advanced ProcCall"
+ set waitfor = FALSE
+ callproc_blocking()
+
+/client/proc/callproc_blocking(list/get_retval)
+ if(!check_rights(R_DEBUG))
+ return
+
+ var/datum/target
+ var/targetselected = FALSE
+ var/returnval
+
+ switch(alert("Proc owned by something?",,"Yes","No"))
+ if("Yes")
+ targetselected = TRUE
+ var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT, VV_MARKED_DATUM, VV_TEXT_LOCATE, VV_PROCCALL_RETVAL))
+ if (!value["class"] || !value["value"])
+ return
+ target = value["value"]
+ if(!istype(target))
+ to_chat(usr, "Invalid target. ")
+ return
+ if("No")
+ target = null
+ targetselected = FALSE
+
+ var/procpath = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null
+ if(!procpath)
+ return
+
+ //strip away everything but the proc name
+ var/list/proclist = splittext(procpath, "/")
+ if (!length(proclist))
+ return
+
+ var/procname = proclist[proclist.len]
+ var/proctype = ("verb" in proclist) ? "verb" :"proc"
+
+ if(targetselected)
+ if(!hascall(target, procname))
+ to_chat(usr, "Error: callproc(): type [target.type] has no [proctype] named [procpath]. ")
+ return
+ else
+ procpath = "/[proctype]/[procname]"
+ if(!text2path(procpath))
+ to_chat(usr, "Error: callproc(): [procpath] does not exist. ")
+ return
+
+ var/list/lst = get_callproc_args()
+ if(!lst)
+ return
+
+ if(targetselected)
+ if(!target)
+ to_chat(usr, "Error: callproc(): owner of proc no longer exists. ")
+ return
+ var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
+ log_admin(msg)
+ message_admins(msg) //Proccall announce removed.
+ admin_ticket_log(target, msg)
+ returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc
+ else
+ //this currently has no hascall protection. wasn't able to get it working.
+ log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
+ message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed.
+ returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Advanced ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ if(get_retval)
+ get_retval += returnval
+ . = get_callproc_returnval(returnval, procname)
+ if(.)
+ to_chat(usr, .)
+
+GLOBAL_VAR(AdminProcCaller)
+GLOBAL_PROTECT(AdminProcCaller)
+GLOBAL_VAR_INIT(AdminProcCallCount, 0)
+GLOBAL_PROTECT(AdminProcCallCount)
+GLOBAL_VAR(LastAdminCalledTargetRef)
+GLOBAL_PROTECT(LastAdminCalledTargetRef)
+GLOBAL_VAR(LastAdminCalledTarget)
+GLOBAL_PROTECT(LastAdminCalledTarget)
+GLOBAL_VAR(LastAdminCalledProc)
+GLOBAL_PROTECT(LastAdminCalledProc)
+GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
+GLOBAL_PROTECT(AdminProcCallSpamPrevention)
+
+/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
+ if(target && procname == "Del")
+ to_chat(usr, "Calling Del() is not allowed")
+ return
+
+ if(target != GLOBAL_PROC && !target.CanProcCall(procname))
+ to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!")
+ return
+ var/current_caller = GLOB.AdminProcCaller
+ var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
+ if(!ckey)
+ CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]")
+ if(current_caller && current_caller != ckey)
+ if(!GLOB.AdminProcCallSpamPrevention[ckey])
+ to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish. ")
+ GLOB.AdminProcCallSpamPrevention[ckey] = TRUE
+ UNTIL(!GLOB.AdminProcCaller)
+ to_chat(usr, "Running your proc ")
+ GLOB.AdminProcCallSpamPrevention -= ckey
+ else
+ UNTIL(!GLOB.AdminProcCaller)
+ GLOB.LastAdminCalledProc = procname
+ if(target != GLOBAL_PROC)
+ GLOB.LastAdminCalledTargetRef = REF(target)
+ GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
+ ++GLOB.AdminProcCallCount
+ . = world.WrapAdminProcCall(target, procname, arguments)
+ if(--GLOB.AdminProcCallCount == 0)
+ GLOB.AdminProcCaller = null
+
+//adv proc call this, ya nerds
+/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
+ if(target == GLOBAL_PROC)
+ return call(procname)(arglist(arguments))
+ else if(target != world)
+ return call(target, procname)(arglist(arguments))
+ else
+ log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
+
+/proc/IsAdminAdvancedProcCall()
+#ifdef TESTING
+ return FALSE
+#else
+ return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey
+#endif
+
+/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf)
+ set category = "Debug"
+ set name = "Atom ProcCall"
+ set waitfor = 0
+
+ if(!check_rights(R_DEBUG))
+ return
+
+ var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null
+ if(!procname)
+ return
+ if(!hascall(A,procname))
+ to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname]. ")
+ return
+ var/list/lst = get_callproc_args()
+ if(!lst)
+ return
+
+ if(!A || !IsValidSrc(A))
+ to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists. ")
+ return
+ log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
+ var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
+ message_admins(msg)
+ admin_ticket_log(A, msg)
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+ var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
+ . = get_callproc_returnval(returnval,procname)
+ if(.)
+ to_chat(usr, .)
+
+/client/proc/get_callproc_args()
+ var/argnum = input("Number of arguments","Number:",0) as num|null
+ if(isnull(argnum))
+ return
+
+ . = list()
+ var/list/named_args = list()
+ while(argnum--)
+ var/named_arg = input("Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null
+ var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
+ if (!value["class"])
+ return
+ if(named_arg)
+ named_args[named_arg] = value["value"]
+ else
+ . += value["value"]
+ if(LAZYLEN(named_args))
+ . += named_args
+
+/client/proc/get_callproc_returnval(returnval,procname)
+ . = ""
+ if(islist(returnval))
+ var/list/returnedlist = returnval
+ . = ""
+ if(returnedlist.len)
+ var/assoc_check = returnedlist[1]
+ if(istext(assoc_check) && (returnedlist[assoc_check] != null))
+ . += "[procname] returned an associative list:"
+ for(var/key in returnedlist)
+ . += "\n[key] = [returnedlist[key]]"
+
+ else
+ . += "[procname] returned a list:"
+ for(var/elem in returnedlist)
+ . += "\n[elem]"
+ else
+ . = "[procname] returned an empty list"
+ . += " "
+
+ else
+ . = "[procname] returned: [!isnull(returnval) ? returnval : "null"] "
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index d8985391e63..b818572835e 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -26,204 +26,6 @@ Because if you select a player mob as owner it tries to do the proc for
But you can call procs that are of type /mob/living/carbon/human/proc/ for that player.
*/
-/client/proc/callproc()
- set category = "Debug"
- set name = "Advanced ProcCall"
- set waitfor = FALSE
-
- if(!check_rights(R_DEBUG))
- return
-
- var/datum/target = null
- var/targetselected = FALSE
- var/returnval = null
-
- if(alert("Proc owned by something?",,"Yes","No") == "Yes")
- targetselected = TRUE
- var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT))
- if (!value["class"] || !value["value"])
- return
- target = value["value"]
-
- var/procpath = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null
- if(!procpath)
- return
-
- //strip away everything but the proc name
- var/list/proclist = splittext(procpath, "/")
- if (!length(proclist))
- return
-
- var/procname = proclist[proclist.len]
- var/proctype = ("verb" in proclist) ? "verb" :"proc"
-
- if(targetselected)
- if(!hascall(target, procname))
- to_chat(usr, "Error: callproc(): type [target.type] has no [proctype] named [procpath]. ")
- return
- else
- procpath = "/[proctype]/[procname]"
- if(!text2path(procpath))
- to_chat(usr, "Error: callproc(): [procpath] does not exist. ")
- return
-
- var/list/lst = get_callproc_args()
- if(!lst)
- return
-
- if(targetselected)
- if(!target)
- to_chat(usr, "Error: callproc(): owner of proc no longer exists. ")
- return
- var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no argument"]."
- log_admin(msg)
- message_admins(msg)
- admin_ticket_log(target, msg)
- returnval = WrapAdminProcCall(target, procname, lst)
- else
- var/msg = "[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no argument"]."
- log_admin(msg)
- message_admins(msg)
- returnval = WrapAdminProcCall(GLOBAL_PROC, procpath, lst) //calling globals needs full qualified name (e.g /proc/foo)
- . = get_callproc_returnval(returnval, procname)
- if(.)
- to_chat(usr, .)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Advanced ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-GLOBAL_VAR(AdminProcCaller)
-GLOBAL_PROTECT(AdminProcCaller)
-GLOBAL_VAR_INIT(AdminProcCallCount, 0)
-GLOBAL_PROTECT(AdminProcCallCount)
-GLOBAL_VAR(LastAdminCalledTargetRef)
-GLOBAL_PROTECT(LastAdminCalledTargetRef)
-GLOBAL_VAR(LastAdminCalledTarget)
-GLOBAL_PROTECT(LastAdminCalledTarget)
-GLOBAL_VAR(LastAdminCalledProc)
-GLOBAL_PROTECT(LastAdminCalledProc)
-GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
-GLOBAL_PROTECT(AdminProcCallSpamPrevention)
-
-/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
- if(target != GLOBAL_PROC && procname == "Del")
- to_chat(usr, "Calling Del() is not allowed ")
- return
-
- if(target != GLOBAL_PROC && !target.CanProcCall(procname))
- to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!")
- return
- var/current_caller = GLOB.AdminProcCaller
- var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
- if(!ckey)
- CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]")
- if(current_caller && current_caller != ckey)
- if(!GLOB.AdminProcCallSpamPrevention[ckey])
- to_chat(usr, "Another set of admin called procs are still running, your proc will be run after theirs finish. ")
- GLOB.AdminProcCallSpamPrevention[ckey] = TRUE
- UNTIL(!GLOB.AdminProcCaller)
- to_chat(usr, "Running your proc ")
- GLOB.AdminProcCallSpamPrevention -= ckey
- else
- UNTIL(!GLOB.AdminProcCaller)
- GLOB.LastAdminCalledProc = procname
- if(target != GLOBAL_PROC)
- GLOB.LastAdminCalledTargetRef = "[REF(target)]"
- GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
- ++GLOB.AdminProcCallCount
- . = world.WrapAdminProcCall(target, procname, arguments)
- if(--GLOB.AdminProcCallCount == 0)
- GLOB.AdminProcCaller = null
-
-//adv proc call this, ya nerds
-/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
- if(target == GLOBAL_PROC)
- return call(procname)(arglist(arguments))
- else if(target != world)
- return call(target, procname)(arglist(arguments))
- else
- log_admin_private("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
-
-/proc/IsAdminAdvancedProcCall()
-#ifdef TESTING
- return FALSE
-#else
- return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey
-#endif
-
-/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf)
- set category = "Debug"
- set name = "Atom ProcCall"
- set waitfor = FALSE
-
- if(!check_rights(R_DEBUG))
- return
-
- var/procname = input("Proc name, eg: fake_blood","Proc:", null) as text|null
- if(!procname)
- return
- if(!hascall(A,procname))
- to_chat(usr, "Error: callproc_datum(): type [A.type] has no proc named [procname]. ")
- return
- var/list/lst = get_callproc_args()
- if(!lst)
- return
-
- if(!A || !IsValidSrc(A))
- to_chat(usr, "Error: callproc_datum(): owner of proc no longer exists. ")
- return
- var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
- log_admin(msg)
- message_admins(msg)
- admin_ticket_log(A, msg)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
- var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
- . = get_callproc_returnval(returnval,procname)
- if(.)
- to_chat(usr, .)
-
-/client/proc/get_callproc_args()
- var/argnum = input("Number of arguments","Number:",0) as num|null
- if(isnull(argnum))
- return
-
- . = list()
- var/list/named_args = list()
- while(argnum--)
- var/named_arg = input("Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null
- var/value = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
- if (!value["class"])
- return
- if(named_arg)
- named_args[named_arg] = value["value"]
- else
- . += value["value"]
- if(LAZYLEN(named_args))
- . += named_args
-
-/client/proc/get_callproc_returnval(returnval,procname)
- . = ""
- if(islist(returnval))
- var/list/returnedlist = returnval
- . = ""
- if(returnedlist.len)
- var/assoc_check = returnedlist[1]
- if(istext(assoc_check) && (returnedlist[assoc_check] != null))
- . += "[procname] returned an associative list:"
- for(var/key in returnedlist)
- . += "\n[key] = [returnedlist[key]]"
-
- else
- . += "[procname] returned a list:"
- for(var/elem in returnedlist)
- . += "\n[elem]"
- else
- . = "[procname] returned an empty list"
- . += " "
-
- else
- . = "[procname] returned: [!isnull(returnval) ? returnval : "null"] "
-
-
/client/proc/Cell()
set category = "Debug"
set name = "Air Status in Location"
@@ -342,65 +144,6 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
else
alert("Invalid mob")
-/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/radio/headset = "HEADSET",
- /obj/item/clothing/head/helmet/space = "SPESSHELMET",
- /obj/item/book/manual = "MANUAL",
- /obj/item/reagent_containers/food/drinks = "DRINK", //longest paths comes first
- /obj/item/reagent_containers/food = "FOOD",
- /obj/item/reagent_containers = "REAGENT_CONTAINERS",
- /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/open = "OPEN",
- /turf/closed = "CLOSED",
- /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]/" /*findtextEx(typename,"[tn]/",1,2)*/ )
- typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/"))
- break
- .[typename] = type
-
-/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
- for(var/key in L)
- var/value = L[key]
- if(findtext("[key]", filter) || findtext("[value]", filter))
- matches[key] = value
- return matches
//TODO: merge the vievars version into this or something maybe mayhaps
/client/proc/cmd_debug_del_all(object as text)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 136a085dd83..601442ed197 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -594,31 +594,6 @@ Traitors and the like can also be revived with the previous role mostly intact.
admin_delete(A)
-/client/proc/admin_delete(datum/D)
- var/atom/A = D
- var/coords = ""
- var/jmp_coords = ""
- if(istype(A))
- var/turf/T = get_turf(A)
- if(T)
- coords = "at [COORD(T)]"
- jmp_coords = "at [ADMIN_COORDJMP(T)]"
- else
- jmp_coords = coords = "in nullspace"
-
- 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] [jmp_coords]")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- if(isturf(D))
- var/turf/T = D
- T.ScrapeAway()
- else
- vv_update_display(D, "deleted", VV_MSG_DELETED)
- qdel(D)
- if(!QDELETED(D))
- vv_update_display(D, "deleted", "")
-
/client/proc/cmd_admin_list_open_jobs()
set category = "Admin"
set name = "Manage Job Slots"
diff --git a/code/modules/admin/view_variables/admin_delete.dm b/code/modules/admin/view_variables/admin_delete.dm
new file mode 100644
index 00000000000..947ad5db2ca
--- /dev/null
+++ b/code/modules/admin/view_variables/admin_delete.dm
@@ -0,0 +1,24 @@
+/client/proc/admin_delete(datum/D)
+ var/atom/A = D
+ var/coords = ""
+ var/jmp_coords = ""
+ if(istype(A))
+ var/turf/T = get_turf(A)
+ if(T)
+ coords = "at [COORD(T)]"
+ jmp_coords = "at [ADMIN_COORDJMP(T)]"
+ else
+ jmp_coords = coords = "in nullspace"
+
+ 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] [jmp_coords]")
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ if(isturf(D))
+ var/turf/T = D
+ T.ScrapeAway()
+ else
+ vv_update_display(D, "deleted", VV_MSG_DELETED)
+ qdel(D)
+ if(!QDELETED(D))
+ vv_update_display(D, "deleted", "")
diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm
new file mode 100644
index 00000000000..23f85cba156
--- /dev/null
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -0,0 +1,76 @@
+#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
+/proc/debug_variable(name, value, level, datum/D, sanitize = TRUE) //if D is a list, name will be index, and value will be assoc value.
+ var/header
+ if(D)
+ if(islist(D))
+ var/index = name
+ if (value)
+ name = D[name] //name is really the index until this line
+ else
+ value = D[name]
+ header = "([VV_HREF_TARGET_1V(D, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_REMOVE, "-", index)]) "
+ else
+ header = " ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_MASSEDIT, "M", name)]) "
+ else
+ header = " "
+
+ var/item
+ if (isnull(value))
+ item = "[VV_HTML_ENCODE(name)] = null "
+
+ else if (istext(value))
+ item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\" "
+
+ else if (isicon(value))
+ #ifdef VARSICON
+ var/icon/I = icon(value)
+ var/rnd = rand(1,10000)
+ var/rname = "tmp[REF(I)][rnd].png"
+ usr << browse_rsc(I, rname)
+ item = "[VV_HTML_ENCODE(name)] = ([value] ) "
+ #else
+ item = "[VV_HTML_ENCODE(name)] = /icon ([value] )"
+ #endif
+
+ else if (isfile(value))
+ item = "[VV_HTML_ENCODE(name)] = '[value]' "
+
+ else if (istype(value, /datum))
+ var/datum/DV = value
+ if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it.
+ item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV] [DV.type]"
+ else
+ item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV.type]"
+
+ else if (islist(value))
+ var/list/L = value
+ var/list/items = list()
+
+ if (L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD)))
+ 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 (isnull(val)) // we still want to display non-null false values, such as 0 or ""
+ val = key
+ key = i
+
+ items += debug_variable(key, val, level + 1, sanitize = sanitize)
+
+ item = "[VV_HTML_ENCODE(name)] = /list ([L.len]) "
+ else
+ item = "[VV_HTML_ENCODE(name)] = /list ([L.len]) "
+
+ else if (name in GLOB.bitfields)
+ var/list/flags = list()
+ for (var/i in GLOB.bitfields[name])
+ if (value & GLOB.bitfields[name][i])
+ flags += i
+ item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(jointext(flags, ", "))]"
+ else
+ item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)] "
+
+ return "[header][item] "
+
+#undef VV_HTML_ENCODE
diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm
new file mode 100644
index 00000000000..3f90002edc0
--- /dev/null
+++ b/code/modules/admin/view_variables/get_variables.dm
@@ -0,0 +1,271 @@
+/client/proc/vv_get_class(var_name, var_value)
+ if(isnull(var_value))
+ . = VV_NULL
+
+ else if(isnum(var_value))
+ if(var_name in GLOB.bitfields)
+ . = VV_BITFIELD
+ else
+ . = VV_NUM
+
+ 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, /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
+ . = VV_NULL
+
+/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name)
+ . = 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_FILE,
+ VV_NEW_ATOM,
+ VV_NEW_DATUM,
+ VV_NEW_TYPE,
+ VV_NEW_LIST,
+ VV_NULL,
+ VV_RESTORE_DEFAULT,
+ VV_TEXT_LOCATE,
+ VV_PROCCALL_RETVAL,
+ )
+
+ var/markstring
+ if(!(VV_MARKED_DATUM in restricted_classes))
+ markstring = "[VV_MARKED_DATUM] (CURRENT: [(istype(holder) && istype(holder.marked_datum))? holder.marked_datum.type : "NULL"])"
+ classes += markstring
+
+ 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"] == markstring)
+ .["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_BITFIELD)
+ .["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value)
+ 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_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 GLOB.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_PROCCALL_RETVAL)
+ var/list/get_retval = list()
+ callproc_blocking(get_retval)
+ .["value"] = get_retval[1] //should have been set in proccall!
+ if(.["value"] == null)
+ .["class"] = null
+ return
+
+ if(VV_NEW_ATOM)
+ var/type = pick_closest_path(FALSE)
+ if(!type)
+ .["class"] = null
+ return
+ .["type"] = type
+ var/atom/newguy = new type()
+ newguy.datum_flags |= DF_VAR_EDITED
+ .["value"] = newguy
+
+ if(VV_NEW_DATUM)
+ var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types())
+ if(!type)
+ .["class"] = null
+ return
+ .["type"] = type
+ var/datum/newguy = new type()
+ newguy.datum_flags |= DF_VAR_EDITED
+ .["value"] = newguy
+
+ 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
+ var/datum/newguy = new type()
+ if(istype(newguy))
+ newguy.datum_flags |= DF_VAR_EDITED
+ .["value"] = newguy
+
+ if(VV_NEW_LIST)
+ .["value"] = list()
+ .["type"] = /list
+
+ if(VV_TEXT_LOCATE)
+ var/datum/D
+ do
+ var/ref = input("Enter reference:", "Reference") as null|text
+ if(!ref)
+ break
+ D = locate(ref)
+ if(!D)
+ alert("Invalid ref!")
+ continue
+ if(!D.can_vv_mark())
+ alert("Datum can not be marked!")
+ continue
+ while(!D)
+ .["type"] = D.type
+ .["value"] = D
diff --git a/code/modules/admin/view_variables/mark_datum.dm b/code/modules/admin/view_variables/mark_datum.dm
new file mode 100644
index 00000000000..9f1b3337500
--- /dev/null
+++ b/code/modules/admin/view_variables/mark_datum.dm
@@ -0,0 +1,12 @@
+/client/proc/mark_datum(datum/D)
+ if(!holder)
+ return
+ if(holder.marked_datum)
+ vv_update_display(holder.marked_datum, "marked", "")
+ holder.marked_datum = D
+ vv_update_display(D, "marked", VV_MSG_MARKED)
+
+/client/proc/mark_datum_mapview(datum/D as mob|obj|turf|area in view(view))
+ set category = "Debug"
+ set name = "Mark Object"
+ mark_datum(D)
diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/view_variables/mass_edit_variables.dm
similarity index 94%
rename from code/modules/admin/verbs/massmodvar.dm
rename to code/modules/admin/view_variables/mass_edit_variables.dm
index 75050b5e621..e29580b3b27 100644
--- a/code/modules/admin/verbs/massmodvar.dm
+++ b/code/modules/admin/view_variables/mass_edit_variables.dm
@@ -1,265 +1,271 @@
-/client/proc/cmd_mass_modify_object_variables(atom/A, var_name)
- set category = "Debug"
- set name = "Mass Edit Variables"
- set desc="(target) Edit all instances of a target item's variables"
-
- var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL items)
-
- if(!check_rights(R_VAREDIT))
- return
-
- if(A && A.type)
- method = vv_subtype_prompt(A.type)
-
- src.massmodify_variables(A, var_name, method)
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Mass Edit Variables") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/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)
- 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 || !O.can_vv_get(variable))
- return
- var/default
- var/var_value = O.vars[variable]
-
- if(variable in GLOB.VVckey_edit)
- to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.")
- return
- if(variable in GLOB.VVlocked)
- if(!check_rights(R_DEBUG))
- return
- if(variable in GLOB.VVicon_edit_lock)
- if(!check_rights(R_FUN|R_DEBUG))
- return
- if(variable in GLOB.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
-
- default = vv_get_class(variable, var_value)
-
- if(isnull(default))
- to_chat(src, "Unable to determine variable type.")
- else
- to_chat(src, "Variable appears to be [uppertext(default)] .")
-
- to_chat(src, "Variable contains: [var_value]")
-
- 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]")
-
- 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
-
- if (class == VV_MESSAGE)
- class = VV_TEXT
-
- 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(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]]")
-
- 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]]")
-
- 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
- many = FALSE
-
- 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()
-
- if (D.vv_edit_var(variable, new_value) != FALSE)
- accepted++
- else
- rejected++
- new_value = null
- CHECK_TICK
-
- 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
-
-
- 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")
-
- log_world("### 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)")
-
-
-/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 GLOB.mob_list)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /obj/machinery/door))
- for(var/obj/machinery/door/thing in GLOB.airlocks)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /obj/machinery))
- for(var/obj/machinery/thing in GLOB.machines)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /obj))
- for(var/obj/thing in world)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /atom/movable))
- for(var/atom/movable/thing in world)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /turf))
- for(var/turf/thing in world)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /atom))
- for(var/atom/thing in world)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /client))
- for(var/client/thing in GLOB.clients)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else if (ispath(T, /datum))
- for(var/datum/thing)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
-
- else
- for(var/datum/thing in world)
- if (typecache[thing.type])
- . += thing
- CHECK_TICK
+/client/proc/cmd_mass_modify_object_variables(atom/A, var_name)
+ set category = "Debug"
+ set name = "Mass Edit Variables"
+ set desc="(target) Edit all instances of a target item's variables"
+
+ var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL items)
+
+ if(!check_rights(R_VAREDIT))
+ return
+
+ if(A && A.type)
+ method = vv_subtype_prompt(A.type)
+
+ src.massmodify_variables(A, var_name, method)
+ SSblackbox.record_feedback("tally", "admin_verb", 1, "Mass Edit Variables") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/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)
+ 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 || !O.can_vv_get(variable))
+ return
+ var/default
+ var/var_value = O.vars[variable]
+
+ if(variable in GLOB.VVckey_edit)
+ to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.")
+ return
+ if(variable in GLOB.VVlocked)
+ if(!check_rights(R_DEBUG))
+ return
+ if(variable in GLOB.VVicon_edit_lock)
+ if(!check_rights(R_FUN|R_DEBUG))
+ return
+ if(variable in GLOB.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
+
+ default = vv_get_class(variable, var_value)
+
+ if(isnull(default))
+ to_chat(src, "Unable to determine variable type.")
+ else
+ to_chat(src, "Variable appears to be [uppertext(default)] .")
+
+ to_chat(src, "Variable contains: [var_value]")
+
+ 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]")
+
+ 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
+
+ if (class == VV_MESSAGE)
+ class = VV_TEXT
+
+ 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(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]]")
+
+ 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]]")
+
+ 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
+ many = FALSE
+
+ 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()
+
+ if (D.vv_edit_var(variable, new_value) != FALSE)
+ accepted++
+ else
+ rejected++
+ new_value = null
+ CHECK_TICK
+
+ 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
+
+
+ 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")
+
+ log_world("### 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)")
+
+//not using global lists as vv is a debug function and debug functions should rely on as less things as possible.
+/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 world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /obj/machinery/door))
+ for(var/obj/machinery/door/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /obj/machinery))
+ for(var/obj/machinery/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /obj/item))
+ for(var/obj/item/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /obj))
+ for(var/obj/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /atom/movable))
+ for(var/atom/movable/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /turf))
+ for(var/turf/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /atom))
+ for(var/atom/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /client))
+ for(var/client/thing in GLOB.clients)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else if (ispath(T, /datum))
+ for(var/datum/thing)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
+
+ else
+ for(var/datum/thing in world)
+ if (typecache[thing.type])
+ . += thing
+ CHECK_TICK
diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/view_variables/modify_variables.dm
similarity index 58%
rename from code/modules/admin/verbs/modifyvariables.dm
rename to code/modules/admin/view_variables/modify_variables.dm
index 485cc2f6a53..aea991ae390 100644
--- a/code/modules/admin/verbs/modifyvariables.dm
+++ b/code/modules/admin/view_variables/modify_variables.dm
@@ -1,644 +1,388 @@
-GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "virus", "viruses", "cuffed", "last_eaten", "unlock_content", "force_ending"))
-GLOBAL_PROTECT(VVlocked)
-GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize"))
-GLOBAL_PROTECT(VVicon_edit_lock)
-GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey"))
-GLOBAL_PROTECT(VVckey_edit)
-GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "bound_height", "bound_width", "bound_x", "bound_y"))
-GLOBAL_PROTECT(VVpixelmovement)
-
-
-/client/proc/vv_get_class(var/var_name, var/var_value)
- if(isnull(var_value))
- . = VV_NULL
-
- else if (isnum(var_value))
- if (var_name in GLOB.bitfields)
- . = VV_BITFIELD
- else
- . = VV_NUM
-
- 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, /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
- . = VV_NULL
-
-/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name)
- . = 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_FILE,
- VV_NEW_ATOM,
- VV_NEW_DATUM,
- VV_NEW_TYPE,
- VV_NEW_LIST,
- VV_NULL,
- VV_RESTORE_DEFAULT
- )
-
- 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_BITFIELD)
- .["value"] = input_bitfield(usr, "Editing bitfield: [var_name]", var_name, current_value)
- 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_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 GLOB.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
- var/atom/newguy = new type()
- newguy.datum_flags |= DF_VAR_EDITED
- .["value"] = newguy
-
- if (VV_NEW_DATUM)
- var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types())
- if (!type)
- .["class"] = null
- return
- .["type"] = type
- var/datum/newguy = new type()
- newguy.datum_flags |= DF_VAR_EDITED
- .["value"] = newguy
-
- 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
- var/datum/newguy = new type()
- if(istype(newguy))
- newguy.datum_flags |= DF_VAR_EDITED
- .["value"] = newguy
-
-
- 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) //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 += var_value
-
- switch(alert("Would you like to associate a value with the list entry?",,"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.")
- 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.")
- 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/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_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 = uniqueList(L)
- if (!O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
- 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.")
- 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 = alert(src, "Do you want to edit the key or its 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]
-
- default = vv_get_class(objectvar, variable)
-
- to_chat(src, "Variable appears to be [uppertext(default)] .")
-
- to_chat(src, "Variable contains: [variable]")
-
- 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]")
-
- 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.")
- 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(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_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
- if(param_var_name in GLOB.VVpixelmovement)
- if(!check_rights(R_DEBUG))
- return FALSE
- var/prompt = alert(usr, "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 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])")
- return
- variable = param_var_name
-
- else
- 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
- if(!variable)
- return
-
- if(!O.can_vv_get(variable))
- return
-
- var_value = O.vars[variable]
- if(!vv_varname_lockcheck(variable))
- return
- if(istype(O, /datum/armor))
- var/prompt = alert(src, "Editing this var changes this value on potentially thousands of items that share the same combination of armor values. If you want to edit the armor of just one item, use the \"Modify armor values\" dropdown item", "DANGER", "ABORT ", "Continue", " ABORT")
- if (prompt != "Continue")
- return
-
-
- var/default = vv_get_class(variable, var_value)
-
- if(isnull(default))
- to_chat(src, "Unable to determine variable type.")
- else
- to_chat(src, "Variable appears to be [uppertext(default)] .")
-
- to_chat(src, "Variable contains: [var_value]")
-
- 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]")
-
- 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.")
- 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
+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 = 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) //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 += var_value
+
+ switch(alert("Would you like to associate a value with the list entry?",,"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.")
+ 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.")
+ 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/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()
+ listclearnulls(L)
+ if (!O.vv_edit_var(objectvar, L))
+ to_chat(src, "Your edit was rejected by the object.")
+ 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 = uniqueList(L)
+ if (!O.vv_edit_var(objectvar, L))
+ to_chat(src, "Your edit was rejected by the object.")
+ 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.")
+ 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 = alert(src, "Do you want to edit the key or its 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
+ 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
+ var/found = L[variable]
+ if(!isnull(found))
+ old_assoc_value = found
+ //
+
+ default = vv_get_class(objectvar, variable)
+
+ to_chat(src, "Variable appears to be [uppertext(default)] .")
+
+ to_chat(src, "Variable contains: [variable]")
+
+ 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]")
+
+ 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.")
+ 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.")
+ 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])")
+ return
+ variable = param_var_name
+
+ else
+ 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
+ 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.")
+ else
+ to_chat(src, "Variable appears to be [uppertext(default)] .")
+
+ to_chat(src, "Variable contains: [var_value]")
+
+ 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]")
+
+ 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.")
+ return
+ vv_update_display(O, "varedited", VV_MSG_EDITED)
+ 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
diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm
new file mode 100644
index 00000000000..3fc8e6b11bd
--- /dev/null
+++ b/code/modules/admin/view_variables/topic.dm
@@ -0,0 +1,129 @@
+//DO NOT ADD MORE TO THIS FILE.
+//Use vv_do_topic() for datums!
+/client/proc/view_var_Topic(href, href_list, hsrc)
+ if( (usr.client != src) || !src.holder || !holder.CheckAdminHref(href, href_list))
+ return
+ var/target = GET_VV_TARGET
+ vv_do_basic(target, href_list, href)
+ if(istype(target, /datum))
+ var/datum/D = target
+ D.vv_do_topic(href_list)
+ else if(islist(target))
+ vv_do_list(target, href_list)
+ if(href_list["Vars"])
+ debug_variables(locate(href_list["Vars"]))
+
+//Stuff below aren't in dropdowns/etc.
+
+ if(check_rights(R_VAREDIT))
+
+ //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records).
+
+ if(href_list["rename"])
+ if(!check_rights(NONE))
+ return
+
+ var/mob/M = locate(href_list["rename"]) in GLOB.mob_list
+ if(!istype(M))
+ to_chat(usr, "This can only be used on instances of type /mob")
+ return
+
+ var/new_name = stripped_input(usr,"What would you like to name this mob?","Input a name",M.real_name,MAX_NAME_LEN)
+ if( !new_name || !M )
+ return
+
+ message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].")
+ M.fully_replace_character_name(M.real_name,new_name)
+ vv_update_display(M, "name", new_name)
+ vv_update_display(M, "real_name", M.real_name || "No real name")
+
+ else if(href_list["rotatedatum"])
+ if(!check_rights(NONE))
+ return
+
+ var/atom/A = locate(href_list["rotatedatum"])
+ if(!istype(A))
+ to_chat(usr, "This can only be done to instances of type /atom")
+ return
+
+ switch(href_list["rotatedir"])
+ if("right")
+ A.setDir(turn(A.dir, -45))
+ if("left")
+ A.setDir(turn(A.dir, 45))
+ vv_update_display(A, "dir", dir2text(A.dir))
+
+
+ else if(href_list["makehuman"])
+ if(!check_rights(R_SPAWN))
+ return
+
+ var/mob/living/carbon/monkey/Mo = locate(href_list["makehuman"]) in GLOB.mob_list
+ if(!istype(Mo))
+ to_chat(usr, "This can only be done to instances of type /mob/living/carbon/monkey")
+ return
+
+ if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
+ return
+ if(!Mo)
+ to_chat(usr, "Mob doesn't exist anymore")
+ return
+ holder.Topic(href, list("humanone"=href_list["makehuman"]))
+
+ else if(href_list["adjustDamage"] && href_list["mobToDamage"])
+ if(!check_rights(NONE))
+ return
+
+ var/mob/living/L = locate(href_list["mobToDamage"]) in GLOB.mob_list
+ if(!istype(L))
+ return
+
+ var/Text = href_list["adjustDamage"]
+
+ var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num
+
+ if(!L)
+ to_chat(usr, "Mob doesn't exist anymore")
+ return
+
+ var/newamt
+ switch(Text)
+ if("brute")
+ L.adjustBruteLoss(amount)
+ newamt = L.getBruteLoss()
+ if("fire")
+ L.adjustFireLoss(amount)
+ newamt = L.getFireLoss()
+ if("toxin")
+ L.adjustToxLoss(amount)
+ newamt = L.getToxLoss()
+ if("oxygen")
+ L.adjustOxyLoss(amount)
+ newamt = L.getOxyLoss()
+ if("brain")
+ L.adjustOrganLoss(ORGAN_SLOT_BRAIN, amount)
+ newamt = L.getOrganLoss(ORGAN_SLOT_BRAIN)
+ if("clone")
+ L.adjustCloneLoss(amount)
+ newamt = L.getCloneLoss()
+ if("stamina")
+ L.adjustStaminaLoss(amount)
+ newamt = L.getStaminaLoss()
+ else
+ to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
+ return
+
+ if(amount != 0)
+ var/log_msg = "[key_name(usr)] dealt [amount] amount of [Text] damage to [key_name(L)]"
+ message_admins("[key_name(usr)] dealt [amount] amount of [Text] damage to [ADMIN_LOOKUPFLW(L)]")
+ log_admin(log_msg)
+ admin_ticket_log(L, "[log_msg] ")
+ vv_update_display(L, Text, "[newamt]")
+
+
+ //Finally, refresh if something modified the list.
+ if(href_list["datumrefresh"])
+ var/datum/DAT = locate(href_list["datumrefresh"])
+ if(istype(DAT, /datum) || istype(DAT, /client))
+ debug_variables(DAT)
+
diff --git a/code/modules/admin/view_variables/topic_basic.dm b/code/modules/admin/view_variables/topic_basic.dm
new file mode 100644
index 00000000000..2a0f3d7302f
--- /dev/null
+++ b/code/modules/admin/view_variables/topic_basic.dm
@@ -0,0 +1,51 @@
+//Not using datum.vv_do_topic for very basic/low level debug things, incase the datum's vv_do_topic is runtiming/whatnot.
+/client/proc/vv_do_basic(datum/target, href_list)
+ var/target_var = GET_VV_VAR_TARGET
+ if(check_rights(R_VAREDIT))
+ if(target_var)
+ if(href_list[VV_HK_BASIC_EDIT])
+ if(!modify_variables(target, target_var, 1))
+ return
+ switch(target_var)
+ if("name")
+ vv_update_display(target, "name", "[target]")
+ if("dir")
+ var/atom/A = target
+ if(istype(A))
+ vv_update_display(target, "dir", dir2text(A.dir) || A.dir)
+ if("ckey")
+ var/mob/living/L = target
+ if(istype(L))
+ vv_update_display(target, "ckey", L.ckey || "No ckey")
+ if("real_name")
+ var/mob/living/L = target
+ if(istype(L))
+ vv_update_display(target, "real_name", L.real_name || "No real name")
+ if(href_list[VV_HK_BASIC_CHANGE])
+ modify_variables(target, target_var, 0)
+ if(href_list[VV_HK_BASIC_MASSEDIT])
+ cmd_mass_modify_object_variables(target, target_var)
+ if(check_rights(R_ADMIN, FALSE))
+ if(href_list[VV_HK_EXPOSE])
+ var/value = vv_get_value(VV_CLIENT)
+ if (value["class"] != VV_CLIENT)
+ return
+ var/client/C = value["value"]
+ if (!C)
+ return
+ if(!target)
+ to_chat(usr, "The object you tried to expose to [C] no longer exists (nulled or hard-deled) ")
+ return
+ message_admins("[key_name_admin(usr)] Showed [key_name_admin(C)] a VV window ")
+ log_admin("Admin [key_name(usr)] Showed [key_name(C)] a VV window of a [target]")
+ to_chat(C, "[holder.fakekey ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
+ C.debug_variables(target)
+ if(check_rights(R_DEBUG))
+ if(href_list[VV_HK_DELETE])
+ usr.client.admin_delete(target)
+ if (isturf(src)) // show the turf that took its place
+ usr.client.debug_variables(src)
+ if(href_list[VV_HK_MARK])
+ usr.client.mark_datum(target)
+ if(href_list[VV_HK_CALLPROC])
+ usr.client.callproc_datum(target)
diff --git a/code/modules/admin/view_variables/topic_list.dm b/code/modules/admin/view_variables/topic_list.dm
new file mode 100644
index 00000000000..349d9da698a
--- /dev/null
+++ b/code/modules/admin/view_variables/topic_list.dm
@@ -0,0 +1,43 @@
+//LISTS - CAN NOT DO VV_DO_TOPIC BECAUSE LISTS AREN'T DATUMS :(
+/client/proc/vv_do_list(list/target, href_list)
+ var/target_index = text2num(GET_VV_VAR_TARGET)
+ if(check_rights(R_VAREDIT))
+ if(target_index)
+ if(href_list[VV_HK_LIST_EDIT])
+ mod_list(target, null, "list", "contents", target_index, autodetect_class = TRUE)
+ if(href_list[VV_HK_LIST_CHANGE])
+ mod_list(target, null, "list", "contents", target_index, autodetect_class = FALSE)
+ if(href_list[VV_HK_LIST_REMOVE])
+ var/variable = target[target_index]
+ var/prompt = alert("Do you want to remove item number [target_index] from list?", "Confirm", "Yes", "No")
+ if (prompt != "Yes")
+ return
+ target.Cut(target_index, target_index+1)
+ log_world("### 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]")
+ if(href_list[VV_HK_LIST_ADD])
+ mod_list_add(target, null, "list", "contents")
+ if(href_list[VV_HK_LIST_ERASE_DUPES])
+ uniqueList_inplace(target)
+ log_world("### 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")
+ if(href_list[VV_HK_LIST_ERASE_NULLS])
+ listclearnulls(target)
+ log_world("### 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")
+ if(href_list[VV_HK_LIST_SET_LENGTH])
+ var/value = vv_get_value(VV_NUM)
+ if (value["class"] != VV_NUM || value["value"] > max(50000, target.len)) //safety - would rather someone not put an extra 0 and erase the server's memory lmao.
+ return
+ target.len = value["value"]
+ log_world("### ListVarEdit by [src]: /list len: [target.len]")
+ log_admin("[key_name(src)] modified list's len: [target.len]")
+ message_admins("[key_name_admin(src)] modified list's len: [target.len]")
+ if(href_list[VV_HK_LIST_SHUFFLE])
+ shuffle_inplace(target)
+ log_world("### 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")
diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm
new file mode 100644
index 00000000000..2c8ad4f3670
--- /dev/null
+++ b/code/modules/admin/view_variables/view_variables.dm
@@ -0,0 +1,271 @@
+/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(!usr.client || !usr.client.holder) //This is usr because admins can call the proc on other clients, even if they're not admins, to show them VVs.
+ to_chat(usr, "You need to be an administrator to access this. ")
+ return
+
+ if(!D)
+ return
+
+ var/islist = islist(D)
+ if(!islist && !istype(D))
+ return
+
+ var/title = ""
+ var/refid = REF(D)
+ var/icon/sprite
+ var/hash
+
+ var/type = islist? /list : D.type
+
+ if(istype(D, /atom))
+ sprite = getFlatIcon(D)
+ hash = md5(sprite)
+ src << browse_rsc(sprite, "vv[hash].png")
+
+ title = "[D] ([REF(D)]) = [type]"
+ var/formatted_type = replacetext("[type]", "/", "/")
+
+ var/sprite_text
+ if(sprite)
+ sprite_text = " "
+ var/list/header = islist(D)? list("/list ") : D.vv_get_header()
+
+ var/marked_line
+ if(holder && holder.marked_datum && holder.marked_datum == D)
+ marked_line = VV_MSG_MARKED
+ var/varedited_line
+ if(!islist && (D.datum_flags & DF_VAR_EDITED))
+ varedited_line = VV_MSG_EDITED
+ var/deleted_line
+ if(!islist && D.gc_destroyed)
+ deleted_line = VV_MSG_DELETED
+
+ var/list/dropdownoptions
+ if (islist)
+ dropdownoptions = list(
+ "---",
+ "Add Item" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_ADD),
+ "Remove Nulls" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_ERASE_NULLS),
+ "Remove Dupes" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_ERASE_DUPES),
+ "Set len" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_SET_LENGTH),
+ "Shuffle" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_SHUFFLE),
+ "Show VV To Player" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_EXPOSE),
+ "---"
+ )
+ for(var/i in 1 to length(dropdownoptions))
+ var/name = dropdownoptions[i]
+ var/link = dropdownoptions[name]
+ dropdownoptions[i] = "[name] "
+ else
+ dropdownoptions = D.vv_get_dropdown()
+
+ var/list/names = list()
+ if(!islist)
+ for(var/V in D.vars)
+ names += V
+ sleep(1)
+
+ 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) && IS_VALID_ASSOC_KEY(key))
+ value = L[key]
+ variable_html += debug_variable(i, value, 0, L)
+ else
+ names = sortList(names)
+ for(var/V in names)
+ if(D.can_vv_get(V))
+ variable_html += D.vv_get_var(V)
+
+ var/html = {"
+
+
+ [title]
+
+
+
+
+
+
+
+
+
+
+
+ [sprite_text]
+
+ [header.Join()]
+
+
+
+
+
+ [formatted_type]
+ [marked_line]
+ [varedited_line]
+ [deleted_line]
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+ [variable_html.Join()]
+
+
+
+
+"}
+ src << browse(html, "window=variables[refid];size=475x650")
+
+/client/proc/vv_update_display(datum/D, span, content)
+ src << output("[span]:[content]", "variables[REF(D)].browser:replace_span")
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index f9e42bb3df9..82674d4dbdd 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -395,17 +395,13 @@
/datum/spacevine_controller/vv_get_dropdown()
. = ..()
- . += "---"
- .["Delete Vines"] = "?_src_=[REF(src)];[HrefToken()];purge_vines=1"
+ VV_DROPDOWN_OPTION(VV_HK_SPACEVINE_PURGE, "Delete Vines")
-/datum/spacevine_controller/Topic(href, href_list)
- if(..() || !check_rights(R_ADMIN, FALSE) || !usr.client.holder.CheckAdminHref(href, href_list))
- return
-
- if(href_list["purge_vines"])
- if(alert(usr, "Are you sure you want to delete this spacevine cluster?", "Delete Vines", "Yes", "No") != "Yes")
- return
- DeleteVines()
+/datum/spacevine_controller/vv_do_topic(href_list)
+ . = ..()
+ if(href_list[VV_HK_SPACEVINE_PURGE])
+ if(alert(usr, "Are you sure you want to delete this spacevine cluster?", "Delete Vines", "Yes", "No") == "Yes")
+ DeleteVines()
/datum/spacevine_controller/proc/DeleteVines() //this is kill
QDEL_LIST(vines) //this will also qdel us
diff --git a/code/modules/keybindings/bindings_atom.dm b/code/modules/keybindings/bindings_atom.dm
index 4aac76bd67e..5f3e879237d 100644
--- a/code/modules/keybindings/bindings_atom.dm
+++ b/code/modules/keybindings/bindings_atom.dm
@@ -15,4 +15,4 @@
movement_dir &= ~(NORTH|SOUTH)
if((movement_dir & EAST) && (movement_dir & WEST))
movement_dir &= ~(EAST|WEST)
- user.Move(get_step(src, movement_dir), movement_dir)
\ No newline at end of file
+ user.Move(get_step(src, movement_dir), movement_dir)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index c5ab00c37c8..66270d97d8f 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -895,14 +895,121 @@
/mob/living/carbon/vv_get_dropdown()
. = ..()
- . += "---"
- .["Make AI"] = "?_src_=vars;[HrefToken()];makeai=[REF(src)]"
- .["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=[REF(src)]"
- .["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=[REF(src)]"
- .["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=[REF(src)]"
- .["Give martial arts"] = "?_src_=vars;[HrefToken()];givemartialart=[REF(src)]"
- .["Give brain trauma"] = "?_src_=vars;[HrefToken()];givetrauma=[REF(src)]"
- .["Cure brain traumas"] = "?_src_=vars;[HrefToken()];curetraumas=[REF(src)]"
+ VV_DROPDOWN_OPTION("", "---------")
+ VV_DROPDOWN_OPTION(VV_HK_MAKE_AI, "Make AI")
+ VV_DROPDOWN_OPTION(VV_HK_MODIFY_BODYPART, "Modify bodypart")
+ VV_DROPDOWN_OPTION(VV_HK_MODIFY_ORGANS, "Modify organs")
+ VV_DROPDOWN_OPTION(VV_HK_HALLUCINATION, "Hallucinate")
+ VV_DROPDOWN_OPTION(VV_HK_MARTIAL_ART, "Give Martial Arts")
+ VV_DROPDOWN_OPTION(VV_HK_GIVE_TRAUMA, "Give Brain Trauma")
+ VV_DROPDOWN_OPTION(VV_HK_CURE_TRAUMA, "Cure Brain Traumas")
+
+/mob/living/carbon/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_MODIFY_BODYPART])
+ if(!check_rights(R_SPAWN))
+ return
+ var/edit_action = input(usr, "What would you like to do?","Modify Body Part") as null|anything in list("add","remove", "augment")
+ if(!edit_action)
+ return
+ var/list/limb_list = list()
+ if(edit_action == "remove" || edit_action == "augment")
+ for(var/obj/item/bodypart/B in bodyparts)
+ limb_list += B.body_zone
+ if(edit_action == "remove")
+ limb_list -= BODY_ZONE_CHEST
+ else
+ limb_list = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+ for(var/obj/item/bodypart/B in bodyparts)
+ limb_list -= B.body_zone
+ var/result = input(usr, "Please choose which body part to [edit_action]","[capitalize(edit_action)] Body Part") as null|anything in limb_list
+ if(result)
+ var/obj/item/bodypart/BP = get_bodypart(result)
+ switch(edit_action)
+ if("remove")
+ if(BP)
+ BP.drop_limb()
+ else
+ to_chat(usr, "[src] doesn't have such bodypart.")
+ if("add")
+ if(BP)
+ to_chat(usr, "[src] already has such bodypart.")
+ else
+ if(!regenerate_limb(result))
+ to_chat(usr, "[src] cannot have such bodypart.")
+ if("augment")
+ if(ishuman(src))
+ if(BP)
+ BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE)
+ else
+ to_chat(usr, "[src] doesn't have such bodypart.")
+ else
+ to_chat(usr, "Only humans can be augmented.")
+ admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src]")
+ if(href_list[VV_HK_MAKE_AI])
+ if(!check_rights(R_SPAWN))
+ return
+ if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
+ return
+ usr.client.holder.Topic("vv_override", list("makeai"=href_list[VV_HK_TARGET]))
+ if(href_list[VV_HK_MODIFY_ORGANS])
+ if(!check_rights(NONE))
+ return
+ usr.client.manipulate_organs(src)
+ if(href_list[VV_HK_MARTIAL_ART])
+ if(!check_rights(NONE))
+ return
+ var/list/artpaths = subtypesof(/datum/martial_art)
+ var/list/artnames = list()
+ for(var/i in artpaths)
+ var/datum/martial_art/M = i
+ artnames[initial(M.name)] = M
+ var/result = input(usr, "Choose the martial art to teach","JUDO CHOP") as null|anything in artnames
+ if(!usr)
+ return
+ if(QDELETED(src))
+ to_chat(usr, "Mob doesn't exist anymore")
+ return
+ if(result)
+ var/chosenart = artnames[result]
+ var/datum/martial_art/MA = new chosenart
+ MA.teach(src)
+ log_admin("[key_name(usr)] has taught [MA] to [key_name(src)].")
+ message_admins("[key_name_admin(usr)] has taught [MA] to [key_name_admin(src)]. ")
+ if(href_list[VV_HK_GIVE_TRAUMA])
+ if(!check_rights(NONE))
+ return
+ var/list/traumas = subtypesof(/datum/brain_trauma)
+ var/result = input(usr, "Choose the brain trauma to apply","Traumatize") as null|anything in traumas
+ if(!usr)
+ return
+ if(QDELETED(src))
+ to_chat(usr, "Mob doesn't exist anymore")
+ return
+ if(!result)
+ return
+ var/datum/brain_trauma/BT = gain_trauma(result)
+ if(BT)
+ log_admin("[key_name(usr)] has traumatized [key_name(src)] with [BT.name]")
+ message_admins("[key_name_admin(usr)] has traumatized [key_name_admin(src)] with [BT.name]. ")
+ if(href_list[VV_HK_CURE_TRAUMA])
+ if(!check_rights(NONE))
+ return
+ cure_all_traumas(TRAUMA_RESILIENCE_ABSOLUTE)
+ log_admin("[key_name(usr)] has cured all traumas from [key_name(src)].")
+ message_admins("[key_name_admin(usr)] has cured all traumas from [key_name_admin(src)]. ")
+ if(href_list[VV_HK_HALLUCINATION])
+ if(!check_rights(NONE))
+ return
+ var/list/hallucinations = subtypesof(/datum/hallucination)
+ var/result = input(usr, "Choose the hallucination to apply","Send Hallucination") as null|anything in hallucinations
+ if(!usr)
+ return
+ if(QDELETED(src))
+ to_chat(usr, "Mob doesn't exist anymore")
+ return
+ if(result)
+ new result(src, TRUE)
/mob/living/carbon/can_resist()
return bodyparts.len > 2 && ..()
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index b758ee3ac1e..d52e9f61a90 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -804,15 +804,95 @@
/mob/living/carbon/human/vv_get_dropdown()
. = ..()
- . += "---"
- .["Make monkey"] = "?_src_=vars;[HrefToken()];makemonkey=[REF(src)]"
- .["Set Species"] = "?_src_=vars;[HrefToken()];setspecies=[REF(src)]"
- .["Make cyborg"] = "?_src_=vars;[HrefToken()];makerobot=[REF(src)]"
- .["Make alien"] = "?_src_=vars;[HrefToken()];makealien=[REF(src)]"
- .["Make slime"] = "?_src_=vars;[HrefToken()];makeslime=[REF(src)]"
- .["Toggle Purrbation"] = "?_src_=vars;[HrefToken()];purrbation=[REF(src)]"
- .["Copy outfit"] = "?_src_=vars;[HrefToken()];copyoutfit=[REF(src)]"
- .["Add/Remove Quirks"] = "?_src_=vars;[HrefToken()];modquirks=[REF(src)]"
+ VV_DROPDOWN_OPTION("", "---------")
+ VV_DROPDOWN_OPTION(VV_HK_COPY_OUTFIT, "Copy Outfit")
+ VV_DROPDOWN_OPTION(VV_HK_MOD_QUIRKS, "Add/Remove Quirks")
+ VV_DROPDOWN_OPTION(VV_HK_MAKE_MONKEY, "Make Monkey")
+ VV_DROPDOWN_OPTION(VV_HK_MAKE_CYBORG, "Make Cyborg")
+ VV_DROPDOWN_OPTION(VV_HK_MAKE_SLIME, "Make Slime")
+ VV_DROPDOWN_OPTION(VV_HK_MAKE_ALIEN, "Make Alien")
+ VV_DROPDOWN_OPTION(VV_HK_SET_SPECIES, "Set Species")
+ VV_DROPDOWN_OPTION(VV_HK_PURRBATION, "Toggle Purrbation")
+
+/mob/living/carbon/human/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_COPY_OUTFIT])
+ if(!check_rights(R_SPAWN))
+ return
+ copy_outfit()
+ if(href_list[VV_HK_MOD_QUIRKS])
+ if(!check_rights(R_SPAWN))
+ return
+
+ var/list/options = list("Clear"="Clear")
+ for(var/x in subtypesof(/datum/quirk))
+ var/datum/quirk/T = x
+ var/qname = initial(T.name)
+ options[has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T
+
+ var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options
+ if(result)
+ if(result == "Clear")
+ for(var/datum/quirk/q in roundstart_quirks)
+ remove_quirk(q.type)
+ else
+ var/T = options[result]
+ if(has_quirk(T))
+ remove_quirk(T)
+ else
+ add_quirk(T,TRUE)
+ if(href_list[VV_HK_MAKE_MONKEY])
+ if(!check_rights(R_SPAWN))
+ return
+ if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
+ return
+ usr.client.holder.Topic("vv_override", list("monkeyone"=href_list[VV_HK_TARGET]))
+ if(href_list[VV_HK_MAKE_CYBORG])
+ if(!check_rights(R_SPAWN))
+ return
+ if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
+ return
+ usr.client.holder.Topic("vv_override", list("makerobot"=href_list[VV_HK_TARGET]))
+ if(href_list[VV_HK_MAKE_ALIEN])
+ if(!check_rights(R_SPAWN))
+ return
+ if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
+ return
+ usr.client.holder.Topic("vv_override", list("makealien"=href_list[VV_HK_TARGET]))
+ if(href_list[VV_HK_MAKE_SLIME])
+ if(!check_rights(R_SPAWN))
+ return
+ if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform")
+ return
+ usr.client.holder.Topic("vv_override", list("makeslime"=href_list[VV_HK_TARGET]))
+ if(href_list[VV_HK_SET_SPECIES])
+ if(!check_rights(R_SPAWN))
+ return
+ var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list
+ if(result)
+ var/newtype = GLOB.species_list[result]
+ admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src] to [result]")
+ set_species(newtype)
+ if(href_list[VV_HK_PURRBATION])
+ if(!check_rights(R_SPAWN))
+ return
+ if(!ishumanbasic(src))
+ to_chat(usr, "This can only be done to the basic human species at the moment.")
+ return
+ var/success = purrbation_toggle(src)
+ if(success)
+ to_chat(usr, "Put [src] on purrbation.")
+ log_admin("[key_name(usr)] has put [key_name(src)] on purrbation.")
+ var/msg = "[key_name_admin(usr)] has put [key_name(src)] on purrbation. "
+ message_admins(msg)
+ admin_ticket_log(src, msg)
+
+ else
+ to_chat(usr, "Removed [src] from purrbation.")
+ log_admin("[key_name(usr)] has removed [key_name(src)] from purrbation.")
+ var/msg = "[key_name_admin(usr)] has removed [key_name(src)] from purrbation. "
+ message_admins(msg)
+ admin_ticket_log(src, msg)
/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user)
if(pulling == target && grab_state >= GRAB_AGGRESSIVE && stat == CONSCIOUS)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 448be7692b7..8095d126872 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1305,3 +1305,19 @@
update_transform()
if("lighting_alpha")
sync_lighting_plane_alpha()
+
+/mob/living/vv_get_header()
+ . = ..()
+ var/refid = REF(src)
+ . += {"
+ [ckey || "No ckey"] / [real_name || "No real name"]
+
+ BRUTE:[getBruteLoss()]
+ FIRE:[getFireLoss()]
+ TOXIN:[getToxLoss()]
+ OXY:[getOxyLoss()]
+ CLONE:[getCloneLoss()]
+ BRAIN:[getOrganLoss(ORGAN_SLOT_BRAIN)]
+ STAMINA:[getStaminaLoss()]
+
+ "}
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 5d643851d87..071c0fbdf38 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1128,18 +1128,65 @@
*/
/mob/vv_get_dropdown()
. = ..()
- . += "---"
- .["Gib"] = "?_src_=vars;[HrefToken()];gib=[REF(src)]"
- .["Give Spell"] = "?_src_=vars;[HrefToken()];give_spell=[REF(src)]"
- .["Remove Spell"] = "?_src_=vars;[HrefToken()];remove_spell=[REF(src)]"
- .["Give Disease"] = "?_src_=vars;[HrefToken()];give_disease=[REF(src)]"
- .["Toggle Godmode"] = "?_src_=vars;[HrefToken()];godmode=[REF(src)]"
- .["Drop Everything"] = "?_src_=vars;[HrefToken()];drop_everything=[REF(src)]"
- .["Regenerate Icons"] = "?_src_=vars;[HrefToken()];regenerateicons=[REF(src)]"
- .["Show player panel"] = "?_src_=vars;[HrefToken()];mob_player_panel=[REF(src)]"
- .["Toggle Build Mode"] = "?_src_=vars;[HrefToken()];build_mode=[REF(src)]"
- .["Assume Direct Control"] = "?_src_=vars;[HrefToken()];direct_control=[REF(src)]"
- .["Offer Control to Ghosts"] = "?_src_=vars;[HrefToken()];offer_control=[REF(src)]"
+ VV_DROPDOWN_OPTION("", "---------")
+ VV_DROPDOWN_OPTION(VV_HK_GIB, "Gib")
+ VV_DROPDOWN_OPTION(VV_HK_GIVE_SPELL, "Give Spell")
+ VV_DROPDOWN_OPTION(VV_HK_REMOVE_SPELL, "Remove Spell")
+ VV_DROPDOWN_OPTION(VV_HK_GIVE_DISEASE, "Give Disease")
+ VV_DROPDOWN_OPTION(VV_HK_GODMODE, "Toggle Godmode")
+ VV_DROPDOWN_OPTION(VV_HK_DROP_ALL, "Drop Everything")
+ VV_DROPDOWN_OPTION(VV_HK_REGEN_ICONS, "Regenerate Icons")
+ VV_DROPDOWN_OPTION(VV_HK_PLAYER_PANEL, "Show player panel")
+ VV_DROPDOWN_OPTION(VV_HK_BUILDMODE, "Toggle Buildmode")
+ VV_DROPDOWN_OPTION(VV_HK_DIRECT_CONTROL, "Assume Direct Control")
+ VV_DROPDOWN_OPTION(VV_HK_OFFER_GHOSTS, "Offer Control to Ghosts")
+
+/mob/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_REGEN_ICONS])
+ if(!check_rights(NONE))
+ return
+ regenerate_icons()
+ if(href_list[VV_HK_PLAYER_PANEL])
+ if(!check_rights(NONE))
+ return
+ usr.client.holder.show_player_panel(src)
+ if(href_list[VV_HK_GODMODE])
+ if(!check_rights(R_ADMIN))
+ return
+ usr.client.cmd_admin_godmode(src)
+ if(href_list[VV_HK_GIVE_SPELL])
+ if(!check_rights(NONE))
+ return
+ usr.client.give_spell(src)
+ if(href_list[VV_HK_REMOVE_SPELL])
+ if(!check_rights(NONE))
+ return
+ usr.client.remove_spell(src)
+ if(href_list[VV_HK_GIVE_DISEASE])
+ if(!check_rights(NONE))
+ return
+ usr.client.give_disease(src)
+ if(href_list[VV_HK_GIB])
+ if(!check_rights(R_FUN))
+ return
+ usr.client.cmd_admin_gib(src)
+ if(href_list[VV_HK_BUILDMODE])
+ if(!check_rights(R_BUILD))
+ return
+ togglebuildmode(src)
+ if(href_list[VV_HK_DROP_ALL])
+ if(!check_rights(NONE))
+ return
+ usr.client.cmd_admin_drop_everything(src)
+ if(href_list[VV_HK_DIRECT_CONTROL])
+ if(!check_rights(NONE))
+ return
+ usr.client.cmd_assume_direct_control(src)
+ if(href_list[VV_HK_OFFER_GHOSTS])
+ if(!check_rights(NONE))
+ return
+ offer_control(src)
/**
* extra var handling for the logging var
diff --git a/interface/skin.dmf b/interface/skin.dmf
index b2f1f195c92..dd821398806 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -2,21 +2,21 @@ macro "default"
menu "menu"
- elem
+ elem
name = "&File"
command = ""
saved-params = "is-checked"
- elem
+ elem
name = "&Quick screenshot\tF2"
command = ".screenshot auto"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = "&Save screenshot as...\tShift+F2"
command = ".screenshot"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = ""
command = ""
category = "&File"
@@ -26,21 +26,21 @@ menu "menu"
command = ".reconnect"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = "&Quit\tAlt-F4"
command = ".quit"
category = "&File"
saved-params = "is-checked"
- elem
+ elem
name = "&Help"
command = ""
saved-params = "is-checked"
- elem
+ elem
name = "&Admin Help\tF1"
command = "adminhelp"
category = "&Help"
saved-params = "is-checked"
- elem
+ elem
name = "&Hotkeys"
command = "hotkeys-help"
category = "&Help"
@@ -50,10 +50,11 @@ menu "menu"
window "mainwindow"
elem "mainwindow"
type = MAIN
- pos = 0,0
+ pos = 372,0
size = 640x440
anchor1 = none
anchor2 = none
+ background-color = none
is-default = true
saved-params = "pos;size;is-minimized;is-maximized"
icon = 'icons\\tg_32.png'
@@ -65,10 +66,29 @@ window "mainwindow"
size = 634x417
anchor1 = 0,0
anchor2 = 100,100
+ background-color = none
saved-params = "splitter"
left = "mapwindow"
right = "infowindow"
is-vert = true
+ elem "asset_cache_browser"
+ type = BROWSER
+ pos = 0,0
+ size = 200x200
+ anchor1 = none
+ anchor2 = none
+ background-color = none
+ is-visible = false
+ saved-params = ""
+ elem "tooltip"
+ type = BROWSER
+ pos = 0,0
+ size = 999x999
+ anchor1 = none
+ anchor2 = none
+ background-color = none
+ is-visible = false
+ saved-params = ""
elem "input"
type = INPUT
pos = 3,420
@@ -79,52 +99,39 @@ window "mainwindow"
is-default = true
border = sunken
saved-params = "command"
- elem "saybutton"
- type = BUTTON
- pos = 600,420
- size = 40x20
- anchor1 = 100,100
- anchor2 = none
- saved-params = "is-checked"
- text = "Chat"
- command = ".winset \"saybutton.is-checked=true ? input.command=\"!say \\\"\" : input.command=\"\"saybutton.is-checked=true ? mebutton.is-checked=false\"\"saybutton.is-checked=true ? oocbutton.is-checked=false\""
- button-type = pushbox
elem "oocbutton"
type = BUTTON
pos = 520,420
size = 40x20
anchor1 = 100,100
anchor2 = none
+ background-color = none
saved-params = "is-checked"
text = "OOC"
command = ".winset \"oocbutton.is-checked=true ? input.command=\"!ooc \\\"\" : input.command=\"\"oocbutton.is-checked=true ? mebutton.is-checked=false\"\"oocbutton.is-checked=true ? saybutton.is-checked=false\""
button-type = pushbox
+ elem "saybutton"
+ type = BUTTON
+ pos = 600,420
+ size = 40x20
+ anchor1 = 100,100
+ anchor2 = none
+ background-color = none
+ saved-params = "is-checked"
+ text = "Chat"
+ command = ".winset \"saybutton.is-checked=true ? input.command=\"!say \\\"\" : input.command=\"\"saybutton.is-checked=true ? mebutton.is-checked=false\"\"saybutton.is-checked=true ? oocbutton.is-checked=false\""
+ button-type = pushbox
elem "mebutton"
type = BUTTON
pos = 560,420
size = 40x20
anchor1 = 100,100
anchor2 = none
+ background-color = none
saved-params = "is-checked"
text = "Me"
command = ".winset \"mebutton.is-checked=true ? input.command=\"!me \\\"\" : input.command=\"\"mebutton.is-checked=true ? saybutton.is-checked=false\"\"mebutton.is-checked=true ? oocbutton.is-checked=false\""
button-type = pushbox
- elem "asset_cache_browser"
- type = BROWSER
- pos = 0,0
- size = 200x200
- anchor1 = none
- anchor2 = none
- is-visible = false
- saved-params = ""
- elem "tooltip"
- type = BROWSER
- pos = 0,0
- size = 999x999
- anchor1 = none
- anchor2 = none
- is-visible = false
- saved-params = ""
window "mapwindow"
elem "mapwindow"
@@ -151,10 +158,11 @@ window "mapwindow"
window "infowindow"
elem "infowindow"
type = MAIN
- pos = 0,0
+ pos = 372,0
size = 640x480
anchor1 = none
anchor2 = none
+ background-color = none
saved-params = "pos;size;is-minimized;is-maximized"
is-pane = true
elem "info"
@@ -163,6 +171,7 @@ window "infowindow"
size = 640x445
anchor1 = 0,0
anchor2 = 100,100
+ background-color = none
saved-params = "splitter"
left = "statwindow"
right = "outputwindow"
@@ -173,6 +182,7 @@ window "infowindow"
size = 104x20
anchor1 = 3,0
anchor2 = 19,0
+ background-color = none
saved-params = "is-checked"
text = "Changelog"
command = "changelog"
@@ -182,6 +192,7 @@ window "infowindow"
size = 100x20
anchor1 = 19,0
anchor2 = 34,0
+ background-color = none
saved-params = "is-checked"
text = "Rules"
command = "rules"
@@ -191,6 +202,7 @@ window "infowindow"
size = 100x20
anchor1 = 34,0
anchor2 = 50,0
+ background-color = none
saved-params = "is-checked"
text = "Wiki"
command = "wiki"
@@ -200,6 +212,7 @@ window "infowindow"
size = 100x20
anchor1 = 50,0
anchor2 = 66,0
+ background-color = none
saved-params = "is-checked"
text = "Forum"
command = "forum"
@@ -209,6 +222,7 @@ window "infowindow"
size = 100x20
anchor1 = 66,0
anchor2 = 81,0
+ background-color = none
saved-params = "is-checked"
text = "Github"
command = "github"
@@ -218,6 +232,7 @@ window "infowindow"
size = 100x20
anchor1 = 81,0
anchor2 = 97,0
+ background-color = none
saved-params = "is-checked"
text = "Report Issue"
command = "report-issue"
@@ -225,7 +240,7 @@ window "infowindow"
window "outputwindow"
elem "outputwindow"
type = MAIN
- pos = 0,0
+ pos = 372,0
size = 640x480
anchor1 = none
anchor2 = none
@@ -258,7 +273,6 @@ window "preferences_window"
size = 1280x1000
anchor1 = none
anchor2 = none
- background-color = none
is-visible = false
saved-params = "pos;size;is-minimized;is-maximized"
statusbar = false
@@ -268,7 +282,6 @@ window "preferences_window"
size = 960x1000
anchor1 = 0,0
anchor2 = 75,100
- background-color = none
saved-params = ""
elem "character_preview_map"
type = MAP
@@ -282,7 +295,7 @@ window "preferences_window"
window "statwindow"
elem "statwindow"
type = MAIN
- pos = 0,0
+ pos = 372,0
size = 640x480
anchor1 = none
anchor2 = none
@@ -291,7 +304,7 @@ window "statwindow"
elem "stat"
type = INFO
pos = 0,0
- size = 640x480
+ size = 636x418
anchor1 = 0,0
anchor2 = 100,100
is-default = true
diff --git a/tgstation.dme b/tgstation.dme
index d5df97ad70d..d92f94a7a9d 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -144,6 +144,7 @@
#include "code\__HELPERS\text.dm"
#include "code\__HELPERS\time.dm"
#include "code\__HELPERS\type2type.dm"
+#include "code\__HELPERS\type_processing.dm"
#include "code\__HELPERS\typelists.dm"
#include "code\__HELPERS\unsorted.dm"
#include "code\__HELPERS\view.dm"
@@ -1167,6 +1168,7 @@
#include "code\modules\admin\team_panel.dm"
#include "code\modules\admin\topic.dm"
#include "code\modules\admin\whitelist.dm"
+#include "code\modules\admin\callproc\callproc.dm"
#include "code\modules\admin\verbs\adminhelp.dm"
#include "code\modules\admin\verbs\adminjump.dm"
#include "code\modules\admin\verbs\adminpm.dm"
@@ -1188,8 +1190,6 @@
#include "code\modules\admin\verbs\map_template_loadverb.dm"
#include "code\modules\admin\verbs\mapping.dm"
#include "code\modules\admin\verbs\maprotation.dm"
-#include "code\modules\admin\verbs\massmodvar.dm"
-#include "code\modules\admin\verbs\modifyvariables.dm"
#include "code\modules\admin\verbs\one_click_antag.dm"
#include "code\modules\admin\verbs\onlyone.dm"
#include "code\modules\admin\verbs\panicbunker.dm"
@@ -1204,6 +1204,16 @@
#include "code\modules\admin\verbs\SDQL2\SDQL_2.dm"
#include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm"
#include "code\modules\admin\verbs\SDQL2\SDQL_2_wrappers.dm"
+#include "code\modules\admin\view_variables\admin_delete.dm"
+#include "code\modules\admin\view_variables\debug_variables.dm"
+#include "code\modules\admin\view_variables\get_variables.dm"
+#include "code\modules\admin\view_variables\mark_datum.dm"
+#include "code\modules\admin\view_variables\mass_edit_variables.dm"
+#include "code\modules\admin\view_variables\modify_variables.dm"
+#include "code\modules\admin\view_variables\topic.dm"
+#include "code\modules\admin\view_variables\topic_basic.dm"
+#include "code\modules\admin\view_variables\topic_list.dm"
+#include "code\modules\admin\view_variables\view_variables.dm"
#include "code\modules\antagonists\_common\antag_datum.dm"
#include "code\modules\antagonists\_common\antag_helpers.dm"
#include "code\modules\antagonists\_common\antag_hud.dm"