mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Haunt fix
This commit is contained in:
@@ -432,7 +432,7 @@ public class ComputerUtilCard {
|
||||
* @param list
|
||||
* @return a {@link forge.game.card.Card} object.
|
||||
*/
|
||||
public static Card getBestCreatureToBounceAI(final CardCollectionView list) {
|
||||
public static Card getBestCreatureToBounceAI(final Iterable<Card> list) {
|
||||
if (Iterables.size(list) == 1) {
|
||||
return Iterables.get(list, 0);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
|
||||
@Override
|
||||
public SpellAbility getAbilityToPlay(Card hostCard, List<SpellAbility> abilities, ITriggerEvent triggerEvent) {
|
||||
if (abilities.size() == 0) {
|
||||
if (abilities.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return abilities.get(0);
|
||||
|
||||
@@ -1216,7 +1216,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
if (choice == null) { // can't find anything left
|
||||
if (sa.getTargets().size() == 0 || !sa.isTargetNumberValid()) {
|
||||
if (sa.getTargets().isEmpty() || !sa.isTargetNumberValid()) {
|
||||
if (!mandatory) {
|
||||
sa.resetTargets();
|
||||
}
|
||||
@@ -1432,7 +1432,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
|
||||
CardCollection list = new CardCollection(CardUtil.getValidCardsToTarget(tgt, sa));
|
||||
List<Card> list = CardUtil.getValidCardsToTarget(tgt, sa);
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.Map;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.ai.AiController;
|
||||
import forge.ai.AiPlayerPredicates;
|
||||
@@ -197,8 +196,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
|
||||
// mass zone change for creatures: if in dire danger, do it; otherwise, only do it if the opponent's
|
||||
// creatures are better in value
|
||||
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
||||
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
||||
if (CardLists.getNotType(oppType, "Creature").isEmpty() && CardLists.getNotType(computerType, "Creature").isEmpty()) {
|
||||
if (game.getCombat() != null && ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
|
||||
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS)
|
||||
&& game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
|
||||
@@ -225,17 +223,15 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
} else if (origin.equals(ZoneType.Graveyard)) {
|
||||
if (sa.usesTargeting()) {
|
||||
// search targetable Opponents
|
||||
final Iterable<Player> oppList = Iterables.filter(ai.getOpponents(),
|
||||
PlayerPredicates.isTargetableBy(sa));
|
||||
final PlayerCollection oppList = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
|
||||
|
||||
if (Iterables.isEmpty(oppList)) {
|
||||
if (oppList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the one with the most in graveyard
|
||||
// zone is visible so evaluate which would be hurt the most
|
||||
Player oppTarget = Collections.max(Lists.newArrayList(oppList),
|
||||
AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
||||
Player oppTarget = Collections.max(oppList, AiPlayerPredicates.compareByZoneValue(sa.getParam("ChangeType"), origin, sa));
|
||||
|
||||
// set the target
|
||||
if (!oppTarget.getCardsIn(ZoneType.Graveyard).isEmpty()) {
|
||||
@@ -270,18 +266,14 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
return (curHandSize + minAdv - 1 < numExiledWithSrc) || (!noDiscard && numExiledWithSrc >= ai.getMaxHandSize());
|
||||
}
|
||||
} else if (origin.equals(ZoneType.Stack)) {
|
||||
// time stop can do something like this:
|
||||
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
|
||||
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
|
||||
// otherwise, this situation doesn't exist
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destination.equals(ZoneType.Battlefield)) {
|
||||
if (sa.hasParam("GainControl")) {
|
||||
// Check if the cards are valuable enough
|
||||
if (CardLists.getNotType(oppType, "Creature").size() == 0
|
||||
&& CardLists.getNotType(computerType, "Creature").size() == 0) {
|
||||
if (CardLists.getNotType(oppType, "Creature").isEmpty() && CardLists.getNotType(computerType, "Creature").isEmpty()) {
|
||||
if ((ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard
|
||||
.evaluateCreatureList(oppType)) < 400) {
|
||||
return false;
|
||||
@@ -294,8 +286,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
}
|
||||
} else {
|
||||
// don't activate if human gets more back than AI does
|
||||
if ((CardLists.getNotType(oppType, "Creature").size() == 0)
|
||||
&& (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
||||
if (CardLists.getNotType(oppType, "Creature").isEmpty() && CardLists.getNotType(computerType, "Creature").isEmpty()) {
|
||||
if (ComputerUtilCard.evaluateCreatureList(computerType) <= (ComputerUtilCard
|
||||
.evaluateCreatureList(oppType) + 100)) {
|
||||
return false;
|
||||
@@ -354,8 +345,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
return true;
|
||||
|
||||
// if AI creature is better than Human Creature
|
||||
return ComputerUtilCard.evaluateCreatureList(aiCards) >= ComputerUtilCard
|
||||
.evaluateCreatureList(humanCards);
|
||||
return ComputerUtilCard.evaluateCreatureList(aiCards) >= ComputerUtilCard.evaluateCreatureList(humanCards);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -416,15 +406,13 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
// if the AI is using it defensively, then something else needs to occur
|
||||
// if only creatures are affected evaluate both lists and pass only
|
||||
// if human creatures are more valuable
|
||||
if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) {
|
||||
if (ComputerUtilCard.evaluateCreatureList(computerType) >= ComputerUtilCard
|
||||
.evaluateCreatureList(humanType)) {
|
||||
if (CardLists.getNotType(humanType, "Creature").isEmpty() && CardLists.getNotType(computerType, "Creature").isEmpty()) {
|
||||
if (ComputerUtilCard.evaluateCreatureList(computerType) >= ComputerUtilCard.evaluateCreatureList(humanType)) {
|
||||
return false;
|
||||
}
|
||||
} // otherwise evaluate both lists by CMC and pass only if human
|
||||
// permanents are more valuable
|
||||
else if (ComputerUtilCard.evaluatePermanentList(computerType) >= ComputerUtilCard
|
||||
.evaluatePermanentList(humanType)) {
|
||||
else if (ComputerUtilCard.evaluatePermanentList(computerType) >= ComputerUtilCard.evaluatePermanentList(humanType)) {
|
||||
return false;
|
||||
}
|
||||
} else if (origin.equals(ZoneType.Graveyard)) {
|
||||
@@ -457,11 +445,7 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
} else if (origin.equals(ZoneType.Exile)) {
|
||||
|
||||
} else if (origin.equals(ZoneType.Stack)) {
|
||||
// time stop can do something like this:
|
||||
// Origin$ Stack | Destination$ Exile | SubAbility$ DBSkip
|
||||
// DBSKipToPhase | DB$SkipToPhase | Phase$ Cleanup
|
||||
// otherwise, this situation doesn't exist
|
||||
return false;
|
||||
// currently only exists indirectly (e.g. Summary Dismissal via PlayAi)
|
||||
}
|
||||
|
||||
if (destination.equals(ZoneType.Battlefield)) {
|
||||
@@ -469,25 +453,22 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
|
||||
if (mandatory) {
|
||||
return true;
|
||||
}
|
||||
if (sa.getParam("GainControl") != null) {
|
||||
if (sa.hasParam("GainControl")) {
|
||||
// Check if the cards are valuable enough
|
||||
if ((CardLists.getNotType(humanType, "Creature").size() == 0) && (CardLists.getNotType(computerType, "Creature").size() == 0)) {
|
||||
return (ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard
|
||||
.evaluateCreatureList(humanType)) >= 1;
|
||||
if (CardLists.getNotType(humanType, "Creature").isEmpty() && CardLists.getNotType(computerType, "Creature").isEmpty()) {
|
||||
return (ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard.evaluateCreatureList(humanType)) >= 1;
|
||||
} // otherwise evaluate both lists by CMC and pass only if human
|
||||
// permanents are less valuable
|
||||
else return (ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard
|
||||
return (ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard
|
||||
.evaluatePermanentList(humanType)) >= 1;
|
||||
} else {
|
||||
// don't activate if human gets more back than AI does
|
||||
if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) {
|
||||
return ComputerUtilCard.evaluateCreatureList(computerType) > ComputerUtilCard
|
||||
.evaluateCreatureList(humanType);
|
||||
} // otherwise evaluate both lists by CMC and pass only if human
|
||||
// permanents are less valuable
|
||||
else return ComputerUtilCard.evaluatePermanentList(computerType) > ComputerUtilCard
|
||||
.evaluatePermanentList(humanType);
|
||||
}
|
||||
|
||||
// don't activate if human gets more back than AI does
|
||||
if (CardLists.getNotType(humanType, "Creature").isEmpty() && CardLists.getNotType(computerType, "Creature").isEmpty()) {
|
||||
return ComputerUtilCard.evaluateCreatureList(computerType) > ComputerUtilCard.evaluateCreatureList(humanType);
|
||||
} // otherwise evaluate both lists by CMC and pass only if human
|
||||
// permanents are less valuable
|
||||
return ComputerUtilCard.evaluatePermanentList(computerType) > ComputerUtilCard.evaluatePermanentList(humanType);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -59,7 +59,7 @@ public class ClashAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
|
||||
for (Player p : options) {
|
||||
if (p.getCardsIn(ZoneType.Library).size() == 0)
|
||||
if (p.getCardsIn(ZoneType.Library).isEmpty())
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,7 @@ public class CounterAi extends SpellAbilityAi {
|
||||
|
||||
if (sa.usesTargeting()) {
|
||||
final SpellAbility topSA = ComputerUtilAbility.getTopSpellAbilityOnStack(game, sa);
|
||||
if ((topSA.isSpell() && !CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa)) || topSA.getActivatingPlayer() == ai
|
||||
|| ai.getAllies().contains(topSA.getActivatingPlayer())) {
|
||||
if ((topSA.isSpell() && !CardFactoryUtil.isCounterableBy(topSA.getHostCard(), sa)) || ai.getYourTeam().contains(topSA.getActivatingPlayer())) {
|
||||
// might as well check for player's friendliness
|
||||
return false;
|
||||
} else if (sa.hasParam("ConditionWouldDestroy") && !CounterEffect.checkForConditionWouldDestroy(sa, topSA)) {
|
||||
|
||||
@@ -89,7 +89,7 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
// filter AIs battlefield by what I can target
|
||||
List<Card> targetables = CardLists.getTargetableCards(ai.getCardsIn(ZoneType.Battlefield), sa);
|
||||
|
||||
if (targetables.size() == 0) {
|
||||
if (targetables.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -153,11 +153,11 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
CardCollectionView targetables = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
|
||||
final List<Card> compTargetables = CardLists.filterControlledBy(targetables, ai);
|
||||
|
||||
if (targetables.size() == 0) {
|
||||
if (targetables.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mandatory && compTargetables.size() == 0) {
|
||||
if (!mandatory && compTargetables.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class RegenerateAllAi extends SpellAbilityAi {
|
||||
list = CardLists.getValidCards(list, valid, hostCard.getController(), hostCard, sa);
|
||||
list = CardLists.filter(list, CardPredicates.isController(ai));
|
||||
|
||||
if (list.size() == 0) {
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -339,7 +339,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
final String fromGraveyard = " from the graveyard";
|
||||
|
||||
if (destination.equals(ZoneType.Battlefield)) {
|
||||
final boolean attacking = (sa.hasParam("Attacking"));
|
||||
final boolean attacking = sa.hasParam("Attacking");
|
||||
if (ZoneType.Graveyard.equals(origin)) {
|
||||
sb.append("Return").append(targetname).append(fromGraveyard).append(" to the battlefield");
|
||||
} else {
|
||||
@@ -565,6 +565,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
|
||||
// If a card is moved to library from the stack, remove its spells from the stack
|
||||
if (sa.hasParam("Fizzle")) {
|
||||
// TODO only AI still targets as card, try to remove it
|
||||
if (gameCard.isInZone(ZoneType.Exile) || gameCard.isInZone(ZoneType.Hand) || gameCard.isInZone(ZoneType.Stack)) {
|
||||
// This only fizzles spells, not anything else.
|
||||
game.getStack().remove(gameCard);
|
||||
|
||||
@@ -1318,8 +1318,8 @@ public class CardFactoryUtil {
|
||||
|
||||
// Second, create the trigger that runs when the haunted creature dies
|
||||
final StringBuilder sbDies = new StringBuilder();
|
||||
sbDies.append("Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Exile |");
|
||||
sbDies.append("ValidCard$ Creature.HauntedBy | Execute$ ").append(hauntSVarName);
|
||||
sbDies.append("Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Exile");
|
||||
sbDies.append(" | ValidCard$ Creature.HauntedBy | Execute$ ").append(hauntSVarName);
|
||||
sbDies.append(" | TriggerDescription$ ").append(hauntDescription);
|
||||
|
||||
final Trigger hauntedDies = TriggerHandler.parseTrigger(sbDies.toString(), card, intrinsic);
|
||||
@@ -1369,7 +1369,7 @@ public class CardFactoryUtil {
|
||||
// First, create trigger that runs when the haunter goes to the graveyard
|
||||
final StringBuilder sbHaunter = new StringBuilder();
|
||||
sbHaunter.append("Mode$ ChangesZone | Origin$ ");
|
||||
sbHaunter.append(card.isCreature() ? "Battlefield" : "Stack | ResolvedCard$ True");
|
||||
sbHaunter.append(card.isCreature() ? "Battlefield" : "Stack | Fizzle$ False");
|
||||
sbHaunter.append(" | Destination$ Graveyard | ValidCard$ Card.Self");
|
||||
sbHaunter.append(" | Secondary$ True | TriggerDescription$ Haunt (").append(inst.getReminderText()).append(")");
|
||||
|
||||
|
||||
@@ -135,10 +135,14 @@ public class TriggerChangesZone extends Trigger {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParam("ResolvedCard")) {
|
||||
if (hasParam("Fizzle")) {
|
||||
if (!runParams.containsKey(AbilityKey.Fizzle)) {
|
||||
return false;
|
||||
}
|
||||
Boolean val = (Boolean) runParams.get(AbilityKey.Fizzle);
|
||||
if ("True".equals(getParam("Fizzle")) != val) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check number of lands ETB this turn on triggered card's controller
|
||||
|
||||
Reference in New Issue
Block a user