Files
llamamod/notes/editor-tools-objects.md
T

104 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Level editor – objects, scripts, paths, cameras (`editor.tools.objects`)
Plugin `ObjectsEditorPlugin` (phase 3, discovered automatically). Parameter decoding and the strat
VM: `notes/editor-object-schemas.md`. Tests: `ObjectToolsTest` (needs `-Dllamamod.cd`),
screenshot tour `ObjectsTour` (test classes).
![camera zone](img/editor/obj_camera_zone.jpg)
## What it adds
| Part | Content |
|---|---|
| **Objects panel** (left dock) | every placeable type of the base with rendered thumbnail, category filter, search (name + schema summary), favourites (right-click – it never selects or switches to the Place tool, nor does the click that closes the menu; ★ filter), "parameters decoded" marker; drag into the viewport or click → Place tool. Quick setups: Player start, Checkpoint, Exit, Secret, Coin line, Coin arc |
| **Place tool** (P) | click = place on the collision floor under the cursor (snap to floor, grid snap), yaw = camera direction / facing the camera / 0° / type default; click + drag an object = move along the ground (its waypoints move along); drag the yellow knob = rotate (angle snap; a checkpoint's respawn node turns with it). Modes for the setups |
| Setups | **Player start** moves the existing player (exactly one) or places a Llama with one start camera node 2 cells behind (moving keeps door nodes, carries the near nodes and the village fly-by trigger). **Checkpoint** = CheckPointPost + respawn node 0.4 ahead (floor height). **Exit** = LevelDoor in the cell centre + 1×1 FLOOR brush with collision material 16; target picked in the options bar (world/chapter names, "world finished 7,7,7"). **Secret** = Secret with a 3×3 box (2 nodes). Guards, alligators and spawners get one 4×4 box (and guards event bit 0) on every way of adding an object (`Edits.placeStrat`: Place tool, drag from Objects panel / Assets, double-click) |
| **Coin tool** (O) | Line: two clicks, coins every *spacing*; Arc: centre + point on the circle, *count* coins over *arc°*; Coin/Coin5, height above floor, follow floor; live preview |
| **Waypoint tool** (N) | select an object (or node): click = add node after the selected node / at the chain end (creates the chain if missing), Ctrl+click = insert before, drag = move (keeps height above floor), Backspace/Del = remove and relink; Reverse; orient new nodes along the path |
| **Zones & triggers** (J) | handles: 4 corners of box zones (node pairs: Secret, CameraSide/Lift, ObjectDontDropInBox, FallOffNoHurt …), radius knob for RADIUS parameters (CutSceneTrigger, guards, CameraFocusPoint) |
| Inspector "Object parameters" | named, typed fields (check boxes, choices, counts, seconds, distances) with doc/range/original-value tooltips, raw hex, inline validation, "Reset to defaults"; LevelDoor target picker with "Leads to: …"; waypoint row (role, node count, Create/Edit path, Unlink, Delete path, Link… to an existing chain, Look through). Undo-aware refresh |
| Inspector "Waypoint chain" | node i of n, owner and role, ◀ ▶, select object, delete node, look through |
| Viewport layers | *Path directions* (arrows, dashed wrap segment for patrol paths, node yaw ticks), *Zone areas & trigger radii* (boxes for all zone objects, fills for the selection), *Camera frusta* (CameraSide/Lift: computed game camera from height/distance/node yaw; cut-scene cameras: a frustum per node from node yaw/pitch) |
| Look through camera (Ctrl+Shift+L, View → Camera) | viewport takes the camera of the selected node / cut-scene camera / camera zone; while active, flying writes back node position + yaw/pitch, or for CameraSide the height/distance params and node yaw (one undo step per pilot session) |
| Problems (Level → Check, Build) | guards/alligators/spawners the game would remove (no start box / odd node count), alligators outside their boxes, spawners facing away from their first box, guards waiting for an event bit (and whether any object in the level sets it), a start camera inside the llama, chains not stored contiguously (error, Tools → Repair waypoint chains), param count, out-of-range values, missing required waypoints / odd box pairs, types whose originals always have a chain, LevelDoor without material-16 floor in its cell, impossible exit targets and (7,7,7) in TestLevel, far respawn nodes, several player starts |
| Copy / paste between levels | Edit → Copy objects (Ctrl+Shift+C) puts JSON (objects + chains + brushes, relative to their centre) on the system clipboard; Paste objects (Ctrl+Shift+V) at the view centre in any level – types are matched by name/variant in the target base (unknown types skipped with a message) |
| Prefabs | Tools → Save selection as prefab… / asset browser tab "Prefabs": `<data>/editor/prefabs/<name>.json` + thumbnail; double-click = place at view centre, drag = place under the cursor |
| Delete | `edit.delete` is re-registered: a selection of waypoints is removed with the chain relinked |
Engine fact used throughout: the MAP loader links waypoint record i to i+1 when "next" ≠ 0, so a
chain must be contiguous in the node list (`ChainOps` inserts at the right index and the check /
repair action guard it).
## Files
`ObjectsEditorPlugin` (registration, actions, waypoint inspector), `Schemas`/`ParamSchema`/
`TypeStats` (+ `type-stats.tsv`), `ParamsSection`, `ObjectChecks`, `Guards`, `EventBits`, `PlaceTool`, `Setups`,
`ExitTarget`, `CoinTool`, `PathTool`, `ChainOps`, `ZoneTool`, `ObjectOverlays`, `CameraView`
(zone camera + pilot), `ObjectPalette`, `ObjectClipboard`, `Prefabs`, `LinkDialog`, `Ground`
(floor finder on the collision polygons of tiles + brush preview tiles), `Session`, `Roles`, `Draw`
(all game↔render conversions in one place).
Shared changes: `editor.ui.view.SceneRenderer.brushOutput()` getter (brush preview tiles for the
floor finder); `editor.model.WaypointBoxes` + box setup in `Edits.placeStrat`;
`editor.model.cmd.PlayerStart` (also used by the shell's new-level templates); `editor.ui.TypeMenus`
(object type context menu, used by Assets → Objects and the Objects panel; the objects plugin adds
Edit type… / Delete type… for custom types through `editor.tools.strats.CustomTypes`).
## Verified
UI (Xvfb, `ObjectsTour`): `img/editor/obj_camera_zone.jpg`, `obj_cutscene_frusta.jpg`,
`obj_pilot.jpg`, `obj_exit_inspector.jpg`, `obj_place.jpg`, `obj_zone_handles.jpg`,
`obj_trigger_radius.jpg`, `obj_problems.jpg`, `obj_newlevel_path.jpg`, `obj_newlevel_camera.jpg`,
`obj_coin_arc.jpg`, `obj_newlevel_saved.jpg` (no problems), `obj_beetle_path.jpg`.
In game (apptest, display :7, new level on base t1l1m002 as TestLevel chapter 2; island of brushes,
coin line + coin arc, checkpoint, exit to Village chapter 2, CameraSide zone at the exit,
JumpingBeetleBlue with a 4-node patrol path, CityGuard with two boxes):
* level loads, coins of line and arc stand where placed (`obj_game_coins.jpg`);
* walking through the checkpoint shows "KONTROLLPUNKT"; falling into the sea respawns the llama at
the post (`obj_game_checkpoint.jpg`);
* entering the camera zone switches to the zone camera (high view, `obj_game_camera_zone.jpg`);
* stepping on the exit floor shows the tally "TestLevel Kapitel 2"; Enter → "Dorf Kapitel 2"
loads = the chosen chapter (`obj_game_exit.jpg`);
* the beetle jumps back and forth along its path (`obj_game_patrol.jpg`);
* (2026-11-07, display :25) a CityGuard placed with the Place tool (one box) appears and attacks
the llama (`obj_game_guard.jpg`); one with a square path beside it never appears (editor:
`obj_guard_removed.jpg`). Village 1 with the door tile swapped open: start on the pad 42.5/3.5/49.37
→ tally "Dorf Kapitel 1" at once (`obj_game_village_door.jpg`); at the door's own position
42.5/3.5/49.8 or in the original (door closed) → nothing.
* (2026-11-07, objs2, display :25, TestLevel on village 2 / village 1 / t1l4m002): old player
setup on village 2 = camera in the llama's head until it moves, new setup = camera behind it
from the start (`obj_game_startcam_v2.jpg`); a CityGuard added through the Add-object path
walks in its box; an Alligator added the same way appears and snaps (`obj_game_alligator.jpg`);
original village 1 with the start moved and turned (240°): the fly-by plays at the new start and
ends behind the llama (`obj_game_village1_moved_start.jpg`). Editor: type menu in Assets →
Objects / Objects panel (`obj_type_menu.jpg`, `obj_palette_type_menu.jpg`), event-bit info
(`obj_event_bit_info.jpg`, `obj_guard_event_inspector.jpg`).
## Known limits
* Parameters are decoded for ~35 common types (confidence per type in the schema doc); the rest
shows raw values with the statistics of the originals.
* Solved 2026-11-07: the CityGuard "not showing up" was removed by its own script – guards need
their start inside a waypoint box (node pairs), the tour had given it a 4-node square path
(editor-object-schemas.md "Guards"). The exit check "no material 16 in the door cell" in
originals came from the door floor being in the tile's swap model (RubyIdolDoor & co.); the
pad at 43,48 next to the village door is a house entrance. Tile cells follow the engine's
loader exactly (`Tile.cellX/cellZ` = ftol(x − 0.5), −ftol(z + 0.5)); `ObjectChecks` tests the
door cell like the engine (tiles listed in the cell, any height, swap models), same as
`GeometryChecks`. Tests: `GuardsTest`, `OriginalExitsTest`, `OriginalStartsTest`.
* Snap-to-floor (`Ground`) follows the engine's floor test (FUN_004036d0, used for strats too):
only tiles listed in the point's cell; then their swap models; a neighbour tile's polygon only
where the cell has no floor at all (`Hit.source`). `cellBelow` is the strict engine test (own cell,
death floors included) that the play-test `FloorFinder` and boot-to-level's crash check use (one
implementation since 2026-11-07). `GroundTest` compares the fallbacks with it on three originals. The Place tool drag moves only a player start's nodes within 2.5 cells.
* Zone heights are estimated (+1.2 above the lower node); the engine tests x/z only.
* Camera preview of CameraSide assumes the player in the box centre and default distance 2 /
height 1 when the parameters are 0; the game's FOV is approximated by 60°.
* Look-through of the village cut-scene camera's first node looks into a wall (its first node may
be a fly-in start); not compared with the game.
* Pasting into another base matches types by name; object models/code are not imported (that is
the job of `editor.tools.catalog`).