From b065db77ed3b9e6b7e9da689d1193981bf18de0e Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Mon, 28 Feb 2022 13:38:06 +0000 Subject: [PATCH 1/3] Resolve "Kamigawa: Compleated" --- .../src/main/java/forge/ai/ComputerUtilMana.java | 3 +++ .../src/main/java/forge/game/ForgeScript.java | 2 ++ .../main/java/forge/game/card/CardFactoryUtil.java | 5 +++++ .../java/forge/game/spellability/SpellAbility.java | 8 ++++++++ .../cardsfolder/upcoming/tamiyo_compleated_sage.txt | 13 +++++++++++++ .../match/input/InputPayManaOfCostPayment.java | 1 + .../java/forge/player/HumanPlaySpellAbility.java | 4 ++-- 7 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/tamiyo_compleated_sage.txt diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 990666fa459..23ffb4a9a98 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -795,6 +795,9 @@ public class ComputerUtilMana { if (toPay.isPhyrexian()) { cost.payPhyrexian(); + if (!test) { + sa.setSpendPhyrexianMana(true); + } } else if (lifeInsteadOfBlack) { cost.decreaseShard(ManaCostShard.BLACK, 1); } diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index 90a8d8a02fe..263e03e5b96 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -182,6 +182,8 @@ public class ForgeScript { return sa.hasParam("Daybound"); } else if (property.equals("Nightbound")) { return sa.hasParam("Nightbound"); + } else if (property.equals("paidPhyrexianMana")) { + return sa.getSpendPhyrexianMana(); } else if (property.equals("MayPlaySource")) { StaticAbility m = sa.getMayPlay(); if (m == null) { diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index e216ebfb222..b8ec1f4eca3 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2147,6 +2147,11 @@ public class CardFactoryUtil { re.setOverridingAbility(saReturn); + inst.addReplacement(re); + } else if (keyword.equals("Compleated")) { + String sb = "etbCounter:LOYALTY:-2:ValidCard$ Card.CastSa Spell.paidPhyrexianMana:If life was paid, this planeswalker enters with two fewer loyalty counters"; + final ReplacementEffect re = makeEtbCounter(sb, card, intrinsic); + inst.addReplacement(re); } else if (keyword.startsWith("Dredge")) { final String dredgeAmount = keyword.split(":")[1]; 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 8cc1569dd8a..8d494be7d60 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -115,6 +115,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private Player targetingPlayer = null; private Pair controlledByPlayer = null; private ManaCostBeingPaid manaCostBeingPaid = null; + private boolean spentPhyrexian = false; private SpellAbility grantorOriginal = null; private StaticAbility grantorStatic = null; @@ -613,6 +614,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit payingMana.clear(); } + public final boolean getSpendPhyrexianMana() { + return this.spentPhyrexian; + } + public final void setSpendPhyrexianMana(boolean value) { + this.spentPhyrexian = value; + } + public final void applyPayingManaEffects() { Card host = getHostCard(); diff --git a/forge-gui/res/cardsfolder/upcoming/tamiyo_compleated_sage.txt b/forge-gui/res/cardsfolder/upcoming/tamiyo_compleated_sage.txt new file mode 100644 index 00000000000..0c8b707251b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/tamiyo_compleated_sage.txt @@ -0,0 +1,13 @@ +Name:Tamiyo, Compleated Sage +ManaCost:2 G PGU U +Types:Legendary Planeswalker Tamiyo +Loyalty:5 +K:Compleated +A:AB$ Tap | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Artifact,Creature | TgtPrompt$ Select up to one target artifact or creature | TargetMin$ 0 | TargetMax$ 1 | SubAbility$ DBPump | SpellDescription$ Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step. +SVar:DBPump:DB$ Pump | Defined$ Targeted | KW$ HIDDEN This card doesn't untap during your next untap step. | Duration$ Permanent +A:AB$ ChangeZone | Cost$ SubCounter | Planeswalker$ True | ValidTgts$ Permanent.nonLand+cmcEQX | TgtPrompt$ Select target nonland permanent card with mana value X | AILogic$ BestCard | Origin$ Graveyard | Destination$ Exile | SubAbility$ DBCopy | SpellDescription$ Exile target nonland permanent card with mana value X from your graveyard. +SVar:DBCopy:DB$ CopyPermanent | Defined$ Targeted | SpellDescription$ Create a token that's a copy of that card. +SVar:X:Count$xPaid +A:AB$ Token | Cost$ SubCounter<7/LOYALTY> | Planeswalker$ True | Ultimate$ True | TokenScript$ tamiyos_notebook | SpellDescription$ Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card." +DeckHas:Ability$Token & Type$Artifact +Oracle:Compleated ({PGU} can be paid with {G}, {U}, or 2 life. If life was paid, this planeswalker enters with two fewer loyalty counters.)\n[+1]: Tap up to one target artifact or creature. It doesn't untap during its controller's next untap step.\n[−X]: Exile target nonland permanent card with mana value X from your graveyard. Create a token that's a copy of that card.\n[−7]: Create Tamiyo's Notebook, a legendary colorless artifact token with "Spells you cast cost {2} less to cast" and "{T}: Draw a card." diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java index e8b414817e6..7df67e46d03 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputPayManaOfCostPayment.java @@ -46,6 +46,7 @@ public class InputPayManaOfCostPayment extends InputPayMana { if (player == selected) { if (player.canPayLife(this.phyLifeToLose + 2, this.effect, saPaidFor)) { if (manaCost.payPhyrexian()) { + saPaidFor.setSpendPhyrexianMana(true); this.phyLifeToLose += 2; } else { if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) { diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 2b09d24bce5..33d65aba859 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -217,9 +217,9 @@ public class HumanPlaySpellAbility { fromZone.add(oldCard, zonePosition >= 0 ? Integer.valueOf(zonePosition) : null); ability.setHostCard(oldCard); ability.setXManaCostPaid(null); + ability.setSpendPhyrexianMana(false); if (ability.hasParam("Announce")) { - final String announce = ability.getParam("Announce"); - for (final String aVar : announce.split(",")) { + for (final String aVar : ability.getParam("Announce").split(",")) { final String varName = aVar.trim(); if (!varName.equals("X")) { ability.setSVar(varName, "0"); From e09aa4af9534367c937b3d3c2d7ae51fc46026b0 Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Mon, 28 Feb 2022 22:41:28 +0100 Subject: [PATCH 2/3] Fix card KW on player --- .../src/main/java/forge/ai/AiController.java | 4 +--- .../main/java/forge/ai/AiCostDecision.java | 6 ++---- .../src/main/java/forge/ai/AiProfileUtil.java | 3 +-- .../src/main/java/forge/ai/ComputerUtil.java | 4 ++-- .../java/forge/ai/ComputerUtilAbility.java | 6 ++---- .../main/java/forge/ai/ComputerUtilCard.java | 8 +++----- .../java/forge/ai/PlayerControllerAi.java | 19 +++++++++---------- .../src/main/java/forge/game/GameAction.java | 1 + .../forge/game/player/PlayerController.java | 15 +++++++-------- .../forge/game/spellability/SpellAbility.java | 3 +-- .../res/cardsfolder/b/bag_of_holding.txt | 2 +- forge-gui/res/cardsfolder/c/carbonize.txt | 4 ++-- forge-gui/res/cardsfolder/d/disintegrate.txt | 4 ++-- .../res/cardsfolder/s/scorching_lava.txt | 4 ++-- 14 files changed, 36 insertions(+), 47 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 7b00f98b9bb..572fc0e2f25 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -160,9 +160,7 @@ public class AiController { all.add(ccvPlayerLibrary.get(0)); } - for (final Player opp : player.getOpponents()) { - all.addAll(opp.getCardsIn(ZoneType.Exile)); - } + all.addAll(player.getOpponents().getCardsIn(ZoneType.Exile)); final List spellAbilities = Lists.newArrayList(); for (final Card c : all) { diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 2b6f3500335..d18fe49da37 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -344,8 +344,7 @@ public class AiCostDecision extends CostDecisionMakerBase { } } chosen = chosen.subList(0, c); - } - else { + } else { chosen = ComputerUtil.choosePutToLibraryFrom(player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c, ability); } return chosen.isEmpty() ? null : PaymentDecision.card(chosen); @@ -363,8 +362,7 @@ public class AiCostDecision extends CostDecisionMakerBase { Card card; if (cost.getType().equals("Creature.YouCtrl")) { card = ComputerUtilCard.getWorstCreatureAI(typeList); - } - else { + } else { card = ComputerUtilCard.getWorstPermanentAI(typeList, false, false, false, false); } return PaymentDecision.card(card); diff --git a/forge-ai/src/main/java/forge/ai/AiProfileUtil.java b/forge-ai/src/main/java/forge/ai/AiProfileUtil.java index e119d6b5ca0..a1848a9b558 100644 --- a/forge-ai/src/main/java/forge/ai/AiProfileUtil.java +++ b/forge-ai/src/main/java/forge/ai/AiProfileUtil.java @@ -119,8 +119,7 @@ public class AiProfileUtil { * @return ArrayList - an array of strings containing all * available profiles. */ - public static List getAvailableProfiles() - { + public static List getAvailableProfiles() { final List availableProfiles = new ArrayList<>(); final File dir = new File(AI_PROFILE_DIR); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 9ff0c3f8b41..f130e08e967 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -703,13 +703,13 @@ public class ComputerUtil { if (pow <= 0) { continue; } - totalPower += pow; if (pow >= amount) { // If the power of this creature matches the totalPower needed // Might as well only use this creature? tapList.clear(); } tapList.add(next); + totalPower = CardLists.getTotalPower(tapList, true, sa.hasParam("Crew")); if (totalPower >= amount) { break; } @@ -793,7 +793,7 @@ public class ComputerUtil { boolean exceptSelf = "ExceptSelf".equals(source.getParam("AILogic")); boolean removedSelf = false; - if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit")) { + if (isOptional && (source.hasParam("Devour") || source.hasParam("Exploit"))) { if (source.hasParam("Exploit")) { for (Trigger t : host.getTriggers()) { if (t.getMode() == TriggerType.Exploited) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java index 8766eff1f05..bfcad761181 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java @@ -72,10 +72,8 @@ public class ComputerUtilAbility { if (!player.getCardsIn(ZoneType.Library).isEmpty()) { all.add(player.getCardsIn(ZoneType.Library).get(0)); } - for (Player p : game.getPlayers()) { - all.addAll(p.getCardsIn(ZoneType.Exile)); - all.addAll(p.getCardsIn(ZoneType.Battlefield)); - } + all.addAll(game.getPlayers().getCardsIn(ZoneType.Exile)); + all.addAll(game.getPlayers().getCardsIn(ZoneType.Battlefield)); return all; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index eae57125e27..939f79f7a26 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1361,7 +1361,7 @@ public class ComputerUtilCard { if (!Iterables.any(oppCreatures, CardPredicates.possibleBlockers(pumped))) { threat *= 2; } - if (c.getNetPower() == 0 && c == sa.getHostCard() && power > 0 ) { + if (c.getNetPower() == 0 && c == sa.getHostCard() && power > 0) { threat *= 4; //over-value self +attack for 0 power creatures which may be pumped further after attacking } chance += threat; @@ -1378,9 +1378,7 @@ public class ComputerUtilCard { && ComputerUtilMana.hasEnoughManaSourcesToCast(sa, ai)) { combatTrick = true; - final List kws = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) - : Lists.newArrayList(); - for (String kw : kws) { + for (String kw : keywords) { if (!kw.equals("Trample") && !kw.equals("First Strike") && !kw.equals("Double Strike")) { combatTrick = false; break; @@ -1565,7 +1563,7 @@ public class ComputerUtilCard { } //5. if the life of the computer is in danger, try to pump blockers blocking Tramplers - if (combat.isBlocking(c) && toughness > 0 ) { + if (combat.isBlocking(c) && toughness > 0) { List blockedBy = combat.getAttackersBlockedBy(c); boolean attackerHasTrample = false; for (Card b : blockedBy) { diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 1406126a762..616e66069e4 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -242,11 +242,11 @@ public class PlayerControllerAi extends PlayerController { SpellAbility selected; do { selected = chooseSingleSpellForEffect(remaining, sa, title, params); - if ( selected != null ) { + if (selected != null) { remaining.remove(selected); selecteds.add(selected); } - } while ( (selected != null ) && (selecteds.size() < num) ); + } while (selected != null && selecteds.size() < num); return selecteds; } @@ -544,12 +544,6 @@ public class PlayerControllerAi extends PlayerController { return getAi().chooseCardsToDelve(genericAmount, grave); } - @Override - public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate filter, boolean optional) { - // AI currently can't do this. But when it can it will need to be based on Ability API - return null; - } - @Override public CardCollectionView chooseCardsToDiscardUnlessType(int num, CardCollectionView hand, String uType, SpellAbility sa) { String [] splitUTypes = uType.split(","); @@ -1099,6 +1093,12 @@ public class PlayerControllerAi extends PlayerController { return brains.doTrigger(currentAbility, true); } + @Override + public TargetChoices chooseNewTargetsFor(SpellAbility ability, Predicate filter, boolean optional) { + // AI currently can't do this. But when it can it will need to be based on Ability API + return null; + } + @Override public boolean chooseCardsPile(SpellAbility sa, CardCollectionView pile1, CardCollectionView pile2, String faceUp) { if (faceUp.equals("True")) { @@ -1341,8 +1341,7 @@ public class PlayerControllerAi extends PlayerController { } @Override - public List chooseOptionalCosts(SpellAbility chosen, - List optionalCostValues) { + public List chooseOptionalCosts(SpellAbility chosen, List optionalCostValues) { List chosenOptCosts = Lists.newArrayList(); Cost costSoFar = chosen.getPayCosts().copy(); diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 4d93d16a262..08ac6758544 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -1308,6 +1308,7 @@ public class GameAction { desCreats = new CardCollection(); } desCreats.add(c); + c.setHasBeenDealtDeathtouchDamage(false); checkAgain = true; break; } diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index b596ce17c6c..137bcc5b8a3 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -101,7 +101,7 @@ public abstract class PlayerController { public final SpellAbility getAbilityToPlay(final Card hostCard, final List abilities) { return getAbilityToPlay(hostCard, abilities, null); } public abstract SpellAbility getAbilityToPlay(Card hostCard, List abilities, ITriggerEvent triggerEvent); - //public abstract void playFromSuspend(Card c); + @Deprecated public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets); public abstract void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets); @@ -130,13 +130,13 @@ public abstract class PlayerController { public abstract List chooseSpellAbilitiesForEffect(List spells, SpellAbility sa, String title, int num, Map params); - public abstract SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title, - Map params); + public abstract SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title, Map params); public abstract List chooseEntitiesForEffect(FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer, Map params); public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message); public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner); + public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question); public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message); public abstract boolean confirmTrigger(WrappedAbility sa); public abstract Player chooseStartingPlayer(boolean isFirstGame); @@ -170,10 +170,11 @@ public abstract class PlayerController { /** p = target player, validCards - possible discards, min cards to discard */ public abstract CardCollectionView chooseCardsToDiscardFrom(Player playerDiscard, SpellAbility sa, CardCollection validCards, int min, int max); + public abstract CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa); + public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard); public abstract CardCollectionView chooseCardsToDelve(int genericAmount, CardCollection grave); public abstract CardCollectionView chooseCardsToRevealFromHand(int min, int max, CardCollectionView valid); - public abstract CardCollectionView chooseCardsToDiscardUnlessType(int min, CardCollectionView hand, String param, SpellAbility sa); public abstract List chooseSaToActivateFromOpeningHand(List usableFromOpeningHand); public abstract Mana chooseManaFromPool(List manaChoices); @@ -183,7 +184,6 @@ public abstract class PlayerController { } public abstract Object vote(SpellAbility sa, String prompt, List options, ListMultimap votes, Player forPlayer); - public abstract boolean confirmReplacementEffect(ReplacementEffect replacementEffect, SpellAbility effectSA, GameEntity affected, String question); public abstract CardCollectionView getCardsToMulligan(Player firstPlayer); public abstract boolean mulliganKeepHand(Player player, int cardsToReturn); @@ -194,7 +194,6 @@ public abstract class PlayerController { public abstract List chooseSpellAbilityToPlay(); public abstract boolean playChosenSpellAbility(SpellAbility sa); - public abstract CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard); public abstract boolean payManaOptional(Card card, Cost cost, SpellAbility sa, String prompt, ManaPaymentPurpose purpose); public abstract int chooseNumberForKeywordCost(SpellAbility sa, Cost cost, KeywordInterface keyword, String prompt, int max); @@ -213,6 +212,8 @@ public abstract class PlayerController { public boolean chooseBinary(SpellAbility sa, String question, BinaryChoiceType kindOfChoice, Map params) { return chooseBinary(sa, question, kindOfChoice); } public abstract boolean chooseFlipResult(SpellAbility sa, Player flipper, boolean[] results, boolean call); + + @Deprecated public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap); public abstract List chooseModeForAbility(SpellAbility sa, List possible, int min, int num, boolean allowRepeat); @@ -250,7 +251,6 @@ public abstract class PlayerController { public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, boolean effect) { return payManaCost(costPartMana, sa, prompt, null, effect); } - public final boolean payManaCost(CostPartMana costPartMana, SpellAbility sa, String prompt, ManaConversionMatrix matrix, boolean effect) { return payManaCost(costPartMana.getManaCostFor(sa), costPartMana, sa, prompt, matrix, effect); } @@ -261,7 +261,6 @@ public abstract class PlayerController { public abstract List chooseCardsForSplice(SpellAbility sa, List cards); public abstract String chooseCardName(SpellAbility sa, Predicate cpp, String valid, String message); - public abstract String chooseCardName(SpellAbility sa, List faces, String message); public abstract Card chooseDungeon(Player player, List dungeonCards, String message); 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 8d494be7d60..4df71c13d17 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -913,8 +913,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit // if alternate state is viewed while card uses original if (node.isIntrinsic() && node.cardState != null && node.cardState.getCard() == node.getHostCard()) { currentName = node.cardState.getName(); - } - else { + } else { currentName = node.getHostCard().getName(); } desc = CardTranslation.translateMultipleDescriptionText(desc, currentName); diff --git a/forge-gui/res/cardsfolder/b/bag_of_holding.txt b/forge-gui/res/cardsfolder/b/bag_of_holding.txt index 1833b22d7f3..026655ffea8 100644 --- a/forge-gui/res/cardsfolder/b/bag_of_holding.txt +++ b/forge-gui/res/cardsfolder/b/bag_of_holding.txt @@ -5,7 +5,7 @@ T:Mode$ Discarded | ValidCard$ Card.YouCtrl | TriggerZones$ Battlefield | Execut SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCard | Origin$ Graveyard | Destination$ Exile A:AB$ Draw | Cost$ 2 T | NumCards$ 1 | SpellDescription$ Draw a card, then discard a card. | SubAbility$ DBDiscard SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose -AI:RemoveDeck:All A:AB$ ChangeZoneAll | Cost$ 4 T Sac<1/CARDNAME> | ChangeType$ Card.ExiledWithSource | Origin$ Exile | Destination$ Hand | AILogic$ DiscardAllAndRetExiled.noDiscard.minAdv2 | SpellDescription$ Return all cards exiled with CARDNAME to their owner's hand. +AI:RemoveDeck:All AI:RemoveDeck:Random Oracle:Whenever you discard a card, exile that card from your graveyard.\n{2}, {T}: Draw a card, then discard a card.\n{4}, {T}, Sacrifice Bag of Holding: Return all cards exiled with Bag of Holding to their owner's hand. diff --git a/forge-gui/res/cardsfolder/c/carbonize.txt b/forge-gui/res/cardsfolder/c/carbonize.txt index 39cff6eba3b..17cd5453302 100644 --- a/forge-gui/res/cardsfolder/c/carbonize.txt +++ b/forge-gui/res/cardsfolder/c/carbonize.txt @@ -1,6 +1,6 @@ Name:Carbonize ManaCost:2 R Types:Instant -A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DB | ReplaceDyingDefined$ Targeted.Creature | SpellDescription$ CARDNAME deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. -SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature | StackDescription$ None +A:SP$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 3 | SubAbility$ DB | ReplaceDyingDefined$ ThisTargetedCard.Creature | SpellDescription$ CARDNAME deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. +SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature | StackDescription$ None Oracle:Carbonize deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. diff --git a/forge-gui/res/cardsfolder/d/disintegrate.txt b/forge-gui/res/cardsfolder/d/disintegrate.txt index 91fe3100764..550182c28a2 100644 --- a/forge-gui/res/cardsfolder/d/disintegrate.txt +++ b/forge-gui/res/cardsfolder/d/disintegrate.txt @@ -1,7 +1,7 @@ Name:Disintegrate ManaCost:X R Types:Sorcery -A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DB | ReplaceDyingDefined$ Targeted.Creature | SpellDescription$ CARDNAME deals X damage to any target. That creature can't be regenerated this turn. If the creature would die this turn, exile it instead. -SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature +A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SubAbility$ DB | ReplaceDyingDefined$ ThisTargetedCard.Creature | SpellDescription$ CARDNAME deals X damage to any target. That creature can't be regenerated this turn. If the creature would die this turn, exile it instead. +SVar:DB:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature SVar:X:Count$xPaid Oracle:Disintegrate deals X damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. diff --git a/forge-gui/res/cardsfolder/s/scorching_lava.txt b/forge-gui/res/cardsfolder/s/scorching_lava.txt index 3085bdf1987..17a9793554b 100644 --- a/forge-gui/res/cardsfolder/s/scorching_lava.txt +++ b/forge-gui/res/cardsfolder/s/scorching_lava.txt @@ -2,6 +2,6 @@ Name:Scorching Lava ManaCost:1 R Types:Instant K:Kicker:R -A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | ReplaceDyingDefined$ Targeted.Creature | ReplaceDyingCondition$ Kicked | SubAbility$ KickingLava | SpellDescription$ CARDNAME deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. -SVar:KickingLava:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ Targeted.Creature | Condition$ Kicked +A:SP$ DealDamage | Cost$ 1 R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ 2 | ReplaceDyingDefined$ ThisTargetedCard.Creature | ReplaceDyingCondition$ Kicked | SubAbility$ KickingLava | SpellDescription$ CARDNAME deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. +SVar:KickingLava:DB$ Pump | KW$ HIDDEN CARDNAME can't be regenerated. | Defined$ ParentTarget | ConditionDefined$ ParentTarget | ConditionPresent$ Creature | Condition$ Kicked Oracle:Kicker {R} (You may pay an additional {R} as you cast this spell.)\nScorching Lava deals 2 damage to any target. If this spell was kicked, that creature can't be regenerated this turn and if it would die this turn, exile it instead. From af2ace701cf63dbcadcc2beda72b023a5ae124a5 Mon Sep 17 00:00:00 2001 From: TRT <> Date: Tue, 1 Mar 2022 11:50:41 +0100 Subject: [PATCH 3/3] Fix import --- forge-ai/src/main/java/forge/ai/AiAttackController.java | 5 ++++- forge-ai/src/main/java/forge/ai/AiController.java | 6 +++--- forge-ai/src/main/java/forge/ai/ComputerUtilCard.java | 1 - forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 280c76a1027..9f4428ae97c 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -449,6 +449,9 @@ public class AiAttackController { return false; } + // TODO for multiplayer this should either add some heuristics + // so the other opponents attack power is also measured in + // or refactor it with aiLifeInDanger somehow if performance impact isn't too bad CardLists.sortByPowerDesc(oppList); for (Card attacker : oppList) { if (!ComputerUtilCombat.canAttackNextTurn(attacker)) { @@ -1037,7 +1040,7 @@ public class AiAttackController { && defendingOpponent != null && ComputerUtil.countUsefulCreatures(ai) > ComputerUtil.countUsefulCreatures(defendingOpponent) && ai.getLife() > defendingOpponent.getLife() - && !ComputerUtilCombat.lifeInDanger(ai, combat) + && !ComputerUtilCombat.lifeInDanger(ai, combat) // this isn't really doing anything unless the attacking player in combat isn't the AI (which currently isn't used like that) && (ComputerUtilMana.getAvailableManaEstimate(ai) > 0) || tradeIfTappedOut && (ComputerUtilMana.getAvailableManaEstimate(defendingOpponent) == 0) || MyRandom.percentTrue(extraChanceIfOppHasMana) && (!tradeIfLowerLifePressure || (ai.getLifeLostLastTurn() + ai.getLifeLostThisTurn() < diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 572fc0e2f25..04e9397af74 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -701,7 +701,7 @@ public class AiController { // but should work in most circumstances to ensure safety in whatever the AI is using this for. if (manaSources.size() >= cost.getConvertedManaCost()) { for (Card c : manaSources) { - AiCardMemory.rememberCard(player, c, memSet); + memory.rememberCard(c, memSet); } return true; } @@ -837,7 +837,7 @@ public class AiController { // When processing a new SA, clear the previously remembered cards that have been marked to avoid re-entry // which might potentially cause a stack overflow. - AiCardMemory.clearMemorySet(this, AiCardMemory.MemorySet.MARKED_TO_AVOID_REENTRY); + memory.clearMemorySet(AiCardMemory.MemorySet.MARKED_TO_AVOID_REENTRY); // TODO before suspending some spells try to predict if relevant targets can be expected if (sa.getApi() != null) { @@ -1488,7 +1488,7 @@ public class AiController { predictedCombatNextTurn = null; // Reset priority mana reservation that's meant to work for one spell only - AiCardMemory.clearMemorySet(player, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL); + memory.clearMemorySet(AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_NEXT_SPELL); if (useSimulation) { return singleSpellAbilityList(simPicker.chooseSpellAbilityToPlay(null)); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 939f79f7a26..3d74f20b4fd 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1,7 +1,6 @@ package forge.ai; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index b1b584302cd..ed8202a1422 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -417,6 +417,7 @@ public class ComputerUtilCombat { return false; } + // TODO check for replacement effect instead CardCollectionView otb = ai.getCardsIn(ZoneType.Battlefield); // Special cases: // AI can't lose in combat in presence of Worship (with creatures)