mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
- Added War Cadence
- new preference for enabling/disabling the prompt for block costs of 0.
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -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/wanderwine_prophets.txt svneol=native#text/plain
|
||||||
res/cardsfolder/w/waning_wurm.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_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_chariot.txt svneol=native#text/plain
|
||||||
res/cardsfolder/w/war_dance.txt svneol=native#text/plain
|
res/cardsfolder/w/war_dance.txt svneol=native#text/plain
|
||||||
res/cardsfolder/w/war_elemental.txt -text
|
res/cardsfolder/w/war_elemental.txt -text
|
||||||
|
|||||||
13
res/cardsfolder/w/war_cadence.txt
Normal file
13
res/cardsfolder/w/war_cadence.txt
Normal file
@@ -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.
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
package forge.card.ability.ai;
|
package forge.card.ability.ai;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import forge.Card;
|
import forge.Card;
|
||||||
|
import forge.CardLists;
|
||||||
|
import forge.CardPredicates.Presets;
|
||||||
import forge.card.ability.SpellAbilityAi;
|
import forge.card.ability.SpellAbilityAi;
|
||||||
import forge.card.spellability.SpellAbility;
|
import forge.card.spellability.SpellAbility;
|
||||||
|
import forge.game.Game;
|
||||||
import forge.game.ai.ComputerUtil;
|
import forge.game.ai.ComputerUtil;
|
||||||
import forge.game.ai.ComputerUtilCombat;
|
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;
|
import forge.game.player.Player;
|
||||||
|
|
||||||
public class StoreSVarAi extends SpellAbilityAi {
|
public class StoreSVarAi extends SpellAbilityAi {
|
||||||
@@ -14,7 +25,50 @@ public class StoreSVarAi extends SpellAbilityAi {
|
|||||||
//Tree of Redemption
|
//Tree of Redemption
|
||||||
|
|
||||||
final Card source = sa.getSourceCard();
|
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<Card> possibleAttackers = ai.getCreaturesInPlay();
|
||||||
|
List<Card> possibleBlockers = opp.getCreaturesInPlay();
|
||||||
|
possibleBlockers = CardLists.filter(possibleBlockers, Presets.UNTAPPED);
|
||||||
|
int oppLife = opp.getLife();
|
||||||
|
int potentialDmg = 0;
|
||||||
|
List<Card> currentAttackers = new ArrayList<Card>();
|
||||||
|
|
||||||
|
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()))) {
|
|| (ai.getLife() > 5 && !ComputerUtilCombat.lifeInSeriousDanger(ai, ai.getGame().getCombat()))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -664,19 +664,25 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
|
|
||||||
private static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) {
|
private static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) {
|
||||||
Cost blockCost = new Cost(ManaCost.ZERO, true);
|
Cost blockCost = new Cost(ManaCost.ZERO, true);
|
||||||
|
boolean hasBlockCost = false;
|
||||||
// Sort abilities to apply them in proper order
|
// Sort abilities to apply them in proper order
|
||||||
for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
|
List<ZoneType> checkZones = ZoneType.listValueOf("Battlefield,Command");
|
||||||
|
for (Card card : game.getCardsIn(checkZones)) {
|
||||||
final ArrayList<StaticAbility> staticAbilities = card.getStaticAbilities();
|
final ArrayList<StaticAbility> staticAbilities = card.getStaticAbilities();
|
||||||
for (final StaticAbility stAb : staticAbilities) {
|
for (final StaticAbility stAb : staticAbilities) {
|
||||||
Cost c1 = stAb.getBlockCost(blocker, attacker);
|
Cost c1 = stAb.getBlockCost(blocker, attacker);
|
||||||
if ( c1 != null )
|
if ( c1 != null ) {
|
||||||
blockCost.add(c1);
|
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) {
|
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;
|
return hasPaid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,6 +306,12 @@ public class HumanPlay {
|
|||||||
if (parts.isEmpty() || costPart.getAmount().equals("0")) {
|
if (parts.isEmpty() || costPart.getAmount().equals("0")) {
|
||||||
return GuiDialog.confirm(source, "Do you want to pay 0?" + orString);
|
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
|
//the following costs do not need inputs
|
||||||
for (CostPart part : parts) {
|
for (CostPart part : parts) {
|
||||||
|
|||||||
@@ -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.getCbOverlayCardManaCost(), FPref.UI_OVERLAY_CARD_MANA_COST));
|
||||||
lstControls.add(Pair.of(view.getCbShowMatchBackgroundImage(), FPref.UI_MATCH_IMAGE_VISIBLE));
|
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.getCbUseThemedComboBox(), FPref.UI_THEMED_COMBOBOX));
|
||||||
|
lstControls.add(Pair.of(view.getCbPromptFreeBlocks(), FPref.MATCHPREF_PROMPT_FREE_BLOCKS));
|
||||||
|
|
||||||
for(final Pair<JCheckBox, FPref> kv : lstControls) {
|
for(final Pair<JCheckBox, FPref> kv : lstControls) {
|
||||||
kv.getKey().addItemListener(new ItemListener() {
|
kv.getKey().addItemListener(new ItemListener() {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
|||||||
private final JCheckBox cbCompactMainMenu = new OptionsCheckBox("Use Compact Main Sidebar Menu");
|
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 cbUseThemedComboBox = new OptionsCheckBox("Themed ComboBox");
|
||||||
|
private final JCheckBox cbPromptFreeBlocks = new OptionsCheckBox("Free Block Handling");
|
||||||
|
|
||||||
private final Map<FPref, KeyboardShortcutField> shortcutFields = new HashMap<FPref, KeyboardShortcutField>();
|
private final Map<FPref, KeyboardShortcutField> shortcutFields = new HashMap<FPref, KeyboardShortcutField>();
|
||||||
|
|
||||||
@@ -147,6 +148,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
|||||||
pnlPrefs.add(cbCloneImgSource, regularConstraints);
|
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(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
|
// Deck building options
|
||||||
pnlPrefs.add(new SectionLabel("Random Deck Generation"), sectionConstraints);
|
pnlPrefs.add(new SectionLabel("Random Deck Generation"), sectionConstraints);
|
||||||
|
|
||||||
@@ -489,6 +493,11 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
|||||||
return cbCloneImgSource;
|
return cbCloneImgSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return {@link javax.swing.JCheckBox} */
|
||||||
|
public JCheckBox getCbPromptFreeBlocks() {
|
||||||
|
return cbPromptFreeBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return {@link javax.swing.JCheckBox} */
|
/** @return {@link javax.swing.JCheckBox} */
|
||||||
public JCheckBox getCbEnableSounds() {
|
public JCheckBox getCbEnableSounds() {
|
||||||
return cbEnableSounds;
|
return cbEnableSounds;
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
|
|||||||
|
|
||||||
UI_FOR_TOUCHSCREN("false"),
|
UI_FOR_TOUCHSCREN("false"),
|
||||||
|
|
||||||
|
MATCHPREF_PROMPT_FREE_BLOCKS("false"),
|
||||||
|
|
||||||
SUBMENU_CURRENTMENU (EMenuItem.CONSTRUCTED.toString()),
|
SUBMENU_CURRENTMENU (EMenuItem.CONSTRUCTED.toString()),
|
||||||
SUBMENU_SANCTIONED ("true"),
|
SUBMENU_SANCTIONED ("true"),
|
||||||
SUBMENU_GAUNTLET ("false"),
|
SUBMENU_GAUNTLET ("false"),
|
||||||
|
|||||||
Reference in New Issue
Block a user