acdream/docs/research/2026-07-11-combat-target-camera-pseudocode.md
Erik 5276a83087 fix(combat): match retail live attack feedback
Keep AttackDone control statuses out of chat, preserve dead motion across zero-velocity position updates, render the authored power meter from right to left, and retain the rotatable viewer offset during target tracking.

Co-Authored-By: Codex <codex@openai.com>
2026-07-11 21:10:01 +02:00

84 lines
2.9 KiB
Markdown

# Retail combat target camera pseudocode
Source: Sept 2013 EoR named retail client.
- `ClientCombatSystem::UpdateTargetTracking` (`0x0056A950`)
- `ClientCombatSystem::TrackTarget` (`0x0056AED0`)
- `CameraSet::TrackTarget` (`0x00458280`)
- `CameraSet::SetTargetForOffset` (`0x004576E0`)
- `CameraManager::SetTargetObject` (`0x00456380`)
- `CameraManager::UpdateCamera` (`0x00456660`)
## Option consumer
```text
UpdateTargetTracking():
if ViewCombatTarget is enabled
and combat mode is Melee or Missile
and current attack target is valid:
camera.TrackTarget(attackTarget)
else:
camera.TrackTarget(0)
```
## Camera state
```text
CameraSet.TrackTarget(targetId):
if targetId != 0:
targeting = true
camera.SetTargetObject(targetId, wholeObject)
camera.targetOffset = (0, 0, 0.5 metres)
else:
targeting = false
SetTargetForOffset(camera.viewerOffset)
SetTargetForOffset(viewerOffset), while targeting:
camera.targetStatus = LOOK_AT_OBJECT | LOOK_AT_PIVOT
CameraSet.Rotate(angle), including while targeting:
viewerOffset.xy = rotate(viewerOffset.xy, angle)
SetTargetForOffset(viewerOffset)
```
## Per-frame pose
```text
pivot = player part position + transformed pivotOffset
direction = zero
if targetStatus contains LOOK_AT_OBJECT and target object exists:
targetPoint = target.position localToGlobal targetOffset
direction += normalize(targetPoint - pivot)
headingFrame = frameWhoseForwardIs(direction)
desiredEye = pivot + headingFrame.transformVector(viewerOffset)
if targetStatus contains LOOK_AT_PIVOT:
desiredForward = normalize(pivot - desiredEye)
damp current viewer toward (desiredEye, desiredForward)
```
The target therefore changes the direction of the boom around the player; it
does not replace the player as the boom's pivot. `LOOK_AT_PIVOT` keeps the
player centered while the target-facing boom keeps the combat target in view.
The viewer offset remains independently rotatable: manual camera orbit rotates
that offset inside the target-facing frame rather than being discarded or
snapped back to zero.
## Port mapping
- `GameWindow.GetCombatCameraTargetPoint` applies the option/mode/valid-target
gates and transforms the target-local 0.5 metre Z offset.
- `RetailChaseCamera.ComputeTrackedHeading` ports the pivot-to-target direction.
- `RetailChaseCamera.ComputeDesiredPose` ports the heading-frame transform and
preserves the viewer-offset yaw while tracking.
- The diagnostic legacy `ChaseCamera` remains intentionally non-retail under
existing divergence row TS-19; the production retail camera owns this behavior.
The ACE and ACME reference trees are not present in this worktree (only the
in-tree WorldBuilder reference is available), so no interpretation-aid cross
check was possible. The named retail functions above are complete for this
behavior and remain the ground truth.