Commit Graph

6 Commits

Author SHA1 Message Date
foxtacles
e0a1ac781f
Add free camera controls (#10)
* Add free camera orbit controls to multiplayer third-person camera

Replace the fixed camera offset with dynamic orbit parameters (yaw,
pitch, distance) computed from spherical coordinates. Mouse wheel
controls zoom, right-click drag controls orbit, and two-finger
touch gestures support pinch-zoom and orbit on touchscreens.

The controls are always active when third-person mode is enabled.
A single SDL event forwarding hook is added to the main event loop;
all other changes are contained within the multiplayer extension.

https://claude.ai/code/session_013FyPCrJSaHxiJwdfGBVnYP

* Fix linker error and refactor orbit camera controls

Move HandleSDLEvent extension call from ISLE (no EXTENSIONS define) into
LegoInputManager::UpdateLastInputMethod in LEGO1 to fix unresolved
symbol errors. DRY up orbit camera code with ClampPitch/ClampDistance/
ResetOrbitState/ApplyOrbitCamera helpers. Simplify direction vector
computation. Fix mouse orbit yaw direction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-08 06:23:57 +01:00
foxtacles
a9747dec11
Character customization (#8)
* WIP: Add character customization to multiplayer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Refine character customization: fix message buffering, DRY up code, request-based model

- Register NetworkManager with TickleManager via HandleCreate hook in
  LegoOmni::Create(), so packets are processed continuously instead of
  buffering between Connect() and OnWorldEnabled()
- Spawn unspawned remote players in OnWorldEnabled() (created before
  world was ready)
- Switch to request-based customization: HandleROIClick sends
  MSG_CUSTOMIZE to server, server echoes to all peers, HandleCustomize
  applies state and plays effects
- DRY up SwitchVariant to delegate LOD cloning to ApplyHatVariant
- Add ApplyChange helper consolidating the switch-on-changeType pattern
- Fix InitFromActorInfo to derive dependent color parts from independent
  parts (matching Unpack rules)
- Remove dead code: m_hasBeenTicked, ApplyCustomizeChange on
  RemotePlayer, m_localCustomizeState on NetworkManager
- Add null ROI checks in HandleCustomize for unspawned players
- Move MSG_CUSTOMIZE constant to shared protocol.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix

* Fix character customization bugs from code review

- BUG-1: Add bounds check in SwitchColor to prevent OOB access from
  unvalidated network input (p_partIndex could exceed array bounds)
- BUG-2: Enforce allowRemoteCustomize on receiver side in HandleCustomize
  (was only checked on sender side, byppassable by malicious client)
- BUG-3: Document stale-state sound asymmetry for remote targets
- OBS-1: Remove unused bodyVariantIndex from CustomizeState (never
  modified, wasted 3 bits per state message)
- NAME-1: Fix p_ prefix convention on ApplyCustomizeChange parameters
2026-03-07 23:20:55 +01:00
foxtacles
ed4e248be4
Implement display actors (#6)
* Implement display actor override for multiplayer extension

Add displayActorIndex to the multiplayer protocol, allowing players to
choose any of the 66 character models from g_actorInfoInit via the
multiplayer:actor INI setting. The visual display is decoupled from the
gameplay actor ID while maintaining backward compatibility.

- Protocol: Add displayActorIndex field to PlayerStateMsg and validation helpers
- RemotePlayer: Use display actor name for cloning instead of actorId
- NetworkManager: Broadcast/handle displayActorIndex, respawn on display change
- ThirdPersonCamera: Create/manage display clone ROI for local player override
- INI: Read multiplayer:actor setting and resolve to g_actorInfoInit index

* Use array syntax for INI option access in display actor setup

Consistent with how relayUrl and room are read from options.

* Fix display actor ROI handling in 3rd person camera

- Fix direction flip targeting display clone instead of native ROI in
  Disable(), ReinitForCharacter(), and OnCamAnimEnd(). The native ROI is
  the source of truth for TransformPointOfView and Tick() sync.
- Fix use-after-free: DestroyDisplayClone() now nulls m_playerROI when
  it points to the destroyed clone, preventing dangling pointer access
  in BuildRideAnimation after a 3rd→1st→3rd person toggle on a vehicle.
- Recreate display clone in ReinitForCharacter() vehicle branch.
- Extract EnsureDisplayROI() helper to deduplicate clone setup pattern.
- Move IsValidDisplayActorIndex() to charactercloner.h, replacing magic
  number 66 with sizeOfArray(g_actorInfoInit).

* Remove display actors plan document
2026-03-07 18:02:53 +01:00
foxtacles
dcf3b66173
Fix 3rd person camera 180-degree flip after cam anim ends (#5)
* Fix 3rd person camera 180-degree flip after cam anim ends

When a cam anim (NPC interaction cutscene) ends, FUN_1004b6d0 places the
player actor at the camera's final position using the camera's direction,
which uses standard convention (z = visual forward). The 3rd person camera
relies on backward-z convention (z = visual backward, from TurnAround).
This mismatch caused the camera to face 180 degrees wrong permanently
after any cam anim.

Add a HandleCamAnimEnd extension hook in FUN_1004b6d0 that, when the 3rd
person camera is active, flips the ROI direction back to backward-z and
re-establishes the correct camera position.
2026-03-07 17:32:21 +01:00
foxtacles
c8c3b7276e
Fix 3rd/1st person camera switch direction bugs (#4)
- Fix broadcast direction: use IsActive() instead of IsROITurnedAround()
  so the negate in BroadcastLocalState only fires when movement inversion
  is active, not based on a default-true flag

- Fix vehicle ROI direction for 3rd-person camera: undo Enter()'s
  TurnAround on small vehicles so the backward-z convention is preserved.
  Vehicles are placed with ROI z opposite to visual forward, and Enter()'s
  TurnAround breaks this for 3rd-person rendering. Applied in both
  OnActorEnter (entering while 3rd-person enabled) and ReinitForCharacter
  (enabling 3rd-person while already on a vehicle)

- Fix vehicle direction on exit: apply extra TurnAround in OnActorExit
  when 3rd-person is active, since Exit()'s TurnAround assumes Enter()'s
  TurnAround is still in effect

- Add WrappedUpdateWorldData() after manual direction flips in Disable()
  and ReinitForCharacter() to keep bounding volumes consistent and prevent
  stale world data from causing momentary camera/direction glitches

- Remove unused IsROITurnedAround() method

- Fix data race between WASM exports and game thread
2026-03-07 05:49:39 +01:00
foxtacles
37b328a595
3rd person camera (#3)
* Add feasibility plan for reusing multiplayer animation system for third-person camera

Evaluates reusing the multiplayer extension's RemotePlayer animation system
(BuildROIMap, AssignROIIndices, ApplyAnimationTransformation) for the local
player to enable a third-person camera mode. Conclusion: feasible with only
3 single-line extension hooks added to core game code.

https://claude.ai/code/session_01NC3zdQZ4nqEcYjyvStqcdD

* WIP: Third-person camera with animation reuse and movement fix

* Fix third-person camera bugs: vehicles, remote facing, emote distortion (#2)

- Fix spawn pose and building re-entry by applying idle frame 0 and
  reinitializing on world enable
- Handle vehicle transitions: ride animations for small vehicles,
  first-person fallback for large vehicles and helicopter
- Keep vehicle dashboards visible for exit controls
- Disable third-person camera for large vehicles, fix ROI cleanup
- Move HandleActorExit hook to end of Exit() for immediate reinit
- Fix remote player facing 180 degrees wrong by negating direction
  in BroadcastLocalState when third-person camera is active
- Fix Hat Tip emote distortion from compounding transform scale by
  saving clean parent transform at emote start and restoring after
  each frame's animation application

* DRY cleanup for third-person camera branch

- Extract shared DetectVehicleType() to protocol.h/cpp (was duplicated
  in ThirdPersonCamera and NetworkManager)
- Remove no-op HandlePostApplyTransform hook chain (called every frame
  for every LegoPathActor but did nothing)
- Add ThirdPersonCamera::ClearAnimCaches() helper (pattern repeated 5x)
- Add AnimUtils::EnsureROIMapVisibility() inline helper (loop repeated
  5x across ThirdPersonCamera and RemotePlayer)
- Remove redundant static_cast in multiplayer.cpp (UserActor() already
  returns LegoPathActor*)
- Delete THIRD_PERSON_CAMERA_ANIMATION_REUSE_PLAN.md development artifact
2026-03-07 01:38:45 +01:00