From 7ec72765d760908f2a030bf326de3191052541ec Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 3 Sep 2026 05:32:18 +0200 Subject: [PATCH] =?UTF-8?q?checkpoint(render):=20G2=20owner=20findings=20?= =?UTF-8?q?=E2=80=94=20terrain=20punch=20assigned=20to=20S4,=20portal-haze?= =?UTF-8?q?=20probe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner G2 check at 4b0b29e4: Facility Hub PASS, Holtburg house PASS, cathedral leak unchanged (S3/S4). Two new findings: - doorway-sized fragments of houses on the terrace below show through the hill in Holtburg: PortalDepthMaskRenderer draws every visited cell's exit-portal fan with depth compare Always + write, so a doorway behind terrain still punches far depth through it. Pre-existing; S4 owns the punch (ledger row updated). - the purple UnHide haze (script type 0x75, played on the retail Hidden->visible physics-state edge) no longer shows on the local player after a portal. Under investigation: temporary print-only traces [pes-spawn]/[pes-vis] under ACDREAM_DUMP_PLAYSCRIPT=1 (launch-options row amended; both die with the investigation) and a timed arrival route tools/overhaul-selfgate/route-portal-haze.txt. Co-Authored-By: Claude Fable 5.1 --- docs/launch-options.md | 2 +- ...-09-01-campaign-overhaul-world-solidity.md | 2 +- .../ContentEffectsAudioComposition.cs | 4 +++ src/AcDream.Core/Vfx/ParticleHookSink.cs | 21 +++++++++--- src/AcDream.Core/Vfx/ParticleSystem.cs | 14 ++++++++ tools/overhaul-selfgate/route-portal-haze.txt | 33 +++++++++++++++++++ 6 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 tools/overhaul-selfgate/route-portal-haze.txt diff --git a/docs/launch-options.md b/docs/launch-options.md index c8cbaa73..1b1445fc 100644 --- a/docs/launch-options.md +++ b/docs/launch-options.md @@ -246,7 +246,7 @@ $env:ACDREAM_FRAME_HISTORY = "$scratch\frames.csv" |---|---|---|---|---|---| | `ACDREAM_CAPTURE_PLAYER_QUANTA` | `=` (any non-whitespace path) | Opt-in JSON-Lines trace of every admitted player physics quantum (position/orientation/velocity/contact-plane snapshots at each stage boundary of `CPhysicsObj::UpdateObjectInternal`) | Appends+flushes one JSON line per physics quantum to the file (real file I/O on the physics tick when enabled); disabled path costs one static string null/empty check, no allocation. Read once into a mutable static property (settable via `ResetForTest`) rather than a typed options object. | unset (disabled, zero-alloc) | `PlayerPhysicsQuantumCapture` static class (`AcDream.Runtime/Gameplay/PlayerPhysicsQuantumCapture.cs:22`) | | `ACDREAM_DUMP_MOTION` | `=1` | prints `UM`/`[UM_STALE]`/`[MOTIONDONE]`/`VU.land`/raw-hex wire dump lines tracing inbound `UpdateMotion` handling, remote ground-contact edges, and motion-done callbacks (bug-a/#32 stuck-cast subthread is temporary; core trace is long-lived) | print-only, but the raw-site reads in `LiveEntityNetworkUpdateController.cs` and `UpdateMotion.cs` fire on EVERY inbound motion/UM event (not cached) — `Environment.GetEnvironmentVariable` call per packet even when off; `UpdateMotion.cs`'s branch additionally builds a `StringBuilder` hex dump when on. Rule-5 violation (raw reads outside a diagnostics-owner class) at 5+ call sites | off | THREE independent readers: `PhysicsDiagnostics.DumpMotionEnabled` (owner, appears unconsumed — see Notes), `AnimationPresentationDiagnostics.FromEnvironment()` (App owner record, cached once at startup, consumed by `LiveEntityAnimationPresenter`), and raw `Environment.GetEnvironmentVariable` reads scattered across `LiveEntityNetworkUpdateController.cs` (4 sites) + `Core.Net/Messages/UpdateMotion.cs:163` + `Runtime/Physics/RuntimeRemotePhysicsUpdater.cs:630` | -| `ACDREAM_DUMP_PLAYSCRIPT` | `="1"` (ordinal) | Traces PhysicsScript playback: missing/empty script resolution, malformed `StartTime` entries, and other `[pes]`-prefixed hook-dispatch events | print-only (`Console.WriteLine`) at all 4 use sites (`:85-86,136,300,328`) | unset (off) | `PhysicsScriptRunner.DiagEnabled` (`PhysicsScriptRunner.cs:61-62`) — per-instance settable property seeded from the env var, not a shared static diagnostics-owner class | +| `ACDREAM_DUMP_PLAYSCRIPT` | `="1"` (ordinal) | Traces PhysicsScript playback: missing/empty script resolution, malformed `StartTime` entries, and other `[pes]`-prefixed hook-dispatch events; TEMPORARY (2026-09-03 portal-haze investigation, delete with it): `[pes-spawn]` per emitter spawn (owner, handle, owner cell, hidden-presentation state) from `ParticleHookSink` and `[pes-vis]` per renderable-index flip (view eligibility, presentation visibility, owner cell) from `ParticleSystem`, both through the vfx diagnostic sink | print-only (`Console.WriteLine`) at all 4 runner use sites (`:85-86,136,300,328`) plus the two temporary vfx traces | unset (off) | `PhysicsScriptRunner.DiagEnabled` (`PhysicsScriptRunner.cs:61-62`) — per-instance settable property seeded from the env var, not a shared static diagnostics-owner class; the two temporary traces read the same env var once into a private static (`ParticleSystem.DiagEnabled`, `ParticleHookSink.SpawnTraceEnabled`) | | `ACDREAM_DUMP_SURFACES` | `="1"` (ordinal) | One-shot (per session) surface-format histogram dump for the atlas-opportunity audit — fires once after `_dumpFrameCounter>=600` OnRender ticks AND `_uploadMetadata.Count>=100` uploaded textures; writes to the host diagnostics directory | Doc comment claims "Zero cost when off" but `_uploadMetadata[name]=(w,h,fmt)` (`TextureCache.cs:1042`) is written **unconditionally on every texture upload regardless of the flag** — real (small) always-on dictionary-write cost. `TickSurfaceHistogramDumpIfEnabled` also re-reads `Environment.GetEnvironmentVariable` every OnRender frame (not cached) until the one-shot fires. Dump-write failures are caught and logged to stderr, not fatal. | unset (off) | `TextureCache` (`TextureCache.cs:102-113` fields, gate at `TextureCache.cs:802-812`, dump at `TextureCache.cs:814-829`), Phase N.6 slice 1 | | `ACDREAM_FRAME_PROF` | `=1` | master toggle for the frame profiler: CPU frame time, GPU time samples, per-stage CPU attribution, per-frame alloc/GC, `[frame-prof]` report every ~5 s (doc: "permanent apparatus ... do not strip with session probes") | when on, samples `GC.GetAllocatedBytesForCurrentThread()` and stage-scope timing every frame (cheap, by design); its own XML doc claims a GPU-query self-disable tied to `ACDREAM_WB_DIAG=1` that `FrameProfiler.cs` says no longer exists — see Notes #1; startup-only in practice (its DebugPanel/DebugVM mirror is unreachable — #434)| false (off) | `RenderingDiagnostics.FrameProfEnabled` / `FrameProfiler` | | `ACDREAM_PROBE_ENVCELL` | `=1` | emits one `[envcells]` line per indoor frame: `CellsRendered`/`TrianglesDrawn` + ourBldgs/otherBldgs/filter counts (phase a8 relic; its own render pass was removed but the probe was kept) | print-only; implicitly turned on whenever `ACDREAM_PROBE_VIS` is on (getter is `_probeEnvCellEnabled \ | \ | `RenderingDiagnostics.ProbeEnvCellEnabled` (backing field OR'd with `ProbeVisibilityEnabled`) | diff --git a/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md b/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md index a9fdc552..84d4f3e7 100644 --- a/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md +++ b/docs/plans/2026-09-01-campaign-overhaul-world-solidity.md @@ -659,7 +659,7 @@ Update immediately when a slice changes state. Chat is not the ledger. | v1 OH1 | SUPERSEDED | research contracts committed at `5cd4fd2c`/`5d907ae9` (kept, binding) | T0–T3 evidence grammar parked on `quarantine/oh1-evidence-grammar-2026-09-02` | reviews found the grammar a false oracle three times | n/a | n/a | see §15 | | S1 Geometry | **CLOSED — G1 PASSED 2026-09-02** | `oh2-cellstruct-surface-contract.md` (§3.5 arbitrated on the binary) | chunk A `acf17246`; chunk B `b681717c`; hygiene `0840d5fb`; review fixes `e2543d0e`; G1 regression fix `8c6563ca` (segment/batch pairing) + its pin test (next commit) | lead verified both chunks against the named pseudo-C and the paired binary; two Opus lens reviews, 8 findings all verified by the lead and fixed; the review round's own upload-order change desynchronized cell-shell index segments from batches (first G1 launch: magenta/stretched/missing walls in every dungeon) and was root-caused and fixed by the lead before the re-gate | build 0/0; Content 213/213 (incl. PreparedPackage on the recipe-8 pak); Core 4,948/4,948 (one load-sensitive allocation test outside S1 passes alone); Bake 21/21; Launcher.Core 365/365; Runtime 1,884/1,884; App hermetic 6,757/6,757 | **G1 PASS**: Facility Hub stairs/doorways, Holtburg house interior, ordinary dungeon, town portal network all whole with correct materials on `8c6563ca` + recipe 8; cathedral shows the ORIGINAL symptom family (neighbor-cell walls drawn through portals over the room), explicitly outside G1 and carried to S3/S4 | dev pak recipe 8 (597,369,112 B; `acdream.recipe7.pak` kept); TOC delta explained; corpus scan old-only 3,197 (all untextured), new-only 0, unexplained 0 | | Capture | READY | §7 + `tools/walk-oracle/oh/README.md` | scripts committed `ebaa41df` + `14d8fe64`; lead resolved DBObj::m_DID=+0x28 and the cdecl alpha-function conventions; recon attach re-confirms | — | recon dt/x/uf dumps at session start | owner session ~1 h | before S3 | -| S2 World graph | CODE-COMPLETE — G2 PROVISIONAL PASS (self-gate 2026-09-03, owner double-check owed) | `s2-membership-ownership-map.md` (`e7ad25a7`) + Contracts A/B; closeout evidence §8/§9 | chunk 1a `059b8883`; chunk 1b `5a2792d6`; chunk 2 `707d2803` (render statics borrow the registry's retail array; render flood deleted); chunk 3 `afbd2410` (one flood per registration feeds collision rows and render entries; staged SetPosition carries both products); chunk 4 `75ea269d` (movement publishes from the transition's cells; children inherit at registration); chunk 5 + closeout `c94a1a40` (borrowed per-cell views, render-only registration, sweeps/fallbacks deleted; reflood forwards the part array; NON-COLLIDING DAT STATICS register render-only from both publishers; the dual-review fix batch: Suspend clears the retail product, attach/detach advance the mutation revision, an attached child never floods on its own, RemoveLandblock/RetireOwnerFromLandblock prune retail rows, render-only owners move with their destination cell (retail's zero-sphere `find_cell_list` 0x0052b4e0, verified statically 2026-09-03 — AD-117 item 1 dropped), empty part array == null, per-move closures removed, EnvCell shells out of the entity-id index, index predicate compares the id, dead per-cell scene indices deleted) | lead corrected 1a's route decision before landing; chunk 2 verified by the lead; chunk 5's agent report missed the non-colliding-static population (found by the self-gate PIXEL DIFF, not by eye: Facility wall panels grey); S2 dual review (arch + retail lens, Opus) produced 12 + 7 findings, every one lead-verified against the source before an edit; the four BLOCKING/MAJOR lifetime gaps were real | at `c94a1a40`: Core 4,984/4,984; Content 214/214; Runtime 1,884/1,884; App hermetic lane 6,760/6,760; App InstalledDat 217 pass / 1 skip / 2 pre-existing #383; App Windows 1/1 | G2 PROVISIONAL PASS. Self-gate runs (kit `ea546f38`, pwsh): `logs/selfgate-20260902-231426-baseline-g1-binary` (reference), `…231642-s2-chunk3`, `…233845-s2-chunk4` (identical), `logs/selfgate-20260903-002632-s2-chunk5-reflood` (FAIL: Facility wall panels grey, left-wall mean RGB 29,26,23 vs baseline 65,17,21 — missing non-colliding statics), `logs/selfgate-20260903-010022-s2-review-fixes-3` (candidate: panels back at 79,20,25 = chunk-3/4; cathedral 13.6% / Facility 15.0% / house 13.4% px differ vs baseline, the chunk-3/4 band; graceful logout, zero exceptions). Membership-probe run `logs/selfgate-20260903-003013-s2-chunk5-membership-probe`: `[walk-membership]` fired only on two transient Holtburg login frames, never in Facility Hub or the cathedral. Connected R6 soak on the chunk-4 build `6717a3e5` (`logs/connected-r6-soak-20260902-234016.report.json`): 9 destinations in 511 s, zero invariant failures, graceful exit — route PASS (its `-CollisionShadowEvery` referee is the obsolete I5 graph-vs-flat comparator; retire in S5). Connected R6 soak on the FINAL S2 build (`logs/connected-r6-soak-20260903-010403.report.json`, binary matches source, exit 0, graceful=True): 9 canonical checkpoints, 0 failures, 10 warnings (server-side population drift + DAT VFX table messages), 522 s — route PASS. Morning handoff: `docs/research/2026-09-01-overhaul/2026-09-03-g2-morning-handoff.md` | owner double-checks G2 (handoff checklist); then the owner capture session (the S3 packet §7 names the two traces); then S3 | +| S2 World graph | CODE-COMPLETE — G2 OPEN: owner check 2026-09-03 passed 2 of 3 poses, cathedral leak unchanged (S3/S4), two new owner findings under investigation | `s2-membership-ownership-map.md` (`e7ad25a7`) + Contracts A/B; closeout evidence §8/§9 | chunk 1a `059b8883`; chunk 1b `5a2792d6`; chunk 2 `707d2803` (render statics borrow the registry's retail array; render flood deleted); chunk 3 `afbd2410` (one flood per registration feeds collision rows and render entries; staged SetPosition carries both products); chunk 4 `75ea269d` (movement publishes from the transition's cells; children inherit at registration); chunk 5 + closeout `c94a1a40` (borrowed per-cell views, render-only registration, sweeps/fallbacks deleted; reflood forwards the part array; NON-COLLIDING DAT STATICS register render-only from both publishers; the dual-review fix batch: Suspend clears the retail product, attach/detach advance the mutation revision, an attached child never floods on its own, RemoveLandblock/RetireOwnerFromLandblock prune retail rows, render-only owners move with their destination cell (retail's zero-sphere `find_cell_list` 0x0052b4e0, verified statically 2026-09-03 — AD-117 item 1 dropped), empty part array == null, per-move closures removed, EnvCell shells out of the entity-id index, index predicate compares the id, dead per-cell scene indices deleted) | lead corrected 1a's route decision before landing; chunk 2 verified by the lead; chunk 5's agent report missed the non-colliding-static population (found by the self-gate PIXEL DIFF, not by eye: Facility wall panels grey); S2 dual review (arch + retail lens, Opus) produced 12 + 7 findings, every one lead-verified against the source before an edit; the four BLOCKING/MAJOR lifetime gaps were real | at `c94a1a40`: Core 4,984/4,984; Content 214/214; Runtime 1,884/1,884; App hermetic lane 6,760/6,760; App InstalledDat 217 pass / 1 skip / 2 pre-existing #383; App Windows 1/1 | G2 PROVISIONAL PASS. Self-gate runs (kit `ea546f38`, pwsh): `logs/selfgate-20260902-231426-baseline-g1-binary` (reference), `…231642-s2-chunk3`, `…233845-s2-chunk4` (identical), `logs/selfgate-20260903-002632-s2-chunk5-reflood` (FAIL: Facility wall panels grey, left-wall mean RGB 29,26,23 vs baseline 65,17,21 — missing non-colliding statics), `logs/selfgate-20260903-010022-s2-review-fixes-3` (candidate: panels back at 79,20,25 = chunk-3/4; cathedral 13.6% / Facility 15.0% / house 13.4% px differ vs baseline, the chunk-3/4 band; graceful logout, zero exceptions). Membership-probe run `logs/selfgate-20260903-003013-s2-chunk5-membership-probe`: `[walk-membership]` fired only on two transient Holtburg login frames, never in Facility Hub or the cathedral. Connected R6 soak on the chunk-4 build `6717a3e5` (`logs/connected-r6-soak-20260902-234016.report.json`): 9 destinations in 511 s, zero invariant failures, graceful exit — route PASS (its `-CollisionShadowEvery` referee is the obsolete I5 graph-vs-flat comparator; retire in S5). Connected R6 soak on the FINAL S2 build (`logs/connected-r6-soak-20260903-010403.report.json`, binary matches source, exit 0, graceful=True): 9 canonical checkpoints, 0 failures, 10 warnings (server-side population drift + DAT VFX table messages), 522 s — route PASS. OWNER G2 CHECK 2026-09-03 (client at `4b0b29e4`): Facility Hub PASS, Holtburg house PASS; cathedral: the original leak unchanged, NOT worse (S3/S4 target, G2 bar met); TWO NEW OWNER FINDINGS block G2 closure — (a) parts of buildings leak through terrain outdoors in Holtburg — owner screenshot shows doorway-sized fragments of the houses on the terrace BELOW showing through the hill; mechanism identified: `PortalDepthMaskRenderer` draws every visited cell's `OtherCellId == 0xFFFF` portal fan (`RetailPViewPassExecutor` `DrawDepthFan`) with depth compare `Always` + depth write, so a building doorway BEHIND terrain still punches far depth through the hill and the interior draws through the hole; retail's far punch cannot be `Always` against terrain — the exact z-func is S4's first question (the #117 punch stencil is already on S4's delete list; the retail-world contract §5.4 wording must be re-read at the capture). Pre-existing (not an S2 regression), assigned to S4; (b) the purple portal-exit haze on the character no longer appears on arrival (the character just appears) — under investigation, root cause + fix owed before G2 closes. Morning handoff: `docs/research/2026-09-01-overhaul/2026-09-03-g2-morning-handoff.md` | owner double-checks G2 (handoff checklist); then the owner capture session (the S3 packet §7 names the two traces); then S3 | | S3 Walk | PACKET DRAFTED 2026-09-03 — implementation BLOCKED on the §7 capture session | `s3-walk-ownership-map.md` (retail table, current owners, four chunks, review lenses, gate, the three exact captures to ask for) + built-mesh/world contracts | — | — | — | folded into G3 | owner capture session first (§7 of the packet names the three traces); chunk 2 may start on the decomp oracle alone | | S4 Depth + alpha | PLANNED | depth/alpha contracts + captures | — | — | — | G3 | retires AP-34 | | S5 Consumers + closeout | PLANNED | landscape contract; AP-232 | — | — | — | G4 | retires AP-117/AP-232; deletes probes | diff --git a/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs b/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs index ae4e4d58..ac568d0c 100644 --- a/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs +++ b/src/AcDream.App/Composition/ContentEffectsAudioComposition.cs @@ -470,6 +470,10 @@ internal sealed class ContentEffectsAudioCompositionPhase : Fault(ContentEffectsAudioCompositionPoint.EmitterRegistryPublished); ParticleSystem particles = _factory.CreateParticleSystem(emitters); + // TEMPORARY (2026-09-03 portal-haze investigation): the system's + // own trace is gated inside on ACDREAM_DUMP_PLAYSCRIPT=1. + particles.DiagnosticSink = message => + _dependencies.Error($"vfx: {message}"); _publication.PublishParticleSystem(particles); Fault(ContentEffectsAudioCompositionPoint.ParticleSystemPublished); diff --git a/src/AcDream.Core/Vfx/ParticleHookSink.cs b/src/AcDream.Core/Vfx/ParticleHookSink.cs index c6077ae9..7d928e85 100644 --- a/src/AcDream.Core/Vfx/ParticleHookSink.cs +++ b/src/AcDream.Core/Vfx/ParticleHookSink.cs @@ -63,6 +63,9 @@ public sealed class ParticleHookSink : IAnimationHookSink public Action? DiagnosticSink { get; set; } + private static readonly bool SpawnTraceEnabled = + Environment.GetEnvironmentVariable("ACDREAM_DUMP_PLAYSCRIPT") == "1"; + /// /// Diagnostic ownership counts used by lifecycle stress gates. These count /// the sink's retained bookkeeping, not only emitters still present in the @@ -431,11 +434,19 @@ public sealed class ParticleHookSink : IAnimationHookSink return; } _system.UpdateEmitterOwnerPosition(handle, ownerPosition); - _system.UpdateEmitterOwnerCell( - handle, - _cells is not null && _cells.TryGetCellId(ownerLocalId, out uint cellId) - ? cellId - : 0u); + uint ownerCellId = 0u; + bool haveOwnerCell = + _cells is not null && _cells.TryGetCellId(ownerLocalId, out ownerCellId); + _system.UpdateEmitterOwnerCell(handle, haveOwnerCell ? ownerCellId : 0u); + // TEMPORARY (2026-09-03 portal-haze investigation, dies with it): + // print-only, gated on ACDREAM_DUMP_PLAYSCRIPT=1 like the runner. + if (SpawnTraceEnabled) + { + DiagnosticSink?.Invoke( + $"[pes-spawn] emitter=0x{emitterInfoId:X8} owner=0x{ownerLocalId:X8} handle={handle} " + + $"cell=0x{(haveOwnerCell ? ownerCellId : 0u):X8} " + + $"hiddenPresentation={_hiddenPresentationOwners.Contains(ownerLocalId)} pass={renderPass}"); + } if (_hiddenPresentationOwners.Contains(ownerLocalId)) { _system.SetEmitterPresentationVisible(handle, false); diff --git a/src/AcDream.Core/Vfx/ParticleSystem.cs b/src/AcDream.Core/Vfx/ParticleSystem.cs index 818587e6..0ba88bcb 100644 --- a/src/AcDream.Core/Vfx/ParticleSystem.cs +++ b/src/AcDream.Core/Vfx/ParticleSystem.cs @@ -860,11 +860,25 @@ public sealed class ParticleSystem : IParticleSystem } } + /// TEMPORARY (2026-09-03 portal-haze investigation, dies with + /// it): print-only trace of renderable-index flips, wired under + /// ACDREAM_DUMP_PLAYSCRIPT=1 beside the hook sink's trace. + public Action? DiagnosticSink { get; set; } + + private static readonly bool DiagEnabled = + Environment.GetEnvironmentVariable("ACDREAM_DUMP_PLAYSCRIPT") == "1"; + private void RefreshRenderableIndex(ParticleEmitter emitter, bool wasRenderable) { bool isRenderable = IsRenderable(emitter); if (wasRenderable == isRenderable) return; + if (DiagEnabled) + DiagnosticSink?.Invoke( + $"[pes-vis] handle={emitter.Handle} owner=0x{emitter.AttachedObjectId:X8} " + + $"cell=0x{emitter.OwnerCellId:X8} renderable={isRenderable} " + + $"viewEligible={emitter.ViewEligible} presentationVisible={emitter.PresentationVisible} " + + $"pass={emitter.RenderPass}"); SortedSet index = _renderableHandlesByPass[RenderPassIndex(emitter.RenderPass)]; diff --git a/tools/overhaul-selfgate/route-portal-haze.txt b/tools/overhaul-selfgate/route-portal-haze.txt new file mode 100644 index 00000000..726ba409 --- /dev/null +++ b/tools/overhaul-selfgate/route-portal-haze.txt @@ -0,0 +1,33 @@ +# Portal-exit haze probe route (2026-09-03, owner finding at the G2 check). +# Teleport, then shoot at short intervals after the world reveals so the +# UnHide (0x75) script's emitters are in frame if they draw at all. + +wait world-visible 180000 +sleep 3000 +screenshot h00-before 15000 + +# Facility Hub pose (interior arrival) +command /teleloc 8A02015E 60.251431 -38.848198 -5.995000 0.995004 0.000000 0.000000 -0.099833 +wait world-visible 90000 +screenshot h01-arrive-0ms 15000 +sleep 400 +screenshot h02-arrive-400ms 15000 +sleep 600 +screenshot h03-arrive-1000ms 15000 +sleep 1000 +screenshot h04-arrive-2000ms 15000 +sleep 2000 +screenshot h05-arrive-4000ms 15000 + +# Holtburg house pose (a second arrival, outdoor landblock) +command /teleloc A9B4013F 134.555893 11.423566 94.005005 0.996917 0.000000 0.000000 -0.078459 +wait world-visible 90000 +screenshot h11-arrive-0ms 15000 +sleep 400 +screenshot h12-arrive-400ms 15000 +sleep 600 +screenshot h13-arrive-1000ms 15000 +sleep 1000 +screenshot h14-arrive-2000ms 15000 + +close-client