Skip to content

Implement AssignDamageReq — manual combat damage distribution #197

@delebedev

Description

@delebedev

Problem

AssignDamageReq (GRE type 30) is never sent. Combat damage is auto-distributed by the engine. The player can't choose how to split damage across multiple blockers — trample, deathtouch+trample, and asymmetric block scenarios all resolve without player input.

This is a functional gap, not cosmetic — it changes game outcomes.

Pointers

  • Proto: AssignDamageReq, AssignDamageResp, AssignDamageConfirmation, DamageAssigner, DamageAssignment in messages.proto
  • Arena-notes: docs/combat-flow.md — full wire sequence including Phase 3 (assign damage), sub-message field tables, ResultCode enum
  • Arena-notes: docs/request-dispatch-table.md — AssignDamageRequest client class shape
  • Recordings: protocol examples exist (scan with just tape proto decode-recording for AssignDamageReq greType)
  • Existing combat code: MatchSession already handles DeclareAttackers/Blockers with two-phase pattern. AssignDamage is single-phase (Req → Resp → Confirmation)
  • Bridge: check how Forge currently resolves combat damage in WebPlayerController — that's where the engine blocks waiting for assignment

Wire sequence (from arena-notes combat-flow.md)

SERVER → CLIENT:  AssignDamageReq        (repeated DamageAssigner)
CLIENT → SERVER:  AssignDamageResp       (same DamageAssigner with assignedDamage filled)
SERVER → CLIENT:  AssignDamageConfirmation (ResultCode)

Single-phase — no iterative update loop like attackers/blockers.

Key proto shape

DamageAssigner: one per attacking creature that needs assignment. Contains totalDamage (power), canIgnoreBlockers (trample flag), and repeated DamageAssignment slots with minDamage/maxDamage bounds per recipient. Client fills assignedDamage on each slot and sends back.

Constraints

  • Auto-distribution should remain the default — only send AssignDamageReq when damage assignment is genuinely ambiguous (multiple blockers, trample)
  • Must work for both seats: MatchSession handles human player, FamiliarSession should auto-resolve (assign min to each, remainder to last/player)
  • Follows the existing combat handler pattern — wire it in MatchHandler dispatch alongside DeclareAttackers/Blockers

Open questions (for scoping agent to resolve)

  1. Auto-assign setting: Arena has an "auto-assign combat damage" checkbox. Is this purely client-side (client auto-fills AssignDamageResp) or does the server skip AssignDamageReq when the setting is active? Check SettingsMessage fields and recording patterns for correlation.
  2. OrderReq for blocker ordering: When an attacker is blocked by multiple creatures, the real server sends OrderReq (GRE type 17) before AssignDamageReq to let the player order damage recipients. Is this needed for correctness or does Forge handle ordering internally?
  3. Forge bridge surface: How does WebPlayerController currently intercept the damage assignment decision? Is there an existing prompt/override point, or does it need a new CompletableFuture bridge?

Scope

In: AssignDamageReq/Resp/Confirmation GRE handshake. Building and sending the request when assignment is ambiguous. Handling the response and submitting to engine.

Out: Damage prevention/redirection, replacement effects on damage, first-strike-specific edge cases (those are separate systems). OrderReq for blocker ordering can be a follow-up.

References

  • docs/systems-map.md — Tier 2 gap
  • docs/catalog.yaml — combat damage section

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions