Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Alania, Divergent Storm #12601

Merged
merged 12 commits into from
Aug 15, 2024

Conversation

jimga150
Copy link
Contributor

@jimga150 jimga150 commented Jul 24, 2024

#11853

Assumption made here:
"if it's the first instant spell, the first sorcery spell, or the first Otter spell other than Alania you've cast this turn" means that this will trigger up to three times per turn, once for each spell of those three criteria, assuming that none of those three spells fits both of those criteria.

Question: How can I cause the AlaniaDivergentStormWatcher not count the cast of Alania, Divergent Storm, like in the oracle text? I assume this would happen by having the watcher check to see if the SPELL_CAST event belongs to a Spell that owns this instance of the watcher, but its possible that there's a better strategy.

  • Add "other than Alania" clause functionality

Write test:

  • Two otters (on turn Alania is cast, as well as next turn)
  • Two instants
  • Two sorceries
  • Card that isnt any of the above
  • Case where all opponents have hexproof (no valid target, ergo no copy)

@github-actions github-actions bot added the cards label Jul 24, 2024
@jimga150
Copy link
Contributor Author

Did strict oracle text checks get turned back on?

Copy link
Contributor

@xenohedron xenohedron left a comment

Choose a reason for hiding this comment

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

For the Otter other than case, perhaps you could store the first and second Otter spells cast. Then start by checking the first, but if it's the source MOR, then instead check the second.

}
}

// Based on FirstSpellCastThisTurnWatcher
Copy link
Contributor

Choose a reason for hiding this comment

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

well the reference watcher is bugged, because it's checking id without zcc, needs to save MageObjectReference instead.

Mage.Sets/src/mage/cards/a/AlaniaDivergentStorm.java Outdated Show resolved Hide resolved
Mage.Sets/src/mage/cards/a/AlaniaDivergentStorm.java Outdated Show resolved Hide resolved
@xenohedron
Copy link
Contributor

Did strict oracle text checks get turned back on?

don't worry about the verify failure, it's a rarity error in BLC, scryfall fixed it upstream, we need to update it to match

@jimga150
Copy link
Contributor Author

Is there a way to get a MageObjectReference pointing to the stack spell that became the permanent whose UUID you have now? Or am i going about this wrong?

Right now i'm trying to get this in order to pass it into the watcher's validation function, so it can check it against the first saved Otter cast.

@xenohedron
Copy link
Contributor

Yeah, MageObjectReference has a constructor that takes an offset, so either +1 or -1 (depending on how you do it) will match spell to the permanent it becomes.

@jimga150
Copy link
Contributor Author

That doesn't seem to hit a match with the recorded MOR of the first Otter cast.

My watch function records like so:

        Spell spell = (Spell) game.getObject(event.getTargetId());
        if (spell == null) {
            return;
        }
        if (!game.isSimulation()){
            logger.info("Spell cast: " + spell.getName());
            logger.info("Spell ID: " + spell.getId());
        }
        UUID spellControllerID = spell.getControllerId();
        MageObjectReference spellMOR = new MageObjectReference(spell, game);
        if (spell.getCardType(game).contains(CardType.INSTANT) &&
                !playerFirstInstantCast.containsKey(spellControllerID)) {
            playerFirstInstantCast.put(spellControllerID, spellMOR);
        }
        if (spell.getCardType(game).contains(CardType.SORCERY) &&
                !playerFirstSorceryCast.containsKey(spellControllerID)) {
            playerFirstSorceryCast.put(spellControllerID, spellMOR);
        }
        if (spell.getSubtype(game).contains(SubType.OTTER)){
            if (!game.isSimulation()){
                logger.info("Is Otter!");
            }
            if (!playerFirstOtterCast.containsKey(spellControllerID)) {
                playerFirstOtterCast.put(spellControllerID, spellMOR);
                if (!game.isSimulation()){
                    logger.info("Is First");
                }
            } else if (!playerSecondOtterCast.containsKey(spellControllerID)) {
                playerSecondOtterCast.put(spellControllerID, spellMOR);
                if (!game.isSimulation()){
                    logger.info("Is Second");
                }
            }
        }

The apply() function then grabs the necessary info to pass into the validator:

        Spell spell = (Spell) source.getEffects().get(0).getValue("spellCast");
        if (spell == null) {
            return false;
        }
        Permanent sourcePermanent = source.getSourcePermanentOrLKI(game);
        if (!game.isSimulation()){
            logger.info("sourcePermanent: " + sourcePermanent);
            logger.info("name: " + sourcePermanent.getName());
        }
        // Get source permanent MOR from when it was on the stack
        MageObjectReference sourceSpellMOR = new MageObjectReference(sourcePermanent, game, -1); //This in theory should be the spell that became the source permanent
        AlaniaDivergentStormWatcher watcher = game.getState().getWatcher(AlaniaDivergentStormWatcher.class);
        UUID spellControllerID = spell.getControllerId();
        MageObjectReference spellMOR = new MageObjectReference(spell, game);
        return watcher.spellIsFirstISOCast(spellControllerID, spellMOR, sourceSpellMOR, game);

