PaidLists now store if the CostPart source is linked to host

This commit is contained in:
tool4EvEr
2023-03-29 17:33:51 +02:00
parent 99f8cca233
commit 101306e61c
20 changed files with 125 additions and 113 deletions

View File

@@ -172,7 +172,7 @@ public class PlayAi extends SpellAbilityAi {
abCost = new Cost(sa.getParam("PlayCost"), false); abCost = new Cost(sa.getParam("PlayCost"), false);
} }
spell = (Spell) spell.copyWithDefinedCost(abCost); spell = (Spell) spell.copyWithManaCostReplaced(spell.getActivatingPlayer(), abCost);
} }
if (AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !(isOptional || sa.hasParam("Optional")), true)) { if (AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !(isOptional || sa.hasParam("Optional")), true)) {
// Before accepting, see if the spell has a valid number of targets (it should at this point). // Before accepting, see if the spell has a valid number of targets (it should at this point).

View File

@@ -144,7 +144,7 @@ public class AbilityUtils {
c = hostCard.getEnchantingCard(); c = hostCard.getEnchantingCard();
if (c == null && sa instanceof SpellAbility) { if (c == null && sa instanceof SpellAbility) {
SpellAbility root = ((SpellAbility)sa).getRootAbility(); SpellAbility root = ((SpellAbility)sa).getRootAbility();
CardCollection sacrificed = root.getPaidList("Sacrificed"); CardCollection sacrificed = root.getPaidList("Sacrificed", true);
if (sacrificed != null && !sacrificed.isEmpty()) { if (sacrificed != null && !sacrificed.isEmpty()) {
c = sacrificed.getFirst().getEnchantingCard(); c = sacrificed.getFirst().getEnchantingCard();
} }
@@ -742,29 +742,29 @@ public class AbilityUtils {
} else { // these ones only for handling lists } else { // these ones only for handling lists
Iterable<Card> list = null; Iterable<Card> list = null;
if (calcX[0].startsWith("Sacrificed")) { if (calcX[0].startsWith("Sacrificed")) {
list = sa.getRootAbility().getPaidList("Sacrificed"); list = sa.getRootAbility().getPaidList("Sacrificed", true);
} }
else if (calcX[0].startsWith("Discarded")) { else if (calcX[0].startsWith("Discarded")) {
final SpellAbility root = sa.getRootAbility(); final SpellAbility root = sa.getRootAbility();
list = root.getPaidList("Discarded"); list = root.getPaidList("Discarded", true);
if (null == list && root.isTrigger()) { if (null == list && root.isTrigger()) {
list = root.getHostCard().getSpellPermanent().getPaidList("Discarded"); list = root.getHostCard().getSpellPermanent().getPaidList("Discarded", true);
} }
} }
else if (calcX[0].startsWith("Exiled")) { else if (calcX[0].startsWith("Exiled")) {
list = sa.getRootAbility().getPaidList("Exiled"); list = sa.getRootAbility().getPaidList("Exiled", true);
} }
else if (calcX[0].startsWith("Milled")) { else if (calcX[0].startsWith("Milled")) {
list = sa.getRootAbility().getPaidList("Milled"); list = sa.getRootAbility().getPaidList("Milled", true);
} }
else if (calcX[0].startsWith("Tapped")) { else if (calcX[0].startsWith("Tapped")) {
list = sa.getRootAbility().getPaidList("Tapped"); list = sa.getRootAbility().getPaidList("Tapped", true);
} }
else if (calcX[0].startsWith("Revealed")) { else if (calcX[0].startsWith("Revealed")) {
list = sa.getRootAbility().getPaidList("Revealed"); list = sa.getRootAbility().getPaidList("Revealed", true);
} }
else if (calcX[0].startsWith("Returned")) { else if (calcX[0].startsWith("Returned")) {
list = sa.getRootAbility().getPaidList("Returned"); list = sa.getRootAbility().getPaidList("Returned", true);
} }
else if (calcX[0].startsWith("Targeted")) { else if (calcX[0].startsWith("Targeted")) {
list = sa.findTargetedCards(); list = sa.findTargetedCards();
@@ -1606,40 +1606,40 @@ public class AbilityUtils {
if (sa.hasParam("RememberCostCards") && !sa.getPaidHash().isEmpty()) { if (sa.hasParam("RememberCostCards") && !sa.getPaidHash().isEmpty()) {
List <Card> noList = Lists.newArrayList(); List <Card> noList = Lists.newArrayList();
Map<String, CardCollection> paidLists = sa.getPaidHash(); Table<String, Boolean, CardCollection> paidLists = sa.getPaidHash();
if (sa.hasParam("RememberCostExcept")) { if (sa.hasParam("RememberCostExcept")) {
noList.addAll(AbilityUtils.getDefinedCards(host, sa.getParam("RememberCostExcept"), sa)); noList.addAll(AbilityUtils.getDefinedCards(host, sa.getParam("RememberCostExcept"), sa));
} }
if (paidLists.containsKey("Exiled")) { if (paidLists.contains("Exiled", true)) {
final CardCollection paidListExiled = sa.getPaidList("Exiled"); final CardCollection paidListExiled = sa.getPaidList("Exiled", true);
for (final Card exiledAsCost : paidListExiled) { for (final Card exiledAsCost : paidListExiled) {
if (!noList.contains(exiledAsCost)) { if (!noList.contains(exiledAsCost)) {
host.addRemembered(exiledAsCost); host.addRemembered(exiledAsCost);
} }
} }
} else if (paidLists.containsKey("Sacrificed")) { } else if (paidLists.contains("Sacrificed", true)) {
final CardCollection paidListSacrificed = sa.getPaidList("Sacrificed"); final CardCollection paidListSacrificed = sa.getPaidList("Sacrificed", true);
for (final Card sacrificedAsCost : paidListSacrificed) { for (final Card sacrificedAsCost : paidListSacrificed) {
if (!noList.contains(sacrificedAsCost)) { if (!noList.contains(sacrificedAsCost)) {
host.addRemembered(sacrificedAsCost); host.addRemembered(sacrificedAsCost);
} }
} }
} else if (paidLists.containsKey("Tapped")) { } else if (paidLists.contains("Tapped", true)) {
final CardCollection paidListTapped = sa.getPaidList("Tapped"); final CardCollection paidListTapped = sa.getPaidList("Tapped", true);
for (final Card tappedAsCost : paidListTapped) { for (final Card tappedAsCost : paidListTapped) {
if (!noList.contains(tappedAsCost)) { if (!noList.contains(tappedAsCost)) {
host.addRemembered(tappedAsCost); host.addRemembered(tappedAsCost);
} }
} }
} else if (paidLists.containsKey("Unattached")) { } else if (paidLists.contains("Unattached", true)) {
final CardCollection paidListUnattached = sa.getPaidList("Unattached"); final CardCollection paidListUnattached = sa.getPaidList("Unattached", true);
for (final Card unattachedAsCost : paidListUnattached) { for (final Card unattachedAsCost : paidListUnattached) {
if (!noList.contains(unattachedAsCost)) { if (!noList.contains(unattachedAsCost)) {
host.addRemembered(unattachedAsCost); host.addRemembered(unattachedAsCost);
} }
} }
} else if (paidLists.containsKey("Discarded")) { } else if (paidLists.contains("Discarded", true)) {
final CardCollection paidListDiscarded = sa.getPaidList("Discarded"); final CardCollection paidListDiscarded = sa.getPaidList("Discarded", true);
for (final Card discardedAsCost : paidListDiscarded) { for (final Card discardedAsCost : paidListDiscarded) {
if (!noList.contains(discardedAsCost)) { if (!noList.contains(discardedAsCost)) {
host.addRemembered(discardedAsCost); host.addRemembered(discardedAsCost);
@@ -3860,29 +3860,29 @@ public class AbilityUtils {
SpellAbility root = ((SpellAbility)sa).getRootAbility(); SpellAbility root = ((SpellAbility)sa).getRootAbility();
// TODO do we really need these checks? // TODO do we really need these checks?
if (defined.startsWith("SacrificedCards")) { if (defined.startsWith("SacrificedCards")) {
list = root.getPaidList("SacrificedCards"); list = root.getPaidList("SacrificedCards", true);
} else if (defined.startsWith("Sacrificed")) { } else if (defined.startsWith("Sacrificed")) {
list = root.getPaidList("Sacrificed"); list = root.getPaidList("Sacrificed", true);
} else if (defined.startsWith("Revealed")) { } else if (defined.startsWith("Revealed")) {
list = root.getPaidList("Revealed"); list = root.getPaidList("Revealed", true);
} else if (defined.startsWith("DiscardedCards")) { } else if (defined.startsWith("DiscardedCards")) {
list = root.getPaidList("DiscardedCards"); list = root.getPaidList("DiscardedCards", true);
} else if (defined.startsWith("Discarded")) { } else if (defined.startsWith("Discarded")) {
list = root.getPaidList("Discarded"); list = root.getPaidList("Discarded", true);
} else if (defined.startsWith("ExiledCards")) { } else if (defined.startsWith("ExiledCards")) {
list = root.getPaidList("ExiledCards"); list = root.getPaidList("ExiledCards", true);
} else if (defined.startsWith("Exiled")) { } else if (defined.startsWith("Exiled")) {
list = root.getPaidList("Exiled"); list = root.getPaidList("Exiled", true);
} else if (defined.startsWith("Milled")) { } else if (defined.startsWith("Milled")) {
list = root.getPaidList("Milled"); list = root.getPaidList("Milled", true);
} else if (defined.startsWith("TappedCards")) { } else if (defined.startsWith("TappedCards")) {
list = root.getPaidList("TappedCards"); list = root.getPaidList("TappedCards", true);
} else if (defined.startsWith("Tapped")) { } else if (defined.startsWith("Tapped")) {
list = root.getPaidList("Tapped"); list = root.getPaidList("Tapped", true);
} else if (defined.startsWith("UntappedCards")) { } else if (defined.startsWith("UntappedCards")) {
list = root.getPaidList("UntappedCards"); list = root.getPaidList("UntappedCards", true);
} else if (defined.startsWith("Untapped")) { } else if (defined.startsWith("Untapped")) {
list = root.getPaidList("Untapped"); list = root.getPaidList("Untapped", true);
} }
} }
return list; return list;

View File

@@ -696,7 +696,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
} }
if (sa.isNinjutsu()) { if (sa.isNinjutsu()) {
// Ninjutsu need to get the Defender of the Returned Creature // Ninjutsu need to get the Defender of the Returned Creature
final Card returned = sa.getPaidList("Returned").getFirst(); final Card returned = sa.getPaidList("Returned", true).getFirst();
final GameEntity defender = game.getCombat().getDefenderByAttacker(returned); final GameEntity defender = game.getCombat().getDefenderByAttacker(returned);
game.getCombat().addAttacker(movedCard, defender); game.getCombat().addAttacker(movedCard, defender);
game.getCombat().getBandOfAttacker(movedCard).setBlocked(false); game.getCombat().getBandOfAttacker(movedCard).setBlocked(false);

View File

@@ -371,7 +371,7 @@ public class PlayEffect extends SpellAbilityEffect {
abCost = new Cost(cost, false); abCost = new Cost(cost, false);
} }
tgtSA = tgtSA.copyWithDefinedCost(abCost); tgtSA = tgtSA.copyWithManaCostReplaced(tgtSA.getActivatingPlayer(), abCost);
} }
if (!optional) { if (!optional) {

View File

@@ -5847,7 +5847,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
timesCrewedThisTurn += 1; timesCrewedThisTurn += 1;
Map<AbilityKey, Object> runParams = AbilityKey.newMap(); Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Vehicle, this); runParams.put(AbilityKey.Vehicle, this);
runParams.put(AbilityKey.Crew, sa.getPaidList("TappedCards")); runParams.put(AbilityKey.Crew, sa.getPaidList("TappedCards", true));
game.getTriggerHandler().runTrigger(TriggerType.BecomesCrewed, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.BecomesCrewed, runParams, false);
} }

View File

@@ -866,9 +866,9 @@ public class CardProperty {
} else if (restriction.equals("MovedToGrave")) { } else if (restriction.equals("MovedToGrave")) {
if (!(spellAbility instanceof SpellAbility)) { if (!(spellAbility instanceof SpellAbility)) {
final SpellAbility root = ((SpellAbility) spellAbility).getRootAbility(); final SpellAbility root = ((SpellAbility) spellAbility).getRootAbility();
if (root != null && (root.getPaidList("MovedToGrave") != null) if (root != null && (root.getPaidList("MovedToGrave", true) != null)
&& !root.getPaidList("MovedToGrave").isEmpty()) { && !root.getPaidList("MovedToGrave", true).isEmpty()) {
final CardCollectionView cards = root.getPaidList("MovedToGrave"); final CardCollectionView cards = root.getPaidList("MovedToGrave", true);
for (final Card c : cards) { for (final Card c : cards) {
String name = c.getName(); String name = c.getName();
if (StringUtils.isEmpty(name)) { if (StringUtils.isEmpty(name)) {

View File

@@ -210,6 +210,10 @@ public class Cost implements Serializable {
costParts.add(new CostPartMana(cost, null)); costParts.add(new CostPartMana(cost, null));
} }
public Cost(String parse, final boolean bAbility) {
this(parse, bAbility, true);
}
/** /**
* <p> * <p>
* Constructor for Cost. * Constructor for Cost.
@@ -219,7 +223,7 @@ public class Cost implements Serializable {
* @param bAbility * @param bAbility
* a boolean. * a boolean.
*/ */
public Cost(String parse, final boolean bAbility) { public Cost(String parse, final boolean bAbility, final boolean intrinsic) {
this.isAbility = bAbility; this.isAbility = bAbility;
// when adding new costs for cost string, place them here // when adding new costs for cost string, place them here
@@ -249,6 +253,9 @@ public class Cost implements Serializable {
if (cp instanceof CostPartMana) { if (cp instanceof CostPartMana) {
parsedMana = (CostPartMana) cp; parsedMana = (CostPartMana) cp;
} else { } else {
if (cp instanceof CostPartWithList) {
((CostPartWithList)cp).setIntrinsic(intrinsic);
}
this.costParts.add(cp); this.costParts.add(cp);
} }
else else

View File

@@ -161,7 +161,7 @@ public class CostAdjustment {
count = 1; count = 1;
} }
if (count > 0) { if (count > 0) {
Cost part = new Cost(scost, sa.isAbility()); Cost part = new Cost(scost, sa.isAbility(), sa.getHostCard().equals(hostCard));
cost.mergeTo(part, count); cost.mergeTo(part, count);
} }
} }

View File

@@ -105,7 +105,7 @@ public class CostExileFromStack extends CostPart {
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) { public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability, final boolean effect) {
Game game = ai.getGame(); Game game = ai.getGame();
for (final SpellAbility sa : decision.sp) { for (final SpellAbility sa : decision.sp) {
ability.addCostToHashList(CardUtil.getLKICopy(sa.getHostCard()), "Exiled"); ability.addCostToHashList(CardUtil.getLKICopy(sa.getHostCard()), "Exiled", true);
SpellAbilityStackInstance si = game.getStack().getInstanceFromSpellAbility(sa); SpellAbilityStackInstance si = game.getStack().getInstanceFromSpellAbility(sa);
if (si != null) { if (si != null) {
game.getStack().remove(si); game.getStack().remove(si);

View File

@@ -94,7 +94,7 @@ public class CostMill extends CostPart {
Map<AbilityKey, Object> moveParams = AbilityKey.newMap(); Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.LastStateBattlefield, ai.getGame().getLastStateBattlefield()); moveParams.put(AbilityKey.LastStateBattlefield, ai.getGame().getLastStateBattlefield());
moveParams.put(AbilityKey.LastStateGraveyard, ai.getGame().getLastStateGraveyard()); moveParams.put(AbilityKey.LastStateGraveyard, ai.getGame().getLastStateGraveyard());
ability.getPaidHash().put("Milled", (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, false, ability, table, moveParams)); ability.getPaidHash().put("Milled", true, (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, false, ability, table, moveParams));
table.triggerChangesZoneAll(ai.getGame(), ability); table.triggerChangesZoneAll(ai.getGame(), ability);
return true; return true;
} }

View File

@@ -127,9 +127,9 @@ public class CostPartMana extends CostPart {
} }
public ManaCost getManaCostFor(SpellAbility sa) { public ManaCost getManaCostFor(SpellAbility sa) {
if (isExiledCreatureCost() && sa.getPaidList(CostExile.HashLKIListKey)!= null && !sa.getPaidList(CostExile.HashLKIListKey).isEmpty()) { if (isExiledCreatureCost() && sa.getPaidList(CostExile.HashLKIListKey, true)!= null && !sa.getPaidList(CostExile.HashLKIListKey, true).isEmpty()) {
// back from the brink // back from the brink
return sa.getPaidList(CostExile.HashLKIListKey).get(0).getManaCost(); return sa.getPaidList(CostExile.HashLKIListKey, true).get(0).getManaCost();
} }
if (isEnchantedCreatureCost() && sa.getHostCard().isEnchantingCard()) { if (isEnchantedCreatureCost() && sa.getHostCard().isEnchantingCard()) {
return sa.getHostCard().getEnchantingCard().getManaCost(); return sa.getHostCard().getEnchantingCard().getManaCost();

View File

@@ -38,6 +38,8 @@ public abstract class CostPartWithList extends CostPart {
private final CardCollection lkiList = new CardCollection(); private final CardCollection lkiList = new CardCollection();
protected final CardCollection cardList = new CardCollection(); protected final CardCollection cardList = new CardCollection();
private boolean intrinsic = true;
protected final CardZoneTable table = new CardZoneTable(); protected final CardZoneTable table = new CardZoneTable();
// set is here because executePayment() adds card to list, while ai's decide payment does the same thing. // set is here because executePayment() adds card to list, while ai's decide payment does the same thing.
// set allows to avoid duplication // set allows to avoid duplication
@@ -50,6 +52,10 @@ public abstract class CostPartWithList extends CostPart {
return cardList; return cardList;
} }
public final void setIntrinsic(boolean b) {
intrinsic = b;
}
/** /**
* Reset list. * Reset list.
*/ */
@@ -71,11 +77,11 @@ public abstract class CostPartWithList extends CostPart {
} }
final String lkiPaymentMethod = getHashForLKIList(); final String lkiPaymentMethod = getHashForLKIList();
for (final Card card : lkiList) { for (final Card card : lkiList) {
sa.addCostToHashList(card, lkiPaymentMethod); sa.addCostToHashList(card, lkiPaymentMethod, intrinsic);
} }
final String cardPaymentMethod = getHashForCardList(); final String cardPaymentMethod = getHashForCardList();
for (final Card card : cardList) { for (final Card card : cardList) {
sa.addCostToHashList(card, cardPaymentMethod); sa.addCostToHashList(card, cardPaymentMethod, intrinsic);
} }
} }

View File

@@ -147,7 +147,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
private List<SpellAbility> paidAbilities = Lists.newArrayList(); private List<SpellAbility> paidAbilities = Lists.newArrayList();
private Integer xManaCostPaid = null; private Integer xManaCostPaid = null;
private HashMap<String, CardCollection> paidLists = Maps.newHashMap(); private TreeBasedTable<String, Boolean, CardCollection> paidLists = TreeBasedTable.create();
private EnumMap<AbilityKey, Object> triggeringObjects = AbilityKey.newMap(); private EnumMap<AbilityKey, Object> triggeringObjects = AbilityKey.newMap();
@@ -688,22 +688,29 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
// Combined PaidLists // Combined PaidLists
public Map<String, CardCollection> getPaidHash() { public TreeBasedTable<String, Boolean, CardCollection> getPaidHash() {
return paidLists; return paidLists;
} }
public void setPaidHash(final Map<String, CardCollection> hash) { public void setPaidHash(final TreeBasedTable<String, Boolean, CardCollection> hash) {
paidLists = Maps.newHashMap(hash); paidLists = TreeBasedTable.create(hash);
} }
public CardCollection getPaidList(final String str) { // use if it doesn't matter if payment was caused by extrinsic cost modifier
return paidLists.get(str); public Iterable<Card> getPaidList(final String str) {
return Iterables.concat(paidLists.row(str).values());
} }
public void addCostToHashList(final Card c, final String str) {
if (!paidLists.containsKey(str)) { public CardCollection getPaidList(final String str, final boolean intrinsic) {
paidLists.put(str, new CardCollection()); return paidLists.get(str, intrinsic);
} }
paidLists.get(str).add(c);
public void addCostToHashList(final Card c, final String str, final boolean intrinsic) {
if (!paidLists.contains(str, intrinsic)) {
paidLists.put(str, intrinsic, new CardCollection());
} }
paidLists.get(str, intrinsic).add(c);
}
public void resetPaidHash() { public void resetPaidHash() {
paidLists.clear(); paidLists.clear();
} }
@@ -1369,62 +1376,51 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
} }
} }
if (tr.isSameController()) { if (tr.isSameController() && entity instanceof Card) {
Player newController; Player newController;
if (entity instanceof Card) {
newController = ((Card) entity).getController(); newController = ((Card) entity).getController();
for (final Card c : targetChosen.getTargetCards()) { for (final Card c : targetChosen.getTargetCards()) {
if (entity != c && !c.getController().equals(newController)) if (entity != c && !c.getController().equals(newController))
return false; return false;
} }
} }
}
if (tr.isDifferentControllers()) { if (tr.isDifferentControllers() && entity instanceof Card) {
Player newController; Player newController;
if (entity instanceof Card) {
newController = ((Card) entity).getController(); newController = ((Card) entity).getController();
for (final Card c : targetChosen.getTargetCards()) { for (final Card c : targetChosen.getTargetCards()) {
if (entity != c && c.getController().equals(newController)) if (entity != c && c.getController().equals(newController))
return false; return false;
} }
} }
}
if (tr.isWithoutSameCreatureType()) { if (tr.isWithoutSameCreatureType() && entity instanceof Card) {
if (entity instanceof Card) {
for (final Card c : targetChosen.getTargetCards()) { for (final Card c : targetChosen.getTargetCards()) {
if (entity != c && c.sharesCreatureTypeWith((Card) entity)) { if (entity != c && c.sharesCreatureTypeWith((Card) entity)) {
return false; return false;
} }
} }
} }
}
if (tr.isWithSameCreatureType()) { if (tr.isWithSameCreatureType() && entity instanceof Card) {
if (entity instanceof Card) {
for (final Card c : targetChosen.getTargetCards()) { for (final Card c : targetChosen.getTargetCards()) {
if (entity != c && !c.sharesCreatureTypeWith((Card) entity)) { if (entity != c && !c.sharesCreatureTypeWith((Card) entity)) {
return false; return false;
} }
} }
} }
}
if (tr.isWithSameCardType()) { if (tr.isWithSameCardType() && entity instanceof Card) {
if (entity instanceof Card) {
for (final Card c : targetChosen.getTargetCards()) { for (final Card c : targetChosen.getTargetCards()) {
if (entity != c && !c.sharesCardTypeWith((Card) entity)) { if (entity != c && !c.sharesCardTypeWith((Card) entity)) {
return false; return false;
} }
} }
} }
}
if (entity instanceof GameEntity) { if (entity instanceof GameEntity) {
String[] validTgt = tr.getValidTgts();
GameEntity e = (GameEntity)entity; GameEntity e = (GameEntity)entity;
if (!e.isValid(validTgt, getActivatingPlayer(), getHostCard(), this)) { if (!e.isValid(tr.getValidTgts(), getActivatingPlayer(), getHostCard(), this)) {
return false; return false;
} }
if (hasParam("TargetType") && !e.isValid(getParam("TargetType").split(","), getActivatingPlayer(), getHostCard(), this)) { if (hasParam("TargetType") && !e.isValid(getParam("TargetType").split(","), getActivatingPlayer(), getHostCard(), this)) {

View File

@@ -27,6 +27,7 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.common.collect.TreeBasedTable;
import forge.game.GameObject; import forge.game.GameObject;
import forge.game.IIdentifiable; import forge.game.IIdentifiable;
@@ -85,7 +86,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
private Integer xManaPaid = null; private Integer xManaPaid = null;
// Other Paid things // Other Paid things
private final Map<String, CardCollection> paidHash; private final TreeBasedTable<String, Boolean, CardCollection> paidHash;
// Additional info // Additional info
// is Kicked, is Buyback // is Kicked, is Buyback
@@ -108,7 +109,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView {
activatingPlayer = sa.getActivatingPlayer(); activatingPlayer = sa.getActivatingPlayer();
// Payment info // Payment info
paidHash = Maps.newHashMap(ability.getPaidHash()); paidHash = TreeBasedTable.create(ability.getPaidHash());
ability.resetPaidHash(); ability.resetPaidHash();
splicedCards = sa.getSplicedCards(); splicedCards = sa.getSplicedCards();

View File

@@ -961,7 +961,7 @@ public final class StaticAbilityContinuous {
AbilityUtils.getDefinedPlayers(affectedCard, params.get("MayPlayPlayer"), stAb).get(0) : AbilityUtils.getDefinedPlayers(affectedCard, params.get("MayPlayPlayer"), stAb).get(0) :
controller; controller;
affectedCard.setMayPlay(mayPlayController, mayPlayWithoutManaCost, affectedCard.setMayPlay(mayPlayController, mayPlayWithoutManaCost,
mayPlayAltCost != null ? new Cost(mayPlayAltCost, false) : null, additional, mayPlayWithFlash, mayPlayAltCost != null ? new Cost(mayPlayAltCost, false, affectedCard.equals(hostCard)) : null, additional, mayPlayWithFlash,
mayPlayGrantZonePermissions, stAb); mayPlayGrantZonePermissions, stAb);
// If the MayPlay effect only affected itself, check if it is in graveyard and give other player who cast Shaman's Trance MayPlay // If the MayPlay effect only affected itself, check if it is in graveyard and give other player who cast Shaman's Trance MayPlay

View File

@@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@@ -404,8 +405,7 @@ public abstract class Trigger extends TriggerReplacementBase {
} }
} else if ("Sacrificed".equals(condition)) { } else if ("Sacrificed".equals(condition)) {
final SpellAbility trigSA = (SpellAbility) runParams.get(AbilityKey.CastSA); final SpellAbility trigSA = (SpellAbility) runParams.get(AbilityKey.CastSA);
if (trigSA != null && if (trigSA != null && Iterables.isEmpty(trigSA.getPaidList("Sacrificed"))) {
(trigSA.getPaidList("Sacrificed") == null || trigSA.getPaidList("Sacrificed").isEmpty())) {
return false; return false;
} }
} else if ("AttackedPlayerWithMostLife".equals(condition)) { } else if ("AttackedPlayerWithMostLife".equals(condition)) {

View File

@@ -7,6 +7,7 @@ import java.util.Set;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.TreeBasedTable;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.game.Game; import forge.game.Game;
@@ -108,23 +109,23 @@ public class WrappedAbility extends Ability {
} }
@Override @Override
public void setPaidHash(final Map<String, CardCollection> hash) { public void setPaidHash(final TreeBasedTable<String, Boolean, CardCollection> hash) {
sa.setPaidHash(hash); sa.setPaidHash(hash);
} }
@Override @Override
public Map<String, CardCollection> getPaidHash() { public TreeBasedTable<String, Boolean, CardCollection> getPaidHash() {
return sa.getPaidHash(); return sa.getPaidHash();
} }
@Override @Override
public CardCollection getPaidList(final String str) { public CardCollection getPaidList(final String str, boolean intrinsic) {
return sa.getPaidList(str); return sa.getPaidList(str, intrinsic);
} }
@Override @Override
public void addCostToHashList(final Card c, final String str) { public void addCostToHashList(final Card c, final String str, final boolean intrinsic) {
sa.addCostToHashList(c, str); sa.addCostToHashList(c, str, intrinsic);
} }
@Override @Override

View File

@@ -362,7 +362,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
if (sp.hasParam("Crew")) { if (sp.hasParam("Crew")) {
// Trigger crews! // Trigger crews!
runParams.put(AbilityKey.Vehicle, sp.getHostCard()); runParams.put(AbilityKey.Vehicle, sp.getHostCard());
runParams.put(AbilityKey.Crew, sp.getPaidList("TappedCards")); runParams.put(AbilityKey.Crew, sp.getPaidList("TappedCards", true));
game.getTriggerHandler().runTrigger(TriggerType.Crewed, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.Crewed, runParams, false);
} }
} else { } else {

View File

@@ -1347,10 +1347,10 @@ public final class CMatchUI
enchantedEntityView = enchantedEntity.getView(); enchantedEntityView = enchantedEntity.getView();
numSmallImages++; numSmallImages++;
} else if ((sa.getRootAbility() != null) } else if ((sa.getRootAbility() != null)
&& (sa.getRootAbility().getPaidList("Sacrificed") != null) && (sa.getRootAbility().getPaidList("Sacrificed", true) != null)
&& !sa.getRootAbility().getPaidList("Sacrificed").isEmpty()) { && !sa.getRootAbility().getPaidList("Sacrificed", true).isEmpty()) {
// If the player activated its ability by sacrificing the enchantment, the enchantment has not anything attached anymore and the ex-enchanted card has to be searched in other ways.. for example, the green enchantment "Carapace" // If the player activated its ability by sacrificing the enchantment, the enchantment has not anything attached anymore and the ex-enchanted card has to be searched in other ways.. for example, the green enchantment "Carapace"
enchantedEntity = sa.getRootAbility().getPaidList("Sacrificed").get(0).getEnchantingCard(); enchantedEntity = sa.getRootAbility().getPaidList("Sacrificed", true).get(0).getEnchantingCard();
if (enchantedEntity != null) { if (enchantedEntity != null) {
enchantedEntityView = enchantedEntity.getView(); enchantedEntityView = enchantedEntity.getView();
numSmallImages++; numSmallImages++;

View File

@@ -1,8 +1,9 @@
Name:Barrin's Spite Name:Barrin's Spite
ManaCost:2 U B ManaCost:2 U B
Types:Sorcery Types:Sorcery
A:SP$ Pump | Cost$ 2 U B | ValidTgts$ Creature | TgtPrompt$ Choose two target creatures controlled by the same player | TargetMin$ 2 | TargetMax$ 2 | TargetUnique$ True | TargetsWithSameController$ True | IsCurse$ True | SubAbility$ DBChooseSac | StackDescription$ SpellDescription | SpellDescription$ Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand. A:SP$ Pump | Cost$ 2 U B | ValidTgts$ Creature | TgtPrompt$ Choose two target creatures controlled by the same player | TargetMin$ 2 | TargetMax$ 2 | TargetUnique$ True | TargetsWithSameController$ True | RememberTargets$ True | IsCurse$ True | SubAbility$ DBChooseSac | StackDescription$ SpellDescription | SpellDescription$ Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand.
SVar:DBChooseSac:DB$ ChooseCard | DefinedCards$ Targeted | Defined$ TargetedController | ChoiceTitle$ Choose one to sacrifice | SubAbility$ DBSac | StackDescription$ None | AILogic$ WorstCard SVar:DBChooseSac:DB$ ChooseCard | Choices$ Card.IsRemembered | Defined$ TargetedController | ChoiceTitle$ Choose one to sacrifice | ForgetChosen$ True | SubAbility$ DBSac | StackDescription$ None | AILogic$ WorstCard
SVar:DBSac:DB$ Destroy | Defined$ ChosenCard | Sacrifice$ True | SubAbility$ DBBounce | StackDescription$ None SVar:DBSac:DB$ Destroy | Defined$ ChosenCard | Sacrifice$ True | SubAbility$ DBBounce | StackDescription$ None
SVar:DBBounce:DB$ ChangeZone | Defined$ Targeted | Origin$ Battlefield | Destination$ Hand | StackDescription$ None SVar:DBBounce:DB$ ChangeZone | Defined$ Remembered | Origin$ Battlefield | Destination$ Hand | SubAbility$ DBCleanup | StackDescription$ None
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
Oracle:Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand. Oracle:Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand.