SacrificeOnce Trigger

This commit is contained in:
Hans Mackowiak
2024-07-28 21:34:25 +02:00
parent a58a197945
commit 23d0b05547
11 changed files with 134 additions and 54 deletions

View File

@@ -1442,10 +1442,7 @@ public class GameAction {
sacrificeList = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, sacrificeList, ZoneType.Graveyard, null); sacrificeList = (CardCollection) GameActionUtil.orderCardsByTheirOwners(game, sacrificeList, ZoneType.Graveyard, null);
orderedSacrificeList = true; orderedSacrificeList = true;
} }
for (Card c : sacrificeList) { sacrifice(sacrificeList, null, true, mapParams);
c.updateWasDestroyed(true);
sacrifice(c, null, true, mapParams);
}
setHoldCheckingStaticAbilities(false); setHoldCheckingStaticAbilities(false);
table.triggerChangesZoneAll(game, null); table.triggerChangesZoneAll(game, null);
@@ -1891,15 +1888,37 @@ public class GameAction {
return true; return true;
} }
public final Card sacrifice(final Card c, final SpellAbility source, final boolean effect, Map<AbilityKey, Object> params) { public final CardCollection sacrifice(final Iterable<Card> list, final SpellAbility source, final boolean effect, Map<AbilityKey, Object> params) {
if (!c.canBeSacrificedBy(source, effect)) { Multimap<Player, Card> lki = MultimapBuilder.hashKeys().arrayListValues().build();
return null; CardCollection result = new CardCollection();
for (Card c : list) {
if (c == null) {
continue;
}
if (!c.canBeSacrificedBy(source, effect)) {
continue;
}
Card lkiCopy = ((CardCollection) params.get(AbilityKey.LastStateBattlefield)).get(c);
c.getController().addSacrificedThisTurn(lkiCopy, source);
lki.put(c.getController(), lkiCopy);
c.updateWasDestroyed(true);
Card changed = sacrificeDestroy(c, source, params);
if (changed != null) {
result.add(changed);
}
} }
for (Map.Entry<Player, Collection<Card>> e : lki.asMap().entrySet()) {
c.getController().addSacrificedThisTurn(c, source); // Run triggers
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(e.getKey());
c.updateWasDestroyed(true); runParams.put(AbilityKey.Cards, new CardCollection(e.getValue()));
return sacrificeDestroy(c, source, params); runParams.put(AbilityKey.Cause, source);
game.getTriggerHandler().runTrigger(TriggerType.SacrificedOnce, runParams, false);
}
return result;
} }
public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, Map<AbilityKey, Object> params) { public final boolean destroy(final Card c, final SpellAbility sa, final boolean regenerate, Map<AbilityKey, Object> params) {

View File

@@ -60,10 +60,8 @@ public class BalanceEffect extends SpellAbilityEffect {
if (zone.equals(ZoneType.Hand)) { if (zone.equals(ZoneType.Hand)) {
discardedMap.put(p, p.getController().chooseCardsToDiscardFrom(p, sa, validCards.get(i), numToBalance, numToBalance)); discardedMap.put(p, p.getController().chooseCardsToDiscardFrom(p, sa, validCards.get(i), numToBalance, numToBalance));
} else { // Battlefield } else { // Battlefield
for (Card card : p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid)) { CardCollectionView list = p.getController().choosePermanentsToSacrifice(sa, numToBalance, numToBalance, validCards.get(i), valid);
if (null == card) continue; game.getAction().sacrifice(list, sa, true, params);
game.getAction().sacrifice(card, sa, true, params);
}
} }
} }

View File

@@ -78,15 +78,13 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
Map<AbilityKey, Object> params = AbilityKey.newMap(); Map<AbilityKey, Object> params = AbilityKey.newMap();
CardZoneTable zoneMovements = AbilityKey.addCardZoneTableParams(params, sa); CardZoneTable zoneMovements = AbilityKey.addCardZoneTableParams(params, sa);
for (Card sac : list) { for (Card sac : game.getAction().sacrifice(list, sa, true, params)) {
final Card lKICopy = zoneMovements.getLastStateBattlefield().get(sac); final Card lKICopy = zoneMovements.getLastStateBattlefield().get(sac);
if (game.getAction().sacrifice(sac, sa, true, params) != null) { if (remSacrificed) {
if (remSacrificed) { host.addRemembered(lKICopy);
host.addRemembered(lKICopy); }
} if (sa.hasParam("ImprintSacrificed")) {
if (sa.hasParam("ImprintSacrificed")) { host.addImprintedCard(lKICopy);
host.addImprintedCard(lKICopy);
}
} }
} }

View File

@@ -99,7 +99,7 @@ public class SacrificeEffect extends SpellAbilityEffect {
if (host.getController().equals(activator) && game.getZoneOf(host).is(ZoneType.Battlefield)) { if (host.getController().equals(activator) && game.getZoneOf(host).is(ZoneType.Battlefield)) {
if (!optional || activator.getController().confirmAction(sa, null, if (!optional || activator.getController().confirmAction(sa, null,
Localizer.getInstance().getMessage("lblDoYouWantSacrificeThis", host.getName()), null)) { Localizer.getInstance().getMessage("lblDoYouWantSacrificeThis", host.getName()), null)) {
if (game.getAction().sacrifice(host, sa, true, params) != null && remSacrificed) { if (game.getAction().sacrifice(new CardCollection(host), sa, true, params) != null && remSacrificed) {
host.addRemembered(host); host.addRemembered(host);
} }
} }
@@ -155,25 +155,32 @@ public class SacrificeEffect extends SpellAbilityEffect {
choosenToSacrifice = GameActionUtil.orderCardsByTheirOwners(game, choosenToSacrifice, ZoneType.Graveyard, sa); choosenToSacrifice = GameActionUtil.orderCardsByTheirOwners(game, choosenToSacrifice, ZoneType.Graveyard, sa);
for (Card sac : choosenToSacrifice) { if (destroy) {
Card lKICopy = zoneMovements.getLastStateBattlefield().get(sac); for (Card sac : choosenToSacrifice) {
boolean wasSacrificed = !destroy && game.getAction().sacrifice(sac, sa, true, params) != null; Card lKICopy = zoneMovements.getLastStateBattlefield().get(sac);
boolean wasDestroyed = destroy && game.getAction().destroy(sac, sa, true, params); if (game.getAction().destroy(sac, sa, true, params) && remSacrificed) {
// Run Devour Trigger host.addRemembered(lKICopy);
if (devour) { }
host.addDevoured(lKICopy);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Devoured, lKICopy);
game.getTriggerHandler().runTrigger(TriggerType.Devoured, runParams, false);
} }
if (exploit) { } else {
host.addExploited(lKICopy); for (Card sac : game.getAction().sacrifice(choosenToSacrifice, sa, true, params)) {
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(host); Card lKICopy = zoneMovements.getLastStateBattlefield().get(sac);
runParams.put(AbilityKey.Exploited, lKICopy); // Run Devour Trigger
game.getTriggerHandler().runTrigger(TriggerType.Exploited, runParams, false); if (devour) {
} host.addDevoured(lKICopy);
if ((wasDestroyed || wasSacrificed) && remSacrificed) { final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
host.addRemembered(lKICopy); runParams.put(AbilityKey.Devoured, lKICopy);
game.getTriggerHandler().runTrigger(TriggerType.Devoured, runParams, false);
}
if (exploit) {
host.addExploited(lKICopy);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(host);
runParams.put(AbilityKey.Exploited, lKICopy);
game.getTriggerHandler().runTrigger(TriggerType.Exploited, runParams, false);
}
if (remSacrificed) {
host.addRemembered(lKICopy);
}
} }
} }
} }

View File

@@ -38,7 +38,7 @@ public class CostForage extends CostPartWithList {
public String toString() { public String toString() {
return "Forage"; return "Forage";
} }
@Override @Override
protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { return null; } protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { return null; }
@Override @Override
@@ -60,7 +60,7 @@ public class CostForage extends CostPartWithList {
} else if (targetCards.size() == 1) { } else if (targetCards.size() == 1) {
Map<AbilityKey, Object> moveParams = AbilityKey.newMap(); Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
AbilityKey.addCardZoneTableParams(moveParams, table); AbilityKey.addCardZoneTableParams(moveParams, table);
CardCollection result = new CardCollection(game.getAction().sacrifice(targetCards.getFirst(), ability, effect, moveParams)); CardCollection result = game.getAction().sacrifice(targetCards, ability, effect, moveParams);
triggerForage(payer); triggerForage(payer);
return result; return result;
} else { } else {

View File

@@ -32,6 +32,7 @@ import forge.card.mana.ManaCostShard;
import forge.game.Game; import forge.game.Game;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardZoneTable; import forge.game.card.CardZoneTable;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -341,7 +342,7 @@ public class CostPayment extends ManaConversionMatrix {
if (test) { if (test) {
sa.resetSacrificedAsOffering(); sa.resetSacrificedAsOffering();
} else if (costIsPaid) { } else if (costIsPaid) {
game.getAction().sacrifice(offering, sa, false, params); game.getAction().sacrifice(new CardCollection(offering), sa, false, params);
} }
} }
if (sa.isEmerge()) { if (sa.isEmerge()) {
@@ -353,7 +354,7 @@ public class CostPayment extends ManaConversionMatrix {
if (test) { if (test) {
sa.resetSacrificedAsEmerge(); sa.resetSacrificedAsEmerge();
} else if (costIsPaid) { } else if (costIsPaid) {
game.getAction().sacrifice(emerge, sa, false, params); game.getAction().sacrifice(new CardCollection(emerge), sa, false, params);
sa.setSacrificedAsEmerge(game.getChangeZoneLKIInfo(emerge)); sa.setSacrificedAsEmerge(game.getChangeZoneLKIInfo(emerge));
} }
} }

View File

@@ -162,13 +162,17 @@ public class CostSacrifice extends CostPartWithList {
} }
@Override @Override
protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { protected Card doPayment(Player payer, SpellAbility ability, Card targetCard, final boolean effect) { return null; }
final Game game = targetCard.getGame(); @Override
protected boolean canPayListAtOnce() { return true; }
@Override
protected CardCollectionView doListPayment(Player payer, SpellAbility ability, CardCollectionView targetCards, final boolean effect) {
final Game game = ability.getHostCard().getGame();
// no table there, it is already handled by CostPartWithList // no table there, it is already handled by CostPartWithList
Map<AbilityKey, Object> moveParams = AbilityKey.newMap(); Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
AbilityKey.addCardZoneTableParams(moveParams, table); AbilityKey.addCardZoneTableParams(moveParams, table);
return game.getAction().sacrifice(targetCard, ability, effect, moveParams); return game.getAction().sacrifice(targetCards, ability, effect, moveParams);
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -2295,11 +2295,10 @@ public class Player extends GameEntity implements Comparable<Player> {
investigatedThisTurn = 0; investigatedThisTurn = 0;
} }
public final void addSacrificedThisTurn(final Card c, final SpellAbility source) { public final void addSacrificedThisTurn(final Card cpy, final SpellAbility source) {
// Play the Sacrifice sound // Play the Sacrifice sound
game.fireEvent(new GameEventCardSacrificed()); game.fireEvent(new GameEventCardSacrificed());
final Card cpy = CardCopyService.getLKICopy(c);
sacrificedThisTurn.add(cpy); sacrificedThisTurn.add(cpy);
// Run triggers // Run triggers

View File

@@ -0,0 +1,53 @@
package forge.game.trigger;
import java.util.Map;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.spellability.SpellAbility;
import forge.util.Localizer;
public class TriggerSacrificedOnce extends Trigger {
public TriggerSacrificedOnce(Map<String, String> params, Card host, boolean intrinsic) {
super(params, host, intrinsic);
}
@Override
public boolean performTest(Map<AbilityKey, Object> runParams) {
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
return false;
}
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
return false;
}
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Cards))) {
return false;
}
return true;
}
@Override
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
CardCollection cards = (CardCollection) runParams.get(AbilityKey.Cards);
if (hasParam("ValidCard")) {
cards = CardLists.getValidCards(cards, getParam("ValidCard"), getHostCard().getController(), getHostCard(), this);
}
sa.setTriggeringObject(AbilityKey.Cards, cards);
sa.setTriggeringObject(AbilityKey.Amount, cards.size());
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player, AbilityKey.Cause);
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblPlayer")).append(": ").append(sa.getTriggeringObject(AbilityKey.Player)).append(", ");
sb.append(Localizer.getInstance().getMessage("lblAmount")).append(": ").append(sa.getTriggeringObject(AbilityKey.Amount));
return sb.toString();
}
}

