Skip to content

Commit

Permalink
[MIRROR] Redoes how appearance VV works because it scares me (#2173)
Browse files Browse the repository at this point in the history
* Redoes how appearance VV works because it scares me (#82851)

* Redoes how appearance VV works because it scares me

---------

Co-authored-by: LemonInTheDark <[email protected]>
  • Loading branch information
2 people authored and StealsThePRs committed Apr 25, 2024
1 parent 0cfd747 commit 9afa91d
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 373 deletions.
234 changes: 118 additions & 116 deletions .github/guides/VISUALS.md

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions code/__DEFINES/vv.dm
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@

//Helpers for vv_get_dropdown()
#define VV_DROPDOWN_OPTION(href_key, name) . += "<option value='?_src_=vars;[HrefToken()];[href_key]=TRUE;target=[REF(src)]'>[name]</option>"
//Same with VV_DROPDOWN_OPTION, but global proc doesn't have src
#define VV_DROPDOWN_OPTION_APPEARANCE(thing, href_key, name) . += "<option value='?_src_=vars;[HrefToken()];[href_key]=TRUE;target=[REF(thing)]'>[name]</option>"

// VV HREF KEYS
#define VV_HK_TARGET "target"
Expand Down
13 changes: 13 additions & 0 deletions code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,19 @@ DEFINE_BITFIELD(vis_flags, list(
"VIS_UNDERLAY" = VIS_UNDERLAY,
))

// I am so sorry. Required because vis_flags is both undefinable and unreadable on mutable_appearance
// But we need to display them anyway. See /mutable_appearance/appearance_mirror
DEFINE_BITFIELD(_vis_flags, list(
"VIS_HIDE" = VIS_HIDE,
"VIS_INHERIT_DIR" = VIS_INHERIT_DIR,
"VIS_INHERIT_ICON" = VIS_INHERIT_ICON,
"VIS_INHERIT_ICON_STATE" = VIS_INHERIT_ICON_STATE,
"VIS_INHERIT_ID" = VIS_INHERIT_ID,
"VIS_INHERIT_LAYER" = VIS_INHERIT_LAYER,
"VIS_INHERIT_PLANE" = VIS_INHERIT_PLANE,
"VIS_UNDERLAY" = VIS_UNDERLAY,
))

DEFINE_BITFIELD(zap_flags, list(
"ZAP_ALLOW_DUPLICATES" = ZAP_ALLOW_DUPLICATES,
"ZAP_MACHINE_EXPLOSIVE" = ZAP_MACHINE_EXPLOSIVE,
Expand Down
325 changes: 95 additions & 230 deletions code/modules/admin/view_variables/debug_variable_appearance.dm
Original file line number Diff line number Diff line change
@@ -1,236 +1,101 @@
/* < OH MY GOD. Can't you just make "/image/proc/foo()" instead of making these? >
* /appearance is a hardcoded byond type, and it is very internal type.
* Its type is actually /image, but it isn't truly /image. We defined it as "/appearance"
* new procs to /image will only work to actual /image references, but...
* /appearance references are not capable of executing procs, because these are not real /image
* This is why these global procs exist. Welcome to the curse.
*/
#define ADD_UNUSED_VAR(varlist, thing, varname) if(NAMEOF(##thing, ##varname)) ##varlist += #varname
#define RESULT_VARIABLE_NOT_FOUND "_switch_result_variable_not_found"

/// An alias datum that allows us to access and view the variables of an appearance by keeping certain known, yet undocumented, variables that we can access and read in a datum for debugging purposes.
/// Kindly do not use this outside of a debugging context.
/image/appearance
parent_type = /atom/movable // This is necessary to access the variables on compile-time.

// var/override // Sad point. We can't steal byond internal variable name
#ifdef OPENDREAM
// opendream doens't support mouse_drop_zone yet. Remove this once OD supports it.
var/mouse_drop_zone
#endif

/image/appearance/New(loc, ...)
. = ..()
CRASH("something tried to use '/image/appearance', but this isn't actual type we use. Do not fucking do this.")

/// Makes a var list of /appearance type actually uses. This will be only called once.
/proc/build_virtual_appearance_vars()
var/list/used_variables = list("vis_flags") // manual listing.
. = used_variables
var/list/unused_var_names = list()

var/image/appearance/nameof_reference // We don't copy vars from this.
pass(nameof_reference) // compiler complains unused variable
ADD_UNUSED_VAR(unused_var_names, nameof_reference, appearance) // it only does self-reference
ADD_UNUSED_VAR(unused_var_names, nameof_reference, x) // xyz are always 0
ADD_UNUSED_VAR(unused_var_names, nameof_reference, y)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, z)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, weak_reference) // it's not a good idea to make a weak_ref on this, and this won't have it
ADD_UNUSED_VAR(unused_var_names, nameof_reference, vars) // inherited from /image, but /appearance hasn't this

// Even if these vars are essential for image, these only exists in an actual type
ADD_UNUSED_VAR(unused_var_names, nameof_reference, filter_data)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, realized_overlays)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, realized_underlays)

// we have no reason to show these, right?
ADD_UNUSED_VAR(unused_var_names, nameof_reference, _active_timers)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, _datum_components)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, _listen_lookup)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, _signal_procs)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, __auxtools_weakref_id)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, _status_traits)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, cooldowns)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, datum_flags)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, verbs)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, gc_destroyed)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, harddel_deets_dumped)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, open_uis)
ADD_UNUSED_VAR(unused_var_names, nameof_reference, tgui_shared_states)

var/image/dummy_image = image(null, null) // actual type we'll copy variable names
for(var/each in dummy_image.vars) // try to inherit var list from /image
if(each in unused_var_names)
continue
used_variables += each
del(dummy_image)
dummy_image = null

return used_variables

/// debug_variable() proc but made for /appearance type specifically
/proc/debug_variable_appearance(var_name, appearance)
var/value
try
value = locate_appearance_variable(var_name, appearance)
catch
return "<li style='backgroundColor:white'>(READ ONLY) <font color='blue'>[var_name] = (untrackable)</font></li>"
if(value == RESULT_VARIABLE_NOT_FOUND)
return "<li style='backgroundColor:white'>(READ ONLY) [var_name] <font color='blue'>(Undefined var name in switch)</font></li>"
return "<li style='backgroundColor:white'>(READ ONLY) [var_name] = [_debug_variable_value(var_name, value, 0, appearance, sanitize = TRUE, display_flags = NONE)]</li>"

/// manually locate a variable through string value.
/// appearance type needs a manual var referencing because it doesn't have "vars" variable internally.
/// There's no way doing this in a fancier way.
/proc/locate_appearance_variable(var_name, image/appearance/appearance) // WARN: /image/appearance is a mocking type, not real one
switch(var_name) // Welcome to this curse
// appearance doesn't have "vars" variable.
// This means you need to target a variable manually through this way.

