mirror of
https://github.com/correl/mage.git
synced 2024-11-15 03:00:16 +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.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.AbilityImpl;
|
import mage.abilities.AbilityImpl;
|
||||||
|
import mage.abilities.ActivatedAbility;
|
||||||
import mage.abilities.TriggeredAbility;
|
import mage.abilities.TriggeredAbility;
|
||||||
import mage.abilities.common.PassAbility;
|
import mage.abilities.common.PassAbility;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
|
@ -94,9 +95,9 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void simulateOptions(Game game) {
|
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);
|
playables = filterAbilities(game, playables, suggested);
|
||||||
for (Ability ability : playables) {
|
for (ActivatedAbility ability : playables) {
|
||||||
if (ability.getAbilityType() == AbilityType.MANA) {
|
if (ability.getAbilityType() == AbilityType.MANA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -186,15 +187,15 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
* @param suggested
|
* @param suggested
|
||||||
* @return
|
* @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()) {
|
if (playables.isEmpty()) {
|
||||||
return playables;
|
return playables;
|
||||||
}
|
}
|
||||||
if (suggested == null || suggested.isEmpty()) {
|
if (suggested == null || suggested.isEmpty()) {
|
||||||
return playables;
|
return playables;
|
||||||
}
|
}
|
||||||
List<Ability> filtered = new ArrayList<>();
|
List<ActivatedAbility> filtered = new ArrayList<>();
|
||||||
for (Ability ability : playables) {
|
for (ActivatedAbility ability : playables) {
|
||||||
Card card = game.getCard(ability.getSourceId());
|
Card card = game.getCard(ability.getSourceId());
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
for (String s : suggested) {
|
for (String s : suggested) {
|
||||||
|
@ -212,7 +213,7 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
return playables;
|
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()) {
|
if (options.isEmpty()) {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package mage.player.ai;
|
package mage.player.ai;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.ActivatedAbility;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.common.PassAbility;
|
import mage.abilities.common.PassAbility;
|
||||||
import mage.abilities.costs.mana.GenericManaCost;
|
import mage.abilities.costs.mana.GenericManaCost;
|
||||||
|
@ -45,16 +46,16 @@ public class MCTSPlayer extends ComputerPlayer {
|
||||||
return new MCTSPlayer(this);
|
return new MCTSPlayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<Ability> getPlayableAbilities(Game game) {
|
protected List<ActivatedAbility> getPlayableAbilities(Game game) {
|
||||||
List<Ability> playables = getPlayable(game, true);
|
List<ActivatedAbility> playables = getPlayable(game, true);
|
||||||
playables.add(pass);
|
playables.add(pass);
|
||||||
return playables;
|
return playables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Ability> getPlayableOptions(Game game) {
|
public List<Ability> getPlayableOptions(Game game) {
|
||||||
List<Ability> all = new ArrayList<>();
|
List<Ability> all = new ArrayList<>();
|
||||||
List<Ability> playables = getPlayableAbilities(game);
|
List<ActivatedAbility> playables = getPlayableAbilities(game);
|
||||||
for (Ability ability: playables) {
|
for (ActivatedAbility ability: playables) {
|
||||||
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
|
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
|
||||||
if (options.isEmpty()) {
|
if (options.isEmpty()) {
|
||||||
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Ability getAction(Game game) {
|
private Ability getAction(Game game) {
|
||||||
List<Ability> playables = getPlayableAbilities(game);
|
List<ActivatedAbility> playables = getPlayableAbilities(game);
|
||||||
Ability ability;
|
Ability ability;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (playables.size() == 1) {
|
if (playables.size() == 1) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package mage.player.ai;
|
package mage.player.ai;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.ActivatedAbility;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.TriggeredAbility;
|
import mage.abilities.TriggeredAbility;
|
||||||
import mage.abilities.common.PassAbility;
|
import mage.abilities.common.PassAbility;
|
||||||
|
@ -59,10 +60,10 @@ public class SimulatedPlayer extends ComputerPlayer {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void simulateOptions(Game game, Ability previousActions) {
|
protected void simulateOptions(Game game, ActivatedAbility previousActions) {
|
||||||
allActions.add(previousActions);
|
allActions.add(previousActions);
|
||||||
List<Ability> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||||
for (Ability ability: playables) {
|
for (ActivatedAbility ability: playables) {
|
||||||
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
|
List<Ability> options = game.getPlayer(playerId).getPlayableOptions(ability, game);
|
||||||
if (options.isEmpty()) {
|
if (options.isEmpty()) {
|
||||||
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
||||||
|
|
|
@ -1056,7 +1056,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
actingPlayer = game.getPlayer(game.getPriorityPlayerId());
|
actingPlayer = game.getPlayer(game.getPriorityPlayerId());
|
||||||
}
|
}
|
||||||
if (actingPlayer != null) {
|
if (actingPlayer != null) {
|
||||||
useableAbilities = actingPlayer.getUseableActivatedAbilities(object, zone, game);
|
useableAbilities = actingPlayer.getPlayableActivatedAbilities(object, zone, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object instanceof Card
|
if (object instanceof Card
|
||||||
|
|
|
@ -425,7 +425,7 @@ public final class SystemUtil {
|
||||||
game.firePriorityEvent(opponent.getId());
|
game.firePriorityEvent(opponent.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Ability> abilities = opponent.getPlayable(game, true);
|
List<ActivatedAbility> abilities = opponent.getPlayable(game, true);
|
||||||
Map<String, String> choices = new HashMap<>();
|
Map<String, String> choices = new HashMap<>();
|
||||||
abilities.forEach(ability -> {
|
abilities.forEach(ability -> {
|
||||||
MageObject object = ability.getSourceObject(game);
|
MageObject object = ability.getSourceObject(game);
|
||||||
|
@ -437,10 +437,10 @@ public final class SystemUtil {
|
||||||
choice.setKeyChoices(choices);
|
choice.setKeyChoices(choices);
|
||||||
if (feedbackPlayer.choose(Outcome.Detriment, choice, game) && choice.getChoiceKey() != null) {
|
if (feedbackPlayer.choose(Outcome.Detriment, choice, game) && choice.getChoiceKey() != null) {
|
||||||
String needId = choice.getChoiceKey();
|
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()) {
|
if (ability.isPresent()) {
|
||||||
// TODO: set priority for player?
|
// TODO: set priority for player?
|
||||||
ActivatedAbility activatedAbility = (ActivatedAbility) ability.get();
|
ActivatedAbility activatedAbility = ability.get();
|
||||||
game.informPlayers(feedbackPlayer.getLogName() + " as another player " + opponent.getLogName()
|
game.informPlayers(feedbackPlayer.getLogName() + " as another player " + opponent.getLogName()
|
||||||
+ " trying to force an activate ability: " + activatedAbility.getGameLogMessage(game));
|
+ " trying to force an activate ability: " + activatedAbility.getGameLogMessage(game));
|
||||||
if (opponent.activateAbility(activatedAbility, game)) {
|
if (opponent.activateAbility(activatedAbility, game)) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_Flashback_Split() {
|
public void test_Flashback_SplitLeft() {
|
||||||
// {1}{U}
|
// {1}{U}
|
||||||
// When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn.
|
// 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.HAND, playerA, "Snapcaster Mage", 1);
|
||||||
|
@ -67,4 +67,36 @@ public class CastSplitCardsWithFlashbackTest extends CardTestPlayerBase {
|
||||||
assertGraveyardCount(playerB, "Bident of Thassa", 1);
|
assertGraveyardCount(playerB, "Bident of Thassa", 1);
|
||||||
assertPermanentCount(playerB, "Bow of Nylea", 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) {
|
private Ability getAction(Game game) {
|
||||||
List<Ability> playables = getPlayableAbilities(game);
|
List<ActivatedAbility> playables = getPlayableAbilities(game);
|
||||||
Ability ability;
|
Ability ability;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (playables.size() == 1) {
|
if (playables.size() == 1) {
|
||||||
|
@ -115,8 +115,8 @@ public class RandomPlayer extends ComputerPlayer {
|
||||||
return ability;
|
return ability;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<Ability> getPlayableAbilities(Game game) {
|
protected List<ActivatedAbility> getPlayableAbilities(Game game) {
|
||||||
List<Ability> playables = getPlayable(game, true);
|
List<ActivatedAbility> playables = getPlayable(game, true);
|
||||||
playables.add(pass);
|
playables.add(pass);
|
||||||
return playables;
|
return playables;
|
||||||
}
|
}
|
||||||
|
|
|
@ -587,14 +587,14 @@ public class TestPlayer implements Player {
|
||||||
if (groups.length > 2 && !checkExecuteCondition(groups, game)) {
|
if (groups.length > 2 && !checkExecuteCondition(groups, game)) {
|
||||||
break;
|
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])) {
|
if (isAbilityHaveTargetNameOrAlias(game, ability, groups[0])) {
|
||||||
int bookmark = game.bookmarkState();
|
int bookmark = game.bookmarkState();
|
||||||
Ability newAbility = ability.copy();
|
ActivatedAbility newAbility = ability.copy();
|
||||||
if (groups.length > 1 && !groups[1].equals("target=" + NO_TARGET)) {
|
if (groups.length > 1 && !groups[1].equals("target=" + NO_TARGET)) {
|
||||||
groupsForTargetHandling = groups;
|
groupsForTargetHandling = groups;
|
||||||
}
|
}
|
||||||
if (computerPlayer.activateAbility((ActivatedAbility) newAbility, game)) {
|
if (computerPlayer.activateAbility(newAbility, game)) {
|
||||||
actions.remove(action);
|
actions.remove(action);
|
||||||
groupsForTargetHandling = null;
|
groupsForTargetHandling = null;
|
||||||
foundNoAction = 0; // Reset enless loop check because of no action
|
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));
|
System.out.println("Total abilities: " + (abilities != null ? abilities.size() : 0));
|
||||||
if (abilities == null) {
|
if (abilities == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -1714,6 +1714,8 @@ public class TestPlayer implements Player {
|
||||||
private void chooseStrictModeFailed(String choiceType, Game game, String reason, boolean printAbilities) {
|
private void chooseStrictModeFailed(String choiceType, Game game, String reason, boolean printAbilities) {
|
||||||
if (strictChooseMode && !AICanChooseInStrictMode) {
|
if (strictChooseMode && !AICanChooseInStrictMode) {
|
||||||
if (printAbilities) {
|
if (printAbilities) {
|
||||||
|
printStart("Available mana for " + computerPlayer.getName());
|
||||||
|
printMana(game, computerPlayer.getManaAvailable(game));
|
||||||
printStart("Available abilities for " + computerPlayer.getName());
|
printStart("Available abilities for " + computerPlayer.getName());
|
||||||
printAbilities(game, computerPlayer.getPlayable(game, true));
|
printAbilities(game, computerPlayer.getPlayable(game, true));
|
||||||
printEnd();
|
printEnd();
|
||||||
|
@ -2712,8 +2714,8 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LinkedHashMap<UUID, ActivatedAbility> getUseableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
public LinkedHashMap<UUID, ActivatedAbility> getPlayableActivatedAbilities(MageObject object, Zone zone, Game game) {
|
||||||
return computerPlayer.getUseableActivatedAbilities(object, zone, game);
|
return computerPlayer.getPlayableActivatedAbilities(object, zone, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3171,12 +3173,8 @@ public class TestPlayer implements Player {
|
||||||
return computerPlayer.getManaAvailable(game);
|
return computerPlayer.getManaAvailable(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Permanent> getAvailableManaProducersWithCost(Game game) {
|
|
||||||
return computerPlayer.getAvailableManaProducersWithCost(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||||
return computerPlayer.getPlayable(game, hidden);
|
return computerPlayer.getPlayable(game, hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1023,7 +1023,7 @@ public class PlayerStub implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1038,7 +1038,7 @@ public class PlayerStub implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import mage.ObjectColor;
|
||||||
import mage.abilities.*;
|
import mage.abilities.*;
|
||||||
import mage.abilities.hint.Hint;
|
import mage.abilities.hint.Hint;
|
||||||
import mage.abilities.hint.HintUtils;
|
import mage.abilities.hint.HintUtils;
|
||||||
|
import mage.abilities.keyword.FlashbackAbility;
|
||||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||||
import mage.cards.repository.PluginClassloaderRegistery;
|
import mage.cards.repository.PluginClassloaderRegistery;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
|
@ -312,6 +313,21 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
||||||
// dynamic
|
// dynamic
|
||||||
all.addAll(cardState.getAbilities());
|
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;
|
return all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -627,13 +627,13 @@ public interface Player extends MageItem, Copyable<Player> {
|
||||||
|
|
||||||
ManaOptions getManaAvailable(Game game);
|
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);
|
List<Ability> getPlayableOptions(Ability ability, Game game);
|
||||||
|
|
||||||
Map<UUID, Integer> getPlayableObjects(Game game, Zone zone);
|
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);
|
boolean addCounters(Counter counter, Game game);
|
||||||
|
|
||||||
|
|
|
@ -1499,7 +1499,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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<>();
|
LinkedHashMap<UUID, ActivatedAbility> useable = new LinkedHashMap<>();
|
||||||
boolean previousState = game.inCheckPlayableState();
|
boolean previousState = game.inCheckPlayableState();
|
||||||
game.setCheckPlayableState(true);
|
game.setCheckPlayableState(true);
|
||||||
|
@ -1510,12 +1510,33 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect and filter playable activated abilities
|
// collect and filter playable activated abilities
|
||||||
List<Ability> allPlayable = getPlayable(game, true, zone, false);
|
// GUI: user clicks on card, but it must activate ability from any card's parts (main, left, right)
|
||||||
for (Ability ability : allPlayable) {
|
UUID needId1, needId2, needId3;
|
||||||
if (ability instanceof ActivatedAbility) {
|
if (object instanceof SplitCard) {
|
||||||
if (object.hasAbility(ability, game)) {
|
needId1 = object.getId();
|
||||||
useable.putIfAbsent(ability.getId(), (ActivatedAbility) ability);
|
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 {
|
} finally {
|
||||||
|
@ -1524,58 +1545,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
return useable;
|
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) {
|
protected LinkedHashMap<UUID, ActivatedManaAbilityImpl> getUseableManaAbilities(MageObject object, Zone zone, Game game) {
|
||||||
LinkedHashMap<UUID, ActivatedManaAbilityImpl> useable = new LinkedHashMap<>();
|
LinkedHashMap<UUID, ActivatedManaAbilityImpl> useable = new LinkedHashMap<>();
|
||||||
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
boolean canUse = !(object instanceof Permanent) || ((Permanent) object).canUseActivatedAbilities(game);
|
||||||
|
@ -3182,7 +3151,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
return false;
|
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) {
|
if (fromZone == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3190,41 +3168,22 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
// BASIC abilities
|
// BASIC abilities
|
||||||
if (card instanceof SplitCard) {
|
if (card instanceof SplitCard) {
|
||||||
SplitCard splitCard = (SplitCard) card;
|
SplitCard splitCard = (SplitCard) card;
|
||||||
getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), splitCard.getLeftHalfCard().getAbilities(), availableMana, output);
|
getPlayableFromCardSingle(game, fromZone, splitCard.getLeftHalfCard(), getActivatedOnly(splitCard.getLeftHalfCard().getAbilities(game)), availableMana, output);
|
||||||
getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), splitCard.getRightHalfCard().getAbilities(), availableMana, output);
|
getPlayableFromCardSingle(game, fromZone, splitCard.getRightHalfCard(), getActivatedOnly(splitCard.getRightHalfCard().getAbilities(game)), availableMana, output);
|
||||||
getPlayableFromCardSingle(game, fromZone, splitCard, splitCard.getSharedAbilities(game), availableMana, output);
|
getPlayableFromCardSingle(game, fromZone, splitCard, getActivatedOnly(splitCard.getSharedAbilities(game)), availableMana, output);
|
||||||
} else if (card instanceof AdventureCard) {
|
} else if (card instanceof AdventureCard) {
|
||||||
// adventure must use different card characteristics for different spells (main or adventure)
|
// adventure must use different card characteristics for different spells (main or adventure)
|
||||||
AdventureCard adventureCard = (AdventureCard) card;
|
AdventureCard adventureCard = (AdventureCard) card;
|
||||||
getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), adventureCard.getSpellCard().getAbilities(), availableMana, output);
|
getPlayableFromCardSingle(game, fromZone, adventureCard.getSpellCard(), getActivatedOnly(adventureCard.getSpellCard().getAbilities(game)), availableMana, output);
|
||||||
getPlayableFromCardSingle(game, fromZone, adventureCard, adventureCard.getSharedAbilities(game), availableMana, output);
|
getPlayableFromCardSingle(game, fromZone, adventureCard, getActivatedOnly(adventureCard.getSharedAbilities(game)), availableMana, output);
|
||||||
} else {
|
} else {
|
||||||
getPlayableFromCardSingle(game, fromZone, card, card.getAbilities(game), availableMana, output);
|
getPlayableFromCardSingle(game, fromZone, card, getActivatedOnly(card.getAbilities(game)), availableMana, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DYNAMIC ADDED abilities
|
// DYNAMIC ADDED abilities are adds in getAbilities(game)
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
// 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)) {
|
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
|
||||||
boolean isPlaySpell = (ability instanceof SpellAbility);
|
boolean isPlaySpell = (ability instanceof SpellAbility);
|
||||||
|
@ -3300,12 +3259,12 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Ability> getPlayable(Game game, boolean hidden) {
|
public List<ActivatedAbility> getPlayable(Game game, boolean hidden) {
|
||||||
return getPlayable(game, hidden, Zone.ALL, true);
|
return getPlayable(game, hidden, Zone.ALL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Ability> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
|
public List<ActivatedAbility> getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
|
||||||
List<Ability> playable = new ArrayList<>();
|
List<ActivatedAbility> playable = new ArrayList<>();
|
||||||
if (shouldSkipGettingPlayable(game)) {
|
if (shouldSkipGettingPlayable(game)) {
|
||||||
return playable;
|
return playable;
|
||||||
}
|
}
|
||||||
|
@ -3402,22 +3361,20 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// eliminate duplicate activated abilities (uses for AI plays)
|
// eliminate duplicate activated abilities (uses for AI plays)
|
||||||
Map<String, Ability> activatedUnique = new HashMap<>();
|
Map<String, ActivatedAbility> activatedUnique = new HashMap<>();
|
||||||
List<Ability> activatedAll = new ArrayList<>();
|
List<ActivatedAbility> activatedAll = new ArrayList<>();
|
||||||
|
|
||||||
// activated abilities from battlefield objects
|
// activated abilities from battlefield objects
|
||||||
if (fromAll || fromZone == Zone.BATTLEFIELD) {
|
if (fromAll || fromZone == Zone.BATTLEFIELD) {
|
||||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
|
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
|
||||||
List<Ability> battlePlayable = new ArrayList<>();
|
List<ActivatedAbility> battlePlayable = new ArrayList<>();
|
||||||
getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
|
getPlayableFromCardAll(game, Zone.BATTLEFIELD, permanent, availableMana, battlePlayable);
|
||||||
for (Ability ability : battlePlayable) {
|
for (ActivatedAbility ability : battlePlayable) {
|
||||||
if (ability instanceof ActivatedAbility) {
|
|
||||||
activatedUnique.putIfAbsent(ability.toString(), ability);
|
activatedUnique.putIfAbsent(ability.toString(), ability);
|
||||||
activatedAll.add(ability);
|
activatedAll.add(ability);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// activated abilities from stack objects
|
// activated abilities from stack objects
|
||||||
if (fromAll || fromZone == Zone.STACK) {
|
if (fromAll || fromZone == Zone.STACK) {
|
||||||
|
@ -3467,7 +3424,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Map<UUID, Integer> getPlayableObjects(Game game, Zone zone) {
|
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<>();
|
Map<UUID, Integer> playableObjects = new HashMap<>();
|
||||||
for (Ability ability : playableAbilities) {
|
for (Ability ability : playableAbilities) {
|
||||||
if (ability.getSourceId() != null) {
|
if (ability.getSourceId() != null) {
|
||||||
|
@ -3518,7 +3475,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
@Override
|
@Override
|
||||||
public List<Ability> getPlayableOptions(Ability ability, Game game) {
|
public List<Ability> getPlayableOptions(Ability ability, Game game) {
|
||||||
List<Ability> options = new ArrayList<>();
|
List<Ability> options = new ArrayList<>();
|
||||||
|
|
||||||
if (ability.isModal()) {
|
if (ability.isModal()) {
|
||||||
addModeOptions(options, ability, game);
|
addModeOptions(options, ability, game);
|
||||||
} else if (!ability.getTargets().getUnchosen().isEmpty()) {
|
} else if (!ability.getTargets().getUnchosen().isEmpty()) {
|
||||||
|
|
Loading…
Reference in a new issue