mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 09:48:02 +00:00
CanBlockAny: rewrite for 'can block an additional' and 'can block any number'
This commit is contained in:
@@ -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--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user