// appearance vars in DM document
if(NAMEOF(appearance, alpha))
return appearance.alpha
if(NAMEOF(appearance, appearance_flags))
return appearance.appearance_flags
if(NAMEOF(appearance, blend_mode))
return appearance.blend_mode
if(NAMEOF(appearance, color))
return appearance.color
if(NAMEOF(appearance, desc))
return appearance.desc
if(NAMEOF(appearance, gender))
return appearance.gender
if(NAMEOF(appearance, icon))
return appearance.icon
if(NAMEOF(appearance, icon_state))
return appearance.icon_state
if(NAMEOF(appearance, invisibility))
return appearance.invisibility
if(NAMEOF(appearance, infra_luminosity))
return appearance.infra_luminosity
if(NAMEOF(appearance, filters))
return appearance.filters
if(NAMEOF(appearance, layer))
return appearance.layer
if(NAMEOF(appearance, luminosity))
return appearance.luminosity
if(NAMEOF(appearance, maptext))
return appearance.maptext
if(NAMEOF(appearance, maptext_width))
return appearance.maptext_width
if(NAMEOF(appearance, maptext_height))
return appearance.maptext_height
if(NAMEOF(appearance, maptext_x))
return appearance.maptext_x
if(NAMEOF(appearance, maptext_y))
return appearance.maptext_y
if(NAMEOF(appearance, mouse_over_pointer))
return appearance.mouse_over_pointer
if(NAMEOF(appearance, mouse_drag_pointer))
return appearance.mouse_drag_pointer
if(NAMEOF(appearance, mouse_drop_pointer))
return appearance.mouse_drop_pointer
if(NAMEOF(appearance, mouse_drop_zone))
return appearance:mouse_drop_zone
if(NAMEOF(appearance, mouse_opacity))
return appearance.mouse_opacity
if(NAMEOF(appearance, name))
return appearance.name
if(NAMEOF(appearance, opacity))
return appearance.opacity
if(NAMEOF(appearance, overlays))
return appearance.overlays
if("override") // only /image has this. mocking type can't steal byond internal var name
var/image/image_appearance = appearance
return image_appearance.override
if(NAMEOF(appearance, pixel_x))
return appearance.pixel_x
if(NAMEOF(appearance, pixel_y))
return appearance.pixel_y
if(NAMEOF(appearance, pixel_w))
return appearance.pixel_w
if(NAMEOF(appearance, pixel_z))
return appearance.pixel_z
if(NAMEOF(appearance, plane))
return appearance.plane
if(NAMEOF(appearance, render_source))
return appearance.render_source
if(NAMEOF(appearance, render_target))
return appearance.render_target
if(NAMEOF(appearance, suffix))
return appearance.suffix
if(NAMEOF(appearance, text))
return appearance.text
if(NAMEOF(appearance, transform))
return appearance.transform
if(NAMEOF(appearance, underlays))
return appearance.underlays

if(NAMEOF(appearance, parent_type))
return appearance.parent_type
if(NAMEOF(appearance, type))
return /image/appearance // don't fool people

// These are not documented ones but trackable values. Maybe we'd want these.
if(NAMEOF(appearance, animate_movement))
return appearance.animate_movement
if(NAMEOF(appearance, dir))
return appearance.dir
if(NAMEOF(appearance, glide_size))
return appearance.glide_size
if("pixel_step_size")
return "" //atom_appearance.pixel_step_size
// DM compiler complains this

// I am not sure if these will be ever detected, but I made a connection just in case.
if(NAMEOF(appearance, contents)) // It's not a thing, but I don't believe how DM will change /appearance in future.
return appearance.contents
if(NAMEOF(appearance, loc)) // same reason above
return appearance.loc
if(NAMEOF(appearance, vis_contents)) // same reason above
return appearance.vis_contents
if(NAMEOF(appearance, vis_flags)) // DM document says /appearance has this, but it throws error
return appearance.vis_flags

// we wouldn't need these, but let's these trackable anyway...
if(NAMEOF(appearance, density))
return appearance.density
if(NAMEOF(appearance, screen_loc))
return appearance.screen_loc
if(NAMEOF(appearance, verbs))
return appearance.verbs
if(NAMEOF(appearance, tag))
return appearance.tag
return RESULT_VARIABLE_NOT_FOUND

/// Shows a header name on top when you investigate an appearance
/proc/vv_get_header_appearance(image/thing)
/// Shows a header name on top when you investigate an appearance/image
/image/vv_get_header()
. = list()
var/icon_name = "<b>[thing.icon || "null"]</b><br/>"
var/icon_name = "<b>[icon || "null"]</b><br/>"
. += replacetext(icon_name, "icons/obj", "") // shortens the name. We know the path already.
if(thing.icon)
. += thing.icon_state ? "\"[thing.icon_state]\"" : "(icon_state = null)"
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 "<a href='?_src_=vars;[HrefToken()];Vars=[display_ref]'>[display_name] (<span class='value'>[display_value]</span>) [display_ref]</a>"

/// 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
// vis flags can't be displayed by mutable appearances cause it don't make sense as overlays, but appearances do carry them
// can't use the name either for byond reasons
var/_vis_flags

// all alone at the end of the universe
GLOBAL_DATUM_INIT(pluto, /atom/movable, new /atom/movable(null))

