diff --git a/citadel.dme b/citadel.dme
index 0d1f7b51809..e0c4fd5244c 100644
--- a/citadel.dme
+++ b/citadel.dme
@@ -531,6 +531,7 @@
#include "code\_globals\tgui.dm"
#include "code\_globals\time_vars.dm"
#include "code\_globals\traits.dm"
+#include "code\_globals\lists\admin.dm"
#include "code\_globals\lists\clients.dm"
#include "code\_globals\lists\clothing.dm"
#include "code\_globals\lists\keybindings.dm"
@@ -2487,6 +2488,7 @@
#include "code\modules\admin\verbs\server\set_next_map.dm"
#include "code\modules\admin\view_variables\admin_delete.dm"
#include "code\modules\admin\view_variables\color_matrix_editor.dm"
+#include "code\modules\admin\view_variables\debug_variable_appearance.dm"
#include "code\modules\admin\view_variables\debug_variables.dm"
#include "code\modules\admin\view_variables\filteriffic.dm"
#include "code\modules\admin\view_variables\get_variables.dm"
@@ -2494,6 +2496,7 @@
#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\nobody_wants_to_learn_matrix_math.dm"
#include "code\modules\admin\view_variables\reference_tracking.dm"
#include "code\modules\admin\view_variables\topic.dm"
#include "code\modules\admin\view_variables\topic_basic.dm"
@@ -3953,7 +3956,6 @@
#include "code\modules\mob\living\say.dm"
#include "code\modules\mob\living\status_procs.dm"
#include "code\modules\mob\living\throwing.dm"
-#include "code\modules\mob\living\vv.dm"
#include "code\modules\mob\living\bot\assembly.dm"
#include "code\modules\mob\living\bot\bot.dm"
#include "code\modules\mob\living\bot\cleanbot.dm"
diff --git a/code/__DEFINES/_core.dm b/code/__DEFINES/_core.dm
index c90a3c5c705..4d3fd8b3c9a 100644
--- a/code/__DEFINES/_core.dm
+++ b/code/__DEFINES/_core.dm
@@ -15,3 +15,9 @@
/// A null statement to guard against EmptyBlock lint without necessitating the use of pass()
/// Used to avoid proc-call overhead. But use sparingly. Probably pointless in most places.
#define EMPTY_BLOCK_GUARD ;
+
+// THIS IS ON THIS FILE NOT IN `typeids.dm` DUE TO DEFINE LOAD ORDER!!!
+// Refs contain a type id within their string that can be used to identify byond types.
+// Custom types that we define don't get a unique id, but this is useful for identifying
+// types that don't normally have a way to run istype() on them.
+#define TYPEID(thing) copytext(REF(thing), 4, 6)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 2790d21bef0..ccfb7dd1814 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -17,20 +17,29 @@
#define isatom(A) (isloc(A))
-#define isdatum(D) (istype(D, /datum))
-
-#define ismutableappearance(D) (istype(D, /mutable_appearance))
-
-#define isimage(D) (istype(D, /image))
+#define isdatum(thing) (istype(thing, /datum))
#define isweakref(D) (istype(D, /datum/weakref))
+#define isimage(thing) (istype(thing, /image))
+
+GLOBAL_VAR_INIT(magic_appearance_detecting_image, new /image) // appearances are awful to detect safely, but this seems to be the best way ~ninjanomnom
+#define isappearance(thing) (!isimage(thing) && !ispath(thing) && istype(GLOB.magic_appearance_detecting_image, thing))
+
+// The filters list has the same ref type id as a filter, but isnt one and also isnt a list, so we have to check if the thing has Cut() instead
+GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur")))
+#define isfilter(thing) (!hascall(thing, "Cut") && TYPEID(thing) == GLOB.refid_filter)
+
+#define ismutableappearance(D) (istype(D, /mutable_appearance))
+
//Datums
#define isTaurTail(A) istype(A, /datum/sprite_accessory/tail/legacy_taur)
//Turfs
+//#define isturf(A) (istype(A, /turf)) This is actually a byond built-in. Added here for completeness sake.
+
#define isfloorturf(A) (istype(A, /turf/simulated/floor))
#define isopenturf(A) istype(A, /turf/simulated/open)
@@ -39,9 +48,10 @@
#define ismineralturf(A) istype(A, /turf/simulated/mineral)
-//Objs
+//Objects
///override the byond proc because it returns true on children of /atom/movable that aren't objs
#define isobj(A) istype(A, /obj)
+
#define isitem(A) (istype(A, /obj/item))
#define isclothing(A) (istype(A, /obj/item/clothing))
@@ -68,49 +78,50 @@
//Mobs
-#define isAI(A) istype(A, /mob/living/silicon/ai)
-
-#define isalien(A) istype(A, /mob/living/carbon/alien)
-
-#define isanimal_legacy_this_is_broken(A) istype(A, /mob/living/simple_animal)
+#define isliving(A) istype(A, /mob/living)
#define isbrain(A) istype(A, /mob/living/carbon/brain)
+//Carbon mobs
+
#define iscarbon(A) istype(A, /mob/living/carbon)
-#define iscorgi(A) istype(A, /mob/living/simple_mob/animal/passive/dog/corgi)
-
-#define isDrone(A) istype(A, /mob/living/silicon/robot/drone)
-
-#define isEye(A) istype(A, /mob/observer/eye)
-
#define ishuman(A) istype(A, /mob/living/carbon/human)
-#define isliving(A) istype(A, /mob/living)
+#define isdummy(A) (istype(A, /mob/living/carbon/human/dummy))
+//More carbon mobs
+#define isalien(A) istype(A, /mob/living/carbon/alien)
+
+//Silicon mobs
+#define issilicon(A) istype(A, /mob/living/silicon)
+#define isAI(A) istype(A, /mob/living/silicon/ai)
+#define isrobot(A) istype(A, /mob/living/silicon/robot)
+#define ispAI(A) istype(A, /mob/living/silicon/pai)
+#define isDrone(A) istype(A, /mob/living/silicon/robot/drone)
#define isMatriarchDrone(A) istype(A, /mob/living/silicon/robot/drone/matriarch)
-#define ismouse(A) istype(A, /mob/living/simple_mob/animal/passive/mouse/)
+
+//Simple animals
+#define issimplemob(A) istype(A, /mob/living/simple_mob)
+#define isanimal_legacy_this_is_broken(A) istype(A, /mob/living/simple_animal)
+
+#define iscorgi(A) istype(A, /mob/living/simple_mob/animal/passive/dog/corgi)
+#define ismouse(A) istype(A, /mob/living/simple_mob/animal/passive/mouse)
+#define isslime(A) istype(A, /mob/living/simple_mob/slime)
+#define isxeno(A) istype(A, /mob/living/simple_mob/xeno)
+
+//Eye mobs
+#define isEye(A) istype(A, /mob/observer/eye)
+
+//Dead mobs
+#define isobserver(A) istype(A, /mob/observer/dead)
#define isnewplayer(A) istype(A, /mob/new_player)
-#define isobserver(A) istype(A, /mob/observer/dead)
-
-#define ispAI(A) istype(A, /mob/living/silicon/pai)
-
-#define isrobot(A) istype(A, /mob/living/silicon/robot)
-
-#define issilicon(A) istype(A, /mob/living/silicon)
-
-#define isvoice(A) istype(A, /mob/living/voice)
-
-#define isslime(A) istype(A, /mob/living/simple_mob/slime)
-
+//Misc mobs
#define isbot(A) istype(A, /mob/living/bot)
-
-#define isxeno(A) istype(A, /mob/living/simple_mob/xeno)
-
-#define issimplemob(A) istype(A, /mob/living/simple_mob)
+#define isvoice(A) istype(A, /mob/living/voice)
/proc/is_species_type(atom/A, path)
if(!istype(A, /mob/living/carbon/human))
diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm
index 223d833136f..4128f7a0312 100644
--- a/code/__DEFINES/vv.dm
+++ b/code/__DEFINES/vv.dm
@@ -1,6 +1,8 @@
#define VV_NUM "Number"
#define VV_TEXT "Text"
-#define VV_MESSAGE "Mutiline Text"
+#define VV_MESSAGE "Multiline Text"
+#define VV_COLOR "Color"
+#define VV_COLOR_MATRIX "Color Matrix"
#define VV_ICON "Icon"
#define VV_ATOM_REFERENCE "Atom Reference"
#define VV_DATUM_REFERENCE "Datum Reference"
@@ -18,11 +20,13 @@
#define VV_NEW_LIST "New List"
#define VV_NEW_ALIST "New A-List"
#define VV_NULL "NULL"
+#define VV_INFINITY "Infinity"
#define VV_RESTORE_DEFAULT "Restore to Default"
#define VV_MARKED_DATUM "Marked Datum"
#define VV_BITFIELD "Bitfield"
#define VV_TEXT_LOCATE "Custom Reference Locate"
#define VV_PROCCALL_RETVAL "Return Value of Proccall"
+#define VV_WEAKREF "Weak Reference Datum"
#define VV_MSG_MARKED "Marked Object "
#define VV_MSG_EDITED "Var Edited "
@@ -32,11 +36,11 @@
#define VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD 150
//#define IS_VALID_ASSOC_KEY(V) (istext(V) || ispath(V) || isdatum(V) || islist(V))
-///hhmmm..
-#define IS_VALID_ASSOC_KEY(V) (!isnum(V))
+#define IS_VALID_ASSOC_KEY(V) (!isnum(V)) //hhmmm..
+
//General helpers
-#define VV_HREF_TARGET_INTERNAL(target, href_key) "?_src_=vars;[HrefToken()];[href_key]=TRUE;[VV_HK_TARGET]=[REF(target)]"
-#define VV_HREF_TARGETREF_INTERNAL(targetref, href_key) "?_src_=vars;[HrefToken()];[href_key]=TRUE;[VV_HK_TARGET]=[targetref]"
+#define VV_HREF_TARGET_INTERNAL(target, href_key) "byond://?_src_=vars;[HrefToken()];[href_key]=TRUE;[VV_HK_TARGET]=[REF(target)]"
+#define VV_HREF_TARGETREF_INTERNAL(targetref, href_key) "byond://?_src_=vars;[HrefToken()];[href_key]=TRUE;[VV_HK_TARGET]=[targetref]"
#define VV_HREF_TARGET(target, href_key, text) "[text] "
#define VV_HREF_TARGETREF(targetref, href_key, text) "[text] "
///for stuff like basic varedits, one variable
@@ -47,25 +51,16 @@
#define GET_VV_VAR_TARGET href_list[VV_HK_VARNAME]
//Helper for getting something to vv_do_topic in general
-#define VV_TOPIC_LINK(datum, href_key, text) "text "
+#define VV_TOPIC_LINK(datum, href_key, text) "text "
//Helpers for vv_get_dropdown()
#define VV_DROPDOWN_OPTION(href_key, name) . += "[name] "
-//! enums for vv target types
-/// is datum
-#define VVING_A_DATUM 1
-/// is list
-#define VVING_A_LIST 2
-/// is appearance
-#define VVING_A_APPEARANCE 3
-
// VV HREF KEYS
#define VV_HK_TARGET "target"
///name or index of var for 1 variable targetting hrefs.
#define VV_HK_VARNAME "targetvar"
-/// to view an appearance virtual object
-#define VV_HK_VIEW_APPEARANCE "vv_appearance"
+
// vv_do_list() keys
#define VV_HK_LIST_ADD "listadd"
#define VV_HK_LIST_EDIT "listedit"
@@ -87,6 +82,8 @@
#define VV_HK_CALLPROC "proc_call"
#define VV_HK_MARK "mark"
#define VV_HK_ADDCOMPONENT "addcomponent"
+#define VV_HK_REMOVECOMPONENT "removecomponent"
+#define VV_HK_MASS_REMOVECOMPONENT "massremovecomponent"
#define VV_HK_MODIFY_TRAITS "modtraits"
// /atom
@@ -98,9 +95,24 @@
#define VV_HK_TRIGGER_EXPLOSION "explode"
#define VV_HK_EDIT_FILTERS "edit_filters"
#define VV_HK_EDIT_COLOR_MATRIX "edit_color_matrix"
+#define VV_HK_TEST_MATRIXES "test_matrixes"
#define VV_HK_EDIT_ARMOR "edit_armor"
+// /atom/movable
+#define VV_HK_GET_MOVABLE "get_movable"
+
+// /obj
+#define VV_HK_MASS_DEL_TYPE "mass_delete_type"
+
// /mob
+#define VV_HK_GIB "gib"
+#define VV_HK_GIVE_SPELL "give_spell"
+#define VV_HK_GIVE_DISEASE "give_disease"
+#define VV_HK_GODMODE "godmode"
+#define VV_HK_DROP_ALL "dropall"
+#define VV_HK_PLAYER_PANEL "player_panel"
+#define VV_HK_BUILDMODE "buildmode"
+#define VV_HK_DIRECT_CONTROL "direct_control"
#define VV_HK_TRIGGER_OFFER_MOB_TO_GHOSTS "offer_mob_to_ghosts"
/// used on /mob as well as /obj/item/organ
#define VV_HK_ADD_PHYSIOLOGY_MODIFIER "add_physiology_mod"
@@ -114,3 +126,8 @@
#define VV_HK_ID_MOD "id_mod"
#define VV_HK_WEAKREF_RESOLVE "weakref_resolve"
+
+// Flags for debug_variable() that do little things to what we end up rendering
+
+/// ALWAYS render a reduced list, useful for fuckoff big datums that need to be condensed for the sake of client load
+#define VV_ALWAYS_CONTRACT_LIST (1<<0)
diff --git a/code/__HELPERS/_lists_tg.dm b/code/__HELPERS/_lists_tg.dm
index 6d04dfad755..017f6e397b1 100644
--- a/code/__HELPERS/_lists_tg.dm
+++ b/code/__HELPERS/_lists_tg.dm
@@ -1,3 +1,11 @@
+// Generic listoflist safe add and removal macros:
+///If value is a list, wrap it in a list so it can be used with list add/remove operations
+#define LIST_VALUE_WRAP_LISTS(value) (islist(value) ? list(value) : value)
+///Add an untyped item to a list, taking care to handle list items by wrapping them in a list to remove the footgun
+#define UNTYPED_LIST_ADD(list, item) (list += LIST_VALUE_WRAP_LISTS(item))
+///Remove an untyped item to a list, taking care to handle list items by wrapping them in a list to remove the footgun
+#define UNTYPED_LIST_REMOVE(list, item) (list -= LIST_VALUE_WRAP_LISTS(item))
+
//Removes any null entries from the list
//Returns TRUE if the list had nulls, FALSE otherwise
diff --git a/code/__HELPERS/ref.dm b/code/__HELPERS/ref.dm
index b22983ef6ad..a5e73cec571 100644
--- a/code/__HELPERS/ref.dm
+++ b/code/__HELPERS/ref.dm
@@ -1,10 +1,10 @@
/**
* \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
* If it ever becomes necesary to get a more performant REF(), this lies here in wait
- * #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
+ * #define REF(thing) (thing && isdatum(thing) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : ref(thing))
*/
/proc/REF(input)
- if(istype(input, /datum))
+ if(isdatum(input))
var/datum/thing = input
if(thing.datum_flags & DF_USE_TAG)
if(!thing.tag)
diff --git a/code/_globals/lists/admin.dm b/code/_globals/lists/admin.dm
new file mode 100644
index 00000000000..003fff141aa
--- /dev/null
+++ b/code/_globals/lists/admin.dm
@@ -0,0 +1,16 @@
+
+// A list of all the special byond lists that need to be handled different by vv
+GLOBAL_LIST_INIT(vv_special_lists, init_special_list_names())
+
+/proc/init_special_list_names()
+ var/list/output = list()
+ var/obj/sacrifice = new
+ for(var/varname in sacrifice.vars)
+ var/value = sacrifice.vars[varname]
+ if(!islist(value))
+ if(!isdatum(value) && hascall(value, "Cut"))
+ output += varname
+ continue
+ if(isnull(locate(REF(value))))
+ output += varname
+ return output
diff --git a/code/_globals/lists/misc.dm b/code/_globals/lists/misc.dm
index 50633060ed1..ec2c596bfaa 100644
--- a/code/_globals/lists/misc.dm
+++ b/code/_globals/lists/misc.dm
@@ -12,3 +12,5 @@ GLOBAL_LIST_EMPTY(tagger_locations)
/// Cache of the width and height of icon files, to avoid repeating the same expensive operation
GLOBAL_LIST_EMPTY(icon_dimensions)
+
+GLOBAL_LIST_INIT(color_vars, list("color"))
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index 745ad7051e4..34de9c789ba 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -42,14 +42,18 @@
VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete")
VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player")
VV_DROPDOWN_OPTION(VV_HK_ADDCOMPONENT, "Add Component/Element")
+ VV_DROPDOWN_OPTION(VV_HK_REMOVECOMPONENT, "Remove Component/Element")
+ VV_DROPDOWN_OPTION(VV_HK_MASS_REMOVECOMPONENT, "Mass Remove Component/Element")
VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRAITS, "Modify Traits")
-//This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks!
-//href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables!
-//This proc is for "high level" actions like admin heal/set species/etc/etc. The low level debugging things should go in admin/view_variables/topic_basic.dm incase this runtimes.
+/**
+ * This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks!
+ * href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables!
+ * This proc is for "high level" actions like admin heal/set species/etc/etc. The low level debugging things should go in admin/view_variables/topic_basic.dm in case this runtimes.
+ */
/datum/proc/vv_do_topic(list/href_list)
- if(!usr || !usr.client || !usr.client.holder || !check_rights(NONE))
- return FALSE //This is VV, not to be called by anything else.
+ if(!usr || !usr.client || !usr.client.holder || !check_rights(R_VAREDIT))
+ return FALSE //This is VV, not to be called by anything else.
if(href_list[VV_HK_MODIFY_TRAITS])
usr.client.holder.modify_traits(src)
return TRUE
diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm
index e0ddef6c723..767dbccc344 100644
--- a/code/datums/weakref.dm
+++ b/code/datums/weakref.dm
@@ -68,7 +68,7 @@
var/reference
/datum/weakref/New(datum/thing)
- reference = ref(thing)
+ reference = REF(thing)
/datum/weakref/Destroy(force)
var/datum/target = resolve()
diff --git a/code/game/atoms/atom-vv.dm b/code/game/atoms/atom-vv.dm
index 1ec689354fe..4c62851fa68 100644
--- a/code/game/atoms/atom-vv.dm
+++ b/code/game/atoms/atom-vv.dm
@@ -19,7 +19,7 @@
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
VV_DROPDOWN_OPTION(VV_HK_EDIT_FILTERS, "Edit Filters")
VV_DROPDOWN_OPTION(VV_HK_EDIT_COLOR_MATRIX, "Edit Color as Matrix")
- // VV_DROPDOWN_OPTION(VV_HK_TEST_MATRIXES, "Test Matrices")
+ VV_DROPDOWN_OPTION(VV_HK_TEST_MATRIXES, "Test Matrices")
// VV_DROPDOWN_OPTION(VV_HK_ADD_AI, "Add AI controller")
VV_DROPDOWN_OPTION(VV_HK_EDIT_ARMOR, "Edit Armor")
@@ -29,14 +29,16 @@
if(!.)
return
- if(href_list[VV_HK_ADD_REAGENT] && check_rights(R_VAREDIT))
+ if(href_list[VV_HK_ADD_REAGENT])
+ if(!check_rights(R_VAREDIT))
+ return
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", "Search", "Choose from a list", "I'm feeling lucky"))
+ switch(tgui_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)
@@ -50,7 +52,7 @@
else
valid_id = TRUE
if(!valid_id)
- to_chat(usr, "A reagent with that ID doesn't exist! ")
+ to_chat(usr, SPAN_WARNING("A reagent with that ID doesn't exist!"), confidential = TRUE)
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), GLOBAL_PROC_REF(cmp_typepaths_asc))
if("I'm feeling lucky")
@@ -68,7 +70,9 @@
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))
+ if(href_list[VV_HK_MODIFY_TRANSFORM])
+ if(!check_rights(R_VAREDIT))
+ return
var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate","Shear")
var/matrix/M = transform
if(!result)
@@ -109,8 +113,8 @@
num_spins = -1
if(!num_spins)
return
- var/spin_speed = input(usr, "How fast?", "Spin Animation") as null|num
- if(!spin_speed)
+ var/spins_per_sec = input(usr, "How many spins per second?", "Spin Animation") as null|num
+ if(!spins_per_sec)
return
var/direction = input(usr, "Which direction?", "Spin Animation") in list("Clockwise", "Counter-clockwise")
switch(direction)
@@ -120,7 +124,7 @@
direction = 0
else
return
- SpinAnimation(spin_speed, num_spins, direction)
+ SpinAnimation(1 SECONDS / spins_per_sec, num_spins, direction)
if(href_list[VV_HK_STOP_ALL_ANIMATIONS])
if(!check_rights(R_VAREDIT))
@@ -130,13 +134,20 @@
animate(src, transform = null, flags = ANIMATION_END_NOW) // Literally just fucking stop animating entirely because admin said so
return
- if(href_list[VV_HK_EDIT_FILTERS] && check_rights(R_VAREDIT))
- var/client/C = usr.client
- C?.open_filter_editor(src)
+ if(href_list[VV_HK_EDIT_FILTERS])
+ if(!check_rights(R_VAREDIT))
+ return
+ usr.client?.open_filter_editor(src)
- if(href_list[VV_HK_EDIT_COLOR_MATRIX] && check_rights(R_VAREDIT))
- var/client/C = usr.client
- C?.open_color_matrix_editor(src)
+ if(href_list[VV_HK_EDIT_COLOR_MATRIX])
+ if(!check_rights(R_VAREDIT))
+ return
+ usr.client?.open_color_matrix_editor(src)
+
+ if(href_list[VV_HK_TEST_MATRIXES])
+ if(!check_rights(R_VAREDIT))
+ return
+ usr.client?.open_matrix_tester(src)
if(href_list[VV_HK_EDIT_ARMOR] && check_rights(R_VAREDIT))
// todo: tgui armor editor?
@@ -157,10 +168,10 @@
/atom/vv_get_header()
. = ..()
- if(!isliving(src))
- var/refid = REF(src)
- . += "[VV_HREF_TARGETREF_1V(refid, VV_HK_BASIC_EDIT, "[src]", NAMEOF(src, name))]"
- . += "<< [dir2text(dir) || dir] >> "
+ var/refid = REF(src)
+ // . += "[VV_HREF_TARGETREF(refid, VV_HK_AUTO_RENAME, "[src] ")]"
+ . += "[src] "
+ . += "<< [dir2text(dir) || dir] >> "
/**
* call back when a var is edited on this atom
diff --git a/code/game/atoms/movable/movable.dm b/code/game/atoms/movable/movable.dm
index 887b73b3430..03880dd2636 100644
--- a/code/game/atoms/movable/movable.dm
+++ b/code/game/atoms/movable/movable.dm
@@ -569,3 +569,21 @@
if(!isnull(render_target))
return
render_target = "[make_us_invisible? "*":""][REF(src)]-[rand(1,1000)]-[world.time]"
+
+/atom/movable/vv_get_dropdown()
+ . = ..()
+ VV_DROPDOWN_OPTION("", "---------")
+ VV_DROPDOWN_OPTION(VV_HK_GET_MOVABLE, "Get Movable")
+
+/atom/movable/vv_do_topic(list/href_list)
+ . = ..()
+
+ if(!.)
+ return
+
+ if(href_list[VV_HK_GET_MOVABLE])
+ if(!check_rights(R_ADMIN))
+ return
+ if(QDELETED(src))
+ return
+ forceMove(get_turf(usr))
diff --git a/code/game/objects/obj.dm b/code/game/objects/obj.dm
index ec6442c14e6..73ee673d647 100644
--- a/code/game/objects/obj.dm
+++ b/code/game/objects/obj.dm
@@ -1044,3 +1044,51 @@
switch(var_name)
if(NAMEOF(src, hides_underfloor))
set_hides_underfloor(var_value)
+
+/obj/vv_get_dropdown()
+ . = ..()
+ VV_DROPDOWN_OPTION("", "---")
+ VV_DROPDOWN_OPTION(VV_HK_MASS_DEL_TYPE, "Delete all of type")
+
+/obj/vv_do_topic(list/href_list)
+ . = ..()
+
+ if(!.)
+ return
+
+ if(href_list[VV_HK_MASS_DEL_TYPE])
+ if(!check_rights(R_DEBUG|R_SERVER))
+ return
+ var/action_type = tgui_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(tgui_alert(usr, "Are you really sure you want to delete all objects of type [type]?",,list("Yes","No")) != "Yes")
+ return
+ if(tgui_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) "))
diff --git a/code/modules/admin/view_variables/debug_variable_appearance.dm b/code/modules/admin/view_variables/debug_variable_appearance.dm
new file mode 100644
index 00000000000..36cd09a169a
--- /dev/null
+++ b/code/modules/admin/view_variables/debug_variable_appearance.dm
@@ -0,0 +1,73 @@
+/// Shows a header name on top when you investigate an appearance/image
+/image/vv_get_header()
+ . = list()
+ var/icon_name = "[icon || "null"] "
+ . += replacetext(icon_name, "icons/obj", "") // shortens the name. We know the path already.
+ if(icon)
+ . += icon_state ? "\"[icon_state]\"" : "(icon_state = null)"
+
+/// Makes nice short vv names for images
+/image/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ var/display_name = "[type]"
+ if("[src]" != "[type]") // If we have a name var, let's use it.
+ display_name = "[src] [type]"
+
+ var/display_value
+ var/list/icon_file_name = splittext("[icon]", "/")
+ if(length(icon_file_name))
+ display_value = icon_file_name[length(icon_file_name)]
+ else
+ display_value = "null"
+
+ if(icon_state)
+ display_value = "[display_value]:[icon_state]"
+
+ var/display_ref = get_vv_link_ref()
+ return "[display_name] ([display_value] ) [display_ref] "
+
+/// Returns the ref string to use when displaying this image in the vv menu of something else
+/image/proc/get_vv_link_ref()
+ return REF(src)
+
+// It is endlessly annoying to display /appearance directly for stupid byond reasons, so we copy everything we care about into a holder datum
+// That we can override procs on and store other vars on and such.
+/mutable_appearance/appearance_mirror
+ // So people can see where it came from
+ var/appearance_ref
+
+// arg is actually an appearance, typed as mutable_appearance as closest mirror
+/mutable_appearance/appearance_mirror/New(mutable_appearance/appearance_father)
+ . = ..() // /mutable_appearance/New() copies over all the appearance vars MAs care about by default
+ appearance_ref = REF(appearance_father)
+
+// This means if the appearance loses refs before a click it's gone, but that's consistent to other datums so it's fine
+// Need to ref the APPEARANCE because we just free on our own, which sorta fucks this operation up you know?
+/mutable_appearance/appearance_mirror/get_vv_link_ref()
+ return appearance_ref
+
+/mutable_appearance/appearance_mirror/can_vv_get(var_name)
+ var/static/datum/beloved = new()
+ if(beloved.vars.Find(var_name)) // If datums have it, get out
+ return FALSE
+ // Could make an argument for this but I think they will just confuse people, so yeeet
+ if(var_name == NAMEOF(src, vis_contents))
+ return FALSE
+ return ..()
+
+/mutable_appearance/appearance_mirror/vv_get_var(var_name)
+ // No editing for you
+ var/value = vars[var_name]
+ return "(READ ONLY) [var_name] = [_debug_variable_value(var_name, value, 0, src, sanitize = TRUE, display_flags = NONE)] "
+
+/mutable_appearance/appearance_mirror/vv_get_dropdown()
+ SHOULD_CALL_PARENT(FALSE)
+
+ . = list()
+ VV_DROPDOWN_OPTION("", "---")
+ VV_DROPDOWN_OPTION(VV_HK_CALLPROC, "Call Proc")
+ VV_DROPDOWN_OPTION(VV_HK_MARK, "Mark Object")
+ VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete")
+ VV_DROPDOWN_OPTION(VV_HK_EXPOSE, "Show VV To Player")
+
+/proc/get_vv_appearance(mutable_appearance/appearance) // actually appearance yadeeyada
+ return new /mutable_appearance/appearance_mirror(appearance)
diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm
index 133dfff0884..78f732763bb 100644
--- a/code/modules/admin/view_variables/debug_variables.dm
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -1,86 +1,132 @@
#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/list/D_l = D
+/proc/debug_variable(name, value, level, datum/owner, sanitize = TRUE, display_flags = NONE) //if D is a list, name will be index, and value will be assoc value.
+ if(owner)
+ if(islist(owner))
+ var/list/list_owner = owner
var/index = name
- if (value)
- name = D_l[name] //name is really the index until this line
+ if (isnull(value))
+ value = list_owner[name]
else
- value = D_l[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)]) "
+ name = list_owner[name] //name is really the index until this line
+ . = " ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(owner, 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)]) "
+ . = " ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_MASSEDIT, "M", name)]) "
else
- header = " "
+ . = " "
- var/item
- var/datum/bitfield/maybe_bitfield
- if (isnull(value))
- item = "[VV_HTML_ENCODE(name)] = null "
+ var/name_part = VV_HTML_ENCODE(name)
+ if(level > 0 || islist(owner)) //handling keys in assoc lists
+ if(istype(name,/datum))
+ name_part = "[VV_HTML_ENCODE(name)] [REF(name)] "
+ else if(islist(name))
+ var/list/list_value = name
+ name_part = " /list ([length(list_value)]) [REF(name)] "
- else if(IS_APPEARANCE(value))
- item = "[VV_HTML_ENCODE(name)] [ref(value)] = /appearance "
+ . = "[.][name_part] = "
- else if (istext(value))
- item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\" "
+ var/item = _debug_variable_value(name, value, level, owner, sanitize, display_flags)
- else if (isicon(value))
+ return "[.][item] "
+
+// This is split into a separate proc mostly to make errors that happen not break things too much
+/proc/_debug_variable_value(name, value, level, datum/owner, sanitize, display_flags)
+ if(isappearance(value))
+ value = get_vv_appearance(value)
+
+ . = "DISPLAY_ERROR: ([value] [REF(value)])" // Make sure this line can never runtime
+
+ if(isnull(value))
+ return "null "
+
+ if(istext(value))
+ return "\"[VV_HTML_ENCODE(value)]\" "
+
+ if(isicon(value))
#ifdef VARSICON
- var/icon/I = icon(value)
+ var/icon/icon_value = 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] ) "
+ var/rname = "tmp[REF(icon_value)][rnd].png"
+ usr << browse_rsc(icon_value, rname)
+ return "([value] ) "
#else
- item = "[VV_HTML_ENCODE(name)] = /icon ([value] )"
+ return "/icon ([value] )"
#endif
- else if (isfile(value))
- item = "[VV_HTML_ENCODE(name)] = '[value]' "
+ if(isfilter(value))
+ var/datum/filter_value = value
+ return "/filter ([filter_value.type] [REF(filter_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]"
+ if(isfile(value))
+ return "'[value]' "
- else if (islist(value))
- var/list/L = value
+ if(isdatum(value))
+ var/datum/datum_value = value
+ return datum_value.debug_variable_value(name, level, owner, sanitize, display_flags)
+
+ if(islist(value) || (name in GLOB.vv_special_lists)) // Some special lists aren't detectable as a list through istype
+ var/list/list_value = value
var/list/items = list()
- // don't expand if it's:
- // 1. overlays - this info is rarely needing to be accessed unless you're doing overlay debugging
- // 2. underlays - ditto
- // 3. GLOB - there's a metric ton of lists on global variables and we want to avoid admins needing to download MB's of data instantly
- // 4. if the list is too long otherwise
- if (L.len > 0 && !(name == "underlays" || name == "overlays" || D == GLOB || 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]
+
+ // This is because some lists either don't count as lists or a locate on their ref will return null
+ var/link_vars = "Vars=[REF(value)]"
+ if(name in GLOB.vv_special_lists)
+ link_vars = "Vars=[REF(owner)];special_varname=[name]"
+
+ if (!(display_flags & VV_ALWAYS_CONTRACT_LIST) && list_value.len > 0 && list_value.len <= (IS_NORMAL_LIST(list_value) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD))
+ for (var/i in 1 to list_value.len)
+ var/key = list_value[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 ""
+ if (IS_NORMAL_LIST(list_value) && !isnum(key))
+ val = list_value[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]) "
+ return "/list ([list_value.len]) "
+ return "/list ([list_value.len]) "
- else if (istype(D) && isnum(value) && (maybe_bitfield = fetch_bitfield(D.type, name)))
- var/list/flags = list()
+ // if it's a number, is it a bitflag?
+ var/list/valid_bitflags = list()
+ if(!isnum(name) && istype(owner))
+ var/datum/bitfield/maybe_bitfield = fetch_bitfield(owner.type, name)
+ if (!maybe_bitfield)
+ return "[VV_HTML_ENCODE(value)] "
for(var/i in 1 to maybe_bitfield.get_declared_count())
var/bit = maybe_bitfield.bits[i]
- if(value & bit)
- flags += maybe_bitfield.names[i]
- item = "[VV_HTML_ENCODE(name)] = [jointext(flags, ", ")]"
- else
- item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)] "
+ valid_bitflags[maybe_bitfield.get_bit_name(bit)] = bit
- return "[header][item]"
+ if(!length(valid_bitflags))
+ return "[VV_HTML_ENCODE(value)] "
+
+ var/list/flags = list()
+ for (var/bit_name in valid_bitflags)
+ if (value & valid_bitflags[bit_name])
+ flags += bit_name
+ if(length(flags))
+ return "[VV_HTML_ENCODE(flags.Join(", "))]"
+ return "NONE"
+
+/datum/proc/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ if("[src]" != "[type]") // If we have a name var, let's use it.
+ return "[src] [type] [REF(src)] "
+ else
+ return "[type] [REF(src)] "
+
+/datum/weakref/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ . = ..()
+ return "[.] (Resolve) "
+
+/matrix/debug_variable_value(name, level, datum/owner, sanitize, display_flags)
+ return {"
+
+
+
+ [a] [d] 0
+ [b] [e] 0
+ [c] [f] 1
+
+
"} //TODO link to modify_transform wrapper for all matrices
#undef VV_HTML_ENCODE
diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm
index 8e21287e482..fb04c5ba1d9 100644
--- a/code/modules/admin/view_variables/get_variables.dm
+++ b/code/modules/admin/view_variables/get_variables.dm
@@ -11,6 +11,8 @@
else if(istext(var_value))
if(findtext(var_value, "\n"))
. = VV_MESSAGE
+ else if(findtext(var_value, GLOB.is_color))
+ . = VV_COLOR
else
. = VV_TEXT
@@ -26,7 +28,10 @@
else if(istype(var_value, /client))
. = VV_CLIENT
- else if(istype(var_value, /datum))
+ else if(isweakref(var_value))
+ . = VV_WEAKREF
+
+ else if(isdatum(var_value))
. = VV_DATUM_REFERENCE
else if(ispath(var_value))
@@ -38,7 +43,11 @@
. = VV_TYPE
else if(islist(var_value))
- . = VV_LIST
+ if(var_name in GLOB.color_vars)
+ . = VV_COLOR_MATRIX
+ else
+ . = VV_LIST
+
else if(isalist(var_value))
. = VV_ALIST
@@ -56,6 +65,8 @@
VV_TEXT,
VV_MESSAGE,
VV_ICON,
+ VV_COLOR,
+ VV_COLOR_MATRIX,
VV_ATOM_REFERENCE,
VV_DATUM_REFERENCE,
VV_MOB_REFERENCE,
@@ -70,9 +81,11 @@
VV_NEW_LIST,
VV_NEW_ALIST,
VV_NULL,
+ VV_INFINITY,
VV_RESTORE_DEFAULT,
VV_TEXT_LOCATE,
VV_PROCCALL_RETVAL,
+ VV_WEAKREF,
)
var/markstring
@@ -178,6 +191,19 @@
return
.["value"] = things[value]
+ if(VV_WEAKREF)
+ 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"] = WEAKREF(things[value])
+
if(VV_CLIENT)
.["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients
if(.["value"] == null)
@@ -250,8 +276,22 @@
.["value"] = newguy
if(VV_NEW_LIST)
- .["value"] = list()
.["type"] = /list
+ var/list/value = list()
+
+ var/expectation = alert("Would you like to populate the list", "Populate List?", "Yes", "No")
+ if(!expectation || expectation == "No")
+ .["value"] = value
+ return .
+
+ var/list/insert = null
+ while(TRUE)
+ insert = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
+ if(!insert["class"])
+ break
+ value += LIST_VALUE_WRAP_LISTS(insert["value"])
+
+ .["value"] = value
if(VV_NEW_ALIST)
.["value"] = alist()
@@ -265,14 +305,25 @@
break
D = locate(ref)
if(!D)
- alert("Invalid ref!")
- continue
- if(!istype(D))
- alert("Not a datum.")
+ tgui_alert(usr,"Invalid ref!")
continue
if(!D.can_vv_mark())
- alert("Datum can not be marked!")
+ tgui_alert(usr,"Datum can not be marked!")
continue
while(!D)
.["type"] = D.type
.["value"] = D
+
+ if(VV_COLOR)
+ .["value"] = input("Enter new color:", "Color", current_value) as color|null
+ if(.["value"] == null)
+ .["class"] = null
+ return
+
+ if(VV_COLOR_MATRIX)
+ .["value"] = open_color_matrix_editor()
+ if(.["value"] == COLOR_MATRIX_IDENTITY) //identity is equivalent to null
+ .["class"] = null
+
+ if(VV_INFINITY)
+ .["value"] = INFINITY
diff --git a/code/modules/admin/view_variables/helpers_LEGACY.dm b/code/modules/admin/view_variables/helpers_LEGACY.dm
index faa691443ea..b4d2b302832 100644
--- a/code/modules/admin/view_variables/helpers_LEGACY.dm
+++ b/code/modules/admin/view_variables/helpers_LEGACY.dm
@@ -7,21 +7,11 @@
/mob/get_view_variables_options_legacy()
return ..() + {"
- Show player panel
- ---
Give Modifier
- Give Spell
- Give Disease
- Give TG-style Disease
- Toggle Godmode
- Toggle Build Mode
Make Space Ninja
Make 2spooky
- Assume Direct Control
- Drop Everything
-
Regenerate Icons
Add Language
Remove Language
@@ -32,10 +22,6 @@
Add Verb
Remove Verb
- ---
- Gib
- Trigger explosion
- Trigger EM pulse
"}
/mob/living/carbon/human/get_view_variables_options_legacy()
@@ -46,8 +32,3 @@
Make monkey
Make alien
"}
-
-/obj/get_view_variables_options_legacy()
- return ..() + {"
- Delete all of type
- "}
diff --git a/code/modules/admin/view_variables/mark_datum.dm b/code/modules/admin/view_variables/mark_datum.dm
index b608c57abab..756fc74da1c 100644
--- a/code/modules/admin/view_variables/mark_datum.dm
+++ b/code/modules/admin/view_variables/mark_datum.dm
@@ -2,10 +2,17 @@
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, TYPE_PROC_REF(/datum/admins, handle_marked_del))
vv_update_display(D, "marked", VV_MSG_MARKED)
+/datum/admins/proc/handle_marked_del(datum/source)
+ SIGNAL_HANDLER
+ UnregisterSignal(marked_datum, COMSIG_PARENT_QDELETING)
+ marked_datum = null
+
/client/proc/mark_datum_mapview(datum/D in world)
set category = "Debug"
set name = "Mark Object"
diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm
index 1865898d413..8e2ab1053b3 100644
--- a/code/modules/admin/view_variables/modify_variables.dm
+++ b/code/modules/admin/view_variables/modify_variables.dm
@@ -1,15 +1,15 @@
-GLOBAL_LIST_INIT(VVlocked, list("vars", "datum_flags", "client", "mob")) //Requires DEBUG
+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_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_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"))
+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")
+ var/process_vars = tgui_alert(usr,"\[] detected in string, process as variables?","Process Variables?",list("Yes","No"))
if(process_vars == "Yes")
. = string2listofvars(new_var, O)
@@ -17,14 +17,14 @@ GLOBAL_PROTECT(VVpixelmovement)
//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)
+/client/proc/vv_subtype_prompt(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 (subtypes?.len)
+ switch(tgui_alert(usr,"Strict object type detection?", "Type detection", list("Strictly this type","This type and subtypes", "Cancel")))
if("Strictly this type")
return FALSE
if("This type and subtypes")
@@ -50,14 +50,14 @@ GLOBAL_PROTECT(VVpixelmovement)
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
+ // fancy type,
+ // fancy type with the base type removed from the begaining,
+ // the type with the base type removed from the begaining
var/fancytype = types[D.type]
if (findtext(fancytype, types[type]))
- fancytype = copytext(fancytype, length(types[type])+1)
- var/shorttype = copytext("[D.type]", length("[type]")+1)
- if (length(shorttype) > length(fancytype))
+ fancytype = copytext(fancytype, length(types[type]) + 1)
+ var/shorttype = copytext("[D.type]", length("[type]") + 1)
+ if (length_char(shorttype) > length_char(fancytype))
shorttype = fancytype
if (!length(shorttype))
shorttype = "/"
@@ -65,7 +65,6 @@ GLOBAL_PROTECT(VVpixelmovement)
.["[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)
@@ -79,7 +78,6 @@ GLOBAL_PROTECT(VVpixelmovement)
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"]
@@ -95,15 +93,14 @@ GLOBAL_PROTECT(VVpixelmovement)
if (O)
L = L.Copy()
- L.len++
- L[L.len] = var_value
+ L += list(var_value) //var_value could be a list
- switch(alert("Would you like to associate a value with the list entry?",,"Yes","No"))
+ switch(tgui_alert(usr,"Would you like to associate a value with the list entry?",,list("Yes","No")))
if("Yes")
L[var_value] = mod_list_add_ass(O) //hehe
if (O)
if (O.vv_edit_var(objectvar, L) == FALSE)
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]")
@@ -113,11 +110,11 @@ GLOBAL_PROTECT(VVpixelmovement)
if(!check_rights(R_VAREDIT))
return
if(!istype(L, /list))
- to_chat(src, "Not a List.")
+ to_chat(src, "Not a List.", confidential = TRUE)
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")
+ var/confirm = tgui_alert(usr, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", list("Continue", "Abort"))
if(confirm != "Continue")
return
@@ -145,7 +142,7 @@ GLOBAL_PROTECT(VVpixelmovement)
L = L.Copy()
listclearnulls(L)
if (!O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS")
@@ -155,7 +152,7 @@ GLOBAL_PROTECT(VVpixelmovement)
if(variable == "(CLEAR DUPES)")
L = uniqueList(L)
if (!O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES")
@@ -165,7 +162,7 @@ GLOBAL_PROTECT(VVpixelmovement)
if(variable == "(SHUFFLE)")
L = shuffle(L)
if (!O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE")
@@ -174,12 +171,11 @@ GLOBAL_PROTECT(VVpixelmovement)
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")
+ var/prompt = tgui_alert(usr, "Do you want to edit the key or its assigned value?", "Associated List", list("Key", "Assigned Value", "Cancel"))
if (prompt == "Cancel")
return
if (prompt == "Assigned Value")
@@ -187,7 +183,7 @@ GLOBAL_PROTECT(VVpixelmovement)
assoc_key = L[index]
var/default
var/variable
- var/old_assoc_value //EXPERIMENTAL - Keep old associated value while modifying key, if any
+ var/old_assoc_value //EXPERIMENTAL - Keep old associated value while modifying key, if any
if(is_normal_list)
if (assoc)
variable = L[assoc_key]
@@ -202,9 +198,9 @@ GLOBAL_PROTECT(VVpixelmovement)
default = vv_get_class(objectvar, variable)
- to_chat(src, "Variable appears to be [uppertext(default)] .")
+ to_chat(src, "Variable appears to be [uppertext(default)] .", confidential = TRUE)
- to_chat(src, "Variable contains: [variable]")
+ to_chat(src, "Variable contains: [variable]", confidential = TRUE)
if(default == VV_NUM)
var/dir_text = ""
@@ -220,7 +216,7 @@ GLOBAL_PROTECT(VVpixelmovement)
dir_text += "WEST"
if(dir_text)
- to_chat(usr, "If a direction, direction is: [dir_text]")
+ to_chat(usr, "If a direction, direction is: [dir_text]", confidential = TRUE)
var/original_var = variable
@@ -250,7 +246,7 @@ GLOBAL_PROTECT(VVpixelmovement)
L.Cut(index, index+1)
if (O)
if (O.vv_edit_var(objectvar, L))
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]")
@@ -262,7 +258,6 @@ GLOBAL_PROTECT(VVpixelmovement)
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
@@ -272,7 +267,7 @@ GLOBAL_PROTECT(VVpixelmovement)
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.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]")
log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]")
@@ -300,7 +295,7 @@ GLOBAL_PROTECT(VVpixelmovement)
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])")
+ to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])", confidential = TRUE)
return
variable = param_var_name
@@ -325,11 +320,11 @@ GLOBAL_PROTECT(VVpixelmovement)
var/default = vv_get_class(variable, var_value, O)
if(isnull(default))
- to_chat(src, "Unable to determine variable type.")
+ to_chat(src, "Unable to determine variable type.", confidential = TRUE)
else
- to_chat(src, "Variable appears to be [uppertext(default)] .")
+ to_chat(src, "Variable appears to be [uppertext(default)] .", confidential = TRUE)
- to_chat(src, "Variable contains: [var_value]")
+ to_chat(src, "Variable contains: [var_value]", confidential = TRUE)
if(default == VV_NUM)
var/dir_text = ""
@@ -344,7 +339,7 @@ GLOBAL_PROTECT(VVpixelmovement)
dir_text += "WEST"
if(dir_text)
- to_chat(src, "If a direction, direction is: [dir_text]")
+ to_chat(src, "If a direction, direction is: [dir_text]", confidential = TRUE)
if(autodetect_class && default != VV_NULL)
if (default == VV_TEXT)
@@ -391,7 +386,7 @@ GLOBAL_PROTECT(VVpixelmovement)
if (O.vv_edit_var(variable, var_new) == FALSE)
- to_chat(src, "Your edit was rejected by the object.")
+ to_chat(src, "Your edit was rejected by the object.", confidential = TRUE)
return
vv_update_display(O, "varedited", VV_MSG_EDITED)
log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]")
diff --git a/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm b/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
new file mode 100644
index 00000000000..625057051fe
--- /dev/null
+++ b/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
@@ -0,0 +1,80 @@
+
+/**
+ * ## nobody wants to learn matrix math!
+ *
+ * More than just a completely true statement, this datum is created as a tgui interface
+ * allowing you to modify each vector until you know what you're doing.
+ * Much like filterrific, 'nobody wants to learn matrix math' is meant for developers like you and I
+ * to implement interesting matrix transformations without the hassle if needing to know... algebra? Damn, i'm stupid.
+ */
+/datum/nobody_wants_to_learn_matrix_math
+ var/atom/target
+ var/matrix/testing_matrix
+
+/datum/nobody_wants_to_learn_matrix_math/New(atom/target)
+ src.target = target
+ testing_matrix = matrix(target.transform)
+
+/datum/nobody_wants_to_learn_matrix_math/Destroy(force)
+ QDEL_NULL(testing_matrix)
+ return ..()
+
+/datum/nobody_wants_to_learn_matrix_math/ui_state(mob/user)
+ return ADMIN_STATE(R_VAREDIT)
+
+/datum/nobody_wants_to_learn_matrix_math/on_ui_close(mob/user, datum/tgui/ui, embedded)
+ qdel(src)
+
+/datum/nobody_wants_to_learn_matrix_math/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "MatrixMathTester")
+ ui.open()
+
+/datum/nobody_wants_to_learn_matrix_math/ui_data()
+ var/list/data = list()
+ data["matrix_a"] = testing_matrix.a
+ data["matrix_b"] = testing_matrix.b
+ data["matrix_c"] = testing_matrix.c
+ data["matrix_d"] = testing_matrix.d
+ data["matrix_e"] = testing_matrix.e
+ data["matrix_f"] = testing_matrix.f
+ data["pixelated"] = target.appearance_flags & PIXEL_SCALE
+ return data
+
+/datum/nobody_wants_to_learn_matrix_math/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("change_var")
+ var/matrix_var_name = params["var_name"]
+ var/matrix_var_value = params["var_value"]
+ if(testing_matrix.vv_edit_var(matrix_var_name, matrix_var_value) == FALSE)
+ to_chat(src, "Your edit was rejected by the object. This is a bug with the matrix tester, not your fault, so report it on GitHub.", confidential = TRUE)
+ return
+ set_transform()
+ if("scale")
+ testing_matrix.Scale(params["x"], params["y"])
+ set_transform()
+ if("translate")
+ testing_matrix.Translate(params["x"], params["y"])
+ set_transform()
+ if("shear")
+ testing_matrix.Shear(params["x"], params["y"])
+ set_transform()
+ if("turn")
+ testing_matrix.Turn(params["angle"])
+ set_transform()
+ if("toggle_pixel")
+ target.appearance_flags ^= PIXEL_SCALE
+
+/datum/nobody_wants_to_learn_matrix_math/proc/set_transform()
+ animate(target, transform = testing_matrix, time = 0.5 SECONDS)
+ testing_matrix = matrix(target.transform)
+
+/client/proc/open_matrix_tester(atom/in_atom)
+ if(holder)
+ var/datum/nobody_wants_to_learn_matrix_math/matrix_tester = new(in_atom)
+ matrix_tester.ui_interact(mob)
diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm
index 1454199bead..f03198a45dc 100644
--- a/code/modules/admin/view_variables/topic.dm
+++ b/code/modules/admin/view_variables/topic.dm
@@ -1,33 +1,51 @@
//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))
+ if(!check_rights_for(src, R_VAREDIT) || !holder.CheckAdminHref(href, href_list))
return
var/target = GET_VV_TARGET
vv_do_basic(target, href_list, href)
- if(istype(target, /datum))
+ if(isdatum(target))
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"]))
+ var/datum/vars_target = locate(href_list["Vars"])
+ if(href_list["special_varname"]) // Some special vars can't be located even if you have their ref, you have to use this instead
+ vars_target = vars_target.vars[href_list["special_varname"]]
+ debug_variables(vars_target)
//~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_VAREDIT)) return
-
- var/mob/M = locate(href_list["rename"])
+ 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")
+ to_chat(usr, "This can only be used on instances of type /mob", confidential = TRUE)
return
- var/new_name = sanitize(input(usr,"What would you like to name this mob?","Input a name",M.real_name) as text|null, MAX_NAME_LEN)
- if( !new_name || !M ) 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)
- href_list["datumrefresh"] = href_list["rename"]
+ vv_update_display(M, "name", new_name)
+ vv_update_display(M, "real_name", M.real_name || "No real name")
+
+ else if(href_list["rotatedatum"])
+
+ 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["varnameedit"] && href_list["datumedit"])
if(!check_rights(R_VAREDIT)) return
@@ -39,48 +57,6 @@
modify_variables(D, href_list["varnameedit"], 1)
- else if(href_list["varnamechange"] && href_list["datumchange"])
- if(!check_rights(R_VAREDIT)) return
-
- var/D = locate(href_list["datumchange"])
- if(!istype(D,/datum) && !istype(D,/client))
- to_chat(usr, "This can only be used on instances of types /client or /datum")
- return
-
- modify_variables(D, href_list["varnamechange"], 0)
-
- else if(href_list["varnamemass"] && href_list["datummass"])
- if(!check_rights(R_VAREDIT)) return
-
- var/atom/A = locate(href_list["datummass"])
- if(!istype(A))
- to_chat(usr, "This can only be used on instances of type /atom")
- return
-
- cmd_mass_modify_object_variables(A, href_list["varnamemass"])
-
- else if(href_list["mob_player_panel"])
- if(!check_rights(0)) return
-
- var/mob/M = locate(href_list["mob_player_panel"])
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.holder.show_player_panel(M)
- href_list["datumrefresh"] = href_list["mob_player_panel"]
-
- else if(href_list["give_spell"])
- if(!check_rights(R_ADMIN|R_FUN)) return
-
- var/mob/M = locate(href_list["give_spell"])
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.give_spell(M)
- href_list["datumrefresh"] = href_list["give_spell"]
-
else if(href_list["give_modifier"])
if(!check_rights(R_ADMIN|R_FUN|R_DEBUG))
return
@@ -93,71 +69,6 @@
src.admin_give_modifier(M)
href_list["datumrefresh"] = href_list["give_modifier"]
- else if(href_list["give_disease2"])
- if(!check_rights(R_ADMIN|R_FUN)) return
-
- var/mob/M = locate(href_list["give_disease2"])
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.give_disease2(M)
- href_list["datumrefresh"] = href_list["give_spell"]
-
- else if(href_list["godmode"])
- if(!check_rights(R_REJUVINATE)) return
-
- var/mob/M = locate(href_list["godmode"])
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- src.cmd_admin_godmode(M)
- href_list["datumrefresh"] = href_list["godmode"]
-
- else if(href_list["gib"])
- if(!check_rights(0)) return
-
- var/mob/M = locate(href_list["gib"])
- 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"])
- if(!istype(M))
- to_chat(usr, "This can only be used on instances of type /mob")
- return
-
- togglebuildmode(M)
- href_list["datumrefresh"] = href_list["build_mode"]
-
- else if(href_list["drop_everything"])
- if(!check_rights(R_DEBUG|R_ADMIN)) return
-
- var/mob/M = locate(href_list["drop_everything"])
- 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(0)) return
-
- var/mob/M = locate(href_list["direct_control"])
- 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["make_skeleton"])
if(!check_rights(R_FUN)) return
@@ -169,95 +80,6 @@
H.ChangeToSkeleton()
href_list["datumrefresh"] = href_list["make_skeleton"]
- 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(usr, "Strict type ([O.type]) or type and all subtypes?","Type Selection", "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 [O.type]?","Delete All?", "Yes","No") != "Yes")
- return
-
- if(alert(usr, "Second confirmation required. Delete?","REALLY?", "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)
- 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)
- 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["explode"])
- if(!check_rights(R_DEBUG|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)
- href_list["datumrefresh"] = href_list["explode"]
-
- else if(href_list["emp"])
- if(!check_rights(R_DEBUG|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)
- href_list["datumrefresh"] = href_list["emp"]
-
- else if(href_list["mark_object"])
- if(!check_rights(0)) 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
-
- src.holder.marked_datum = D
- href_list["datumrefresh"] = href_list["mark_object"]
-
- else if(href_list["rotatedatum"])
- if(!check_rights(0)) 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))
- href_list["datumrefresh"] = href_list["rotatedatum"]
-
else if(href_list["makemonkey"])
if(!check_rights(R_SPAWN)) return
@@ -485,32 +307,51 @@
else if(href_list["adjustDamage"] && href_list["mobToDamage"])
if(!check_rights(R_DEBUG|R_ADMIN|R_FUN|R_EVENT)) return
- var/mob/living/L = locate(href_list["mobToDamage"])
- if(!istype(L)) 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(usr, "Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num
+ var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num|null
- if(!L)
- to_chat(usr, "Mob doesn't exist anymore")
+ if (isnull(amount))
return
+ if(!L)
+ to_chat(usr, "Mob doesn't exist anymore", confidential = TRUE)
+ return
+
+ var/newamt
switch(Text)
- if("brute") amount > 0? L.take_overall_damage(brute = amount) : L.heal_overall_damage(brute = -amount)
- if("fire") amount > 0? L.take_overall_damage(burn = amount) : L.heal_overall_damage(burn = -amount)
- if("toxin") L.adjustToxLoss(amount)
- if("oxygen")L.adjustOxyLoss(amount)
- if("brain") L.adjustBrainLoss(amount)
- if("clone") L.adjustCloneLoss(amount)
+ 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.adjustBrainLoss(amount)
+ newamt = L.getBrainLoss()
+ if("clone")
+ L.adjustCloneLoss(amount)
+ newamt = L.getCloneLoss()
else
- to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]")
+ to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]", confidential = TRUE)
return
if(amount != 0)
- log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]")
- message_admins("[key_name(usr)] dealt [amount] amount of [Text] damage to [L] ")
- href_list["datumrefresh"] = href_list["mobToDamage"]
+ 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["expose"])
if(!check_rights(R_ADMIN, FALSE))
@@ -535,7 +376,8 @@
to_chat(C, "[is_under_stealthmin() ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
C.debug_variables(thing)
+ //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))
+ if(isdatum(DAT) || 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
index 98a5b3f999c..0811e2c8453 100644
--- a/code/modules/admin/view_variables/topic_basic.dm
+++ b/code/modules/admin/view_variables/topic_basic.dm
@@ -21,6 +21,7 @@
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])
@@ -34,51 +35,95 @@
if (!C)
return
if(!target)
- to_chat(usr, "The object you tried to expose to [C] no longer exists (nulled or hard-deled) ")
+ 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 ")
+ 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, "[is_under_stealthmin() ? "an Administrator" : "[usr.client.key]"] has granted you access to view a View Variables window")
+ to_chat(C, "[is_under_stealthmin() ? "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(src)) // show the turf that took its place
- usr.client.debug_variables(src)
+ if (isturf(target)) // show the turf that took its place
+ usr.client.debug_variables(target)
return
- if(href_list[VV_HK_VIEW_APPEARANCE])
- var/appearance/A = locate(href_list[VV_HK_VIEW_APPEARANCE])
- if(!A || !IS_APPEARANCE(A))
- to_chat(usr, SPAN_WARNING("Invalid ref: [href_list[VV_HK_VIEW_APPEARANCE]]"))
- return
- usr.client.debug_variables(A)
+
if(href_list[VV_HK_MARK])
usr.client.mark_datum(target)
if(href_list[VV_HK_ADDCOMPONENT])
if(!check_rights(NONE))
return
var/list/names = list()
- var/list/componentsubtypes = subtypesof(/datum/component)
+ var/list/componentsubtypes = sortList(subtypesof(/datum/component), GLOBAL_PROC_REF(cmp_typepaths_asc))
+ names += "---Components---"
names += componentsubtypes
- names += subtypesof(/datum/element)
- var/result = tgui_input_list(usr, "Choose a component/element to add", "better know what ur fuckin doin pal", names)
- if(!usr || !result || result == "---Components---" || result == "---Elements---")
+ names += "---Elements---"
+ names += sortList(subtypesof(/datum/element), GLOBAL_PROC_REF(cmp_typepaths_asc))
+
+ var/result = tgui_input_list(usr, "Choose a component/element to add", "Add Component", names)
+ if(isnull(result))
return
+ if(!usr || result == "---Components---" || result == "---Elements---")
+ return
+
if(QDELETED(src))
- to_chat(usr, "That thing doesn't exist anymore!")
+ to_chat(usr, "That thing doesn't exist anymore!", confidential = TRUE)
return
+
var/list/lst = get_callproc_args()
if(!lst)
return
- lst.Insert(1, result)
+
var/datumname = "error"
+ lst.Insert(1, result)
if(result in componentsubtypes)
datumname = "component"
target._AddComponent(lst)
else
datumname = "element"
target._AddElement(lst)
- log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(src)].")
- message_admins("[key_name_admin(usr)] has added [result] [datumname] to [target] ([ADMIN_VV(target)]). ")
+ log_admin("[key_name(usr)] has added [result] [datumname] to [key_name(target)].")
+ message_admins(SPAN_NOTICE("[key_name_admin(usr)] has added [result] [datumname] to [key_name_admin(target)]."))
+ if(href_list[VV_HK_REMOVECOMPONENT] || href_list[VV_HK_MASS_REMOVECOMPONENT])
+ if(!check_rights(NONE))
+ return
+ var/mass_remove = href_list[VV_HK_MASS_REMOVECOMPONENT]
+ var/list/components = target.datum_components.Copy()
+ var/list/names = list()
+ names += "---Components---"
+ if(length(components))
+ names += sortList(components, GLOBAL_PROC_REF(cmp_typepaths_asc))
+ names += "---Elements---"
+ // We have to list every element here because there is no way to know what element is on this object without doing some sort of hack.
+ names += sortList(subtypesof(/datum/element), GLOBAL_PROC_REF(cmp_typepaths_asc))
+ var/path = tgui_input_list(usr, "Choose a component/element to remove. All elements listed here may not be on the datum.", "Remove element", names)
+ if(isnull(path))
+ return
+ if(!usr || path == "---Components---" || path == "---Elements---")
+ return
+ if(QDELETED(src))
+ to_chat(usr, "That thing doesn't exist anymore!")
+ return
+ var/list/targets_to_remove_from = list(target)
+ if(mass_remove)
+ var/method = vv_subtype_prompt(target.type)
+ targets_to_remove_from = get_all_of_type(target.type, method)
+
+ if(alert(usr, "Are you sure you want to mass-delete [path] on [target.type]?", "Mass Remove Confirmation", "Yes", "No") == "No")
+ return
+
+ for(var/datum/target_to_remove_from as anything in targets_to_remove_from)
+ if(ispath(path, /datum/element))
+ var/list/lst = get_callproc_args()
+ if(!lst)
+ lst = list()
+ lst.Insert(1, path)
+ target._RemoveElement(lst)
+ else
+ var/list/components_actual = target_to_remove_from.GetComponents(path)
+ for(var/to_delete in components_actual)
+ qdel(to_delete)
+
+ message_admins(SPAN_NOTICE("[key_name_admin(usr)] has [mass_remove? "mass" : ""] removed [path] component from [mass_remove? target.type : key_name_admin(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
index 349d9da698a..c5dece8139f 100644
--- a/code/modules/admin/view_variables/topic_list.dm
+++ b/code/modules/admin/view_variables/topic_list.dm
@@ -9,7 +9,7 @@
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")
+ var/prompt = tgui_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)
@@ -30,7 +30,7 @@
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.
+ 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]")
diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm
index a08f7d7b6ab..555d19355c4 100644
--- a/code/modules/admin/view_variables/view_variables.dm
+++ b/code/modules/admin/view_variables/view_variables.dm
@@ -1,44 +1,87 @@
-// todo: refactor number.. 4?
-// thise is all snowflakey.
-/client/proc/debug_variables(datum/D in world)
+#define ICON_STATE_CHECKED 1 /// this dmi is checked. We don't check this one anymore.
+#define ICON_STATE_NULL 2 /// this dmi has null-named icon_state, allowing it to show a sprite on vv editor.
+
+/client/proc/debug_variables(datum/thing 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. ")
+ 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, SPAN_DANGER("You need to be an administrator to access this."), confidential = TRUE)
return
- if(!D)
+ if(!thing)
return
- var/vtype
- var/type
- var/refid = REF(D)
- var/ref = ref(D)
- var/list/header
- var/title = "#unkw"
+ SSassets.send_asset_pack(usr, /datum/asset_pack/simple/vv)
+
+ if(isappearance(thing))
+ thing = get_vv_appearance(thing) // this is /mutable_appearance/our_bs_subtype
+ var/islist = islist(thing) || (!isdatum(thing) && hascall(thing, "Cut")) // Some special lists don't count as lists, but can be detected by if they have list procs
+ if(!islist && !isdatum(thing))
+ return
+
+ var/title = ""
+ var/refid = REF(thing)
+ var/icon/sprite
+ var/hash
+
+ var/type = islist ? /list : thing.type
+ var/no_icon = FALSE
+
+ if(isatom(thing))
+ sprite = get_flat_icon(thing)
+ if(!sprite)
+ no_icon = TRUE
+
+ else if(isimage(thing))
+ // icon_state=null shows first image even if dmi has no icon_state for null name.
+ // This list remembers which dmi has null icon_state, to determine if icon_state=null should display a sprite
+ // (NOTE: icon_state="" is correct, but saying null is obvious)
+ var/static/list/dmi_nullstate_checklist = list()
+ var/image/image_object = thing
+ var/icon_filename_text = "[image_object.icon]" // "icon(null)" type can exist. textifying filters it.
+ if(icon_filename_text)
+ if(image_object.icon_state)
+ sprite = icon(image_object.icon, image_object.icon_state)
+
+ else // it means: icon_state=""
+ if(!dmi_nullstate_checklist[icon_filename_text])
+ dmi_nullstate_checklist[icon_filename_text] = ICON_STATE_CHECKED
+ if(icon_exists(image_object.icon, ""))
+ // this dmi has nullstate. We'll allow "icon_state=null" to show image.
+ dmi_nullstate_checklist[icon_filename_text] = ICON_STATE_NULL
+
+ if(dmi_nullstate_checklist[icon_filename_text] == ICON_STATE_NULL)
+ sprite = icon(image_object.icon, image_object.icon_state)
+
+ var/sprite_text
+ if(sprite)
+ hash = md5(sprite)
+ src << browse_rsc(sprite, "vv[hash].png")
+ sprite_text = no_icon ? "\[NO ICON\]" : "
"
+
+ title = "[thing] ([REF(thing)]) = [type]"
+ var/formatted_type = replacetext("[type]", "/", "/")
+
+ var/list/header = islist ? list("/list ") : thing.vv_get_header()
+
+ var/ref_line = "@[copytext(refid, 2, -1)]" // get rid of the brackets, add a @ prefix for copy pasting in asay
+
+ var/marked_line
+ if(holder && holder.marked_datum && holder.marked_datum == thing)
+ marked_line = VV_MSG_MARKED
+ var/varedited_line
+ if(!islist && (thing.datum_flags & DF_VAR_EDITED))
+ varedited_line = VV_MSG_EDITED
+ var/deleted_line
+ if(!islist && thing.gc_destroyed)
+ deleted_line = VV_MSG_DELETED
+
var/list/dropdownoptions
- // welcome to yanderedev
- // but i assure you this is necessary unless we switch(typeid).
- // vv refactor #4 when?
- if(isdatum(D))
- vtype = VVING_A_DATUM
- type = D.type
- header = D.vv_get_header()
- if(refid == ref)
- title = "[D] ([ref]) = [type]"
- else
- title = "[D] ([refid]/[ref]) = [type]"
- dropdownoptions = D.vv_get_dropdown()
- dropdownoptions += D.get_view_variables_options_legacy()
- header += D.get_view_variables_header_legacy()
- else if(islist(D))
- vtype = VVING_A_LIST
- type = /list
- header = list("/list [ref] ")
- title = "/list [ref]"
+ if (islist)
dropdownoptions = list(
"---",
"Add Item" = VV_HREF_TARGETREF_INTERNAL(refid, VV_HK_LIST_ADD),
@@ -53,89 +96,38 @@
var/name = dropdownoptions[i]
var/link = dropdownoptions[name]
dropdownoptions[i] = "[name] "
- else if(IS_APPEARANCE(D))
- vtype = VVING_A_APPEARANCE
- type = /appearance
- header = list("virtual appearance [ref] ")
- title = "virtual appearance [ref]"
- dropdownoptions = list()
else
- to_chat(usr, "Invalid vtype.")
- return
-
- var/icon/sprite
- var/hash
-
- var/no_icon = FALSE
-
- if(istype(D, /atom))
- sprite = get_flat_icon(D)
- if(sprite)
- hash = md5(sprite)
- src << browse_rsc(sprite, "vv[hash].png")
- else
- no_icon = TRUE
-
- var/formatted_type = replacetext("[type]", "/", "/")
-
- var/sprite_text
- if(sprite)
- sprite_text = no_icon? "\[NO ICON\]" : " "
-
- var/marked_line
- if(holder && holder.marked_datum && holder.marked_datum == D)
- marked_line = VV_MSG_MARKED
- var/varedited_line
- if(vtype == VVING_A_DATUM && (D.datum_flags & DF_VAR_EDITED))
- varedited_line = VV_MSG_EDITED
- var/deleted_line
- if(vtype == VVING_A_DATUM && D.gc_destroyed)
- deleted_line = VV_MSG_DELETED
+ dropdownoptions = thing.vv_get_dropdown()
var/list/names = list()
- var/list/variable_html = list()
- switch(vtype)
- if(VVING_A_DATUM)
- for(var/V in D.vars)
- names += V
- if(VVING_A_LIST)
- if(VVING_A_APPEARANCE)
- for(var/V in global._appearance_var_list)
- names += V
- sleep(1)
- switch(vtype)
- if(VVING_A_DATUM)
- names = sortList(names)
- for(var/V in names)
- if(D.can_vv_get(V))
- variable_html += D.vv_get_var(V, TRUE)
- if(VVING_A_LIST)
- 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)
- if(VVING_A_APPEARANCE)
- // lol, lmao
- for(var/V in names)
- variable_html += __appearance_v_debug(D, V)
+ if(!islist)
+ for(var/varname in thing.vars)
+ names += varname
+ sleep(1 TICKS)
+
+ var/list/variable_html = list()
+ if(islist)
+ var/list/list_value = thing
+ for(var/i in 1 to list_value.len)
+ var/key = list_value[i]
+ var/value
+ if(IS_NORMAL_LIST(list_value) && IS_VALID_ASSOC_KEY(key))
+ value = list_value[key]
+ variable_html += debug_variable(i, value, 0, list_value)
+ else
+ names = sortList(names)
+ for(var/varname in names)
+ if(thing.can_vv_get(varname))
+ variable_html += thing.vv_get_var(varname)
+
+ var/datum/asset_pack/simple/vv/asset = SSassets.resolve_asset_pack(/datum/asset_pack/simple/vv)
var/html = {"
+
[title]
-
+