From 895b7b54c5d17c3fdb1c5712ec9ba51a24599237 Mon Sep 17 00:00:00 2001 From: Northmoc <103371817+Northmoc@users.noreply.github.com> Date: Wed, 29 May 2024 06:58:50 -0400 Subject: [PATCH] MH3: nethergoyf.txt + support (#5311) --- .../main/java/forge/game/cost/CostExile.java | 11 ++++++++ .../res/cardsfolder/upcoming/nethergoyf.txt | 10 +++++++ forge-gui/res/languages/de-DE.properties | 1 + forge-gui/res/languages/en-US.properties | 1 + forge-gui/res/languages/es-ES.properties | 1 + forge-gui/res/languages/fr-FR.properties | 1 + forge-gui/res/languages/it-IT.properties | 1 + forge-gui/res/languages/ja-JP.properties | 1 + forge-gui/res/languages/pt-BR.properties | 1 + forge-gui/res/languages/zh-CN.properties | 1 + .../match/input/InputSelectCardsFromList.java | 4 +-- .../input/InputSelectEntitiesFromList.java | 22 +++++++++++----- .../match/input/InputSelectManyBase.java | 4 ++- .../java/forge/player/HumanCostDecision.java | 26 +++++++++++++++++-- 14 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/nethergoyf.txt diff --git a/forge-game/src/main/java/forge/game/cost/CostExile.java b/forge-game/src/main/java/forge/game/cost/CostExile.java index 5269e8dff2f..5a716b773ea 100644 --- a/forge-game/src/main/java/forge/game/cost/CostExile.java +++ b/forge-game/src/main/java/forge/game/cost/CostExile.java @@ -202,12 +202,23 @@ public class CostExile extends CostPartWithList { type = TextUtil.fastReplace(type, "+withSharedCardType", ""); } + int nTypes = -1; + if (type.contains("+withTypesGE")) { + String num = type.split("withTypesGE")[1]; + type = TextUtil.fastReplace(type, TextUtil.concatNoSpace("+withTypesGE", num), ""); + nTypes = Integer.parseInt(num); + } + if (!type.contains("X") || ability.getXManaCostPaid() != null) { list = CardLists.getValidCards(list, type.split(";"), payer, source, ability); } int amount = this.getAbilityAmount(ability); + if (nTypes > -1) { + if (CardFactoryUtil.getCardTypesFromList(list) < nTypes) return false; + } + if (sharedType) { // will need more logic if cost ever wants more than 2 that share a type if (list.size() < amount) return false; for (int i = 0; i < list.size(); i++) { diff --git a/forge-gui/res/cardsfolder/upcoming/nethergoyf.txt b/forge-gui/res/cardsfolder/upcoming/nethergoyf.txt new file mode 100644 index 00000000000..5ce7c8818f8 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/nethergoyf.txt @@ -0,0 +1,10 @@ +Name:Nethergoyf +ManaCost:B +Types:Creature Lhurgoyf +PT:*/1+* +S:Mode$ Continuous | EffectZone$ All | CharacteristicDefining$ True | SetPower$ Count$ValidGraveyard Card.YouOwn$CardTypes | SetToughness$ Count$ValidGraveyard Card.YouOwn$CardTypes/Plus.1 | Description$ CARDNAME's power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1. +K:Escape:2 B ExileFromGrave +SVar:X:Count$xPaid +AI:RemoveDeck:All +DeckHints:Ability$Discard|Sacrifice|Graveyard +Oracle:Nethergoyf's power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1.\nEscape—{2}{B}, Exile any number of other cards from your graveyard with four or more card types among them. (You may cast this card from your graveyard for its escape cost.) diff --git a/forge-gui/res/languages/de-DE.properties b/forge-gui/res/languages/de-DE.properties index 691729e6bf5..755e7bcbb76 100644 --- a/forge-gui/res/languages/de-DE.properties +++ b/forge-gui/res/languages/de-DE.properties @@ -2479,6 +2479,7 @@ lblSelectATargetToSacrifice=Wähle ein(e) {0} zum Opfern (noch {1}) lblSelectOneOfCardsToTapAlreadyChosen=Wähle eine Karte zum tappen. Bereits gewählt: lblSelectACreatureToTap=Wähle eine Kreatur zum Tappen. lblSelectToExile=Select {0} or more to exile +lblSelectAnyNumToExile=Select any number to exile lblEnoughValidCardNotToPayTheCost=Nicht genug gültige Karten zum Tappen übrig um die Kosten zu bezahlen. lblCostPaymentInvalid=Bezahlung der Kosten unmöglich lblSelectATargetToTap=Wähle ein(e) {0} zum Tappen (noch {1}) diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index b6239712def..cee36275d34 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -2492,6 +2492,7 @@ lblSelectATargetToSacrifice=Select {0} to sacrifice ({1} left) lblSelectOneOfCardsToTapAlreadyChosen=Select one of the cards to tap. Already chosen: lblSelectACreatureToTap=Select a creature to tap. lblSelectToExile=Select {0} or more to exile +lblSelectAnyNumToExile=Select any number to exile lblCollectEvidence=Exile evidence with a total CMC {0} or more lblEnoughValidCardNotToPayTheCost=Not enough valid cards left to tap to pay the cost. lblCostPaymentInvalid=Cost payment invalid diff --git a/forge-gui/res/languages/es-ES.properties b/forge-gui/res/languages/es-ES.properties index d8682093458..3ada17f4313 100644 --- a/forge-gui/res/languages/es-ES.properties +++ b/forge-gui/res/languages/es-ES.properties @@ -2480,6 +2480,7 @@ lblSelectATargetToSacrifice=Selecciona un {0} para sacrificar ({1} pendiente) lblSelectOneOfCardsToTapAlreadyChosen=Selecciona una de las cartas para girar. Ya elegido: lblSelectACreatureToTap=Selecciona una criatura para girar. lblSelectToExile=Selecciona {0} o más para exilar +lblSelectAnyNumToExile=Select any number to exile lblEnoughValidCardNotToPayTheCost=No quedan suficientes cartas válidas para girar para pagar el coste. lblCostPaymentInvalid=Pago del coste no válido lblSelectATargetToTap=Selecciona un/a {0} para girar ({1} pendiente) diff --git a/forge-gui/res/languages/fr-FR.properties b/forge-gui/res/languages/fr-FR.properties index 5f77286c599..e8a8b325a77 100644 --- a/forge-gui/res/languages/fr-FR.properties +++ b/forge-gui/res/languages/fr-FR.properties @@ -2483,6 +2483,7 @@ lblSelectATargetToSacrifice=Sélectionnez {0} à sacrifier ({1} à gauche) lblSelectOneOfCardsToTapAlreadyChosen=S\u00e9lectionnez une des cartes \u00e0 tapoter. Déjà choisi : lblSelectACreatureToTap=S\u00e9lectionnez une cr\u00e9ature \u00e0 engager. lblSelectToExile=Sélectionnez {0} ou plus pour exiler +lblSelectAnyNumToExile=Select any number to exile lblEnoughValidCardNotToPayTheCost=Il ne reste plus assez de cartes valides à toucher pour payer le coût. lblCostPaymentInvalid=Paiement de coût invalide lblSelectATargetToTap=Sélectionnez un(e) {0} à appuyer ({1} à gauche) diff --git a/forge-gui/res/languages/it-IT.properties b/forge-gui/res/languages/it-IT.properties index 84cdc2e92cb..8d842f541ed 100644 --- a/forge-gui/res/languages/it-IT.properties +++ b/forge-gui/res/languages/it-IT.properties @@ -2480,6 +2480,7 @@ lblSelectATargetToSacrifice=Seleziona una carta {0} da scarificare ({1} rimasta/ lblSelectOneOfCardsToTapAlreadyChosen=Seleziona una delle carta da tappare. Già scelto: lblSelectACreatureToTap=Seleziona una creatura da tappare. lblSelectToExile=Seleziona {0} o più per esiliare +lblSelectAnyNumToExile=Select any number to exile lblEnoughValidCardNotToPayTheCost=Non sono rimaste abbastanza carte valide da tappare per pagare il costo lblCostPaymentInvalid=Pagamento del costo non valido lblSelectATargetToTap=Seleziona una carta {0} da tapapre ({1} rimasta/e) diff --git a/forge-gui/res/languages/ja-JP.properties b/forge-gui/res/languages/ja-JP.properties index 6f46e31d649..a5a4c06a783 100644 --- a/forge-gui/res/languages/ja-JP.properties +++ b/forge-gui/res/languages/ja-JP.properties @@ -2479,6 +2479,7 @@ lblSelectATargetToSacrifice=生け贄に捧げ {0}を選択(残り{1}) lblSelectOneOfCardsToTapAlreadyChosen=タップするカードを選ぶ。既に選択: lblSelectACreatureToTap=タップするクリーチャーを選ぶ。 lblSelectToExile=追放するには{0}個以上を選択してください +lblSelectAnyNumToExile=Select any number to exile lblEnoughValidCardNotToPayTheCost=コストを支払いためにタップできるカードが足りません。 lblCostPaymentInvalid=無効な支払い lblSelectATargetToTap=タップする {0}を選択(残り{1}) diff --git a/forge-gui/res/languages/pt-BR.properties b/forge-gui/res/languages/pt-BR.properties index 6f1640f6478..9e86a80d44f 100644 --- a/forge-gui/res/languages/pt-BR.properties +++ b/forge-gui/res/languages/pt-BR.properties @@ -2555,6 +2555,7 @@ lblSelectATargetToSacrifice=Selecione {0} para sacrificar ({1} restantes) lblSelectOneOfCardsToTapAlreadyChosen=Escolha uma das cartas para virar. Já escolhido\: lblSelectACreatureToTap=Escolha as criaturas para virar. lblSelectToExile=Selecione {0} ou mais para exilar +lblSelectAnyNumToExile=Select any number to exile lblEnoughValidCardNotToPayTheCost=Cartas válidas insuficientes para pagar o custo. lblCostPaymentInvalid=Custo de pagamento inválido lblSelectATargetToTap=Escolha um(n) {0} para virar ({1} restante) diff --git a/forge-gui/res/languages/zh-CN.properties b/forge-gui/res/languages/zh-CN.properties index 9ab882819cd..548b43ff052 100644 --- a/forge-gui/res/languages/zh-CN.properties +++ b/forge-gui/res/languages/zh-CN.properties @@ -2484,6 +2484,7 @@ lblSelectATargetToSacrifice=选择一个{0}进行牺牲(还剩{1}) lblSelectOneOfCardsToTapAlreadyChosen=选择其中的一张进行横置。已经选择: lblSelectACreatureToTap=选择一个生物进行横置 lblSelectToExile=选择{0}个或更多要放逐的 +lblSelectAnyNumToExile=Select any number to exile lblEnoughValidCardNotToPayTheCost=没有足够的有效卡牌用于支付费用。 lblCostPaymentInvalid=付费失败 lblSelectATargetToTap=选择{0}进行横置(还剩{1}) diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsFromList.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsFromList.java index bd419bc4a50..1611f2497dd 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsFromList.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectCardsFromList.java @@ -24,8 +24,8 @@ public class InputSelectCardsFromList extends InputSelectEntitiesFromList super(controller, min, max, validCards, sa); } - public InputSelectCardsFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView validCards, final SpellAbility sa, final int tally) { - super(controller, min, max, validCards, sa, tally); + public InputSelectCardsFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView validCards, final SpellAbility sa, final String tallyType, final int tally) { + super(controller, min, max, validCards, sa, tallyType, tally); } } diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectEntitiesFromList.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectEntitiesFromList.java index b6dcbf46ee0..c129b45b240 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectEntitiesFromList.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectEntitiesFromList.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.List; import forge.game.GameEntity; +import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardView; @@ -32,15 +33,15 @@ public class InputSelectEntitiesFromList extends InputSele protected Iterable zonesShown; // want to hide these zones when input done public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView validChoices0) { - this(controller, min, max, validChoices0, null, 0); + this(controller, min, max, validChoices0, null, "", 0); } public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView validChoices0, final SpellAbility sa0) { - this(controller, min, max, validChoices0, sa0, 0); + this(controller, min, max, validChoices0, sa0, "", 0); } - public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView validChoices0, final SpellAbility sa0, final int tally0) { - super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()), sa0, tally0); + public InputSelectEntitiesFromList(final PlayerControllerHuman controller, final int min, final int max, final FCollectionView validChoices0, final SpellAbility sa0, final String tallyType0, final int tally0) { + super(controller, Math.min(min, validChoices0.size()), Math.min(max, validChoices0.size()), sa0, tallyType0, tally0); validChoices = validChoices0; if (min > validChoices.size()) { // pfps does this really do anything useful?? System.out.println(String.format("Trying to choose at least %d things from a list with only %d things!", min, validChoices.size())); @@ -142,10 +143,17 @@ public class InputSelectEntitiesFromList extends InputSele } else if (sa.getPayCosts().hasSpecificCostType(CostExile.class) && tally > 0) { - msg.append("\n").append(Localizer.getInstance().getMessage("lblCMC")).append(": "); - msg.append(CardLists.getTotalCMC((FCollection)getSelected())).append(" / ").append(tally); + msg.append("\n"); + if (tallyType.equals("CMC")) { + msg.append(Localizer.getInstance().getMessage("lblCMC")).append(": "); + msg.append(CardLists.getTotalCMC((FCollection)getSelected())).append(" / ").append(tally); + } else if (tallyType.equals("Types")) { + msg.append(Localizer.getInstance().getMessage("lblTypes")).append(": "); + msg.append(AbilityUtils.countCardTypesFromList((FCollection)getSelected(), false)); + msg.append(" / ").append(tally); + } + } } - } return msg.toString(); } diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectManyBase.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectManyBase.java index ea7229736bd..3c4b62f2908 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectManyBase.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputSelectManyBase.java @@ -23,6 +23,7 @@ public abstract class InputSelectManyBase extends InputSyn protected boolean allowCancel = false; protected SpellAbility sa = null; protected CardView card; + protected String tallyType; protected int tally; protected String message = "Source-Card-Name - Select %d more card(s)"; @@ -44,12 +45,13 @@ public abstract class InputSelectManyBase extends InputSyn } } - protected InputSelectManyBase(final PlayerControllerHuman controller, final int min, final int max, final SpellAbility sa0, final int tally0) { + protected InputSelectManyBase(final PlayerControllerHuman controller, final int min, final int max, final SpellAbility sa0, final String tallyType0, final int tally0) { this(controller,min,max); this.sa = sa0; if (sa0 != null) { this.card = sa0.getView().getHostCard(); } + this.tallyType = tallyType0; this.tally = tally0; } diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 6e2119f0ed2..b0a0f5d7722 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -21,6 +21,7 @@ import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; +import forge.game.card.CardFactoryUtil; import forge.game.card.CardLists; import forge.game.card.CardPredicates; import forge.game.card.CardPredicates.Presets; @@ -84,7 +85,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { CardCollection list = CardLists.filter(player.getCardsIn(ZoneType.Graveyard), CardPredicates.canExiledBy(ability, isEffect())); final int total = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); final InputSelectCardsFromList inp = - new InputSelectCardsFromList(controller, 0, list.size(), list, ability, total); + new InputSelectCardsFromList(controller, 0, list.size(), list, ability, "CMC", total); inp.setMessage(Localizer.getInstance().getMessage("lblCollectEvidence", total)); inp.setCancelAllowed(true); inp.showAndWait(); @@ -265,6 +266,12 @@ public class HumanCostDecision extends CostDecisionMakerBase { sharedType = true; type = TextUtil.fastReplace(type, "+withSharedCardType", ""); } + int nTypes = -1; + if (type.contains("+withTypesGE")) { + String num = type.split("withTypesGE")[1]; + type = TextUtil.fastReplace(type, TextUtil.concatNoSpace("+withTypesGE", num), ""); + nTypes = Integer.parseInt(num); + } CardCollection list; if (cost.zoneRestriction != 1) { @@ -286,7 +293,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { int needed = Integer.parseInt(cost.getAmount().split("\\+")[0]); final int total = AbilityUtils.calculateAmount(source, totalM, ability); final InputSelectCardsFromList inp = - new InputSelectCardsFromList(controller, needed, list.size(), list, ability, total); + new InputSelectCardsFromList(controller, needed, list.size(), list, ability, "CMC", total); inp.setMessage(Localizer.getInstance().getMessage("lblSelectToExile", Lang.getNumeral(needed))); inp.setCancelAllowed(true); inp.showAndWait(); @@ -297,6 +304,21 @@ public class HumanCostDecision extends CostDecisionMakerBase { return PaymentDecision.card(inp.getSelected()); } + if (nTypes > -1) { + final InputSelectCardsFromList inp = new InputSelectCardsFromList(controller, 1, list.size(), list, + ability, "Types", nTypes); + inp.setMessage(cost.getAmount().equals("X") ? + Localizer.getInstance().getMessage("lblSelectAnyNumToExile") : + Localizer.getInstance().getMessage("lblSelectToExile", Lang.getNumeral(nTypes))); + inp.setCancelAllowed(true); + inp.showAndWait(); + if (inp.hasCancelled() || + !Expressions.compare(CardFactoryUtil.getCardTypesFromList(list), "GE", nTypes)) { + return null; + } + return PaymentDecision.card(inp.getSelected()); + } + int c = cost.getAbilityAmount(ability); if (list.size() < c) {