// 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
// We copy over our appearance onto an atom. This is done so we can read vars carried by but not accessible on appearances
GLOB.pluto.appearance = appearance_father
_vis_flags = GLOB.pluto.vis_flags
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
// If it is one of the two args on /image, yeet (I am sorry)
if(var_name == NAMEOF(src, realized_overlays))
return FALSE
if(var_name == NAMEOF(src, realized_underlays))
return FALSE
// Filtering out the stuff I know we don't care about
if(var_name == NAMEOF(src, x))
return FALSE
if(var_name == NAMEOF(src, y))
return FALSE
if(var_name == NAMEOF(src, z))
return FALSE
// Could make an argument for these but I think they will just confuse people, so yeeet
#ifndef SPACEMAN_DMM // Spaceman doesn't believe in contents on appearances, sorry lads
if(var_name == NAMEOF(src, contents))
return FALSE
#endif
if(var_name == NAMEOF(src, loc))
return FALSE
if(var_name == NAMEOF(src, vis_contents))
return FALSE
return ..()

/image/vv_get_header() // it should redirect to global proc version because /appearance can't call a proc, unless we want dupe code here
return vv_get_header_appearance(src)
/mutable_appearance/appearance_mirror/vv_get_var(var_name)
// No editing for you
var/value = vars[var_name]
return "<li style='backgroundColor:white'>(READ ONLY) [var_name] = [_debug_variable_value(var_name, value, 0, src, sanitize = TRUE, display_flags = NONE)]</li>"

/// Makes a format name for shortened vv name.
/proc/get_appearance_vv_summary_name(image/thing)
var/icon_file_name = thing.icon ? splittext("[thing.icon]", "/") : "null"
if(islist(icon_file_name))
icon_file_name = length(icon_file_name) ? icon_file_name[length(icon_file_name)] : "null"
if(thing.icon_state)
return "[icon_file_name]:[thing.icon_state]"
else
return "[icon_file_name]"
/mutable_appearance/appearance_mirror/vv_get_dropdown()
SHOULD_CALL_PARENT(FALSE)

/proc/vv_get_dropdown_appearance(image/thing)
. = list()
// Don't add any vv option carelessly unless you have a good reason to add one for /appearance.
// /appearance type shouldn't allow general options. Even "Mark Datum" is a questionable behaviour here.
VV_DROPDOWN_OPTION_APPEARANCE(thing, "", "---")
VV_DROPDOWN_OPTION_APPEARANCE(thing, VV_HK_EXPOSE, "Show VV To Player") // only legit option
return .

#undef ADD_UNUSED_VAR
#undef RESULT_VARIABLE_NOT_FOUND
VV_DROPDOWN_OPTION("", "---")
VV_DROPDOWN_OPTION(VV_HK_CALLPROC, "Call Proc")
VV_DROPDOWN_OPTION(VV_HK_MARK, "Mark Object")
VV_DROPDOWN_OPTION(VV_HK_TAG, "Tag Datum")
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)
10 changes: 3 additions & 7 deletions code/modules/admin/view_variables/debug_variables.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@

// 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)
if(isappearance(value))
value = get_vv_appearance(value)

. = "<font color='red'>DISPLAY_ERROR:</font> ([value] [REF(value)])" // Make sure this line can never runtime

if(isnull(value))
Expand All @@ -49,13 +52,6 @@
return "/icon (<span class='value'>[value]</span>)"
#endif


if(isappearance(value)) // Reminder: Do not replace this into /image/debug_variable_value() proc. /appearance can't do that.
return "<a href='?_src_=vars;[HrefToken()];Vars=[REF(value)]'>/appearance (<span class='value'>[get_appearance_vv_summary_name(value)]</span>) [REF(value)]</a>"

if(isimage(value))
return "<a href='?_src_=vars;[HrefToken()];Vars=[REF(value)]'>[value:type] (<span class='value'>[get_appearance_vv_summary_name(value)]</span>) [REF(value)]</a>"

if(isfilter(value))
var/datum/filter_value = value
return "/filter (<span class='value'>[filter_value.type] [REF(filter_value)]</span>)"
Expand Down
Loading

0 comments on commit 9afa91d

Please sign in to comment.