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

2.9 KiB

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

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

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

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.