class_name InGameEditor extends Control ## In-game "build mode" UI (docs/architecture/editor.md ยง7.2 `InGameEditor`). ## ## Built entirely from `Control` nodes in code (matching this codebase's ## existing convention for dynamic UI -- see `game.gd`'s `_show_build_stamp` ## / `_show_status`) rather than a hand-authored `.tscn`, so there is one ## fewer place for a scene-file/script mismatch to hide. It is only ever ## instantiated when the player enters build mode (see `game.gd`'s ## `_toggle_build_mode`), so it costs nothing during normal play. ## ## Composition matches the doc exactly: ## EditorToolbar -- tool mode switcher ## PalettePanel -- catalog-sourced object palette ## EditorViewport -- the 2D tile/object canvas ## InspectorPanel -- property editor for the selected object ## EditorCore -- shared core (passed in, not owned/created here) ## ## This UI (and `EditorCore`) never talk to the network -- persisting edits ## is a sibling task's job (world_persistence_impl.md's batch-PUT save ## path). Closing this editor just discards the UI; the in-memory ## `ChunkEditorModel` it was pointed at is unaffected either way. signal closed var core: EditorCore var _toolbar: EditorToolbar var _palette: PalettePanel var _inspector: InspectorPanel var _viewport: EditorViewport var _close_button: Button func _init(p_core: EditorCore) -> void: core = p_core name = "InGameEditor" set_anchors_preset(Control.PRESET_FULL_RECT) mouse_filter = Control.MOUSE_FILTER_STOP func _ready() -> void: _build_ui() _refresh_history_buttons() func _build_ui() -> void: var bg := ColorRect.new() bg.color = Color(0.08, 0.08, 0.09, 0.96) bg.set_anchors_preset(Control.PRESET_FULL_RECT) bg.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(bg) var root_vbox := VBoxContainer.new() root_vbox.set_anchors_preset(Control.PRESET_FULL_RECT) root_vbox.offset_left = 12 root_vbox.offset_top = 12 root_vbox.offset_right = -12 root_vbox.offset_bottom = -12 add_child(root_vbox) var top_bar := HBoxContainer.new() root_vbox.add_child(top_bar) _toolbar = EditorToolbar.new() _toolbar.size_flags_horizontal = Control.SIZE_EXPAND_FILL top_bar.add_child(_toolbar) _close_button = Button.new() _close_button.text = "Close (F2)" _close_button.pressed.connect(_on_close_pressed) top_bar.add_child(_close_button) var body := HBoxContainer.new() body.size_flags_vertical = Control.SIZE_EXPAND_FILL root_vbox.add_child(body) var viewport_scroll := ScrollContainer.new() viewport_scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL viewport_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL body.add_child(viewport_scroll) _viewport = EditorViewport.new() viewport_scroll.add_child(_viewport) var sidebar := VBoxContainer.new() sidebar.custom_minimum_size = Vector2(240, 0) body.add_child(sidebar) _palette = PalettePanel.new() sidebar.add_child(_palette) _inspector = InspectorPanel.new() sidebar.add_child(_inspector) _viewport.setup(core, 0) _palette.set_catalog(core.catalog) _inspector.setup(core) _toolbar.set_active_tool(core.tools.get_active_tool_id()) _toolbar.tool_selected.connect(_on_tool_selected) _toolbar.undo_pressed.connect(_on_undo_pressed) _toolbar.redo_pressed.connect(_on_redo_pressed) _palette.object_selected.connect(_on_palette_object_selected) _inspector.delete_requested.connect(_on_inspector_delete_requested) _viewport.edit_applied.connect(_on_edit_applied) core.selection.selection_changed.connect(_on_selection_changed) func _unhandled_key_input(event: InputEvent) -> void: if not (event is InputEventKey): return var key := event as InputEventKey if not key.pressed or key.echo: return if key.ctrl_pressed and key.keycode == KEY_Z: _on_undo_pressed() get_viewport().set_input_as_handled() elif key.ctrl_pressed and key.keycode == KEY_Y: _on_redo_pressed() get_viewport().set_input_as_handled() elif key.keycode == KEY_DELETE and core.selection.has_selection(): core.delete_object(core.selection.get_selected()) _on_edit_applied() get_viewport().set_input_as_handled() func _on_tool_selected(tool_id: String) -> void: core.tools.set_active_tool(tool_id) func _on_palette_object_selected(catalog_id: String) -> void: _viewport.pending_catalog_id = catalog_id core.tools.set_active_tool("object_place") _toolbar.set_active_tool("object_place") func _on_inspector_delete_requested(object_id: String) -> void: core.delete_object(object_id) _on_edit_applied() func _on_undo_pressed() -> void: core.undo() _on_edit_applied() func _on_redo_pressed() -> void: core.redo() _on_edit_applied() func _on_edit_applied() -> void: _viewport.queue_redraw() _inspector.show_object(core.selection.get_selected()) _refresh_history_buttons() func _on_selection_changed(object_id: String) -> void: _inspector.show_object(object_id) func _refresh_history_buttons() -> void: _toolbar.set_history_state(core.undo_redo.can_undo(), core.undo_redo.can_redo()) func _on_close_pressed() -> void: closed.emit() queue_free()