/// Allows developers to see a breakdown of an atom or a specific target and edit some of the values /datum/appearance_debugger var/datum/admins/owner /// Currently debugged atom or mutable appearance, hence /datum var/datum/debug_target /// A list of copies of the currently debugged appearance and its children for access from the UI var/list/mutable_appearance/appearance_copies /// Assoc list of ref -> appearance as to prevent refreshing of dynamic appearances var/list/mutable_appearance/appearance_cache /// Mapview used to display the hovered over appearance var/atom/movable/screen/map_view/proxy_view_hover /// Mapview used to display the selected appearance var/atom/movable/screen/map_view/proxy_view_selected /// Assoc list of ref -> list of atoms to be displayed in said ref atom's vis_contents var/list/atom/movable/fake_vis /// Should we display a popup that the appearance in-game has updated? var/update_warning = FALSE /datum/appearance_debugger/New(datum/admins/owner) src.owner = owner proxy_view_hover = new() proxy_view_hover.generate_view("appearance_debugger_[REF(owner)]_proxy_hover") proxy_view_selected = new() proxy_view_selected.generate_view("appearance_debugger_[REF(owner)]_proxy_selected") /datum/appearance_debugger/Destroy() if (owner) owner.appearance_debug = null owner = null if (fake_vis) for (var/ref_id in fake_vis) var/list/all_fakes = fake_vis[ref_id] QDEL_LIST(all_fakes) fake_vis.Cut() QDEL_NULL(proxy_view_hover) QDEL_NULL(proxy_view_selected) return ..() /datum/appearance_debugger/proc/get_appearance_data(atom/appearance_owner) var/mutable_appearance/target = appearance_owner if (isatom(appearance_owner)) target = appearance_cache["[REF(appearance_owner)]"] || new /mutable_appearance(appearance_owner.appearance) appearance_cache["[REF(appearance_owner)]"] = target if (!fake_vis["[REF(appearance_owner)]"] && (ismovable(appearance_owner) || isturf(appearance_owner))) var/atom/movable/as_movable = appearance_owner var/list/false_vis_contents = list() fake_vis["[REF(appearance_owner)]"] = false_vis_contents for (var/atom/something in (isturf(as_movable) ? (as_movable.vis_contents + as_movable.contents) : as_movable.vis_contents)) var/atom/movable/mimic = new() mimic.appearance = new /mutable_appearance(something.appearance) false_vis_contents += mimic var/list/data = list( "type" = isatom(appearance_owner) ? "atom" : (isimage(appearance_owner) ? "image" : "appearance"), "alpha" = target.alpha, "flags" = target.appearance_flags, "blend_mode" = target.blend_mode, "color" = target.color, "dir" = target.dir, "icon" = length("[target.icon]") ? "[target.icon]" : null, "icon_state" = target.icon_state, "invisibility" = target.invisibility, "layer" = target.layer, "name" = target.name, "maptext" = target.maptext, "maptext_width" = target.maptext_width, "maptext_height" = target.maptext_height, "maptext_x" = target.maptext_x, "maptext_y" = target.maptext_y, "mouse_opacity" = target.mouse_opacity, "pixel_x" = target.pixel_x, "pixel_y" = target.pixel_y, "pixel_w" = target.pixel_w, "pixel_z" = target.pixel_z, "plane" = target.plane, "plane_true" = PLANE_TO_TRUE(target.plane), "render_source" = target.render_source, "render_target" = target.render_target, "screen_loc" = target.screen_loc, ) if (!(target in appearance_copies)) appearance_copies += target data["id"] = length(appearance_copies) else data["id"] = appearance_copies.Find(target) var/list/filter_data = list() for (var/list/our_filter as anything in target.filter_data) filter_data += list(our_filter) data["filters"] = filter_data var/list/underlay_data = list() for (var/mutable_appearance/underlay as anything in target.underlays) underlay_data += list(get_appearance_data(underlay)) data["underlays"] = underlay_data var/list/overlay_data = list() for (var/mutable_appearance/overlay as anything in target.overlays) overlay_data += list(get_appearance_data(overlay)) data["overlays"] = overlay_data // Display previews if it is either an instance icon or a file and we have icon_state set if ((isicon(target.icon) && !isfile(target.icon)) || (target.icon && target.icon_state)) var/icon/used_icon = icon(target.icon, target.icon_state, (isimage(target) && target.dir) ? target.dir : SOUTH, frame = 1) if (istext(target.color)) used_icon.Blend(target.color, ICON_MULTIPLY) data["embed_icon"] = icon2base64(used_icon) data["transform"] = list(target.transform.a, target.transform.b, target.transform.c, target.transform.d, target.transform.e, target.transform.f) // Turfs aren't movables but they still can have vis_flags/contents so lets just count them as such if (ismovable(appearance_owner) || isturf(appearance_owner)) var/atom/movable/as_movable = appearance_owner data["vis_flags"] = as_movable.vis_flags // Maybe should be cached but I'm too lazy and don't think this'll matter enough var/list/vis_data = list() // Turfs also inherit their contents as vis_contents for (var/atom/vis_thing as anything in isturf(as_movable) ? (as_movable.vis_contents + as_movable.contents) : as_movable.vis_contents) vis_data += list(get_appearance_data(vis_thing)) data["vis_contents"] = vis_data // Handle all dynamically modified layers if (target.layer > FLOOR_EMISSIVE_START_LAYER && target.layer < FLOOR_EMISSIVE_END_LAYER) data["layer_text_override"] = "FLOOR_EMISSIVE_LAYER (+[target.layer - FLOOR_EMISSIVE_START_LAYER])" else if (target.layer > GAS_PIPE_HIDDEN_LAYER && target.layer < GAS_PIPE_HIDDEN_LAYER + 0.006) data["layer_text_override"] = "GAS_PIPE_HIDDEN_LAYER (+[target.layer - GAS_PIPE_HIDDEN_LAYER])" else if (target.layer > GAS_PIPE_VISIBLE_LAYER && target.layer < GAS_PIPE_VISIBLE_LAYER + 0.006) data["layer_text_override"] = "GAS_PIPE_VISIBLE_LAYER (+[target.layer - GAS_PIPE_VISIBLE_LAYER])" else if (target.layer > PLUMBING_PIPE_VISIBILE_LAYER && target.layer < PLUMBING_PIPE_VISIBILE_LAYER + (FIFTH_DUCT_LAYER * 2 / 3333)) data["layer_text_override"] = "PLUMBING_PIPE_VISIBILE_LAYER (+[target.layer - PLUMBING_PIPE_VISIBILE_LAYER])" return data /datum/appearance_debugger/ui_data(mob/user) return list( "updateWarning" = update_warning, ) /datum/appearance_debugger/ui_static_data(mob/user) return list( "mainAppearance" = get_appearance_data(debug_target), "planeToText" = GLOB.admin_readable_planes, "layerToText" = GLOB.admin_readable_layers, "flagsToText" = get_valid_bitflags("appearance_flags"), "visToText" = get_valid_bitflags("vis_flags"), "blendToText" = GLOB.blend_names, "mapRefHover" = proxy_view_hover.assigned_map, "mapRefSelected" = proxy_view_selected.assigned_map, ) /datum/appearance_debugger/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return switch(action) if("swapMapViewHover") var/appearance_id = text2num(params["id"]) proxy_view_hover.appearance = appearance_copies[appearance_id] for (var/atom/movable/something as anything in proxy_view_hover.vis_contents) proxy_view_hover.vis_contents -= something for (var/ref_id in appearance_cache) if (appearance_copies[appearance_id] != appearance_cache[ref_id]) continue if (fake_vis[ref_id]) proxy_view_hover.vis_contents |= fake_vis[ref_id] // Needs screenloc to be set since we're setting the appearance and carrying over target's screenloc proxy_view_hover.set_position(1, 1) if("swapMapViewSelected") var/appearance_id = text2num(params["id"]) proxy_view_selected.appearance = appearance_copies[appearance_id] for (var/atom/movable/something as anything in proxy_view_selected.vis_contents) proxy_view_selected.vis_contents -= something for (var/ref_id in appearance_cache) if (appearance_copies[appearance_id] != appearance_cache[ref_id]) continue if (fake_vis[ref_id]) proxy_view_selected.vis_contents |= fake_vis[ref_id] proxy_view_selected.set_position(1, 1) if("refreshAppearance") update_warning = FALSE appearance_copies = list() appearance_cache = list() for (var/ref_id in fake_vis) var/list/all_fakes = fake_vis[ref_id] QDEL_LIST(all_fakes) fake_vis = list() update_static_data_for_all_viewers() if("vvAppearance") if (!check_rights(R_VAREDIT)) return var/appearance_id = text2num(params["id"]) usr.client.debug_variables(appearance_copies[appearance_id]) /datum/appearance_debugger/ui_assets(mob/user) return list(get_asset_datum(/datum/asset/simple/plane_background)) /datum/appearance_debugger/ui_state(mob/user) return ADMIN_STATE(R_DEBUG) /datum/appearance_debugger/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "AppearanceDebug") ui.open() proxy_view_hover.display_to(user, ui.window) proxy_view_selected.display_to(user, ui.window) /datum/appearance_debugger/ui_close(mob/user) . = ..() // Reset appearances when the UI is closed proxy_view_hover.appearance = new /mutable_appearance() proxy_view_selected.appearance = new /mutable_appearance() proxy_view_hover.vis_contents.Cut() proxy_view_selected.vis_contents.Cut() if (isatom(debug_target)) UnregisterSignal(debug_target, COMSIG_ATOM_UPDATE_APPEARANCE) /datum/appearance_debugger/proc/set_target(mutable_appearance/new_target) if (isatom(debug_target)) UnregisterSignal(debug_target, COMSIG_ATOM_UPDATE_APPEARANCE) update_warning = FALSE // Can be an atom! debug_target = new_target if (isatom(debug_target)) RegisterSignal(debug_target, COMSIG_ATOM_UPDATE_APPEARANCE, PROC_REF(warn_update)) appearance_copies = list() appearance_cache = list() for (var/ref_id in fake_vis) var/list/all_fakes = fake_vis[ref_id] QDEL_LIST(all_fakes) fake_vis = list() update_static_data_for_all_viewers() /datum/appearance_debugger/proc/warn_update() SIGNAL_HANDLER update_warning = TRUE