From ea26f6c68980dcd3b097358c2e61bf43838b6c18 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 24 May 2020 11:02:51 +0000 Subject: [PATCH] Human Play: try to remove usage of game.getCard --- .../src/main/java/forge/ai/AiController.java | 17 +- .../main/java/forge/ai/AiCostDecision.java | 2 +- .../java/forge/ai/ComputerUtilAbility.java | 4 +- .../main/java/forge/ai/ComputerUtilCard.java | 6 +- .../java/forge/ai/ComputerUtilCombat.java | 10 +- .../main/java/forge/ai/ComputerUtilCost.java | 2 +- .../main/java/forge/ai/ComputerUtilMana.java | 27 +- .../main/java/forge/ai/CreatureEvaluator.java | 2 +- .../java/forge/ai/PlayerControllerAi.java | 4 +- .../main/java/forge/ai/SpellAbilityAi.java | 2 +- .../main/java/forge/ai/ability/AnimateAi.java | 1 - .../main/java/forge/ai/ability/AttachAi.java | 4 +- .../java/forge/ai/ability/ChangeZoneAi.java | 8 +- .../java/forge/ai/ability/ChooseCardAi.java | 2 +- .../forge/ai/ability/CopySpellAbilityAi.java | 4 +- .../java/forge/ai/ability/CountersPutAi.java | 3 +- .../forge/ai/ability/CountersPutAllAi.java | 2 +- .../java/forge/ai/ability/DamageAllAi.java | 3 +- .../java/forge/ai/ability/DamageDealAi.java | 12 +- .../main/java/forge/ai/ability/DrawAi.java | 28 +- .../main/java/forge/ai/ability/EffectAi.java | 4 +- .../java/forge/ai/ability/LifeGainAi.java | 1 - .../java/forge/ai/ability/ManaEffectAi.java | 4 +- .../main/java/forge/ai/ability/PlayAi.java | 8 +- .../main/java/forge/ai/ability/ProtectAi.java | 2 +- .../main/java/forge/ai/ability/PumpAi.java | 2 +- .../java/forge/ai/ability/PumpAiBase.java | 4 +- .../ai/ability/RearrangeTopOfLibraryAi.java | 2 +- .../main/java/forge/ai/ability/ScryAi.java | 13 +- .../main/java/forge/ai/ability/SurveilAi.java | 10 +- .../main/java/forge/ai/ability/TapAiBase.java | 4 +- .../main/java/forge/ai/ability/UntapAi.java | 3 +- .../src/main/java/forge/game/ForgeScript.java | 2 +- forge-game/src/main/java/forge/game/Game.java | 75 +-- .../main/java/forge/game/GameEntityCache.java | 5 + .../main/java/forge/game/GameEntityView.java | 6 + .../java/forge/game/GameEntityViewMap.java | 52 ++ .../java/forge/game/ability/AbilityUtils.java | 2 +- .../src/main/java/forge/game/card/Card.java | 12 +- .../java/forge/game/card/CardFactory.java | 50 -- .../main/java/forge/game/card/CardUtil.java | 2 +- .../main/java/forge/game/player/Player.java | 6 +- .../game/replacement/ReplaceProduceMana.java | 2 +- .../java/forge/game/spellability/Spell.java | 6 +- .../forge/game/spellability/SpellAbility.java | 25 +- .../game/trigger/TriggerTapsForMana.java | 2 +- .../forge/match/input/InputPayManaSimple.java | 161 ------ .../java/forge/match/input/InputProxy.java | 2 +- .../java/forge/player/HumanCostDecision.java | 87 +-- .../src/main/java/forge/player/HumanPlay.java | 127 ++--- .../forge/player/HumanPlaySpellAbility.java | 2 +- .../forge/player/PlayerControllerHuman.java | 494 ++++++++++-------- .../java/forge/player/TargetSelection.java | 29 +- 53 files changed, 596 insertions(+), 753 deletions(-) create mode 100644 forge-game/src/main/java/forge/game/GameEntityViewMap.java delete mode 100644 forge-gui/src/main/java/forge/match/input/InputPayManaSimple.java diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index cbbc8867b77..b8777e5be1c 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -769,7 +769,7 @@ public class AiController { return AiPlayDecision.CantPlayAi; } } - else if (sa.getPayCosts() != null){ + else { Cost payCosts = sa.getPayCosts(); ManaCost mana = payCosts.getTotalMana(); if (mana != null) { @@ -858,7 +858,7 @@ public class AiController { int neededMana = 0; boolean dangerousRecurringCost = false; - Cost costWithBuyback = sa.getPayCosts() != null ? sa.getPayCosts().copy() : Cost.Zero; + Cost costWithBuyback = sa.getPayCosts().copy(); for (OptionalCostValue opt : GameActionUtil.getOptionalCostValues(sa)) { if (opt.getType() == OptionalCost.Buyback) { costWithBuyback.add(opt.getCost()); @@ -907,8 +907,8 @@ public class AiController { public int compare(final SpellAbility a, final SpellAbility b) { // sort from highest cost to lowest // we want the highest costs first - int a1 = a.getPayCosts() == null ? 0 : a.getPayCosts().getTotalMana().getCMC(); - int b1 = b.getPayCosts() == null ? 0 : b.getPayCosts().getTotalMana().getCMC(); + int a1 = a.getPayCosts().getTotalMana().getCMC(); + int b1 = b.getPayCosts().getTotalMana().getCMC(); // deprioritize SAs explicitly marked as preferred to be activated last compared to all other SAs if (a.hasParam("AIActivateLast") && !b.hasParam("AIActivateLast")) { @@ -927,12 +927,12 @@ public class AiController { // deprioritize pump spells with pure energy cost (can be activated last, // since energy is generally scarce, plus can benefit e.g. Electrostatic Pummeler) int a2 = 0, b2 = 0; - if (a.getApi() == ApiType.Pump && a.getPayCosts() != null && a.getPayCosts().getCostEnergy() != null) { + if (a.getApi() == ApiType.Pump && a.getPayCosts().getCostEnergy() != null) { if (a.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) { a2 = a.getPayCosts().getCostEnergy().convertAmount(); } } - if (b.getApi() == ApiType.Pump && b.getPayCosts() != null && b.getPayCosts().getCostEnergy() != null) { + if (b.getApi() == ApiType.Pump && b.getPayCosts().getCostEnergy() != null) { if (b.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) { b2 = b.getPayCosts().getCostEnergy().convertAmount(); } @@ -956,8 +956,7 @@ public class AiController { return 1; } - if (a.getHostCard().equals(b.getHostCard()) && a.getApi() == b.getApi() - && a.getPayCosts() != null && b.getPayCosts() != null) { + if (a.getHostCard().equals(b.getHostCard()) && a.getApi() == b.getApi()) { // Cheaper Spectacle costs should be preferred // FIXME: Any better way to identify that these are the same ability, one with Spectacle and one not? // (looks like it's not a full-fledged alternative cost as such, and is not processed with other alt costs) @@ -1479,7 +1478,7 @@ public class AiController { } for (SpellAbility sa : card.getSpellAbilities()) { - if (sa.getPayCosts() != null && sa.isAbility() + if (sa.isAbility() && sa.getPayCosts().getCostMana() != null && sa.getPayCosts().getCostMana().getMana().getCMC() > 0 && (!sa.getPayCosts().hasTapCost() || !isTapLand) diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index aa6f83699ca..95a967e337c 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -494,7 +494,7 @@ public class AiCostDecision extends CostDecisionMakerBase { @Override public boolean apply(Card card) { for (final SpellAbility sa : card.getSpellAbilities()) { - if (sa.isManaAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { + if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) { return true; } } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java index 2550137674d..e844a474749 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilAbility.java @@ -113,9 +113,7 @@ public class ComputerUtilAbility { List priorityAltSa = Lists.newArrayList(); List otherAltSa = Lists.newArrayList(); for (SpellAbility altSa : saAltCosts) { - if (altSa.getPayCosts() == null || sa.getPayCosts() == null) { - otherAltSa.add(altSa); - } else if (sa.getPayCosts().isOnlyManaCost() + if (sa.getPayCosts().isOnlyManaCost() && altSa.getPayCosts().isOnlyManaCost() && sa.getPayCosts().getTotalMana().compareTo(altSa.getPayCosts().getTotalMana()) == 1) { // the alternative cost is strictly cheaper, so why not? (e.g. Omniscience etc.) priorityAltSa.add(altSa); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 2b57c1b55fb..ca62736912f 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1474,7 +1474,7 @@ public class ComputerUtilCard { if (totalPowerUnblocked >= opp.getLife()) { return true; } else if (totalPowerUnblocked > dmg && sa.getHostCard() != null && sa.getHostCard().isInPlay()) { - if (sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost()) { + if (sa.getPayCosts().hasNoManaCost()) { return true; // always activate abilities which cost no mana and which can increase unblocked damage } } @@ -1785,10 +1785,6 @@ public class ComputerUtilCard { for (Card c : otb) { for (SpellAbility sa : c.getSpellAbilities()) { - if (sa.getPayCosts() == null) { - continue; - } - CostPayEnergy energyCost = sa.getPayCosts().getCostEnergy(); if (energyCost != null) { int amount = energyCost.convertAmount(); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index da25cab609d..0b2ac9d2f5e 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -1028,7 +1028,7 @@ public class ComputerUtilCombat { return power; } for (SpellAbility ability : blocker.getAllSpellAbilities()) { - if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) { + if (!(ability instanceof AbilityActivated)) { continue; } if (ability.hasParam("ActivationPhases") || ability.hasParam("SorcerySpeed") || ability.hasParam("ActivationZone")) { @@ -1203,7 +1203,7 @@ public class ComputerUtilCombat { return toughness; } for (SpellAbility ability : blocker.getAllSpellAbilities()) { - if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) { + if (!(ability instanceof AbilityActivated)) { continue; } @@ -1426,7 +1426,7 @@ public class ComputerUtilCombat { return power; } for (SpellAbility ability : attacker.getAllSpellAbilities()) { - if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) { + if (!(ability instanceof AbilityActivated)) { continue; } if (ability.hasParam("ActivationPhases") || ability.hasParam("SorcerySpeed") || ability.hasParam("ActivationZone")) { @@ -1662,7 +1662,7 @@ public class ComputerUtilCombat { return toughness; } for (SpellAbility ability : attacker.getAllSpellAbilities()) { - if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) { + if (!(ability instanceof AbilityActivated)) { continue; } @@ -2517,7 +2517,7 @@ public class ComputerUtilCombat { final Player controller = combatant.getController(); for (Card c : controller.getCardsIn(ZoneType.Battlefield)) { for (SpellAbility ability : c.getAllSpellAbilities()) { - if (!(ability instanceof AbilityActivated) || ability.getPayCosts() == null) { + if (!(ability instanceof AbilityActivated)) { continue; } if (ability.getApi() != ApiType.Pump) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index c94a5e7e16d..89b6ddf4436 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -529,7 +529,7 @@ public class ComputerUtilCost { public boolean apply(Card card) { boolean hasManaSa = false; for (final SpellAbility sa : card.getSpellAbilities()) { - if (sa.isManaAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { + if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) { hasManaSa = true; break; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 55e72a71141..28e5eaf9835 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -507,16 +507,10 @@ public class ComputerUtilMana { } } else { - if (saPayment.getPayCosts() != null) { - final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment); - if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment))) { - saList.remove(saPayment); - continue; - } - } - else { - System.err.println("Ability " + saPayment + " from " + saPayment.getHostCard() + " had NULL as payCost"); - saPayment.getHostCard().tap(); + final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment); + if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment))) { + saList.remove(saPayment); + continue; } ai.getGame().getStack().addAndUnfreeze(saPayment); @@ -837,10 +831,9 @@ public class ComputerUtilMana { if (checkCosts) { // Check if AI can still play this mana ability ma.setActivatingPlayer(ai); - if (ma.getPayCosts() != null) { // if the AI can't pay the additional costs skip the mana ability - if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) { - return false; - } + // if the AI can't pay the additional costs skip the mana ability + if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) { + return false; } else if (sourceCard.isTapped()) { return false; @@ -1144,7 +1137,7 @@ public class ComputerUtilMana { ManaCostBeingPaid cost = new ManaCostBeingPaid(mana, restriction); // Tack xMana Payments into mana here if X is a set value - if (sa.getPayCosts() != null && (cost.getXcounter() > 0 || extraMana > 0)) { + if (cost.getXcounter() > 0 || extraMana > 0) { int manaToAdd = 0; if (test && extraMana > 0) { final int multiplicator = Math.max(cost.getXcounter(), 1); @@ -1218,7 +1211,7 @@ public class ComputerUtilMana { for (SpellAbility ma : src.getManaAbilities()) { ma.setActivatingPlayer(p); if (!checkPlayable || ma.canPlay()) { - int costsToActivate = ma.getPayCosts() != null && ma.getPayCosts().getCostMana() != null ? ma.getPayCosts().getCostMana().convertAmount() : 0; + int costsToActivate = ma.getPayCosts().getCostMana() != null ? ma.getPayCosts().getCostMana().convertAmount() : 0; int producedMana = ma.getParamOrDefault("Produced", "").split(" ").length; int producedAmount = AbilityUtils.calculateAmount(src, ma.getParamOrDefault("Amount", "1"), ma); @@ -1594,7 +1587,7 @@ public class ComputerUtilMana { } public static int determineMaxAffordableX(Player ai, SpellAbility sa) { - if (sa.getPayCosts() == null || sa.getPayCosts().getCostMana() == null) { + if (sa.getPayCosts().getCostMana() == null) { return -1; } diff --git a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java index 3b7a9c7890a..de339fad5c4 100644 --- a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java @@ -242,7 +242,7 @@ public class CreatureEvaluator implements Function { && "+X".equals(sa.getParam("NumDef")) && !sa.usesTargeting() && (!sa.hasParam("Defined") || "Self".equals(sa.getParam("Defined")))) { - if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) { + if (sa.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) { // Electrostatic Pummeler, can be expanded for similar cards int initPower = getEffectivePower(sa.getHostCard()); int pumpedPower = initPower; diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 4735bc78dc6..9c5d555a2e2 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1216,7 +1216,7 @@ public class PlayerControllerAi extends PlayerController { public List chooseOptionalCosts(SpellAbility chosen, List optionalCostValues) { List chosenOptCosts = Lists.newArrayList(); - Cost costSoFar = chosen.getPayCosts() != null ? chosen.getPayCosts().copy() : Cost.Zero; + Cost costSoFar = chosen.getPayCosts().copy(); for (OptionalCostValue opt : optionalCostValues) { // Choose the optional cost if it can be paid (to be improved later, check for playability and other conditions perhaps) @@ -1255,7 +1255,7 @@ public class PlayerControllerAi extends PlayerController { // TODO: improve the logic depending on the keyword and the playability of the cost-modified SA (enough targets present etc.) int chosenAmount = 0; - Cost costSoFar = sa.getPayCosts() != null ? sa.getPayCosts().copy() : Cost.Zero; + Cost costSoFar = sa.getPayCosts().copy(); for (int i = 0; i < max; i++) { costSoFar.add(cost); diff --git a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java index a4ef2d5bcee..61d161caace 100644 --- a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java @@ -267,7 +267,7 @@ public abstract class SpellAbilityAi { // TODO probably also consider if winter orb or similar are out - if (sa.getPayCosts() == null || sa instanceof AbilitySub) { + if (sa instanceof AbilitySub) { return true; // This is only true for Drawbacks and triggers } diff --git a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java index baa0d00b7c5..b33948b293b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -247,7 +247,6 @@ public class AnimateAi extends SpellAbilityAi { final Player ai = sa.getActivatingPlayer(); final PhaseHandler ph = ai.getGame().getPhaseHandler(); final boolean alwaysActivatePWAbility = sa.hasParam("Planeswalker") - && sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostPutCounter.class) && sa.getTargetRestrictions() != null && sa.getTargetRestrictions().getMinTargets(sa.getHostCard(), sa) == 0; diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index 23648c94401..1bc5028175f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -398,7 +398,7 @@ public class AttachAi extends SpellAbilityAi { if (!c.isCreature() && !c.getType().hasSubtype("Vehicle") && !c.isTapped()) { // try to identify if this thing can actually tap for (SpellAbility ab : c.getAllSpellAbilities()) { - if (ab.getPayCosts() != null && ab.getPayCosts().hasTapCost()) { + if (ab.getPayCosts().hasTapCost()) { return true; } } @@ -560,7 +560,7 @@ public class AttachAi extends SpellAbilityAi { @Override public boolean apply(final Card c) { for (final SpellAbility sa : c.getSpellAbilities()) { - if (sa.isAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { + if (sa.isAbility() && sa.getPayCosts().hasTapCost()) { return false; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java index 40c59393086..160066bea92 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -1065,8 +1065,7 @@ public class ChangeZoneAi extends SpellAbilityAi { if (destination.equals(ZoneType.Exile) || origin.contains(ZoneType.Battlefield)) { // don't rush bouncing stuff when not going to attack - if (!immediately && sa.getPayCosts() != null - && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) + if (!immediately && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) && game.getPhaseHandler().isPlayerTurn(ai) && ai.getCreaturesInPlay().isEmpty()) { return false; @@ -1103,8 +1102,8 @@ public class ChangeZoneAi extends SpellAbilityAi { } } - boolean doWithoutTarget = sa.hasParam("Planeswalker") && sa.getTargetRestrictions() != null - && sa.getTargetRestrictions().getMinTargets(source, sa) == 0 && sa.getPayCosts() != null + boolean doWithoutTarget = sa.hasParam("Planeswalker") && sa.usesTargeting() + && sa.getTargetRestrictions().getMinTargets(source, sa) == 0 && sa.getPayCosts().hasSpecificCostType(CostPutCounter.class); if (list.isEmpty() && !doWithoutTarget) { @@ -1790,6 +1789,7 @@ public class ChangeZoneAi extends SpellAbilityAi { } public boolean doReturnCommanderLogic(SpellAbility sa, Player aiPlayer) { + @SuppressWarnings("unchecked") Map originalParams = (Map)sa.getReplacingObject(AbilityKey.OriginalParams); SpellAbility causeSa = (SpellAbility)originalParams.get(AbilityKey.Cause); SpellAbility causeSub = null; diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java index 1beefd77b09..e16e748ed8a 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java @@ -233,7 +233,7 @@ public class ChooseCardAi extends SpellAbilityAi { return false; } for (SpellAbility sa : c.getAllSpellAbilities()) { - if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { + if (sa.getPayCosts().hasTapCost()) { return false; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java b/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java index d130c18a666..25a05760964 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java @@ -29,8 +29,8 @@ public class CopySpellAbilityAi extends SpellAbilityAi { final SpellAbility top = game.getStack().peekAbility(); if (top != null - && top.getPayCosts() != null && top.getPayCosts().getCostMana() != null - && sa.getPayCosts() != null && sa.getPayCosts().getCostMana() != null + && top.getPayCosts().getCostMana() != null + && sa.getPayCosts().getCostMana() != null && top.getPayCosts().getCostMana().getMana().getCMC() >= sa.getPayCosts().getCostMana().getMana().getCMC() + diff) { // The copied spell has a significantly higher CMC than the copy spell, consider copying chance = 100; diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java index b39f4468b27..70f568a6954 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java @@ -454,7 +454,6 @@ public class CountersPutAi extends SpellAbilityAi { // but try to do it in Main 2 then so that the AI has a chance to play creatures first. if (list.isEmpty() && sa.hasParam("Planeswalker") - && sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostPutCounter.class) && sa.isTargetNumberValid() && sa.getTargets().getNumTargeted() == 0 @@ -706,7 +705,7 @@ public class CountersPutAi extends SpellAbilityAi { SpellAbility testSa = sa; int countX = 0; int nonXGlyphs = 0; - while (testSa != null && testSa.getPayCosts() != null && countX == 0) { + while (testSa != null && countX == 0) { countX = testSa.getPayCosts().getTotalMana().countX(); nonXGlyphs = testSa.getPayCosts().getTotalMana().getGlyphCount() - countX; testSa = testSa.getSubAbility(); diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutAllAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutAllAi.java index 6142b9c71ff..8c883637620 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAllAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAllAi.java @@ -120,7 +120,7 @@ public class CountersPutAllAi extends SpellAbilityAi { //Check for cards that could profit from the ability PhaseHandler phase = ai.getGame().getPhaseHandler(); if (type.equals("P1P1") && sa.isAbility() && source.isCreature() - && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost() + && sa.getPayCosts().hasTapCost() && sa instanceof AbilitySub && (!phase.getNextTurn().equals(ai) || phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS))) { diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java index cbede1c1a11..e36a2a236dc 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java @@ -53,7 +53,6 @@ public class DamageAllAi extends SpellAbilityAi { x = source.getCounters(CounterType.LOYALTY); } if (x == -1) { - Player bestOpp = determineOppToKill(ai, sa, source, dmg); if (determineOppToKill(ai, sa, source, dmg) != null) { // we already know we can kill a player, so go for it return true; @@ -138,7 +137,7 @@ public class DamageAllAi extends SpellAbilityAi { } int minGain = 200; // The minimum gain in destroyed creatures - if (sa.getPayCosts() != null && sa.getPayCosts().isReusuableResource()) { + if (sa.getPayCosts().isReusuableResource()) { if (computerList.isEmpty()) { minGain = 10; // nothing to lose // no creatures to lose and player can be damaged diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java index 797c18d441f..1a5e908a7a4 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java @@ -285,7 +285,7 @@ public class DamageDealAi extends DamageAiBase { } } - if ("XCountersDamage".equals(logic) && sa.getPayCosts() != null) { + if ("XCountersDamage".equals(logic)) { // Check to ensure that we have enough counters to remove per the defined PayX for (CostPart part : sa.getPayCosts().getCostParts()) { if (part instanceof CostRemoveCounter) { @@ -449,7 +449,7 @@ public class DamageDealAi extends DamageAiBase { int pwScore = curLoyalty * 10; for (SpellAbility sa : pw.getSpellAbilities()) { - if (sa.hasParam("Ultimate") && sa.getPayCosts() != null) { + if (sa.hasParam("Ultimate")) { Integer loyaltyCost = 0; CostRemoveCounter remLoyalty = sa.getPayCosts().getCostPartByType(CostRemoveCounter.class); if (remLoyalty != null) { @@ -794,8 +794,7 @@ public class DamageDealAi extends DamageAiBase { if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai)) || (SpellAbilityAi.isSorcerySpeed(sa) && phase.is(PhaseType.MAIN2)) || ("PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai)) - || sa.getPayCosts() == null || immediately - || this.shouldTgtP(ai, sa, dmg, noPrevention)) && + || immediately || shouldTgtP(ai, sa, dmg, noPrevention)) && (!avoidTargetP(ai, sa))) { tcs.add(enemy); if (divided) { @@ -1126,8 +1125,7 @@ public class DamageDealAi extends DamageAiBase { continue; } // currently works only with cards that don't have additional costs (only mana is supported) - if (ab.getPayCosts() != null - && (ab.getPayCosts().hasNoManaCost() || ab.getPayCosts().hasOnlySpecificCostType(CostPartMana.class))) { + if (ab.getPayCosts().hasNoManaCost() || ab.getPayCosts().hasOnlySpecificCostType(CostPartMana.class)) { String dmgDef = "0"; if (ab.getApi() == ApiType.DealDamage) { dmgDef = ab.getParamOrDefault("NumDmg", "0"); @@ -1151,7 +1149,7 @@ public class DamageDealAi extends DamageAiBase { } // FIXME: should it also check restrictions for targeting players? - ManaCost costSa = sa.getPayCosts() != null ? sa.getPayCosts().getTotalMana() : ManaCost.NO_COST; + ManaCost costSa = sa.getPayCosts().getTotalMana(); ManaCost costAb = ab.getPayCosts().getTotalMana(); // checked for null above ManaCost total = ManaCost.combine(costSa, costAb); SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false)); diff --git a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java index 59438a2fb52..6fbe29ccdd1 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java @@ -262,22 +262,20 @@ public class DrawAi extends SpellAbilityAi { // Draw up to max hand size but leave at least 3 in library numCards = Math.min(computerMaxHandSize - computerHandSize, computerLibrarySize - 3); - if (sa.getPayCosts() != null) { - if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) { - // [Necrologia, Pay X Life : Draw X Cards] - // Don't draw more than what's "safe" and don't risk a near death experience - // Maybe would be better to check for "serious danger" and take more risk? - while ((ComputerUtil.aiLifeInDanger(ai, false, numCards) && (numCards > 0))) { - numCards--; - } - } else if (sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) { - // [e.g. Krav, the Unredeemed and other cases which say "Sacrifice X creatures: draw X cards] - // TODO: Add special logic to limit/otherwise modify the ChosenX value here + if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) { + // [Necrologia, Pay X Life : Draw X Cards] + // Don't draw more than what's "safe" and don't risk a near death experience + // Maybe would be better to check for "serious danger" and take more risk? + while ((ComputerUtil.aiLifeInDanger(ai, false, numCards) && (numCards > 0))) { + numCards--; + } + } else if (sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) { + // [e.g. Krav, the Unredeemed and other cases which say "Sacrifice X creatures: draw X cards] + // TODO: Add special logic to limit/otherwise modify the ChosenX value here - // Skip this ability if nothing is to be chosen for sacrifice - if (numCards <= 0) { - return false; - } + // Skip this ability if nothing is to be chosen for sacrifice + if (numCards <= 0) { + return false; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/EffectAi.java b/forge-ai/src/main/java/forge/ai/ability/EffectAi.java index 609aec7c92d..77a20350e69 100644 --- a/forge-ai/src/main/java/forge/ai/ability/EffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/EffectAi.java @@ -113,7 +113,7 @@ public class EffectAi extends SpellAbilityAi { } else if (logic.equals("SpellCopy")) { // fetch Instant or Sorcery and AI has reason to play this turn // does not try to get itself - final ManaCost costSa = sa.getPayCosts() != null ? sa.getPayCosts().getTotalMana() : ManaCost.NO_COST; + final ManaCost costSa = sa.getPayCosts().getTotalMana(); final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate() { @Override public boolean apply(final Card c) { @@ -135,7 +135,7 @@ public class EffectAi extends SpellAbilityAi { AiPlayDecision decision = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(ab); // see if we can pay both for this spell and for the Effect spell we're considering if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) { - ManaCost costAb = ab.getPayCosts() != null ? ab.getPayCosts().getTotalMana() : ManaCost.NO_COST; + ManaCost costAb = ab.getPayCosts().getTotalMana(); ManaCost total = ManaCost.combine(costSa, costAb); SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false)); // can we pay both costs? diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java index 41751f85882..5ad2c283477 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java @@ -88,7 +88,6 @@ public class LifeGainAi extends SpellAbilityAi { if (lifeCritical && sa.isAbility() && sa.getHostCard() != null && sa.getHostCard().isCreature() - && sa.getPayCosts() != null && (sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class) || sa.getPayCosts().hasSpecificCostType(CostSacrifice.class))) { if (!game.getStack().isEmpty()) { SpellAbility saTop = game.getStack().peekAbility(); diff --git a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java index 60eaeef180f..6158b777e9b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java @@ -81,7 +81,7 @@ public class ManaEffectAi extends SpellAbilityAi { return true; // handled elsewhere, does not meet the standard requirements } - return sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource() + return sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource() && sa.getSubAbility() == null && ComputerUtil.playImmediately(ai, sa); // return super.checkApiLogic(ai, sa); } @@ -119,7 +119,7 @@ public class ManaEffectAi extends SpellAbilityAi { int numCounters = 0; int manaSurplus = 0; if ("XChoice".equals(host.getSVar("X")) - && sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) { + && sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) { CounterType ctrType = CounterType.KI; // Petalmane Baku for (CostPart part : sa.getPayCosts().getCostParts()) { if (part instanceof CostRemoveCounter) { diff --git a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java index 96c22f1d519..3d5922a9122 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java @@ -84,8 +84,8 @@ public class PlayAi extends SpellAbilityAi { return ComputerUtil.targetPlayableSpellCard(ai, cards, sa, sa.hasParam("WithoutManaCost")); } else if (logic.startsWith("NeedsChosenCard")) { int minCMC = 0; - if (sa.getPayCosts() != null && sa.getPayCosts().getCostMana() != null) { - minCMC = sa.getPayCosts().getCostMana().getMana().getCMC(); + if (sa.getPayCosts().getCostMana() != null) { + minCMC = sa.getPayCosts().getTotalMana().getCMC(); } validOpts = CardLists.filter(validOpts, CardPredicates.greaterCMC(minCMC)); return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null) != null; @@ -156,9 +156,7 @@ public class PlayAi extends SpellAbilityAi { if (sa.hasParam("WithoutManaCost")) { // Try to avoid casting instants and sorceries with X in their cost, since X will be assumed to be 0. if (!(spell instanceof SpellPermanent)) { - if (spell.getPayCosts() != null - && spell.getPayCosts().getCostMana() != null - && spell.getPayCosts().getCostMana().getMana().countX() > 0) { + if (spell.getPayCosts().getTotalMana().countX() > 0) { continue; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java b/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java index 310fa6e32af..79e0f5bcbc7 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java @@ -202,7 +202,7 @@ public class ProtectAi extends SpellAbilityAi { if (game.getStack().isEmpty()) { // If the cost is tapping, don't activate before declare // attack/block - if ((sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) { + if (sa.getPayCosts().hasTapCost()) { if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && game.getPhaseHandler().isPlayerTurn(ai)) { list.remove(sa.getHostCard()); diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java index ce161caee80..af93f8a1b72 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java @@ -515,7 +515,7 @@ public class PumpAi extends PumpAiBase { if (game.getStack().isEmpty()) { // If the cost is tapping, don't activate before declare // attack/block - if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { + if (sa.getPayCosts().hasTapCost()) { if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && game.getPhaseHandler().isPlayerTurn(ai)) { list.remove(sa.getHostCard()); diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java index eb2ab18e1d1..aba764dbe89 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java @@ -94,7 +94,7 @@ public abstract class PumpAiBase extends SpellAbilityAi { List attackers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate() { @Override public boolean apply(final Card c) { - if (c.equals(sa.getHostCard()) && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost() + if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost() && (combat == null || !combat.isAttacking(c))) { return false; } @@ -112,7 +112,7 @@ public abstract class PumpAiBase extends SpellAbilityAi { List attackers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate() { @Override public boolean apply(final Card c) { - if (c.equals(sa.getHostCard()) && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost() + if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost() && (combat == null || !combat.isAttacking(c))) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java b/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java index e8092eccfa2..4de6ff75187 100644 --- a/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java @@ -26,7 +26,7 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi { final PhaseHandler ph = aiPlayer.getGame().getPhaseHandler(); final Card source = sa.getHostCard(); - if (source.isPermanent() && sa.getRestrictions().isInstantSpeed() && sa.getPayCosts() != null + if (source.isPermanent() && sa.getRestrictions().isInstantSpeed() && (sa.getPayCosts().hasTapCost() || sa.getPayCosts().hasManaCost())) { // If it has an associated cost, try to only do this before own turn if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java index cf01c1d8026..1733c7eaee7 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java @@ -46,12 +46,10 @@ public class ScryAi extends SpellAbilityAi { // and right before the beginning of AI's turn, if possible, to avoid mana locking the AI and also to // try to scry right before drawing a card. Also, avoid tapping creatures in the AI's turn, if possible, // even if there's no mana cost. - if (sa.getPayCosts() != null) { - if (sa.getPayCosts().hasTapCost() - && (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature())) - && !SpellAbilityAi.isSorcerySpeed(sa)) { - return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN); - } + if (sa.getPayCosts().hasTapCost() + && (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature())) + && !SpellAbilityAi.isSorcerySpeed(sa)) { + return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN); } // AI logic to scry in Main 1 if there is no better option, otherwise scry at opponent's EOT @@ -76,8 +74,7 @@ public class ScryAi extends SpellAbilityAi { boolean hasSomethingElse = false; for (Card c : CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.not(CardPredicates.Presets.LANDS))) { for (SpellAbility ab : c.getAllSpellAbilities()) { - if (ab.getPayCosts() != null - && ab.getPayCosts().hasManaCost() + if (ab.getPayCosts().hasManaCost() && ComputerUtilMana.hasEnoughManaSourcesToCast(ab, ai)) { // TODO: currently looks for non-Scry cards, can most certainly be made smarter. if (ab.getApi() != ApiType.Scry) { diff --git a/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java b/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java index eb2beab480b..fb5e6efa0f7 100644 --- a/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java @@ -47,12 +47,10 @@ public class SurveilAi extends SpellAbilityAi { // and right before the beginning of AI's turn, if possible, to avoid mana locking the AI and also to // try to scry right before drawing a card. Also, avoid tapping creatures in the AI's turn, if possible, // even if there's no mana cost. - if (sa.getPayCosts() != null) { - if (sa.getPayCosts().hasTapCost() - && (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature())) - && !SpellAbilityAi.isSorcerySpeed(sa)) { - return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN); - } + if (sa.getPayCosts().hasTapCost() + && (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature())) + && !SpellAbilityAi.isSorcerySpeed(sa)) { + return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN); } // in the player's turn Surveil should only be done in Main1 or in Upkeep if able diff --git a/forge-ai/src/main/java/forge/ai/ability/TapAiBase.java b/forge-ai/src/main/java/forge/ai/ability/TapAiBase.java index 6a408821cbe..34fbe8f5c8b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TapAiBase.java +++ b/forge-ai/src/main/java/forge/ai/ability/TapAiBase.java @@ -126,7 +126,7 @@ public abstract class TapAiBase extends SpellAbilityAi { } for (final SpellAbility sa : c.getSpellAbilities()) { - if (sa.isAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { + if (sa.isAbility() && sa.getPayCosts().hasTapCost()) { return true; } } @@ -147,7 +147,7 @@ public abstract class TapAiBase extends SpellAbilityAi { } for (final SpellAbility sa : c.getSpellAbilities()) { - if (sa.isAbility() && sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { + if (sa.isAbility() && sa.getPayCosts().hasTapCost()) { return true; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/UntapAi.java b/forge-ai/src/main/java/forge/ai/ability/UntapAi.java index 5e2659fdacc..16170b99919 100644 --- a/forge-ai/src/main/java/forge/ai/ability/UntapAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/UntapAi.java @@ -153,12 +153,11 @@ public class UntapAi extends SpellAbilityAi { // Try to avoid potential infinite recursion, // e.g. Kiora's Follower untapping another Kiora's Follower and repeating infinitely - if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostTap.class)) { + if (sa.getPayCosts().hasOnlySpecificCostType(CostTap.class)) { CardCollection toRemove = new CardCollection(); for (Card c : untapList) { for (SpellAbility ab : c.getAllSpellAbilities()) { if (ab.getApi() == ApiType.Untap - && ab.getPayCosts() != null && ab.getPayCosts().hasOnlySpecificCostType(CostTap.class) && ab.canTarget(source)) { toRemove.add(c); diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index 86e376aa245..057e55f203e 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -81,7 +81,7 @@ public class ForgeScript { return !cardState.getTypeWithChanges().hasSubtype(subType); } else if (property.equals("hasActivatedAbilityWithTapCost")) { for (final SpellAbility sa : cardState.getSpellAbilities()) { - if (sa.isAbility() && (sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) { + if (sa.isAbility() && sa.getPayCosts().hasTapCost()) { return true; } } diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index fd4f935ca3c..1b2884070d5 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -6,12 +6,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -79,7 +79,7 @@ public class Game { private final GameLog gameLog = new GameLog(); private final Zone stackZone = new Zone(ZoneType.Stack, this); - + private CardCollection lastStateBattlefield = new CardCollection(); private CardCollection lastStateGraveyard = new CardCollection(); @@ -97,7 +97,7 @@ public class Game { private GameStage age = GameStage.BeforeMulligan; private GameOutcome outcome; - private final GameView view; + private final GameView view; private final Tracker tracker = new Tracker(); public Player getMonarch() { @@ -177,19 +177,6 @@ public class Game { playerCache.put(Integer.valueOf(id), player); } - private final GameEntityCache cardCache = new GameEntityCache<>(); - public Card getCard(CardView cardView) { - return cardCache.get(cardView); - } - public void addCard(int id, Card card) { - cardCache.put(Integer.valueOf(id), card); - } - public CardCollection getCardList(Iterable cardViews) { - CardCollection list = new CardCollection(); - cardCache.addToList(cardViews, list); - return list; - } - // methods that deal with saving, retrieving and clearing LKI information about cards on zone change private final HashMap changeZoneLKIInfo = new HashMap<>(); public final void addChangeZoneLKIInfo(Card c) { @@ -385,7 +372,7 @@ public class Game { } }); } - + /** * The Direction in which the turn order of this Game currently proceeds. */ @@ -537,6 +524,33 @@ public class Game { return visit.getFound(notFound); } + private static class CardIdVisitor extends Visitor { + Card found = null; + int id; + + private CardIdVisitor(final int id) { + this.id = id; + } + + @Override + public boolean visit(Card object) { + if (this.id == object.getId()) { + found = object; + } + return found == null; + } + + public Card getFound() { + return found; + } + } + + public Card findById(int id) { + CardIdVisitor visit = new CardIdVisitor(id); + this.forEachCardInGame(visit); + return visit.getFound(); + } + // Allows visiting cards in game without allocating a temporary list. public void forEachCardInGame(Visitor visitor) { for (final Player player : getPlayers()) { @@ -771,11 +785,11 @@ public class Game { public Multimap chooseCardsForAnte(final boolean matchRarity) { Multimap anteed = ArrayListMultimap.create(); - + if (matchRarity) { - + boolean onePlayerHasTimeShifted = false; - + List validRarities = new ArrayList<>(Arrays.asList(CardRarity.values())); for (final Player player : getPlayers()) { final Set playerRarity = getValidRarities(player.getCardsIn(ZoneType.Library)); @@ -791,24 +805,24 @@ public class Game { } return anteed; } - + //If possible, don't ante basic lands if (validRarities.size() > 1) { validRarities.remove(CardRarity.BasicLand); } - + if (validRarities.contains(CardRarity.Special)) { onePlayerHasTimeShifted = false; } - + CardRarity anteRarity = validRarities.get(MyRandom.getRandom().nextInt(validRarities.size())); - + System.out.println("Rarity chosen for ante: " + anteRarity.name()); - + for (final Player player : getPlayers()) { CardCollection library = new CardCollection(player.getCardsIn(ZoneType.Library)); CardCollection toRemove = new CardCollection(); - + //Remove all cards that aren't of the chosen rarity for (Card card : library) { if (onePlayerHasTimeShifted && card.getRarity() == CardRarity.Special) { @@ -827,16 +841,16 @@ public class Game { } } } - + library.removeAll(toRemove); - + if (library.size() > 0) { //Make sure that matches were found. If not, use the original method to choose antes Card ante = library.get(MyRandom.getRandom().nextInt(library.size())); anteed.put(player, ante); } else { chooseRandomCardsForAnte(player, anteed); } - + } } else { @@ -874,7 +888,6 @@ public class Game { } public void clearCaches() { - cardCache.clear(); lastStateBattlefield.clear(); lastStateGraveyard.clear(); diff --git a/forge-game/src/main/java/forge/game/GameEntityCache.java b/forge-game/src/main/java/forge/game/GameEntityCache.java index b5532f2bf96..de4716be035 100644 --- a/forge-game/src/main/java/forge/game/GameEntityCache.java +++ b/forge-game/src/main/java/forge/game/GameEntityCache.java @@ -13,6 +13,11 @@ public class GameEntityCache entities) { + for (Entity e : entities) { + put(e.getId(), e); + } + } public void remove(Integer id) { entityCache.remove(id); diff --git a/forge-game/src/main/java/forge/game/GameEntityView.java b/forge-game/src/main/java/forge/game/GameEntityView.java index 0349b4c8e54..5684e41d424 100644 --- a/forge-game/src/main/java/forge/game/GameEntityView.java +++ b/forge-game/src/main/java/forge/game/GameEntityView.java @@ -24,6 +24,12 @@ public abstract class GameEntityView extends TrackableObject { return collection; } + public static GameEntityViewMap getMap(Iterable spabs) { + GameEntityViewMap gameViewCache = new GameEntityViewMap(); + gameViewCache.putAll(spabs); + return gameViewCache; + } + protected GameEntityView(final int id0, final Tracker tracker) { super(id0, tracker); } diff --git a/forge-game/src/main/java/forge/game/GameEntityViewMap.java b/forge-game/src/main/java/forge/game/GameEntityViewMap.java new file mode 100644 index 00000000000..e30161405bc --- /dev/null +++ b/forge-game/src/main/java/forge/game/GameEntityViewMap.java @@ -0,0 +1,52 @@ +package forge.game; + +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ForwardingMap; +import com.google.common.collect.Maps; + +import forge.trackable.TrackableCollection; + +public class GameEntityViewMap extends ForwardingMap { + private Map dataMap = Maps.newLinkedHashMap(); + + @Override + protected Map delegate() { + return dataMap; + } + + @SuppressWarnings("unchecked") + public void put(Entity e) { + this.put((View) e.getView(), e); + } + + public void putAll(Iterable entities) { + for (Entity e : entities) { + put(e); + } + } + + public void remove(Entity e) { + this.remove(e.getView()); + } + + public void removeAll(Iterable entities) { + for (Entity e : entities) { + remove(e); + } + } + + public void addToList(Iterable views, List list) { + for (View view : views) { + Entity entity = get(view); + if (entity != null) { + list.add(entity); + } + } + } + + public TrackableCollection getTrackableKeys() { + return new TrackableCollection(this.keySet()); + } +} diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 279508dd5e6..5005ecb07b2 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1344,7 +1344,7 @@ public class AbilityUtils { Player pl = sa.getActivatingPlayer(); final Game game = pl.getGame(); - if (sa.isTrigger() && sa.getParent() == null && sa.getPayCosts() != null) { + if (sa.isTrigger() && sa.getParent() == null) { // when trigger cost are paid before the effect does resolve, need to clean the trigger game.getTriggerHandler().resetActiveTriggers(); } 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 1f6261c1f6f..b8c474a2d53 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -295,7 +295,7 @@ public class Card extends GameEntity implements Comparable { * @param id0 the unique id of the new card. */ public Card(final int id0, final Game game0) { - this(id0, null, true, game0); + this(id0, null, game0); } /** @@ -307,15 +307,9 @@ public class Card extends GameEntity implements Comparable { * @see IPaperCard */ public Card(final int id0, final IPaperCard paperCard0, final Game game0) { - this(id0, paperCard0, true, game0); - } - public Card(final int id0, final IPaperCard paperCard0, final boolean allowCache, final Game game0) { super(id0); game = game0; - if (id0 >= 0 && allowCache && game != null) { - game.addCard(id0, this); - } paperCard = paperCard0; view = new CardView(id0, game == null ? null : game.getTracker()); currentState = new CardState(view.getCurrentState(), this); @@ -2083,9 +2077,7 @@ public class Card extends GameEntity implements Comparable { if (sa.isAdventure() && state.getView().getState().equals(CardStateName.Original)) { StringBuilder sbSA = new StringBuilder(); sbSA.append("Adventure — ").append(getState(CardStateName.Adventure).getName()); - if (sa.getPayCosts() != null) { - sbSA.append(" ").append(sa.getPayCosts().toSimpleString()); - } + sbSA.append(" ").append(sa.getPayCosts().toSimpleString()); sbSA.append(": "); sbSA.append(sAbility); sAbility = sbSA.toString(); diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 9bd7cf1b9ce..8f47c474362 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -118,39 +118,6 @@ public class CardFactory { } - /** - *

- * copyCardWithChangedStats - *

- * - * This method copies the card together with certain temporarily changed stats of the card - * (namely, changed color, changed types, changed keywords). - * - * copyCardWithChangedStats must NOT be used for ordinary card copy operations because - * according to MTG rules the changed text (including keywords, types) is not copied over - * to cards cloned by another card. However, this method is useful, for example, for certain - * triggers that demand the latest information about the changes to the card which is lost - * when the card changes its zone after GameAction::changeZone is called. - * - * @param in - * a {@link forge.game.card.Card} object. - * @param assignNewId - * a boolean - * @return a {@link forge.game.card.Card} object. - */ - public static final Card copyCardWithChangedStats(final Card in, boolean assignNewId) { - Card out = copyCard(in, assignNewId); - - // Copy changed color, type, keyword arrays (useful for some triggers that require - // information about the latest state of the card as it left the battlefield) - out.setChangedCardColors(in.getChangedCardColors()); - out.setChangedCardKeywords(in.getChangedCardKeywords()); - out.setChangedCardTypes(in.getChangedCardTypesMap()); - out.setChangedCardNames(in.getChangedCardNames()); - - return out; - } - /** *

* copySpellHost. @@ -471,23 +438,6 @@ public class CardFactory { CardFactoryUtil.addAbilityFactoryAbilities(c, face.getAbilities()); } - /** - * Create a copy of a card, including its copiable characteristics (but not - * abilities). - * @param from - * @param newOwner - * @return - */ - public static Card copyCopiableCharacteristics(final Card from, final Player newOwner) { - int id = newOwner == null ? 0 : newOwner.getGame().nextCardId(); - final Card c = new Card(id, from.getPaperCard(), from.getGame()); - c.setOwner(newOwner); - c.setSetCode(from.getSetCode()); - - copyCopiableCharacteristics(from, c); - return c; - } - /** * Copy the copiable characteristics of one card to another, taking the * states of both cards into account. diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index 931909dafbd..a0af629a150 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -206,7 +206,7 @@ public final class CardUtil { .build() ); - final Card newCopy = new Card(in.getId(), in.getPaperCard(), false, in.getGame()); + final Card newCopy = new Card(in.getId(), in.getPaperCard(), in.getGame()); newCopy.setSetCode(in.getSetCode()); newCopy.setOwner(in.getOwner()); newCopy.setController(in.getController(), 0); diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 36bcb6d24ee..5082c6e7b63 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -3067,7 +3067,7 @@ public class Player extends GameEntity implements Comparable { public void createMonarchEffect() { final PlayerZone com = getZone(ZoneType.Command); if (monarchEffect == null) { - monarchEffect = new Card(game.nextCardId(), null, false, game); + monarchEffect = new Card(game.nextCardId(), null, game); monarchEffect.setOwner(this); monarchEffect.setImageKey("t:monarch"); monarchEffect.setName("The Monarch"); @@ -3163,7 +3163,7 @@ public class Player extends GameEntity implements Comparable { final PlayerZone com = getZone(ZoneType.Command); if(bless) { - blessingEffect = new Card(game.nextCardId(), null, false, game); + blessingEffect = new Card(game.nextCardId(), null, game); blessingEffect.setOwner(this); blessingEffect.setImageKey("t:blessing"); blessingEffect.setName("City's Blessing"); @@ -3257,7 +3257,7 @@ public class Player extends GameEntity implements Comparable { final PlayerZone com = getZone(ZoneType.Command); - keywordEffect = new Card(game.nextCardId(), null, false, game); + keywordEffect = new Card(game.nextCardId(), null, game); keywordEffect.setImmutable(true); keywordEffect.setOwner(this); keywordEffect.setName("Keyword Effects"); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplaceProduceMana.java b/forge-game/src/main/java/forge/game/replacement/ReplaceProduceMana.java index 13e8b68f782..5eb37bf753c 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplaceProduceMana.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplaceProduceMana.java @@ -34,7 +34,7 @@ public class ReplaceProduceMana extends ReplacementEffect { //Check for tapping if (!hasParam("NoTapCheck")) { final SpellAbility manaAbility = (SpellAbility) runParams.get(AbilityKey.AbilityMana); - if (manaAbility == null || manaAbility.getRootAbility().getPayCosts() == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) { + if (manaAbility == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) { return false; } } diff --git a/forge-game/src/main/java/forge/game/spellability/Spell.java b/forge-game/src/main/java/forge/game/spellability/Spell.java index 2978d406f34..7901469e500 100644 --- a/forge-game/src/main/java/forge/game/spellability/Spell.java +++ b/forge-game/src/main/java/forge/game/spellability/Spell.java @@ -145,10 +145,8 @@ public abstract class Spell extends SpellAbility implements java.io.Serializable return false; } - if (this.getPayCosts() != null) { - if (!CostPayment.canPayAdditionalCosts(this.getPayCosts(), this)) { - return false; - } + if (!CostPayment.canPayAdditionalCosts(this.getPayCosts(), this)) { + return false; } return checkOtherRestrictions(card); 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 255451962a7..d98d29df52b 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -442,16 +442,10 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } public boolean costHasX() { - if (getPayCosts() == null) { - return false; - } return getPayCosts().hasXInAnyCostPart(); } public boolean costHasManaX() { - if (getPayCosts() == null) { - return false; - } if (getPayCosts().hasNoManaCost()) { return false; } @@ -890,9 +884,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit clone.triggeringObjects = AbilityKey.newMap(this.triggeringObjects); - if (getPayCosts() != null) { - clone.setPayCosts(getPayCosts().copy()); - } + clone.setPayCosts(getPayCosts().copy()); if (manaPart != null) { clone.manaPart = new AbilityManaPart(host, mapParams); } @@ -1422,16 +1414,15 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit int maxTargets = getTargetRestrictions().getMaxTargets(hostCard, this); int numTargets = getTargets().getNumTargeted(); - if (maxTargets == 0 && this.getPayCosts() != null - && this.getPayCosts().hasSpecificCostType(CostRemoveCounter.class) - && this.hasSVar(this.getParam("TargetMax")) - && this.getSVar(this.getParam("TargetMax")).startsWith("Count$CardCounters") - && this.getHostCard() != null && this.getHostCard().hasSVar("CostCountersRemoved")) { + if (maxTargets == 0 && getPayCosts().hasSpecificCostType(CostRemoveCounter.class) + && hasSVar(getParam("TargetMax")) + && getSVar(getParam("TargetMax")).startsWith("Count$CardCounters") + && getHostCard() != null && getHostCard().hasSVar("CostCountersRemoved")) { // TODO: Current AI implementation removes the counters during payment before the // ability is added to stack, resulting in maxTargets=0 at this point. We are // assuming here that the AI logic specified a legal number, and that number ended // up being in CostCountersRemoved that is created on the card during payment. - maxTargets = Integer.parseInt(this.getHostCard().getSVar("CostCountersRemoved")); + maxTargets = Integer.parseInt(getHostCard().getSVar("CostCountersRemoved")); } return minTargets <= numTargets && maxTargets >= numTargets; @@ -1775,9 +1766,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit targetRestrictions.applyTargetTextChanges(this); } - if (getPayCosts() != null) { - getPayCosts().applyTextChangeEffects(this); - } + getPayCosts().applyTextChangeEffects(this); stackDescription = AbilityUtils.applyDescriptionTextChangeEffects(originalStackDescription, this); description = AbilityUtils.applyDescriptionTextChangeEffects(originalDescription, this); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerTapsForMana.java b/forge-game/src/main/java/forge/game/trigger/TriggerTapsForMana.java index bd32d65a794..6cbf96cc1aa 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerTapsForMana.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerTapsForMana.java @@ -60,7 +60,7 @@ public class TriggerTapsForMana extends Trigger { //Check for tapping if (!hasParam("NoTapCheck")) { final SpellAbility manaAbility = (SpellAbility) runParams.get(AbilityKey.AbilityMana); - if (manaAbility == null || manaAbility.getRootAbility().getPayCosts() == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) { + if (manaAbility == null || !manaAbility.getRootAbility().getPayCosts().hasTapCost()) { return false; } } diff --git a/forge-gui/src/main/java/forge/match/input/InputPayManaSimple.java b/forge-gui/src/main/java/forge/match/input/InputPayManaSimple.java deleted file mode 100644 index 1f8be1adb47..00000000000 --- a/forge-gui/src/main/java/forge/match/input/InputPayManaSimple.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Forge: Play Magic: the Gathering. - * Copyright (C) 2011 Forge Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package forge.match.input; - -import forge.card.mana.ManaAtom; -import forge.card.mana.ManaCost; -import forge.card.mana.ManaCostShard; -import forge.game.Game; -import forge.game.card.Card; -import forge.game.mana.ManaCostBeingPaid; -import forge.game.player.Player; -import forge.game.spellability.SpellAbility; -import forge.model.FModel; -import forge.player.PlayerControllerHuman; -import forge.properties.ForgePreferences; -import forge.util.ITriggerEvent; -import forge.util.Localizer; - -//pays the cost of a card played from the player's hand -//the card is removed from the players hand if the cost is paid -//CANNOT be used for ABILITIES -public class InputPayManaSimple extends InputPayMana { - // anything that uses this should be converted to Ability_Cost - /** Constant serialVersionUID=3467312982164195091L. */ - private static final long serialVersionUID = 3467312982164195091L; - - private final Card originalCard; - private final ManaCost originalManaCost; - - public InputPayManaSimple(final PlayerControllerHuman controller, final Game game, final SpellAbility sa, final ManaCostBeingPaid manaCostToPay) { - super(controller, sa, sa.getActivatingPlayer()); - this.originalManaCost = manaCostToPay.toManaCost(); - this.originalCard = sa.getHostCard(); - - if (sa.getHostCard().isCopiedSpell() && sa.isSpell()) { - this.manaCost = new ManaCostBeingPaid(ManaCost.ZERO); - game.getStack().add(this.saPaidFor); - } - else { - this.manaCost = manaCostToPay; - } - } - - @Override - protected void onManaAbilityPaid() { - if (this.manaCost.isPaid()) { - this.originalCard.setSunburstValue(this.manaCost.getSunburst()); - } - } - - /** {@inheritDoc} */ - @Override - protected final void onPlayerSelected(final Player selected, final ITriggerEvent triggerEvent) { - if (player == selected) { - if (player.canPayLife(this.phyLifeToLose + 2)) { - if (manaCost.payPhyrexian()) { - this.phyLifeToLose += 2; - } else { - if (player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK)) { - manaCost.decreaseShard(ManaCostShard.BLACK, 1); - this.phyLifeToLose += 2; - } - } - } - - this.showMessage(); - } - } - - /** - *

- * done. - *

- */ - @Override - protected void done() { - this.originalCard.setSunburstValue(this.manaCost.getSunburst()); - - if (this.phyLifeToLose > 0) { - player.payLife(this.phyLifeToLose, this.originalCard); - } - if (!this.saPaidFor.getHostCard().isCopiedSpell()) { - if (this.saPaidFor.isSpell()) { - this.saPaidFor.setHostCard(game.getAction().moveToStack(this.originalCard, null)); - } - } - } - - /** {@inheritDoc} */ - @Override - protected final void onCancel() { - player.getManaPool().refundManaPaid(this.saPaidFor); - // Update UI - - this.stop(); - } - - /** {@inheritDoc} */ - @Override - public final void showMessage() { - if (isFinished()) { return; } - - updateButtons(); - - if (this.manaCost.isPaid() && !new ManaCostBeingPaid(this.originalManaCost).isPaid()) { - this.done(); - this.stop(); - } - else { - updateMessage(); - } - } - - /* (non-Javadoc) - * @see forge.control.input.InputPayManaBase#updateMessage() - */ - @Override - protected String getMessage() { - final StringBuilder msg = new StringBuilder(); - final Localizer localizer = Localizer.getInstance(); - if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_DETAILED_SPELLDESC_IN_PROMPT)) { - msg.append(saPaidFor.getStackDescription().replace("(Targeting ERROR)", "")).append("\n\n"); - } - msg.append(localizer.getMessage("lblPayManaCost")).append(" ").append(this.manaCost.toString(false, player.getManaPool())); - if (this.phyLifeToLose > 0) { - msg.append(" ").append(String.format(localizer.getMessage("lblLifePaidForPhyrexianMana"), this.phyLifeToLose)); - } - - boolean isLifeInsteadBlack = player.hasKeyword("PayLifeInsteadOf:B") && manaCost.hasAnyKind(ManaAtom.BLACK); - - if (manaCost.containsPhyrexianMana() || isLifeInsteadBlack) { - StringBuilder sb = new StringBuilder(); - if (manaCost.containsPhyrexianMana() && !isLifeInsteadBlack) { - sb.append(localizer.getMessage("lblClickOnYourLifeTotalToPayLifeForPhyrexianMana")); - } else if (!manaCost.containsPhyrexianMana() && isLifeInsteadBlack) { - sb.append(localizer.getMessage("lblClickOnYourLifeTotalToPayLifeForBlackMana")); - } else if (manaCost.containsPhyrexianMana() && isLifeInsteadBlack) { - sb.append(localizer.getMessage("lblClickOnYourLifeTotalToPayLifeForPhyrexianOrBlackMana")); - } - msg.append("\n(").append(sb).append(")"); - } - - // has its own variant of checkIfPaid - return msg.toString(); - } -} diff --git a/forge-gui/src/main/java/forge/match/input/InputProxy.java b/forge-gui/src/main/java/forge/match/input/InputProxy.java index b503dc8ae74..4d6047fbd06 100644 --- a/forge-gui/src/main/java/forge/match/input/InputProxy.java +++ b/forge-gui/src/main/java/forge/match/input/InputProxy.java @@ -110,7 +110,7 @@ public class InputProxy implements Observer { } private Card getCard(final CardView cardView) { - return controller.getGame().getCard(cardView); + return controller.getCard(cardView); } public final String getActivateAction(final CardView cardView) { diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 9f95f80be1b..2e2a5dea062 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -14,6 +14,8 @@ import com.google.common.collect.Lists; import forge.card.CardType; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityView; +import forge.game.GameEntityViewMap; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardCollection; @@ -35,6 +37,7 @@ import forge.match.input.InputSelectManyBase; import forge.util.Aggregates; import forge.util.TextUtil; import forge.util.collect.FCollectionView; +import forge.util.gui.SGuiChoose; import forge.util.ITriggerEvent; import forge.util.Localizer; import forge.util.CardTranslation; @@ -310,11 +313,12 @@ public class HumanCostDecision extends CostDecisionMakerBase { if (nNeeded == 0) { return PaymentDecision.number(0); } - final Game game = controller.getGame(); - final Player p = game.getPlayer(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileFromWhoseZone", cost.getFrom().getTranslatedName()), PlayerView.getCollection(payableZone))); - if (p == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(payableZone); + final PlayerView pv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileFromWhoseZone", cost.getFrom().getTranslatedName()), gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return null; } + final Player p = gameCachePlayer.get(pv); final CardCollection typeList = CardLists.filter(list, CardPredicates.isOwner(p)); final int count = typeList.size(); @@ -322,8 +326,13 @@ public class HumanCostDecision extends CostDecisionMakerBase { return null; } - final CardCollection toExile = game.getCardList(controller.getGui().many(Localizer.getInstance().getMessage("lblExileFromZone", cost.getFrom().getTranslatedName()), Localizer.getInstance().getMessage("lblToBeExiled"), nNeeded, CardView.getCollection(typeList), null)); - return PaymentDecision.card(toExile); + GameEntityViewMap gameCacheExile = GameEntityView.getMap(typeList); + List views = controller.getGui().many( + Localizer.getInstance().getMessage("lblExileFromZone", cost.getFrom().getTranslatedName()), + Localizer.getInstance().getMessage("lblToBeExiled"), nNeeded, gameCacheExile.getTrackableKeys(), null); + List result = Lists.newArrayList(); + gameCacheExile.addToList(views, result); + return PaymentDecision.card(result); } @Override @@ -395,20 +404,17 @@ public class HumanCostDecision extends CostDecisionMakerBase { return PaymentDecision.card(list); } - private Card getCard(final CardView cardView) { - return controller.getGame().getCard(cardView); - } - private PaymentDecision exileFromMiscZone(final CostExile cost, final SpellAbility sa, final int nNeeded, final CardCollection typeList) { if (typeList.size() < nNeeded) { return null; } + GameEntityViewMap gameCacheCard = GameEntityView.getMap(typeList); + final CardCollection exiled = new CardCollection(); for (int i = 0; i < nNeeded; i++) { - final Card c = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileProgressFromZone", String.valueOf(i + 1), String.valueOf(nNeeded), cost.getFrom().getTranslatedName()), CardView.getCollection(typeList))); - if (c == null) { return null; } + final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileProgressFromZone", String.valueOf(i + 1), String.valueOf(nNeeded), cost.getFrom().getTranslatedName()), gameCacheCard.getTrackableKeys()); + if (cv == null || !gameCacheCard.containsKey(cv)) { return null; } - typeList.remove(c); - exiled.add(c); + exiled.add(gameCacheCard.remove(cv)); } return PaymentDecision.card(exiled); } @@ -438,12 +444,17 @@ public class HumanCostDecision extends CostDecisionMakerBase { if (ability.isOptionalTrigger()) { min = 0; } - final CardCollection choice = controller.getGame().getCardList(controller.getGui().many(Localizer.getInstance().getMessage("lblChooseAnExiledCardPutIntoGraveyard"), Localizer.getInstance().getMessage("lblToGraveyard"), min, c, CardView.getCollection(list), CardView.get(source))); - - if (choice == null || choice.size() < c) { + GameEntityViewMap gameCacheExile = GameEntityView.getMap(list); + List views = controller.getGui().many( + Localizer.getInstance().getMessage("lblChooseAnExiledCardPutIntoGraveyard"), + Localizer.getInstance().getMessage("lblToGraveyard"), min, c, CardView.getCollection(list), CardView.get(source)); + + if (views == null || views.size() < c) { return null; } - return PaymentDecision.card(choice); + List result = Lists.newArrayList(); + gameCacheExile.addToList(views, result); + return PaymentDecision.card(result); } @Override @@ -555,11 +566,12 @@ public class HumanCostDecision extends CostDecisionMakerBase { return PaymentDecision.players(oppsThatCanGainLife); } - final Player chosenToGain = controller.getGame().getPlayer(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblCardChooseAnOpponentToGainNLife", CardTranslation.getTranslatedName(source.getName()), String.valueOf(c)), PlayerView.getCollection(oppsThatCanGainLife))); - if (chosenToGain == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(oppsThatCanGainLife); + final PlayerView pv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblCardChooseAnOpponentToGainNLife", CardTranslation.getTranslatedName(source.getName()), String.valueOf(c)), gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return null; } - return PaymentDecision.players(Lists.newArrayList(chosenToGain)); + return PaymentDecision.players(Lists.newArrayList(gameCachePlayer.get(pv))); } @Override @@ -694,13 +706,13 @@ public class HumanCostDecision extends CostDecisionMakerBase { } final CardCollection chosen = new CardCollection(); + GameEntityViewMap gameCacheCard = GameEntityView.getMap(typeList); for (int i = 0; i < nNeeded; i++) { - final Card c = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblFromZonePutToLibrary", fromZone.getTranslatedName()), CardView.getCollection(typeList))); - if (c == null) { + final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblFromZonePutToLibrary", fromZone.getTranslatedName()), gameCacheCard.getTrackableKeys()); + if (cv == null || !gameCacheCard.containsKey(cv)) { return null; } - typeList.remove(c); - chosen.add(c); + chosen.add(gameCacheCard.remove(cv)); } return PaymentDecision.card(chosen); } @@ -710,10 +722,12 @@ public class HumanCostDecision extends CostDecisionMakerBase { return PaymentDecision.number(0); } - final Player p = controller.getGame().getPlayer(controller.getGui().oneOrNone(TextUtil.concatNoSpace(Localizer.getInstance().getMessage("lblPutCardsFromWhoseZone"), fromZone.getTranslatedName()), PlayerView.getCollection(payableZone))); - if (p == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(payableZone); + PlayerView pv = SGuiChoose.oneOrNone(TextUtil.concatNoSpace(Localizer.getInstance().getMessage("lblPutCardsFromWhoseZone"), fromZone.getTranslatedName()), gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return null; } + Player p = gameCachePlayer.get(pv); final CardCollection typeList = CardLists.filter(list, CardPredicates.isOwner(p)); if (typeList.size() < nNeeded) { @@ -721,13 +735,13 @@ public class HumanCostDecision extends CostDecisionMakerBase { } final CardCollection chosen = new CardCollection(); + GameEntityViewMap gameCacheCard = GameEntityView.getMap(typeList); for (int i = 0; i < nNeeded; i++) { - final Card c = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblPutZoneCardsToLibrary", fromZone.getTranslatedName()), CardView.getCollection(typeList))); - if (c == null) { + final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblPutZoneCardsToLibrary", fromZone.getTranslatedName()), gameCacheCard.getTrackableKeys()); + if (cv == null || !gameCacheCard.containsKey(cv)) { return null; } - typeList.remove(c); - chosen.add(c); + chosen.add(gameCacheCard.remove(cv)); } return PaymentDecision.card(chosen); } @@ -1068,15 +1082,14 @@ public class HumanCostDecision extends CostDecisionMakerBase { } // Rift Elemental only - always removes 1 counter, so there will be no code for N counters. - final List suspended = Lists.newArrayList(); - for (final Card crd : validCards) { - if (crd.getCounters(cost.counter) > 0) { - suspended.add(CardView.get(crd)); - } + GameEntityViewMap gameCacheSuspended = GameEntityView.getMap(CardLists.filter(validCards, CardPredicates.hasCounter(cost.counter))); + + final CardView cv = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblRemoveCountersFromAInZoneCard", cost.zone.getTranslatedName()), gameCacheSuspended.getTrackableKeys()); + if (cv == null || !gameCacheSuspended.containsKey(cv)) { + return null; } - final Card card = getCard(controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblRemoveCountersFromAInZoneCard", cost.zone.getTranslatedName()), suspended)); - return null == card ? null : PaymentDecision.card(card, c); + return PaymentDecision.card(gameCacheSuspended.get(cv), c); } @Override diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index 207f901b27b..b7b4a93f735 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -6,6 +6,8 @@ import forge.FThreads; import forge.card.mana.ManaCost; import forge.game.Game; import forge.game.GameActionUtil; +import forge.game.GameEntityView; +import forge.game.GameEntityViewMap; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.ability.effects.CharmEffect; @@ -23,7 +25,6 @@ import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; import forge.match.input.InputPayMana; import forge.match.input.InputPayManaOfCostPayment; -import forge.match.input.InputPayManaSimple; import forge.match.input.InputSelectCardsFromList; import forge.util.TextUtil; import forge.util.collect.FCollectionView; @@ -46,7 +47,7 @@ public class HumanPlay { *

* playSpellAbility. *

- * + * * @param sa * a {@link forge.game.spellability.SpellAbility} object. */ @@ -94,34 +95,9 @@ public class HumanPlay { source.animateBestow(); } - // Need to check PayCosts, and Ability + All SubAbilities for Target - boolean newAbility = sa.getPayCosts() != null; - SpellAbility ability = sa; - while ((ability != null) && !newAbility) { - final TargetRestrictions tgt = ability.getTargetRestrictions(); - - newAbility |= tgt != null; - ability = ability.getSubAbility(); - } - // System.out.println("Playing:" + sa.getDescription() + " of " + sa.getHostCard() + " new = " + newAbility); - if (newAbility) { - - final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); - if (!req.playAbility(true, false, false)) { - if (flippedToCast && !castFaceDown) { - source.turnFaceDown(true); - } - return false; - } - } else if (payManaCostIfNeeded(controller, p, sa)) { - if (sa.isSpell() && !source.isCopiedSpell()) { - sa.setHostCard(p.getGame().getAction().moveToStack(source, sa)); - } - - p.getGame().getStack().add(sa); - } else { - // Failed to pay costs, revert to original state + final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); + if (!req.playAbility(true, false, false)) { if (flippedToCast && !castFaceDown) { source.turnFaceDown(true); } @@ -133,7 +109,7 @@ public class HumanPlay { /** * choose optional additional costs. For HUMAN only * @param p - * + * * @param original * the original sa * @return an ArrayList. @@ -159,31 +135,11 @@ public class HumanPlay { //final List abilities = GameActionUtil.getOptionalCosts(original); } - private static boolean payManaCostIfNeeded(final PlayerControllerHuman controller, final Player p, final SpellAbility sa) { - final ManaCostBeingPaid manaCost; - if (sa.getHostCard().isCopiedSpell() && sa.isSpell()) { - manaCost = new ManaCostBeingPaid(ManaCost.ZERO); - } - else { - manaCost = new ManaCostBeingPaid(sa.getPayCosts().getTotalMana()); - CostAdjustment.adjust(manaCost, sa, null, false); - } - - boolean isPaid = manaCost.isPaid(); - - if (!isPaid) { - InputPayManaSimple inputPay = new InputPayManaSimple(controller, p.getGame(), sa, manaCost); - inputPay.showAndWait(); - isPaid = inputPay.isPaid(); - } - return isPaid; - } - /** *

* playSpellAbilityForFree. *

- * + * * @param sa * a {@link forge.game.spellability.SpellAbility} object. */ @@ -193,33 +149,22 @@ public class HumanPlay { source.setSplitStateToPlayAbility(sa); - if (sa.getPayCosts() != null) { - if (!sa.isCopied()) { - if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) { - CharmEffect.makeChoices(sa); - } - sa = AbilityUtils.addSpliceEffects(sa); + if (!sa.isCopied()) { + if (sa.getApi() == ApiType.Charm && !sa.isWrapper()) { + CharmEffect.makeChoices(sa); } + sa = AbilityUtils.addSpliceEffects(sa); + } - final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); - req.playAbility(mayChooseNewTargets, true, false); - } - else { - if (sa.isSpell()) { - final Card c = sa.getHostCard(); - if (!c.isCopiedSpell()) { - sa.setHostCard(game.getAction().moveToStack(c, sa)); - } - } - game.getStack().add(sa); - } + final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); + req.playAbility(mayChooseNewTargets, true, false); } /** *

* playSpellAbility_NoStack. *

- * + * * @param sa * a {@link forge.game.spellability.SpellAbility} object. */ @@ -230,14 +175,8 @@ public class HumanPlay { public final static void playSpellAbilityNoStack(final PlayerControllerHuman controller, final Player player, final SpellAbility sa, boolean useOldTargets) { sa.setActivatingPlayer(player); - if (sa.getPayCosts() != null) { - final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); - - req.playAbility(!useOldTargets, false, true); - } - else if (payManaCostIfNeeded(controller, player, sa)) { - AbilityUtils.resolve(sa); - } + final HumanPlaySpellAbility req = new HumanPlaySpellAbility(controller, sa); + req.playAbility(!useOldTargets, false, true); } // ------------------------------------------------------------------------ @@ -506,14 +445,13 @@ public class HumanPlay { } else { // replace this with input CardCollection newList = new CardCollection(); + GameEntityViewMap gameCacheList = GameEntityView.getMap(list); for (int i = 0; i < nNeeded; i++) { - final Card c = p.getGame().getCard(SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblExileFromZone", from.getTranslatedName()), CardView.getCollection(list))); - if (c == null) { + final CardView cv = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblExileFromZone", from.getTranslatedName()), gameCacheList.getTrackableKeys()); + if (cv == null || !gameCacheList.containsKey(cv)) { return false; } - - list.remove(c); - newList.add(c); + newList.add(gameCacheList.remove(cv)); } costExile.payAsDecided(p, PaymentDecision.card(newList), sourceAbility); } @@ -543,27 +481,28 @@ public class HumanPlay { payableZone.add(player); } } - Player chosen = controller.getGame().getPlayer(SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardFromWhoseZone", from.getTranslatedName()), PlayerView.getCollection(payableZone))); - if (chosen == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(payableZone); + PlayerView pv = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardFromWhoseZone", from.getTranslatedName()), gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return false; } + Player chosen = gameCachePlayer.get(pv); List typeList = CardLists.filter(list, CardPredicates.isOwner(chosen)); + GameEntityViewMap gameCacheTypeList = GameEntityView.getMap(typeList); for (int i = 0; i < amount; i++) { - if (typeList.isEmpty()) { + if (gameCacheTypeList.isEmpty()) { return false; } - - final Card c = p.getGame().getCard(SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardToLibrary"), CardView.getCollection(typeList))); - - if (c != null) { - typeList.remove(c); - p.getGame().getAction().moveToLibrary(c, Integer.parseInt(((CostPutCardToLib) part).getLibPos()), null); - } - else { + final CardView cv = SGuiChoose.oneOrNone(Localizer.getInstance().getMessage("lblPutCardToLibrary"), gameCacheTypeList.getTrackableKeys()); + if (cv == null || !gameCacheTypeList.containsKey(cv)) { return false; } + final Card c = gameCacheTypeList.get(cv); + + gameCacheTypeList.remove(c); + p.getGame().getAction().moveToLibrary(c, Integer.parseInt(((CostPutCardToLib) part).getLibPos()), null); } } else { // Tainted Specter, Gurzigost, etc. diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 5a863164800..370d250b308 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -114,7 +114,7 @@ public class HumanPlaySpellAbility { ability = GameActionUtil.addExtraKeywordCost(ability); - Cost abCost = ability.getPayCosts() == null ? new Cost("0", ability.isAbility()) : ability.getPayCosts(); + Cost abCost = ability.getPayCosts(); CostPayment payment = new CostPayment(abCost, ability); // TODO Apply this to the SAStackInstance instead of the Player diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 9e94b7609f9..d2bdbf9033b 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -96,6 +96,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont private final Localizer localizer = Localizer.getInstance(); protected Map spellViewCache = null; + protected GameEntityViewMap gameCache = new GameEntityViewMap<>(); public PlayerControllerHuman(final Game game0, final Player p, final LobbyPlayer lp) { super(game0, p, lp); @@ -151,7 +152,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (t instanceof Card) { tempShowCard((Card) t); } else if (t instanceof CardView) { - tempShowCard(game.getCard((CardView) t)); + tempShowCard(getCard((CardView) t)); } } } @@ -287,14 +288,16 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (defender != null && assignDamageAsIfNotBlocked(attacker)) { map.put(null, damageDealt); } else { - final List vBlockers = CardView.getCollection(blockers); if ((attacker.hasKeyword(Keyword.TRAMPLE) && defender != null) || (blockers.size() > 1)) { + GameEntityViewMap gameCacheBlockers = GameEntityView.getMap(blockers); final CardView vAttacker = CardView.get(attacker); final GameEntityView vDefender = GameEntityView.get(defender); - final Map result = getGui().assignCombatDamage(vAttacker, vBlockers, damageDealt, + final Map result = getGui().assignCombatDamage(vAttacker, gameCacheBlockers.getTrackableKeys(), damageDealt, vDefender, overrideOrder); for (final Entry e : result.entrySet()) { - map.put(game.getCard(e.getKey()), e.getValue()); + if (gameCacheBlockers.containsKey(e.getKey())) { + map.put(gameCacheBlockers.get(e.getKey()), e.getValue()); + } } } else { map.put(blockers.get(0), damageDealt); @@ -377,9 +380,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont final boolean useUiPointAtCard = (FModel.getPreferences().getPrefBoolean(FPref.UI_SELECT_FROM_CARD_DISPLAYS) && (!GuiBase.getInterface().isLibgdxPort())) ? - (cz.is(ZoneType.Battlefield) || cz.is(ZoneType.Hand) || cz.is(ZoneType.Library) || - cz.is(ZoneType.Graveyard) || cz.is(ZoneType.Exile) || cz.is(ZoneType.Flashback) || cz.is(ZoneType.Command)) : - (cz.is(ZoneType.Hand) && cz.getPlayer() == player || cz.is(ZoneType.Battlefield)); + (cz.is(ZoneType.Battlefield) || cz.is(ZoneType.Hand) || cz.is(ZoneType.Library) || + cz.is(ZoneType.Graveyard) || cz.is(ZoneType.Exile) || cz.is(ZoneType.Flashback) || cz.is(ZoneType.Command)) : + (cz.is(ZoneType.Hand) && cz.getPlayer() == player || cz.is(ZoneType.Battlefield)); if (!useUiPointAtCard) { return false; } @@ -412,23 +415,15 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } tempShowCards(sourceList); - final CardCollection choices = getGame().getCardList(getGui().many(title, localizer.getMessage("lblChosenCards"), min, max, - CardView.getCollection(sourceList), CardView.get(sa.getHostCard()))); + GameEntityViewMap gameCachechoose = GameEntityView.getMap(sourceList); + List views = getGui().many(title, localizer.getMessage("lblChosenCards"), min, max, + gameCachechoose.getTrackableKeys(), CardView.get(sa.getHostCard())); endTempShowCards(); - + final CardCollection choices = new CardCollection(); + gameCachechoose.addToList(views, choices); return choices; } - // pfps there should be a better way - private GameEntity convertToEntity(final GameEntityView view) { - if (view instanceof CardView) { - return game.getCard((CardView) view); - } else if (view instanceof PlayerView) { - return game.getPlayer((PlayerView) view); - } else return null; - } - - @SuppressWarnings("unchecked") @Override public T chooseSingleEntityForEffect(final FCollectionView optionList, final DelayedReveal delayedReveal, final SpellAbility sa, final String title, final boolean isOptional, @@ -454,6 +449,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (delayedReveal != null) { tempShow(delayedReveal.getCards()); } + + GameEntityViewMap gameCacheChoose = GameEntityView.getMap(optionList); + if (useSelectCardsInput(optionList)) { final InputSelectEntitiesFromList input = new InputSelectEntitiesFromList<>(this, isOptional ? 0 : 1, 1, optionList, sa); @@ -465,21 +463,25 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } final GameEntityView result = getGui().chooseSingleEntityForEffect(title, - GameEntityView.getEntityCollection(optionList), delayedReveal, isOptional); + gameCacheChoose.getTrackableKeys(), delayedReveal, isOptional); endTempShowCards(); - return (T) convertToEntity(result); + if (result != null || !gameCacheChoose.containsKey(result)) { + return null; + } + return gameCacheChoose.get(result); } - @SuppressWarnings("unchecked") @Override - public List chooseEntitiesForEffect(final FCollectionView optionList, final int min, final int max, - final DelayedReveal delayedReveal, final SpellAbility sa, final String title, final Player targetedPlayer) { + public List chooseEntitiesForEffect(final FCollectionView optionList, final int min, + final int max, final DelayedReveal delayedReveal, final SpellAbility sa, final String title, + final Player targetedPlayer) { // useful details for debugging problems with the mass select logic Sentry.getContext().addExtra("Card", sa.getCardView().toString()); Sentry.getContext().addExtra("SpellAbility", sa.toString()); - // Human is supposed to read the message and understand from it what to // choose + // Human is supposed to read the message and understand from it what to // + // choose if (optionList.isEmpty()) { if (delayedReveal != null) { reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), @@ -494,29 +496,24 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont tempShow(optionList); if (useSelectCardsInput(optionList)) { - final InputSelectEntitiesFromList input = new InputSelectEntitiesFromList<>(this, min, max, - optionList, sa); + final InputSelectEntitiesFromList input = new InputSelectEntitiesFromList<>(this, min, max, optionList, + sa); input.setCancelAllowed(min == 0); input.setMessage(MessageUtil.formatMessage(title, player, targetedPlayer)); input.showAndWait(); endTempShowCards(); return (List) input.getSelected(); - } - final List chosen = getGui().chooseEntitiesForEffect(title, - GameEntityView.getEntityCollection(optionList), min, max, delayedReveal); - endTempShowCards(); - - List results = new ArrayList<>(); //pfps I'm not sure that the chosens should be modified this way - if (chosen instanceof List && chosen.size() > 0) { - for (GameEntityView entry: chosen) { - if (entry instanceof CardView) { - results.add((T)game.getCard((CardView) entry)); - } - if (entry instanceof PlayerView) { - results.add((T)game.getPlayer((PlayerView) entry)); - } - } } + GameEntityViewMap gameCacheEntity = GameEntityView.getMap(optionList); + final List views = getGui().chooseEntitiesForEffect(title, gameCacheEntity.getTrackableKeys(), min, max, delayedReveal); + endTempShowCards(); + + List results = Lists.newArrayList(); + + if (views != null) { + gameCacheEntity.addToList(views, results); + } + return results; } @@ -559,7 +556,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see * forge.game.player.PlayerController#confirmAction(forge.card.spellability. * SpellAbility, java.lang.String, java.lang.String) @@ -686,43 +683,50 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont @Override public CardCollection orderBlockers(final Card attacker, final CardCollection blockers) { + GameEntityViewMap gameCacheBlockers = GameEntityView.getMap(blockers); final CardView vAttacker = CardView.get(attacker); getGui().setPanelSelection(vAttacker); - return game.getCardList(getGui().order(localizer.getMessage("lblChooseDamageOrderFor", CardTranslation.getTranslatedName(vAttacker.getName())), localizer.getMessage("lblDamagedFirst"), - CardView.getCollection(blockers), vAttacker)); + List chosen = getGui().order(localizer.getMessage("lblChooseDamageOrderFor", CardTranslation.getTranslatedName(vAttacker.getName())), localizer.getMessage("lblDamagedFirst"), + gameCacheBlockers.getTrackableKeys(), vAttacker); + CardCollection chosenCards = new CardCollection(); + gameCacheBlockers.addToList(chosen, chosenCards); + return chosenCards; } @Override public List exertAttackers(List attackers) { - HashMap mapCVtoC = new HashMap<>(); - for (Card card : attackers) { - mapCVtoC.put(card.getView(), card); - } - List chosen; - List choices = new ArrayList<>(mapCVtoC.keySet()); - chosen = getGui().order(localizer.getMessage("lblExertAttackersConfirm"), localizer.getMessage("lblExerted"), 0, choices.size(), choices, null, null, false); - List chosenCards = new ArrayList<>(); - for (CardView cardView : chosen) { - chosenCards.add(mapCVtoC.get(cardView)); - } + GameEntityViewMap gameCacheExert = GameEntityView.getMap(attackers); + List chosen = getGui().order(localizer.getMessage("lblExertAttackersConfirm"), localizer.getMessage("lblExerted"), + 0, gameCacheExert.size(), gameCacheExert.getTrackableKeys(), null, null, false); + + List chosenCards = new CardCollection(); + gameCacheExert.addToList(chosen, chosenCards); return chosenCards; } @Override public CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) { + GameEntityViewMap gameCacheBlockers = GameEntityView.getMap(oldBlockers); final CardView vAttacker = CardView.get(attacker); getGui().setPanelSelection(vAttacker); - return game.getCardList(getGui().insertInList( + List chosen = getGui().insertInList( localizer.getMessage("lblChooseBlockerAfterWhichToPlaceAttackert", CardTranslation.getTranslatedName(vAttacker.getName())), - CardView.get(blocker), CardView.getCollection(oldBlockers))); + CardView.get(blocker), CardView.getCollection(oldBlockers)); + CardCollection chosenCards = new CardCollection(); + gameCacheBlockers.addToList(chosen, chosenCards); + return chosenCards; } @Override public CardCollection orderAttackers(final Card blocker, final CardCollection attackers) { + GameEntityViewMap gameCacheAttackers = GameEntityView.getMap(attackers); final CardView vBlocker = CardView.get(blocker); getGui().setPanelSelection(vBlocker); - return game.getCardList(getGui().order(localizer.getMessage("lblChooseDamageOrderFor", CardTranslation.getTranslatedName(vBlocker.getName())), localizer.getMessage("lblDamagedFirst"), - CardView.getCollection(attackers), vBlocker)); + List chosen = getGui().order(localizer.getMessage("lblChooseDamageOrderFor", CardTranslation.getTranslatedName(vBlocker.getName())), localizer.getMessage("lblDamagedFirst"), + CardView.getCollection(attackers), vBlocker); + CardCollection chosenCards = new CardCollection(); + gameCacheAttackers.addToList(chosen, chosenCards); + return chosenCards; } @Override @@ -739,7 +743,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } final String fm = MessageUtil.formatMessage(message, getLocalPlayerView(), owner); if (!cards.isEmpty()) { - tempShowCards(game.getCardList(cards)); + tempShowCards(getCardList(cards)); getGui().reveal(fm, cards); endTempShowCards(); } else { @@ -749,23 +753,27 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } public List manipulateCardList(final String title, final Iterable cards, final Iterable manipulable, final boolean toTop, final boolean toBottom, final boolean toAnywhere) { - Iterable result = getGui().manipulateCardList(title, CardView.getCollection(cards), CardView.getCollection(manipulable), toTop, toBottom, toAnywhere); - return game.getCardList(result); + GameEntityViewMap gameCacheManipulate = GameEntityView.getMap(cards); + gameCacheManipulate.putAll(manipulable); + List views = getGui().manipulateCardList(title, CardView.getCollection(cards), CardView.getCollection(manipulable), toTop, toBottom, toAnywhere); + List result = new CardCollection(); + gameCacheManipulate.addToList(views, result); + return result; } public ImmutablePair arrangeForMove(final String title, final FCollectionView cards, final List manipulable, final boolean topOK, final boolean bottomOK) { - List result = manipulateCardList(localizer.getMessage("lblMoveCardstoToporBbottomofLibrary"), cards, manipulable, topOK, bottomOK, false); + List result = manipulateCardList(localizer.getMessage("lblMoveCardstoToporBbottomofLibrary"), cards, manipulable, topOK, bottomOK, false); CardCollection toBottom = new CardCollection(); CardCollection toTop = new CardCollection(); - for (int i = 0; i=0 && manipulable.contains(result.get(i)); i-- ) { - toBottom.add(result.get(i)); + for (int i = 0; i=0 && manipulable.contains(result.get(i)); i-- ) { + toBottom.add(result.get(i)); + } + } + return ImmutablePair.of(toTop,toBottom); } @Override @@ -789,16 +797,24 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont toBottom = topN; } } else { - toBottom = game.getCardList(getGui().many(localizer.getMessage("lblSelectCardsToBeOutOnTheBottomOfYourLibrary"), - localizer.getMessage("lblCardsToPutOnTheBottom"), -1, CardView.getCollection(topN), null)); + GameEntityViewMap cardCacheScry = GameEntityView.getMap(topN); + + toBottom = new CardCollection(); + List views = getGui().many(localizer.getMessage("lblSelectCardsToBeOutOnTheBottomOfYourLibrary"), + localizer.getMessage("lblCardsToPutOnTheBottom"), -1, cardCacheScry.getTrackableKeys(), null); + cardCacheScry.addToList(views, toBottom); + topN.removeAll(toBottom); if (topN.isEmpty()) { toTop = null; } else if (topN.size() == 1) { toTop = topN; } else { - toTop = game.getCardList(getGui().order(localizer.getMessage("lblArrangeCardsToBePutOnTopOfYourLibrary"), - localizer.getMessage("lblTopOfLibrary"), CardView.getCollection(topN), null)); + GameEntityViewMap cardCacheOrder = GameEntityView.getMap(topN); + toTop = new CardCollection(); + views = getGui().order(localizer.getMessage("lblArrangeCardsToBePutOnTopOfYourLibrary"), + localizer.getMessage("lblTopOfLibrary"), cardCacheOrder.getTrackableKeys(), null); + cardCacheOrder.addToList(views, toTop); } } } @@ -827,16 +843,22 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont toGrave = topN; } } else { - toGrave = game.getCardList(getGui().many(localizer.getMessage("lblSelectCardsToBePutIntoTheGraveyard"), - localizer.getMessage("lblCardsToPutInTheGraveyard"), -1, CardView.getCollection(topN), null)); + GameEntityViewMap gameCacheSurveil = GameEntityView.getMap(topN); + toGrave = new CardCollection(); + List views = getGui().many(localizer.getMessage("lblSelectCardsToBePutIntoTheGraveyard"), + localizer.getMessage("lblCardsToPutInTheGraveyard"), -1, gameCacheSurveil.getTrackableKeys(), null); + gameCacheSurveil.addToList(views, toGrave); topN.removeAll(toGrave); if (topN.isEmpty()) { toTop = null; } else if (topN.size() == 1) { toTop = topN; } else { - toTop = game.getCardList(getGui().order(localizer.getMessage("lblArrangeCardsToBePutOnTopOfYourLibrary"), - localizer.getMessage("lblTopOfLibrary"), CardView.getCollection(topN), null)); + GameEntityViewMap cardCacheOrder = GameEntityView.getMap(topN); + toTop = new CardCollection(); + views = getGui().order(localizer.getMessage("lblArrangeCardsToBePutOnTopOfYourLibrary"), + localizer.getMessage("lblTopOfLibrary"), cardCacheOrder.getTrackableKeys(), null); + cardCacheOrder.addToList(views, toTop); } } endTempShowCards(); @@ -882,32 +904,28 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } } - List choices; tempShowCards(cards); + GameEntityViewMap gameCacheMove = GameEntityView.getMap(cards); + List choices = gameCacheMove.getTrackableKeys(); + switch (destinationZone) { case Library: - choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoLibrary"), localizer.getMessage("lblClosestToTop"), - CardView.getCollection(cards), null); + choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoLibrary"), localizer.getMessage("lblClosestToTop"), choices, null); break; case Battlefield: - choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutOntoBattlefield"), localizer.getMessage("lblPutFirst"), - CardView.getCollection(cards), null); + choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutOntoBattlefield"), localizer.getMessage("lblPutFirst"), choices, null); break; case Graveyard: - choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoGraveyard"), localizer.getMessage("lblClosestToBottom"), - CardView.getCollection(cards), null); + choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoGraveyard"), localizer.getMessage("lblClosestToBottom"), choices, null); break; case PlanarDeck: - choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoPlanarDeck"), localizer.getMessage("lblClosestToTop"), - CardView.getCollection(cards), null); + choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoPlanarDeck"), localizer.getMessage("lblClosestToTop"), choices, null); break; case SchemeDeck: - choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoSchemeDeck"), localizer.getMessage("lblClosestToTop"), - CardView.getCollection(cards), null); + choices = getGui().order(localizer.getMessage("lblChooseOrderCardsPutIntoSchemeDeck"), localizer.getMessage("lblClosestToTop"), choices, null); break; case Stack: - choices = getGui().order(localizer.getMessage("lblChooseOrderCopiesCast"), localizer.getMessage("lblPutFirst"), CardView.getCollection(cards), - null); + choices = getGui().order(localizer.getMessage("lblChooseOrderCopiesCast"), localizer.getMessage("lblPutFirst"), choices, null); break; default: System.out.println("ZoneType " + destinationZone + " - Not Ordered"); @@ -915,7 +933,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return cards; } endTempShowCards(); - return game.getCardList(choices); + CardCollection result = new CardCollection(); + gameCacheMove.addToList(choices, result); + return result; } @Override @@ -923,10 +943,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont final CardCollection valid, final int min, final int max) { if (p != player) { tempShowCards(valid); - final CardCollection choices = game - .getCardList(getGui().many(String.format(localizer.getMessage("lblChooseMinCardToDiscard"), min), - localizer.getMessage("lblDiscarded"), min, min, CardView.getCollection(valid), null)); + GameEntityViewMap gameCacheDiscard = GameEntityView.getMap(valid); + List views = getGui().many(String.format(localizer.getMessage("lblChooseMinCardToDiscard"), min), + localizer.getMessage("lblDiscarded"), min, min, gameCacheDiscard.getTrackableKeys(), null); endTempShowCards(); + final CardCollection choices = new CardCollection(); + gameCacheDiscard.addToList(views, choices); return choices; } @@ -949,25 +971,26 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont cntChoice.add(Integer.valueOf(i)); } final int chosenAmount = getGui().one(localizer.getMessage("lblDelveHowManyCards"), cntChoice.build()).intValue(); - for (int i = 0; i < chosenAmount; i++) { - final CardView nowChosen = getGui().oneOrNone(localizer.getMessage("lblExileWhichCard", String.valueOf(i + 1), String.valueOf(chosenAmount)), CardView.getCollection(grave)); - if (nowChosen == null) { + GameEntityViewMap gameCacheGrave = GameEntityView.getMap(grave); + for (int i = 0; i < chosenAmount; i++) { + String title = localizer.getMessage("lblExileWhichCard", String.valueOf(i + 1), String.valueOf(chosenAmount)); + final CardView nowChosen = getGui().oneOrNone(title, gameCacheGrave.getTrackableKeys()); + + if (nowChosen == null || !gameCacheGrave.containsKey(nowChosen)) { // User canceled,abort delving. toExile.clear(); break; } - final Card card = game.getCard(nowChosen); - grave.remove(card); - toExile.add(card); + toExile.add(gameCacheGrave.remove(nowChosen)); } return toExile; } /* * (non-Javadoc) - * + * * @see * forge.game.player.PlayerController#chooseTargets(forge.card.spellability. * SpellAbility, forge.card.spellability.SpellAbilityStackInstance) @@ -991,7 +1014,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see * forge.game.player.PlayerController#chooseCardsToDiscardUnlessType(int, * java.lang.String, forge.card.spellability.SpellAbility) @@ -1020,7 +1043,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see * forge.game.player.PlayerController#chooseManaFromPool(java.util.List) */ @@ -1038,7 +1061,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.game.player.PlayerController#chooseSomeType(java.lang.String, * java.lang.String, java.util.List, java.util.List, java.lang.String) */ @@ -1185,7 +1208,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see * forge.game.player.PlayerController#confirmReplacementEffect(forge.card. * replacement.ReplacementEffect, forge.card.spellability.SpellAbility, @@ -1355,6 +1378,8 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont @Override public List chooseSaToActivateFromOpeningHand(final List usableFromOpeningHand) { + + final CardCollection srcCards = new CardCollection(); for (final SpellAbility sa : usableFromOpeningHand) { srcCards.add(sa.getHostCard()); @@ -1363,10 +1388,15 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (srcCards.isEmpty()) { return result; } + GameEntityViewMap gameCacheOpenHand = GameEntityView.getMap(srcCards); + final List chosen = getGui().many(localizer.getMessage("lblChooseCardsActivateOpeningHandandOrder"), localizer.getMessage("lblActivateFirst"), -1, CardView.getCollection(srcCards), null); for (final CardView view : chosen) { - final Card c = game.getCard(view); + if (!gameCacheOpenHand.containsKey(view)) { + continue; + } + final Card c = gameCacheOpenHand.get(view); for (final SpellAbility sa : usableFromOpeningHand) { if (sa.getHostCard() == c) { result.add(sa); @@ -1474,7 +1504,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.game.player.PlayerController#chooseModeForAbility(forge.card. * spellability.SpellAbility, java.util.List, int, int) */ @@ -1983,7 +2013,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#setCanPlayUnlimitedLands(boolean) */ @Override @@ -1994,7 +2024,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#setViewAllCards(boolean) */ @Override @@ -2007,7 +2037,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#generateMana() */ @Override @@ -2042,7 +2072,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#dumpGameState() */ @Override @@ -2069,7 +2099,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#setupGameState() */ @Override @@ -2109,7 +2139,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#tutorForCard() */ @Override @@ -2140,7 +2170,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#addCountersToPermanent() */ @Override @@ -2160,12 +2190,14 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont public void modifyCountersOnPermanent(boolean subtract) { final String titleMsg = subtract ? localizer.getMessage("lblRemoveCountersFromWhichCard") : localizer.getMessage("lblAddCountersToWhichCard"); - final CardCollectionView cards = game.getCardsIn(ZoneType.Battlefield); - final Card card = game - .getCard(getGui().oneOrNone(titleMsg, CardView.getCollection(cards))); - if (card == null) { + + GameEntityViewMap gameCacheCounters = GameEntityView.getMap(game.getCardsIn(ZoneType.Battlefield)); + + final CardView cv = getGui().oneOrNone(titleMsg, gameCacheCounters.getTrackableKeys()); + if (cv == null || !gameCacheCounters.containsKey(cv)) { return; } + final Card card = gameCacheCounters.get(cv); final ImmutableList counters = subtract ? ImmutableList.copyOf(card.getCounters().keySet()) : CounterType.values; @@ -2190,7 +2222,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#tapPermanents() */ @Override @@ -2216,7 +2248,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#untapPermanents() */ @Override @@ -2242,16 +2274,19 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#setPlayerLife() */ @Override public void setPlayerLife() { - final Player player = game.getPlayer( - getGui().oneOrNone(localizer.getMessage("lblSetLifeforWhichPlayer"), PlayerView.getCollection(game.getPlayers()))); - if (player == null) { + + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(game.getPlayers()); + + final PlayerView pv = getGui().oneOrNone(localizer.getMessage("lblSetLifeforWhichPlayer"), gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return; } + final Player player = gameCachePlayer.get(pv); final Integer life = getGui().getInteger(localizer.getMessage("lblSetLifetoWhat"), 0); if (life == null) { @@ -2263,7 +2298,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#winGame() */ @Override @@ -2289,7 +2324,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#addCardToHand() */ @Override @@ -2375,12 +2410,21 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } } - final Player p = repeatLast ? lastAddedPlayer - : game.getPlayer(getGui().oneOrNone(message, - PlayerView.getCollection(game.getPlayers()))); - if (p == null) { - return; + Player pOld = lastAddedPlayer; + if (repeatLast) { + if (pOld == null) { + return; + } + } else { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(game.getPlayers()); + PlayerView pv = getGui().oneOrNone(message, gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { + return; + } + pOld = gameCachePlayer.get(pv); } + final Player p = pOld; + final CardDb carddb = FModel.getMagicDb().getCommonCards(); final List faces = Lists.newArrayList(carddb.getAllFaces()); @@ -2478,70 +2522,76 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#exileCardsFromHand() */ @Override public void exileCardsFromHand() { - final Player p = game.getPlayer(getGui().oneOrNone(localizer.getMessage("lblExileCardsFromPlayerHandConfirm"), - PlayerView.getCollection(game.getPlayers()))); - if (p == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(game.getPlayers()); + + final PlayerView pv = getGui().oneOrNone(localizer.getMessage("lblExileCardsFromPlayerHandConfirm"), + gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return; } + Player p = gameCachePlayer.get(pv); - final CardCollection selection; + GameEntityViewMap gameCacheExile = GameEntityView.getMap(p.getCardsIn(ZoneType.Hand)); - CardCollectionView cardsInHand = p.getCardsIn(ZoneType.Hand); - selection = game.getCardList(getGui().many(localizer.getMessage("lblChooseCardsExile"), localizer.getMessage("lblDiscarded"), 0, -1, - CardView.getCollection(cardsInHand), null)); + List views = getGui().many(localizer.getMessage("lblChooseCardsExile"), localizer.getMessage("lblDiscarded"), 0, -1, + gameCacheExile.getTrackableKeys(), null); - if (selection != null && selection.size() > 0) { - for (Card c : selection) { - if (c == null) { - continue; - } - if (game.getAction().moveTo(ZoneType.Exile, c, null) != null) { - StringBuilder sb = new StringBuilder(); - sb.append(p).append(" exiles ").append(c).append(" due to Dev Cheats."); - game.getGameLog().add(GameLogEntryType.DISCARD, sb.toString()); - } else { - game.getGameLog().add(GameLogEntryType.INFORMATION, "DISCARD CHEAT ERROR"); - } + final CardCollection selection = new CardCollection(); + gameCacheExile.addToList(views, selection); + + for (Card c : selection) { + if (c == null) { + continue; + } + if (game.getAction().moveTo(ZoneType.Exile, c, null) != null) { + StringBuilder sb = new StringBuilder(); + sb.append(p).append(" exiles ").append(c).append(" due to Dev Cheats."); + game.getGameLog().add(GameLogEntryType.DISCARD, sb.toString()); + } else { + game.getGameLog().add(GameLogEntryType.INFORMATION, "DISCARD CHEAT ERROR"); } } } /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#exileCardsFromBattlefield() */ @Override public void exileCardsFromBattlefield() { - final Player p = game.getPlayer(getGui().oneOrNone(localizer.getMessage("lblExileCardsFromPlayerBattlefieldConfirm"), - PlayerView.getCollection(game.getPlayers()))); - if (p == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(game.getPlayers()); + + final PlayerView pv = getGui().oneOrNone(localizer.getMessage("lblExileCardsFromPlayerBattlefieldConfirm"), + gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return; } + Player p = gameCachePlayer.get(pv); - final CardCollection selection; + GameEntityViewMap gameCacheExile = GameEntityView.getMap(p.getCardsIn(ZoneType.Battlefield)); - CardCollectionView cardsInPlay = p.getCardsIn(ZoneType.Battlefield); - selection = game.getCardList(getGui().many(localizer.getMessage("lblChooseCardsExile"), localizer.getMessage("lblDiscarded"), 0, -1, - CardView.getCollection(cardsInPlay), null)); + List views = getGui().many(localizer.getMessage("lblChooseCardsExile"), localizer.getMessage("lblDiscarded"), 0, -1, + gameCacheExile.getTrackableKeys(), null); - if (selection != null && selection.size() > 0) { - for (Card c : selection) { - if (c == null) { - continue; - } - if (game.getAction().moveTo(ZoneType.Exile, c, null) != null) { - StringBuilder sb = new StringBuilder(); - sb.append(p).append(" exiles ").append(c).append(" due to Dev Cheats."); - game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString()); - } else { - game.getGameLog().add(GameLogEntryType.INFORMATION, "EXILE FROM PLAY CHEAT ERROR"); - } + final CardCollection selection = new CardCollection(); + gameCacheExile.addToList(views, selection); + + for (Card c : selection) { + if (c == null) { + continue; + } + if (game.getAction().moveTo(ZoneType.Exile, c, null) != null) { + StringBuilder sb = new StringBuilder(); + sb.append(p).append(" exiles ").append(c).append(" due to Dev Cheats."); + game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString()); + } else { + game.getGameLog().add(GameLogEntryType.INFORMATION, "EXILE FROM PLAY CHEAT ERROR"); } } } @@ -2553,48 +2603,53 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont */ @Override public void removeCardsFromGame() { - final Player p = game.getPlayer(getGui().oneOrNone(localizer.getMessage("lblRemoveCardBelongingWitchPlayer"), - PlayerView.getCollection(game.getPlayers()))); - if (p == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(game.getPlayers()); + + final PlayerView pv = getGui().oneOrNone(localizer.getMessage("lblRemoveCardBelongingWitchPlayer"), + gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return; } + Player p = gameCachePlayer.get(pv); final String zone = getGui().one(localizer.getMessage("lblRemoveCardFromWhichZone"), Arrays.asList("Hand", "Battlefield", "Library", "Graveyard", "Exile")); - final CardCollection selection; - CardCollectionView cards = p.getCardsIn(ZoneType.smartValueOf(zone)); - selection = game.getCardList(getGui().many(localizer.getMessage("lblChooseCardsRemoveFromGame"), localizer.getMessage("lblRemoved"), 0, -1, - CardView.getCollection(cards), null)); + GameEntityViewMap gameCacheExile = GameEntityView.getMap(cards); + List views = getGui().many(localizer.getMessage("lblChooseCardsRemoveFromGame"), localizer.getMessage("lblRemoved"), 0, -1, + gameCacheExile.getTrackableKeys(), null); - if (selection != null && selection.size() > 0) { - for (Card c : selection) { - if (c == null) { - continue; - } - c.getZone().remove(c); - c.ceaseToExist(); + final CardCollection selection = new CardCollection(); + gameCacheExile.addToList(views, selection); - StringBuilder sb = new StringBuilder(); - sb.append(p).append(" removes ").append(c).append(" from game due to Dev Cheats."); - game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString()); + for (Card c : selection) { + if (c == null) { + continue; } + c.getZone().remove(c); + c.ceaseToExist(); + + StringBuilder sb = new StringBuilder(); + sb.append(p).append(" removes ").append(c).append(" from game due to Dev Cheats."); + game.getGameLog().add(GameLogEntryType.ZONE_CHANGE, sb.toString()); } } /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#riggedPlanarRoll() */ @Override public void riggedPlanarRoll() { - final Player player = game.getPlayer( - getGui().oneOrNone(localizer.getMessage("lblWhichPlayerShouldRoll"), PlayerView.getCollection(game.getPlayers()))); - if (player == null) { + GameEntityViewMap gameCachePlayer = GameEntityView.getMap(game.getPlayers()); + + final PlayerView pv = getGui().oneOrNone(localizer.getMessage("lblWhichPlayerShouldRoll"), gameCachePlayer.getTrackableKeys()); + if (pv == null || !gameCachePlayer.containsKey(pv)) { return; } + final Player player = gameCachePlayer.get(pv); final PlanarDice res = getGui().oneOrNone(localizer.getMessage("lblChooseResult"), PlanarDice.values); if (res == null) { @@ -2613,7 +2668,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont /* * (non-Javadoc) - * + * * @see forge.player.IDevModeCheats#planeswalkTo() */ @Override @@ -2868,7 +2923,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont @Override public void reorderHand(final CardView card, final int index) { final PlayerZone hand = player.getZone(ZoneType.Hand); - hand.reorder(game.getCard(card), index); + hand.reorder(getCard(card), index); player.updateZoneForView(hand); } @@ -2880,30 +2935,25 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont @Override public List chooseCardsForSplice(SpellAbility sa, List cards) { - HashMap mapCVtoC = new HashMap<>(); - for (Card card : cards) { - mapCVtoC.put(card.getView(), card); - } - List choices = new ArrayList<>(mapCVtoC.keySet()); - List chosen; - chosen = getGui().many( + GameEntityViewMap gameCacheSplice = GameEntityView.getMap(cards); + + List chosen = getGui().many( localizer.getMessage("lblChooseCardstoSpliceonto"), localizer.getMessage("lblChosenCards"), 0, - choices.size(), - choices, + gameCacheSplice.size(), + gameCacheSplice.getTrackableKeys(), sa.getHostCard().getView() ); - List chosenCards = new ArrayList<>(); - for (CardView cardView : chosen) { - chosenCards.add(mapCVtoC.get(cardView)); - } + + List chosenCards = new CardCollection(); + gameCacheSplice.addToList(chosen, chosenCards); return chosenCards; } /* * (non-Javadoc) - * + * * @see forge.game.player.PlayerController#chooseOptionalCosts(forge.game. * spellability.SpellAbility, java.util.List) */ @@ -2942,5 +2992,25 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return result; } + public Card getCard(final CardView cardView) { + if (gameCache.containsKey(cardView)) { + return gameCache.get(cardView); + } + + final Card c = getGame().findById(cardView.getId()); + gameCache.put(cardView, c); + return c; + } + + public CardCollection getCardList(Iterable cardViews) { + CardCollection result = new CardCollection(); + for(CardView cardView : cardViews){ + final Card c = this.getCard(cardView); + if (c != null) { + result.add(c); + } + } + return result; + } } diff --git a/forge-gui/src/main/java/forge/player/TargetSelection.java b/forge-gui/src/main/java/forge/player/TargetSelection.java index 914f22e0916..dc4c7a7a418 100644 --- a/forge-gui/src/main/java/forge/player/TargetSelection.java +++ b/forge-gui/src/main/java/forge/player/TargetSelection.java @@ -6,12 +6,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -21,6 +21,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.game.Game; import forge.game.GameEntity; +import forge.game.GameEntityView; +import forge.game.GameEntityViewMap; import forge.game.GameObject; import forge.game.card.Card; import forge.game.card.CardUtil; @@ -44,7 +46,7 @@ import java.util.Map; *

* Target_Selection class. *

- * + * * @author Forge * @version $Id: TargetSelection.java 25148 2014-03-12 08:28:52Z swordshine $ */ @@ -69,8 +71,8 @@ public class TargetSelection { if (!canTarget) { throw new RuntimeException("TargetSelection.chooseTargets called for ability that does not target - " + ability); } - - // Number of targets is explicitly set only if spell is being redirected (ex. Swerve or Redirect) + + // Number of targets is explicitly set only if spell is being redirected (ex. Swerve or Redirect) final int minTargets = numTargets != null ? numTargets.intValue() : tgt.getMinTargets(ability.getHostCard(), ability); final int maxTargets = numTargets != null ? numTargets.intValue() : tgt.getMaxTargets(ability.getHostCard(), ability); //final int maxTotalCMC = tgt.getMaxTotalCMC(ability.getHostCard(), ability); @@ -99,10 +101,10 @@ public class TargetSelection { // Mandatory target selection, that has no candidates but enough targets (Min == 0, but no choices) return true; } - + final List zones = tgt.getZone(); final boolean mandatory = tgt.getMandatory() && hasCandidates; - + final boolean choiceResult; final boolean random = tgt.isRandomTarget(); if (random) { @@ -167,7 +169,7 @@ public class TargetSelection { choiceResult = this.chooseCardFromList(validTargets, true, mandatory); } } - // some inputs choose cards one-by-one and need to be called again + // some inputs choose cards one-by-one and need to be called again return choiceResult && chooseTargets(numTargets); } @@ -175,6 +177,8 @@ public class TargetSelection { // Send in a list of valid cards, and popup a choice box to target final Game game = ability.getActivatingPlayer().getGame(); + GameEntityViewMap gameCacheChooseCard = GameEntityView.getMap(choices); + final List crdsBattle = Lists.newArrayList(); final List crdsExile = Lists.newArrayList(); final List crdsGrave = Lists.newArrayList(); @@ -228,7 +232,7 @@ public class TargetSelection { // is there a more elegant way of doing this? choicesFiltered.add(msgDone); } - + Object chosen = null; if (!choices.isEmpty() && mandatory) { chosen = controller.getGui().one(getTgt().getVTSelection(), choicesFiltered); @@ -245,7 +249,10 @@ public class TargetSelection { } if (chosen instanceof CardView) { - ability.getTargets().add(game.getCard((CardView) chosen)); + if (!gameCacheChooseCard.containsKey(chosen)) { + return false; + } + ability.getTargets().add(gameCacheChooseCard.get((CardView) chosen)); } return true; } @@ -292,7 +299,7 @@ public class TargetSelection { if (madeChoice instanceof StackItemView) { ability.getTargets().add(stackItemViewCache.get(madeChoice).getSpellAbility(true)); } - else {// 'FINISH TARGETING' chosen + else {// 'FINISH TARGETING' chosen bTargetingDone = true; } }