Merge branch 'ranar-2' into 'master'

Ranar v2

See merge request core-developers/forge!4534
This commit is contained in:
Michael Kamensky
2021-04-17 04:22:26 +00:00
36 changed files with 89 additions and 40 deletions

View File

@@ -1260,7 +1260,7 @@ public class GameAction {
if (game.getCombat() != null) {
game.getCombat().removeAbsentCombatants();
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, null);
if (!checkAgain) {
break; // do not continue the loop
}

View File

@@ -657,7 +657,7 @@ public abstract class SpellAbilityEffect {
untilTable.put(cell.getColumnKey(), cell.getRowKey(), movedCard);
}
}
untilTable.triggerChangesZoneAll(game);
untilTable.triggerChangesZoneAll(game, null);
}
};

View File

@@ -74,7 +74,7 @@ public class AmassEffect extends TokenEffectBase {
makeTokens(prototype, activator, sa, 1, true, false, triggerList, combatChanged);
if (!useZoneTable) {
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
triggerList.clear();
}
game.fireEvent(new GameEventTokenCreated());

View File

@@ -43,7 +43,7 @@ public class AttachEffect extends SpellAbilityEffect {
if (newZone != previousZone) {
table.put(previousZone, newZone, c);
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
final Card source = sa.getHostCard();

View File

@@ -68,6 +68,6 @@ public class BalanceEffect extends SpellAbilityEffect {
discard(sa, table, discardedMap);
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -268,7 +268,7 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
}
}
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
if (sa.hasParam("UntilHostLeavesPlay")) {
source.addLeavesPlayCommand(untilHostLeavesPlayCommand(triggerList, source));

View File

@@ -811,7 +811,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
game.getAction().reveal(commandCards, player, true, "Revealed cards in ");
}
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
counterTable.triggerCountersPutAll(game);
@@ -1411,7 +1411,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged());
}
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
if (sa.hasParam("UntilHostLeavesPlay")) {
source.addLeavesPlayCommand(untilHostLeavesPlayCommand(triggerList, source));

View File

@@ -17,7 +17,7 @@ public class ChangeZoneResolveEffect extends SpellAbilityEffect {
CardZoneTable table = sa.getChangeZoneTable();
if (table != null) {
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
table.clear();
}
}

View File

@@ -187,7 +187,7 @@ public class CopyPermanentEffect extends TokenEffectBase {
} // end foreach Card
if (!useZoneTable) {
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
triggerList.clear();
}
if (combatChanged.isTrue()) {

View File

@@ -97,7 +97,7 @@ public class DestroyAllEffect extends SpellAbilityEffect {
card.addRemembered(CardUtil.getLKICopy(c, cachedMap));
}
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -104,7 +104,7 @@ public class DestroyEffect extends SpellAbilityEffect {
}
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
protected void internalDestroy(Card gameCard, SpellAbility sa, CardZoneTable table, Map<Integer, Card> cachedMap) {

View File

@@ -406,7 +406,7 @@ public class DigEffect extends SpellAbilityEffect {
game.fireEvent(new GameEventCombatChanged());
}
//table trigger there
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
counterTable.triggerCountersPutAll(game);
}

View File

@@ -183,7 +183,7 @@ public class DigMultipleEffect extends SpellAbilityEffect {
}
}
//table trigger there
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -260,7 +260,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged());
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
} // end resolve
}

View File

@@ -292,6 +292,6 @@ public class DiscardEffect extends SpellAbilityEffect {
discard(sa, table, discardedMap);
// run trigger if something got milled
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
} // discardResolve()
}

View File

@@ -91,7 +91,7 @@ public class ExploreEffect extends SpellAbilityEffect {
game.getTriggerHandler().runTrigger(TriggerType.Explores, AbilityKey.mapFromCard(c), false);
}
table.triggerCountersPutAll(game);
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -45,7 +45,7 @@ public class InvestigateEffect extends TokenEffectBase {
MutableBoolean combatChanged = new MutableBoolean(false);
makeTokens(prototype, p, sa, 1, true, false, triggerList, combatChanged);
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
p.addInvestigatedThisTurn();
game.fireEvent(new GameEventTokenCreated());

View File

@@ -21,7 +21,7 @@ public class LearnEffect extends SpellAbilityEffect {
for (Player p : getTargetPlayers(sa)) {
p.learnLesson(sa, table);
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -81,7 +81,7 @@ public class MillEffect extends SpellAbilityEffect {
}
// run trigger if something got milled
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
@Override

View File

@@ -40,6 +40,6 @@ public class PermanentEffect extends SpellAbilityEffect {
if (newZone != previousZone) {
table.put(previousZone, newZone, c);
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -173,7 +173,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
sa.setDamageMap(null);
}
if (sa.hasParam("ChangeZoneTable")) {
sa.getChangeZoneTable().triggerChangesZoneAll(game);
sa.getChangeZoneTable().triggerChangesZoneAll(game, sa);
sa.setChangeZoneTable(null);
}
}

View File

@@ -97,7 +97,7 @@ public class SacrificeAllEffect extends SpellAbilityEffect {
}
}
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -186,7 +186,7 @@ public class SacrificeEffect extends SpellAbilityEffect {
}
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, sa);
}
@Override

