diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm
index 92a8e2e8d5a..9df55cd5fd7 100644
--- a/code/_onclick/hud/rendering/render_plate.dm
+++ b/code/_onclick/hud/rendering/render_plate.dm
@@ -210,7 +210,7 @@
name = "Lighting plate"
documentation = "Anything on this plane will be multiplied with the plane it's rendered onto (typically the game plane).\
That's how lighting functions at base. Because it uses BLEND_MULTIPLY and occasionally color matrixes, it needs a backdrop of blackness.\
-
See This byond post\
+
See this byond post\
Lemme see uh, we're masked by the emissive plane so it can actually function (IE: make things glow in the dark).\
We're also masked by the overlay lighting plane, which contains all the well overlay lights in the game. It draws to us and also the game plane.\
Masks us out so it has the breathing room to apply its effect.\
diff --git a/code/modules/admin/verbs/plane_debugger.dm b/code/modules/admin/verbs/plane_debugger.dm
index 90a40a6ce08..8c9f1fbfa32 100644
--- a/code/modules/admin/verbs/plane_debugger.dm
+++ b/code/modules/admin/verbs/plane_debugger.dm
@@ -7,6 +7,9 @@
var/current_group = PLANE_GROUP_MAIN
/// Weakref to the mob to edit
var/datum/weakref/mob_ref
+ /// Has the target been set explicitly (via VV) or implicitly (via orbit)
+ /// Orbit targets will get unset whenever you stop orbiting them
+ var/explicit_mirror = FALSE
var/datum/visual_data/tracking/stored
var/datum/visual_data/mirroring/mirror
@@ -22,7 +25,7 @@
owner = null
return ..()
-/datum/plane_master_debug/proc/set_target(mob/new_mob)
+/datum/plane_master_debug/proc/set_target(mob/new_mob, explicit = TRUE)
QDEL_NULL(mirror)
QDEL_NULL(stored)
@@ -39,10 +42,13 @@
RegisterSignal(owner.owner.mob, COMSIG_MOB_LOGOUT, PROC_REF(on_our_logout), override = TRUE)
mirror = new()
mirror.shadow(new_mob)
+ SStgui.update_uis(owner.owner.mob)
if(new_mob == owner.owner.mob)
+ explicit_mirror = FALSE
return
+ explicit_mirror = explicit
create_store()
/datum/plane_master_debug/proc/on_our_logout(mob/source)
@@ -62,10 +68,16 @@
mirror.set_mirror_target(owner.owner.mob)
/datum/plane_master_debug/proc/get_target()
- var/mob/target = mob_ref?.resolve()
- if(!target?.hud_used)
+ var/mob/cur_target = mob_ref?.resolve()
+ var/mob/target = cur_target
+ if(!target?.hud_used || !explicit_mirror)
target = owner.owner.mob
- set_target(target)
+ if (ismob(target.orbit_target)) // If we're orbiting someone, swap to them if possible
+ var/mob/as_mob = target.orbit_target
+ if (as_mob.hud_used)
+ target = target.orbit_target
+ if (cur_target != target)
+ set_target(target, FALSE)
return target
/// Setter for mirror_target, basically allows for enabling/disabiling viewing through mob's sight
@@ -73,8 +85,8 @@
if(value == mirror_target)
return
mirror_target = value
- // Refresh our target and mirrors and such
- set_target(get_target())
+ // Refresh our target and mirrors and such, but keep explicit/implicit mirroring
+ set_target(get_target(), explicit_mirror)
/datum/plane_master_debug/ui_state(mob/user)
return ADMIN_STATE(R_DEBUG)
@@ -99,12 +111,12 @@
var/datum/hud/our_hud = reference_frame.hud_used
var/list/our_groups = our_hud.master_groups
- if(!our_groups[current_group])
+ if (!our_groups[current_group])
// We assume we'll always have at least one group
current_group = our_groups[length(our_hud.master_groups)]
var/list/groups = list()
- for(var/key in our_groups)
+ for (var/key in our_groups)
groups += key
data["enable_group_view"] = length(groups) > 1
@@ -112,30 +124,14 @@
data["present_groups"] = groups
var/list/plane_info = list()
- data["plane_info"] = plane_info
- var/list/relay_deets = list()
- data["relay_info"] = relay_deets
- var/list/filter_connections = list()
- data["filter_connect"] = filter_connections
-
- var/list/filter_queue = list()
-
- // Assoc of render targets -> planes
- // Gotta be able to look these up so filter stuff can work
- var/list/render_target_to_plane = list()
- // Assoc list of pending planes -> relays
- // Used to ensure the incoming_relays list is filled, even if the relay's generated before the plane's processed
- var/list/pending_relays = list()
var/list/our_planes = our_hud?.get_planes_from(current_group)
- for(var/plane_string as anything in our_planes)
+ for (var/plane_string as anything in our_planes)
var/list/this_plane = list()
var/atom/movable/screen/plane_master/plane = our_planes[plane_string]
- var/string_plane = "[plane.plane]"
this_plane["name"] = plane.name
this_plane["documentation"] = plane.documentation
this_plane["plane"] = plane.plane
- this_plane["our_ref"] = string_plane
this_plane["offset"] = plane.offset
this_plane["real_plane"] = plane.real_plane
this_plane["renders_onto"] = plane.render_relay_planes
@@ -143,191 +139,39 @@
this_plane["color"] = plane.color
this_plane["alpha"] = plane.alpha
this_plane["render_target"] = plane.render_target
- this_plane["intended_hidden"] = plane.force_hidden
+ this_plane["force_hidden"] = plane.force_hidden
+ var/list/relays = list()
+ var/list/filters = list()
- var/list/incoming_relays = list()
- this_plane["incoming_relays"] = incoming_relays
-
- for(var/pending_relay in pending_relays[string_plane])
- incoming_relays += pending_relay
- var/list/this_relay = relay_deets[pending_relay]
- this_relay["target_index"] = length(incoming_relays)
-
-
- this_plane["outgoing_relays"] = list()
-
- // You can think of relays as connections between plane master "nodes
- // They do have some info of their own tho, best to pass that along
- for(var/atom/movable/render_plane_relay/relay in plane.relays)
- var/string_target = "[relay.plane]"
+ for (var/atom/movable/render_plane_relay/relay as anything in plane.relays)
var/list/this_relay = list()
this_relay["name"] = relay.name
this_relay["source"] = plane.plane
- this_relay["source_ref"] = string_plane
this_relay["target"] = relay.plane
- this_relay["target_ref"] = string_target
this_relay["layer"] = relay.layer
+ this_relay["our_ref"] = "[plane.plane]-[relay.plane]"
+ this_relay["blend_mode"] = GLOB.blend_names["[relay.blend_mode]"]
+ relays += list(this_relay)
- // Now taht we've encoded our relay, we need to hand out references to it to our source plane, alongside the target plane
- var/relay_ref = "[string_plane]-[string_target]"
- this_relay["our_ref"] = relay_ref
- relay_deets[relay_ref] = this_relay
- this_plane["outgoing_relays"] += relay_ref
-
- // If we've already encoded our target plane, update its incoming relays list
- // Otherwise, we'll handle this later
- var/list/existing_target = plane_info[string_target]
- if(existing_target)
- existing_target["incoming_relays"] += relay_ref
- else
- var/list/pending_plane = pending_relays[string_target]
- if(!pending_plane)
- pending_plane = list()
- pending_relays[string_target] = pending_plane
- pending_plane += relay_ref
-
- this_plane["incoming_filters"] = list()
- this_plane["outgoing_filters"] = list()
- // We're gonna collect a list of filters, partly because they're useful info
- // But also because they can be used as connections, and we need to support that
- for(var/filter_id in plane.filter_data)
+ for (var/filter_id in plane.filter_data)
var/list/filter = plane.filter_data[filter_id]
if(!filter["render_source"])
continue
+
var/list/filter_info = filter.Copy()
- filter_info["target_ref"] = string_plane
filter_info["name"] = filter_id
- filter_queue += list(filter_info)
+ filter_info["our_ref"] = "[plane.plane]-[filter_id]"
+ filters += list(filter_info)
- plane_info[plane_string] = this_plane
- render_target_to_plane[plane.render_target] = this_plane
+ this_plane["relays"] = relays
+ this_plane["filters"] = filters
- for(var/list/filter in filter_queue)
- var/source = filter["render_source"]
- var/list/source_plane = render_target_to_plane[source]
- var/list/target_plane = plane_info[filter["target_ref"]]
- var/source_ref = source_plane["our_ref"]
- filter["source_ref"] = source_ref
- var/our_ref = "[source_ref]-[filter["target_ref"]]-filter"
- filter["our_ref"] = our_ref
- filter_connections[our_ref] = filter
- source_plane["outgoing_filters"] += our_ref
- target_plane["incoming_filters"] += our_ref
+ plane_info += list(this_plane)
- // Only load this once. Prevents leaving off orphaned components
- if(!depth_stack[current_group])
- depth_stack[current_group] = treeify(plane_info, relay_deets, filter_connections)
-
- // We will use this js side to arrange our plane masters and such
- // It's essentially a stack of where they should be displayed
- data["depth_stack"] = depth_stack[current_group]
+ data["planes"] += plane_info
return data
-// Reading this in the queue tells the search to increase the depth, and then push another increase command to the end of the stack
-// This way we ensure groupings always stay together, and depth is respected
-#define COMMAND_DEPTH_INCREASE "increase_depth"
-#define COMMAND_NEXT_PARENT "next_parent"
-
-/// Takes a list of js formatted planes, and turns it into a tree based off the back connections of relays
-/// So start at the top master plane, and work down
-/// Haha jerry what if I added commands to my list parser lmao lol
-/datum/plane_master_debug/proc/treeify(list/plane_info, list/relay_info, list/filter_connections)
- // List in the form [depth in num] -> list(list(plane_ref -> parent_ref, ...), ...)
- var/list/treelike_output = list()
- // List in the form plane ref -> current depth
- var/list/plane_to_depth = list()
- // List of items/commands to process. FIFO queue, to ensure the brackets are built correctly
- var/list/processing_queue = list()
- // A FIFO queue of parents. Used so planes can have refs to their direct parent, to make sorting easier
- var/list/parents = list("")
- var/parent_head = 1
- // The current depth of our search, used with treelike_output
- var/depth = 0
- // Push a depth increase onto the queue, to properly setup the sorta looping effect it has
- processing_queue += COMMAND_DEPTH_INCREASE
- processing_queue += "[RENDER_PLANE_MASTER]"
- // We need to do a c style loop here because we are expanding the queue, and so need to update our conditional
- for(var/i = 1; i <= length(processing_queue); i++)
- var/entry = processing_queue[i]
- // We've reached the end of a depth block
- // Increment the depth and stick another command on the end of the queue
- if(entry == COMMAND_DEPTH_INCREASE)
- // The plane to continue on with, assuming we can find an unvisited head to use
- var/continue_on_with = ""
- // Don't wanna infinite loop now
- if(i == length(processing_queue))
- for(var/plane in TRUE_PLANE_TO_OFFSETS(RENDER_PLANE_MASTER))
- if(!plane_to_depth["[plane]"])
- continue_on_with = "[plane]"
- // We only want to handle one plane master at a time
- break
- if(!continue_on_with)
- continue
- // Increment our depth
- depth += 1
- treelike_output += list(list())
- // If this isn't the end, stick another entry on the end to ensure batches work proper
- processing_queue += COMMAND_DEPTH_INCREASE
- // If we found a plane to use to extend our process, tack it on the end here as god intended
- if(continue_on_with)
- processing_queue += continue_on_with
- continue
- if(entry == COMMAND_NEXT_PARENT)
- parent_head += 1
- continue
-
- var/old_queue_len = length(processing_queue)
- var/existing_depth = plane_to_depth[entry]
- // If we've seen you before, remove your last entry
- // We always want inputs before outputs in the stack
- if(existing_depth)
- treelike_output[existing_depth] -= entry
-
- // If it's not a command, it must be a plane string
- var/list/plane = plane_info[entry]
- /// We want master planes to ALWAYS bubble down to their own space.
- /// Just ignore this if this is the head we're processing, yeah?
- if(PLANE_TO_TRUE(plane["real_plane"]) == RENDER_PLANE_MASTER && i > 2)
- // If there's other stuff already in your depth entry, or there's more then one thing (a depth increase command)
- // Left in the queue, "bubble" down a layer.
- if(length(treelike_output[depth]) || i + 1 != length(processing_queue))
- processing_queue += COMMAND_NEXT_PARENT
- parents += parents[parent_head]
- processing_queue += entry
- continue
- // Add all the planes that pipe into us to the queue, Intentionally allows dupes
- // If we find the same entry twice, it'll get moved down the depth stack
- for(var/relay_string in plane["incoming_relays"])
- var/list/relay = relay_info[relay_string]
- processing_queue += relay["source_ref"]
- for(var/filter_ref in plane["incoming_filters"])
- var/list/filter = filter_connections[filter_ref]
- processing_queue += filter["source_ref"]
-
- // If the queue has grown, we're a parent, so stick us in the parent queue
- if(old_queue_len != length(processing_queue))
- parents += entry
- // Stick a parent increase right before our children show up in the queue. That way we're properly set as their parent
- processing_queue.Insert(old_queue_len + 1, COMMAND_NEXT_PARENT)
- // Stick us in the output at our designated depth
- var/list/plane_packet = list()
- plane_packet[entry] = parents[parent_head]
- treelike_output[depth] += plane_packet
- plane_to_depth[entry] = depth
-
- /// Walk treelike output, remove allll the empty lists we've accidentially generated
- for(var/depth_index = 1; depth_index <= length(treelike_output); depth_index++)
- var/list/layer = treelike_output[depth_index]
- if(!length(layer))
- treelike_output.Cut(depth_index, depth_index + 1)
- depth_index -= 1
-
- return treelike_output
-
-#undef COMMAND_DEPTH_INCREASE
-#undef COMMAND_NEXT_PARENT
-
/datum/plane_master_debug/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
@@ -343,51 +187,63 @@
switch(action)
if("rebuild")
group.rebuild_hud()
+
if("reset_mob")
set_target(null)
+
if("toggle_mirroring")
set_mirroring(!mirror_target)
+
if("vv_mob")
owner.owner.debug_variables(reference_frame)
+
if("set_group")
current_group = params["target_group"]
+
if("connect_relay")
var/source_plane = params["source"]
var/target_plane = params["target"]
+ var/blend_mode = text2num(params["mode"])
var/atom/movable/screen/plane_master/source = our_planes["[source_plane]"]
if(source.get_relay_to(target_plane)) // Fuck off
return
- source.add_relay_to(target_plane)
+ source.add_relay_to(target_plane, blend_mode != BLEND_DEFAULT ? blend_mode : null)
return TRUE
+
if("disconnect_relay")
var/source_plane = params["source"]
var/target_plane = params["target"]
var/atom/movable/screen/plane_master/source = our_planes["[source_plane]"]
source.remove_relay_from(text2num(target_plane))
return TRUE
+
if("disconnect_filter")
var/target_plane = params["target"]
var/atom/movable/screen/plane_master/filtered_plane = our_planes["[target_plane]"]
filtered_plane.remove_filter(params["name"])
return TRUE
+
if("vv_plane")
var/plane_edit = params["edit"]
var/atom/movable/screen/plane_master/edit = our_planes["[plane_edit]"]
var/mob/user = ui.user
user?.client?.debug_variables(edit)
return TRUE
+
if("set_alpha")
var/plane_edit = params["edit"]
var/atom/movable/screen/plane_master/edit = our_planes["[plane_edit]"]
var/newalpha = params["alpha"]
animate(edit, 0.4 SECONDS, alpha = newalpha)
return TRUE
+
if("edit_color_matrix")
var/plane_edit = params["edit"]
var/atom/movable/screen/plane_master/edit = our_planes["[plane_edit]"]
var/mob/user = ui.user
user?.client?.open_color_matrix_editor(edit)
return TRUE
+
if("edit_filters")
var/plane_edit = params["edit"]
var/atom/movable/screen/plane_master/edit = our_planes["[plane_edit]"]
diff --git a/tgui/packages/tgui/drag.ts b/tgui/packages/tgui/drag.ts
index 131b5d3e4e6..09eacfe1f80 100644
--- a/tgui/packages/tgui/drag.ts
+++ b/tgui/packages/tgui/drag.ts
@@ -40,7 +40,7 @@ export const getWindowSize = (): [number, number] => [
];
// Set window position
-const setWindowPosition = (vec: [number, number]) => {
+export const setWindowPosition = (vec: [number, number]) => {
const byondPos = vecAdd(vec, screenOffset);
return Byond.winset(Byond.windowId, {
pos: byondPos[0] + ',' + byondPos[1],
diff --git a/tgui/packages/tgui/interfaces/AlertModal.tsx b/tgui/packages/tgui/interfaces/AlertModal.tsx
index a3f41b7d85a..cdac53e6f79 100644
--- a/tgui/packages/tgui/interfaces/AlertModal.tsx
+++ b/tgui/packages/tgui/interfaces/AlertModal.tsx
@@ -36,7 +36,7 @@ export function AlertModal(props) {
// Stolen wholesale from fontcode
function textWidth(text: string, font: string, fontsize: number) {
// default font height is 12 in tgui
- font = fontsize + 'x ' + font;
+ font = fontsize + 'px ' + font;
const c = document.createElement('canvas');
const ctx = c.getContext('2d') as CanvasRenderingContext2D;
ctx.font = font;
@@ -53,7 +53,7 @@ export function AlertModal(props) {
// At least one of the buttons has a long text message
const isVerbose = buttons.some(
(button) =>
- textWidth(button, '', large_buttons ? 14 : 12) > // 14 is the larger font size for large buttons
+ textWidth(button, 'Verdana, Geneva', large_buttons ? 14 : 12) > // 14 is the larger font size for large buttons
windowWidth / buttons.length - paddingMagicNumber,
);
const largeSpacing = isVerbose && large_buttons ? 20 : 15;
diff --git a/tgui/packages/tgui/interfaces/MCDependencyDebug.tsx b/tgui/packages/tgui/interfaces/MCDependencyDebug.tsx
index 52c4b20fc07..8ef76f461ca 100644
--- a/tgui/packages/tgui/interfaces/MCDependencyDebug.tsx
+++ b/tgui/packages/tgui/interfaces/MCDependencyDebug.tsx
@@ -79,7 +79,6 @@ function GraphNode(props: GraphNodeProps) {
style={{
width: '100%',
height: '100%',
- position: 'absolute',
}}
viewBox="0, 0, 100, 100"
>
@@ -110,7 +109,6 @@ function GraphNode(props: GraphNodeProps) {
style={{
width: '100%',
height: '100%',
- position: 'absolute',
}}
viewBox="0, 0, 100, 100"
>
@@ -213,7 +211,9 @@ export function MCDependencyDebug(props) {
const subsystemLayer: SubsystemLayer = {};
for (let i = 0; i < subsystemsGraph.length; i++) {
const subsystem = subsystemsGraph[i];
- evaluateSubsystemLayer(subsystem, 1, subsystemLayer);
+ if (subsystem.dependents.length === 0) {
+ evaluateSubsystemLayer(subsystem, 1, subsystemLayer);
+ }
}
return subsystemLayer;
}, [subsystems]);
diff --git a/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx b/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx
deleted file mode 100644
index cf17c90e565..00000000000
--- a/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx
+++ /dev/null
@@ -1,1116 +0,0 @@
-import { sortBy } from 'common/collections';
-import { Component, createRef, RefObject } from 'react';
-import {
- Box,
- Button,
- Dropdown,
- InfinitePlane,
- LabeledList,
- Modal,
- Section,
- Slider,
- Stack,
- Tooltip,
-} from 'tgui-core/components';
-import { flow } from 'tgui-core/fp';
-import { classes, shallowDiffers } from 'tgui-core/react';
-
-import { resolveAsset } from '../assets';
-import { useBackend, useLocalState } from '../backend';
-import { Window } from '../layouts';
-import { Connection, Connections, Position } from './common/Connections';
-import { MOUSE_BUTTON_LEFT, noop } from './IntegratedCircuit/constants';
-
-enum ConnectionType {
- Relay,
- Filter,
-}
-
-enum ConnectionDirection {
- Incoming,
- Outgoing,
-}
-
-type ConnectionRef = {
- ref: string;
- sort_by: number;
-};
-
-type Plane = {
- name: string;
- documentation: string;
- plane: number;
- our_ref: string;
- offset: number;
- real_plane: number;
- renders_onto: number[];
- blend_mode: number;
- color: string | number[];
- alpha: number;
- render_target: string;
- incoming_relays: string[];
- outgoing_relays: string[];
- incoming_filters: string[];
- outgoing_filters: string[];
- intended_hidden: boolean;
-
- incoming_connections: ConnectionRef[];
- outgoing_connections: ConnectionRef[];
-
- x: number;
- y: number;
- step_size: number;
- size_x: number;
- size_y: number;
-};
-
-type Relay = {
- name: string;
- layer: number;
-};
-
-type Filter = {
- type: string;
- name: string;
- render_source: string;
-};
-
-// Type of something that spawn a connection
-type Connected = {
- connect_color: string;
- source: number;
- source_ref: string;
- target: number;
- target_ref: string;
- our_ref: string;
-
- source_index: number;
- target_index: number;
-
- connect_type: ConnectionType;
-};
-
-interface AssocPlane {
- [index: string]: Plane;
-}
-
-interface AssocRelays {
- [index: string]: Relay & Connected;
-}
-
-interface AssocFilters {
- [index: string]: Filter & Connected;
-}
-
-interface AssocConnected {
- [index: string]: Connected;
-}
-
-interface AssocString {
- [index: string]: string;
-}
-
-type PlaneDebugData = {
- our_group: string;
- present_groups: string[];
- enable_group_view: boolean;
- relay_info: AssocRelays;
- plane_info: AssocPlane;
- filter_connect: AssocFilters;
- depth_stack: AssocString[];
- mob_name: string;
- mob_ref: string;
- our_ref: string;
- tracking_active: boolean;
-};
-
-// Stolen wholesale from fontcode
-function textWidth(text: string, font: string, fontsize: number) {
- // default font height is 12 in tgui
- font = fontsize + 'x ' + font;
- const c = document.createElement('canvas');
- const ctx = c.getContext('2d') as CanvasRenderingContext2D;
- ctx.font = font;
- return ctx.measureText(text).width;
-}
-
-const planeToPosition = function (plane: Plane, index, is_incoming): Position {
- return {
- x: is_incoming ? plane.x : plane.x + plane.size_x,
- y:
- 29 +
- plane.y +
- plane.step_size * index +
- (plane.step_size - plane.step_size / 3),
- };
-};
-
-// Takes a plane, returns the amount of node space it will need
-const getPlaneNodeHeight = function (plane: Plane): number {
- return Math.max(
- plane.incoming_relays.length + plane.incoming_filters.length,
- plane.outgoing_relays.length + plane.outgoing_filters.length,
- );
-};
-
-const sortConnectionRefs = function (
- refs: ConnectionRef[],
- direction: ConnectionDirection,
- connectSources: AssocConnected,
-) {
- refs = sortBy(refs, (connection: ConnectionRef) => connection.sort_by);
- refs.map((connection, index) => {
- let connectSource = connectSources[connection.ref];
- if (direction === ConnectionDirection.Outgoing) {
- connectSource.source_index = index;
- } else if (direction === ConnectionDirection.Incoming) {
- connectSource.target_index = index;
- }
- });
- return refs;
-};
-
-const addConnectionRefs = function (
- read_from: string[],
- add_type: ConnectionDirection,
- add_to: ConnectionRef[],
- reference: AssocConnected,
- plane_info: AssocPlane,
-) {
- for (const ref of read_from) {
- const connected = reference[ref];
- let our_plane;
- // If we're incoming, use the target ref, and vis versa
- if (add_type === ConnectionDirection.Incoming) {
- our_plane = plane_info[connected.source_ref];
- } else if (add_type === ConnectionDirection.Outgoing) {
- our_plane = plane_info[connected.target_ref];
- }
- add_to.push({
- ref: ref,
- sort_by: our_plane.plane,
- });
- }
-};
-
-// Takes a list of planes, uses the depth stack to position them
-const positionPlanes = (connectSources: AssocConnected) => {
- const { data } = useBackend();
- const { plane_info, relay_info, filter_connect, depth_stack } = data;
-
- // First, we concatinate our connection sources
- // We need them in one list partly for later purposes
- // But also so we can set their source/target index nicely
- for (const ref of Object.keys(relay_info)) {
- let connection_source: Connected = relay_info[ref];
- connection_source.connect_type = ConnectionType.Relay;
- connection_source.connect_color = 'blue';
- connectSources[ref] = connection_source;
- }
- for (const ref of Object.keys(filter_connect)) {
- let connection_source: Connected = filter_connect[ref];
- connection_source.connect_type = ConnectionType.Filter;
- connection_source.connect_color = 'purple';
- connectSources[ref] = connection_source;
- }
-
- for (const plane_ref of Object.keys(plane_info)) {
- let our_plane = plane_info[plane_ref];
- const incoming_conct: ConnectionRef[] = [] as any;
- const outgoing_conct: ConnectionRef[] = [] as any;
- addConnectionRefs(
- our_plane.incoming_relays,
- ConnectionDirection.Incoming,
- incoming_conct,
- relay_info,
- plane_info,
- );
- addConnectionRefs(
- our_plane.incoming_filters,
- ConnectionDirection.Incoming,
- incoming_conct,
- filter_connect,
- plane_info,
- );
- addConnectionRefs(
- our_plane.outgoing_relays,
- ConnectionDirection.Outgoing,
- outgoing_conct,
- relay_info,
- plane_info,
- );
- addConnectionRefs(
- our_plane.outgoing_filters,
- ConnectionDirection.Outgoing,
- outgoing_conct,
- filter_connect,
- plane_info,
- );
- our_plane.incoming_connections = sortConnectionRefs(
- incoming_conct,
- ConnectionDirection.Incoming,
- connectSources,
- );
- our_plane.outgoing_connections = sortConnectionRefs(
- outgoing_conct,
- ConnectionDirection.Outgoing,
- connectSources,
- );
- }
-
- // First we sort by the plane of each member,
- // then we sort by the plane of each member's head
- // This way we get a nicely sorted list
- // and get rid of the now unneeded parent refs
- const stack = depth_stack.map((layer) =>
- flow([
- (planes) => sortBy(planes, (plane: string) => plane_info[plane].plane),
- (planes) =>
- sortBy(planes, (plane: string) => {
- const read_from = plane_info[layer[plane]];
- if (!read_from) {
- return 0;
- }
- return read_from.plane;
- }),
- ])(Object.keys(layer)),
- );
-
- let base_x = 0;
- let longest_name = 0;
- let tallest_stack = 0;
- for (const layer of stack) {
- base_x += longest_name;
- base_x += 150;
- let new_longest = 0;
- let last_node_len = 0;
- let base_y = 0;
- for (const plane_ref of layer) {
- const old_y = base_y;
- const plane = plane_info[plane_ref];
- // - because we want to work backwards rather then forwards
- plane.x = -base_x;
- // I am assuming the height of a plane master with two connections looks
- // like 50% name 50% (two) nodes
- base_y += 45;
- // One extra for the relay add button
- base_y += 19 * (last_node_len + 1);
- // We need to know how large node steps are for later
- plane.step_size = 19;
- plane.y = base_y;
- const width = textWidth(plane.name, '', 16) + 30;
- plane.size_x = width;
- plane.size_y = old_y - base_y;
- new_longest = Math.max(new_longest, width);
- last_node_len = getPlaneNodeHeight(plane);
- }
- longest_name = new_longest;
- tallest_stack = Math.max(tallest_stack, base_y);
- }
-
- // Now that we've got everything stacked, we need to center it
- for (const layer of stack) {
- const last_ref = layer[layer.length - 1];
- const last_plane = plane_info[last_ref];
- const delta_tall = tallest_stack - last_plane.y;
- // Now that we know how "off" our height is, we can correct it
- // We halve because otherwise this looks dumb
- const offset = delta_tall / 2;
- for (const plane_ref of layer) {
- const plane = plane_info[plane_ref];
- plane.y += offset;
- }
- }
-};
-
-const arrayRemove = function (arr: any, value) {
- return arr.filter((element) => element !== value);
-};
-
-export class PlaneMasterDebug extends Component {
- constructor(props) {
- super(props);
- this.handlePortClick = this.handlePortClick.bind(this);
- }
-
- handlePortClick(connection: Connected, isOutput, event) {
- if (event.button !== MOUSE_BUTTON_LEFT) {
- return;
- }
- const { act, data } = useBackend();
- const { plane_info } = data;
-
- event.preventDefault();
- if (connection.connect_type === ConnectionType.Relay) {
- // Close the connection
- act('disconnect_relay', {
- source: connection.source_ref,
- target: connection.target_ref,
- });
- let source_plane = plane_info[connection.source_ref];
- let target_plane = plane_info[connection.source_ref];
- source_plane.outgoing_relays = arrayRemove(
- source_plane.outgoing_relays,
- connection.our_ref,
- );
- target_plane.incoming_relays = arrayRemove(
- target_plane.incoming_relays,
- connection.our_ref,
- );
- } else if (connection.connect_type === ConnectionType.Filter) {
- // Close the connection
- const filter = connection as Filter & Connected;
- act('disconnect_filter', {
- target: filter.target_ref,
- name: filter.name,
- });
- let source_plane = plane_info[connection.source_ref];
- let target_plane = plane_info[connection.source_ref];
- source_plane.outgoing_filters = arrayRemove(
- source_plane.outgoing_filters,
- connection.our_ref,
- );
- target_plane.incoming_filters = arrayRemove(
- target_plane.incoming_filters,
- connection.our_ref,
- );
- }
- }
-
- render() {
- const { act, data } = useBackend();
- const { plane_info, mob_name } = data;
- const [showAdd, setShowAdd] = useLocalState('showAdd', false);
-
- const [connectSources, setConnectSouces] = useLocalState(
- 'connectionSources',
- {},
- );
-
- positionPlanes(connectSources);
-
- const connections: Connection[] = [];
-
- for (const ref of Object.keys(connectSources)) {
- const connect = connectSources[ref];
- const source_plane = plane_info[connect.source_ref];
- const target_plane = plane_info[connect.target_ref];
- connections.push({
- color: connect.connect_color,
- from: planeToPosition(source_plane, connect.source_index, false),
- to: planeToPosition(target_plane, connect.target_index, true),
- ref: ref,
- });
- }
-
- return (
-
-
-
- {Object.keys(plane_info).map(
- (plane_key, index) =>
- plane_key && (
-
- ),
- )}
-
-
-
-
-
- );
- }
-}
-
-type PlaneMasterProps = {
- name: string;
- incoming_connections: ConnectionRef[];
- outgoing_connections: ConnectionRef[];
- connected_list: AssocConnected;
- our_plane: Plane;
- x: number;
- y: number;
- onPortMouseDown: Function;
- act: Function;
-};
-
-class PlaneMaster extends Component {
- shouldComponentUpdate(nextProps, nextState) {
- const { incoming_connections, outgoing_connections } = this
- .props as PlaneMasterProps;
-
- return (
- shallowDiffers(this.props, nextProps) ||
- shallowDiffers(this.state as object, nextState) ||
- shallowDiffers(incoming_connections, nextProps.incoming_connections) ||
- shallowDiffers(outgoing_connections, nextProps.outgoing_connections)
- );
- }
-
- render() {
- const {
- name,
- incoming_connections,
- outgoing_connections,
- connected_list,
- our_plane,
- x,
- y,
- onPortMouseDown = noop,
- act = noop,
- ...rest
- } = this.props as PlaneMasterProps;
- const [showAdd, setShowAdd] = useLocalState('showAdd', false);
- const [currentPlane, setCurrentPlane] = useLocalState('currentPlane', {});
- const [readPlane, setReadPlane] = useLocalState('readPlane', '');
-
- // Assigned onto the ports
- const PortOptions = {
- onPortMouseDown: onPortMouseDown,
- };
- return (
-
-
- {name}
-
-
-
-
-
- {incoming_connections.map((con_ref, portIndex) => (
-
-
-
- ))}
-
-
-
-
- {outgoing_connections.map((con_ref, portIndex) => (
-
-
-
- ))}
-
-
-
-
-
-
-
- );
- }
-}
-
-type PortProps = {
- connection: Connected;
- isOutput?: boolean;
- onPortMouseDown?: Function;
- act: Function;
-};
-class Port extends Component {
- // Ok so like, we're basically treating iconRef as a string here
- // Mostly so svg can work later. You're really not supposed to do this.
- // Should really be a RefObject
- // But it's how it was being done in circuit code, so eh
- iconRef: RefObject | RefObject | any;
-
- constructor(props) {
- super(props);
- this.iconRef = createRef();
- this.handlePortMouseDown = this.handlePortMouseDown.bind(this);
- }
-
- handlePortMouseDown(e) {
- const {
- connection,
- isOutput,
- onPortMouseDown = noop,
- } = this.props as PortProps;
- onPortMouseDown(connection, isOutput, e);
- }
-
- render() {
- const { connection, isOutput, ...rest } = this.props as PortProps;
-
- return (
-
-
-
-
-
-
-
-
- );
- }
-}
-
-const DrawAbovePlane = (props) => {
- const [showAdd, setShowAdd] = useLocalState('showAdd', false);
- const [showInfo, setShowInfo] = useLocalState('showInfo', false);
- const [readPlane, setReadPlane] = useLocalState('readPlane', '');
-
- const { act, data } = useBackend();
- // Plane groups don't use relays right now, because of a byond bug
- // This exists mostly so enabling viewing them is easy and simple
- const { enable_group_view } = data;
-
- return (
- <>
- {!!readPlane && }
- {!readPlane && (
- <>
-
-
-
-
-
- >
- )}
- {!!enable_group_view && }
- {!!showAdd && }
- {!!showInfo && }
- >
- );
-};
-
-const PlaneWindow = (props) => {
- const { data, act } = useBackend();
- const { plane_info } = data;
- const [readPlane, setReadPlane] = useLocalState('readPlane', '');
-
- const workingPlane: Plane = plane_info[readPlane];
-
- // NOT sanitized, since this would only be editable by admins or coders
- const doc_html = {
- __html: workingPlane.documentation,
- };
-
- const setAlpha = (event, value) =>
- act('set_alpha', {
- edit: workingPlane.our_ref,
- alpha: value,
- });
-
- return (
-
-
-
-
-
-
-
- >
- }
- >
-
-
-
-
-
-
- {workingPlane.plane}
-
-
-
-
- {workingPlane.offset}
-
-
-
-
- {workingPlane.render_target || '""'}
-
-
-
-
- {workingPlane.blend_mode}
-
-
-
-
- {workingPlane.intended_hidden}
-
-
-
-
-
-
-
-
-
- Alpha ({workingPlane.alpha})
-
-
-
- );
-};
-
-const InfoButton = (props) => {
- const [showInfo, setShowInfo] = useLocalState('showInfo', false);
- const { no_position } = props;
- const foreign = has_foreign_mob();
-
- return (
-