which is:

public boolean spellIsFirstISOCast(UUID controllerID, MageObjectReference spell, MageObjectReference AlaniaMOR, Game game) {

        MageObjectReference firstOtterMOR = playerFirstOtterCast.get(controllerID);

        if (!game.isSimulation()){
            logger.info("firstOtterMOR: " + firstOtterMOR.getSourceId());
            logger.info("spell: " + spell.getSourceId());
            logger.info("AlaniaMOR: " + AlaniaMOR.getSourceId());
//            logger.info(AlaniaMOR.getCard(game).getName());
        }

        if (firstOtterMOR != null && firstOtterMOR.equals(AlaniaMOR)) {
            firstOtterMOR = playerSecondOtterCast.get(controllerID);
            if (!game.isSimulation()){
                logger.info("oops, that was Alania. ACTUAL firstOtterMOR: " + firstOtterMOR);
            }
        }

        return spell.equals(playerFirstInstantCast.get(controllerID)) ||
                spell.equals(playerFirstSorceryCast.get(controllerID)) ||
                (spell.equals(firstOtterMOR));
    }

Casting Alania and a second otter yields:

INFO  2024-07-24 23:44:16,997 Spell cast: Alania, Divergent Storm                                                        =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:16,997 Spell ID: 7d16035d-8746-4589-9717-b8a6613f40c5                                             =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:16,998 Is Otter!                                                                                  =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:16,998 Is First                                                                                   =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:23,379 Spell cast: Coruscation Mage                                                               =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:23,379 Spell ID: 912e8030-7d68-4962-adb2-b5b7820769a6                                             =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:23,379 Is Otter!                                                                                  =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:23,379 Is Second                                                                                  =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.watch 
INFO  2024-07-24 23:44:23,380 sourcePermanent: Alania, Divergent Storm [50a], C, AlaniaDivergentStorm, BLB:204::0, 3/5   =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormCondition.apply 
INFO  2024-07-24 23:44:23,380 name: Alania, Divergent Storm                                                              =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormCondition.apply 
INFO  2024-07-24 23:44:23,380 firstOtterMOR: 7d16035d-8746-4589-9717-b8a6613f40c5                                        =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.spellIsFirstISOCast 
INFO  2024-07-24 23:44:23,380 spell: 912e8030-7d68-4962-adb2-b5b7820769a6                                                =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.spellIsFirstISOCast 
INFO  2024-07-24 23:44:23,380 AlaniaMOR: 50a1b828-e420-4594-8f44-cafa48146f90                                            =>[GAME e7ff68a3-4313-4231-ae59-c5726185cf98] AlaniaDivergentStormWatcher.spellIsFirstISOCast 

@jimga150
Copy link
Contributor Author

jimga150 commented Aug 2, 2024

Yeah, i'm stumped for this. It looks like spells entering the battlefield get a new UUID from what it had on the stack, so making a MOR of a permanent with an offset of -1 doesnt net you a MOR that is equivalent to the MOR of the spell that was cast to make that permanent. How is this supposed to work?

@github-actions github-actions bot added the tests label Aug 11, 2024
@jimga150
Copy link
Contributor Author

I think i cracked it. Turns out the UUID of a spell on a stack is derived from its SpellAbility, not the Card that was cast.

@jimga150 jimga150 marked this pull request as ready for review August 11, 2024 14:11
this.getTargets().clearChosen();
paid = false;
if (this.getTargets().choose(Outcome.DrawCard, controllerId, source.getSourceId(), source, game)) {
Player opponent = game.getPlayer(this.getTargets().get(0).getTargets().get(0));
Copy link
Contributor

Choose a reason for hiding this comment

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

can't you just use this.getTargets().getFirstTarget()?

if (opponent == null || !opponent.canRespond()){
return false;
}
paid = opponent.drawCards(1, source, game) != 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

a little weird to be using != 0 rather than > 0

MageObjectReference spellMOR = new MageObjectReference(spell, game);
if (spell.getCardType(game).contains(CardType.INSTANT) &&
!playerFirstInstantCast.containsKey(spellControllerID)) {
playerFirstInstantCast.put(spellControllerID, spellMOR);
Copy link
Contributor

Choose a reason for hiding this comment

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

putIfAbsent might be simpler syntax

@xenohedron xenohedron merged commit 9a872b4 into magefree:master Aug 15, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants