OTJ: more saddle support and cards (#4947)

This commit is contained in:
Northmoc
2024-04-07 04:27:25 -04:00
committed by GitHub
parent 2c7556de92
commit 98f8a031a5
26 changed files with 215 additions and 6 deletions

View File

@@ -282,6 +282,9 @@ public class GameCopier {
if (card.getHaunting() != null) {
otherCard.setHaunting(cardMap.get(card.getHaunting()));
}
if (card.getSaddledByThisTurn() != null) {
otherCard.setSaddledByThisTurn(card.getSaddledByThisTurn());
}
if (card.getEffectSource() != null) {
otherCard.setEffectSource(cardMap.get(card.getEffectSource()));
}

View File

@@ -55,6 +55,13 @@ public class AlterAttributeEffect extends SpellAbilityEffect {
case "Saddled":
// currently clean up in Card manually
altered = c.setSaddled(activate);
if (altered) {
CardCollection saddlers = (CardCollection) sa.getPaidList("TappedCards", true);
c.addSaddledByThisTurn(saddlers);
Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
runParams.put(AbilityKey.Crew, saddlers);
c.getGame().getTriggerHandler().runTrigger(TriggerType.BecomesSaddled, runParams, false);
}
break;
// Other attributes: renown, monstrous, suspected, etc

View File

@@ -219,6 +219,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
private boolean renowned;
private boolean solved = false;
private boolean saddled = false;
private int timesSaddledThisTurn = 0;
private CardCollection saddledByThisTurn;
private Long suspectedTimestamp = null;
private StaticAbility suspectedStatic = null;
@@ -6393,11 +6395,32 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
return true;
}
public final int getTimesSaddledThisTurn() {
return timesSaddledThisTurn;
}
public final CardCollection getSaddledByThisTurn() {
return saddledByThisTurn;
}
public final void addSaddledByThisTurn(final CardCollection saddlers) {
if (saddledByThisTurn != null) saddledByThisTurn.addAll(saddlers);
else saddledByThisTurn = saddlers;
}
public final void setSaddledByThisTurn(final CardCollection saddlers) {
saddledByThisTurn = saddlers;
}
public void resetSaddled() {
final boolean changed = isSaddled();
setSaddled(false);
if (saddledByThisTurn != null) saddledByThisTurn = null;
timesSaddledThisTurn = 0;
if (changed) updateAbilityTextForView();
}
public final boolean isSaddled() {
return saddled;
}
public final boolean setSaddled(final boolean saddled) {
this.saddled = saddled;
if (saddled) timesSaddledThisTurn++;
return true;
}
@@ -7028,7 +7051,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
if (!StaticAbilityNoCleanupDamage.damageNotRemoved(this)) {
setDamage(0);
}
setSaddled(false);
setHasBeenDealtDeathtouchDamage(false);
setHasBeenDealtExcessDamageThisTurn(false);
setRegeneratedThisTurn(0);
@@ -7045,6 +7067,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
resetMayPlayTurn();
resetExertedThisTurn();
resetCrewed();
resetSaddled();
resetChosenModeTurn();
resetAbilityResolvedThisTurn();
}

View File

@@ -298,6 +298,7 @@ public class CardCopyService {
newCopy.setRenowned(copyFrom.isRenowned());
newCopy.setSolved(copyFrom.isSolved());
newCopy.setSaddled(copyFrom.isSaddled());
if (newCopy.isSaddled()) newCopy.setSaddledByThisTurn(copyFrom.getSaddledByThisTurn());
newCopy.setSuspectedTimestamp(copyFrom.getSuspectedTimestamp());
newCopy.setColor(copyFrom.getColor().getColor());

View File

@@ -1894,9 +1894,7 @@ public class CardProperty {
return false;
}
} else if (property.equals("CrewedThisTurn")) {
if (!source.getCrewedByThisTurn().contains(card)) {
return false;
}
if (!hasTimestampMatch(card, source.getCrewedByThisTurn())) return false;
} else if (property.equals("HasDevoured")) {
if (card.getDevouredCards().isEmpty()) {
return false;
@@ -1937,6 +1935,8 @@ public class CardProperty {
if (!card.isSaddled()) {
return false;
}
} else if (property.equals("SaddledThisTurn")) {
if (!hasTimestampMatch(card, source.getSaddledByThisTurn())) return false;
} else if (property.equals("IsSuspected")) {
if (!card.isSuspected()) {
return false;
@@ -2191,4 +2191,14 @@ public class CardProperty {
return true;
}
private static boolean hasTimestampMatch (final Card card, final CardCollection coll) {
boolean match = false;
for (Card c : coll) {
if (c.equalsWithGameTimestamp(card)) {
match = true;
break;
}
}
return match;
}
}

View File

@@ -0,0 +1,45 @@
package forge.game.trigger;
import java.util.Map;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import forge.util.Localizer;
public class TriggerBecomesSaddled extends Trigger {
public TriggerBecomesSaddled(final Map<String, String> params, final Card host, final boolean intrinsic) {
super(params, host, intrinsic);
}
@Override
public boolean performTest(Map<AbilityKey, Object> runParams) {
if (!matchesValidParam("ValidSaddled", runParams.get(AbilityKey.Card))) {
return false;
}
if (hasParam("FirstTimeSaddled")) {
Card v = (Card) runParams.get(AbilityKey.Card);
if (v.getTimesSaddledThisTurn() != 1) {
return false;
}
}
return true;
}
// For now, since Saddled is so much like Crew, just use AbilityKey.Crew for cards that tap to saddle
@Override
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Card, AbilityKey.Crew);
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblSaddled")).append(": ").append(sa.getTriggeringObject(AbilityKey.Card));
sb.append(" ");
sb.append(Localizer.getInstance().getMessage("lblSaddledBy")).append(": ").append(sa.getTriggeringObject(AbilityKey.Crew));
return sb.toString();
}
}

View File

@@ -32,6 +32,7 @@ public enum TriggerType {
BecomeRenowned(TriggerBecomeRenowned.class),
BecomesCrewed(TriggerBecomesCrewed.class),
BecomesPlotted(TriggerBecomesPlotted.class),
BecomesSaddled(TriggerBecomesSaddled.class),
BecomesTarget(TriggerBecomesTarget.class),
BecomesTargetOnce(TriggerBecomesTargetOnce.class),
BlockersDeclared(TriggerBlockersDeclared.class),

View File

@@ -2,6 +2,6 @@ Name:Retrieve
ManaCost:2 G
Types:Sorcery
A:SP$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TgtPrompt$ Select up to one target creature card in your graveyard | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Creature.YouOwn | SubAbility$ DBReturn | StackDescription$ SpellDescription | SpellDescription$ Return up to one target creature card
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Land.YouOwn | TgtPrompt$ Select up to one target noncreature permanent card in your graveyard | SubAbility$ DBExile | StackDescription$ SpellDescription | SpellDescription$ and up to one target noncreature permanent card
SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Permanent.nonCreature+YouOwn | TgtPrompt$ Select up to one target noncreature permanent card in your graveyard | SubAbility$ DBExile | StackDescription$ SpellDescription | SpellDescription$ and up to one target noncreature permanent card
SVar:DBExile:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | StackDescription$ SpellDescription | SpellDescription$ to your hand. Exile CARDNAME.
Oracle:Return up to one target creature card and up to one target noncreature permanent card from your graveyard to your hand. Exile Retrieve.

View File

@@ -0,0 +1,11 @@
Name:Calamity, Galloping Inferno
ManaCost:4 R R
Types:Legendary Creature Horse Mount
PT:4/6
K:Haste
T:Mode$ Attacks | ValidCard$ Card.Self+IsSaddled | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever CARDNAME attacks while saddled, choose a nonlegendary creature that saddled it this turn and create a tapped and attacking token that's a copy of it. Sacrifice that token at the beginning of the next end step. Repeat this process once.
SVar:TrigCopy:DB$ CopyPermanent | Choices$ Creature.nonLegendary+SaddledThisTurn | TokenTapped$ True | TokenAttacking$ True | AtEOT$ Sacrifice | SubAbility$ DBRepeat
SVar:DBRepeat:DB$ CopyPermanent | Choices$ Creature.nonLegendary+SaddledThisTurn | TokenTapped$ True | TokenAttacking$ True | AtEOT$ Sacrifice
K:Saddle:1
DeckHas:Ability$Sacrifice
Oracle:Haste\nWhenever Calamity, Galloping Inferno attacks while saddled, choose a nonlegendary creature that saddled it this turn and create a tapped and attacking token that's a copy of it. Sacrifice that token at the beginning of the next end step. Repeat this process once.\nSaddle 1

View File

@@ -0,0 +1,14 @@
Name:Fortune, Loyal Steed
ManaCost:2 W
Types:Legendary Creature Beast Mount
PT:2/4
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigScry | TriggerDescription$ When CARDNAME enters the battlefield, scry 2.
SVar:TrigScry:DB$ Scry | ScryNum$ 2
T:Mode$ Attacks | ValidCard$ Card.Self+IsSaddled | TriggerZones$ Battlefield | Execute$ TrigDelayedTrig | TriggerDescription$ Whenever NICKNAME attacks while saddled, at end of combat, exile it and up to one creature that saddled it this turn, then return those cards to the battlefield under their owner's control.
SVar:TrigDelayedTrig:DB$ DelayedTrigger | Mode$ Phase | Phase$ EndCombat | ValidPlayer$ Player | Execute$ TrigChoose | TriggerDescription$ At end of combat, exile it and up to one creature that saddled it this turn, then return those cards to the battlefield under their owner's control.
SVar:TrigChoose:DB$ ChooseCard | MinAmount$ 0 | Choices$ Creature.SaddledThisTurn | SubAbility$ DBExile
SVar:DBExile:DB$ ChangeZone | Defined$ Self & ChosenCard | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBReturn
SVar:DBReturn:DB$ ChangeZone | Defined$ Remembered | Origin$ All | Destination$ Battlefield | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
K:Saddle:1
Oracle:When Fortune, Loyal Steed enters the battlefield, scry 2.\nWhenever Fortune attacks while saddled, at end of combat, exile it and up to one creature that saddled it this turn, then return those cards to the battlefield under their owner's control.\nSaddle 1

View File

@@ -0,0 +1,11 @@
Name:Gila Courser
ManaCost:2 R
Types:Creature Lizard Mount
PT:4/2
T:Mode$ Attacks | ValidCard$ Card.Self+IsSaddled | TriggerZones$ Battlefield | Execute$ TrigExile | TriggerDescription$ Whenever CARDNAME attacks while saddled, exile the top card of your library. Until the end of your next turn, you may play that card.
SVar:TrigExile:DB$ Dig | DigNum$ 1 | ChangeNum$ All | DestinationZone$ Exile | RememberChanged$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | RememberObjects$ RememberedCard | Duration$ UntilTheEndOfYourNextTurn | StaticAbilities$ Play | SubAbility$ DBCleanup | ForgetOnMoved$ Exile
SVar:Play:Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered | AffectedZone$ Exile | Description$ Until the end of your next turn, you may play that card.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
K:Saddle:1
Oracle:Whenever Gila Courser attacks while saddled, exile the top card of your library. Until the end of your next turn, you may play that card.\nSaddle 1 (Tap any number of other creatures you control with total power 1 or more: This Mount becomes saddled until end of turn. Saddle only as a sorcery.)

View File

@@ -0,0 +1,8 @@
Name:Quilled Charger
ManaCost:3 R
Types:Creature Porcupine Mount
PT:4/3
T:Mode$ Attacks | ValidCard$ Card.Self+IsSaddled | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks while saddled, it gains +1/+2 and gains menace until end of turn. (It can't be blocked except by two or more creatures.)
SVar:TrigPump:DB$ Pump | NumAtt$ 1 | NumDef$ 2 | KW$ Menace
K:Saddle:2
Oracle:Whenever Quilled Charger attacks while saddled, it gets +1/+2 and gains menace until end of turn. (It can't be blocked except by two or more creatures.)\nSaddle 2 (Tap any number of other creatures you control with total power 2 or more: This Mount becomes saddled until end of turn. Saddle only as a sorcery.)

View File

@@ -0,0 +1,9 @@
Name:Rambling Possum
ManaCost:2 G
Types:Creature Possum Mount
PT:3/3
T:Mode$ Attacks | ValidCard$ Card.Self+IsSaddled | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks while saddled, it gains +1/+2 until end of turn. Then you may return any number of creatures that saddled it this turn to their owner's hand.
SVar:TrigPump:DB$ Pump | NumAtt$ 1 | NumDef$ 2 | SubAbility$ DBChangeZone
SVar:DBChangeZone:DB$ ChangeZone | Origin$ Battlefield | Destination$ Hand | ChangeType$ Creature.SaddledThisTurn | ChangeNum$ Count$Valid Creature.SaddledThisTurn | Hidden$ True
K:Saddle:1
Oracle:Whenever Rambling Possum attacks while saddled, it gains +1/+2 until end of turn. Then you may return any number of creatures that saddled it this turn to their owner's hand.\nSaddle 1 (Tap any number of other creatures you control with total power 1 or more: This Mount becomes saddled until end of turn. Saddle only as a sorcery.)

View File

@@ -0,0 +1,11 @@
Name:Seraphic Steed
ManaCost:G W
Types:Creature Unicorn Mount
PT:2/2
K:First Strike
K:Lifelink
T:Mode$ Attacks | ValidCard$ Card.Self+IsSaddled | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever CARDNAME attacks while saddled, create a 3/3 white Angel creature token with flying.
SVar:TrigToken:DB$ Token | TokenScript$ w_3_3_angel_flying
K:Saddle:4
DeckHas:Ability$LifeGain|Token & Type$Angel
Oracle:First strike, lifelink\nWhenever Seraphic Steed attacks while saddled, create a 3/3 white Angel creature token with flying.\nSaddle 4 (Tap any number of other creatures you control with total power 4 or more: This Mount becomes saddled until end of turn. Saddle only as a sorcery.)

View File

@@ -0,0 +1,11 @@
Name:Stubborn Burrowfiend
ManaCost:1 G
Types:Creature Badger Beast Mount
PT:2/2
T:Mode$ BecomesSaddled | ValidSaddled$ Card.Self | FirstTimeSaddled$ True | TriggerZones$ Battlefield | Execute$ TrigMill | TriggerDescription$ Whenever CARDNAME becomes saddled for the first time each turn, mill two cards, then CARDNAME gets +X/+X until end of turn, where X is the number of creature cards in your graveyard.
SVar:TrigMill:DB$ Mill | NumCards$ 2 | SubAbility$ DBPump
SVar:DBPump:DB$ Pump | NumAtt$ Count$TypeInYourYard.Creature | NumDef$ Count$TypeInYourYard.Creature
K:Saddle:2
DeckHas:Ability$Mill
DeckHints:Ability$Sacrifice|Mill|Dredge|Discard
Oracle:Whenever Stubborn Burrowfiend becomes saddled for the first time each turn, mill two cards, then Stubborn Burrowfiend gets +X/+X until end of turn, where X is the number of creature cards in your graveyard.\nSaddle 2 (Tap any number of other creatures you control with total power 2 or more: This Mount becomes saddled until end of turn. Saddle only as a sorcery.)

View File

@@ -0,0 +1,11 @@
Name:The Gitrog, Ravenous Ride
ManaCost:3 B G
Types:Legendary Creature Frog Horror Mount
PT:6/5
K:Trample
K:Haste
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, you may sacrifice a creature that saddled it this turn. If you do, draw X cards, then put up to X land cards from your hand onto the battlefield tapped, where X is the sacrificed creature's power.
SVar:TrigDraw:AB$ Draw | Cost$ Sac<1/Creature.SaddledThisTurn/creature that saddled it this turn> | NumCards$ Sacrificed$CardPower | SubAbility$ DBChangeZone
SVar:DBChangeZone:DB$ ChangeZone | ConditionDefined$ Sacrificed | ConditionPresent$ Card | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | ChangeNum$ Sacrificed$CardPower | Tapped$ True
K:Saddle:1
Oracle:Trample, haste\nWhenever The Gitrog, Ravenous Ride deals combat damage to a player, you may sacrifice a creature that saddled it this turn. If you do, draw X cards, then put up to X land cards from your hand onto the battlefield tapped, where X is the sacrificed creature's power.\nSaddle 1

View File

@@ -0,0 +1,9 @@
Name:Trained Arynx
ManaCost:1 W
Types:Creature Cat Beast Mount
PT:3/1
T:Mode$ Attacks | ValidCard$ Card.Self+IsSaddled | TriggerZones$ Battlefield | Execute$ TrigPump | TriggerDescription$ Whenever CARDNAME attacks while saddled, it gains first strike until end of turn. Scry 1. (Look at the top card of your library. You may put that card on the bottom.)
SVar:TrigPump:DB$ Pump | KW$ First Strike | SubAbility$ DBScry
SVar:DBScry:DB$ Scry
K:Saddle:2
Oracle:Whenever Trained Arynx attacks while saddled, it gains first strike until end of turn. Scry 1. (Look at the top card of your library. You may put that card on the bottom.)\nSaddle 2 (Tap any number of other creatures you control with total power 2 or more: This Mount becomes saddled until end of turn. Saddle only as a sorcery.)

View File

@@ -10,4 +10,4 @@ SVar:DBEmblem:DB$ Effect | Name$ Emblem - Wrenn and Seven | Image$ emblem_wrenn_
SVar:UnlimitedHand:Mode$ Continuous | EffectZone$ Command | Affected$ You | SetMaxHandSize$ Unlimited | Description$ You have no maximum hand size.
SVar:X:Count$ValidHand Land.YouCtrl
DeckHas:Ability$Token|Graveyard
Oracle:[+1]: Reveal the top four cards of your library. Put all land cards revealed this way into your hand and the rest into your graveyard.\n[0]: Put any number of land cards from your hand onto the battlefield.\n[-3]: Create a green Treefolk creature token with reach and "This creature's power and toughness are each equal to the number of lands you control."\n[-8]: Return all permanent cards from your graveyard to your hand. You get an emblem with "You have no maximum hand size."
Oracle:[+1]: Reveal the top four cards of your library. Put all land cards revealed this way into your hand and the rest into your graveyard.\n[0]: Put any number of land cards from your hand onto the battlefield tapped.\n[-3]: Create a green Treefolk creature token with reach and "This creature's power and toughness are each equal to the number of lands you control."\n[-8]: Return all permanent cards from your graveyard to your hand. You get an emblem with "You have no maximum hand size."

View File

@@ -1551,6 +1551,9 @@ lblMonstrous=Monströs
lblRenowned=Ruhm
#TriggerBecomesPlotted.java
lblPlotted=Geplottet
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=Quelle
lblTarget=Ziel

View File

@@ -1556,6 +1556,9 @@ lblMonstrous=Monstrous
lblRenowned=Renowned
#TriggerBecomesPlotted.java
lblPlotted=Plotted
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=Source
lblTarget=Target

View File

@@ -1552,6 +1552,9 @@ lblMonstrous=Monstruoso
lblRenowned=Renombrado
#TriggerBecomesPlotted.java
lblPlotted=Trazado
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=Origen
lblTarget=Objetivo

View File

@@ -1555,6 +1555,9 @@ lblMonstrous=Monstrueux
lblRenowned=Renommé
#TriggerBecomesPlotted.java
lblPlotted=Tracé
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=Source
lblTarget=Cible

View File

@@ -1552,6 +1552,9 @@ lblMonstrous=Mostruoso
lblRenowned=Rinomato
#TriggerBecomesPlotted.java
lblPlotted=Tracciato
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=Fonte
lblTarget=Bersaglio

View File

@@ -1553,6 +1553,9 @@ lblMonstrous=怪物的
lblRenowned=高名である
#TriggerBecomesPlotted.java
lblPlotted=プロットされた
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=発生源
lblTarget=対象

View File

@@ -1588,6 +1588,9 @@ lblMonstrous=Monstruoso
lblRenowned=Renovado
#TriggerBecomesPlotted.java
lblPlotted=Traçado
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=Origem
lblTarget=Alvo

View File

@@ -1556,6 +1556,9 @@ lblMonstrous=已蛮化
lblRenowned=已铭勇
#TriggerBecomesPlotted.java
lblPlotted=绘制的
#TriggerBecomesSaddled.java
lblSaddled=Saddled
lblSaddledBy=Saddled by
#TriggerBecomesTarget.java
lblSource=来源
lblTarget=目标