/")
-
- 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(!isappearance && ("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(!isappearance && !islist && (D.datum_flags & DF_VAR_EDITED))
- varedited_line = VV_MSG_EDITED
- var/deleted_line
- if(!isappearance && !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 if (!isappearance)
- 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 && !isappearance)
- 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 if(isappearance(D))
- variable_html += debug_variable("type", D:type, 0, D)
- variable_html += debug_variable("name", D:name, 0, D)
- variable_html += debug_variable("desc", D:desc, 0, D)
- variable_html += debug_variable("suffix", D:suffix, 0, D)
- variable_html += debug_variable("text", D:text, 0, D)
- variable_html += debug_variable("icon", D:icon, 0, D)
- variable_html += debug_variable("icon_state", D:icon_state, 0, D)
- variable_html += debug_variable("visibility", D:visibility, 0, D)
- variable_html += debug_variable("luminosity", D:luminosity, 0, D)
- variable_html += debug_variable("opacity", D:opacity, 0, D)
- variable_html += debug_variable("density", D:density, 0, D)
- variable_html += debug_variable("verbs", D:verbs, 0, D)
- variable_html += debug_variable("dir", D:dir, 0, D)
- variable_html += debug_variable("gender", D:gender, 0, D)
- variable_html += debug_variable("tag", D:tag, 0, D)
- variable_html += debug_variable("overlays", D:overlays, 0, D)
- variable_html += debug_variable("underlays", D:underlays, 0, D)
- variable_html += debug_variable("layer", D:layer, 0, D)
- variable_html += debug_variable("parent_type", D:parent_type, 0, D)
- variable_html += debug_variable("mouse_over_pointer", D:mouse_over_pointer, 0, D)
- variable_html += debug_variable("mouse_drag_pointer", D:mouse_drag_pointer, 0, D)
- variable_html += debug_variable("mouse_drop_pointer", D:mouse_drop_pointer, 0, D)
- variable_html += debug_variable("mouse_drop_zone", D:mouse_drop_zone, 0, D)
- variable_html += debug_variable("animate_movement", D:animate_movement, 0, D)
- variable_html += debug_variable("screen_loc", D:screen_loc, 0, D)
- variable_html += debug_variable("infra_luminosity", D:infra_luminosity, 0, D)
- variable_html += debug_variable("invisibility", D:invisibility, 0, D)
- variable_html += debug_variable("mouse_opacity", D:mouse_opacity, 0, D)
- variable_html += debug_variable("pixel_x", D:pixel_x, 0, D)
- variable_html += debug_variable("pixel_y", D:pixel_y, 0, D)
- variable_html += debug_variable("pixel_step_size", D:pixel_step_size, 0, D)
- variable_html += debug_variable("pixel_z", D:pixel_z, 0, D)
- variable_html += debug_variable("override", D:override, 0, D)
- variable_html += debug_variable("glide_size", D:glide_size, 0, D)
- variable_html += debug_variable("maptext", D:maptext, 0, D)
- variable_html += debug_variable("maptext_width", D:maptext_width, 0, D)
- variable_html += debug_variable("maptext_height", D:maptext_height, 0, D)
- variable_html += debug_variable("transform", D:transform, 0, D)
- variable_html += debug_variable("alpha", D:alpha, 0, D)
- variable_html += debug_variable("color", D:color, 0, D)
- variable_html += debug_variable("blend_mode", D:blend_mode, 0, D)
- variable_html += debug_variable("appearance", D:appearance, 0, D)
- variable_html += debug_variable("maptext_x", D:maptext_x, 0, D)
- variable_html += debug_variable("maptext_y", D:maptext_y, 0, D)
- variable_html += debug_variable("plane", D:plane, 0, D)
- variable_html += debug_variable("appearance_flags", D:appearance_flags, 0, D)
- variable_html += debug_variable("pixel_w", D:pixel_w, 0, D)
- variable_html += debug_variable("render_source", D:render_source, 0, D)
- variable_html += debug_variable("render_target", D:render_target, 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 )
-/// Get displayed variable in VV variable list
-/proc/debug_variable(name, value, level, datum/DA = null, sanitize = TRUE)
- var/header
- if(DA && !isappearance(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)] = [span_value("null")]"
-
- else if (istext(value))
- item = "[VV_HTML_ENCODE(name)] = [span_value("\"[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)] = ([span_value("[value]")]) "
- #else
- item = "[VV_HTML_ENCODE(name)] = /icon ([span_value("[value]")])"
- #endif
-
- else if (isfile(value))
- item = "[VV_HTML_ENCODE(name)] = [span_value("'[value]'")]"
-
- else if(istype(value,/matrix)) // Needs to be before datum
- var/matrix/M = value
- item = {"[VV_HTML_ENCODE(name)] =
-
-
-
- [M.a] [M.d] 0
- [M.b] [M.e] 0
- [M.c] [M.f] 1
-
-
"} //TODO link to modify_transform wrapper for all matrices
-
- else if(isappearance(value))
- var/image/I = value
- item = "[VV_HTML_ENCODE(name)] [REF(value)] = appearance([span_value("[I.icon]")], [span_value("\"[I.icon_state]\"")])"
-
- 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 && !(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)] = [span_value("[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)
- // yogs start - offer control can now be used by mods
- else if(href_list["offer_control"])
- if(!check_rights(R_ADMIN))
- 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)
- // yogs end
-
- //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records).
-
- else if(href_list["rename"])
- if(!check_rights(R_ADMIN))
- 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")
-
-//Needs +VAREDIT past this point
-
-
- else if(check_rights(R_VAREDIT))
-
- 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_BUILDMODE))
- 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)
-
- // yogs - offer control moved up
-
- else if(href_list["set_afk"])
- if(!check_rights(R_ADMIN))
- return
-
- var/mob/M = locate(href_list["set_afk"]) in GLOB.mob_list
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- if(!M.mind)
- to_chat(usr, "This cannot be used on mobs without a mind")
- return
-
- var/timer = input("Input AFK length in minutes, 0 to cancel the current timer", text("Input")) as num|null
- if(timer == null) // Explicit null check for cancel, rather than generic truthyness, so 0 is handled differently
- return
-
- deltimer(M.mind.afk_verb_timer)
- M.mind.afk_verb_used = FALSE
-
- if(!timer)
- return
-
- M.mind.afk_verb_used = TRUE
- M.mind.afk_verb_timer = addtimer(VARSET_CALLBACK(M.mind, afk_verb_used, FALSE), timer MINUTES, TIMER_STOPPABLE);
-
-
- 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(span_notice("[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(span_notice("[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(span_notice("[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
- var/list/reagent_options = sortList(GLOB.chemical_reagents_list)
- switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID"))
- if("Enter ID")
- var/valid_id
- while(!valid_id)
- chosen_id = stripped_input(usr, "Enter the name of the reagent you want to add. (Case Sensitive!)")
- if(!chosen_id) //Get me out of here!
- break
- for(var/ID in reagent_options)
- var/datum/reagent/selected = reagent_options[ID]
- if(selected?.name == chosen_id) //apparently I have to do this because the other method wasn't WORKING
- valid_id = TRUE
- chosen_id = ID
- if(!valid_id)
- to_chat(usr, span_warning("A reagent with that ID doesn't exist!"))
- if("Choose ID")
- chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in reagent_options
- 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(span_notice("[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["radiate"] && check_rights(R_FUN))
- var/atom/A = locate(href_list["radiate"])
- var/strength = input(usr, "Choose the radiation strength.", "Choose the strength.") as num|null
- if(!isnull(strength))
- A.AddComponent(/datum/component/radioactive, strength, src)
-
- 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(span_notice("[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(span_notice("[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(span_notice("[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(usr)] has modified the bodyparts of [H] to [result]") // yogs - Yog Tickets
- 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(usr)] has modified the bodyparts of [C]") // yogs - Yog Tickets
-
-
- 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(usr)] has put [key_name(H)] on purrbation." // yogs - Yog Tickets
- 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(usr)] has removed [key_name(H)] from purrbation." // yogs - Yog Tickets
- message_admins(msg)
- admin_ticket_log(H, msg)
-
- else if(href_list["cluwneing"]) // yogs start -- adds cluwneify verb in VV
- if(!check_rights(R_SPAWN)) return
- var/mob/living/carbon/human/H = locate(href_list["cluwneing"])
- if(!H)
- to_chat(usr, "Mob doesn't exist anymore")
- return
- H.cluwneify()
- message_admins(span_notice("[key_name(usr)] has made [key_name(H)] into a Cluwne."))
- return // yogs end
-
- else if(href_list["makepacman"])
- if(!check_rights(R_SPAWN))
- return
-
- var/mob/living/carbon/human/H = locate(href_list["makepacman"]) 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("makepacman"=href_list["makepacman"]))
-
-
- 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)
- else if(href_list["delete_paint"])
- if(!check_rights(R_ADMIN))
- return
-
- var/obj/structure/sign/painting/P = locate(href_list["delete_paint"])
-
- var/mob/user = usr
- if(!P.persistence_id || !P.C)
- to_chat(user,span_warning("This is not a persistent painting."))
- return
- var/md5 = md5(P.C.get_data_string())
- var/author = P.C.author_ckey
- var/list/current = SSpersistence.paintings[P.persistence_id]
- if(current)
- for(var/list/entry in current)
- if(entry["md5"] == md5)
- current -= entry
- var/png = "data/paintings/[P.persistence_id]/[md5].png"
- fdel(png)
- for(var/obj/structure/sign/painting/PA in SSpersistence.painting_frames)
- if(PA.C && md5(PA.C.get_data_string()) == md5)
- QDEL_NULL(PA.C)
- log_admin("[key_name(user)] has deleted a persistent painting made by [author].")
- message_admins(span_notice("[key_name_admin(user)] has deleted persistent painting made by [author]."))
+/datum/proc/vv_get_header()
+ . = list()
+ if(("name" in vars) && !isatom(src))
+ . += "[vars["name"]] "
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index e0a02c9fe4f6..81cbd4f9b0ab 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -908,15 +908,106 @@
*/
/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)]"
- .["Radiate"] = "?_src_=vars;[HrefToken()];radiate=[REF(src)]"
+ VV_DROPDOWN_SEPERATOR
+ if(!ismovable(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_SHOW_HIDDENPRINTS, "Show Hiddenprint log")
+ 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")
+ VV_DROPDOWN_OPTION(VV_HK_RADIATE, "Radiate")
+
+/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|null
+ if(amount)
+ create_reagents(amount)
+
+ if(reagents)
+ var/chosen_id
+ switch(alert(usr, "Choose a method.", "Add Reagents", list("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, span_warning("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 sortList(subtypesof(/datum/reagent), /proc/cmp_typepaths_asc)
+ 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|null
+ if(amount)
+ reagents.add_reagent(chosen_id, amount)
+ log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to [src]")
+ message_admins(span_notice("[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_SHOW_HIDDENPRINTS])
+ usr.client.cmd_show_hiddenprints(src)
+
+ if(href_list[VV_HK_RADIATE] && check_rights(R_FUN))
+ var/strength = input(usr, "Choose the radiation strength.", "Choose the strength.") as num|null
+ if(!isnull(strength))
+ AddComponent(/datum/component/radioactive, strength)
+
+ 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
+ if(!result)
+ return
+ 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))
+ return
+ transform = M.Scale(x,y)
+ if("Translate")
+ var/x = input(usr, "Choose x mod (negative = left, positive = right)","Transform Mod") as null|num
+ var/y = input(usr, "Choose y mod (negative = down, positive = up)","Transform Mod") as null|num
+ if(isnull(x) || isnull(y))
+ return
+ transform = M.Translate(x,y)
+ if("Rotate")
+ var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num
+ if(isnull(angle))
+ return
+ transform = M.Turn(angle)
+
+ if(href_list[VV_HK_AUTO_RENAME] && check_rights(R_VAREDIT))
+ var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text
+ // Check the new name against the chat filter. If it triggers the IC chat filter, give an option to confirm.
+ if(newname)
+ vv_auto_rename(newname)
+
+/atom/proc/vv_auto_rename(newname)
+ name = newname
+
+/atom/vv_get_header()
+ . = ..()
+ var/refid = REF(src)
+ . += "[VV_HREF_TARGETREF(refid, VV_HK_AUTO_RENAME, "[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 e7b9a1ff08a8..9e05d55325b1 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -829,9 +829,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 2db54d2e6416..d9372cd69b83 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -221,9 +221,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_SEPERATOR
+ 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], fire: [armor.fire], acid: [armor.acid]")
+ message_admins(span_notice("[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], 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(usr, "Strict type ([type]) or type and all subtypes?",,list("Strict type","Type and subtypes","Cancel"))
+ if(action_type == "Cancel" || !action_type)
+ return
+
+ if(alert(usr, "Are you really sure you want to delete all objects of type [type]?",,list("Yes","No")) != "Yes")
+ return
+
+ if(alert(usr, "Second confirmation required. Delete?",,list("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(span_notice("[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(span_notice("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) "))
/obj/examine(mob/user)
. = ..()
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index 4eed032c3b7e..02bb9607c5b2 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -400,4 +400,25 @@
/obj/structure/sign/painting/vv_get_dropdown()
. = ..()
- .["Remove Persistent Painting"] = "?_src_=vars;[HrefToken()];delete_paint=[REF(src)]"
+ VV_DROPDOWN_OPTION(VV_HK_REMOVE_PAINTING, "Remove Persistent Painting")
+
+/obj/structure/sign/painting/vv_do_topic(list/href_list)
+ . = ..()
+ var/mob/user = usr
+ if(!persistence_id || !C)
+ to_chat(user,span_warning("This is not a persistent painting."))
+ return
+ var/md5 = md5(C.get_data_string())
+ var/author = C.author_ckey
+ var/list/current = SSpersistence.paintings[persistence_id]
+ if(current)
+ for(var/list/entry in current)
+ if(entry["md5"] == md5)
+ current -= entry
+ var/png = "data/paintings/[persistence_id]/[md5].png"
+ fdel(png)
+ for(var/obj/structure/sign/painting/PA in SSpersistence.painting_frames)
+ if(PA.C && md5(PA.C.get_data_string()) == md5)
+ QDEL_NULL(PA.C)
+ log_admin("[key_name(user)] has deleted a persistent painting made by [author].")
+ message_admins(span_notice("[key_name_admin(user)] has deleted persistent painting made by [author]."))
diff --git a/code/modules/admin/verbs/hiddenprints.dm b/code/modules/admin/verbs/hiddenprints.dm
new file mode 100644
index 000000000000..c7410b8d6502
--- /dev/null
+++ b/code/modules/admin/verbs/hiddenprints.dm
@@ -0,0 +1,30 @@
+/client/proc/cmd_show_hiddenprints(atom/victim)
+ if(!check_rights(R_ADMIN))
+ return
+
+ var/interface = "A log of every player who has touched [victim], sorted by last touch."
+ var/victim_hiddenprints = victim.return_hiddenprints()
+
+ if(!islist(victim_hiddenprints))
+ victim_hiddenprints = list()
+
+ var/list/hiddenprints = flatten_list(victim_hiddenprints)
+ listclearnulls(hiddenprints)
+
+ if(!length(hiddenprints))
+ hiddenprints = list("Nobody has touched this yet!")
+
+ hiddenprints = sortList(hiddenprints, /proc/cmp_hiddenprint_lasttime_dsc)
+ for(var/record in hiddenprints)
+ interface += "[record] "
+
+ interface += " "
+
+ var/datum/browser/hiddenprint_view = new(usr, "view_hiddenprints_[REF(victim)]", "[victim]'s hiddenprints", 450, 760)
+ hiddenprint_view.set_content(interface)
+ hiddenprint_view.open()
+
+/proc/cmp_hiddenprint_lasttime_dsc(a, b)
+ var/last_a = copytext(a, findtext(a, "\nLast: "))
+ var/last_b = copytext(b, findtext(b, "\nLast: "))
+ return cmp_text_dsc(last_a, last_b)
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 000000000000..e115576a52b7
--- /dev/null
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -0,0 +1,93 @@
+#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
+/// Get displayed variable in VV variable list
+/proc/debug_variable(name, value, level, datum/DA = null, sanitize = TRUE)
+ var/header
+ if(DA && !isappearance(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 = "([VV_HREF_TARGET_1V(DA, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(DA, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(DA, VV_HK_LIST_REMOVE, "-", index)]) "
+ else
+ header = " ([VV_HREF_TARGET_1V(DA, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(DA, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(DA, VV_HK_BASIC_MASSEDIT, "M", name)]) "
+ else
+ header = " "
+
+ var/item
+ if (isnull(value))
+ item = "[VV_HTML_ENCODE(name)] = [span_value("null")]"
+
+ else if (istext(value))
+ item = "[VV_HTML_ENCODE(name)] = [span_value("\"[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)] = ([span_value("[value]")]) "
+ #else
+ item = "[VV_HTML_ENCODE(name)] = /icon ([span_value("[value]")])"
+ #endif
+
+ else if (isfile(value))
+ item = "[VV_HTML_ENCODE(name)] = [span_value("'[value]'")]"
+
+ else if(istype(value,/matrix)) // Needs to be before datum
+ var/matrix/M = value
+ item = {"[VV_HTML_ENCODE(name)] =
+
+
+
+ [M.a] [M.d] 0
+ [M.b] [M.e] 0
+ [M.c] [M.f] 1
+
+
"} //TODO link to modify_transform wrapper for all matrices
+
+ else if(isappearance(value))
+ var/image/I = value
+ item = "[VV_HTML_ENCODE(name)] [REF(value)] = appearance([span_value("[I.icon]")], [span_value("\"[I.icon_state]\"")])"
+
+ 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 && !(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)] = [span_value("[VV_HTML_ENCODE(value)]")]"
+
+ return "[header][item] "
+
+#undef VV_HTML_ENCODE
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 000000000000..e381d56630ae
--- /dev/null
+++ b/code/modules/admin/view_variables/mark_datum.dm
@@ -0,0 +1,13 @@
+/client/proc/mark_datum(datum/D)
+ if(!holder)
+ return
+ if(holder.marked_datum)
+ holder.UnregisterSignal(holder.marked_datum, COMSIG_PARENT_QDELETING)
+ vv_update_display(holder.marked_datum, "marked", "")
+ holder.marked_datum = D
+ holder.RegisterSignal(holder.marked_datum, COMSIG_PARENT_QDELETING, /datum/admins/proc/handle_marked_del)
+ vv_update_display(D, "marked", VV_MSG_MARKED)
+
+/datum/admins/proc/handle_marked_del(datum/source)
+ UnregisterSignal(marked_datum, COMSIG_PARENT_QDELETING)
+ marked_datum = null
diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/view_variables/massmodvar.dm
similarity index 100%
rename from code/modules/admin/verbs/massmodvar.dm
rename to code/modules/admin/view_variables/massmodvar.dm
diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/view_variables/modifyvariables.dm
similarity index 100%
rename from code/modules/admin/verbs/modifyvariables.dm
rename to code/modules/admin/view_variables/modifyvariables.dm
diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm
new file mode 100644
index 000000000000..73f7059c33ed
--- /dev/null
+++ b/code/modules/admin/view_variables/topic.dm
@@ -0,0 +1,114 @@
+//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.
+
+ //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records).
+ if(href_list["rename"])
+ if(!check_rights(R_ADMIN))
+ 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", confidential = TRUE)
+ 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(check_rights(R_VAREDIT))
+ 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", confidential = TRUE)
+ 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["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|null
+
+ if (isnull(amount))
+ return
+
+ if(!L)
+ to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE)
+ 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]", confidential = TRUE)
+ 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) || islist(DAT))
+ 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 000000000000..a20ffacd1414
--- /dev/null
+++ b/code/modules/admin/view_variables/topic_basic.dm
@@ -0,0 +1,54 @@
+//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, span_warning("The object you tried to expose to [C] no longer exists (nulled or hard-deled)"), confidential = TRUE)
+ 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", confidential = TRUE)
+ C.debug_variables(target)
+ if(check_rights(R_DEBUG))
+ if(href_list[VV_HK_DELETE])
+ usr.client.admin_delete(target)
+ if (isturf(target)) // show the turf that took its place
+ usr.client.debug_variables(target)
+ return
+
+ 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 000000000000..7fe1a9c715fe
--- /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(usr,"Do you want to remove item number [target_index] from list?", "Confirm", list("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 000000000000..1466f50fb80e
--- /dev/null
+++ b/code/modules/admin/view_variables/view_variables.dm
@@ -0,0 +1,375 @@
+/client/proc/debug_variables(datum/D in world)
+ set category = "Misc.Server 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) //The usr vs src abuse in this proc is intentional and must not be changed
+ to_chat(usr, span_danger("You need to be an administrator to access this."), confidential = TRUE)
+ return
+
+ if(!D)
+ return
+
+ var/islist = islist(D)
+ var/isappearance = isappearance(D)
+ if (!islist && !istype(D) && !isappearance)
+ return
+
+ var/title = ""
+ var/refid = REF(D)
+ var/icon/sprite
+ var/hash
+
+ var/type = /list
+ if (isappearance)
+ type = /image
+ else if (!islist)
+ type = D.type
+
+
+
+ if(istype(D, /atom) || isappearance)
+ var/atom/AT = D
+ if(AT.icon && AT.icon_state)
+ sprite = new /icon(AT.icon, AT.icon_state)
+ hash = md5(AT.icon)
+ hash = md5(hash + AT.icon_state)
+ 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(!isappearance && !islist && (D.datum_flags & DF_VAR_EDITED))
+ varedited_line = VV_MSG_EDITED
+ var/deleted_line
+ if(!isappearance && !islist && D.gc_destroyed)
+ deleted_line = VV_MSG_DELETED
+
+ var/list/dropdownoptions = list()
+ 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 if (!isappearance)
+ dropdownoptions = D.vv_get_dropdown()
+
+ var/list/names = list()
+ if (!islist && !isappearance)
+ 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 if(isappearance(D))
+ variable_html += debug_variable("type", D:type, 0, D)
+ variable_html += debug_variable("name", D:name, 0, D)
+ variable_html += debug_variable("desc", D:desc, 0, D)
+ variable_html += debug_variable("suffix", D:suffix, 0, D)
+ variable_html += debug_variable("text", D:text, 0, D)
+ variable_html += debug_variable("icon", D:icon, 0, D)
+ variable_html += debug_variable("icon_state", D:icon_state, 0, D)
+ variable_html += debug_variable("visibility", D:visibility, 0, D)
+ variable_html += debug_variable("luminosity", D:luminosity, 0, D)
+ variable_html += debug_variable("opacity", D:opacity, 0, D)
+ variable_html += debug_variable("density", D:density, 0, D)
+ variable_html += debug_variable("verbs", D:verbs, 0, D)
+ variable_html += debug_variable("dir", D:dir, 0, D)
+ variable_html += debug_variable("gender", D:gender, 0, D)
+ variable_html += debug_variable("tag", D:tag, 0, D)
+ variable_html += debug_variable("overlays", D:overlays, 0, D)
+ variable_html += debug_variable("underlays", D:underlays, 0, D)
+ variable_html += debug_variable("layer", D:layer, 0, D)
+ variable_html += debug_variable("parent_type", D:parent_type, 0, D)
+ variable_html += debug_variable("mouse_over_pointer", D:mouse_over_pointer, 0, D)
+ variable_html += debug_variable("mouse_drag_pointer", D:mouse_drag_pointer, 0, D)
+ variable_html += debug_variable("mouse_drop_pointer", D:mouse_drop_pointer, 0, D)
+ variable_html += debug_variable("mouse_drop_zone", D:mouse_drop_zone, 0, D)
+ variable_html += debug_variable("animate_movement", D:animate_movement, 0, D)
+ variable_html += debug_variable("screen_loc", D:screen_loc, 0, D)
+ variable_html += debug_variable("infra_luminosity", D:infra_luminosity, 0, D)
+ variable_html += debug_variable("invisibility", D:invisibility, 0, D)
+ variable_html += debug_variable("mouse_opacity", D:mouse_opacity, 0, D)
+ variable_html += debug_variable("pixel_x", D:pixel_x, 0, D)
+ variable_html += debug_variable("pixel_y", D:pixel_y, 0, D)
+ variable_html += debug_variable("pixel_step_size", D:pixel_step_size, 0, D)
+ variable_html += debug_variable("pixel_z", D:pixel_z, 0, D)
+ variable_html += debug_variable("override", D:override, 0, D)
+ variable_html += debug_variable("glide_size", D:glide_size, 0, D)
+ variable_html += debug_variable("maptext", D:maptext, 0, D)
+ variable_html += debug_variable("maptext_width", D:maptext_width, 0, D)
+ variable_html += debug_variable("maptext_height", D:maptext_height, 0, D)
+ variable_html += debug_variable("transform", D:transform, 0, D)
+ variable_html += debug_variable("alpha", D:alpha, 0, D)
+ variable_html += debug_variable("color", D:color, 0, D)
+ variable_html += debug_variable("blend_mode", D:blend_mode, 0, D)
+ variable_html += debug_variable("appearance", D:appearance, 0, D)
+ variable_html += debug_variable("maptext_x", D:maptext_x, 0, D)
+ variable_html += debug_variable("maptext_y", D:maptext_y, 0, D)
+ variable_html += debug_variable("plane", D:plane, 0, D)
+ variable_html += debug_variable("appearance_flags", D:appearance_flags, 0, D)
+ variable_html += debug_variable("pixel_w", D:pixel_w, 0, D)
+ variable_html += debug_variable("render_source", D:render_source, 0, D)
+ variable_html += debug_variable("render_target", D:render_target, 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]
+
+ [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 032cf232da71..03cdb446322a 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -391,17 +391,14 @@
/datum/spacevine_controller/vv_get_dropdown()
. = ..()
- . += "---"
- .["Delete Vines"] = "?_src_=[REF(src)];[HrefToken()];purge_vines=1"
+ VV_DROPDOWN_SEPERATOR
+ 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", list("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/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 4d4457389c49..cdf4a2f5c056 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -952,14 +952,114 @@
/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_SEPERATOR
+ 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 sortList(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, span_boldwarning("[src] doesn't have such bodypart."))
+ if("add")
+ if(BP)
+ to_chat(usr, span_boldwarning("[src] already has such bodypart."))
+ else
+ if(!regenerate_limb(result))
+ to_chat(usr, span_boldwarning("[src] cannot have such bodypart."))
+ if("augment")
+ if(ishuman(src))
+ if(BP)
+ BP.change_bodypart_status(BODYPART_ROBOTIC, TRUE, TRUE)
+ else
+ to_chat(usr, span_boldwarning("[src] doesn't have such bodypart."))
+ else
+ to_chat(usr, span_boldwarning("Only humans can be augmented."))
+ admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src]")
+ 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 sortList(artnames, /proc/cmp_typepaths_asc)
+ if(!usr)
+ return
+ if(QDELETED(src))
+ to_chat(usr, span_boldwarning("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(span_notice("[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 sortList(traumas, /proc/cmp_typepaths_asc)
+ 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(span_notice("[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(span_notice("[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 sortList(hallucinations, /proc/cmp_typepaths_asc)
+ 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 6aa6e38d7e75..f26c6d22ec0b 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -881,17 +881,60 @@
/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)]"
- .["Make Cluwne"] = "?_src_=vars;[HrefToken()];cluwneing=[REF(src)]" // yogs -- make cluwne
- .["Make Pacman"] = "?_src_=vars;[HrefToken()];makepacman=[REF(src)]" //I LOVE PACMAN
+ VV_DROPDOWN_SEPERATOR
+ VV_DROPDOWN_OPTION(VV_HK_SET_SPECIES, "Set Species")
+ VV_DROPDOWN_OPTION(VV_HK_PURRBATION, "Toggle Purrbation")
+ VV_DROPDOWN_OPTION(VV_HK_COPY_OUTFIT, "Copy Outfit")
+ VV_DROPDOWN_OPTION(VV_HK_MOD_QUIRKS, "Add/Remove Quirks")
+
+/mob/living/carbon/human/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_SET_SPECIES] && check_rights(R_SPAWN))
+ 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(src, "[key_name(usr)] has modified the species of [src] to [result]") // yogs - Yog Tickets
+ set_species(newtype)
+ if(href_list[VV_HK_PURRBATION] && check_rights(R_SPAWN))
+ 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(usr)] has put [key_name(src)] on purrbation." // yogs - Yog Tickets
+ 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(usr)] has removed [key_name(src)] from purrbation." // yogs - Yog Tickets
+ message_admins(msg)
+ admin_ticket_log(src, msg)
+ if(href_list[VV_HK_COPY_OUTFIT] && check_rights(R_SPAWN))
+ copy_outfit()
+ if(href_list[VV_HK_MOD_QUIRKS] && check_rights(R_SPAWN))
+ 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)
/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 78855140bdc4..5005cc5994df 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1385,6 +1385,22 @@
if("lighting_alpha")
sync_lighting_plane_alpha()
+/mob/living/vv_get_header()
+ . = ..()
+ var/refid = REF(src)
+ . += {"
+ [ckey || "No ckey"] / [VV_HREF_TARGETREF_1V(refid, VV_HK_BASIC_EDIT, "[real_name || "no real name"]", NAMEOF(src, real_name))]
+
+ BRUTE:[getBruteLoss()]
+ FIRE:[getFireLoss()]
+ TOXIN:[getToxLoss()]
+ OXY:[getOxyLoss()]
+ CLONE:[getCloneLoss()]
+ BRAIN:[getOrganLoss(ORGAN_SLOT_BRAIN)]
+ STAMINA:[getStaminaLoss()]
+
+ "}
+
/mob/living/proc/is_convert_antag()
var/list/bad_antags = list(
/datum/antagonist/clockcult,
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index b0cae501bfff..272ae1681937 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1187,19 +1187,63 @@
*/
/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)]"
- .["Set AFK Timer"] = "?_src_=vars;[HrefToken()];set_afk=[REF(src)]"
+ VV_DROPDOWN_SEPERATOR
+ 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")
+ VV_DROPDOWN_OPTION(VV_HK_SET_AFK_TIMER, "Set AFK Timer")
+
+
+/mob/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_REGEN_ICONS] && check_rights(R_VAREDIT))
+ regenerate_icons()
+ if(href_list[VV_HK_PLAYER_PANEL])
+ usr.client.holder.show_player_panel(src)
+ if(href_list[VV_HK_GODMODE] && check_rights(R_ADMIN))
+ usr.client.cmd_admin_godmode(src)
+ if(href_list[VV_HK_GIVE_SPELL] && check_rights(R_VAREDIT))
+ usr.client.give_spell(src)
+ if(href_list[VV_HK_REMOVE_SPELL] && check_rights(R_VAREDIT))
+ usr.client.remove_spell(src)
+ if(href_list[VV_HK_GIVE_DISEASE] && check_rights(R_VAREDIT))
+ usr.client.give_disease(src)
+ if(href_list[VV_HK_GIB] && check_rights(R_FUN))
+ usr.client.cmd_admin_gib(src)
+ if(href_list[VV_HK_BUILDMODE] && check_rights(R_BUILDMODE))
+ togglebuildmode(src)
+ if(href_list[VV_HK_DROP_ALL] && check_rights(R_ADMIN))
+ usr.client.cmd_admin_drop_everything(src)
+ if(href_list[VV_HK_DIRECT_CONTROL] && check_rights(R_VAREDIT))
+ usr.client.cmd_assume_direct_control(src)
+ if(href_list[VV_HK_OFFER_GHOSTS] && check_rights(R_ADMIN))
+ offer_control(src)
+ if(href_list[VV_HK_SET_AFK_TIMER] && check_rights(R_ADMIN))
+ if(!mind)
+ to_chat(usr, "This cannot be used on mobs without a mind")
+ return
+
+ var/timer = input("Input AFK length in minutes, 0 to cancel the current timer", text("Input")) as num|null
+ if(timer == null) // Explicit null check for cancel, rather than generic truthyness, so 0 is handled differently
+ return
+
+ deltimer(mind.afk_verb_timer)
+ mind.afk_verb_used = FALSE
+
+ if(!timer)
+ return
+
+ mind.afk_verb_used = TRUE
+ mind.afk_verb_timer = addtimer(VARSET_CALLBACK(mind, afk_verb_used, FALSE), timer MINUTES, TIMER_STOPPABLE);
+
/**
* extra var handling for the logging var
@@ -1210,6 +1254,12 @@
return debug_variable(var_name, logging, 0, src, FALSE)
. = ..()
+/mob/vv_auto_rename(new_name)
+ //Do not do parent's actions, as we *usually* do this differently.
+ fully_replace_character_name(real_name, new_name)
+ usr.client.vv_update_display(src, "name", new_name)
+ usr.client.vv_update_display(src, "real_name", real_name || "No real name")
+
///Show the language menu for this mob
/mob/verb/open_language_menu()
set name = "Open Language Menu"
diff --git a/yogstation.dme b/yogstation.dme
index 5e8a959a231d..59a17a087197 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -1314,14 +1314,13 @@
#include "code\modules\admin\verbs\fps.dm"
#include "code\modules\admin\verbs\getlogs.dm"
#include "code\modules\admin\verbs\ghost_pool_protection.dm"
+#include "code\modules\admin\verbs\hiddenprints.dm"
#include "code\modules\admin\verbs\individual_logging.dm"
#include "code\modules\admin\verbs\machine_upgrade.dm"
#include "code\modules\admin\verbs\manipulate_organs.dm"
#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"
@@ -1336,6 +1335,14 @@
#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\topic_basic.dm"
+#include "code\modules\admin\view_variables\topic_list.dm"
+#include "code\modules\admin\view_variables\mark_datum.dm"
+#include "code\modules\admin\view_variables\modifyvariables.dm"
+#include "code\modules\admin\view_variables\massmodvar.dm"
+#include "code\modules\admin\view_variables\topic.dm"
+#include "code\modules\admin\view_variables\debug_variables.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"