View File

@@ -92,7 +92,7 @@ public class TokenEffect extends TokenEffectBase {
}
if (!useZoneTable) {
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, sa);
triggerList.clear();
}

View File

@@ -5,14 +5,12 @@ package forge.game.card;
import java.util.Map;
import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Table;
import com.google.common.collect.*;
import forge.game.CardTraitBase;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
@@ -53,11 +51,12 @@ public class CardZoneTable extends ForwardingTable<ZoneType, ZoneType, CardColle
return dataMap;
}
public void triggerChangesZoneAll(final Game game) {
public void triggerChangesZoneAll(final Game game, final SpellAbility cause) {
if (!isEmpty()) {
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Cards, new CardZoneTable(this));
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false);
runParams.put(AbilityKey.Cause, cause);
game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, AbilityKey.newMap(runParams), false);
}
}

View File

@@ -237,7 +237,7 @@ public class CostAdjustment {
table.put(ZoneType.Graveyard, d.getZone().getZoneType(), d);
}
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, null);
}
if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) {
adjustCostByConvokeOrImprovise(cost, sa, false, test);
@@ -510,6 +510,13 @@ public class CostAdjustment {
if (!sa.isPwAbility()) {
return false;
}
} else if (type.equals("Foretell")) {
if (!sa.isForetelling()) {
return false;
}
if (st.hasParam("FirstForetell") && activator.getNumForetoldThisTurn() > 0) {
return false;
}
}
}
if (st.hasParam("AffectedZone")) {

View File

@@ -101,7 +101,7 @@ public class CostMill extends CostPart {
public final boolean payAsDecided(final Player ai, final PaymentDecision decision, SpellAbility ability) {
CardZoneTable table = new CardZoneTable();
ability.getPaidHash().put("Milled", (CardCollection) ai.mill(decision.c, ZoneType.Graveyard, false, ability, table));
table.triggerChangesZoneAll(ai.getGame());
table.triggerChangesZoneAll(ai.getGame(), null);
return true;
}

View File

@@ -64,8 +64,6 @@ public abstract class CostPartWithList extends CostPart {
*
* @param sa
* the sa
* @param hash
* the hash
*/
public final void reportPaidCardsTo(final SpellAbility sa) {
if (sa == null) {
@@ -177,7 +175,7 @@ public abstract class CostPartWithList extends CostPart {
// copy table because the original get cleaned after the cost is done
final CardZoneTable copyTable = new CardZoneTable();
copyTable.putAll(table);
copyTable.triggerChangesZoneAll(payer.getGame());
copyTable.triggerChangesZoneAll(payer.getGame(), null);
}
}

View File

@@ -398,7 +398,7 @@ public class PhaseHandler implements java.io.Serializable {
discarded.add(c);
}
}
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, null);
if (!discarded.isEmpty()) {
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
@@ -1068,7 +1068,7 @@ public class PhaseHandler implements java.io.Serializable {
// currently there can be only one Spell put on the Stack at once, or Land Abilities be played
final CardZoneTable triggerList = new CardZoneTable();
triggerList.put(originZone.getZoneType(), currentZone.getZoneType(), saHost);
triggerList.triggerChangesZoneAll(game);
triggerList.triggerChangesZoneAll(game, null);
}
}
@@ -1262,7 +1262,7 @@ public class PhaseHandler implements java.io.Serializable {
/**
* returns the continuous extra turn count
* @param PLayer p
* @param p
* @return int
*/
public int getExtraTurnForPlayer(final Player p) {

View File

@@ -2599,6 +2599,7 @@ public class Player extends GameEntity implements Comparable<Player> {
resetPreventNextDamageWithEffect();
resetNumDrawnThisTurn();
resetNumDiscardedThisTurn();
resetNumForetoldThisTurn();
resetNumTokenCreatedThisTurn();
setNumCardsInHandStartedThisTurnWith(getCardsIn(ZoneType.Hand).size());
clearCreaturesAttackedThisTurn();

View File

@@ -2003,6 +2003,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
return false;
}
}
else if (incR[0].equals("SpellAbility")) {
// Match anything
}
else { //not a spell/ability type
return false;
}

View File

@@ -21,6 +21,10 @@ public class TriggerChangesZoneAll extends Trigger {
public boolean performTest(Map<AbilityKey, Object> runParams) {
final CardZoneTable table = (CardZoneTable) runParams.get(AbilityKey.Cards);
if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
return false;
}
return !filterCards(table).isEmpty();
}
@@ -32,6 +36,7 @@ public class TriggerChangesZoneAll extends Trigger {
sa.setTriggeringObject(AbilityKey.Cards, allCards);
sa.setTriggeringObject(AbilityKey.Amount, allCards.size());
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Cause);
}
@Override

View File

@@ -0,0 +1,12 @@
Name:Hero of Bretagard
ManaCost:2 W
Types:Creature Human Warrior
PT:1/1
T:Mode$ ChangesZoneAll | ValidCause$ SpellAbility.YouCtrl | Origin$ Hand,Battlefield | Destination$ Exile | ValidCards$ Card.YouOwn,Card.inZoneBattlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever a spell or ability you control exiles one or more cards from your hand and/or permanents from the battlefield, put that many +1/+1 counters on CARDNAME.
SVar:TrigPutCounter:DB$PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ X
S:Mode$ Continuous | Affected$ Card.Self | CheckSVar$ Y | SVarCompare$ GT4 | AddKeyword$ Flying | AddType$ Angel | Description$ As long as CARDNAME has five or more counters on it, it has flying and is an Angel in addition to its other types.
S:Mode$ Continuous | Affected$ Card.Self | CheckSVar$ Y | SVarCompare$ GT9 | AddKeyword$ Indestructible | AddType$ Angel & God | Description$ As long as CARDNAME has ten or more counters on it, it indestructible and is a God in addition to its other types.
SVar:X:TriggerCount$Amount
SVar:Y:Count$CardCounters.ALL
DeckHas:Ability$Counters
Oracle:Whenever a spell or ability you control exiles one or more cards from your hand and/or permanents from the battlefield, put that many +1/+1 counters on Hero of Bretagard.\nAs long as Hero of Bretagard has five or more counters on it, it has flying and is an Angel in addition to its other types.\nAs long as Hero of Bretagard has ten or more counters on it, it has indestructible and is a God in addition to its other types.

View File

@@ -0,0 +1,13 @@
Name:Laelia, the Blade Reforged
ManaCost:2 R
Types:Legendary Creature Spirit Warrior
PT:2/2
K:Haste
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ Whenever CARDNAME attacks, exile the top card of your library. You may play that card this turn.
SVar:TrigExile:DB$ Dig | Defined$ You | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | StaticAbilities$ STPlay | RememberObjects$ Remembered | ForgetOnMoved$ Exile | SubAbility$ DBCleanup
SVar:STPlay:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ You may play this card this turn.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
T:Mode$ ChangesZoneAll | ValidCause$ SpellAbility.YouCtrl | Origin$ Library,Graveyard | Destination$ Exile | ValidCards$ Card.YouOwn | Execute$ TrigPutCounter | TriggerDescription$ Whenever you exile one or more cards from your library and/or your graveyard, put a +1/+1 counter on NICKNAME.
SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1
Oracle:Haste\nWhenever Laelia, the Blade Reforged attacks, exile the top card of your library. You may play that card this turn.\nWhenever you exile one or more cards from your library and/or your graveyard, put a +1/+1 counter on Laelia.

View File

@@ -0,0 +1,11 @@
Name:Ranar the Ever-Watchful
ManaCost:2 W U
Types:Legendary Creature Spirit Warrior
PT:2/3
K:Flying
K:Vigilance
S:Mode$ ReduceCost | ValidCard$ Card | Type$ Foretell | Amount$ 2 | FirstForetell$ True | Activator$ You | Description$ The first card you foretell each turn costs {0} to foretell.
T:Mode$ ChangesZoneAll | ValidCause$ SpellAbility.YouCtrl | Origin$ Hand,Battlefield | Destination$ Exile | ValidCards$ Card.YouOwn,Card.inZoneBattlefield | Execute$ TrigToken | TriggerDescription$ Whenever a spell or ability you control exiles one or more cards from your hand and/or permanents from the battlefield, create a 1/1 white Spirit creature token with flying.
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_spirit_flying | TokenOwner$ You
DeckHas:Ability$Token
Oracle:Flying, vigilance\nThe first card you foretell each turn costs {0} to foretell.\nWhenever a spell or ability you control exiles one or more cards from your hand and/or permanents from the battlefield, create a 1/1 white Spirit creature token with flying.

View File

@@ -698,7 +698,7 @@ public class HumanPlay {
ability.clearTappedForConvoke();
}
if (!table.isEmpty() && !manaInputCancelled) {
table.triggerChangesZoneAll(game);
table.triggerChangesZoneAll(game, null);
}
return !manaInputCancelled;
}