mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
Card: refactor hidden keywords into Table Structure
This commit is contained in:
committed by
Michael Kamensky
parent
83a7cc8a3e
commit
bfc938be57
@@ -6,12 +6,12 @@
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -42,7 +42,6 @@ import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.combat.GlobalAttackRestrictions;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerPredicates;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -60,7 +59,7 @@ import forge.util.collect.FCollectionView;
|
||||
* <p>
|
||||
* ComputerUtil_Attack2 class.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
@@ -72,10 +71,10 @@ public class AiAttackController {
|
||||
|
||||
private List<Card> oppList; // holds human player creatures
|
||||
private List<Card> myList; // holds computer creatures
|
||||
|
||||
|
||||
private final Player ai;
|
||||
private Player defendingOpponent;
|
||||
|
||||
|
||||
private int aiAggression = 0; // added by Masher, how aggressive the ai is attack will be depending on circumstances
|
||||
private final boolean nextTurn;
|
||||
|
||||
@@ -108,7 +107,7 @@ public class AiAttackController {
|
||||
|
||||
public AiAttackController(final Player ai, Card attacker) {
|
||||
this.ai = ai;
|
||||
this.defendingOpponent = choosePreferredDefenderPlayer(ai);
|
||||
this.defendingOpponent = choosePreferredDefenderPlayer(ai);
|
||||
this.oppList = getOpponentCreatures(this.defendingOpponent);
|
||||
this.myList = ai.getCreaturesInPlay();
|
||||
this.attackers = new ArrayList<>();
|
||||
@@ -118,7 +117,7 @@ public class AiAttackController {
|
||||
this.blockers = getPossibleBlockers(oppList, this.attackers);
|
||||
this.nextTurn = false;
|
||||
} // overloaded constructor to evaluate single specified attacker
|
||||
|
||||
|
||||
public static List<Card> getOpponentCreatures(final Player defender) {
|
||||
List<Card> defenders = new ArrayList<>(defender.getCreaturesInPlay());
|
||||
Predicate<Card> canAnimate = new Predicate<Card>() {
|
||||
@@ -133,7 +132,7 @@ public class AiAttackController {
|
||||
}
|
||||
for (SpellAbility sa : c.getSpellAbilities()) {
|
||||
if (sa.getApi() == ApiType.Animate) {
|
||||
if (ComputerUtilCost.canPayCost(sa, defender)
|
||||
if (ComputerUtilCost.canPayCost(sa, defender)
|
||||
&& sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) {
|
||||
Card animatedCopy = AnimateAi.becomeAnimated(c, sa);
|
||||
defenders.add(animatedCopy);
|
||||
@@ -143,7 +142,7 @@ public class AiAttackController {
|
||||
}
|
||||
return defenders;
|
||||
}
|
||||
|
||||
|
||||
public void removeBlocker(Card blocker) {
|
||||
this.oppList.remove(blocker);
|
||||
}
|
||||
@@ -200,7 +199,7 @@ public class AiAttackController {
|
||||
* <p>
|
||||
* isEffectiveAttacker.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.game.card.Card} object.
|
||||
* @param combat
|
||||
@@ -501,7 +500,7 @@ public class AiAttackController {
|
||||
// Conservative prediction for vehicles: the AI tries to acknowledge the fact that
|
||||
// at least one creature will tap to crew a blocking vehicle when predicting if an
|
||||
// alpha strike for lethal is viable
|
||||
int maxBlockersAfterCrew = remainingBlockers.size();
|
||||
int maxBlockersAfterCrew = remainingBlockers.size();
|
||||
for (Card c : this.blockers) {
|
||||
CardTypeView cardType = c.getCurrentState().getType();
|
||||
CardCollectionView oppBattlefield = c.getController().getCardsIn(ZoneType.Battlefield);
|
||||
@@ -514,7 +513,7 @@ public class AiAttackController {
|
||||
} else if (c.getName().equals("Peacewalker Colossus")) {
|
||||
// can activate other vehicles for {1}{W}
|
||||
// TODO: the AI should ideally predict how many times it can activate
|
||||
// for now, unless the opponent is tapped out, break at this point
|
||||
// for now, unless the opponent is tapped out, break at this point
|
||||
// and do not predict the blocker limit (which is safer)
|
||||
if (!CardLists.filter(oppBattlefield, Predicates.and(CardPredicates.Presets.UNTAPPED, CardPredicates.Presets.LANDS)).isEmpty()) {
|
||||
maxBlockersAfterCrew = Integer.MAX_VALUE;
|
||||
@@ -600,7 +599,7 @@ public class AiAttackController {
|
||||
maxBlockersAfterCrew--;
|
||||
}
|
||||
unblockedAttackers.addAll(remainingAttackers);
|
||||
|
||||
|
||||
int trampleDamage = 0;
|
||||
for (Card attacker : blockedAttackers) {
|
||||
if (attacker.hasKeyword(Keyword.TRAMPLE)) {
|
||||
@@ -674,7 +673,7 @@ public class AiAttackController {
|
||||
* <p>
|
||||
* Getter for the field <code>attackers</code>.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a {@link forge.game.combat.Combat} object.
|
||||
*/
|
||||
public final void declareAttackers(final Combat combat) {
|
||||
@@ -742,14 +741,9 @@ public class AiAttackController {
|
||||
// TODO: if there are other ways to tap this creature (like mana creature), then don't need to attack
|
||||
mustAttack = true;
|
||||
} else {
|
||||
for (KeywordInterface inst : attacker.getKeywords()) {
|
||||
String s = inst.getOriginal();
|
||||
if (s.equals("CARDNAME attacks each turn if able.")
|
||||
|| s.startsWith("CARDNAME attacks specific player each combat if able")
|
||||
|| s.equals("CARDNAME attacks each combat if able.")) {
|
||||
mustAttack = true;
|
||||
break;
|
||||
}
|
||||
// TODO move to static Ability
|
||||
if (attacker.hasKeyword("CARDNAME attacks each combat if able.") || attacker.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
|
||||
mustAttack = true;
|
||||
}
|
||||
}
|
||||
if (mustAttack || attacker.getController().getMustAttackEntity() != null || attacker.getController().getMustAttackEntityThisTurn() != null) {
|
||||
@@ -799,7 +793,7 @@ public class AiAttackController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Exalted
|
||||
if (combat.getAttackers().isEmpty()) {
|
||||
boolean exalted = ai.countExaltedBonus() > 2;
|
||||
@@ -857,7 +851,7 @@ public class AiAttackController {
|
||||
// examine the potential forces
|
||||
final List<Card> nextTurnAttackers = new ArrayList<>();
|
||||
int candidateCounterAttackDamage = 0;
|
||||
|
||||
|
||||
final Player opp = this.defendingOpponent;
|
||||
// get the potential damage and strength of the AI forces
|
||||
final List<Card> candidateAttackers = new ArrayList<>();
|
||||
@@ -1115,7 +1109,7 @@ public class AiAttackController {
|
||||
* <p>
|
||||
* getAttack.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param c
|
||||
* a {@link forge.game.card.Card} object.
|
||||
* @return a int.
|
||||
@@ -1134,7 +1128,7 @@ public class AiAttackController {
|
||||
* <p>
|
||||
* shouldAttack.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param attacker
|
||||
* a {@link forge.game.card.Card} object.
|
||||
* @param defenders
|
||||
@@ -1179,7 +1173,7 @@ public class AiAttackController {
|
||||
}
|
||||
boolean hasAttackEffect = attacker.getSVar("HasAttackEffect").equals("TRUE") || attacker.hasStartOfKeyword("Annihilator");
|
||||
// is there a gain in attacking even when the blocker is not killed (Lifelink, Wither,...)
|
||||
boolean hasCombatEffect = attacker.getSVar("HasCombatEffect").equals("TRUE")
|
||||
boolean hasCombatEffect = attacker.getSVar("HasCombatEffect").equals("TRUE")
|
||||
|| "Blocked".equals(attacker.getSVar("HasAttackEffect"));
|
||||
|
||||
// contains only the defender's blockers that can actually block the attacker
|
||||
@@ -1201,13 +1195,9 @@ public class AiAttackController {
|
||||
int defPower = CardLists.getTotalPower(validBlockers, true, false);
|
||||
|
||||
if (!hasCombatEffect) {
|
||||
for (KeywordInterface inst : attacker.getKeywords()) {
|
||||
String keyword = inst.getOriginal();
|
||||
if (keyword.equals("Wither") || keyword.equals("Infect")
|
||||
|| keyword.equals("Lifelink") || keyword.startsWith("Afflict")) {
|
||||
hasCombatEffect = true;
|
||||
break;
|
||||
}
|
||||
if (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)
|
||||
|| attacker.hasKeyword(Keyword.LIFELINK) || attacker.hasKeyword(Keyword.AFFLICT)) {
|
||||
hasCombatEffect = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1262,7 +1252,7 @@ public class AiAttackController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!attacker.hasKeyword(Keyword.VIGILANCE) && ComputerUtilCard.canBeKilledByRoyalAssassin(ai, attacker)) {
|
||||
canKillAllDangerous = false;
|
||||
canBeKilled = true;
|
||||
@@ -1272,7 +1262,7 @@ public class AiAttackController {
|
||||
} else if ((canKillAllDangerous || !canBeKilled) && ComputerUtilCard.canBeBlockedProfitably(defendingOpponent, attacker)) {
|
||||
canKillAllDangerous = false;
|
||||
canBeKilled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if the creature cannot block and can kill all opponents they might as
|
||||
// well attack, they do nothing staying back
|
||||
@@ -1286,7 +1276,7 @@ public class AiAttackController {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (numberOfPossibleBlockers > 2
|
||||
if (numberOfPossibleBlockers > 2
|
||||
|| (numberOfPossibleBlockers >= 1 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 1, this.defendingOpponent))
|
||||
|| (numberOfPossibleBlockers == 2 && CombatUtil.canAttackerBeBlockedWithAmount(attacker, 2, this.defendingOpponent))) {
|
||||
canBeBlocked = true;
|
||||
@@ -1416,7 +1406,7 @@ public class AiAttackController {
|
||||
|
||||
return exerters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find a protection type that will make an attacker unblockable.
|
||||
* @param sa ability belonging to ApiType.Protection
|
||||
@@ -1485,10 +1475,10 @@ public class AiAttackController {
|
||||
CardCollection attSorted = new CardCollection(attackersLeft);
|
||||
CardCollection attUnsafe = new CardCollection();
|
||||
CardLists.sortByToughnessDesc(attSorted);
|
||||
|
||||
|
||||
int i = numForcedAttackers;
|
||||
int refPowerValue = 0; // Aggro profiles do not account for the possible blockers' power, conservative profiles do.
|
||||
|
||||
|
||||
if (!playAggro && this.blockers.size() > 0) {
|
||||
// Conservative play: check to ensure that the card can't be killed off while damaged
|
||||
// TODO: currently sorting a copy of this.blockers, but it looks safe to operate on this.blockers directly?
|
||||
@@ -1498,7 +1488,7 @@ public class AiAttackController {
|
||||
CardLists.sortByPowerDesc(blkSorted);
|
||||
refPowerValue += blkSorted.get(0).getCurrentPower();
|
||||
}
|
||||
|
||||
|
||||
for (Card cre : attSorted) {
|
||||
i++;
|
||||
if (i + refPowerValue >= cre.getCurrentToughness()) {
|
||||
@@ -1507,7 +1497,7 @@ public class AiAttackController {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
attackersLeft.removeAll(attUnsafe);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.staticability.StaticAbilityCantAttackBlock;
|
||||
import forge.game.trigger.Trigger;
|
||||
import forge.game.trigger.TriggerType;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -183,9 +184,7 @@ public class AiBlockController {
|
||||
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
|
||||
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")
|
||||
|| attacker.hasKeyword(Keyword.MENACE)) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -296,8 +295,7 @@ public class AiBlockController {
|
||||
|
||||
// 6. Blockers that don't survive until the next turn anyway
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword(Keyword.MENACE)
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -323,7 +321,17 @@ public class AiBlockController {
|
||||
attackersLeft = (new ArrayList<>(currentAttackers));
|
||||
}
|
||||
|
||||
static final Predicate<Card> rampagesOrNeedsManyToBlock = Predicates.or(CardPredicates.containsKeyword("Rampage"), CardPredicates.containsKeyword("CantBeBlockedByAmount GT"));
|
||||
private Predicate<Card> rampagesOrNeedsManyToBlock(final Combat combat) {
|
||||
return Predicates.or(CardPredicates.hasKeyword(Keyword.RAMPAGE), new Predicate<Card>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
// select creature that has a max blocker
|
||||
return StaticAbilityCantAttackBlock.getMinMaxBlocker(input, combat.getDefenderPlayerByAttacker(input)).getRight() < Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Good Gang Blocks means a good trade or no trade
|
||||
/**
|
||||
@@ -334,7 +342,7 @@ public class AiBlockController {
|
||||
* @param combat a {@link forge.game.combat.Combat} object.
|
||||
*/
|
||||
private void makeGangBlocks(final Combat combat) {
|
||||
List<Card> currentAttackers = CardLists.filter(attackersLeft, Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
List<Card> currentAttackers = CardLists.filter(attackersLeft, Predicates.not(rampagesOrNeedsManyToBlock(combat)));
|
||||
List<Card> blockers;
|
||||
|
||||
// Try to block an attacker without first strike with a gang of first strikers
|
||||
@@ -528,7 +536,7 @@ public class AiBlockController {
|
||||
|
||||
// Try to block a Menace attacker with two blockers, neither of which will die
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (!attacker.hasKeyword(Keyword.MENACE) && !attacker.hasStartOfKeyword("CantBeBlockedByAmount LT2")) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -584,9 +592,7 @@ public class AiBlockController {
|
||||
List<Card> killingBlockers;
|
||||
|
||||
for (final Card attacker : attackersLeft) {
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
|| attacker.hasKeyword(Keyword.MENACE)
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) {
|
||||
continue;
|
||||
}
|
||||
if (ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||
@@ -635,10 +641,8 @@ public class AiBlockController {
|
||||
|
||||
Card attacker = attackers.get(0);
|
||||
|
||||
if (attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1
|
||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")
|
||||
|| attacker.hasKeyword(Keyword.MENACE)
|
||||
|| ComputerUtilCombat.attackerHasThreateningAfflict(attacker, ai)) {
|
||||
attackers.remove(0);
|
||||
makeChumpBlocks(combat, attackers);
|
||||
@@ -686,9 +690,7 @@ public class AiBlockController {
|
||||
List<Card> currentAttackers = new ArrayList<>(attackersLeft);
|
||||
|
||||
for (final Card attacker : currentAttackers) {
|
||||
if (!attacker.hasStartOfKeyword("CantBeBlockedByAmount LT")
|
||||
&& !attacker.hasKeyword(Keyword.MENACE)
|
||||
&& !attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
if (StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() <= 1) {
|
||||
continue;
|
||||
}
|
||||
List<Card> possibleBlockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
|
||||
@@ -720,14 +722,14 @@ public class AiBlockController {
|
||||
List<Card> chumpBlockers;
|
||||
|
||||
List<Card> tramplingAttackers = CardLists.getKeyword(attackers, Keyword.TRAMPLE);
|
||||
tramplingAttackers = CardLists.filter(tramplingAttackers, Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
tramplingAttackers = CardLists.filter(tramplingAttackers, Predicates.not(rampagesOrNeedsManyToBlock(combat)));
|
||||
|
||||
// TODO - should check here for a "rampage-like" trigger that replaced the keyword:
|
||||
// "Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."
|
||||
|
||||
for (final Card attacker : tramplingAttackers) {
|
||||
|
||||
if (((attacker.hasStartOfKeyword("CantBeBlockedByAmount LT") || attacker.hasKeyword(Keyword.MENACE)) && !combat.isBlocked(attacker))
|
||||
if (((StaticAbilityCantAttackBlock.getMinMaxBlocker(attacker, combat.getDefenderPlayerByAttacker(attacker)).getLeft() > 1) && !combat.isBlocked(attacker))
|
||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")
|
||||
|| attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||
continue;
|
||||
@@ -751,7 +753,7 @@ public class AiBlockController {
|
||||
private void reinforceBlockersToKill(final Combat combat) {
|
||||
List<Card> safeBlockers;
|
||||
List<Card> blockers;
|
||||
List<Card> targetAttackers = CardLists.filter(blockedButUnkilled, Predicates.not(rampagesOrNeedsManyToBlock));
|
||||
List<Card> targetAttackers = CardLists.filter(blockedButUnkilled, Predicates.not(rampagesOrNeedsManyToBlock(combat)));
|
||||
|
||||
// TODO - should check here for a "rampage-like" trigger that replaced
|
||||
// the keyword: "Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."
|
||||
@@ -1076,8 +1078,7 @@ public class AiBlockController {
|
||||
}
|
||||
|
||||
// assign blockers that have to block
|
||||
chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each turn if able.");
|
||||
chumpBlockers.addAll(CardLists.getKeyword(blockersLeft, "CARDNAME blocks each combat if able."));
|
||||
chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each combat if able.");
|
||||
// if an attacker with lure attacks - all that can block
|
||||
for (final Card blocker : blockersLeft) {
|
||||
if (CombatUtil.mustBlockAnAttacker(blocker, combat, null)) {
|
||||
@@ -1091,7 +1092,6 @@ public class AiBlockController {
|
||||
for (final Card blocker : blockers) {
|
||||
if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
|
||||
&& (CombatUtil.mustBlockAnAttacker(blocker, combat, null)
|
||||
|| blocker.hasKeyword("CARDNAME blocks each turn if able.")
|
||||
|| blocker.hasKeyword("CARDNAME blocks each combat if able."))) {
|
||||
combat.addBlocker(attacker, blocker);
|
||||
if (blocker.getMustBlockCards() != null) {
|
||||
|
||||
@@ -1654,10 +1654,11 @@ public class ComputerUtilCard {
|
||||
Card pumped = CardFactory.copyCard(c, false);
|
||||
pumped.setSickness(c.hasSickness());
|
||||
final long timestamp = c.getGame().getNextTimestamp();
|
||||
final List<String> kws = new ArrayList<>();
|
||||
final List<String> kws = Lists.newArrayList();
|
||||
final List<String> hiddenKws = Lists.newArrayList();
|
||||
for (String kw : keywords) {
|
||||
if (kw.startsWith("HIDDEN")) {
|
||||
pumped.addHiddenExtrinsicKeyword(kw);
|
||||
hiddenKws.add(kw.substring(7));
|
||||
} else {
|
||||
kws.add(kw);
|
||||
}
|
||||
@@ -1686,7 +1687,13 @@ public class ComputerUtilCard {
|
||||
pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp);
|
||||
pumped.setPTBoost(c.getPTBoostTable());
|
||||
pumped.addPTBoost(power + berserkPower, toughness, timestamp, 0);
|
||||
pumped.addChangedCardKeywords(kws, null, false, false, timestamp, 0);
|
||||
|
||||
if (!kws.isEmpty()) {
|
||||
pumped.addChangedCardKeywords(kws, null, false, false, timestamp, 0);
|
||||
}
|
||||
if (!hiddenKws.isEmpty()) {
|
||||
pumped.addHiddenExtrinsicKeywords(timestamp, 0, hiddenKws);
|
||||
}
|
||||
Set<CounterType> types = c.getCounters().keySet();
|
||||
for(CounterType ct : types) {
|
||||
pumped.addCounterFireNoEvents(ct, c.getCounters(ct), ai, sa, true, null);
|
||||
@@ -1699,14 +1706,10 @@ public class ComputerUtilCard {
|
||||
KeywordCollection copiedKeywords = new KeywordCollection();
|
||||
copiedKeywords.insertAll(pumped.getKeywords());
|
||||
List<KeywordInterface> toCopy = Lists.newArrayList();
|
||||
for (KeywordInterface k : c.getKeywords()) {
|
||||
for (KeywordInterface k : c.getUnhiddenKeywords()) {
|
||||
KeywordInterface copiedKI = k.copy(c, true);
|
||||
if (!copiedKeywords.contains(copiedKI.getOriginal())) {
|
||||
if (copiedKI.getHidden()) {
|
||||
pumped.addHiddenExtrinsicKeyword(copiedKI);
|
||||
} else {
|
||||
toCopy.add(copiedKI);
|
||||
}
|
||||
toCopy.add(copiedKI);
|
||||
}
|
||||
}
|
||||
final long timestamp2 = c.getGame().getNextTimestamp(); //is this necessary or can the timestamp be re-used?
|
||||
@@ -1744,7 +1747,7 @@ public class ComputerUtilCard {
|
||||
if (!stAb.hasParam("AddPower") && !stAb.hasParam("AddToughness")) {
|
||||
continue;
|
||||
}
|
||||
if (!vCard.isValid(stAb.getParam("Affected").split(","), c.getController(), c, stAb)) {
|
||||
if (!stAb.matchesValidParam("Affected", vCard)) {
|
||||
continue;
|
||||
}
|
||||
int att = 0;
|
||||
|
||||
@@ -110,7 +110,17 @@ public class ComputerUtilCombat {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final KeywordInterface inst : attacker.getKeywords()) {
|
||||
// TODO replace with Static Ability
|
||||
for (final String keyword : attacker.getHiddenExtrinsicKeywords()) {
|
||||
if (keyword.startsWith("CARDNAME attacks specific player each combat if able")) {
|
||||
final String defined = keyword.split(":")[1];
|
||||
final Player player = AbilityUtils.getDefinedPlayers(attacker, defined, null).get(0);
|
||||
if (!defender.equals(player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final KeywordInterface inst : attacker.getKeywords(Keyword.UNDEFINED)) {
|
||||
final String keyword = inst.getOriginal();
|
||||
if (keyword.startsWith("CARDNAME attacks specific player each combat if able")) {
|
||||
final String defined = keyword.split(":")[1];
|
||||
|
||||
@@ -8,7 +8,6 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.cost.CostPayEnergy;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
|
||||
public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
@@ -35,16 +34,15 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
}
|
||||
int power = getEffectivePower(c);
|
||||
final int toughness = getEffectiveToughness(c);
|
||||
for (KeywordInterface kw : c.getKeywords()) {
|
||||
String keyword = kw.getOriginal();
|
||||
if (keyword.equals("Prevent all combat damage that would be dealt by CARDNAME.")
|
||||
|| keyword.equals("Prevent all damage that would be dealt by CARDNAME.")
|
||||
|| keyword.equals("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
|
||||
|| keyword.equals("Prevent all damage that would be dealt to and dealt by CARDNAME.")) {
|
||||
power = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO replace with ReplacementEffect checks
|
||||
if (c.hasKeyword("Prevent all combat damage that would be dealt by CARDNAME.")
|
||||
|| c.hasKeyword("Prevent all damage that would be dealt by CARDNAME.")
|
||||
|| c.hasKeyword("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
|
||||
|| c.hasKeyword("Prevent all damage that would be dealt to and dealt by CARDNAME.")) {
|
||||
power = 0;
|
||||
}
|
||||
|
||||
if (considerPT) {
|
||||
value += addValue(power * 15, "power");
|
||||
value += addValue(toughness * 10, "toughness: " + toughness);
|
||||
@@ -157,8 +155,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
|
||||
}
|
||||
if (c.hasKeyword("CARDNAME can't block.")) {
|
||||
value -= subValue(10, "cant-block");
|
||||
} else if (c.hasKeyword("CARDNAME attacks each turn if able.")
|
||||
|| c.hasKeyword("CARDNAME attacks each combat if able.")) {
|
||||
} else if (c.hasKeyword("CARDNAME attacks each combat if able.")) {
|
||||
value -= subValue(10, "must-attack");
|
||||
} else if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
|
||||
value -= subValue(10, "must-attack-player");
|
||||
|
||||
@@ -16,7 +16,6 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import forge.ai.AiAttackController;
|
||||
import forge.ai.AiBlockController;
|
||||
import forge.ai.AiCardMemory;
|
||||
import forge.ai.AiController;
|
||||
import forge.ai.AiProps;
|
||||
|
||||
@@ -24,7 +24,6 @@ import forge.game.card.CardFactory;
|
||||
import forge.game.card.CounterType;
|
||||
import forge.game.card.token.TokenInfo;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.keyword.KeywordInterface;
|
||||
import forge.game.phase.PhaseHandler;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.Player;
|
||||
@@ -295,8 +294,8 @@ public class GameCopier {
|
||||
newCard.setChangedCardNames(c.getChangedCardNames());
|
||||
|
||||
// TODO: Is this correct? Does it not duplicate keywords from enchantments and such?
|
||||
for (KeywordInterface kw : c.getHiddenExtrinsicKeywords())
|
||||
newCard.addHiddenExtrinsicKeyword(kw);
|
||||
//for (KeywordInterface kw : c.getHiddenExtrinsicKeywords())
|
||||
// newCard.addHiddenExtrinsicKeyword(kw);
|
||||
if (c.isTapped()) {
|
||||
newCard.setTapped(true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user