1277 lines
19 KiB
GDScript
1277 lines
19 KiB
GDScript
@tool
|
|
class_name LogicGraph
|
|
extends GraphEdit
|
|
|
|
signal scenario_mapping_changed
|
|
|
|
signal edit_rejected(
|
|
message: String
|
|
)
|
|
|
|
signal logic_path_selected(
|
|
cabinet_id: StringName,
|
|
event_id: StringName,
|
|
action_id: StringName,
|
|
device_id: StringName
|
|
)
|
|
|
|
var room: RoomDefinition
|
|
var scenario: ScenarioDefinition
|
|
|
|
var graph_nodes: Dictionary = {}
|
|
|
|
var game_port_events: Dictionary = {}
|
|
|
|
var hardware_node_data: Dictionary = {}
|
|
var mapping_node_data: Dictionary = {}
|
|
|
|
func _ready() -> void:
|
|
minimap_enabled = true
|
|
show_grid = true
|
|
snapping_enabled = true
|
|
snapping_distance = 20
|
|
connection_lines_thickness = 4.0
|
|
|
|
add_theme_color_override(
|
|
"activity",
|
|
Color(0.0, 1.0, 0.278, 1.0)
|
|
)
|
|
|
|
if not connection_request.is_connected(
|
|
_on_connection_request
|
|
):
|
|
connection_request.connect(
|
|
_on_connection_request
|
|
)
|
|
|
|
if not disconnection_request.is_connected(
|
|
_on_disconnection_request
|
|
):
|
|
disconnection_request.connect(
|
|
_on_disconnection_request
|
|
)
|
|
|
|
|
|
func configure(
|
|
room_definition: RoomDefinition,
|
|
scenario_definition: ScenarioDefinition
|
|
) -> void:
|
|
|
|
room = room_definition
|
|
scenario = scenario_definition
|
|
|
|
rebuild()
|
|
|
|
func rebuild() -> void:
|
|
clear_connections()
|
|
|
|
var children_to_remove: Array[Node] = []
|
|
|
|
for child in get_children():
|
|
if child is GraphNode:
|
|
children_to_remove.append(child)
|
|
|
|
for child in children_to_remove:
|
|
#
|
|
# Remove it from the tree immediately so its
|
|
# node name becomes available before we rebuild.
|
|
#
|
|
remove_child(child)
|
|
child.queue_free()
|
|
|
|
graph_nodes.clear()
|
|
game_port_events.clear()
|
|
hardware_node_data.clear()
|
|
mapping_node_data.clear()
|
|
|
|
if room == null:
|
|
return
|
|
|
|
if scenario == null:
|
|
return
|
|
|
|
_build_graph()
|
|
|
|
func _create_graph_node(
|
|
node_name: StringName,
|
|
title: String,
|
|
position: Vector2
|
|
) -> GraphNode:
|
|
|
|
var node := GraphNode.new()
|
|
|
|
node.name = node_name
|
|
node.title = title
|
|
node.position_offset = position
|
|
|
|
node.custom_minimum_size = Vector2(
|
|
220.0,
|
|
100.0
|
|
)
|
|
|
|
add_child(node)
|
|
|
|
#
|
|
# Always register using the actual Node name
|
|
# after it enters the tree.
|
|
#
|
|
graph_nodes[node.name] = node
|
|
|
|
return node
|
|
|
|
func _add_row(
|
|
node: GraphNode,
|
|
text: String
|
|
) -> Label:
|
|
|
|
var label := Label.new()
|
|
|
|
label.text = text
|
|
label.custom_minimum_size.y = 28.0
|
|
|
|
node.add_child(label)
|
|
|
|
return label
|
|
|
|
func _set_ports(
|
|
node: GraphNode,
|
|
row_index: int,
|
|
left_enabled: bool,
|
|
right_enabled: bool
|
|
) -> void:
|
|
|
|
node.set_slot(
|
|
row_index,
|
|
left_enabled,
|
|
0,
|
|
Color.WHITE,
|
|
right_enabled,
|
|
0,
|
|
Color.WHITE
|
|
)
|
|
|
|
func _build_graph() -> void:
|
|
var row_index := 0
|
|
|
|
for assignment in scenario.assignments:
|
|
if assignment == null:
|
|
continue
|
|
|
|
if assignment.cabinet == null:
|
|
continue
|
|
|
|
if assignment.game == null:
|
|
continue
|
|
|
|
_build_assignment(
|
|
assignment,
|
|
row_index
|
|
)
|
|
|
|
row_index += 1
|
|
|
|
func _build_assignment(
|
|
assignment: ScenarioAssignment,
|
|
row_index: int
|
|
) -> void:
|
|
|
|
var base_y := (
|
|
float(row_index) * 500.0
|
|
)
|
|
|
|
var game_node := _create_game_node(
|
|
assignment,
|
|
Vector2(
|
|
50.0,
|
|
base_y
|
|
)
|
|
)
|
|
|
|
var hardware_nodes := (
|
|
_build_available_hardware(
|
|
assignment,
|
|
base_y
|
|
)
|
|
)
|
|
|
|
var mapping_number := 0
|
|
|
|
for mapping in assignment.event_mappings:
|
|
if mapping == null:
|
|
continue
|
|
|
|
for action_id in mapping.hardware_actions:
|
|
|
|
if not hardware_nodes.has(
|
|
action_id
|
|
):
|
|
continue
|
|
|
|
var offset_y := (
|
|
base_y
|
|
+ float(mapping_number) * 150.0
|
|
)
|
|
|
|
var mapping_node := (
|
|
_create_mapping_node(
|
|
assignment,
|
|
mapping,
|
|
action_id,
|
|
Vector2(
|
|
380.0,
|
|
offset_y
|
|
)
|
|
)
|
|
)
|
|
|
|
var hardware_node: GraphNode = (
|
|
hardware_nodes[
|
|
action_id
|
|
]
|
|
)
|
|
|
|
var device := (
|
|
room.get_device_for_action(
|
|
assignment.cabinet.cabinet_id,
|
|
action_id
|
|
)
|
|
)
|
|
|
|
var device_node: GraphNode = null
|
|
|
|
if device != null:
|
|
device_node = (
|
|
_create_device_node(
|
|
device,
|
|
assignment.cabinet.cabinet_id,
|
|
action_id,
|
|
Vector2(
|
|
1050.0,
|
|
hardware_node.position_offset.y
|
|
)
|
|
)
|
|
)
|
|
|
|
_connect_chain(
|
|
game_node,
|
|
mapping,
|
|
mapping_node,
|
|
hardware_node,
|
|
device_node
|
|
)
|
|
|
|
mapping_number += 1
|
|
|
|
func _create_game_node(
|
|
assignment: ScenarioAssignment,
|
|
position: Vector2
|
|
) -> GraphNode:
|
|
|
|
var node_name := StringName(
|
|
"game__%s"
|
|
% assignment.cabinet.cabinet_id
|
|
)
|
|
|
|
var existing := graph_nodes.get(
|
|
node_name
|
|
) as GraphNode
|
|
|
|
if existing != null:
|
|
return existing
|
|
|
|
var node := _create_graph_node(
|
|
node_name,
|
|
"GAME — %s"
|
|
% assignment.cabinet.display_name,
|
|
position
|
|
)
|
|
|
|
_add_row(
|
|
node,
|
|
assignment.game.display_name
|
|
)
|
|
|
|
var port_events: Array[StringName] = []
|
|
|
|
for event_definition in assignment.game.events:
|
|
if event_definition == null:
|
|
continue
|
|
|
|
var event_row := _add_row(
|
|
node,
|
|
"%s [%s]"
|
|
% [
|
|
event_definition.display_name,
|
|
event_definition.event_id
|
|
]
|
|
)
|
|
|
|
_set_ports(
|
|
node,
|
|
event_row.get_index(),
|
|
false,
|
|
true
|
|
)
|
|
|
|
port_events.append(
|
|
event_definition.event_id
|
|
)
|
|
|
|
game_port_events[
|
|
node.name
|
|
] = port_events
|
|
|
|
return node
|
|
|
|
func _create_mapping_node(
|
|
assignment: ScenarioAssignment,
|
|
mapping: ScenarioEventMapping,
|
|
action_id: StringName,
|
|
position: Vector2
|
|
) -> GraphNode:
|
|
|
|
var node_name := StringName(
|
|
"mapping__%s__%s__%s"
|
|
% [
|
|
assignment.cabinet.cabinet_id,
|
|
mapping.game_event,
|
|
action_id
|
|
]
|
|
)
|
|
|
|
var node := _create_graph_node(
|
|
node_name,
|
|
"SCENARIO MAPPING",
|
|
position
|
|
)
|
|
|
|
mapping_node_data[
|
|
node.name
|
|
] = {
|
|
"cabinet_id": assignment.cabinet.cabinet_id,
|
|
"event_id": mapping.game_event,
|
|
"action_id": action_id
|
|
}
|
|
|
|
var event_row := _add_row(
|
|
node,
|
|
String(mapping.game_event)
|
|
)
|
|
|
|
_set_ports(
|
|
node,
|
|
event_row.get_index(),
|
|
true,
|
|
false
|
|
)
|
|
|
|
var action_row := _add_row(
|
|
node,
|
|
String(action_id)
|
|
)
|
|
|
|
_set_ports(
|
|
node,
|
|
action_row.get_index(),
|
|
false,
|
|
true
|
|
)
|
|
|
|
node.node_selected.connect(
|
|
func() -> void:
|
|
_emit_mapping_selection(
|
|
node
|
|
)
|
|
)
|
|
|
|
return node
|
|
|
|
|
|
func _create_hardware_node(
|
|
assignment: ScenarioAssignment,
|
|
action_id: StringName,
|
|
position: Vector2
|
|
) -> GraphNode:
|
|
|
|
var cabinet := assignment.cabinet
|
|
var hardware := cabinet.hardware_config
|
|
|
|
var action: HardwareAction = null
|
|
|
|
if hardware != null:
|
|
action = hardware.get_action(
|
|
action_id
|
|
)
|
|
|
|
var node_name := StringName(
|
|
"hardware__%s__%s"
|
|
% [
|
|
cabinet.cabinet_id,
|
|
action_id
|
|
]
|
|
)
|
|
|
|
var node := _create_graph_node(
|
|
node_name,
|
|
"HARDWARE",
|
|
position
|
|
)
|
|
|
|
hardware_node_data[
|
|
node.name
|
|
] = {
|
|
"cabinet_id": cabinet.cabinet_id,
|
|
"action_id": action_id
|
|
}
|
|
|
|
var input_row := _add_row(
|
|
node,
|
|
String(action_id)
|
|
)
|
|
|
|
_set_ports(
|
|
node,
|
|
input_row.get_index(),
|
|
true,
|
|
false
|
|
)
|
|
|
|
var description := (
|
|
"Cabinet: %s"
|
|
% cabinet.display_name
|
|
)
|
|
|
|
_add_row(
|
|
node,
|
|
description
|
|
)
|
|
|
|
var gpio_text := "GPIO: ?"
|
|
|
|
if action != null:
|
|
gpio_text = (
|
|
"GPIO: %d"
|
|
% action.gpio_pin
|
|
)
|
|
|
|
var output_row := _add_row(
|
|
node,
|
|
gpio_text
|
|
)
|
|
|
|
_set_ports(
|
|
node,
|
|
output_row.get_index(),
|
|
false,
|
|
true
|
|
)
|
|
|
|
return node
|
|
|
|
func _create_device_node(
|
|
device: RoomDeviceDefinition,
|
|
cabinet_id: StringName,
|
|
action_id: StringName,
|
|
position: Vector2
|
|
) -> GraphNode:
|
|
|
|
var node_name := StringName(
|
|
"device__%s__%s__%s"
|
|
% [
|
|
cabinet_id,
|
|
action_id,
|
|
device.device_id
|
|
]
|
|
)
|
|
|
|
var node := _create_graph_node(
|
|
node_name,
|
|
"ROOM DEVICE",
|
|
position
|
|
)
|
|
|
|
var row := _add_row(
|
|
node,
|
|
device.display_name
|
|
)
|
|
|
|
_set_ports(
|
|
node,
|
|
row.get_index(),
|
|
true,
|
|
false
|
|
)
|
|
|
|
return node
|
|
|
|
func _connect_chain(
|
|
game_node: GraphNode,
|
|
mapping: ScenarioEventMapping,
|
|
mapping_node: GraphNode,
|
|
hardware_node: GraphNode,
|
|
device_node: GraphNode
|
|
) -> void:
|
|
|
|
var game_port := _get_game_event_port(
|
|
game_node.name,
|
|
mapping.game_event
|
|
)
|
|
|
|
if game_port >= 0:
|
|
connect_node(
|
|
game_node.name,
|
|
game_port,
|
|
mapping_node.name,
|
|
0
|
|
)
|
|
|
|
connect_node(
|
|
mapping_node.name,
|
|
0,
|
|
hardware_node.name,
|
|
0
|
|
)
|
|
|
|
if device_node != null:
|
|
connect_node(
|
|
hardware_node.name,
|
|
0,
|
|
device_node.name,
|
|
0
|
|
)
|
|
|
|
func _get_game_event_port(
|
|
game_node_name: StringName,
|
|
event_id: StringName
|
|
) -> int:
|
|
|
|
if not game_port_events.has(
|
|
game_node_name
|
|
):
|
|
return -1
|
|
|
|
var events: Array = (
|
|
game_port_events[
|
|
game_node_name
|
|
]
|
|
)
|
|
|
|
for index in range(
|
|
events.size()
|
|
):
|
|
if events[index] == event_id:
|
|
return index
|
|
|
|
return -1
|
|
|
|
func set_path_active(
|
|
cabinet_id: StringName,
|
|
event_id: StringName,
|
|
action_id: StringName,
|
|
device_id: StringName,
|
|
active: bool
|
|
) -> void:
|
|
|
|
var game_name := StringName(
|
|
"game__%s"
|
|
% cabinet_id
|
|
)
|
|
|
|
var mapping_name := StringName(
|
|
"mapping__%s__%s__%s"
|
|
% [
|
|
cabinet_id,
|
|
event_id,
|
|
action_id
|
|
]
|
|
)
|
|
|
|
var hardware_name := StringName(
|
|
"hardware__%s__%s"
|
|
% [
|
|
cabinet_id,
|
|
action_id
|
|
]
|
|
)
|
|
|
|
var device_name := StringName(
|
|
"device__%s__%s__%s"
|
|
% [
|
|
cabinet_id,
|
|
action_id,
|
|
device_id
|
|
]
|
|
)
|
|
|
|
var amount := (
|
|
1.0
|
|
if active
|
|
else 0.0
|
|
)
|
|
|
|
#
|
|
# The game node has one output port per GameEventDefinition,
|
|
# so find the correct one for this event.
|
|
#
|
|
var game_port := _get_game_event_port(
|
|
game_name,
|
|
event_id
|
|
)
|
|
|
|
if (
|
|
game_port >= 0
|
|
and graph_nodes.has(game_name)
|
|
and graph_nodes.has(mapping_name)
|
|
):
|
|
print(
|
|
"[LOGIC PATH] cabinet=%s event=%s action=%s device=%s game_port=%d active=%s"
|
|
% [
|
|
cabinet_id,
|
|
event_id,
|
|
action_id,
|
|
device_id,
|
|
game_port,
|
|
active
|
|
]
|
|
)
|
|
|
|
set_connection_activity(
|
|
game_name,
|
|
game_port,
|
|
mapping_name,
|
|
0,
|
|
amount
|
|
)
|
|
|
|
print("")
|
|
print("======= PATH HIGHLIGHT DEBUG =======")
|
|
print("game_name: ", game_name)
|
|
print("mapping_name: ", mapping_name)
|
|
print("hardware_name: ", hardware_name)
|
|
print("device_name: ", device_name)
|
|
print("game_port: ", game_port)
|
|
|
|
print(
|
|
"game->mapping exists: ",
|
|
is_node_connected(
|
|
game_name,
|
|
game_port,
|
|
mapping_name,
|
|
0
|
|
)
|
|
)
|
|
|
|
print(
|
|
"mapping->hardware exists: ",
|
|
is_node_connected(
|
|
mapping_name,
|
|
0,
|
|
hardware_name,
|
|
0
|
|
)
|
|
)
|
|
|
|
print(
|
|
"hardware->device exists: ",
|
|
is_node_connected(
|
|
hardware_name,
|
|
0,
|
|
device_name,
|
|
0
|
|
)
|
|
)
|
|
|
|
print("activity: ", amount)
|
|
print("====================================")
|
|
print("")
|
|
|
|
if (
|
|
graph_nodes.has(mapping_name)
|
|
and graph_nodes.has(hardware_name)
|
|
):
|
|
set_connection_activity(
|
|
mapping_name,
|
|
0,
|
|
hardware_name,
|
|
0,
|
|
amount
|
|
)
|
|
|
|
if (
|
|
graph_nodes.has(hardware_name)
|
|
and graph_nodes.has(device_name)
|
|
):
|
|
set_connection_activity(
|
|
hardware_name,
|
|
0,
|
|
device_name,
|
|
0,
|
|
amount
|
|
)
|
|
|
|
func _build_available_hardware(
|
|
assignment: ScenarioAssignment,
|
|
base_y: float
|
|
) -> Dictionary:
|
|
|
|
var result: Dictionary = {}
|
|
|
|
var cabinet := assignment.cabinet
|
|
|
|
if cabinet.hardware_config == null:
|
|
return result
|
|
|
|
var index := 0
|
|
|
|
for action in cabinet.hardware_config.actions:
|
|
if action == null:
|
|
continue
|
|
|
|
var position := Vector2(
|
|
720.0,
|
|
base_y + float(index) * 140.0
|
|
)
|
|
|
|
var node := _create_hardware_node(
|
|
assignment,
|
|
action.action_id,
|
|
position
|
|
)
|
|
|
|
result[
|
|
action.action_id
|
|
] = node
|
|
|
|
index += 1
|
|
|
|
return result
|
|
|
|
func _on_connection_request(
|
|
from_node: StringName,
|
|
from_port: int,
|
|
to_node: StringName,
|
|
to_port: int
|
|
) -> void:
|
|
|
|
print("")
|
|
print("======= CONNECTION REQUEST =======")
|
|
print("FROM NODE: ", from_node)
|
|
print("FROM PORT: ", from_port)
|
|
print("TO NODE: ", to_node)
|
|
print("TO PORT: ", to_port)
|
|
print("Game nodes known: ", game_port_events.keys())
|
|
print("Hardware nodes known: ", hardware_node_data.keys())
|
|
print("==================================")
|
|
print("")
|
|
|
|
# ... existing function continues ...
|
|
|
|
if scenario == null:
|
|
return
|
|
|
|
if not game_port_events.has(
|
|
from_node
|
|
):
|
|
_reject_edit(
|
|
"Connections must start from a game event."
|
|
)
|
|
return
|
|
|
|
if not hardware_node_data.has(
|
|
to_node
|
|
):
|
|
_reject_edit(
|
|
"Game events can only connect to cabinet hardware actions."
|
|
)
|
|
return
|
|
|
|
var events: Array = (
|
|
game_port_events[from_node]
|
|
)
|
|
|
|
if (
|
|
from_port < 0
|
|
or
|
|
from_port >= events.size()
|
|
):
|
|
_reject_edit(
|
|
"Invalid game event port."
|
|
)
|
|
return
|
|
|
|
var event_id: StringName = (
|
|
events[from_port]
|
|
)
|
|
|
|
var hardware_data: Dictionary = (
|
|
hardware_node_data[to_node]
|
|
)
|
|
|
|
var cabinet_id: StringName = (
|
|
hardware_data["cabinet_id"]
|
|
)
|
|
|
|
var action_id: StringName = (
|
|
hardware_data["action_id"]
|
|
)
|
|
|
|
var source_cabinet_id := (
|
|
_get_game_node_cabinet_id(
|
|
from_node
|
|
)
|
|
)
|
|
|
|
if source_cabinet_id != cabinet_id:
|
|
_reject_edit(
|
|
"Cannot connect events between different cabinets."
|
|
)
|
|
return
|
|
|
|
if _mapping_exists(
|
|
cabinet_id,
|
|
event_id,
|
|
action_id
|
|
):
|
|
_reject_edit(
|
|
"This scenario mapping already exists."
|
|
)
|
|
return
|
|
|
|
_add_mapping(
|
|
cabinet_id,
|
|
event_id,
|
|
action_id
|
|
)
|
|
|
|
func _get_game_node_cabinet_id(
|
|
game_node_name: StringName
|
|
) -> StringName:
|
|
|
|
for assignment in scenario.assignments:
|
|
if assignment == null:
|
|
continue
|
|
|
|
if assignment.cabinet == null:
|
|
continue
|
|
|
|
var expected_name := StringName(
|
|
"game__%s"
|
|
% assignment.cabinet.cabinet_id
|
|
)
|
|
|
|
if expected_name == game_node_name:
|
|
return assignment.cabinet.cabinet_id
|
|
|
|
return &""
|
|
|
|
func _mapping_exists(
|
|
cabinet_id: StringName,
|
|
event_id: StringName,
|
|
action_id: StringName
|
|
) -> bool:
|
|
|
|
var assignment := (
|
|
scenario.get_assignment_for_cabinet(
|
|
cabinet_id
|
|
)
|
|
)
|
|
|
|
if assignment == null:
|
|
return false
|
|
|
|
for mapping in assignment.event_mappings:
|
|
if mapping == null:
|
|
continue
|
|
|
|
if mapping.game_event != event_id:
|
|
continue
|
|
|
|
if mapping.hardware_actions.has(
|
|
action_id
|
|
):
|
|
return true
|
|
|
|
return false
|
|
|
|
func _add_mapping(
|
|
cabinet_id: StringName,
|
|
event_id: StringName,
|
|
action_id: StringName
|
|
) -> void:
|
|
|
|
var assignment := (
|
|
scenario.get_assignment_for_cabinet(
|
|
cabinet_id
|
|
)
|
|
)
|
|
|
|
if assignment == null:
|
|
_reject_edit(
|
|
"Cabinet assignment could not be found."
|
|
)
|
|
return
|
|
|
|
if assignment.game == null:
|
|
return
|
|
|
|
if not assignment.game.has_event(
|
|
event_id
|
|
):
|
|
_reject_edit(
|
|
"Game '%s' does not define event '%s'."
|
|
% [
|
|
assignment.game.game_id,
|
|
event_id
|
|
]
|
|
)
|
|
return
|
|
|
|
if assignment.cabinet.hardware_config == null:
|
|
return
|
|
|
|
if not assignment.cabinet.hardware_config.has_action(
|
|
action_id
|
|
):
|
|
_reject_edit(
|
|
"Cabinet '%s' does not define hardware action '%s'."
|
|
% [
|
|
cabinet_id,
|
|
action_id
|
|
]
|
|
)
|
|
return
|
|
|
|
var target_mapping: ScenarioEventMapping = null
|
|
|
|
for mapping in assignment.event_mappings:
|
|
if mapping == null:
|
|
continue
|
|
|
|
if mapping.game_event == event_id:
|
|
target_mapping = mapping
|
|
break
|
|
|
|
if target_mapping == null:
|
|
target_mapping = (
|
|
ScenarioEventMapping.new()
|
|
)
|
|
|
|
target_mapping.game_event = event_id
|
|
|
|
assignment.event_mappings.append(
|
|
target_mapping
|
|
)
|
|
|
|
target_mapping.hardware_actions.append(
|
|
action_id
|
|
)
|
|
|
|
print(
|
|
"[LOGIC EDITOR] Added mapping: %s :: %s -> %s"
|
|
% [
|
|
cabinet_id,
|
|
event_id,
|
|
action_id
|
|
]
|
|
)
|
|
|
|
scenario_mapping_changed.emit()
|
|
|
|
rebuild()
|
|
|
|
func _reject_edit(
|
|
message: String
|
|
) -> void:
|
|
|
|
print(
|
|
"[LOGIC EDITOR] REJECTED: ",
|
|
message
|
|
)
|
|
|
|
edit_rejected.emit(
|
|
message
|
|
)
|
|
|
|
func _on_disconnection_request(
|
|
from_node: StringName,
|
|
from_port: int,
|
|
to_node: StringName,
|
|
to_port: int
|
|
) -> void:
|
|
|
|
var mapping_node_name: StringName = &""
|
|
|
|
if mapping_node_data.has(
|
|
from_node
|
|
):
|
|
mapping_node_name = from_node
|
|
|
|
elif mapping_node_data.has(
|
|
to_node
|
|
):
|
|
mapping_node_name = to_node
|
|
|
|
else:
|
|
_reject_edit(
|
|
"This connection belongs to the RoomDefinition and cannot be removed here."
|
|
)
|
|
return
|
|
|
|
var data: Dictionary = (
|
|
mapping_node_data[
|
|
mapping_node_name
|
|
]
|
|
)
|
|
|
|
_remove_mapping(
|
|
data["cabinet_id"],
|
|
data["event_id"],
|
|
data["action_id"]
|
|
)
|
|
|
|
func _remove_mapping(
|
|
cabinet_id: StringName,
|
|
event_id: StringName,
|
|
action_id: StringName
|
|
) -> void:
|
|
|
|
var assignment := (
|
|
scenario.get_assignment_for_cabinet(
|
|
cabinet_id
|
|
)
|
|
)
|
|
|
|
if assignment == null:
|
|
return
|
|
|
|
for mapping in assignment.event_mappings:
|
|
if mapping == null:
|
|
continue
|
|
|
|
if mapping.game_event != event_id:
|
|
continue
|
|
|
|
mapping.hardware_actions.erase(
|
|
action_id
|
|
)
|
|
|
|
#
|
|
# Remove empty mapping Resources.
|
|
#
|
|
if mapping.hardware_actions.is_empty():
|
|
assignment.event_mappings.erase(
|
|
mapping
|
|
)
|
|
|
|
print(
|
|
"[LOGIC EDITOR] Removed mapping: %s :: %s -> %s"
|
|
% [
|
|
cabinet_id,
|
|
event_id,
|
|
action_id
|
|
]
|
|
)
|
|
|
|
scenario_mapping_changed.emit()
|
|
|
|
rebuild()
|
|
|
|
return
|
|
|
|
func _emit_mapping_selection(
|
|
node: GraphNode
|
|
) -> void:
|
|
|
|
if node == null:
|
|
return
|
|
|
|
if not mapping_node_data.has(
|
|
node.name
|
|
):
|
|
return
|
|
|
|
var data: Dictionary = (
|
|
mapping_node_data[
|
|
node.name
|
|
]
|
|
)
|
|
|
|
var cabinet_id: StringName = (
|
|
data["cabinet_id"]
|
|
)
|
|
|
|
var event_id: StringName = (
|
|
data["event_id"]
|
|
)
|
|
|
|
var action_id: StringName = (
|
|
data["action_id"]
|
|
)
|
|
|
|
var device_id: StringName = &""
|
|
|
|
if room != null:
|
|
var device := (
|
|
room.get_device_for_action(
|
|
cabinet_id,
|
|
action_id
|
|
)
|
|
)
|
|
|
|
if device != null:
|
|
device_id = device.device_id
|
|
|
|
logic_path_selected.emit(
|
|
cabinet_id,
|
|
event_id,
|
|
action_id,
|
|
device_id
|
|
)
|
|
|
|
func apply_editor_selection(
|
|
selection: EscapeRoomEditorSelection
|
|
) -> void:
|
|
|
|
if selection == null:
|
|
return
|
|
|
|
_clear_editor_selection_highlight()
|
|
|
|
if selection.cabinet_id != &"":
|
|
_highlight_cabinet_logic(
|
|
selection.cabinet_id
|
|
)
|
|
|
|
if (
|
|
selection.event_id != &""
|
|
and selection.action_id != &""
|
|
):
|
|
_highlight_specific_logic_path(
|
|
selection.cabinet_id,
|
|
selection.event_id,
|
|
selection.action_id,
|
|
selection.device_id
|
|
)
|
|
|
|
func _highlight_cabinet_logic(
|
|
cabinet_id: StringName
|
|
) -> void:
|
|
|
|
for node_name in graph_nodes:
|
|
var node: GraphNode = (
|
|
graph_nodes[
|
|
node_name
|
|
]
|
|
)
|
|
|
|
if node == null:
|
|
continue
|
|
|
|
var node_cabinet_id := (
|
|
_get_graph_node_cabinet_id(
|
|
node_name
|
|
)
|
|
)
|
|
|
|
if node_cabinet_id == cabinet_id:
|
|
node.modulate = Color.WHITE
|
|
|
|
else:
|
|
node.modulate = Color(
|
|
0.5,
|
|
0.5,
|
|
0.5,
|
|
1.0
|
|
)
|
|
|
|
func _clear_editor_selection_highlight() -> void:
|
|
|
|
for node_name in graph_nodes:
|
|
var node: GraphNode = graph_nodes[node_name]
|
|
|
|
if node == null:
|
|
continue
|
|
|
|
node.modulate = Color.WHITE
|
|
|
|
func _highlight_specific_logic_path(
|
|
_cabinet_id: StringName,
|
|
_event_id: StringName,
|
|
_action_id: StringName,
|
|
_device_id: StringName
|
|
) -> void:
|
|
|
|
#
|
|
# Exact editor-selection connection styling
|
|
# will be separate from simulation activity.
|
|
#
|
|
pass
|
|
|
|
func _get_graph_node_cabinet_id(
|
|
node_name: StringName
|
|
) -> StringName:
|
|
|
|
#
|
|
# Game nodes already have a reliable lookup.
|
|
#
|
|
if game_port_events.has(
|
|
node_name
|
|
):
|
|
return _get_game_node_cabinet_id(
|
|
node_name
|
|
)
|
|
|
|
#
|
|
# Scenario mapping nodes.
|
|
#
|
|
if mapping_node_data.has(
|
|
node_name
|
|
):
|
|
var mapping_data: Dictionary = (
|
|
mapping_node_data[
|
|
node_name
|
|
]
|
|
)
|
|
|
|
return mapping_data[
|
|
"cabinet_id"
|
|
]
|
|
|
|
#
|
|
# Hardware nodes.
|
|
#
|
|
if hardware_node_data.has(
|
|
node_name
|
|
):
|
|
var hardware_data: Dictionary = (
|
|
hardware_node_data[
|
|
node_name
|
|
]
|
|
)
|
|
|
|
return hardware_data[
|
|
"cabinet_id"
|
|
]
|
|
|
|
#
|
|
# Device node names are:
|
|
# device__cabinet_id__action_id__device_id
|
|
#
|
|
var node_text := String(
|
|
node_name
|
|
)
|
|
|
|
if node_text.begins_with(
|
|
"device__"
|
|
):
|
|
var parts := node_text.split(
|
|
"__"
|
|
)
|
|
|
|
if parts.size() >= 4:
|
|
return StringName(
|
|
parts[1]
|
|
)
|
|
|
|
return &""
|