mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 02:38:02 +00:00
Merge branch 'fix' into 'master'
Simulation: fix for Pack Rat tokens (bad diff because of missing CMC) See merge request core-developers/forge!5980
This commit is contained in:
@@ -514,9 +514,8 @@ public class AiAttackController {
|
|||||||
if (Iterables.any(oppBattlefield, Predicates.and(CardPredicates.Presets.UNTAPPED, CardPredicates.Presets.LANDS))) {
|
if (Iterables.any(oppBattlefield, Predicates.and(CardPredicates.Presets.UNTAPPED, CardPredicates.Presets.LANDS))) {
|
||||||
maxBlockersAfterCrew = Integer.MAX_VALUE;
|
maxBlockersAfterCrew = Integer.MAX_VALUE;
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
maxBlockersAfterCrew--;
|
|
||||||
}
|
}
|
||||||
|
maxBlockersAfterCrew--;
|
||||||
} else if (cardType.hasSubtype("Vehicle") && !cardType.isCreature()) {
|
} else if (cardType.hasSubtype("Vehicle") && !cardType.isCreature()) {
|
||||||
maxBlockersAfterCrew--;
|
maxBlockersAfterCrew--;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2436,7 +2436,7 @@ public class ComputerUtil {
|
|||||||
CardCollection cardsInPlay = CardLists.getNotType(game.getCardsIn(ZoneType.Battlefield), "Land");
|
CardCollection cardsInPlay = CardLists.getNotType(game.getCardsIn(ZoneType.Battlefield), "Land");
|
||||||
CardCollection humanlist = CardLists.filterControlledBy(cardsInPlay, ai.getOpponents());
|
CardCollection humanlist = CardLists.filterControlledBy(cardsInPlay, ai.getOpponents());
|
||||||
CardCollection computerlist = ai.getCreaturesInPlay();
|
CardCollection computerlist = ai.getCreaturesInPlay();
|
||||||
return (ComputerUtilCard.evaluatePermanentList(computerlist) + 3) < ComputerUtilCard.evaluatePermanentList(humanlist) ? "Carnage" : "Homage";
|
return ComputerUtilCard.evaluatePermanentList(computerlist) + 3 < ComputerUtilCard.evaluatePermanentList(humanlist) ? "Carnage" : "Homage";
|
||||||
case "Judgment":
|
case "Judgment":
|
||||||
if (votes.isEmpty()) {
|
if (votes.isEmpty()) {
|
||||||
CardCollection list = new CardCollection();
|
CardCollection list = new CardCollection();
|
||||||
@@ -2446,9 +2446,8 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ComputerUtilCard.getBestAI(list);
|
return ComputerUtilCard.getBestAI(list);
|
||||||
} else {
|
|
||||||
return Iterables.getFirst(votes.keySet(), null);
|
|
||||||
}
|
}
|
||||||
|
return Iterables.getFirst(votes.keySet(), null);
|
||||||
case "Protection":
|
case "Protection":
|
||||||
if (votes.isEmpty()) {
|
if (votes.isEmpty()) {
|
||||||
List<String> restrictedToColors = Lists.newArrayList();
|
List<String> restrictedToColors = Lists.newArrayList();
|
||||||
@@ -2459,9 +2458,8 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
CardCollection lists = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents());
|
CardCollection lists = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents());
|
||||||
return StringUtils.capitalize(ComputerUtilCard.getMostProminentColor(lists, restrictedToColors));
|
return StringUtils.capitalize(ComputerUtilCard.getMostProminentColor(lists, restrictedToColors));
|
||||||
} else {
|
|
||||||
return Iterables.getFirst(votes.keySet(), null);
|
|
||||||
}
|
}
|
||||||
|
return Iterables.getFirst(votes.keySet(), null);
|
||||||
case "FeatherOrQuill":
|
case "FeatherOrQuill":
|
||||||
// try to mill opponent with Quill vote
|
// try to mill opponent with Quill vote
|
||||||
if (opponent && !controller.cantLose()) {
|
if (opponent && !controller.cantLose()) {
|
||||||
|
|||||||
@@ -2292,9 +2292,8 @@ public class ComputerUtilCombat {
|
|||||||
}
|
}
|
||||||
if (!withoutAbilities) {
|
if (!withoutAbilities) {
|
||||||
return canGainKeyword(combatant, Lists.newArrayList(keyword), combat);
|
return canGainKeyword(combatant, Lists.newArrayList(keyword), combat);
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static boolean canGainKeyword(final Card combatant, final List<String> keywords, final Combat combat) {
|
public final static boolean canGainKeyword(final Card combatant, final List<String> keywords, final Combat combat) {
|
||||||
|
|||||||
@@ -306,7 +306,6 @@ public class ComputerUtilMana {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saHost != null) {
|
|
||||||
if (ma.getPayCosts().hasTapCost() && AiCardMemory.isRememberedCard(ai, ma.getHostCard(), MemorySet.PAYS_TAP_COST)) {
|
if (ma.getPayCosts().hasTapCost() && AiCardMemory.isRememberedCard(ai, ma.getHostCard(), MemorySet.PAYS_TAP_COST)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -353,7 +352,6 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
SpellAbility paymentChoice = ma;
|
SpellAbility paymentChoice = ma;
|
||||||
|
|
||||||
|
|||||||
@@ -1023,9 +1023,8 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
if (blink) {
|
if (blink) {
|
||||||
return c.isToken();
|
return c.isToken();
|
||||||
} else {
|
|
||||||
return c.isToken() || c.getCMC() > 0;
|
|
||||||
}
|
}
|
||||||
|
return c.isToken() || c.getCMC() > 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,7 +315,6 @@ public class ControlGainAi extends SpellAbilityAi {
|
|||||||
} else {
|
} else {
|
||||||
return this.canPlayAI(ai, sa);
|
return this.canPlayAI(ai, sa);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // pumpDrawbackAI()
|
} // pumpDrawbackAI()
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -153,12 +153,11 @@ public abstract class DamageAiBase extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
if (value < 0.2f) { //hard floor to reduce ridiculous odds for instants over time
|
if (value < 0.2f) { //hard floor to reduce ridiculous odds for instants over time
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
final float chance = MyRandom.getRandom().nextFloat();
|
final float chance = MyRandom.getRandom().nextFloat();
|
||||||
return chance < value;
|
return chance < value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -811,9 +811,8 @@ public class DamageDealAi extends DamageAiBase {
|
|||||||
if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && ComputerUtilCombat.getDamageToKill(c, false) <= restDamage) {
|
if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && ComputerUtilCombat.getDamageToKill(c, false) <= restDamage) {
|
||||||
if (c.getController().equals(ai)) {
|
if (c.getController().equals(ai)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
urgent = true;
|
|
||||||
}
|
}
|
||||||
|
urgent = true;
|
||||||
}
|
}
|
||||||
if (c.getController().isOpponentOf(ai) ^ c.getName().equals("Stuffy Doll")) {
|
if (c.getController().isOpponentOf(ai) ^ c.getName().equals("Stuffy Doll")) {
|
||||||
positive = true;
|
positive = true;
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ public class EffectAi extends SpellAbilityAi {
|
|||||||
final Card host = saTop.getHostCard();
|
final Card host = saTop.getHostCard();
|
||||||
if (saTop.getActivatingPlayer() != ai // from opponent
|
if (saTop.getActivatingPlayer() != ai // from opponent
|
||||||
&& host.canDamagePrevented(false) // no prevent damage
|
&& host.canDamagePrevented(false) // no prevent damage
|
||||||
&& host != null && (host.isInstant() || host.isSorcery())
|
&& (host.isInstant() || host.isSorcery())
|
||||||
&& !host.hasKeyword("Prevent all damage that would be dealt by CARDNAME.")) { // valid target
|
&& !host.hasKeyword("Prevent all damage that would be dealt by CARDNAME.")) { // valid target
|
||||||
final ApiType type = saTop.getApi();
|
final ApiType type = saTop.getApi();
|
||||||
if (type == ApiType.DealDamage || type == ApiType.DamageAll) { // burn spell
|
if (type == ApiType.DealDamage || type == ApiType.DamageAll) { // burn spell
|
||||||
|
|||||||
@@ -287,12 +287,11 @@ public class FightAi extends SpellAbilityAi {
|
|||||||
if (canKill(fighter, opponent, pumpAttack)) {
|
if (canKill(fighter, opponent, pumpAttack)) {
|
||||||
if (!canKill(opponent, fighter, -pumpDefense)) { // can survive
|
if (!canKill(opponent, fighter, -pumpDefense)) { // can survive
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
if (MyRandom.getRandom().nextInt(20) < (opponent.getCMC() - fighter.getCMC())) { // trade
|
if (MyRandom.getRandom().nextInt(20) < (opponent.getCMC() - fighter.getCMC())) { // trade
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,10 @@ public class GameCopier {
|
|||||||
if (card.isPaired()) {
|
if (card.isPaired()) {
|
||||||
otherCard.setPairedWith(cardMap.get(card.getPairedWith()));
|
otherCard.setPairedWith(cardMap.get(card.getPairedWith()));
|
||||||
}
|
}
|
||||||
|
if (card.getCopiedPermanent() != null) {
|
||||||
|
// TODO would it be safe to simply reuse the prototype?
|
||||||
|
otherCard.setCopiedPermanent(CardFactory.copyCard(card.getCopiedPermanent(), false));
|
||||||
|
}
|
||||||
// TODO: Verify that the above relationships are preserved bi-directionally or not.
|
// TODO: Verify that the above relationships are preserved bi-directionally or not.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -547,9 +547,8 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
public String getSVar(final String name) {
|
public String getSVar(final String name) {
|
||||||
if (sVars.containsKey(name)) {
|
if (sVars.containsKey(name)) {
|
||||||
return sVars.get(name);
|
return sVars.get(name);
|
||||||
} else {
|
|
||||||
return getSVarFallback().getSVar(name);
|
|
||||||
}
|
}
|
||||||
|
return getSVarFallback().getSVar(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1422,7 +1422,7 @@ public class AbilityUtils {
|
|||||||
|
|
||||||
// do blessing there before condition checks
|
// do blessing there before condition checks
|
||||||
if (sa.isSpell() && sa.isBlessing() && !sa.getHostCard().isPermanent()) {
|
if (sa.isSpell() && sa.isBlessing() && !sa.getHostCard().isPermanent()) {
|
||||||
if (pl != null && pl.getZone(ZoneType.Battlefield).size() >= 10) {
|
if (pl.getZone(ZoneType.Battlefield).size() >= 10) {
|
||||||
pl.setBlessing(true);
|
pl.setBlessing(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1871,9 +1871,8 @@ public class AbilityUtils {
|
|||||||
list = CardLists.getValidCards(list, k[1].split(","), sa.getActivatingPlayer(), c, sa);
|
list = CardLists.getValidCards(list, k[1].split(","), sa.getActivatingPlayer(), c, sa);
|
||||||
if (k[0].contains("TotalToughness")) {
|
if (k[0].contains("TotalToughness")) {
|
||||||
return doXMath(Aggregates.sum(list, CardPredicates.Accessors.fnGetNetToughness), expr, c, ctb);
|
return doXMath(Aggregates.sum(list, CardPredicates.Accessors.fnGetNetToughness), expr, c, ctb);
|
||||||
} else {
|
|
||||||
return doXMath(list.size(), expr, c, ctb);
|
|
||||||
}
|
}
|
||||||
|
return doXMath(list.size(), expr, c, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sq[0].startsWith("LastStateGraveyard")) {
|
if (sq[0].startsWith("LastStateGraveyard")) {
|
||||||
@@ -3208,9 +3207,8 @@ public class AbilityUtils {
|
|||||||
} else if (s[0].contains("DivideEvenlyDown")) {
|
} else if (s[0].contains("DivideEvenlyDown")) {
|
||||||
if (secondaryNum == 0) {
|
if (secondaryNum == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
|
||||||
return num / secondaryNum;
|
|
||||||
}
|
}
|
||||||
|
return num / secondaryNum;
|
||||||
} else if (s[0].contains("Mod")) {
|
} else if (s[0].contains("Mod")) {
|
||||||
return num % secondaryNum;
|
return num % secondaryNum;
|
||||||
} else if (s[0].contains("Abs")) {
|
} else if (s[0].contains("Abs")) {
|
||||||
@@ -3218,15 +3216,13 @@ public class AbilityUtils {
|
|||||||
} else if (s[0].contains("LimitMax")) {
|
} else if (s[0].contains("LimitMax")) {
|
||||||
if (num < secondaryNum) {
|
if (num < secondaryNum) {
|
||||||
return num;
|
return num;
|
||||||
} else {
|
|
||||||
return secondaryNum;
|
|
||||||
}
|
}
|
||||||
|
return secondaryNum;
|
||||||
} else if (s[0].contains("LimitMin")) {
|
} else if (s[0].contains("LimitMin")) {
|
||||||
if (num > secondaryNum) {
|
if (num > secondaryNum) {
|
||||||
return num;
|
return num;
|
||||||
} else {
|
|
||||||
return secondaryNum;
|
|
||||||
}
|
}
|
||||||
|
return secondaryNum;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return num;
|
return num;
|
||||||
|
|||||||
@@ -3037,7 +3037,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return tokenCard;
|
return tokenCard;
|
||||||
}
|
}
|
||||||
public final void setTokenCard(boolean tokenC) {
|
public final void setTokenCard(boolean tokenC) {
|
||||||
if (tokenCard = tokenC) { return; }
|
if (tokenCard == tokenC) { return; }
|
||||||
tokenCard = tokenC;
|
tokenCard = tokenC;
|
||||||
view.updateTokenCard(this);
|
view.updateTokenCard(this);
|
||||||
}
|
}
|
||||||
@@ -5232,9 +5232,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
// this is the amount of damage a creature needs to receive before it dies
|
// this is the amount of damage a creature needs to receive before it dies
|
||||||
public final int getLethal() {
|
public final int getLethal() {
|
||||||
if (hasKeyword("Lethal damage dealt to CARDNAME is determined by its power rather than its toughness.")) {
|
if (hasKeyword("Lethal damage dealt to CARDNAME is determined by its power rather than its toughness.")) {
|
||||||
return getNetPower(); }
|
return getNetPower();
|
||||||
else {
|
}
|
||||||
return getNetToughness(); }
|
return getNetToughness();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is the minimal damage a trampling creature has to assign to a blocker
|
// this is the minimal damage a trampling creature has to assign to a blocker
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ public class TokenInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!typeMap.isEmpty()) {
|
if (!typeMap.isEmpty()) {
|
||||||
|
|
||||||
CardType type = new CardType(result.getType());
|
CardType type = new CardType(result.getType());
|
||||||
final boolean nameGenerated = result.getName().endsWith(" Token");
|
final boolean nameGenerated = result.getName().endsWith(" Token");
|
||||||
boolean typeChanged = false;
|
boolean typeChanged = false;
|
||||||
|
|||||||
@@ -135,8 +135,7 @@ public class CostDiscard extends CostPartWithList {
|
|||||||
if (this.payCostFromSource()) {
|
if (this.payCostFromSource()) {
|
||||||
return source.canBeDiscardedBy(ability, effect);
|
return source.canBeDiscardedBy(ability, effect);
|
||||||
}
|
}
|
||||||
else {
|
else if (type.equals("Hand")) {
|
||||||
if (type.equals("Hand")) {
|
|
||||||
// trying to discard an empty hand always work even with Tamiyo
|
// trying to discard an empty hand always work even with Tamiyo
|
||||||
if (payer.getZone(ZoneType.Hand).isEmpty()) {
|
if (payer.getZone(ZoneType.Hand).isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -186,7 +185,6 @@ public class CostDiscard extends CostPartWithList {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,14 +148,13 @@ public class CostPartMana extends CostPart {
|
|||||||
int timesToPay = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getSVar("NumTimes"), sa);
|
int timesToPay = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getSVar("NumTimes"), sa);
|
||||||
if (timesToPay == 0) {
|
if (timesToPay == 0) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
}
|
||||||
ManaCostBeingPaid totalMana = new ManaCostBeingPaid(getManaToPay());
|
ManaCostBeingPaid totalMana = new ManaCostBeingPaid(getManaToPay());
|
||||||
for (int i = 1; i < timesToPay; i++) {
|
for (int i = 1; i < timesToPay; i++) {
|
||||||
totalMana.addManaCost(getManaToPay());
|
totalMana.addManaCost(getManaToPay());
|
||||||
}
|
}
|
||||||
return totalMana.toManaCost();
|
return totalMana.toManaCost();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return getManaToPay();
|
return getManaToPay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -434,8 +434,8 @@ public class ReplacementHandler {
|
|||||||
private void getPossibleReplaceDamageList(PlayerCollection players, final boolean isCombat, final CardDamageMap damageMap, final SpellAbility cause) {
|
private void getPossibleReplaceDamageList(PlayerCollection players, final boolean isCombat, final CardDamageMap damageMap, final SpellAbility cause) {
|
||||||
for (Map.Entry<GameEntity, Map<Card, Integer>> et : damageMap.columnMap().entrySet()) {
|
for (Map.Entry<GameEntity, Map<Card, Integer>> et : damageMap.columnMap().entrySet()) {
|
||||||
final GameEntity target = et.getKey();
|
final GameEntity target = et.getKey();
|
||||||
int playerIndex = (target instanceof Player ? players.indexOf(((Player) target)) :
|
int playerIndex = target instanceof Player ? players.indexOf(((Player) target)) :
|
||||||
players.indexOf(((Card) target).getController()));
|
players.indexOf(((Card) target).getController());
|
||||||
if (playerIndex == -1) continue;
|
if (playerIndex == -1) continue;
|
||||||
Map<ReplacementEffect, List<Map<AbilityKey, Object>>> replaceCandidateMap = replaceDamageList.get(playerIndex);
|
Map<ReplacementEffect, List<Map<AbilityKey, Object>>> replaceCandidateMap = replaceDamageList.get(playerIndex);
|
||||||
for (Map.Entry<Card, Integer> e : et.getValue().entrySet()) {
|
for (Map.Entry<Card, Integer> e : et.getValue().entrySet()) {
|
||||||
@@ -501,8 +501,8 @@ public class ReplacementHandler {
|
|||||||
Map<ReplacementEffect, List<Map<AbilityKey, Object>>> newReplaceCandidateMap = replaceCandidateMap;
|
Map<ReplacementEffect, List<Map<AbilityKey, Object>>> newReplaceCandidateMap = replaceCandidateMap;
|
||||||
if (!target.equals(newTarget)) {
|
if (!target.equals(newTarget)) {
|
||||||
PlayerCollection players = game.getPlayersInTurnOrder();
|
PlayerCollection players = game.getPlayersInTurnOrder();
|
||||||
int playerIndex = (newTarget instanceof Player ? players.indexOf(((Player) newTarget)) :
|
int playerIndex = newTarget instanceof Player ? players.indexOf(((Player) newTarget)) :
|
||||||
players.indexOf(((Card) newTarget).getController()));
|
players.indexOf(((Card) newTarget).getController());
|
||||||
newReplaceCandidateMap = replaceDamageList.get(playerIndex);
|
newReplaceCandidateMap = replaceDamageList.get(playerIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -386,9 +386,8 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
|||||||
.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
||||||
return InputConfirm.confirm(this, CardView.get(attacker),
|
return InputConfirm.confirm(this, CardView.get(attacker),
|
||||||
localizer.getMessage("lblAssignCombatDamageWerentBlocked"));
|
localizer.getMessage("lblAssignCombatDamageWerentBlocked"));
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user