Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lat.md/lat.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ This directory defines the high-level concepts, business logic, and architecture
- [[wallet-token-balances]] — profile-scoped Base mainnet wallets with encrypted recovery phrases, and on-chain ERC-20 token balance reads via ethers v6.
- [[office-3d-traffic]] — the Office tab's backdrop traffic: car-following and junction-yielding simulation, per-model nose orientation, and instanced fleet rendering in a dozen draw calls.
- [[office-3d-interiors]] — enterable office/bank/showroom interiors: per-location conditional mounting (city unmounts while indoors), camera fly-in rig, interactable objects (ATM → wallet, desk → agent, car → spec card), and idle-agent walking trips between buildings.
- [[office-3d-walk-mode]] — GTA-style walk mode: glass roofs over the enterable buildings, the user's own third-person avatar (WASD + chase camera, shared crowd/collision), doorway-driven interior loading, and proximity Press-E interactions.
- [[office-interactions]] — space representatives: interactive bank tellers whose menu runs account status, balances, and account creation against the hermes-one backend for a chosen agent; the extensible pattern for future spaces (showroom sales, building space).
4 changes: 4 additions & 0 deletions lat.md/office-3d-interiors.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Enterable building interiors on the Office tab: click the office, bank, or car s

The feature spans the screen shell ([[src/renderer/src/screens/Office/Office.tsx]] owns the location state and DOM overlays) and the scene ([[src/renderer/src/screens/Office/office3d/Office3D.tsx]] mounts layers per location).

The same location state is also driven by [[office-3d-walk-mode|walk mode]], where the user's avatar enters buildings by walking through their doorways instead of click + Enter; the buildings additionally wear [[office-3d-walk-mode#Glass roofs|glass roofs]] in the city view.

## Locations & conditional rendering

`OfficeLocation` ("city" | "office" | "bank" | "showroom") lives in `office3d/core/locations.ts` with per-location camera presets, orbit clamps, and shadow centres. Buildings never move — entering only flies the camera and changes what's mounted.
Expand Down Expand Up @@ -48,6 +50,8 @@ Each pedestrian tracks a `place` ("outside" | "bank" | "showroom") from its curr

People never pass through walls, furniture, or each other: a crowd registry separates overlapping people, and per-place static colliders (wall boxes with door gaps, furniture circles) push walkers out. Buildings are entered through doorways only.

