From 52a63c5d98d4156e5f021e229d5cfea741c86cf2 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 13:18:03 -0400 Subject: [PATCH 1/9] Keyword.java - Disturb --- forge-game/src/main/java/forge/game/keyword/Keyword.java | 1 + 1 file changed, 1 insertion(+) diff --git a/forge-game/src/main/java/forge/game/keyword/Keyword.java b/forge-game/src/main/java/forge/game/keyword/Keyword.java index 156c2ccd78a..b7cd0086bdc 100644 --- a/forge-game/src/main/java/forge/game/keyword/Keyword.java +++ b/forge-game/src/main/java/forge/game/keyword/Keyword.java @@ -53,6 +53,7 @@ public enum Keyword { DETHRONE("Dethrone", SimpleKeyword.class, false, "Whenever this creature attacks the player with the most life or tied for the most life, put a +1/+1 counter on it."), DEVOUR("Devour", KeywordWithAmount.class, false, "As this creature enters the battlefield, you may sacrifice any number of creatures. This creature enters the battlefield with {%d:+1/+1 counter} on it for each creature sacrificed this way."), DEVOID("Devoid", SimpleKeyword.class, true, "This card has no color."), + DISTURB("Disturb", KeywordWithCost.class, false, "You may cast this card from your graveyard transformed for its disturb cost."), DOUBLE_STRIKE("Double Strike", SimpleKeyword.class, true, "This creature deals both first-strike and regular combat damage."), DREDGE("Dredge", KeywordWithAmount.class, false, "If you would draw a card, instead you may put exactly {%d:card} from the top of your library into your graveyard. If you do, return this card from your graveyard to your hand. Otherwise, draw a card."), ECHO("Echo", KeywordWithCost.class, false, "At the beginning of your upkeep, if this permanent came under your control since the beginning of your last upkeep, sacrifice it unless you pay %s."), From b817fbc6d1df8e5b8ecfec74e7191baac3e186df Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 13:18:14 -0400 Subject: [PATCH 2/9] beloved_beggar_generous_soul.txt --- .../upcoming/beloved_beggar_generous_soul.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 forge-gui/res/cardsfolder/upcoming/beloved_beggar_generous_soul.txt diff --git a/forge-gui/res/cardsfolder/upcoming/beloved_beggar_generous_soul.txt b/forge-gui/res/cardsfolder/upcoming/beloved_beggar_generous_soul.txt new file mode 100644 index 00000000000..8119a4e8cab --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/beloved_beggar_generous_soul.txt @@ -0,0 +1,20 @@ +Name:Beloved Beggar +ManaCost:1 W +Types:Creature Human Peasant +PT:0/4 +K:Disturb:4 W W +AlternateMode:DoubleFaced +Oracle:Disturb {4}{W}{W} (You may cast this card from your graveyard transformed for its disturb cost.) + +ALTERNATE + +Name:Generous Soul +ManaCost:no cost +Types:Creature Spirit +Colors:white +PT:4/4 +K:Flying +K:Vigilance +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead. +SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard +Oracle:Flying, vigilance\nIf Generous Soul would be put into a graveyard from anywhere, exile it instead. From e4c938035c02fc7110c2d820f9db74d709462708 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 13:19:50 -0400 Subject: [PATCH 3/9] Card.java Disturb keyword desc builder --- forge-game/src/main/java/forge/game/card/Card.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index fb56057b132..20800d449ab 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1943,7 +1943,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } else { sbLong.append(parts[0]).append(" ").append(ManaCostParser.parse(parts[1])).append("\r\n"); } - } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") || keyword.startsWith("Escape") || keyword.startsWith("Foretell:")) { + } else if (keyword.startsWith("Morph") || keyword.startsWith("Megamorph") + || keyword.startsWith("Escape") || keyword.startsWith("Foretell:") + || keyword.startsWith("Disturb")) { String[] k = keyword.split(":"); sbLong.append(k[0]); if (k.length > 1) { @@ -2544,7 +2546,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { sbBefore.append("\r\n"); } else if (keyword.startsWith("Entwine") || keyword.startsWith("Madness") || keyword.startsWith("Miracle") || keyword.startsWith("Recover") - || keyword.startsWith("Escape") || keyword.startsWith("Foretell:")) { + || keyword.startsWith("Escape") || keyword.startsWith("Foretell:") + || keyword.startsWith("Disturb")) { final String[] k = keyword.split(":"); final Cost cost = new Cost(k[1], false); From 8e037b4f14763911e8e9db42e0fc10d55eb4c6fd Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 13:20:33 -0400 Subject: [PATCH 4/9] add Disturb to AlternativeCost --- .../src/main/java/forge/game/spellability/AlternativeCost.java | 1 + 1 file changed, 1 insertion(+) diff --git a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java index c3965363e72..6739271e7c2 100644 --- a/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java +++ b/forge-game/src/main/java/forge/game/spellability/AlternativeCost.java @@ -5,6 +5,7 @@ public enum AlternativeCost { Bestow, Cycling, // ActivatedAbility Dash, + Disturb, Emerge, Escape, Evoke, From 1ad30900fe4f385a78800072befe94f776bacf95 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 13:21:39 -0400 Subject: [PATCH 5/9] CardProperty.java - various IntelliJ autocorrects --- .../java/forge/game/card/CardProperty.java | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 43b6dcde919..6574fb42875 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -832,7 +832,7 @@ public class CardProperty { return Iterables.any(CardUtil.getThisTurnCast("Card", source, spellAbility), CardPredicates.sharesNameWith(card)); } else if (restriction.equals("MovedToGrave")) { if (!(spellAbility instanceof SpellAbility)) { - final SpellAbility root = ((SpellAbility)spellAbility).getRootAbility(); + final SpellAbility root = ((SpellAbility) spellAbility).getRootAbility(); if (root != null && (root.getPaidList("MovedToGrave") != null) && !root.getPaidList("MovedToGrave").isEmpty()) { final CardCollectionView cards = root.getPaidList("MovedToGrave"); @@ -855,7 +855,7 @@ public class CardProperty { if (!(spellAbility instanceof SpellAbility)) { System.out.println("Looking at TriggeredCard but no SA?"); } else { - Card triggeredCard = ((Card)((SpellAbility)spellAbility).getTriggeringObject(AbilityKey.Card)); + Card triggeredCard = ((Card) ((SpellAbility) spellAbility).getTriggeringObject(AbilityKey.Card)); if (triggeredCard != null && card.sharesNameWith(triggeredCard)) { return true; } @@ -924,10 +924,9 @@ public class CardProperty { } } else if (property.startsWith("SecondSpellCastThisTurn")) { final List cards = CardUtil.getThisTurnCast("Card", source, spellAbility); - if (cards.size() < 2) { + if (cards.size() < 2) { return false; - } - else if (cards.get(1) != card) { + } else if (cards.get(1) != card) { return false; } } else if (property.equals("ThisTurnCast")) { @@ -1232,8 +1231,7 @@ public class CardProperty { if (!cards.contains(card)) { return false; } - } - else if (property.startsWith("lowestCMC")) { + } else if (property.startsWith("lowestCMC")) { final CardCollectionView cards = game.getCardsIn(ZoneType.Battlefield); for (final Card crd : cards) { if (!crd.isLand() && !crd.isImmutable()) { @@ -1369,9 +1367,7 @@ public class CardProperty { if (!Expressions.compare(y, property, x)) { return false; } - } - - else if (property.startsWith("ManaCost")) { + } else if (property.startsWith("ManaCost")) { if (!card.getManaCost().getShortString().equals(property.substring(8))) { return false; } @@ -1412,7 +1408,7 @@ public class CardProperty { // These predicated refer to ongoing combat. If no combat happens, they'll return false (meaning not attacking/blocking ATM) else if (property.startsWith("attacking")) { if (null == combat) return false; - if (property.equals("attacking")) return card.isAttacking(); + if (property.equals("attacking")) return card.isAttacking(); if (property.equals("attackingYou")) return combat.isAttacking(card, sourceController); if (property.equals("attackingSame")) { final GameEntity attacked = combat.getDefenderByAttacker(source); @@ -1424,10 +1420,10 @@ public class CardProperty { GameEntity defender = combat.getDefenderByAttacker(card); if (defender instanceof Card) { // attack on a planeswalker that was removed from combat - if (!((Card)defender).isPlaneswalker()) { + if (!((Card) defender).isPlaneswalker()) { return false; } - defender = ((Card)defender).getController(); + defender = ((Card) defender).getController(); } if (!sourceController.equals(defender)) { return false; @@ -1471,12 +1467,17 @@ public class CardProperty { if (combat.isBlocking(card, c)) { return true; } - }; + } + ; return false; } } else if (property.startsWith("sharesBlockingAssignmentWith")) { - if (null == combat) { return false; } - if (null == combat.getAttackersBlockedBy(source) || null == combat.getAttackersBlockedBy(card)) { return false; } + if (null == combat) { + return false; + } + if (null == combat.getAttackersBlockedBy(source) || null == combat.getAttackersBlockedBy(card)) { + return false; + } CardCollection sourceBlocking = new CardCollection(combat.getAttackersBlockedBy(source)); CardCollection thisBlocking = new CardCollection(combat.getAttackersBlockedBy(card)); @@ -1503,16 +1504,17 @@ public class CardProperty { return false; } String valid = property.split(" ")[1]; - for(Card c : blocked) { + for (Card c : blocked) { if (c.isValid(valid, card.getController(), source, spellAbility)) { return true; } } - for(Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) { + for (Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) { if (blocked.contains(c)) { return true; } - }; + } + ; return false; } else if (property.startsWith("blockedByValidThisTurn ")) { CardCollectionView blocked = card.getBlockedByThisTurn(); @@ -1527,7 +1529,8 @@ public class CardProperty { if (blocked.contains(c)) { return true; } - }; + } + ; return false; } else if (property.startsWith("blockedBySourceThisTurn")) { return source.getBlockedByThisTurn().contains(card); From 33b7a3a0bdf6030ed3aefb0019e51505fdcaf65d Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 13:22:46 -0400 Subject: [PATCH 6/9] GameActionUtil.java build Disturb SA --- .../main/java/forge/game/GameActionUtil.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 00065e4492e..bec9e0454c3 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -175,7 +175,23 @@ public final class GameActionUtil { for (final KeywordInterface inst : source.getKeywords()) { final String keyword = inst.getOriginal(); - if (keyword.startsWith("Escape")) { + if (keyword.startsWith("Disturb")) { + final String[] k = keyword.split(":"); + final Cost disturbCost = new Cost(k[1], true); + + final SpellAbility newSA = sa.copyWithManaCostReplaced(activator, disturbCost); + newSA.setActivatingPlayer(activator); + + newSA.setAlternativeCost(AlternativeCost.Disturb); + newSA.getRestrictions().setZone(ZoneType.Graveyard); + + alternatives.add(newSA); + + String stateAb = "DB$ SetState | Defined$ Self | Mode$ Transform"; + AbilitySub setState = (AbilitySub) AbilityFactory.getAbility(stateAb, source); + + newSA.setSubAbility(setState); + } else if (keyword.startsWith("Escape")) { final String[] k = keyword.split(":"); final Cost escapeCost = new Cost(k[1], true); From f28b91c0c23a88597726470b24a8f271d4ac8795 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 13:23:15 -0400 Subject: [PATCH 7/9] SpellAbility.java isDisturb boolean --- .../src/main/java/forge/game/spellability/SpellAbility.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index 050942a1b30..5be31854b06 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1370,6 +1370,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit return isAlternativeCost(AlternativeCost.Dash); } + public final boolean isDisturb() { + return isAlternativeCost(AlternativeCost.Disturb); + } + public final boolean isEscape() { return isAlternativeCost(AlternativeCost.Escape); } From 2283116844d439e801b8306f709c867dca82bcb2 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 22:08:17 -0400 Subject: [PATCH 8/9] GameActionUtil.java improve Disturb --- .../src/main/java/forge/game/GameActionUtil.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index bec9e0454c3..e72ed9f51b1 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -182,15 +182,21 @@ public final class GameActionUtil { final SpellAbility newSA = sa.copyWithManaCostReplaced(activator, disturbCost); newSA.setActivatingPlayer(activator); + newSA.putParam("PrecostDesc", "Disturb —"); + newSA.putParam("CostDesc", disturbCost.toString()); + + // makes new SpellDescription + final StringBuilder desc = new StringBuilder(); + desc.append(newSA.getCostDescription()); + desc.append("(").append(inst.getReminderText()).append(")"); + newSA.setDescription(desc.toString()); + newSA.putParam("AfterDescription", "(Disturbed)"); + newSA.setAlternativeCost(AlternativeCost.Disturb); newSA.getRestrictions().setZone(ZoneType.Graveyard); + newSA.setCardState(source.getAlternateState()); alternatives.add(newSA); - - String stateAb = "DB$ SetState | Defined$ Self | Mode$ Transform"; - AbilitySub setState = (AbilitySub) AbilityFactory.getAbility(stateAb, source); - - newSA.setSubAbility(setState); } else if (keyword.startsWith("Escape")) { final String[] k = keyword.split(":"); final Cost escapeCost = new Cost(k[1], true); From 4680f33b56265150c2eb9cee9d24c7af25389e62 Mon Sep 17 00:00:00 2001 From: Northmoc Date: Fri, 3 Sep 2021 22:08:43 -0400 Subject: [PATCH 9/9] baithook_angler_hook_haunt_drifter.txt --- .../baithook_angler_hook_haunt_drifter.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 forge-gui/res/cardsfolder/upcoming/baithook_angler_hook_haunt_drifter.txt diff --git a/forge-gui/res/cardsfolder/upcoming/baithook_angler_hook_haunt_drifter.txt b/forge-gui/res/cardsfolder/upcoming/baithook_angler_hook_haunt_drifter.txt new file mode 100644 index 00000000000..ee761c99313 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/baithook_angler_hook_haunt_drifter.txt @@ -0,0 +1,19 @@ +Name:Baithook Angler +ManaCost:1 U +Types:Creature Human Peasant +PT:2/1 +K:Disturb:1 U +AlternateMode:DoubleFaced +Oracle:Disturb {1}{U} (You may cast this card from your graveyard transformed for its disturb cost.) + +ALTERNATE + +Name:Hook-Haunt Drifter +ManaCost:no cost +Types:Creature Spirit +Colors:blue +PT:1/2 +K:Flying +R:Event$ Moved | ValidCard$ Card.Self | Destination$ Graveyard | ReplaceWith$ Exile | Description$ If CARDNAME would be put into a graveyard from anywhere, exile it instead. +SVar:Exile:DB$ ChangeZone | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard +Oracle:Flying\nIf Hook-Haunt Drifter would be put into a graveyard from anywhere, exile it instead.