-
-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy pathplane_debugger.dm
400 lines (349 loc) · 14.7 KB
/
plane_debugger.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
/// Used for testing/debugger plane masters and their associated rendering plates
/datum/plane_master_debug
var/datum/admins/owner
/// Assoc list of plane master group key -> its depth stack
var/list/depth_stack = list()
/// The current plane master group we're viewing
var/current_group = PLANE_GROUP_MAIN
/// Weakref to the mob to edit
var/datum/weakref/mob_ref
var/datum/visual_data/tracking/stored
var/datum/visual_data/mirroring/mirror
/// If we are actively mirroring the target of our current ui
var/mirror_target = FALSE
/datum/plane_master_debug/New(datum/admins/owner)
src.owner = owner
/datum/plane_master_debug/Destroy()
if(owner)
owner.plane_debug = null
owner = null
return ..()
/datum/plane_master_debug/proc/set_target(mob/new_mob)
QDEL_NULL(mirror)
QDEL_NULL(stored)
depth_stack = list()
if(!new_mob?.hud_used)
new_mob = owner.owner?.mob
mob_ref = WEAKREF(new_mob)
if(!mirror_target)
UnregisterSignal(owner.owner.mob, COMSIG_MOB_LOGOUT)
return
RegisterSignal(owner.owner.mob, COMSIG_MOB_LOGOUT, PROC_REF(on_our_logout), override = TRUE)
mirror = new()
mirror.shadow(new_mob)
if(new_mob == owner.owner.mob)
return
create_store()
/datum/plane_master_debug/proc/on_our_logout(mob/source)
SIGNAL_HANDLER
// Recreate our stored view, since we've changed mobs now
create_store()
UnregisterSignal(source, COMSIG_MOB_LOGOUT)
RegisterSignal(owner.owner.mob, COMSIG_MOB_LOGOUT, PROC_REF(on_our_logout), override = TRUE)
/// Create or refresh our stored visual data, represeting the viewing mob
/datum/plane_master_debug/proc/create_store()
if(stored)
QDEL_NULL(stored)
stored = new()
stored.shadow(owner.owner.mob)
stored.set_truth(mirror)
mirror.set_mirror_target(owner.owner.mob)
/datum/plane_master_debug/proc/get_target()
var/mob/target = mob_ref?.resolve()
if(!target?.hud_used)
target = owner.owner.mob
set_target(target)
return target
/// Setter for mirror_target, basically allows for enabling/disabiling viewing through mob's sight
/datum/plane_master_debug/proc/set_mirroring(value)
if(value == mirror_target)
return
mirror_target = value
// Refresh our target and mirrors and such
set_target(get_target())
/datum/plane_master_debug/ui_state(mob/user)
return GLOB.admin_state
/datum/plane_master_debug/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PlaneMasterDebug")
ui.open()
/datum/plane_master_debug/ui_assets(mob/user)
return list(get_asset_datum(/datum/asset/simple/plane_background))
/datum/plane_master_debug/ui_data()
var/list/data = list()
var/mob/reference_frame = get_target()
data["mob_name"] = reference_frame.name
data["mob_ref"] = ref(reference_frame)
data["our_ref"] = ref(owner.owner.mob)
data["tracking_active"] = mirror_target
var/datum/hud/our_hud = reference_frame.hud_used
var/list/our_groups = our_hud.master_groups
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)
groups += key
data["enable_group_view"] = length(groups) > 1
data["our_group"] = current_group
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)
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
this_plane["blend_mode"] = GLOB.blend_names["[plane.blend_mode_override || initial(plane.blend_mode)]"]
this_plane["color"] = plane.color
this_plane["alpha"] = plane.alpha
this_plane["render_target"] = plane.render_target
this_plane["intended_hidden"] = plane.force_hidden
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]"
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
// 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)
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)
plane_info[plane_string] = this_plane
render_target_to_plane[plane.render_target] = this_plane
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
// 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]
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(.)
return
var/mob/reference_frame = get_target()
var/datum/hud/our_hud = reference_frame.hud_used
var/datum/plane_master_group/group = our_hud?.master_groups[current_group]
if(!group) // Nothing to act on
return
var/list/our_planes = group.plane_masters
switch(action)
if("refresh")
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/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)
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]"]
var/mob/user = ui.user
user?.client?.open_filter_editor(edit)
return TRUE
/datum/plane_master_debug/ui_close(mob/user)
. = ..()
set_mirroring(FALSE)