Crowd separation is radial plus a tangential bias with fixed world handedness ([[src/renderer/src/screens/Office/office3d/core/collision.ts#applyCrowdSeparation]]): a purely radial push deadlocks two head-on walkers — separation shoves them apart, goal pull shoves them back, and the pair vibrates in place (the sidewalk-glitch bug). The tangent makes an approaching pair sidestep in opposite world directions, so both "pass on the right" and spiral past each other.

Everything lives in [[src/renderer/src/screens/Office/office3d/core/collision.ts]] and works in world coordinates; the office simulation converts its canvas positions at the boundary. Wall colliders mirror the visible geometry including every door gap — the office's south wall gained a real doorway (`OFFICE_DOOR_X` in cityPlan.ts, east of the HQ logo) that trips now walk through instead of phasing the wall. Seats (chairs, beanbags) are deliberately not colliders so agents can reach them, and desk boxes cover only the desk body away from the seat side.

Blocked walkers wall-follow: when the push-out cancels a step, the walker commits to a short slide along the blocking face's tangent — signed toward its goal, re-derived at corners — with goal steering suspended for the burst. (A per-frame perpendicular nudge is not enough: the goal pull re-pins the walker against the face each frame, walking in place forever — the original stuck-at-desk bug.) The static resolver exposes its push normal for this via `resolveStaticColliders`' `pushOut`.
Expand Down
8 changes: 8 additions & 0 deletions lat.md/office-3d-traffic.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ Each vehicle carries static config (model URL, tint, lane, cruise speed, precomp

Car-following: within a lane each vehicle finds the nearest vehicle ahead (wrapped over the traffic loop). Inside `SLOW_GAP` it matches the leader's speed; inside `MIN_GAP` it targets zero — so cars brake, queue behind a stopped leader, and pull away again once it moves. Speeds ease toward the target with separate acceleration/braking rates so stops look like braking rather than snapping.

Vehicles are sized against people: cars are 4.2 world units long, trucks 5.6 — about 2.5 person-heights ([[office-3d-interiors#People & staff|PERSON_WORLD_HEIGHT]] ≈ 1.65), matching real-world proportion. The showroom's display cars use a smaller 3.3/3.5 so they fit the room.

### Braking for people

Cars never drive through a person: anyone outdoors — pedestrians, trip agents, or the walk-mode player, all read from [[src/renderer/src/screens/Office/office3d/core/collision.ts#getCrowdBodies]] — who is in a vehicle's lane corridor ahead makes it creep (`PERSON_SLOW`), then hard-stop (`PERSON_STOP`).

The check is one unwrapped along-axis gap plus a cross-axis corridor test per person, done in the target-speed pass; a person standing on the road holds the queue indefinitely, GTA-style. The sim also publishes every vehicle's live position as a push-out circle (`TRAFFIC_OBSTACLES` in Traffic.tsx) that [[office-3d-walk-mode|walk mode]]'s player resolves against, so walking into a stopped car shoves you off its body instead of clipping through; the list is emptied on unmount so no ghost cars exist indoors.

### Junction yielding

Every E-W/N-S road crossing is a "junction box" (crossing road width plus clearance). A vehicle approaching a box stops before it while cross-axis traffic occupies it, and proceeds once clear.
Expand Down
53 changes: 53 additions & 0 deletions lat.md/office-3d-walk-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Office 3D Walk Mode

GTA-style walk mode on the Office tab: a "Walk around" toggle spawns the user's own avatar outside HQ, WASD/arrows walk it through the city, doorways load interiors, and nearby objects show a Press-E prompt. Esc returns to the sky/orbit view.

Walk mode is a toggle, not a replacement — the classic orbit camera, click-to-focus buildings and the Enter button all still exist when it's off. The two schemes share one location state, so everything the [[office-3d-interiors|interior system]] mounts per location is identical in both.

## Glass roofs

The three enterable buildings wear glass roofs so the sky view reads as a finished city block while interiors (and their agents) stay visible through the panes — a terrarium of agents.

[[src/renderer/src/screens/Office/office3d/objects/Roofs.tsx#GlassRoof]] is one transparent pane plus a metal frame (border beams + mullion grid), mounted per building: the office roof sits beside `Room` in Office3D, the bank and showroom take a `roof` prop on [[src/renderer/src/screens/Office/office3d/objects/Bank.tsx#BankSection]] and [[src/renderer/src/screens/Office/office3d/objects/CarShowroom.tsx#CarShowroom]].

Every wall touches its roof: the office's perimeter walls are all 3.6 (matching the north wall) with the roof flush at 3.62 — the doorway keeps a human-scale 2.2 opening with solid wall above it — and the bank/showroom roofs sit on their own wall tops. A floating gap band here is exactly what street-level walking exposes.

## Street-level scale

The world is proportioned for a walking 1.65-unit person, not just the sky camera: cars ~2.5 person-heights long, 7-unit roads, and backdrop buildings stretched vertically (`BUILDING_Y_STRETCH` in [[src/renderer/src/screens/Office/office3d/objects/CityBackdrop.tsx]]) so a ground floor tops the player.

Footprint normalisation squashes the backdrop GLBs' storeys to person height; stretching height-only restores floor proportions without widening footprints beyond their 5-unit grid cells (which would need a full grid re-lay and invalidate the curated `BACKDROP_OVERRIDES`). Road centres moved one unit outward with the widening so the sidewalk strip (z≈17.2) in front of each lot stays off the asphalt.

Mount rule: always in the city view; kept indoors in walk mode (looking up shows the skylight grid); dropped in orbit-interior mode so the top-down camera stays unobstructed. Glass casts no shadow and writes no depth, so it never occludes the CEO glass walls or the storefront beneath it.

## Player avatar & controller

[[src/renderer/src/screens/Office/office3d/objects/Player.tsx#PlayerLayer]] spawns a gold-tinted man.glb rig with a "You" nameplate at `PLAYER_SPAWN` (outside the HQ south doorway) and runs the whole controller in one `useFrame`.

Input is window-level keydown/keyup by `KeyboardEvent.code` (WASD + arrows, Shift runs), ignoring editable targets and cleared on window blur. Movement is camera-relative: the ground-projected camera forward defines "W", so steering always matches what's on screen. The rig blends idle/walk/run clips by weight, falling back to a faster walk timeScale if the model has no run clip.

The player is a first-class crowd citizen: it registers a crowd body (`setCrowdBody("player", …)`), gets separated from agents/pedestrians, and resolves against the same per-place static colliders via [[src/renderer/src/screens/Office/office3d/core/collision.ts#collidersForPlace]] — so walls, desks and door gaps behave exactly as they do for agents.

Outdoors, cars are solid too: the player pushes out of the live vehicle circles the [[office-3d-traffic#Driving simulation#Braking for people|traffic sim]] publishes (`TRAFFIC_OBSTACLES`), and traffic in turn brakes for the player like for any other person on a road.

Office.tsx exits walk mode automatically when the tab loses visibility, so the window-level key listeners never capture typing elsewhere in the app.

## Chase camera

The third-person camera is the scene's OrbitControls with its target glued to the avatar: each frame the player shifts the camera by the target's delta and re-centres the target at chest height, so mouse orbit and wheel zoom keep working for free.

Walk mode reconfigures the controls (pan off, zoom-to-cursor off, distance clamped to 2.2–11) and skips the location target clamp. `CameraRig` in [[src/renderer/src/screens/Office/office3d/Office3D.tsx]] gains walk-aware rules: engaging walk mode flies down behind the spawn point, doorway transitions mid-walk fly nothing (the camera is already at the player), and disengaging flies back to the location preset.

## Door-triggered interiors

Walking through a doorway swaps the mounted location: the player's `place` is derived each frame from building footprints, and a change is reported to Office.tsx, which maps it onto the `location` state the Enter button sets.

Because collision only admits people through real door gaps, crossing a footprint boundary is only possible at a doorway — the interior mounts exactly as you step inside, and the city returns as you step out. In walk mode the click-to-focus/Enter/Exit-to-city UI is hidden and building clicks don't focus.

## Proximity Press-E interactions

Walking near an ATM, bank teller, showroom car or agent desk shows a bottom-centre `[E]` chip; pressing E fires the same action the click-Interactable fires in orbit mode (wallet section, teller menu, car card, agent sidebar).

Points live in [[src/renderer/src/screens/Office/office3d/interactions/proximity.ts#buildPlayerInteractions]], mirroring the collider constants (ATMs, tellers) and exported car/desk positions; [[src/renderer/src/screens/Office/office3d/interactions/proximity.ts#nearestInteraction]] picks the closest in-range point in the player's current place each frame, reported to the shell only on change. Desk radii (2.3) deliberately overlap along a desk row — columns repeat every 3.78 world units, and nearest-wins keeps the prompt unambiguous. The E handler in Office.tsx dispatches on the point's `kind` and ignores editable targets, so typing in modals never triggers it.

Pressing E on an ATM or teller opens the rep interaction modal, which owns Escape while it's up: Office's walk-mode/interior Escape handler stays detached whenever a rep panel is open (gated on `activeRepId`), so one Escape dismisses only the modal instead of also dropping out of walk mode. Closing the modal re-attaches the handler, so the next Escape exits walk mode as usual.
Loading
Loading