mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
* Copy spell for each other permanents that it could target - fixed that AI can freeze the game, fixed wrong highlighting;
This commit is contained in:
parent
a90fc283d4
commit
926f5f0621
6 changed files with 138 additions and 42 deletions
|
@ -0,0 +1,80 @@
|
|||
package org.mage.test.cards.single.tor;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class RadiateTest extends CardTestPlayerBaseWithAIHelps {
|
||||
|
||||
@Test
|
||||
public void test_Play_Manual() {
|
||||
// Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell
|
||||
// for each other permanent or player the spell could target. Each copy targets a different one of those
|
||||
// permanents and players.
|
||||
addCard(Zone.HAND, playerA, "Radiate", 6); // {3}{R}{R}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||
//
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Kitesail Corsair", 2);
|
||||
|
||||
// cast bolt and copy spell for each another target
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Radiate", "Lightning Bolt", "Lightning Bolt");
|
||||
checkStackSize("before radiate", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||
// must have: 2x for corsairs, 2x for bears, 1x for A
|
||||
checkStackSize("after radiate", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 1 + 5);
|
||||
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, 6); // 6 lands
|
||||
assertPermanentCount(playerB, 0);
|
||||
assertLife(playerA, 20 - 3);
|
||||
assertLife(playerB, 20 - 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Play_AI() {
|
||||
// possible bug: game freeze or Target wasn't handled... TargetWithAdditionalFilter
|
||||
|
||||
// Choose target instant or sorcery spell that targets only a single permanent or player. Copy that spell
|
||||
// for each other permanent or player the spell could target. Each copy targets a different one of those
|
||||
// permanents and players.
|
||||
addCard(Zone.HAND, playerA, "Radiate", 6); // {3}{R}{R}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
|
||||
//
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Kitesail Corsair", 2);
|
||||
|
||||
// cast bolt and copy spell for each another target
|
||||
// must call commands manually cause it's a bad scenario and AI don't cast it itself
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Radiate", "Lightning Bolt", "Lightning Bolt");
|
||||
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); // but AI can choose targets
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, 6); // 6 lands
|
||||
assertPermanentCount(playerB, 0);
|
||||
assertLife(playerA, 20 - 3);
|
||||
assertLife(playerB, 20 - 3);
|
||||
}
|
||||
}
|
|
@ -80,7 +80,7 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
|
|||
return false;
|
||||
}
|
||||
|
||||
// collect objects that can be targeted
|
||||
// generate copies for each possible target, but do not put it to stack (use must choose targets in custom order later)
|
||||
Spell copy = spell.copySpell(source.getControllerId(), game);
|
||||
modifyCopy(copy, game, source);
|
||||
Target sampleTarget = targetsToBeChanged.iterator().next().getTarget(copy);
|
||||
|
@ -135,28 +135,33 @@ public abstract class CopySpellForEachItCouldTargetEffect<T extends MageItem> ex
|
|||
}
|
||||
}
|
||||
|
||||
// allow the copies' controller to choose the order that they go on the stack
|
||||
// allows controller of the copies to choose spells order on stack (by using targeting GUI)
|
||||
for (Player player : game.getPlayers().values()) {
|
||||
if (playerTargetCopyMap.containsKey(player.getId())) {
|
||||
Map<UUID, Spell> targetCopyMap = playerTargetCopyMap.get(player.getId());
|
||||
if (targetCopyMap != null) {
|
||||
while (!targetCopyMap.isEmpty()) {
|
||||
// all checks must be make for new copied spell, not original (controller can be changed)
|
||||
Spell spellSample = targetCopyMap.values().stream().findFirst().get();
|
||||
FilterInPlay<T> setFilter = filter.copy();
|
||||
setFilter.add(new FromSetPredicate(targetCopyMap.keySet()));
|
||||
setFilter.add(new FromSetPredicate(targetCopyMap.keySet())); // allows only unselected targets
|
||||
Target target = new TargetWithAdditionalFilter(sampleTarget, setFilter);
|
||||
target.setNotTarget(false); // it is targeted, not chosen
|
||||
target.setMinNumberOfTargets(0);
|
||||
target.setMinNumberOfTargets(0); // if not selected then it uses first target (see below), same for AI
|
||||
target.setMaxNumberOfTargets(1);
|
||||
target.setTargetName(filter.getMessage() + " that " + spell.getLogName()
|
||||
target.setTargetName(filter.getMessage() + " that " + spellSample.getLogName()
|
||||
+ " could target (" + targetCopyMap.size() + " remaining)");
|
||||
// shortcut if there's only one possible target remaining
|
||||
|
||||
if (targetCopyMap.size() > 1
|
||||
&& target.canChoose(spell.getId(), player.getId(), game)) {
|
||||
&& target.canChoose(spellSample.getId(), player.getId(), game)) {
|
||||
// The original "source" is not applicable here due to the spell being a copy. ie: Zada, Hedron Grinder
|
||||
player.chooseTarget(Outcome.Neutral, target, spell.getSpellAbility(), game); // not source, but the spell that is copied
|
||||
Outcome outcome = spellSample.getSpellAbility().getAllEffects().getOutcome(spellSample.getSpellAbility());
|
||||
player.chooseTarget(outcome, target, spellSample.getSpellAbility(), game); // not source, but the spell that is copied
|
||||
}
|
||||
|
||||
Collection<UUID> chosenIds = target.getTargets();
|
||||
if (chosenIds.isEmpty()) {
|
||||
// uses first target on cancel/non-selected
|
||||
chosenIds = targetCopyMap.keySet();
|
||||
}
|
||||
List<UUID> toDelete = new ArrayList<>();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.filter.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageItem;
|
||||
import mage.filter.FilterImpl;
|
||||
import mage.filter.FilterInPlay;
|
||||
|
@ -10,8 +8,9 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class FilterCreatureOrPlayer extends FilterImpl<MageItem> implements FilterInPlay<MageItem> {
|
||||
|
@ -42,20 +41,24 @@ public class FilterCreatureOrPlayer extends FilterImpl<MageItem> implements Filt
|
|||
|
||||
@Override
|
||||
public boolean match(MageItem o, Game game) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, game);
|
||||
if (super.match(o, game)) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, sourceId, playerId, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, sourceId, playerId, game);
|
||||
if (super.match(o, game)) { // process predicates
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, sourceId, playerId, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, sourceId, playerId, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package mage.filter.common;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageItem;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author JRHerlehy Created on 4/8/18.
|
||||
*/
|
||||
|
@ -33,22 +33,26 @@ public class FilterCreaturePlayerOrPlaneswalker extends FilterPermanentOrPlayer
|
|||
|
||||
@Override
|
||||
public boolean match(MageItem o, Game game) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, game)
|
||||
|| planeswalkerFilter.match((Permanent) o, game);
|
||||
if (super.match(o, game)) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, game)
|
||||
|| planeswalkerFilter.match((Permanent) o, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, sourceId, playerId, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, sourceId, playerId, game)
|
||||
|| planeswalkerFilter.match((Permanent) o, sourceId, playerId, game);
|
||||
if (super.match(o, game)) { // process predicates
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, sourceId, playerId, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return creatureFilter.match((Permanent) o, sourceId, playerId, game)
|
||||
|| planeswalkerFilter.match((Permanent) o, sourceId, playerId, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.filter.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageItem;
|
||||
import mage.filter.FilterImpl;
|
||||
import mage.filter.FilterInPlay;
|
||||
|
@ -11,6 +9,8 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
*/
|
||||
|
@ -46,20 +46,24 @@ public class FilterPermanentOrPlayer extends FilterImpl<MageItem> implements Fil
|
|||
|
||||
@Override
|
||||
public boolean match(MageItem o, Game game) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return permanentFilter.match((Permanent) o, game);
|
||||
if (super.match(o, game)) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return permanentFilter.match((Permanent) o, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) {
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, sourceId, playerId, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return permanentFilter.match((Permanent) o, sourceId, playerId, game);
|
||||
if (super.match(o, game)) { // process predicates
|
||||
if (o instanceof Player) {
|
||||
return playerFilter.match((Player) o, sourceId, playerId, game);
|
||||
} else if (o instanceof Permanent) {
|
||||
return permanentFilter.match((Permanent) o, sourceId, playerId, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public class FilterPermanentOrPlayerWithCounter extends FilterPermanentOrPlayer
|
|||
|
||||
@Override
|
||||
public boolean match(MageItem o, UUID sourceId, UUID playerId, Game game) {
|
||||
if (super.match(o, sourceId, playerId, game)) {
|
||||
if (super.match(o, sourceId, playerId, game)) { // same as parent class, so can call with full params
|
||||
if (o instanceof Player) {
|
||||
return !((Player) o).getCounters().isEmpty();
|
||||
} else if (o instanceof Permanent) {
|
||||
|
|
Loading…
Reference in a new issue