View File

@@ -118,6 +118,7 @@ public enum TriggerType {
RolledDieOnce(TriggerRolledDieOnce.class), RolledDieOnce(TriggerRolledDieOnce.class),
RoomEntered(TriggerEnteredRoom.class), RoomEntered(TriggerEnteredRoom.class),
Sacrificed(TriggerSacrificed.class), Sacrificed(TriggerSacrificed.class),
SacrificedOnce(TriggerSacrificed.class),
Scry(TriggerScry.class), Scry(TriggerScry.class),
SearchedLibrary(TriggerSearchedLibrary.class), SearchedLibrary(TriggerSearchedLibrary.class),
SeekAll(TriggerSeekAll.class), SeekAll(TriggerSeekAll.class),

View File

@@ -534,7 +534,7 @@ public class HumanPlay {
final Card offering = ability.getSacrificedAsOffering(); final Card offering = ability.getSacrificedAsOffering();
offering.setUsedToPay(false); offering.setUsedToPay(false);
if (!manaInputCancelled) { if (!manaInputCancelled) {
game.getAction().sacrifice(offering, ability, false, params); game.getAction().sacrifice(new CardCollection(offering), ability, false, params);
} }
ability.resetSacrificedAsOffering(); ability.resetSacrificedAsOffering();
} }
@@ -542,7 +542,7 @@ public class HumanPlay {
final Card emerge = ability.getSacrificedAsEmerge(); final Card emerge = ability.getSacrificedAsEmerge();
emerge.setUsedToPay(false); emerge.setUsedToPay(false);
if (!manaInputCancelled) { if (!manaInputCancelled) {
game.getAction().sacrifice(emerge, ability, false, params); game.getAction().sacrifice(new CardCollection(emerge), ability, false, params);
ability.setSacrificedAsEmerge(game.getChangeZoneLKIInfo(emerge)); ability.setSacrificedAsEmerge(game.getChangeZoneLKIInfo(emerge));
} else { } else {
ability.resetSacrificedAsEmerge(); ability.resetSacrificedAsEmerge();