acdream/docs/research/2026-07-11-combat-live-gate-corrections-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.6 KiB

Combat live-gate corrections pseudocode

Sources: Sept 2013 EoR named retail client and ACE server source.

  • ClientCombatSystem::HandleAttackDoneEvent (0x0056C500)
  • gmCombatUI::RecvNotice_SetPowerbarLevel (0x004CC0E0)
  • CPhysicsObj::MoveOrTeleport (0x00516330)
  • CameraSet::Rotate (0x00458310)
  • ACE Player.OnAttackDone, Player_Melee.cs
  • ACE WeenieError.YouChargedTooFar, Player_Move.cs

Attack completion versus visible errors

HandleAttackDoneEvent(sequence, error):
    consume error as combat-controller state
    decide whether repeat may continue or the attack aborts
    do not write an error line to chat

ACE OnAttackDone(final sequence):
    send AttackDone(ActionCancelled) to reset the client power meter
    if the player must see a failure:
        send a separate WeenieError event

Therefore AttackDone(ActionCancelled) is silent. A separate YouChargedTooFar (0x003D) remains visible and is formatted by the ordinary WeenieError path. ACE emits it when the charge exceeds 15 metres; target selection itself is not range-limited by retail.

Power meter direction

RecvNotice_SetPowerbarLevel(level):
    find nested meter 0x10000050
    set meter scalar attribute 0x69 to level

authored meter direction = 1
visible fill = rightmost (width * clamp(level)) pixels

The red meter grows from the Power label at the right toward Speed at the left. The green desired-power thumb remains an independent scrollbar child. Clipping the rightmost portion must retain the full tiled image's UV offset.

Persistent death motion

on UpdatePosition(position, velocity):
    update physics pose and velocity
    do not derive an interpreted motion command from velocity

on UpdateMotion(Dead):
    apply the persistent Dead motion cycle through the motion interpreter

A corpse legitimately sends zero velocity. Turning that velocity into Ready overrides the server-authored Dead cycle and makes the creature stand again. Retail keeps position/velocity transport separate from interpreted motion.

Keep in View orbit

See 2026-07-11-combat-target-camera-pseudocode.md. The corrective detail is:

targetHeadingFrame = frame looking from player pivot toward target point
viewerOffset.xy = the independently rotatable camera boom offset
eye = pivot + targetHeadingFrame.transform(viewerOffset)
forward = normalize(pivot - eye)  // LOOK_AT_PIVOT

Tracking changes the frame that transforms viewerOffset; it does not reset or discard the offset. Manual camera orbit therefore remains usable while Keep in View is active.