diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 277128eb06d..b120235014f 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -547,8 +547,7 @@ public class AiAttackController { remainingAttackers.removeAll(unblockedAttackers); for (Card blocker : this.blockers) { - if (blocker.hasKeyword("CARDNAME can block any number of creatures.") - || blocker.hasKeyword("CARDNAME can block an additional ninety-nine creatures each combat.")) { + if (blocker.canBlockAny()) { for (Card attacker : this.attackers) { if (CombatUtil.canBlock(attacker, blocker)) { remainingAttackers.remove(attacker); @@ -564,6 +563,7 @@ public class AiAttackController { if (remainingAttackers.isEmpty() || maxBlockersAfterCrew == 0) { break; } + // TODO replace with better amount if (blocker.hasKeyword("CARDNAME can block an additional creature each combat.")) { blockedAttackers.add(remainingAttackers.get(0)); remainingAttackers.remove(0); @@ -884,7 +884,7 @@ public class AiAttackController { final int outNumber = computerForces - humanForces; for (Card blocker : this.blockers) { - if (blocker.hasKeyword("CARDNAME can block any number of creatures.")) { + if (blocker.canBlockAny()) { aiLifeToPlayerDamageRatio--; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index dec1aaa9f06..a33a8a1e84d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -1581,9 +1581,6 @@ public class AttachAi extends SpellAbilityAi { && CombatUtil.canBlock(card, true); } else if (keyword.equals("Reach")) { return !card.hasKeyword(Keyword.FLYING) && CombatUtil.canBlock(card, true); - } else if (keyword.endsWith("CARDNAME can block an additional creature each combat.")) { - return CombatUtil.canBlock(card, true) && !card.hasKeyword("CARDNAME can block any number of creatures.") - && !card.hasKeyword("CARDNAME can block an additional ninety-nine creatures each combat."); } else if (keyword.equals("CARDNAME can attack as though it didn't have defender.")) { return card.hasKeyword(Keyword.DEFENDER) && card.getNetCombatDamage() + powerBonus > 0; } else if (keyword.equals("Shroud") || keyword.equals("Hexproof")) { diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java index 3f178539be2..9f2f3c61fff 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java @@ -256,7 +256,7 @@ public abstract class PumpAiBase extends SpellAbilityAi { } } return false; - } else if (keyword.equals("Bushido")) { + } else if (keyword.startsWith("Bushido")) { return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) && !opp.getCreaturesInPlay().isEmpty() @@ -335,22 +335,6 @@ public abstract class PumpAiBase extends SpellAbilityAi { && !CardLists.getKeyword(game.getCombat().getAttackers(), Keyword.FLYING).isEmpty() && !card.hasKeyword(Keyword.FLYING) && CombatUtil.canBlock(card); - } else if (keyword.endsWith("CARDNAME can block an additional creature each combat.")) { - if (ph.isPlayerTurn(ai) - || !ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) { - return false; - } - int canBlockNum = 1 + CombatUtil.numberOfAdditionalCreaturesCanBlock(card); - int possibleBlockNum = 0; - for (Card attacker : game.getCombat().getAttackers()) { - if (CombatUtil.canBlock(attacker, card)) { - possibleBlockNum++; - if (possibleBlockNum > canBlockNum) { - break; - } - } - } - return possibleBlockNum > canBlockNum; } else if (keyword.equals("Shroud") || keyword.equals("Hexproof")) { return ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(card); } else if (keyword.equals("Persist")) { diff --git a/forge-game/src/main/java/forge/game/StaticEffect.java b/forge-game/src/main/java/forge/game/StaticEffect.java index 31fe957de37..b27ed2f3d20 100644 --- a/forge-game/src/main/java/forge/game/StaticEffect.java +++ b/forge-game/src/main/java/forge/game/StaticEffect.java @@ -47,11 +47,8 @@ public class StaticEffect { private final Card source; private StaticAbility ability; - private int keywordNumber = 0; private CardCollectionView affectedCards = new CardCollection(); private List affectedPlayers = Lists.newArrayList(); - private int xValue = 0; - private int yValue = 0; private long timestamp = -1; private String chosenType; @@ -88,11 +85,8 @@ public class StaticEffect { private StaticEffect makeMappedCopy(GameObjectMap map) { StaticEffect copy = new StaticEffect(map.map(this.source)); copy.ability = this.ability; - copy.keywordNumber = this.keywordNumber; copy.affectedCards = map.mapCollection(this.affectedCards); copy.affectedPlayers = map.mapList(this.affectedPlayers); - copy.xValue = this.xValue; - copy.yValue = this.yValue; copy.timestamp = this.timestamp; copy.chosenType = this.chosenType; copy.mapParams = this.mapParams; @@ -627,29 +621,6 @@ public class StaticEffect { return this.source; } - /** - *

- * Setter for the field keywordNumber. - *

- * - * @param i - * a int. - */ - public final void setKeywordNumber(final int i) { - this.keywordNumber = i; - } - - /** - *

- * Getter for the field keywordNumber. - *

- * - * @return a int. - */ - public final int getKeywordNumber() { - return this.keywordNumber; - } - /** *

* Getter for the field affectedCards. @@ -692,52 +663,6 @@ public class StaticEffect { this.affectedPlayers = list; } - /** - *

- * Setter for the field xValue. - *

- * - * @param x - * a int. - */ - public final void setXValue(final int x) { - this.xValue = x; - } - - /** - *

- * Getter for the field xValue. - *

- * - * @return a int. - */ - public final int getXValue() { - return this.xValue; - } - - /** - *

- * Setter for the field yValue. - *

- * - * @param y - * a int. - */ - public final void setYValue(final int y) { - this.yValue = y; - } - - /** - *

- * Getter for the field yValue. - *

- * - * @return a int. - */ - public final int getYValue() { - return this.yValue; - } - /** * setParams. TODO Write javadoc for this method. * @@ -788,7 +713,6 @@ public class StaticEffect { String changeColorWordsTo = null; - int keywordMultiplier = 1; boolean setPT = false; String[] addHiddenKeywords = null; String addColors = null; @@ -805,15 +729,6 @@ public class StaticEffect { setPT = true; } - if (params.containsKey("KeywordMultiplier")) { - String multiplier = params.get("KeywordMultiplier"); - if (multiplier.equals("X")) { - keywordMultiplier = getXValue(); - } else { - keywordMultiplier = Integer.valueOf(multiplier); - } - } - if (params.containsKey("AddHiddenKeyword")) { addHiddenKeywords = params.get("AddHiddenKeyword").split(" & "); } @@ -906,9 +821,7 @@ public class StaticEffect { if (addHiddenKeywords != null) { for (final String k : addHiddenKeywords) { - for (int j = 0; j < keywordMultiplier; j++) { - affectedCard.removeHiddenExtrinsicKeyword(k); - } + affectedCard.removeHiddenExtrinsicKeyword(k); } } @@ -949,6 +862,13 @@ public class StaticEffect { affectedCard.removeGoad(getTimestamp()); } + if (params.containsKey("CanBlockAny")) { + affectedCard.removeCanBlockAny(getTimestamp()); + } + if (params.containsKey("CanBlockAmount")) { + affectedCard.removeCanBlockAdditional(getTimestamp()); + } + affectedCard.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering } return affectedCards; diff --git a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java index 4bd8a310b2c..e9c8012375d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PumpEffect.java @@ -64,7 +64,15 @@ public class PumpEffect extends SpellAbilityEffect { if (redrawPT) { gameCard.updatePowerToughnessForView(); } - + + if (sa.hasParam("CanBlockAny")) { + gameCard.addCanBlockAny(timestamp); + } + if (sa.hasParam("CanBlockAmount")) { + int v = AbilityUtils.calculateAmount(host, sa.getParam("CanBlockAmount"), sa, true); + gameCard.addCanBlockAdditional(v, timestamp); + } + if (sa.hasParam("LeaveBattlefield")) { addLeaveBattlefieldReplacement(gameCard, sa, sa.getParam("LeaveBattlefield")); } @@ -77,6 +85,9 @@ public class PumpEffect extends SpellAbilityEffect { @Override public void run() { gameCard.removePTBoost(timestamp); + boolean updateText = false; + updateText = gameCard.removeCanBlockAny(timestamp) || updateText; + updateText = gameCard.removeCanBlockAdditional(timestamp) || updateText; if (keywords.size() > 0) { @@ -88,6 +99,9 @@ public class PumpEffect extends SpellAbilityEffect { gameCard.removeChangedCardKeywords(timestamp); } gameCard.updatePowerToughnessForView(); + if (updateText) { + gameCard.updateAbilityTextForView(); + } game.fireEvent(new GameEventCardStatsChanged(gameCard)); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index e877dd900be..7c33c468ddb 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -123,6 +123,8 @@ public class Card extends GameEntity implements Comparable { private final NavigableMap clonedStates = Maps.newTreeMap(); private final NavigableMap textChangeStates = Maps.newTreeMap(); + private final Map canBlockAdditional = Maps.newTreeMap(); + private final Set canBlockAny = Sets.newHashSet(); // changes that say "replace each instance of one [color,type] by another - timestamp is the key of maps private final CardChangedWords changedTextColors = new CardChangedWords(); @@ -2045,6 +2047,17 @@ public class Card extends GameEntity implements Comparable { sb.append("is goaded by: ").append(Lang.joinHomogenous(getGoaded())); sb.append("\r\n"); } + + // maybe move to CardView for better output + if (canBlockAny()) { + sb.append("CARDNAME can block any number of creatures."); + sb.append("\r\n"); + } else if (!canBlockAdditional.isEmpty()){ + sb.append("CARDNAME can block an additional "); + sb.append(Lang.nounWithNumeral(canBlockAdditional(), "creature")); + sb.append(" creatures each combat."); + sb.append("\r\n"); + } // replace triple line feeds with double line feeds int start; final String s = "\r\n\r\n\r\n"; @@ -6224,4 +6237,35 @@ public class Card extends GameEntity implements Comparable { numberTurnActivations.clear(); numberTurnActivationsStatic.clear(); } + + public void addCanBlockAdditional(int n, long timestamp) { + if (n <= 0) { + return; + } + canBlockAdditional.put(timestamp, n); + } + + public boolean removeCanBlockAdditional(long timestamp) { + return canBlockAdditional.remove(timestamp) != null; + } + + public int canBlockAdditional() { + int result = 0; + for (Integer v : canBlockAdditional.values()) { + result += v; + } + return result; + } + + public void addCanBlockAny(long timestamp) { + canBlockAny.add(timestamp); + } + + public boolean removeCanBlockAny(long timestamp) { + return canBlockAny.remove(timestamp) != null; + } + + public boolean canBlockAny() { + return !canBlockAny.isEmpty(); + } } diff --git a/forge-game/src/main/java/forge/game/card/CardView.java b/forge-game/src/main/java/forge/game/card/CardView.java index d452b7d8123..9636e19ff5a 100644 --- a/forge-game/src/main/java/forge/game/card/CardView.java +++ b/forge-game/src/main/java/forge/game/card/CardView.java @@ -581,16 +581,6 @@ public class CardView extends GameEntityView { } String nonAbilityText = get(TrackableProperty.NonAbilityText); - int blockAdditional = state.getBlockAdditional(); - if (blockAdditional > 1) { - final StringBuilder ab = new StringBuilder(); - ab.append("CARDNAME can block an additional "); - ab.append(blockAdditional); - ab.append(" creatures each combat."); - nonAbilityText = nonAbilityText.replaceFirst("CARDNAME can block an additional creature each combat.", ab.toString()); - nonAbilityText = nonAbilityText.replaceAll("CARDNAME can block an additional creature each combat.", ""); - nonAbilityText = nonAbilityText.replaceAll("\r\n\r\n\r\n", ""); - } if (!nonAbilityText.isEmpty()) { sb.append("\r\n \r\nNon ability features: \r\n"); sb.append(nonAbilityText.replaceAll("CARDNAME", getName())); @@ -989,9 +979,6 @@ public class CardView extends GameEntityView { return get(TrackableProperty.HasTrample); } - public int getBlockAdditional() { - return get(TrackableProperty.BlockAdditional); - } public String getAbilityText() { return get(TrackableProperty.AbilityText); } @@ -1005,7 +992,6 @@ public class CardView extends GameEntityView { set(TrackableProperty.HasInfect, c.hasKeyword(Keyword.INFECT, state)); set(TrackableProperty.HasStorm, c.hasKeyword(Keyword.STORM, state)); set(TrackableProperty.HasTrample, c.hasKeyword(Keyword.TRAMPLE, state)); - set(TrackableProperty.BlockAdditional, c.getAmountOfKeyword("CARDNAME can block an additional creature each combat.", state)); updateAbilityText(c, state); } diff --git a/forge-game/src/main/java/forge/game/combat/CombatUtil.java b/forge-game/src/main/java/forge/game/combat/CombatUtil.java index 50d7c7aae98..1879bbca6d7 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-game/src/main/java/forge/game/combat/CombatUtil.java @@ -430,20 +430,13 @@ public class CombatUtil { } public static boolean canBlockMoreCreatures(final Card blocker, final CardCollectionView blockedBy) { - if (blockedBy.isEmpty() || blocker.hasKeyword("CARDNAME can block any number of creatures.")) { + if (blockedBy.isEmpty() || blocker.canBlockAny()) { return true; } - int canBlockMore = numberOfAdditionalCreaturesCanBlock(blocker); + int canBlockMore = blocker.canBlockAdditional(); return canBlockMore >= blockedBy.size(); } - public static int numberOfAdditionalCreaturesCanBlock(final Card blocker) { - // If Wizards makes a few more of these, we should really just make a generic version - return blocker.getAmountOfKeyword("CARDNAME can block an additional creature each combat.") + - blocker.getAmountOfKeyword("CARDNAME can block an additional ninety-nine creatures.") * 99 + - blocker.getAmountOfKeyword("CARDNAME can block an additional seven creatures each combat.") * 7; - } - // can the attacker be blocked at all? /** *

diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java index d429b7e3d70..21d4edff7d5 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbility.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbility.java @@ -166,7 +166,7 @@ public class StaticAbility extends CardTraitBase implements Comparable addHiddenKeywords = Lists.newArrayList(); @@ -186,16 +185,6 @@ public final class StaticAbilityContinuous { toughnessBonus = AbilityUtils.calculateAmount(hostCard, addT, stAb, true); } - if (params.containsKey("KeywordMultiplier")) { - final String multiplier = params.get("KeywordMultiplier"); - if (multiplier.equals("X")) { - keywordMultiplier = AbilityUtils.calculateAmount(hostCard, "X", stAb); - se.setXValue(keywordMultiplier); - } else { - keywordMultiplier = Integer.valueOf(multiplier); - } - } - if (layer == StaticAbilityLayer.ABILITIES2 && params.containsKey("AddKeyword")) { addKeywords = params.get("AddKeyword").split(" & "); final Iterable chosencolors = hostCard.getChosenColors(); @@ -448,9 +437,7 @@ public final class StaticAbilityContinuous { // add keywords if (addKeywords != null) { - for (int i = 0; i < keywordMultiplier; i++) { - p.addChangedKeywords(addKeywords, removeKeywords == null ? new String[0] : removeKeywords, se.getTimestamp()); - } + p.addChangedKeywords(addKeywords, removeKeywords == null ? new String[0] : removeKeywords, se.getTimestamp()); } // add static abilities @@ -577,9 +564,7 @@ public final class StaticAbilityContinuous { // add HIDDEN keywords if (!addHiddenKeywords.isEmpty()) { for (final String k : addHiddenKeywords) { - for (int j = 0; j < keywordMultiplier; j++) { - affectedCard.addHiddenExtrinsicKeyword(k); - } + affectedCard.addHiddenExtrinsicKeyword(k); } } @@ -718,8 +703,17 @@ public final class StaticAbilityContinuous { } } - if (layer == StaticAbilityLayer.RULES && params.containsKey("Goad")) { - affectedCard.addGoad(se.getTimestamp(), hostCard.getController()); + if (layer == StaticAbilityLayer.RULES) { + if (params.containsKey("Goad")) { + affectedCard.addGoad(se.getTimestamp(), hostCard.getController()); + } + if (params.containsKey("CanBlockAny")) { + affectedCard.addCanBlockAny(se.getTimestamp()); + } + if (params.containsKey("CanBlockAmount")) { + int v = AbilityUtils.calculateAmount(hostCard, params.get("CanBlockAmount"), stAb, true); + affectedCard.addCanBlockAdditional(v, se.getTimestamp()); + } } if (mayLookAt != null) { diff --git a/forge-game/src/main/java/forge/trackable/TrackableProperty.java b/forge-game/src/main/java/forge/trackable/TrackableProperty.java index 232788992bd..47b975554f9 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableProperty.java +++ b/forge-game/src/main/java/forge/trackable/TrackableProperty.java @@ -88,7 +88,6 @@ public enum TrackableProperty { HasTrample(TrackableTypes.BooleanType), YouMayLook(TrackableTypes.BooleanType), OpponentMayLook(TrackableTypes.BooleanType), - BlockAdditional(TrackableTypes.IntegerType), AbilityText(TrackableTypes.StringType), NonAbilityText(TrackableTypes.StringType), FoilIndex(TrackableTypes.IntegerType),