guards need their position inside a pair of waypoint boxes (else the game removes them): placing a guard gives it a box and event bit 0, check and inspector state with box around it button; exit and object checks use the engine's cell rule (tiles listed in the cell, swap models), tile cell from the map loader formula
This commit is contained in:
1 parent
a6708e5fff
commit
d9f1b8c2fe
15 files changed
+424
-40
No files matched your search
@@ -76,10 +76,31 @@ values only.
|
||||
| ObjectDontDropInBox, FallOffNoHurt | box mode (+ game state) | box |
|
||||
| WaterReboundPoints | active | rebound points |
|
||||
| RubyIdolDoor | p1 idols needed, p4 sign offset | – |
|
||||
| CityGuard | p0 view range², p1 chase range², p2 event bit, p3 wall collision | patrol (wraps) |
|
||||
| CityGuard family (high) | p0 view range², p1 chase range² (view capped to it), p2 event bit (0 = active at once), p3 wall collision (CityGuard only) | **boxes** (node pairs), must start inside one – see below |
|
||||
| JumpingBeetleBlue, Spider, Bunny | times in seconds (×30), ranges² | patrol path |
|
||||
|
||||
### Guards (CityGuard, CityGuardWithRubyIdol, CowGuard(NoCoin), GorillaGuard, LizardGuard(NoCoin), WarthogGuard)
|
||||
|
||||
One compiled template (same code in t1l1m002 unit 20 and the others). Start of the script:
|
||||
1. `if (p2 > 0) { hide; wait until g95 & (1 << (p2−1)); unhide } else wait until playerPtr ≠ 0` –
|
||||
p2 is an **event bit**: bits of global 95 are set by talking characters (TipoVillage,
|
||||
ChacaVillage), smash statues, LabBigCutscene, LabSlidingDoorPad … with the same number. All
|
||||
village guards have p2 = 0; 4 lab guards wait for an event.
|
||||
2. Waypoint scan: `wpFirst`, then pairs (`wpNext` twice): odd count → debug "Uneven number of
|
||||
WPs" + **remove**; the start box = first pair whose x/z box (`inWaypointBoxXZ`, op 0x178,
|
||||
inclusive, fixed point) contains the guard and whose first node has field +0x28 (`wpParam`,
|
||||
editor `unk2`) = 0; none → "Guard not in WP box" + **remove**. So the waypoints are **walking
|
||||
areas**, not a patrol path: the guard wanders (random turns) inside the current box; when it
|
||||
leaves it, `waypointStep(2)` looks for another box containing it (boxes with `wpParam ≠ 0` only
|
||||
from another such box), else it turns back (`clampToWaypointBox`). Chasing stays clamped to
|
||||
the boxes; it gives up beyond p1 or when the player leaves.
|
||||
All 41 original guards satisfy this (unit test `GuardsTest`). The editor: placing a guard gives
|
||||
it one 4×4 box and p2 = 0; `ObjectChecks` reports an error when the game would remove it and an
|
||||
info for p2 > 0; the Inspector shows the state plus a **Box around it** button (`Guards`,
|
||||
`Setups.guardBox`). Verified in game: CityGuard with its box on a new level (base village 2)
|
||||
appears and attacks the llama; a second one with a 4-node "square path" next to it never appears.
|
||||
|
||||
The rest (~150 placed types) shows raw values with the original statistics. Next candidates:
|
||||
PressurePadDoor (bit field), TargetRopePlatform, CataSwingingBlade (speeds/angles), RiverLog.
|
||||
Alligator (also box pairs: "Forcing alligator inside first WP box"), PressurePadDoor (bit field), TargetRopePlatform, CataSwingingBlade (speeds/angles), RiverLog.
|
||||
Tooling used (not in the repo): Ghidra script decompiling the handler table, a Python sweep
|
||||
disassembler with an op-name table, "parameter use" context dumps per type.
|
||||
@@ -12,7 +12,7 @@ screenshot tour `ObjectsTour` (test classes).
|
||||
|---|---|
|
||||
| **Objects panel** (left dock) | every placeable type of the base with rendered thumbnail, category filter, search (name + schema summary), favourites (right-click, ★ 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 its chain (camera node 8 cells behind). **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) |
|
||||
| Setups | **Player start** moves the existing player (exactly one) or places a Llama with its chain (camera node 8 cells behind). **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 (any placement) get one 4×4 box and event bit 0 |
|
||||
| **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) |
|
||||
@@ -20,7 +20,7 @@ screenshot tour `ObjectsTour` (test classes).
|
||||
| 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) | 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 |
|
||||
| Problems (Level → Check, Build) | guards the game would remove (no start box / odd node count) and guards waiting for an event bit, 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 |
|
||||
@@ -50,7 +50,7 @@ UI (Xvfb, `ObjectsTour`): `img/editor/obj_camera_zone.jpg`, `obj_cutscene_frusta
|
||||
|
||||
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 a square path):
|
||||
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
|
||||
@@ -58,18 +58,27 @@ JumpingBeetleBlue with a 4-node patrol path, CityGuard with a square path):
|
||||
* 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`).
|
||||
* 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.
|
||||
|
||||
## 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.
|
||||
* The CityGuard (village-2 base) placed in the open did not show up in game – it probably waits for
|
||||
a scene/event; the patrol test was done with JumpingBeetleBlue.
|
||||
* In 8 original levels the exit check reports "no material 16 in the door cell": the exit floor
|
||||
polygons decode one cell off the door (e.g. village door cell 42,49, floor polygon at 43,48) –
|
||||
original tile collision positions vs. strat positions are not fully understood yet; brush-built
|
||||
exits are correct (verified in game). Snap-to-floor on original levels uses the same polygons.
|
||||
* 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`) is geometric: it finds polygons that reach into a cell from a
|
||||
neighbouring tile, which the engine ignores (`FloorFinder` follows the engine for the player).
|
||||
* 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°.
|
||||
|
||||
+3
-1
@@ -114,7 +114,9 @@ Order in the file:
|
||||
|
||||
1. `u32 N (tiles), u32 W, u32 H` (grid)
|
||||
2. **A**: N × `{f32 x, f32 y, f32 z, u32 rot, u32 model, u32 animFlag}` (export space; x,z = cell centre .5).
|
||||
Only used to sort into the cell lists: cell = floor(x) + floor(−z)·W.
|
||||
Only used to sort into the cell lists: cell = ftol(x − 0.5) − ftol(z + 0.5)·W (ftol truncates; 0x42ad4a,
|
||||
constant 0x56e158 = 0.5) = floor(x) + floor(−z)·W for cell-centred tiles (all originals). The player/strat
|
||||
cell of the collision tests is x>>12, z>>12 (FUN_004036d0).
|
||||
3. u32 n + n×u32 (list, referenced by strats)
|
||||
4. u32 n + n×90 bytes (lights/cameras? – objects.md)
|
||||
5. u32 n + n×34 bytes (path nodes)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 155 KiB |
Reference in new issue
Block a user