diff --git a/code/__DEFINES/_helpers.dm b/code/__DEFINES/_helpers.dm
index fdbfde60822..c6a5b860e9a 100644
--- a/code/__DEFINES/_helpers.dm
+++ b/code/__DEFINES/_helpers.dm
@@ -36,3 +36,8 @@
/// : because of the embedded typecheck
#define text_ref(datum) (isdatum(datum) ? (datum:cached_ref ||= "\ref[datum]") : ("\ref[datum]"))
#endif
+
+// 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 901e3c4d90f..d7f69d2646a 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -11,8 +11,14 @@
#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) (!istype(thing, /image) && !ispath(thing) && istype(GLOB.magic_appearance_detecting_image, thing))
+#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 isgenerator(A) (istype(A, /generator))
diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm
index 5196d83b124..ea119597ffa 100644
--- a/code/modules/admin/view_variables/debug_variables.dm
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -1,107 +1,120 @@
#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
/// Get displayed variable in VV variable list
-/proc/debug_variable(name, value, level, datum/D, sanitize = TRUE, display_flags = NONE) //if D is a list, name will be index, and value will be assoc value.
- var/header
- if(D)
- if(islist(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/index = name
if (value)
- name = D[name] //name is really the index until this line
+ name = owner[name] //name is really the index until this line
else
- value = D[name]
- header = "
"
+
+// This is split into a seperate proc mostly to make errors that happen not break things too much
+/proc/_debug_variable_value(name, value, level, datum/owner, sanitize, display_flags)
+ . = "DISPLAY_ERROR"
+
+ 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 = "[name_part] = ([value]) "
+ var/rname = "tmp[REF(icon_value)][rnd].png"
+ usr << browse_rsc(icon_value, rname)
+ return "([value]) "
#else
- item = "[name_part] = /icon ([value])"
+ return "/icon ([value])"
#endif
- else if(isappearance(value))
+ if(isappearance(value))
var/image/actually_an_appearance = value
- item = "[name_part] = /appearance ([actually_an_appearance.icon])"
+ return "/appearance ([actually_an_appearance.icon])"
- else if (isfile(value))
- item = "[name_part] = '[value]'"
+ if(isfilter(value))
+ var/datum/filter_value = value
+ return "/filter ([filter_value.type] [REF(filter_value)])"
- else if(istype(value,/matrix)) // Needs to be before datum
- var/matrix/M = value
- item = {"[name_part] =
-
-
-
-
[M.a]
[M.d]
0
-
[M.b]
[M.e]
0
-
[M.c]
[M.f]
1
-
-
"} //TODO link to modify_transform wrapper for all matrices
+ if(isfile(value))
+ return "'[value]'"
- else if (isdatum(value))
- var/datum/DV = value
- if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it.
- item = "[name_part] = [DV] [DV.type] [REF(value)]"
- else
- item = "[name_part] = [DV.type] [REF(value)]"
- if(istype(value,/datum/weakref))
- var/datum/weakref/weakref = value
- item += " (Resolve)"
+ if(isdatum(value))
+ var/datum/datum_value = value
+ return datum_value.debug_variable_value(name, level, owner, sanitize, display_flags)
- else if (islist(value))
- var/list/L = value
+ if(islist(value) || hascall(value, "Cut")) // Some special lists arent detectable as a list through istype, so we check if it has a list proc instead
+ var/list/list_value = value
var/list/items = list()
- if (!(display_flags & VV_ALWAYS_CONTRACT_LIST) && L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD)))
- for (var/i in 1 to L.len)
- var/key = L[i]
+ 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 (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 = "[name_part] = /list ([L.len])