|
|
|
|
@ -28,7 +28,7 @@ var save_button: Button
|
|
|
|
|
var builder_scene_root: Node2D
|
|
|
|
|
var builder_camera: Camera2D
|
|
|
|
|
|
|
|
|
|
# --- NEW: State Management Enum ---
|
|
|
|
|
# --- State Management Enum ---
|
|
|
|
|
enum BuilderState {
|
|
|
|
|
IDLE,
|
|
|
|
|
PLACING_STRUCTURAL,
|
|
|
|
|
@ -45,6 +45,8 @@ var grid_size: float = 50.0
|
|
|
|
|
|
|
|
|
|
var undo_redo: EditorUndoRedoManager
|
|
|
|
|
|
|
|
|
|
# --- Most of the setup functions remain the same ---
|
|
|
|
|
|
|
|
|
|
func _enter_tree():
|
|
|
|
|
main_screen = MAIN_EDITOR_SCENE.instantiate()
|
|
|
|
|
EditorInterface.get_editor_main_screen().add_child(main_screen)
|
|
|
|
|
@ -179,7 +181,6 @@ func _on_viewport_input(event: InputEvent) -> void:
|
|
|
|
|
if event is InputEventMouseMotion:
|
|
|
|
|
_update_preview_position()
|
|
|
|
|
|
|
|
|
|
# --- REFACTORED: Use match statement for state-based input handling ---
|
|
|
|
|
match current_state:
|
|
|
|
|
BuilderState.IDLE:
|
|
|
|
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.is_pressed():
|
|
|
|
|
@ -198,7 +199,6 @@ func _on_viewport_input(event: InputEvent) -> void:
|
|
|
|
|
on_clear_preview()
|
|
|
|
|
|
|
|
|
|
BuilderState.WIRING:
|
|
|
|
|
# Placeholder for future wiring logic
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -227,8 +227,6 @@ func _on_component_selected(component_scene: PackedScene):
|
|
|
|
|
current_state = BuilderState.PLACING_COMPONENT
|
|
|
|
|
active_scene = component_scene
|
|
|
|
|
preview_node = component_scene.instantiate() as Component
|
|
|
|
|
# You might want a specific material or modulation for component previews
|
|
|
|
|
# preview_node.modulate = Color(0.5, 1.0, 0.5, 0.5)
|
|
|
|
|
builder_scene_root.add_child(preview_node)
|
|
|
|
|
|
|
|
|
|
print("Now placing component: ", component_scene.resource_path)
|
|
|
|
|
@ -256,20 +254,18 @@ func _update_preview_position():
|
|
|
|
|
preview_node.global_position = snapped_pos
|
|
|
|
|
preview_node.rotation = rotation_angle
|
|
|
|
|
BuilderState.PLACING_COMPONENT:
|
|
|
|
|
# For components, we snap to the closest attachment point
|
|
|
|
|
var target_module = _find_first_module()
|
|
|
|
|
if target_module:
|
|
|
|
|
var closest_point = _find_closest_attachment_point(target_module, world_mouse_pos)
|
|
|
|
|
if closest_point:
|
|
|
|
|
preview_node.global_position = closest_point.position
|
|
|
|
|
# You might want to handle rotation differently for components
|
|
|
|
|
# preview_node.rotation = closest_point.rotation
|
|
|
|
|
else:
|
|
|
|
|
preview_node.global_position = world_mouse_pos # Or hide it
|
|
|
|
|
preview_node.global_position = world_mouse_pos
|
|
|
|
|
else:
|
|
|
|
|
preview_node.global_position = world_mouse_pos # Or hide it
|
|
|
|
|
preview_node.global_position = world_mouse_pos
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- REFACTORED: Piece Placement ---
|
|
|
|
|
func _place_piece_from_preview():
|
|
|
|
|
if not is_instance_valid(preview_node) or not is_instance_valid(active_scene):
|
|
|
|
|
return
|
|
|
|
|
@ -288,19 +284,22 @@ func _place_piece_from_preview():
|
|
|
|
|
target_module.owner = builder_scene_root
|
|
|
|
|
|
|
|
|
|
var piece_to_place = active_scene.instantiate()
|
|
|
|
|
target_module.structural_container.add_child(piece_to_place)
|
|
|
|
|
|
|
|
|
|
# --- The main change: Add as a direct child of the module ---
|
|
|
|
|
target_module.add_child(piece_to_place)
|
|
|
|
|
piece_to_place.owner = target_module
|
|
|
|
|
piece_to_place.rotation = rotation_angle
|
|
|
|
|
piece_to_place.global_position = snapped_pos
|
|
|
|
|
|
|
|
|
|
undo_redo.create_action("Place Structural Piece")
|
|
|
|
|
undo_redo.add_do_method(target_module.structural_container, "add_child", piece_to_place)
|
|
|
|
|
undo_redo.add_do_method(target_module, "add_child", piece_to_place)
|
|
|
|
|
undo_redo.add_do_method(piece_to_place, "set_owner", target_module)
|
|
|
|
|
undo_redo.add_do_method(target_module, "_recalculate_collision_shape")
|
|
|
|
|
undo_redo.add_undo_method(target_module.structural_container, "remove_child", piece_to_place)
|
|
|
|
|
undo_redo.add_undo_method(target_module, "remove_child", piece_to_place)
|
|
|
|
|
undo_redo.add_undo_method(target_module, "_recalculate_collision_shape")
|
|
|
|
|
undo_redo.commit_action()
|
|
|
|
|
|
|
|
|
|
# --- Component Placement remains the same ---
|
|
|
|
|
func _place_component_from_preview():
|
|
|
|
|
if not is_instance_valid(preview_node) or not is_instance_valid(active_scene):
|
|
|
|
|
push_error("Cannot place component: Invalid preview or scene.")
|
|
|
|
|
@ -334,6 +333,7 @@ func _place_component_from_preview():
|
|
|
|
|
|
|
|
|
|
preview_node.global_position = closest_point.position
|
|
|
|
|
|
|
|
|
|
# --- Find Nearby Modules remains the same ---
|
|
|
|
|
func _find_nearby_modules(position: Vector2) -> Module:
|
|
|
|
|
const OVERLAP_MARGIN = 20.0
|
|
|
|
|
|
|
|
|
|
@ -367,8 +367,9 @@ func _find_nearby_modules(position: Vector2) -> Module:
|
|
|
|
|
if not result.is_empty():
|
|
|
|
|
var collider = result[0].get("collider")
|
|
|
|
|
if collider is StructuralPiece:
|
|
|
|
|
if collider.get_parent() and collider.get_parent().get_parent() is Module:
|
|
|
|
|
return collider.get_parent().get_parent()
|
|
|
|
|
# --- REFACTORED: The module is now the direct parent/owner ---
|
|
|
|
|
if is_instance_valid(collider.owner) and collider.owner is Module:
|
|
|
|
|
return collider.owner
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
|
|
|
|
@ -383,7 +384,6 @@ func _remove_piece_under_mouse():
|
|
|
|
|
if not viewport: return
|
|
|
|
|
var world_mouse_pos = viewport.get_canvas_transform().affine_inverse() * viewport.get_mouse_position()
|
|
|
|
|
|
|
|
|
|
# Point query to find what's under the mouse
|
|
|
|
|
var space_state = builder_world.direct_space_state
|
|
|
|
|
var query = PhysicsPointQueryParameters2D.new()
|
|
|
|
|
query.position = world_mouse_pos
|
|
|
|
|
@ -394,33 +394,35 @@ func _remove_piece_under_mouse():
|
|
|
|
|
if collider is StructuralPiece:
|
|
|
|
|
_remove_piece_with_undo_redo(collider)
|
|
|
|
|
elif collider is Component:
|
|
|
|
|
# Add logic to remove components here
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- REFACTORED: Piece Removal ---
|
|
|
|
|
func _remove_piece_with_undo_redo(piece: StructuralPiece):
|
|
|
|
|
var module = piece.owner as Module
|
|
|
|
|
var parent = piece.get_parent()
|
|
|
|
|
if not is_instance_valid(module) or not module is Module:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
undo_redo.create_action("Remove Structural Piece")
|
|
|
|
|
|
|
|
|
|
if module.structural_container.get_child_count(false) == 1 and module.get_child_count() > 2 : # Structural and HullVolume containers
|
|
|
|
|
# If it's the last piece, remove the whole module
|
|
|
|
|
# If this is the last structural piece of the module...
|
|
|
|
|
if module.get_structural_pieces().size() == 1:
|
|
|
|
|
# ...remove the entire module.
|
|
|
|
|
undo_redo.add_do_method(builder_scene_root, "remove_child", module)
|
|
|
|
|
undo_redo.add_undo_method(builder_scene_root, "add_child", module)
|
|
|
|
|
undo_redo.add_undo_method(module, "set_owner", builder_scene_root)
|
|
|
|
|
else:
|
|
|
|
|
# Otherwise, just remove the piece
|
|
|
|
|
undo_redo.add_do_method(parent, "remove_child", piece)
|
|
|
|
|
# Otherwise, just remove the single piece from its parent (the module).
|
|
|
|
|
undo_redo.add_do_method(module, "remove_child", piece)
|
|
|
|
|
undo_redo.add_do_method(module, "_recalculate_collision_shape")
|
|
|
|
|
|
|
|
|
|
undo_redo.add_undo_method(parent, "add_child", piece)
|
|
|
|
|
undo_redo.add_undo_method(module, "add_child", piece)
|
|
|
|
|
undo_redo.add_undo_method(piece, "set_owner", module) # Re-assign owner on undo
|
|
|
|
|
undo_redo.add_undo_method(module, "_recalculate_collision_shape")
|
|
|
|
|
|
|
|
|
|
undo_redo.commit_action()
|
|
|
|
|
|
|
|
|
|
# --- Toolbar Button Functions ---
|
|
|
|
|
# --- Toolbar Button Functions (No changes needed) ---
|
|
|
|
|
func _on_rotate_button_pressed():
|
|
|
|
|
rotation_angle = wrapf(rotation_angle + PI / 2, 0, TAU)
|
|
|
|
|
if is_instance_valid(preview_node):
|
|
|
|
|
@ -480,7 +482,8 @@ func _perform_save(file_path: String, module_to_save: Module):
|
|
|
|
|
|
|
|
|
|
func _on_undo_redo_action_committed():
|
|
|
|
|
_refresh_tree_display()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# --- REFACTORED: Tree Display ---
|
|
|
|
|
func _refresh_tree_display():
|
|
|
|
|
if not is_instance_valid(tree_control):
|
|
|
|
|
return
|
|
|
|
|
@ -489,25 +492,23 @@ func _refresh_tree_display():
|
|
|
|
|
var root_item = tree_control.create_item()
|
|
|
|
|
root_item.set_text(0, builder_scene_root.name)
|
|
|
|
|
|
|
|
|
|
# Iterate through all modules and populate the tree.
|
|
|
|
|
for module in builder_scene_root.get_children():
|
|
|
|
|
if module is Module:
|
|
|
|
|
print(module)
|
|
|
|
|
var module_item = tree_control.create_item(root_item)
|
|
|
|
|
module_item.set_text(0, module.name)
|
|
|
|
|
module_item.set_meta("node", module)
|
|
|
|
|
|
|
|
|
|
for piece in module.structural_container.get_children():
|
|
|
|
|
if piece is StructuralPiece:
|
|
|
|
|
var piece_item = tree_control.create_item(module_item)
|
|
|
|
|
piece_item.set_text(0, piece.name)
|
|
|
|
|
piece_item.set_meta("node", piece)
|
|
|
|
|
|
|
|
|
|
# Also show components in the tree
|
|
|
|
|
for component in module.get_children():
|
|
|
|
|
if component is Component:
|
|
|
|
|
var component_item = tree_control.create_item(module_item)
|
|
|
|
|
component_item.set_text(0, component.name)
|
|
|
|
|
component_item.set_meta("node", component)
|
|
|
|
|
# Use the module's helper functions to find children
|
|
|
|
|
for piece in module.get_structural_pieces():
|
|
|
|
|
var piece_item = tree_control.create_item(module_item)
|
|
|
|
|
piece_item.set_text(0, piece.name)
|
|
|
|
|
piece_item.set_meta("node", piece)
|
|
|
|
|
|
|
|
|
|
for component in module.get_components():
|
|
|
|
|
var component_item = tree_control.create_item(module_item)
|
|
|
|
|
component_item.set_text(0, component.name)
|
|
|
|
|
component_item.set_meta("node", component)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _find_closest_attachment_point(module: Module, world_pos: Vector2):
|
|
|
|
|
|