7c37a0dc2f
Implements the in-game editor's shared, data-native core and a Control-based UI on top of it, per docs/architecture/editor.md §7.1/§7.2, scoped to 2D per §8's own risk mitigation (the default world is flat, CHUNK_SIZE_Z=1 -- no 3D gizmos here). EditorCore (scripts/editor/): - ChunkEditorModel wraps a ChunkDataDecoder.ChunkPayload directly (no parallel data model) and adds a TileLayerData.set_tile() mutator alongside the existing get_tile(). - EditorUndoRedoStack wraps Godot's built-in UndoRedo (game-usable, not the editor-only EditorUndoRedoManager) so every mutation is a real, revertible do/undo Callable pair over ChunkEditorModel's raw setters. - SelectionManager tracks the selected object id as UI state, deliberately outside the undo/redo stack. - ToolRegistry + 5 tools (BrushTool, ObjectPlaceTool, SelectTool, EraseTool, TransformTool -- the 5th covers move/rotate/scale, which the doc's tool list doesn't literally name but the task requires) is the single choke point every mutation goes through. - EditorObjectCatalog (assets/editor/object_catalog.json) is a new, small "placeable object" catalog -- AvatarCatalog and SpawnCatalog were checked first and are both scoped to other concerns, so this mirrors their loading convention rather than reusing either. InGameEditor UI (scripts/editor/ui/): EditorViewport (2D top-down tile grid + object dots), EditorToolbar, PalettePanel, InspectorPanel, wired together in InGameEditor. Built from Control nodes in code (matching game.gd's existing dynamic-UI convention) rather than a hand-authored .tscn. Entry point: game.gd gates build mode behind a new toggle_build_mode input action (F2), freezing the player and opening a fresh empty ChunkEditorModel. Loading a *real* chunk from the backend for editing, and saving edits back, are explicitly out of scope -- a sibling task owns the batch-PUT/conflict-handling save path (world_persistence_impl.md). Verification: scripts/smoke_test_editor_core.gd decodes a real backend fixture (backend/tests/fixtures/chunks/simple_tile_chunk.bin), drives place/move/rotate/scale/delete/paint/erase entirely through EditorCore.use_tool()/ToolRegistry (never touching ChunkEditorModel's raw setters directly), undoes and redoes every step, and round-trips the result through encode_binary()/decode_binary() to prove the edited model stays a valid, byte-compatible ChunkPayload. 69/69 assertions pass. Full existing Godot smoke suite (16 other scripts/smoke_test_*.gd, backend + dedicated-server-dependent ones included) still passes with no regressions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2FESCaMXeJpC9gSctiEeD
69 lines
2.9 KiB
Plaintext
69 lines
2.9 KiB
Plaintext
; Engine configuration file.
|
|
; It's best edited using the editor UI and not directly,
|
|
; but it can also be edited by hand.
|
|
|
|
config_version=5
|
|
|
|
[application]
|
|
|
|
config/name="AGP-Game"
|
|
run/main_scene="res://scenes/main_menu.tscn"
|
|
config/features=PackedStringArray("4.6.2")
|
|
agp/api_base_url="http://127.0.0.1:8000"
|
|
|
|
[autoload]
|
|
|
|
ApiClient="*res://scripts/api_client.gd"
|
|
AvatarSystem="*res://scripts/avatar/avatar_system.gd"
|
|
Session="*res://scripts/session.gd"
|
|
|
|
[display]
|
|
|
|
window/size/viewport_width=1920
|
|
window/size/viewport_height=1080
|
|
window/stretch/mode="canvas_items"
|
|
|
|
[input]
|
|
|
|
move_forward={
|
|
"deadzone": 0.5,
|
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
|
]
|
|
}
|
|
move_left={
|
|
"deadzone": 0.5,
|
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
|
]
|
|
}
|
|
move_right={
|
|
"deadzone": 0.5,
|
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
|
]
|
|
}
|
|
move_down={
|
|
"deadzone": 0.5,
|
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
|
]
|
|
}
|
|
interact={
|
|
"deadzone": 0.5,
|
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
|
]
|
|
}
|
|
toggle_build_mode={
|
|
"deadzone": 0.5,
|
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194333,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
|
]
|
|
}
|
|
|
|
[physics]
|
|
|
|
; Fixed 30 Hz sim -- the multiplayer server, client prediction, and snapshot
|
|
; rate all key off this. See docs/architecture/networking.md 3.1.
|
|
common/physics_ticks_per_second=30
|
|
|
|
[rendering]
|
|
|
|
renderer/rendering_method="gl_compatibility"
|
|
renderer/rendering_method.mobile="gl_compatibility"
|