Skip to content

fix(parser): scope "can't attack you or block creatures you control" instead of collapsing to self (#5607)#5878

Merged
matthewevans merged 5 commits into
phase-rs:mainfrom
philluiz2323:fix/flying-cant-attack-you-or-block-5607
Jul 16, 2026
Merged

fix(parser): scope "can't attack you or block creatures you control" instead of collapsing to self (#5607)#5878
matthewevans merged 5 commits into
phase-rs:mainfrom
philluiz2323:fix/flying-cant-attack-you-or-block-5607

Conversation

@philluiz2323

Copy link
Copy Markdown
Contributor

Summary

Fixes #5607. Storm, Windrider — "Creatures with flying can't attack you or block creatures you control" — could not attack. The line collapsed to a self-scoped blanket CantAttack on Storm herself.

Implementation method (required)

  • Produced via the /engine-implementer pipeline
  • Not /engine-implementer — explain why below

Contained static-parser fix, root-caused from a parse dump and covered by parser tests + the full --lib regression. It adds one compound-line parser to the multi-static dispatch and reuses existing static modes and enforcement; no runtime/combat changes.

Root cause

The single-clause "<subject> can't attack you" already parses correctly — a defender-scoped CantAttack (attack_defended = you) affecting the subject. Storm's trailing "or block creatures you control" left an unconsumed remainder, so that subject parser rejected the line, and the generic dispatch arm fell back to:

StaticDefinition { mode: CantAttack, affected: Some(SelfRef), attack_defended: None }

attack_defended: None is a blanket "can't attack" (combat.rs creature_cant_attack_gated), and affected: SelfRef points it at Storm — so the source creature itself couldn't attack.

Fix

A dedicated compound parser in the multi-static dispatch emits the two statics the line actually means (CR 508.1c + CR 509.1b):

  1. a defender-scoped CantAttack (attack_defended = you) — the subject can't attack the source's controller, but may still attack anyone else; and
  2. a BlockRestriction whose filter is the negation of the block target. BlockRestriction { filter } means "can block only creatures matching filter" (combat.rs:1482), so filter: Not(creatures you control) is exactly "can't block creatures you control".

Both are scoped to the subject filter, and the block target is parsed by the full parse_type_phrase grammar, so this is a building block for the defensive-flyer class rather than one card. The enforcement side already handles defender-scoped attack restrictions and BlockRestriction, so no combat-code change is needed.

CR references

  • CR 508.1c — a defender-scoped attack restriction leaves the creature able to attack another target.
  • CR 509.1b — blocker-side "can block only <filter>" restrictions.

Verification

  • cargo test -p engine --lib16675 passed, 0 failed (static-parser changes can affect many cards).
  • New tests: the compound yields two subject-scoped statics (a defender-scoped CantAttack and a negated BlockRestriction), never SelfRef; and the plain single-clause "can't attack you" is unchanged.
  • cargo fmt --all clean.

…reatures you control" (phase-rs#5607)

Storm, Windrider ("Creatures with flying can't attack you or block
creatures you control") could not attack: the whole line parsed to a
self-scoped blanket `CantAttack` on Storm herself.

The single-clause "<subject> can't attack you" already parses correctly
(defender-scoped `CantAttack`, affected = the subject). The trailing "or
block creatures you control" made that subject parser reject (unconsumed
remainder), and the generic dispatch arm then fell back to
`CantAttack { affected: SelfRef }` — a blanket restriction that stopped
the source creature from attacking at all.

Add a dedicated compound parser in the multi-static dispatch that emits the
two correctly-scoped statics the line actually means (CR 508.1c + CR
509.1b):

  1. a defender-scoped `CantAttack` (attack_defended = "you") — the subject
     can't attack the source's controller but may still attack anyone else;
  2. a `BlockRestriction` whose filter is the NEGATION of the block target —
     "can block only things that are NOT creatures you control" is exactly
     "can't block creatures you control" (the CR 509.1b enforcement in
     combat.rs allows a block only when the attacker matches the filter).

Both are scoped to the subject filter and the block target is parsed by the
full type-phrase grammar, so this is a building block for the defensive-flyer
class, not a single card. The enforcement side already handles
defender-scoped attack restrictions and negated block filters, so no runtime
change is required.

Verified: cargo test -p engine --lib -> 16675 passed, 0 failed. New tests
assert the compound yields two scoped statics (never SelfRef) and that the
plain single-clause "can't attack you" is unchanged.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces parser support for the compound combat restriction "can't attack you or block creatures you control" (CR 508.1c + CR 509.1b) by adding the parse_cant_attack_you_or_block_predicate and parse_subject_cant_attack_you_or_block_static functions, and integrating them into the multi-dispatch pipeline. The review feedback suggests adding a defensive check to explicitly reject TargetFilter::Any when parsing the block filter to prevent constructing invalid block restrictions.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread crates/engine/src/parser/oracle_static/shared.rs Outdated
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

Parse changes introduced by this PR · 1 card(s), 2 signature(s) (baseline: main be1c45920bb7)

1 card(s) · static/BlockRestriction:Quality(Not { filter: Typed(TypedFilter { type_filters: [Creat… · added: BlockRestriction:Quality(Not { filter: Typed(TypedFilter { type_filters: [Creature], controller: Some(You), properties: [] }) }) (affects=with Flying creature)

Examples: Storm, Windrider

1 card(s) · static/CantAttack · field affects: selfwith Flying creature

Examples: Storm, Windrider

@matthewevans matthewevans self-assigned this Jul 15, 2026
@matthewevans matthewevans added the enhancement New feature or request label Jul 15, 2026
@matthewevans matthewevans removed their assignment Jul 15, 2026

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved after current-head implementation review.

@matthewevans
matthewevans added this pull request to the merge queue Jul 16, 2026

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved after current-head implementation review.

@matthewevans matthewevans removed their assignment Jul 16, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 16, 2026
@matthewevans matthewevans self-assigned this Jul 16, 2026
Maintainer port preserves the approved Storm parser while retaining main's independent static-parser additions.

Co-authored-by: philluiz2323 <philluiz2323@users.noreply.github.com>

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved after rechecking the maintainer conflict-resolution merge at current head de0145bb29faf8c9f795e0b397bcd9f881580317.

@matthewevans matthewevans removed their assignment Jul 16, 2026
@matthewevans
matthewevans added this pull request to the merge queue Jul 16, 2026
Merged via the queue into phase-rs:main with commit de0d2e2 Jul 16, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Card Bug] Storm, Windrider: Can't attack

2 participants