mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
Additional fix and simplified for playable abilities (see comments b94344341b
)
This commit is contained in:
parent
bd40d90286
commit
cce467a5ec
13 changed files with 142 additions and 137 deletions
|
@ -3,6 +3,7 @@ package mage.player.ai;
|
|||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.AbilityImpl;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.common.PassAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
|
@ -94,9 +95,9 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
}
|
||||
|
||||
protected void simulateOptions(Game game) {
|
||||
List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||
playables = filterAbilities(game, playables, suggested);
|
||||
for (Ability ability : playables) {
|
||||
for (ActivatedAbility ability : playables) {
|
||||
if (ability.getAbilityType() == AbilityType.MANA) {
|
||||
continue;
|
||||
}
|
||||
|
@ -186,15 +187,15 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
* @param suggested
|
||||
* @return
|
||||
*/
|
||||
protected List<Ability> filterAbilities(Game game, List<Ability> playables, List<String> suggested) {
|
||||
protected List<ActivatedAbility> filterAbilities(Game game, List<ActivatedAbility> playables, List<String> suggested) {
|
||||
if (playables.isEmpty()) {
|
||||
return playables;
|
||||
}
|
||||
if (suggested == null || suggested.isEmpty()) {
|
||||
return playables;
|
||||
}
|
||||
List<Ability> filtered = new ArrayList<>();
|
||||
for (Ability ability : playables) {
|
||||
List<ActivatedAbility> filtered = new ArrayList<>();
|
||||
for (ActivatedAbility ability : playables) {
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
if (card != null) {
|
||||
for (String s : suggested) {
|
||||
|
@ -212,7 +213,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
|||
return playables;
|
||||
}
|
||||
|
||||
protected List<Ability> filterOptions(Game game, List<Ability> options, Ability ability, List<String> suggested) {
|
||||
protected List<Ability> filterOptions(Game game, List<Ability> options, ActivatedAbility ability, List<String> suggested) {
|
||||
if (options.isEmpty()) {
|
||||
return options;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
package mage.player.ai;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.PassAbility;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
|
@ -45,16 +46,16 @@ public class MCTSPlayer extends ComputerPlayer {
|
|||
return new MCTSPlayer(this);
|
||||
}
|
||||
|
||||
protected List<Ability> getPlayableAbilities(Game game) {
|
||||
List<Ability> playables = getPlayable(game, true);
|
||||
protected List<ActivatedAbility> getPlayableAbilities(Game game) {
|
||||
List<ActivatedAbility> playables = getPlayable(game, true);
|
||||
playables.add(pass);
|
||||
return playables;
|
||||
}
|
||||
|
||||
public List<Ability> getPlayableOptions(Game game) {
|
||||
List<Ability> all = new ArrayList<>();
|
||||
List<Ability> playables = getPlayableAbilities(game);
|
||||
for (Ability ability: playables) {
|
||||
List<ActivatedAbility> playables = getPlayableAbilities(game);
|
||||
for (ActivatedAbility ability: playables) {
|
||||
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
|
||||
if (options.isEmpty()) {
|
||||
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
||||
|
|
|
@ -73,7 +73,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
|
|||
}
|
||||
|
||||
private Ability getAction(Game game) {
|
||||
List<Ability> playables = getPlayableAbilities(game);
|
||||
List<ActivatedAbility> playables = getPlayableAbilities(game);
|
||||
Ability ability;
|
||||
while (true) {
|
||||
if (playables.size() == 1) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package mage.player.ai;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.common.PassAbility;
|
||||
|
@ -59,10 +60,10 @@ public class SimulatedPlayer extends ComputerPlayer {
|
|||
return list;
|
||||
}
|
||||
|
||||
protected void simulateOptions(Game game, Ability previousActions) {
|
||||
protected void simulateOptions(Game game, ActivatedAbility previousActions) {
|
||||
allActions.add(previousActions);
|
||||
List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||
for (Ability ability: playables) {
|
||||
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||
for (ActivatedAbility ability: playables) {
|
||||
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
|
||||
if (options.isEmpty()) {
|
||||
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
||||
|
|
|
@ -1056,7 +1056,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
actingPlayer = game.getPlayer(game.getPriorityPlayerId());
|
||||
}
|
||||
if (actingPlayer != null) {
|
||||
useableAbilities = actingPlayer.getUseableActivatedAbilities(object, zone, game);
|
||||
useableAbilities = actingPlayer.getPlayableActivatedAbilities(object, zone, game);
|
||||
}
|
||||
|
||||
if (object instanceof Card
|
||||
|
|
|
@ -425,7 +425,7 @@ public final class SystemUtil {
|
|||
game.firePriorityEvent(opponent.getId());
|
||||
}
|
||||
|
||||
List<Ability> abilities = opponent.getPlayable(game, true);
|
||||
List<ActivatedAbility> abilities = opponent.getPlayable(game, true);
|
||||
Map<String, String> choices = new HashMap<>();
|
||||
abilities.forEach(ability -> {
|
||||
MageObject object = ability.getSourceObject(game);
|
||||
|
@ -437,10 +437,10 @@ public final class SystemUtil {
|
|||
choice.setKeyChoices(choices);
|
||||
if (feedbackPlayer.choose(Outcome.Detriment, choice, game) && choice.getChoiceKey() != null) {
|
||||
String needId = choice.getChoiceKey();
|
||||
Optional<Ability> ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst();
|
||||
Optional<ActivatedAbility> ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst();
|
||||
if (ability.isPresent()) {
|
||||
// TODO: set priority for player?
|
||||
ActivatedAbility activatedAbility = (ActivatedAbility) ability.get();
|
||||
ActivatedAbility activatedAbility = ability.get();
|
||||
game.informPlayers(feedbackPlayer.getLogName() + " as another player " + opponent.getLogName()
|
||||
+ " trying to force an activate ability: " + activatedAbility.getGameLogMessage(game));
|
||||
if (opponent.activateAbility(activatedAbility, game)) {
|
||||
|
|
|
@ -37,7 +37,7 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test_Flashback_Split() {
|
||||
public void test_Flashback_SplitLeft() {
|
||||
// {1}{U}
|
||||
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
|
||||
addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
|
||||
|
@ -67,4 +67,36 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerB, "Bident of Thassa", 1);
|
||||
assertPermanentCount(playerB, "Bow of Nylea", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Flashback_SplitRight() {
|
||||
// {1}{U}
|
||||
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
|
||||
addCard(Zone.HAND, playerA, "Snapcaster Mage", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
//
|
||||
// Wear {1}{R} Destroy target artifact.
|
||||
// Tear {W} Destroy target enchantment.
|
||||
addCard(Zone.GRAVEYARD, playerA, "Wear // Tear", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Bident of Thassa", 1); // Legendary Enchantment Artifact
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Bow of Nylea", 1); // Legendary Enchantment Artifact
|
||||
|
||||
// add flashback
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage");
|
||||
addTarget(playerA, "Wear // Tear");
|
||||
|
||||
// cast as flashback
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {W}", "Bident of Thassa");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerB, "Bident of Thassa", 1);
|
||||
assertPermanentCount(playerB, "Bow of Nylea", 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ public class RandomPlayer extends ComputerPlayer {
|
|||
}
|
||||
|
||||
private Ability getAction(Game game) {
|
||||
List<Ability> playables = getPlayableAbilities(game);
|
||||
List<ActivatedAbility> playables = getPlayableAbilities(game);
|
||||
Ability ability;
|
||||
while (true) {
|
||||
if (playables.size() == 1) {
|
||||
|
@ -115,8 +115,8 @@ public class RandomPlayer extends ComputerPlayer {
|
|||
return ability;
|
||||
}
|
||||
|
||||
protected List<Ability> getPlayableAbilities(Game game) {
|
||||
List<Ability> playables = getPlayable(game, true);
|
||||
protected List<ActivatedAbility> getPlayableAbilities(Game game) {
|
||||
List<ActivatedAbility> playables = getPlayable(game, true);
|
||||
playables.add(pass);
|
||||
return playables;
|
||||
}
|
||||
|
|
|
@ -587,14 +587,14 @@ public class TestPlayer implements Player {
|
|||
if (groups.length > 2 && !checkExecuteCondition(groups, game)) {
|
||||
break;
|
||||
}
|
||||
for (Ability ability : computerPlayer.getPlayable(game, true)) { // add wrong action log?
|
||||
for (ActivatedAbility ability : computerPlayer.getPlayable(game, true)) { // add wrong action log?
|
||||
if (isAbilityHaveTargetNameOrAlias(game, ability, groups[0])) {
|
||||
int bookmark = game.bookmarkState();
|
||||
Ability newAbility = ability.copy();
|
||||
ActivatedAbility newAbility = ability.copy();
|
||||
if (groups.length > 1 && !groups[1].equals("target=" + NO_TARGET)) {
|
||||
groupsForTargetHandling = groups;
|
||||
}
|
||||
if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) {
|
||||
if (computerPlayer.activateAbility(newAbility, game)) {
|
||||
actions.remove(action);
|
||||
groupsForTargetHandling = null;
|
||||
foundNoAction = 0; // Reset enless loop check because of no action
|
||||
|
@ -1078,7 +1078,7 @@ public class TestPlayer implements Player {
|
|||
});
|
||||
}
|
||||
|
||||
private void printAbilities(Game game, List<Ability> abilities) {
|
||||
private void printAbilities(Game game, List<? extends Ability> abilities) {
|
||||
System.out.println("Total abilities: " + (abilities != null ? abilities.size() : 0));
|
||||
if (abilities == null) {
|
||||
return;
|
||||
|
@ -1714,6 +1714,8 @@ public class TestPlayer implements Player {
|
|||
private void chooseStrictModeFailed(String choiceType, Game game, String reason, boolean printAbilities) {
|
||||
if (strictChooseMode && !AICanChooseInStrictMode) {
|
||||
if (printAbilities) {
|
||||
printStart("Available mana for " + computerPlayer.getName());
|
||||
printMana(game, computerPlayer.getManaAvailable(game));
|
||||
printStart("Available abilities for " + computerPlayer.getName());
|
||||
printAbilities(game, computerPlayer.getPlayable(game, true));
|
||||
printEnd();
|
||||
|
@ -2712,8 +2714,8 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||
return computerPlayer.getUseableActivatedAbilities(object, zone, game);
|
||||
public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||
return computerPlayer.getPlayableActivatedAbilities(object, zone, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3171,12 +3173,8 @@ public class TestPlayer implements Player {
|
|||
return computerPlayer.getManaAvailable(game);
|
||||
}
|
||||
|
||||
public List<Permanent> getAvailableManaProducersWithCost(Game game) {
|
||||
return computerPlayer.getAvailableManaProducersWithCost(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
||||
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||
return computerPlayer.getPlayable(game, hidden);
|
||||
}
|
||||
|
||||
|
|
|
@ -1023,7 +1023,7 @@ public class PlayerStub implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
||||
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1038,7 +1038,7 @@ public class PlayerStub implements Player {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||
public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import mage.ObjectColor;
|
|||
import mage.abilities.*;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.HintUtils;
|
||||
import mage.abilities.keyword.FlashbackAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.cards.repository.PluginClassloaderRegistery;
|
||||
import mage.constants.*;
|
||||
|
@ -312,6 +313,21 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
// dynamic
|
||||
all.addAll(cardState.getAbilities());
|
||||
|
||||
// workaround to add dynamic flashback ability from main card to all parts (example: Snapcaster Mage gives flashback to split card)
|
||||
if (!this.getId().equals(this.getMainCard().getId())) {
|
||||
CardState mainCardState = game.getState().getCardState(this.getMainCard().getId());
|
||||
if (mainCardState != null
|
||||
&& !mainCardState.hasLostAllAbilities()
|
||||
&& mainCardState.getAbilities().containsClass(FlashbackAbility.class)) {
|
||||
FlashbackAbility flash = new FlashbackAbility(this.getManaCost(), this.isInstant() ? TimingRule.INSTANT : TimingRule.SORCERY);
|
||||
flash.setSourceId(this.getId());
|
||||
flash.setControllerId(this.getOwnerId());
|
||||
flash.setSpellAbilityType(this.getSpellAbility().getSpellAbilityType());
|
||||
flash.setAbilityName(this.getName());
|
||||
all.add(flash);
|
||||
}
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
|
|
|
@ -627,13 +627,13 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
ManaOptions getManaAvailable(Game game);
|
||||
|
||||
List<Ability> getPlayable(Game game, boolean hidden);
|
||||
List<ActivatedAbility> getPlayable(Game game, boolean hidden);
|
||||
|
||||
List<Ability> getPlayableOptions(Ability ability, Game game);
|
||||
|
||||
Map<UUID, Integer> getPlayableObjects(Game game, Zone zone);
|
||||
|
||||
LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game);
|
||||
LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game);
|
||||
|
||||
boolean addCounters(Counter counter, Game game);
|
||||
|
||||
|
|
|
@ -1499,7 +1499,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||
public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
|
||||
boolean previousState = game.inCheckPlayableState();
|
||||
game.setCheckPlayableState(true);
|
||||
|
@ -1510,12 +1510,33 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
// collect and filter playable activated abilities
|
||||
List<Ability> allPlayable = getPlayable(game, true, zone, false);
|
||||
for (Ability ability : allPlayable) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
if (object.hasAbility(ability, game)) {
|
||||
useable.putIfAbsent(ability.getId(), (ActivatedAbility) ability);
|
||||
}
|
||||
// GUI: user clicks on card, but it must activate ability from any card's parts (main, left, right)
|
||||
UUID needId1, needId2, needId3;
|
||||
if (object instanceof SplitCard) {
|
||||
needId1 = object.getId();
|
||||
needId2 = ((SplitCard) object).getLeftHalfCard().getId();
|
||||
needId3 = ((SplitCard) object).getRightHalfCard().getId();
|
||||
} else if (object instanceof AdventureCard) {
|
||||
needId1 = object.getId();
|
||||
needId2 = ((AdventureCard) object).getMainCard().getId();
|
||||
needId3 = ((AdventureCard) object).getSpellCard().getId();
|
||||
} else if (object instanceof AdventureCardSpell) {
|
||||
needId1 = object.getId();
|
||||
needId2 = ((AdventureCardSpell) object).getParentCard().getId();
|
||||
needId3 = object.getId();
|
||||
} else {
|
||||
needId1 = object.getId();
|
||||
needId2 = object.getId();
|
||||
needId3 = object.getId();
|
||||
}
|
||||
|
||||
// workaround to find all abilities first and filter it for one object
|
||||
List<ActivatedAbility> allPlayable = getPlayable(game, true, zone, false);
|
||||
for (ActivatedAbility ability : allPlayable) {
|
||||
if (Objects.equals(ability.getSourceId(), needId1)
|
||||
|| Objects.equals(ability.getSourceId(), needId2)
|
||||
|| Objects.equals(ability.getSourceId(), needId3)) {
|
||||
useable.putIfAbsent(ability.getId(), ability);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -1524,58 +1545,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return useable;
|
||||
}
|
||||
|
||||
// Adds special abilities that are given to non permanents by continuous effects
|
||||
private void getOtherUseableActivatedAbilities(MageObject object, Zone zone, Game game, Map<UUID, ActivatedAbility> useable) {
|
||||
Abilities<ActivatedAbility> otherAbilities = game.getState().getActivatedOtherAbilities(object.getId(), zone);
|
||||
if (otherAbilities != null) {
|
||||
boolean canUse = !(object instanceof Permanent)
|
||||
|| ((Permanent) object).canUseActivatedAbilities(game);
|
||||
for (ActivatedAbility ability : otherAbilities) {
|
||||
if (canUse || ability.getAbilityType() == AbilityType.SPECIAL_ACTION) {
|
||||
Card card = game.getCard(ability.getSourceId());
|
||||
if (card != null) {
|
||||
if (card.isSplitCard() && ability instanceof FlashbackAbility) {
|
||||
FlashbackAbility flashbackAbility;
|
||||
// Left Half
|
||||
if (card.isInstant()) {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(),
|
||||
TimingRule.INSTANT);
|
||||
} else {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getLeftHalfCard().getManaCost(),
|
||||
TimingRule.SORCERY);
|
||||
}
|
||||
flashbackAbility.setSourceId(card.getId());
|
||||
flashbackAbility.setControllerId(card.getOwnerId());
|
||||
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_LEFT);
|
||||
flashbackAbility.setAbilityName(((SplitCard) card).getLeftHalfCard().getName());
|
||||
if (flashbackAbility.canActivate(playerId, game).canActivate()) {
|
||||
useable.put(flashbackAbility.getId(), flashbackAbility);
|
||||
}
|
||||
// Right Half
|
||||
if (card.isInstant()) {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(),
|
||||
TimingRule.INSTANT);
|
||||
} else {
|
||||
flashbackAbility = new FlashbackAbility(((SplitCard) card).getRightHalfCard().getManaCost(),
|
||||
TimingRule.SORCERY);
|
||||
}
|
||||
flashbackAbility.setSourceId(card.getId());
|
||||
flashbackAbility.setControllerId(card.getOwnerId());
|
||||
flashbackAbility.setSpellAbilityType(SpellAbilityType.SPLIT_RIGHT);
|
||||
flashbackAbility.setAbilityName(((SplitCard) card).getRightHalfCard().getName());
|
||||
if (flashbackAbility.canActivate(playerId, game).canActivate()) {
|
||||
useable.put(flashbackAbility.getId(), flashbackAbility);
|
||||
}
|
||||
|
||||
} else {
|
||||
useable.put(ability.getId(), ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected LinkedHashMap<UUID, ActivatedManaAbilityImpl> getUseableManaAbilities(MageObject object, Zone zone, Game game) {
|
||||
LinkedHashMap<UUID, ActivatedManaAbilityImpl> useable = new LinkedHashMap<>();
|
||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||
|
@ -3182,7 +3151,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<Ability> output) {
|
||||
private Abilities<ActivatedAbility> getActivatedOnly(Abilities<Ability> list) {
|
||||
Abilities<ActivatedAbility> res = new AbilitiesImpl<>();
|
||||
list.stream()
|
||||
.filter(a -> a instanceof ActivatedAbility)
|
||||
.map(a -> (ActivatedAbility) a)
|
||||
.forEach(res::add);
|
||||
return res;
|
||||
}
|
||||
|
||||
private void getPlayableFromCardAll(Game game, Zone fromZone, Card card, ManaOptions availableMana, List<ActivatedAbility> output) {
|
||||
if (fromZone == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -3190,41 +3168,22 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
// BASIC abilities
|
||||
if (card instanceof SplitCard) {
|
||||
SplitCard splitCard = (SplitCard) card;
|
||||
getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), getActivatedOnly(splitCard.getLeftHalfCard().getAbilities(game)), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), getActivatedOnly(splitCard.getRightHalfCard().getAbilities(game)), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, splitCard, getActivatedOnly(splitCard.getSharedAbilities(game)), availableMana, output);
|
||||
} else if (card instanceof AdventureCard) {
|
||||
// adventure must use different card characteristics for different spells (main or adventure)
|
||||
AdventureCard adventureCard = (AdventureCard) card;
|
||||
getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), getActivatedOnly(adventureCard.getSpellCard().getAbilities(game)), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, adventureCard, getActivatedOnly(adventureCard.getSharedAbilities(game)), availableMana, output);
|
||||
} else {
|
||||
getPlayableFromCardSingle(game, fromZone, card, card.getAbilities(game), availableMana, output);
|
||||
getPlayableFromCardSingle(game, fromZone, card, getActivatedOnly(card.getAbilities(game)), availableMana, output);
|
||||
}
|
||||
|
||||
// DYNAMIC ADDED abilities
|
||||
if (fromZone != Zone.ALL) { // TODO: test revealed cards with dynamic added abilities
|
||||
// Other activated abilities (added dynamic by effects)
|
||||
LinkedHashMap<UUID, ActivatedAbility> useable;
|
||||
if (card instanceof AdventureCard) {
|
||||
// adventure cards (contains two different cards: main and adventure spell)
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(((AdventureCard) card).getSpellCard(), fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
} else {
|
||||
// all other cards (TODO: check split cards with dynamic added abilities)
|
||||
useable = new LinkedHashMap<>();
|
||||
getOtherUseableActivatedAbilities(card, fromZone, game, useable);
|
||||
output.addAll(useable.values());
|
||||
}
|
||||
}
|
||||
// DYNAMIC ADDED abilities are adds in getAbilities(game)
|
||||
}
|
||||
|
||||
private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
|
||||
private void getPlayableFromCardSingle(Game game, Zone fromZone, Card card, Abilities<ActivatedAbility> candidateAbilities, ManaOptions availableMana, List<ActivatedAbility> output) {
|
||||
// check "can play" condition as affected controller (BUT play from not own hand zone must be checked as original controller)
|
||||
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
|
||||
boolean isPlaySpell = (ability instanceof SpellAbility);
|
||||
|
@ -3300,12 +3259,12 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
||||
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||
return getPlayable(game, hidden, Zone.ALL, true);
|
||||
}
|
||||
|
||||
public List<Ability> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
|
||||
List<Ability> playable = new ArrayList<>();
|
||||
public List<ActivatedAbility> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
|
||||
List<ActivatedAbility> playable = new ArrayList<>();
|
||||
if (shouldSkipGettingPlayable(game)) {
|
||||
return playable;
|
||||
}
|
||||
|
@ -3402,19 +3361,17 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
// eliminate duplicate activated abilities (uses for AI plays)
|
||||
Map<String, Ability> activatedUnique = new HashMap<>();
|
||||
List<Ability> activatedAll = new ArrayList<>();
|
||||
Map<String, ActivatedAbility> activatedUnique = new HashMap<>();
|
||||
List<ActivatedAbility> activatedAll = new ArrayList<>();
|
||||
|
||||
// activated abilities from battlefield objects
|
||||
if (fromAll || fromZone == Zone.BATTLEFIELD) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
|
||||
List<Ability> battlePlayable = new ArrayList<>();
|
||||
List<ActivatedAbility> battlePlayable = new ArrayList<>();
|
||||
getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
|
||||
for (Ability ability : battlePlayable) {
|
||||
if (ability instanceof ActivatedAbility) {
|
||||
activatedUnique.putIfAbsent(ability.toString(), ability);
|
||||
activatedAll.add(ability);
|
||||
}
|
||||
for (ActivatedAbility ability : battlePlayable) {
|
||||
activatedUnique.putIfAbsent(ability.toString(), ability);
|
||||
activatedAll.add(ability);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3467,7 +3424,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
*/
|
||||
@Override
|
||||
public Map<UUID, Integer> getPlayableObjects(Game game, Zone zone) {
|
||||
List<Ability> playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards
|
||||
List<ActivatedAbility> playableAbilities = getPlayable(game, true, zone, false); // do not hide duplicated abilities/cards
|
||||
Map<UUID, Integer> playableObjects = new HashMap<>();
|
||||
for (Ability ability : playableAbilities) {
|
||||
if (ability.getSourceId() != null) {
|
||||
|
@ -3518,7 +3475,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
@Override
|
||||
public List<Ability> getPlayableOptions(Ability ability, Game game) {
|
||||
List<Ability> options = new ArrayList<>();
|
||||
|
||||
if (ability.isModal()) {
|
||||
addModeOptions(options, ability, game);
|
||||
} else if (!ability.getTargets().getUnchosen().isEmpty()) {
|
||||
|
|
Loading…
Reference in a new issue