mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
- Better AI for fetching cards.
This commit is contained in:
@@ -7,7 +7,6 @@ K:Trample
|
|||||||
T:Mode$ Attacks | ValidCard$ Card.Self | DelayedTrigger$ DelTrig | TriggerDescription$ When CARDNAME attacks, sacrifice it at end of combat.
|
T:Mode$ Attacks | ValidCard$ Card.Self | DelayedTrigger$ DelTrig | TriggerDescription$ When CARDNAME attacks, sacrifice it at end of combat.
|
||||||
SVar:DelTrig:Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | Execute$ TrigSacrifice | TriggerDescription$ Sacrifice CARDNAME at end of combat.
|
SVar:DelTrig:Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | Execute$ TrigSacrifice | TriggerDescription$ Sacrifice CARDNAME at end of combat.
|
||||||
SVar:TrigSacrifice:AB$ Sacrifice | Cost$ 0 | Defined$ Self
|
SVar:TrigSacrifice:AB$ Sacrifice | Cost$ 0 | Defined$ Self
|
||||||
SVar:RemAIDeck:True
|
|
||||||
SVar:Rarity:Uncommon
|
SVar:Rarity:Uncommon
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/crumbling_colossus.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/crumbling_colossus.jpg
|
||||||
SetInfo:M12|Uncommon|http://magiccards.info/scans/en/m12/204.jpg
|
SetInfo:M12|Uncommon|http://magiccards.info/scans/en/m12/204.jpg
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ ManaCost:2 B B
|
|||||||
Types:Sorcery
|
Types:Sorcery
|
||||||
Text:no text
|
Text:no text
|
||||||
A:SP$ ChangeZone | Cost$ 2 B B | Origin$ Library | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SpellDescription$ Search your library for a card and put that card into your hand. Then shuffle your library.
|
A:SP$ ChangeZone | Cost$ 2 B B | Origin$ Library | Destination$ Hand | ChangeType$ Card | ChangeNum$ 1 | Mandatory$ True | SpellDescription$ Search your library for a card and put that card into your hand. Then shuffle your library.
|
||||||
SVar:RemAIDeck:True
|
|
||||||
SVar:Rarity:Uncommon
|
SVar:Rarity:Uncommon
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/diabolic_tutor.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/diabolic_tutor.jpg
|
||||||
SetInfo:8ED|Uncommon|http://magiccards.info/scans/en/8e/128.jpg
|
SetInfo:8ED|Uncommon|http://magiccards.info/scans/en/8e/128.jpg
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ ManaCost:G
|
|||||||
Types:Instant
|
Types:Instant
|
||||||
Text:no text
|
Text:no text
|
||||||
A:SP$ ChangeZone | Cost$ G | Origin$ Graveyard | Destination$ Library | LibraryPosition$ 0 | TgtPrompt$ Choose target card in your graveyard | ValidTgts$ Card.YouCtrl | SpellDescription$ Put target card from your graveyard on top of your library.
|
A:SP$ ChangeZone | Cost$ G | Origin$ Graveyard | Destination$ Library | LibraryPosition$ 0 | TgtPrompt$ Choose target card in your graveyard | ValidTgts$ Card.YouCtrl | SpellDescription$ Put target card from your graveyard on top of your library.
|
||||||
SVar:RemAIDeck:True
|
|
||||||
SVar:Rarity:Common
|
SVar:Rarity:Common
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/reclaim.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/reclaim.jpg
|
||||||
SetInfo:EXO|Common|http://magiccards.info/scans/en/ex/120.jpg
|
SetInfo:EXO|Common|http://magiccards.info/scans/en/ex/120.jpg
|
||||||
|
|||||||
@@ -43,9 +43,11 @@ import forge.card.spellability.SpellAbility;
|
|||||||
import forge.card.spellability.SpellAbilityStackInstance;
|
import forge.card.spellability.SpellAbilityStackInstance;
|
||||||
import forge.card.spellability.SpellPermanent;
|
import forge.card.spellability.SpellPermanent;
|
||||||
import forge.card.spellability.Target;
|
import forge.card.spellability.Target;
|
||||||
|
import forge.game.phase.Combat;
|
||||||
import forge.game.phase.CombatUtil;
|
import forge.game.phase.CombatUtil;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.ComputerUtil;
|
import forge.game.player.ComputerUtil;
|
||||||
|
import forge.game.player.ComputerUtilBlock;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.zone.PlayerZone;
|
import forge.game.zone.PlayerZone;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
@@ -1111,12 +1113,14 @@ public final class AbilityFactoryChangeZone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Improve the AI for fetching.
|
// Improve the AI for fetching.
|
||||||
Card c;
|
Card c = null;
|
||||||
if (params.containsKey("AtRandom")) {
|
if (params.containsKey("AtRandom")) {
|
||||||
c = CardUtil.getRandom(fetchList.toArray());
|
c = CardUtil.getRandom(fetchList.toArray());
|
||||||
} else if (defined) {
|
} else if (defined) {
|
||||||
c = fetchList.get(0);
|
c = fetchList.get(0);
|
||||||
} else {
|
} else {
|
||||||
|
fetchList.shuffle();
|
||||||
|
// Save a card as a default, in case we can't find anything suitable.
|
||||||
Card first = fetchList.get(0);
|
Card first = fetchList.get(0);
|
||||||
fetchList = fetchList.filter(new CardListFilter() {
|
fetchList = fetchList.filter(new CardListFilter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -1130,19 +1134,15 @@ public final class AbilityFactoryChangeZone {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (type.contains("Basic")) {
|
if (type.contains("Basic")) {
|
||||||
c = AbilityFactoryChangeZone.basicManaFixing(fetchList);
|
c = AbilityFactoryChangeZone.basicManaFixing(fetchList);
|
||||||
} else if (AbilityFactoryChangeZone.areAllBasics(type)) {
|
} else if (AbilityFactoryChangeZone.areAllBasics(type)) {
|
||||||
c = AbilityFactoryChangeZone.basicManaFixing(fetchList, type);
|
c = AbilityFactoryChangeZone.basicManaFixing(fetchList, type);
|
||||||
} else if (fetchList.getNotType("Creature").size() == 0) {
|
} else if (fetchList.getNotType("Creature").size() == 0) {
|
||||||
c = CardFactoryUtil.getBestCreatureAI(fetchList); // if only
|
c = AbilityFactoryChangeZone.chooseCreature(fetchList);
|
||||||
// creatures
|
|
||||||
// take the
|
|
||||||
// best
|
|
||||||
} else if (ZoneType.Battlefield.equals(destination) || ZoneType.Graveyard.equals(destination)) {
|
} else if (ZoneType.Battlefield.equals(destination) || ZoneType.Graveyard.equals(destination)) {
|
||||||
c = CardFactoryUtil.getMostExpensivePermanentAI(fetchList, sa, false);
|
c = CardFactoryUtil.getMostExpensivePermanentAI(fetchList, sa, false);
|
||||||
} else if (ZoneType.Exile.equals(destination)) {
|
} else if (ZoneType.Exile.equals(destination)) {
|
||||||
// Exiling your own stuff, if Exiling opponents stuff choose
|
// Exiling your own stuff, if Exiling opponents stuff choose best
|
||||||
// best
|
|
||||||
if (destZone.getPlayer().isHuman()) {
|
if (destZone.getPlayer().isHuman()) {
|
||||||
c = CardFactoryUtil.getMostExpensivePermanentAI(fetchList, sa, false);
|
c = CardFactoryUtil.getMostExpensivePermanentAI(fetchList, sa, false);
|
||||||
} else {
|
} else {
|
||||||
@@ -1153,13 +1153,49 @@ public final class AbilityFactoryChangeZone {
|
|||||||
if (origin.contains(ZoneType.Library) && !fetchList.getNotName(card.getName()).isEmpty()) {
|
if (origin.contains(ZoneType.Library) && !fetchList.getNotName(card.getName()).isEmpty()) {
|
||||||
fetchList = fetchList.getNotName(card.getName());
|
fetchList = fetchList.getNotName(card.getName());
|
||||||
}
|
}
|
||||||
fetchList.shuffle();
|
Player ai = AllZone.getComputerPlayer();
|
||||||
c = fetchList.get(0);
|
// Does AI need a land?
|
||||||
|
CardList hand = ai.getCardsIn(ZoneType.Hand);
|
||||||
|
System.out.println("Lands in hand = " + hand.filter(CardListFilter.LANDS).size() + ", on battlefield = " + ai.getCardsIn(ZoneType.Battlefield).filter(CardListFilter.LANDS).size());
|
||||||
|
if (hand.filter(CardListFilter.LANDS).size() == 0 && ai.getCardsIn(ZoneType.Battlefield).filter(CardListFilter.LANDS).size() < 4) {
|
||||||
|
boolean canCastSomething = false;
|
||||||
|
for (Card cardInHand : hand) {
|
||||||
|
canCastSomething |= ComputerUtil.payManaCost(cardInHand.getFirstSpellAbility(), AllZone.getComputerPlayer(), true, 0, false);
|
||||||
|
}
|
||||||
|
if (!canCastSomething) {
|
||||||
|
System.out.println("Pulling a land as there are none in hand, less than 4 on the board, and nothing in hand is castable.");
|
||||||
|
c = basicManaFixing(fetchList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c == null) {
|
||||||
|
System.out.println("Don't need a land or none available; trying for a creature.");
|
||||||
|
fetchList = fetchList.getNotType("Land");
|
||||||
|
// Prefer to pull a creature, generally more useful for AI.
|
||||||
|
c = chooseCreature(fetchList.getType("Creature"));
|
||||||
|
}
|
||||||
|
if (c == null) { // Could not find a creature.
|
||||||
|
if (ai.getLife() <= 5) { // Desperate?
|
||||||
|
// Get something AI can cast soon.
|
||||||
|
System.out.println("5 Life or less, trying to find something castable.");
|
||||||
|
CardListUtil.sortByMostExpensive(fetchList);
|
||||||
|
for (Card potentialCard : fetchList) {
|
||||||
|
if (ComputerUtil.payManaCost(potentialCard.getFirstSpellAbility(), AllZone.getComputerPlayer(), true, 0, false)) {
|
||||||
|
c = potentialCard;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get the best card in there.
|
||||||
|
System.out.println("No creature and lots of life, finding something good.");
|
||||||
|
c = CardFactoryUtil.getBestAI(fetchList);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
c = first;
|
c = first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
System.out.println("Chose " + c.toString());
|
||||||
|
|
||||||
fetched.add(c);
|
fetched.add(c);
|
||||||
fetchList.remove(c);
|
fetchList.remove(c);
|
||||||
@@ -1336,6 +1372,38 @@ public final class AbilityFactoryChangeZone {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some logic for picking a creature card from a list.
|
||||||
|
* @param list
|
||||||
|
* @return Card
|
||||||
|
*/
|
||||||
|
private static Card chooseCreature(CardList list) {
|
||||||
|
Card card = null;
|
||||||
|
Combat combat = new Combat();
|
||||||
|
combat.initiatePossibleDefenders(AllZone.getComputerPlayer());
|
||||||
|
CardList attackers = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
|
||||||
|
for (Card att : attackers) {
|
||||||
|
combat.addAttacker(att);
|
||||||
|
}
|
||||||
|
combat = ComputerUtilBlock.getBlockers(combat, AllZoneUtil.getCreaturesInPlay(AllZone.getComputerPlayer()));
|
||||||
|
|
||||||
|
System.out.println("Life would remain = " + CombatUtil.lifeThatWouldRemain(combat));
|
||||||
|
if (CombatUtil.lifeInDanger(combat)) {
|
||||||
|
// need something AI can cast now
|
||||||
|
CardListUtil.sortByEvaluateCreature(list);
|
||||||
|
for (Card c : list) {
|
||||||
|
if (ComputerUtil.payManaCost(c.getFirstSpellAbility(), AllZone.getComputerPlayer(), true, 0, false)) {
|
||||||
|
card = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// not urgent, get the largest creature possible
|
||||||
|
card = CardFactoryUtil.getBestCreatureAI(list);
|
||||||
|
}
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
// *************************************************************************************
|
// *************************************************************************************
|
||||||
// **************** Known Origin (Battlefield/Graveyard/Exile)
|
// **************** Known Origin (Battlefield/Graveyard/Exile)
|
||||||
@@ -1508,6 +1576,7 @@ public final class AbilityFactoryChangeZone {
|
|||||||
|
|
||||||
CardList list = AllZoneUtil.getCardsIn(origin);
|
CardList list = AllZoneUtil.getCardsIn(origin);
|
||||||
list = list.getValidCards(tgt.getValidTgts(), AllZone.getComputerPlayer(), source);
|
list = list.getValidCards(tgt.getValidTgts(), AllZone.getComputerPlayer(), source);
|
||||||
|
list = list.getNotName(source.getName()); // Don't get the same card back.
|
||||||
|
|
||||||
if (list.size() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
|
if (list.size() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1523,7 +1592,6 @@ public final class AbilityFactoryChangeZone {
|
|||||||
aiPermanents = aiPermanents.filter(new CardListFilter() {
|
aiPermanents = aiPermanents.filter(new CardListFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean addCard(final Card c) {
|
public boolean addCard(final Card c) {
|
||||||
System.out.println("Not Changing Zone");
|
|
||||||
return !c.getSVar("Targeting").equals("Dies");
|
return !c.getSVar("Targeting").equals("Dies");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1574,15 +1642,8 @@ public final class AbilityFactoryChangeZone {
|
|||||||
}
|
}
|
||||||
// counters TODO check good and
|
// counters TODO check good and
|
||||||
// bad counters
|
// bad counters
|
||||||
return SpellPermanent.checkETBEffects(c, null, null); // checks
|
// checks only if there is a dangerous ETB effect
|
||||||
// only
|
return SpellPermanent.checkETBEffects(c, null, null);
|
||||||
// if
|
|
||||||
// there
|
|
||||||
// is
|
|
||||||
// a
|
|
||||||
// dangerous
|
|
||||||
// ETB
|
|
||||||
// effect
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!aiPermanents.isEmpty()) {
|
if (!aiPermanents.isEmpty()) {
|
||||||
@@ -1666,11 +1727,34 @@ public final class AbilityFactoryChangeZone {
|
|||||||
} else {
|
} else {
|
||||||
choice = mostExpensive;
|
choice = mostExpensive;
|
||||||
}
|
}
|
||||||
|
} else if (destination.equals(ZoneType.Hand) || destination.equals(ZoneType.Library)) {
|
||||||
|
CardList nonLands = list.getNotType("Land");
|
||||||
|
// Prefer to pull a creature, generally more useful for AI.
|
||||||
|
choice = chooseCreature(nonLands.getType("Creature"));
|
||||||
|
if (choice == null) { // Could not find a creature.
|
||||||
|
if (AllZone.getComputerPlayer().getLife() <= 5) { // Desperate?
|
||||||
|
// Get something AI can cast soon.
|
||||||
|
System.out.println("5 Life or less, trying to find something castable.");
|
||||||
|
CardListUtil.sortByMostExpensive(nonLands);
|
||||||
|
for (Card potentialCard : nonLands) {
|
||||||
|
if (ComputerUtil.payManaCost(potentialCard.getFirstSpellAbility(), AllZone.getComputerPlayer(), true, 0, false)) {
|
||||||
|
choice = potentialCard;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get the best card in there.
|
||||||
|
System.out.println("No creature and lots of life, finding something good.");
|
||||||
|
choice = CardFactoryUtil.getBestAI(nonLands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (choice == null) {
|
||||||
|
// No creatures or spells?
|
||||||
|
list.shuffle();
|
||||||
|
choice = list.get(0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO AI needs more improvement to it's retrieval (reuse
|
choice = CardFactoryUtil.getBestAI(list);
|
||||||
// some code from spReturn here)
|
|
||||||
list.shuffle();
|
|
||||||
choice = list.get(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
@@ -1680,7 +1764,9 @@ public final class AbilityFactoryChangeZone {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
if (!ComputerUtil.shouldCastLessThanMax(source)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1691,7 +1777,7 @@ public final class AbilityFactoryChangeZone {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* changeKnownUnpreferredTarget.
|
* changeKnownUnpreferredTarget.
|
||||||
@@ -1754,11 +1840,34 @@ public final class AbilityFactoryChangeZone {
|
|||||||
choice = CardFactoryUtil.getBestCreatureToBounceAI(list);
|
choice = CardFactoryUtil.getBestCreatureToBounceAI(list);
|
||||||
} else if (destination.equals(ZoneType.Battlefield) || origin.equals(ZoneType.Battlefield)) {
|
} else if (destination.equals(ZoneType.Battlefield) || origin.equals(ZoneType.Battlefield)) {
|
||||||
choice = CardFactoryUtil.getMostExpensivePermanentAI(list, sa, false);
|
choice = CardFactoryUtil.getMostExpensivePermanentAI(list, sa, false);
|
||||||
|
} else if (destination.equals(ZoneType.Hand) || destination.equals(ZoneType.Library)) {
|
||||||
|
CardList nonLands = list.getNotType("Land");
|
||||||
|
// Prefer to pull a creature, generally more useful for AI.
|
||||||
|
choice = chooseCreature(nonLands.getType("Creature"));
|
||||||
|
if (choice == null) { // Could not find a creature.
|
||||||
|
if (AllZone.getComputerPlayer().getLife() <= 5) { // Desperate?
|
||||||
|
// Get something AI can cast soon.
|
||||||
|
System.out.println("5 Life or less, trying to find something castable.");
|
||||||
|
CardListUtil.sortByMostExpensive(nonLands);
|
||||||
|
for (Card potentialCard : nonLands) {
|
||||||
|
if (ComputerUtil.payManaCost(potentialCard.getFirstSpellAbility(), AllZone.getComputerPlayer(), true, 0, false)) {
|
||||||
|
choice = potentialCard;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get the best card in there.
|
||||||
|
System.out.println("No creature and lots of life, finding something good.");
|
||||||
|
choice = CardFactoryUtil.getBestAI(nonLands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (choice == null) {
|
||||||
|
// No creatures or spells?
|
||||||
|
list.shuffle();
|
||||||
|
choice = list.get(0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO AI needs more improvement to it's retrieval (reuse
|
choice = CardFactoryUtil.getBestAI(list);
|
||||||
// some code from spReturn here)
|
|
||||||
list.shuffle();
|
|
||||||
choice = list.get(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (choice == null) { // can't find anything left
|
if (choice == null) { // can't find anything left
|
||||||
@@ -1766,7 +1875,9 @@ public final class AbilityFactoryChangeZone {
|
|||||||
tgt.resetTargets();
|
tgt.resetTargets();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// TODO is this good enough? for up to amounts?
|
if (!ComputerUtil.shouldCastLessThanMax(source)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,9 @@ import forge.card.spellability.AbilitySub;
|
|||||||
import forge.card.spellability.Spell;
|
import forge.card.spellability.Spell;
|
||||||
import forge.card.spellability.SpellAbility;
|
import forge.card.spellability.SpellAbility;
|
||||||
import forge.card.spellability.Target;
|
import forge.card.spellability.Target;
|
||||||
import forge.game.phase.Combat;
|
|
||||||
import forge.game.phase.CombatUtil;
|
|
||||||
import forge.game.phase.PhaseHandler;
|
import forge.game.phase.PhaseHandler;
|
||||||
import forge.game.phase.PhaseType;
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.ComputerUtil;
|
import forge.game.player.ComputerUtil;
|
||||||
import forge.game.player.ComputerUtilBlock;
|
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.gui.GuiUtils;
|
import forge.gui.GuiUtils;
|
||||||
@@ -1018,7 +1015,7 @@ public class AbilityFactoryPermanentState {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!tapCastLessThanMax(source)) {
|
if (!ComputerUtil.shouldCastLessThanMax(source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1044,7 +1041,7 @@ public class AbilityFactoryPermanentState {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!tapCastLessThanMax(source)) {
|
if (!ComputerUtil.shouldCastLessThanMax(source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1058,31 +1055,6 @@ public class AbilityFactoryPermanentState {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Is it OK to cast this for less than the Max Targets?
|
|
||||||
* @param source
|
|
||||||
*/
|
|
||||||
private static boolean tapCastLessThanMax(final Card source) {
|
|
||||||
boolean ret = true;
|
|
||||||
if (source.getManaCost().countX() > 0) {
|
|
||||||
// If TargetMax is MaxTgts (i.e., an "X" cost), this is fine because AI is limited by mana available.
|
|
||||||
} else {
|
|
||||||
// Otherwise, if life is possibly in danger, then this is fine.
|
|
||||||
Combat combat = new Combat();
|
|
||||||
combat.initiatePossibleDefenders(AllZone.getComputerPlayer());
|
|
||||||
CardList attackers = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
|
|
||||||
for (Card att : attackers) {
|
|
||||||
combat.addAttacker(att);
|
|
||||||
}
|
|
||||||
combat = ComputerUtilBlock.getBlockers(combat, AllZoneUtil.getCreaturesInPlay(AllZone.getComputerPlayer()));
|
|
||||||
if (!CombatUtil.lifeInDanger(combat)) {
|
|
||||||
// Otherwise, return false. Do not play now.
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* tapUnpreferredTargeting.
|
* tapUnpreferredTargeting.
|
||||||
@@ -1168,7 +1140,7 @@ public class AbilityFactoryPermanentState {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!tapCastLessThanMax(source)) {
|
if (!ComputerUtil.shouldCastLessThanMax(source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1189,7 +1161,7 @@ public class AbilityFactoryPermanentState {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (!tapCastLessThanMax(source)) {
|
if (!ComputerUtil.shouldCastLessThanMax(source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ public class CardFactoryInstants {
|
|||||||
public boolean canPlayAI() {
|
public boolean canPlayAI() {
|
||||||
final CardList graveList = AllZone.getHumanPlayer().getCardsIn(ZoneType.Graveyard);
|
final CardList graveList = AllZone.getHumanPlayer().getCardsIn(ZoneType.Graveyard);
|
||||||
|
|
||||||
final int maxX = ComputerUtil.getAvailableMana().size() - 1;
|
final int maxX = ComputerUtil.getAvailableMana(true).size() - 1;
|
||||||
return (maxX >= 3) && (graveList.size() > 0);
|
return (maxX >= 3) && (graveList.size() > 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ public class CardFactorySorceries {
|
|||||||
|
|
||||||
// the computer will at least destroy 2 more human creatures
|
// the computer will at least destroy 2 more human creatures
|
||||||
return ((computer.size() < (human.size() - 1)) || ((AllZone.getComputerPlayer().getLife() < 7) && !human
|
return ((computer.size() < (human.size() - 1)) || ((AllZone.getComputerPlayer().getLife() < 7) && !human
|
||||||
.isEmpty())) && (ComputerUtil.getAvailableMana().size() >= 7);
|
.isEmpty())) && (ComputerUtil.getAvailableMana(true).size() >= 7);
|
||||||
}
|
}
|
||||||
}; // SpellAbility
|
}; // SpellAbility
|
||||||
|
|
||||||
@@ -905,7 +905,7 @@ public class CardFactorySorceries {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPlayAI() {
|
public boolean canPlayAI() {
|
||||||
final int maxX = ComputerUtil.getAvailableMana().size() - 1;
|
final int maxX = ComputerUtil.getAvailableMana(true).size() - 1;
|
||||||
final int humanLife = AllZone.getHumanPlayer().getLife();
|
final int humanLife = AllZone.getHumanPlayer().getLife();
|
||||||
if (maxX >= humanLife) {
|
if (maxX >= humanLife) {
|
||||||
targetPlayers.add(AllZone.getHumanPlayer());
|
targetPlayers.add(AllZone.getHumanPlayer());
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
if (sa instanceof AbilityStatic) {
|
if (sa instanceof AbilityStatic) {
|
||||||
final Cost cost = sa.getPayCosts();
|
final Cost cost = sa.getPayCosts();
|
||||||
if (cost == null && ComputerUtil.payManaCost(sa, AllZone.getComputerPlayer(), false, 0)) {
|
if (cost == null && ComputerUtil.payManaCost(sa, AllZone.getComputerPlayer(), false, 0, true)) {
|
||||||
sa.resolve();
|
sa.resolve();
|
||||||
} else {
|
} else {
|
||||||
final CostPayment pay = new CostPayment(cost, sa);
|
final CostPayment pay = new CostPayment(cost, sa);
|
||||||
@@ -473,7 +473,7 @@ public class ComputerUtil {
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public static boolean canPayCost(final SpellAbility sa, final Player player) {
|
public static boolean canPayCost(final SpellAbility sa, final Player player) {
|
||||||
if (!ComputerUtil.payManaCost(sa, player, true, 0)) {
|
if (!ComputerUtil.payManaCost(sa, player, true, 0, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +510,7 @@ public class ComputerUtil {
|
|||||||
int xMana = 0;
|
int xMana = 0;
|
||||||
|
|
||||||
for (int i = 1; i < 99; i++) {
|
for (int i = 1; i < 99; i++) {
|
||||||
if (!ComputerUtil.payManaCost(sa, player, true, i)) {
|
if (!ComputerUtil.payManaCost(sa, player, true, i, true)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
xMana = i;
|
xMana = i;
|
||||||
@@ -563,7 +563,7 @@ public class ComputerUtil {
|
|||||||
* a {@link forge.card.spellability.SpellAbility} object.
|
* a {@link forge.card.spellability.SpellAbility} object.
|
||||||
*/
|
*/
|
||||||
public static void payManaCost(final SpellAbility sa) {
|
public static void payManaCost(final SpellAbility sa) {
|
||||||
ComputerUtil.payManaCost(sa, AllZone.getComputerPlayer(), false, 0);
|
ComputerUtil.payManaCost(sa, AllZone.getComputerPlayer(), false, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -579,67 +579,17 @@ public class ComputerUtil {
|
|||||||
* (is for canPayCost, if true does not change the game state)
|
* (is for canPayCost, if true does not change the game state)
|
||||||
* @param extraMana
|
* @param extraMana
|
||||||
* a int.
|
* a int.
|
||||||
|
* @param checkPlayable
|
||||||
|
* should we check if playable? use for hypothetical "can AI play this"
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
* @since 1.0.15
|
* @since 1.0.15
|
||||||
*/
|
*/
|
||||||
public static boolean payManaCost(final SpellAbility sa, final Player player, final boolean test,
|
public static boolean payManaCost(final SpellAbility sa, final Player player, final boolean test,
|
||||||
final int extraMana) {
|
final int extraMana, boolean checkPlayable) {
|
||||||
final String mana = sa.getPayCosts() != null ? sa.getPayCosts().getTotalMana() : sa.getManaCost();
|
ManaCost cost = calculateManaCost(sa, test, extraMana);
|
||||||
|
|
||||||
ManaCost cost = new ManaCost(mana);
|
|
||||||
|
|
||||||
cost = Singletons.getModel().getGameAction().getSpellCostChange(sa, cost);
|
|
||||||
|
|
||||||
final ManaPool manapool = player.getManaPool();
|
final ManaPool manapool = player.getManaPool();
|
||||||
|
|
||||||
final Card card = sa.getSourceCard();
|
|
||||||
// Tack xMana Payments into mana here if X is a set value
|
|
||||||
if ((sa.getPayCosts() != null) && (cost.getXcounter() > 0)) {
|
|
||||||
|
|
||||||
int manaToAdd = 0;
|
|
||||||
if (test && (extraMana > 0)) {
|
|
||||||
manaToAdd = extraMana * cost.getXcounter();
|
|
||||||
} else {
|
|
||||||
// For Count$xPaid set PayX in the AFs then use that here
|
|
||||||
// Else calculate it as appropriate.
|
|
||||||
final String xSvar = card.getSVar("X").startsWith("Count$xPaid") ? "PayX" : "X";
|
|
||||||
if (!card.getSVar(xSvar).equals("")) {
|
|
||||||
if (xSvar.equals("PayX")) {
|
|
||||||
manaToAdd = Integer.parseInt(card.getSVar(xSvar)) * cost.getXcounter(); // X
|
|
||||||
} else {
|
|
||||||
manaToAdd = AbilityFactory.calculateAmount(card, xSvar, sa) * cost.getXcounter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cost.increaseColorlessMana(manaToAdd);
|
|
||||||
if (!test) {
|
|
||||||
card.setXManaCostPaid(manaToAdd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make mana needed to avoid negative effect a mandatory cost for the AI
|
|
||||||
if (card.getSVar("ManaNeededToAvoidNegativeEffect") != "") {
|
|
||||||
final String[] negEffects = card.getSVar("ManaNeededToAvoidNegativeEffect").split(",");
|
|
||||||
int amountAdded = 0;
|
|
||||||
for (int nStr = 0; nStr < negEffects.length; nStr++) {
|
|
||||||
// convert long color strings to short color strings
|
|
||||||
if (negEffects[nStr].length() > 1) {
|
|
||||||
negEffects[nStr] = InputPayManaCostUtil.getShortColorString(negEffects[nStr]);
|
|
||||||
}
|
|
||||||
// make mana mandatory for AI
|
|
||||||
if (!cost.isColor(negEffects[nStr])) {
|
|
||||||
cost.combineManaCost(negEffects[nStr]);
|
|
||||||
amountAdded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cost.setManaNeededToAvoidNegativeEffect(negEffects);
|
|
||||||
// TODO: should it be an error condition if amountAdded is greater
|
|
||||||
// than the colorless in the original cost? (ArsenalNut - 120102)
|
|
||||||
// adjust colorless amount to account for added mana
|
|
||||||
cost.decreaseColorlessMana(amountAdded);
|
|
||||||
}
|
|
||||||
|
|
||||||
cost = manapool.payManaFromPool(sa, cost);
|
cost = manapool.payManaFromPool(sa, cost);
|
||||||
|
|
||||||
if (cost.isPaid()) {
|
if (cost.isPaid()) {
|
||||||
@@ -649,82 +599,12 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get map of mana abilities
|
// get map of mana abilities
|
||||||
final HashMap<String, ArrayList<AbilityMana>> manaAbilityMap = ComputerUtil.mapManaSources(player);
|
final HashMap<String, ArrayList<AbilityMana>> manaAbilityMap = ComputerUtil.mapManaSources(player, checkPlayable);
|
||||||
// initialize ArrayList list for mana needed
|
// initialize ArrayList list for mana needed
|
||||||
final ArrayList<ArrayList<AbilityMana>> partSources = new ArrayList<ArrayList<AbilityMana>>();
|
final ArrayList<ArrayList<AbilityMana>> partSources = new ArrayList<ArrayList<AbilityMana>>();
|
||||||
final ArrayList<Integer> partPriority = new ArrayList<Integer>();
|
final ArrayList<Integer> partPriority = new ArrayList<Integer>();
|
||||||
final String[] costParts = cost.toString().replace("X ", "").replace("P", "").split(" ");
|
final String[] costParts = cost.toString().replace("X ", "").replace("P", "").split(" ");
|
||||||
Boolean foundAllSources = true;
|
Boolean foundAllSources = findManaSources(manaAbilityMap, partSources, partPriority, costParts);
|
||||||
if (manaAbilityMap.isEmpty()) {
|
|
||||||
foundAllSources = false;
|
|
||||||
} else {
|
|
||||||
final String[] shortColors = { "W", "U", "B", "R", "G" };
|
|
||||||
// loop over cost parts
|
|
||||||
for (int nPart = 0; nPart < costParts.length; nPart++) {
|
|
||||||
final ArrayList<AbilityMana> srcFound = new ArrayList<AbilityMana>();
|
|
||||||
// Test for:
|
|
||||||
// 1) Colorless
|
|
||||||
// 2) Split e.g. 2/G
|
|
||||||
// 3) Hybrid e.g. U/G
|
|
||||||
// defaults to single short color
|
|
||||||
if (costParts[nPart].matches("[0-9]+")) { // Colorless
|
|
||||||
srcFound.addAll(manaAbilityMap.get("1"));
|
|
||||||
} else if (costParts[nPart].contains("2/")) { // Split
|
|
||||||
final String colorKey = costParts[nPart].replace("2/", "");
|
|
||||||
// add specified color sources first
|
|
||||||
if (manaAbilityMap.containsKey(colorKey)) {
|
|
||||||
srcFound.addAll(manaAbilityMap.get(colorKey));
|
|
||||||
}
|
|
||||||
// add other available colors
|
|
||||||
for (final String color : shortColors) {
|
|
||||||
if (!colorKey.contains(color)) {
|
|
||||||
// Is source available?
|
|
||||||
if (manaAbilityMap.containsKey(color)) {
|
|
||||||
srcFound.addAll(manaAbilityMap.get(color));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (costParts[nPart].length() > 1) { // Hybrid
|
|
||||||
final String firstColor = costParts[nPart].substring(0, 1);
|
|
||||||
final String secondColor = costParts[nPart].substring(2);
|
|
||||||
final Boolean foundFirst = manaAbilityMap.containsKey(firstColor);
|
|
||||||
final Boolean foundSecond = manaAbilityMap.containsKey(secondColor);
|
|
||||||
if (foundFirst || foundSecond) {
|
|
||||||
if (!foundFirst) {
|
|
||||||
srcFound.addAll(manaAbilityMap.get(secondColor));
|
|
||||||
} else if (!foundSecond) {
|
|
||||||
srcFound.addAll(manaAbilityMap.get(firstColor));
|
|
||||||
} else if (manaAbilityMap.get(firstColor).size() > manaAbilityMap.get(secondColor).size()) {
|
|
||||||
srcFound.addAll(manaAbilityMap.get(firstColor));
|
|
||||||
srcFound.addAll(manaAbilityMap.get(secondColor));
|
|
||||||
} else {
|
|
||||||
srcFound.addAll(manaAbilityMap.get(secondColor));
|
|
||||||
srcFound.addAll(manaAbilityMap.get(firstColor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // single color
|
|
||||||
if (manaAbilityMap.containsKey(costParts[nPart])) {
|
|
||||||
srcFound.addAll(manaAbilityMap.get(costParts[nPart]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add sources to array lists
|
|
||||||
partSources.add(nPart, srcFound);
|
|
||||||
// add to sorted priority list
|
|
||||||
if (srcFound.size() > 0) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < partPriority.size(); i++) {
|
|
||||||
if (srcFound.size() <= partSources.get(i).size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
partPriority.add(i, nPart);
|
|
||||||
} else {
|
|
||||||
foundAllSources = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!foundAllSources) {
|
if (!foundAllSources) {
|
||||||
if (!test) {
|
if (!test) {
|
||||||
// real payment should not arrive here
|
// real payment should not arrive here
|
||||||
@@ -746,8 +626,7 @@ public class ComputerUtil {
|
|||||||
final int nPart = partPriority.get(nPriority);
|
final int nPart = partPriority.get(nPriority);
|
||||||
final ArrayList<AbilityMana> manaAbilities = partSources.get(nPart);
|
final ArrayList<AbilityMana> manaAbilities = partSources.get(nPart);
|
||||||
final ManaCost costPart = new ManaCost(costParts[nPart]);
|
final ManaCost costPart = new ManaCost(costParts[nPart]);
|
||||||
// Loop over mana abilities that can be used to current mana cost
|
// Loop over mana abilities that can be used to current mana cost part
|
||||||
// part
|
|
||||||
for (final AbilityMana m : manaAbilities) {
|
for (final AbilityMana m : manaAbilities) {
|
||||||
final Card sourceCard = m.getSourceCard();
|
final Card sourceCard = m.getSourceCard();
|
||||||
|
|
||||||
@@ -758,13 +637,12 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
// Check if AI can still play this mana ability
|
// Check if AI can still play this mana ability
|
||||||
m.setActivatingPlayer(player);
|
m.setActivatingPlayer(player);
|
||||||
// if the AI can't pay the additional costs skip the mana
|
// if the AI can't pay the additional costs skip the mana ability
|
||||||
// ability
|
if (m.getPayCosts() != null && checkPlayable) {
|
||||||
if (m.getPayCosts() != null) {
|
|
||||||
if (!ComputerUtil.canPayAdditionalCosts(m, player)) {
|
if (!ComputerUtil.canPayAdditionalCosts(m, player)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (sourceCard.isTapped()) {
|
} else if (sourceCard.isTapped() && checkPlayable) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,6 +752,157 @@ public class ComputerUtil {
|
|||||||
|
|
||||||
} // payManaCost()
|
} // payManaCost()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all mana sources.
|
||||||
|
* @param manaAbilityMap
|
||||||
|
* @param partSources
|
||||||
|
* @param partPriority
|
||||||
|
* @param costParts
|
||||||
|
* @param foundAllSources
|
||||||
|
* @return Were all mana sources found?
|
||||||
|
*/
|
||||||
|
private static Boolean findManaSources(final HashMap<String, ArrayList<AbilityMana>> manaAbilityMap,
|
||||||
|
final ArrayList<ArrayList<AbilityMana>> partSources, final ArrayList<Integer> partPriority,
|
||||||
|
final String[] costParts) {
|
||||||
|
final String[] shortColors = { "W", "U", "B", "R", "G" };
|
||||||
|
Boolean foundAllSources;
|
||||||
|
if (manaAbilityMap.isEmpty()) {
|
||||||
|
foundAllSources = false;
|
||||||
|
} else {
|
||||||
|
foundAllSources = true;
|
||||||
|
// loop over cost parts
|
||||||
|
for (int nPart = 0; nPart < costParts.length; nPart++) {
|
||||||
|
final ArrayList<AbilityMana> srcFound = new ArrayList<AbilityMana>();
|
||||||
|
// Test for:
|
||||||
|
// 1) Colorless
|
||||||
|
// 2) Split e.g. 2/G
|
||||||
|
// 3) Hybrid e.g. U/G
|
||||||
|
// defaults to single short color
|
||||||
|
if (costParts[nPart].matches("[0-9]+")) { // Colorless
|
||||||
|
srcFound.addAll(manaAbilityMap.get("1"));
|
||||||
|
} else if (costParts[nPart].contains("2/")) { // Split
|
||||||
|
final String colorKey = costParts[nPart].replace("2/", "");
|
||||||
|
// add specified color sources first
|
||||||
|
if (manaAbilityMap.containsKey(colorKey)) {
|
||||||
|
srcFound.addAll(manaAbilityMap.get(colorKey));
|
||||||
|
}
|
||||||
|
// add other available colors
|
||||||
|
for (final String color : shortColors) {
|
||||||
|
if (!colorKey.contains(color)) {
|
||||||
|
// Is source available?
|
||||||
|
if (manaAbilityMap.containsKey(color)) {
|
||||||
|
srcFound.addAll(manaAbilityMap.get(color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (costParts[nPart].length() > 1) { // Hybrid
|
||||||
|
final String firstColor = costParts[nPart].substring(0, 1);
|
||||||
|
final String secondColor = costParts[nPart].substring(2);
|
||||||
|
final Boolean foundFirst = manaAbilityMap.containsKey(firstColor);
|
||||||
|
final Boolean foundSecond = manaAbilityMap.containsKey(secondColor);
|
||||||
|
if (foundFirst || foundSecond) {
|
||||||
|
if (!foundFirst) {
|
||||||
|
srcFound.addAll(manaAbilityMap.get(secondColor));
|
||||||
|
} else if (!foundSecond) {
|
||||||
|
srcFound.addAll(manaAbilityMap.get(firstColor));
|
||||||
|
} else if (manaAbilityMap.get(firstColor).size() > manaAbilityMap.get(secondColor).size()) {
|
||||||
|
srcFound.addAll(manaAbilityMap.get(firstColor));
|
||||||
|
srcFound.addAll(manaAbilityMap.get(secondColor));
|
||||||
|
} else {
|
||||||
|
srcFound.addAll(manaAbilityMap.get(secondColor));
|
||||||
|
srcFound.addAll(manaAbilityMap.get(firstColor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // single color
|
||||||
|
if (manaAbilityMap.containsKey(costParts[nPart])) {
|
||||||
|
srcFound.addAll(manaAbilityMap.get(costParts[nPart]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add sources to array lists
|
||||||
|
partSources.add(nPart, srcFound);
|
||||||
|
// add to sorted priority list
|
||||||
|
if (srcFound.size() > 0) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < partPriority.size(); i++) {
|
||||||
|
if (srcFound.size() <= partSources.get(i).size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
partPriority.add(i, nPart);
|
||||||
|
} else {
|
||||||
|
foundAllSources = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundAllSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the ManaCost for the given SpellAbility.
|
||||||
|
* @param sa
|
||||||
|
* @param test
|
||||||
|
* @param extraMana
|
||||||
|
* @return ManaCost
|
||||||
|
*/
|
||||||
|
private static ManaCost calculateManaCost(final SpellAbility sa, final boolean test, final int extraMana) {
|
||||||
|
final String mana = sa.getPayCosts() != null ? sa.getPayCosts().getTotalMana() : sa.getManaCost();
|
||||||
|
|
||||||
|
ManaCost cost = new ManaCost(mana);
|
||||||
|
|
||||||
|
cost = Singletons.getModel().getGameAction().getSpellCostChange(sa, cost);
|
||||||
|
|
||||||
|
final Card card = sa.getSourceCard();
|
||||||
|
// Tack xMana Payments into mana here if X is a set value
|
||||||
|
if ((sa.getPayCosts() != null) && (cost.getXcounter() > 0)) {
|
||||||
|
|
||||||
|
int manaToAdd = 0;
|
||||||
|
if (test && (extraMana > 0)) {
|
||||||
|
manaToAdd = extraMana * cost.getXcounter();
|
||||||
|
} else {
|
||||||
|
// For Count$xPaid set PayX in the AFs then use that here
|
||||||
|
// Else calculate it as appropriate.
|
||||||
|
final String xSvar = card.getSVar("X").startsWith("Count$xPaid") ? "PayX" : "X";
|
||||||
|
if (!card.getSVar(xSvar).equals("")) {
|
||||||
|
if (xSvar.equals("PayX")) {
|
||||||
|
manaToAdd = Integer.parseInt(card.getSVar(xSvar)) * cost.getXcounter(); // X
|
||||||
|
} else {
|
||||||
|
manaToAdd = AbilityFactory.calculateAmount(card, xSvar, sa) * cost.getXcounter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cost.increaseColorlessMana(manaToAdd);
|
||||||
|
if (!test) {
|
||||||
|
card.setXManaCostPaid(manaToAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make mana needed to avoid negative effect a mandatory cost for the AI
|
||||||
|
if (card.getSVar("ManaNeededToAvoidNegativeEffect") != "") {
|
||||||
|
final String[] negEffects = card.getSVar("ManaNeededToAvoidNegativeEffect").split(",");
|
||||||
|
int amountAdded = 0;
|
||||||
|
for (int nStr = 0; nStr < negEffects.length; nStr++) {
|
||||||
|
// convert long color strings to short color strings
|
||||||
|
if (negEffects[nStr].length() > 1) {
|
||||||
|
negEffects[nStr] = InputPayManaCostUtil.getShortColorString(negEffects[nStr]);
|
||||||
|
}
|
||||||
|
// make mana mandatory for AI
|
||||||
|
if (!cost.isColor(negEffects[nStr])) {
|
||||||
|
cost.combineManaCost(negEffects[nStr]);
|
||||||
|
amountAdded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cost.setManaNeededToAvoidNegativeEffect(negEffects);
|
||||||
|
// TODO: should it be an error condition if amountAdded is greater
|
||||||
|
// than the colorless in the original cost? (ArsenalNut - 120102)
|
||||||
|
// adjust colorless amount to account for added mana
|
||||||
|
cost.decreaseColorlessMana(amountAdded);
|
||||||
|
}
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getProduceableColors.
|
* getProduceableColors.
|
||||||
@@ -914,11 +943,12 @@ public class ComputerUtil {
|
|||||||
* <p>
|
* <p>
|
||||||
* getAvailableMana.
|
* getAvailableMana.
|
||||||
* </p>
|
* </p>
|
||||||
|
* @param checkPlayable
|
||||||
*
|
*
|
||||||
* @return a {@link forge.CardList} object.
|
* @return a {@link forge.CardList} object.
|
||||||
*/
|
*/
|
||||||
public static CardList getAvailableMana() {
|
public static CardList getAvailableMana(boolean checkPlayable) {
|
||||||
return ComputerUtil.getAvailableMana(AllZone.getComputerPlayer());
|
return ComputerUtil.getAvailableMana(AllZone.getComputerPlayer(), checkPlayable);
|
||||||
} // getAvailableMana()
|
} // getAvailableMana()
|
||||||
|
|
||||||
// gets available mana sources and sorts them
|
// gets available mana sources and sorts them
|
||||||
@@ -929,21 +959,26 @@ public class ComputerUtil {
|
|||||||
*
|
*
|
||||||
* @param player
|
* @param player
|
||||||
* a {@link forge.game.player.Player} object.
|
* a {@link forge.game.player.Player} object.
|
||||||
|
* @param checkPlayable
|
||||||
* @return a {@link forge.CardList} object.
|
* @return a {@link forge.CardList} object.
|
||||||
*/
|
*/
|
||||||
public static CardList getAvailableMana(final Player player) {
|
public static CardList getAvailableMana(final Player player, final boolean checkPlayable) {
|
||||||
final CardList list = player.getCardsIn(ZoneType.Battlefield);
|
final CardList list = player.getCardsIn(ZoneType.Battlefield);
|
||||||
final CardList manaSources = list.filter(new CardListFilter() {
|
final CardList manaSources = list.filter(new CardListFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean addCard(final Card c) {
|
public boolean addCard(final Card c) {
|
||||||
for (final AbilityMana am : c.getAIPlayableMana()) {
|
if (checkPlayable) {
|
||||||
am.setActivatingPlayer(player);
|
for (final AbilityMana am : c.getAIPlayableMana()) {
|
||||||
if (am.canPlay()) {
|
am.setActivatingPlayer(player);
|
||||||
return true;
|
if (am.canPlay()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}); // CardListFilter
|
}); // CardListFilter
|
||||||
|
|
||||||
@@ -1098,9 +1133,10 @@ public class ComputerUtil {
|
|||||||
*
|
*
|
||||||
* @param player
|
* @param player
|
||||||
* a {@link forge.game.player.Player} object.
|
* a {@link forge.game.player.Player} object.
|
||||||
|
* @param checkPlayable TODO
|
||||||
* @return HashMap<String, CardList>
|
* @return HashMap<String, CardList>
|
||||||
*/
|
*/
|
||||||
public static HashMap<String, ArrayList<AbilityMana>> mapManaSources(final Player player) {
|
public static HashMap<String, ArrayList<AbilityMana>> mapManaSources(final Player player, boolean checkPlayable) {
|
||||||
final HashMap<String, ArrayList<AbilityMana>> manaMap = new HashMap<String, ArrayList<AbilityMana>>();
|
final HashMap<String, ArrayList<AbilityMana>> manaMap = new HashMap<String, ArrayList<AbilityMana>>();
|
||||||
|
|
||||||
final ArrayList<AbilityMana> whiteSources = new ArrayList<AbilityMana>();
|
final ArrayList<AbilityMana> whiteSources = new ArrayList<AbilityMana>();
|
||||||
@@ -1112,7 +1148,7 @@ public class ComputerUtil {
|
|||||||
final ArrayList<AbilityMana> snowSources = new ArrayList<AbilityMana>();
|
final ArrayList<AbilityMana> snowSources = new ArrayList<AbilityMana>();
|
||||||
|
|
||||||
// Get list of current available mana sources
|
// Get list of current available mana sources
|
||||||
final CardList manaSources = ComputerUtil.getAvailableMana();
|
final CardList manaSources = ComputerUtil.getAvailableMana(checkPlayable);
|
||||||
|
|
||||||
// Loop over all mana sources
|
// Loop over all mana sources
|
||||||
for (int i = 0; i < manaSources.size(); i++) {
|
for (int i = 0; i < manaSources.size(); i++) {
|
||||||
@@ -1122,7 +1158,7 @@ public class ComputerUtil {
|
|||||||
// Loop over all mana abilities for a source
|
// Loop over all mana abilities for a source
|
||||||
for (final AbilityMana m : manaAbilities) {
|
for (final AbilityMana m : manaAbilities) {
|
||||||
m.setActivatingPlayer(AllZone.getComputerPlayer());
|
m.setActivatingPlayer(AllZone.getComputerPlayer());
|
||||||
if (!m.canPlay()) {
|
if (!m.canPlay() && checkPlayable) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1999,4 +2035,29 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is it OK to cast this for less than the Max Targets?
|
||||||
|
* @param source
|
||||||
|
*/
|
||||||
|
public static boolean shouldCastLessThanMax(final Card source) {
|
||||||
|
boolean ret = true;
|
||||||
|
if (source.getManaCost().countX() > 0) {
|
||||||
|
// If TargetMax is MaxTgts (i.e., an "X" cost), this is fine because AI is limited by mana available.
|
||||||
|
} else {
|
||||||
|
// Otherwise, if life is possibly in danger, then this is fine.
|
||||||
|
Combat combat = new Combat();
|
||||||
|
combat.initiatePossibleDefenders(AllZone.getComputerPlayer());
|
||||||
|
CardList attackers = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
|
||||||
|
for (Card att : attackers) {
|
||||||
|
combat.addAttacker(att);
|
||||||
|
}
|
||||||
|
combat = ComputerUtilBlock.getBlockers(combat, AllZoneUtil.getCreaturesInPlay(AllZone.getComputerPlayer()));
|
||||||
|
if (!CombatUtil.lifeInDanger(combat)) {
|
||||||
|
// Otherwise, return false. Do not play now.
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user