From c78689db892e0650ad5e88c3048bc69b6bfb4a02 Mon Sep 17 00:00:00 2001 From: moomarc Date: Tue, 6 Aug 2013 17:39:37 +0000 Subject: [PATCH] - Added War Cadence - new preference for enabling/disabling the prompt for block costs of 0. --- .gitattributes | 1 + res/cardsfolder/w/war_cadence.txt | 13 +++++ .../forge/card/ability/ai/StoreSVarAi.java | 56 ++++++++++++++++++- .../java/forge/game/phase/PhaseHandler.java | 16 ++++-- .../java/forge/game/player/HumanPlay.java | 8 ++- .../home/settings/CSubmenuPreferences.java | 1 + .../home/settings/VSubmenuPreferences.java | 13 ++++- .../forge/properties/ForgePreferences.java | 2 + 8 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 res/cardsfolder/w/war_cadence.txt diff --git a/.gitattributes b/.gitattributes index d1a38a499e3..06ae8de7680 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12542,6 +12542,7 @@ res/cardsfolder/w/wanderwine_hub.txt svneol=native#text/plain res/cardsfolder/w/wanderwine_prophets.txt svneol=native#text/plain res/cardsfolder/w/waning_wurm.txt svneol=native#text/plain res/cardsfolder/w/war_barge.txt -text svneol=unset#text/plain +res/cardsfolder/w/war_cadence.txt -text res/cardsfolder/w/war_chariot.txt svneol=native#text/plain res/cardsfolder/w/war_dance.txt svneol=native#text/plain res/cardsfolder/w/war_elemental.txt -text diff --git a/res/cardsfolder/w/war_cadence.txt b/res/cardsfolder/w/war_cadence.txt new file mode 100644 index 00000000000..d20cdb56e87 --- /dev/null +++ b/res/cardsfolder/w/war_cadence.txt @@ -0,0 +1,13 @@ +Name:War Cadence +ManaCost:2 R +Types:Enchantment +A:AB$ StoreSVar | Cost$ X R | SVar$ PaidNum | Type$ Count | Expression$ xPaid | SubAbility$ CadenceEffect | AILogic$ RestrictBlocking | SpellDescription$ This turn, creatures can't block unless their controller pays X for each blocking creature he or she controls. +SVar:CadenceEffect:DB$ Effect | StaticAbilities$ CadenceStaticAb | SVars$ PaidNum | Stackable$ False | RememberObjects$ Valid Creature.blocking +SVar:CadenceStaticAb:Mode$ CantBlockUnless | ValidCard$ Card.IsNotRemembered | Cost$ PaidNum | References$ PaidNum | EffectZone$ Command | Description$ This turn, creatures can't block unless their controller pays X for each blocking creature he or she controls. +# According to the 10/4/2004 ruling: The ability only applies to blocks declared after it resolves. It will not add costs to any blockers already announced. +SVar:X:Count$xPaid +SVar:PaidNum:Number$0 +SVar:NonStackingEffect:True +SVar:RemAIDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/war_cadence.jpg +Oracle:{X}{R}: This turn, creatures can't block unless their controller pays {X} for each blocking creature he or she controls. diff --git a/src/main/java/forge/card/ability/ai/StoreSVarAi.java b/src/main/java/forge/card/ability/ai/StoreSVarAi.java index 9f40a8d4484..558018e8677 100644 --- a/src/main/java/forge/card/ability/ai/StoreSVarAi.java +++ b/src/main/java/forge/card/ability/ai/StoreSVarAi.java @@ -1,10 +1,21 @@ package forge.card.ability.ai; +import java.util.ArrayList; +import java.util.List; + import forge.Card; +import forge.CardLists; +import forge.CardPredicates.Presets; import forge.card.ability.SpellAbilityAi; import forge.card.spellability.SpellAbility; +import forge.game.Game; import forge.game.ai.ComputerUtil; import forge.game.ai.ComputerUtilCombat; +import forge.game.ai.ComputerUtilMana; +import forge.game.combat.Combat; +import forge.game.combat.CombatUtil; +import forge.game.phase.PhaseHandler; +import forge.game.phase.PhaseType; import forge.game.player.Player; public class StoreSVarAi extends SpellAbilityAi { @@ -14,7 +25,50 @@ public class StoreSVarAi extends SpellAbilityAi { //Tree of Redemption final Card source = sa.getSourceCard(); - if (ComputerUtil.waitForBlocking(sa) || ai.getLife() + 1 >= source.getNetDefense() + final Game game = ai.getGame(); + final Combat combat = game.getCombat(); + final PhaseHandler ph = game.getPhaseHandler(); + final Player opp = ai.getOpponents().get(0); + + if (sa.hasParam("AILogic")) { + if (sa.getPayCosts().getTotalMana().countX() > 0 && source.getSVar("X").equals("Count$xPaid")) { + // Set PayX here to half the remaining mana to allow for Main 2 and other combat shenanigans. + final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai) / 2; + if (xPay == 0) { return false; } + source.setSVar("PayX", Integer.toString(xPay)); + } + + final String logic = sa.getParam("AILogic"); + if (logic.equals("RestrictBlocking")) { + if (!ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_BEGIN) + || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) { + return false; + } + + List possibleAttackers = ai.getCreaturesInPlay(); + List possibleBlockers = opp.getCreaturesInPlay(); + possibleBlockers = CardLists.filter(possibleBlockers, Presets.UNTAPPED); + int oppLife = opp.getLife(); + int potentialDmg = 0; + List currentAttackers = new ArrayList(); + + if (possibleBlockers.size() == 0) { return false; } + + for (final Card creat : possibleAttackers) { + if (CombatUtil.canAttack(creat, opp) && possibleBlockers.size() > 1) { + potentialDmg += creat.getCurrentPower(); + if (potentialDmg >= oppLife) { return true; } + } + if (combat != null && combat.isAttacking(creat)) { + currentAttackers.add(creat); + } + } + + return currentAttackers.size() > possibleBlockers.size(); + } + return false; + } + else if (ComputerUtil.waitForBlocking(sa) || ai.getLife() + 1 >= source.getNetDefense() || (ai.getLife() > 5 && !ComputerUtilCombat.lifeInSeriousDanger(ai, ai.getGame().getCombat()))) { return false; } diff --git a/src/main/java/forge/game/phase/PhaseHandler.java b/src/main/java/forge/game/phase/PhaseHandler.java index 63dc85c794c..3693294f693 100644 --- a/src/main/java/forge/game/phase/PhaseHandler.java +++ b/src/main/java/forge/game/phase/PhaseHandler.java @@ -664,19 +664,25 @@ public class PhaseHandler implements java.io.Serializable { private static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) { Cost blockCost = new Cost(ManaCost.ZERO, true); + boolean hasBlockCost = false; // Sort abilities to apply them in proper order - for (Card card : game.getCardsIn(ZoneType.Battlefield)) { + List checkZones = ZoneType.listValueOf("Battlefield,Command"); + for (Card card : game.getCardsIn(checkZones)) { final ArrayList staticAbilities = card.getStaticAbilities(); for (final StaticAbility stAb : staticAbilities) { Cost c1 = stAb.getBlockCost(blocker, attacker); - if ( c1 != null ) + if ( c1 != null ) { blockCost.add(c1); + hasBlockCost = true; + } } } - - boolean hasPaid = blockCost.getTotalMana().isZero() && blockCost.isOnlyManaCost(); // true if needless to pay + + boolean hasPaid = blockCost.getTotalMana().isZero() && blockCost.isOnlyManaCost() && (!hasBlockCost + || Singletons.getModel().getPreferences().getPrefBoolean(FPref.MATCHPREF_PROMPT_FREE_BLOCKS)); // true if needless to pay + if (!hasPaid) { - hasPaid = blocker.getController().getController().payManaOptional(blocker, blockCost, null, "Pay cost to declare " + blocker + " a blocker", ManaPaymentPurpose.DeclareBlocker); + hasPaid = blocker.getController().getController().payManaOptional(blocker, blockCost, null, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker); } return hasPaid; } diff --git a/src/main/java/forge/game/player/HumanPlay.java b/src/main/java/forge/game/player/HumanPlay.java index e68f58b56f2..ddcd6a82159 100644 --- a/src/main/java/forge/game/player/HumanPlay.java +++ b/src/main/java/forge/game/player/HumanPlay.java @@ -302,10 +302,16 @@ public class HumanPlay { costPart = parts.get(0); } final String orString = prompt != null ? "" : " (or: " + sourceAbility.getStackDescription() + ")"; - + if (parts.isEmpty() || costPart.getAmount().equals("0")) { return GuiDialog.confirm(source, "Do you want to pay 0?" + orString); } + // 0 mana costs were slipping through because CostPart.getAmount returns 1 + else if (costPart instanceof CostPartMana ) { + if (((CostPartMana) costPart).getManaToPay().isZero()) { + return GuiDialog.confirm(source, "Do you want to pay 0?" + orString); + } + } //the following costs do not need inputs for (CostPart part : parts) { diff --git a/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java b/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java index 36090d1270e..f81e444ff1a 100644 --- a/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java +++ b/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java @@ -86,6 +86,7 @@ public enum CSubmenuPreferences implements ICDoc { lstControls.add(Pair.of(view.getCbOverlayCardManaCost(), FPref.UI_OVERLAY_CARD_MANA_COST)); lstControls.add(Pair.of(view.getCbShowMatchBackgroundImage(), FPref.UI_MATCH_IMAGE_VISIBLE)); lstControls.add(Pair.of(view.getCbUseThemedComboBox(), FPref.UI_THEMED_COMBOBOX)); + lstControls.add(Pair.of(view.getCbPromptFreeBlocks(), FPref.MATCHPREF_PROMPT_FREE_BLOCKS)); for(final Pair kv : lstControls) { kv.getKey().addItemListener(new ItemListener() { diff --git a/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java b/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java index c89a326f1d3..caa1cfa0a2f 100644 --- a/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java +++ b/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java @@ -81,8 +81,9 @@ public enum VSubmenuPreferences implements IVSubmenu { private final JCheckBox cbOverlayCardPower = new OptionsCheckBox("Power/Toughness"); private final JCheckBox cbOverlayCardManaCost = new OptionsCheckBox("Mana Cost"); private final JCheckBox cbCompactMainMenu = new OptionsCheckBox("Use Compact Main Sidebar Menu"); - private final JCheckBox cbShowMatchBackgroundImage = new OptionsCheckBox("Show Background Image on Match Screen"); + private final JCheckBox cbShowMatchBackgroundImage = new OptionsCheckBox("Show Background Image on Match Screen"); private final JCheckBox cbUseThemedComboBox = new OptionsCheckBox("Themed ComboBox"); + private final JCheckBox cbPromptFreeBlocks = new OptionsCheckBox("Free Block Handling"); private final Map shortcutFields = new HashMap(); @@ -146,7 +147,10 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbCloneImgSource, regularConstraints); pnlPrefs.add(new NoteLabel("When enabled clones will use their original art instead of the cloned card's art"), regularConstraints); - + + pnlPrefs.add(cbPromptFreeBlocks, regularConstraints); + pnlPrefs.add(new NoteLabel("When enabled, if you would have to pay 0 to block, pay automatically without prompt"), regularConstraints); + // Deck building options pnlPrefs.add(new SectionLabel("Random Deck Generation"), sectionConstraints); @@ -489,6 +493,11 @@ public enum VSubmenuPreferences implements IVSubmenu { return cbCloneImgSource; } + /** @return {@link javax.swing.JCheckBox} */ + public JCheckBox getCbPromptFreeBlocks() { + return cbPromptFreeBlocks; + } + /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbEnableSounds() { return cbEnableSounds; diff --git a/src/main/java/forge/properties/ForgePreferences.java b/src/main/java/forge/properties/ForgePreferences.java index 9aa063bac4f..a76344cf22b 100644 --- a/src/main/java/forge/properties/ForgePreferences.java +++ b/src/main/java/forge/properties/ForgePreferences.java @@ -64,6 +64,8 @@ public class ForgePreferences extends PreferencesStore { UI_FOR_TOUCHSCREN("false"), + MATCHPREF_PROMPT_FREE_BLOCKS("false"), + SUBMENU_CURRENTMENU (EMenuItem.CONSTRUCTED.toString()), SUBMENU_SANCTIONED ("true"), SUBMENU_GAUNTLET ("false"),