CanBlockAny: rewrite for 'can block an additional' and 'can block any number'

This commit is contained in:
Hanmac
2019-09-01 10:41:32 +02:00
parent d3faa740f1
commit 357f146742
11 changed files with 87 additions and 156 deletions

View File

@@ -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--;
}
}

View File

@@ -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")) {

View File

@@ -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")) {

View File

@@ -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<Player> 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;
}
/**
* <p>
* Setter for the field <code>keywordNumber</code>.
* </p>
*
* @param i
* a int.
*/
public final void setKeywordNumber(final int i) {
this.keywordNumber = i;
}
/**
* <p>
* Getter for the field <code>keywordNumber</code>.
* </p>
*
* @return a int.
*/
public final int getKeywordNumber() {
return this.keywordNumber;
}
/**
* <p>
* Getter for the field <code>affectedCards</code>.
@@ -692,52 +663,6 @@ public class StaticEffect {
this.affectedPlayers = list;
}
/**
* <p>
* Setter for the field <code>xValue</code>.
* </p>
*
* @param x
* a int.
*/
public final void setXValue(final int x) {
this.xValue = x;
}
/**
* <p>
* Getter for the field <code>xValue</code>.
* </p>
*
* @return a int.
*/
public final int getXValue() {
return this.xValue;
}
/**
* <p>
* Setter for the field <code>yValue</code>.
* </p>
*
* @param y
* a int.
*/
public final void setYValue(final int y) {
this.yValue = y;
}
/**
* <p>
* Getter for the field <code>yValue</code>.
* </p>
*
* @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;

View File

@@ -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));
}

View File

@@ -123,6 +123,8 @@ public class Card extends GameEntity implements Comparable<Card> {
private final NavigableMap<Long, CardCloneStates> clonedStates = Maps.newTreeMap();
private final NavigableMap<Long, CardCloneStates> textChangeStates = Maps.newTreeMap();
private final Map<Long, Integer> canBlockAdditional = Maps.newTreeMap();
private final Set<Long> 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<Card> {
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<Card> {
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();
}
}

View File

@@ -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);
}

View File

@@ -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?
/**
* <p>

View File

@@ -166,7 +166,7 @@ public class StaticAbility extends CardTraitBase implements Comparable<StaticAbi
layers.add(StaticAbilityLayer.RULES);
}
if (hasParam("IgnoreEffectCost") || hasParam("Goad")) {
if (hasParam("IgnoreEffectCost") || hasParam("Goad") || hasParam("CanBlockAny") || hasParam("CanBlockAmount")) {
layers.add(StaticAbilityLayer.RULES);
}

View File

@@ -110,7 +110,6 @@ public final class StaticAbilityContinuous {
int setPower = Integer.MAX_VALUE;
String setT = "";
int setToughness = Integer.MAX_VALUE;
int keywordMultiplier = 1;
String[] addKeywords = null;
List<String> 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<String> 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) {

View File

@@ -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),