Visitar URL original
GameState: a divided-damage spell put on the stack with a `->` target throws NullPointerException on resolution · Issue #12444 · Card-Forge/forge · GitHub
Skip to content

GameState: a divided-damage spell put on the stack with a -> target throws NullPointerException on resolution #12444

Open

Description

@g56bzz7wz9-cmd

Summary: When a game state or puzzle puts a "divided as you choose" damage spell on the stack with a scripted target (for example humanputonstack=Fire Covenant->701), GameState adds the target but no damage division. Resolving the spell then throws a NullPointerException in DamageDealEffect.resolve.

Environment: Forge desktop 2.0.16-SNAPSHOT-10.09 (build dated 2026-10-09, source a2ba8ce9), macOS, OpenJDK 21. On current master (e5a0f8156, 2026-10-25), GameState.java is identical to that build and DamageDealEffect.java has the same line. Links below point to master.

Steps to reproduce:

  1. Use a game state or puzzle whose [state] section contains:

    ActivePlayer=Human
    ActivePhase=MAIN1
    humanbattlefield=Mountain;Swamp;Swamp
    aibattlefield=Grizzly Bears|Id:701
    humanputonstack=Fire Covenant->701
    
  2. Apply it. Fire Covenant is on the stack, targeting Grizzly Bears.

  3. Let the spell resolve.

Expected: The spell resolves. With a single scripted target, all of the divided damage could go to that target.

Actual: Resolution throws:

java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because the return value of "forge.game.spellability.SpellAbility.getDividedValue(forge.game.GameObject)" is null
	at forge.game.ability.effects.DamageDealEffect.resolve(DamageDealEffect.java:248)
	at forge.game.ability.SpellApiBased.resolve(SpellApiBased.java:44)
	at forge.game.ability.AbilityUtils.resolveApiAbility(AbilityUtils.java:1397)
	at forge.game.ability.AbilityUtils.resolve(AbilityUtils.java:1333)
	at forge.game.zone.MagicStack.resolveStack(MagicStack.java:647)

Just before resolving, the spell on the stack reported targets: [Grizzly Bears], isDividedAsYouChoose() == true and getDividedValue(Grizzly Bears) == null.

Verification: Reproduced on 2026-10-26 with the installed stock desktop JAR. A small headless harness built a two-player game, ran GameState.parse and applyGameOnThread on the [state] section above on the game thread, then called MagicStack.resolveStack(); the trace above is its output. I first tried the same file as a puzzle in the desktop app, but could not finish that run, so the GUI path itself is unconfirmed. Puzzle Mode applies puzzles through the same GameState code. No stock puzzle in res/puzzle pre-casts or puts on the stack a DividedAsYouChoose spell, so the shipped puzzles are not affected.

Root cause: precastSpellFromCard and executeScript parse only the target after ->. handleScriptedTargetingForSA adds it with sa.getTargets().add(...) and never records a divided allocation. DamageDealEffect.resolve then unboxes sa.getDividedValue(o) into an int for each target, and the null value throws. The precast path (humanprecast=...->id) resolves the spell directly and appears to reach the same line; I did not run it.

Suggested fix: In handleScriptedTargetingForSA, when sa.isDividedAsYouChoose(), give the scripted target an allocation along with the target. With one scripted target, that is the whole amount, sa.addDividedAllocation(target, ...). The divided total may need computing first, since getStillToDivide() returns 0 while dividedValue is unset. Alternatively, extend the -> syntax to carry an amount. Hardening DamageDealEffect against a null allocation would avoid the crash, but would hide the missing allocation.

Duplicate search: Searched open and closed issues and PRs (2026-10-26) for Fire Covenant, getDividedValue, DividedAsYouChoose, GameState putonstack, puzzle divided damage, DamageDealEffect NullPointerException, divided damage NullPointerException, puzzle NullPointerException. Related but different: #654 (open: "exile until leaves" broken in GameState / Puzzle Mode), #12378 (merged: random retarget of a divided ability). None describes this crash.


🤖 Investigated and written in collaboration with Claude Code.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions