diff --git a/res/cards.txt b/res/cards.txt index 6c3addc26ac..858abaa43e3 100644 --- a/res/cards.txt +++ b/res/cards.txt @@ -1,3 +1,41 @@ +Windborn Muse +3 W +Creature Spirit +Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you. +2/3 +Flying + +Ghostly Prison +2 W +Enchantment +Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you. + +Propaganda +2 U +Enchantment +Creatures can't attack you unless their controller pays 2 for each creature he or she controls that's attacking you. + +Peacekeeper +2 W +Creature Human +Creatures can't attack +1/1 +Upkeep:1 W + +Arctic Nishoba +5 G +Creature Cat +When Arctic Nishoba is put into a graveyard from the battlefield, you gain 2 life for each age counter on it. +6/6 +Trample +Cumulative upkeep:GW + +Marsh Viper +3 G +Creature Snake +Whenever Marsh Viper deals damage to an opponent, that player gets two poison counters. +1/2 + Force of Will 3 U U Instant @@ -705,8 +743,9 @@ no text Pit Scorpion 2 B Creature Scorpion -Whenever this creature deals damage to a player, that player gets a poison counter. +no text 1/1 +Whenever this creature deals damage to a player, that player gets a poison counter. Bridge from Below B B B diff --git a/res/gui/display_layout.xml b/res/gui/display_layout.xml index bccfaa3c994..d837cee299c 100644 --- a/res/gui/display_layout.xml +++ b/res/gui/display_layout.xml @@ -339,7 +339,7 @@ 255 533 901 - 145 + 146 @@ -358,7 +358,7 @@ 255 - 678 + 679 901 10 @@ -373,9 +373,9 @@ 255 - 688 + 689 901 - 139 + 138 diff --git a/src/forge/CardFactory.java b/src/forge/CardFactory.java index aeaa640402b..9f831baa4cd 100644 --- a/src/forge/CardFactory.java +++ b/src/forge/CardFactory.java @@ -17904,7 +17904,8 @@ return land.size() > 1 && CardFactoryUtil.AI_isMainPhase(); public void selectButtonCancel() {stop();} public void selectCard(Card c, PlayerZone zone) { - if(CardUtil.getColors(c).contains(Constant.Color.Blue) && zone.is(Constant.Zone.Hand)) + if(CardUtil.getColors(c).contains(Constant.Color.Blue) && zone.is(Constant.Zone.Hand) && + !c.equals(card)) { AllZone.GameAction.removeFromGame(c); String player = card.getController(); diff --git a/src/forge/CardFactoryUtil.java b/src/forge/CardFactoryUtil.java index 7e477deded6..8d69169a0d1 100644 --- a/src/forge/CardFactoryUtil.java +++ b/src/forge/CardFactoryUtil.java @@ -505,8 +505,8 @@ public class CardFactoryUtil { if(choices.contains(card)) { - AllZone.getZone(card).remove(card); - AllZone.GameAction.moveToGraveyard(card); + //AllZone.getZone(card).remove(card); + AllZone.GameAction.sacrifice(card); stop(); } } @@ -3025,7 +3025,6 @@ public class CardFactoryUtil return count; } - public static CardList getFastbonds(String player) { CardList list = new CardList(AllZone.getZone(Constant.Zone.Play, player).getCards()); @@ -3033,6 +3032,28 @@ public class CardFactoryUtil return list; } + //total cost to pay for an attacker c, cards like Propaganda, Ghostly Prison, Collective Restraint, ... + public static String getPropagandaCost(Card c) + { + String s = ""; + + PlayerZone play = AllZone.getZone(Constant.Zone.Play, AllZone.GameAction.getOpponent(c.getController())); + CardList list = new CardList(play.getCards()); + list = list.filter(new CardListFilter() + { + public boolean addCard(Card c) + { + return c.getName().equals("Propaganda") || c.getName().equals("Windborn Muse") || c.getName().equals("Ghostly Prison"); + } + }); + int cost = list.size() * 2; + + s = Integer.toString(cost); + + return s; + } + + //do card1 and card2 share any colors? public static boolean sharesColorWith(Card card1, Card card2) { diff --git a/src/forge/CardFactory_Creatures.java b/src/forge/CardFactory_Creatures.java index bae30351a00..521df7e2fdb 100644 --- a/src/forge/CardFactory_Creatures.java +++ b/src/forge/CardFactory_Creatures.java @@ -19368,7 +19368,7 @@ public class CardFactory_Creatures { }//*************** END ************ END ************************** - //*************** END ************ END ************************** + //*************** START *********** START ************************** else if (cardName.equals("Lockjaw Snapper")) { @@ -19408,8 +19408,34 @@ public class CardFactory_Creatures { } };//command card.addDestroyCommand(destroy); - }//*************** START *********** START ************************** + }//*************** END ************ END ************************** + //*************** START *********** START ************************** + else if (cardName.equals("Arctic Nishoba")) + { + final Ability ability = new Ability(card, "0") + { + public void resolve() + { + int lifeGain = card.getCounters(Counters.AGE) * 2; + AllZone.GameAction.getPlayerLife(card.getController()).addLife(lifeGain); + + } + }; + + Command destroy = new Command() + { + private static final long serialVersionUID = 1863551466234257411L; + + public void execute() + { + ability.setStackDescription(card.getName()+ " - gain 2 life for each age counter on it."); + AllZone.Stack.add(ability); + } + };//command + + card.addDestroyCommand(destroy); + }//*************** END ************ END ************************** // Cards with Cycling abilities // -1 means keyword "Cycling" not found if (shouldCycle(card) != -1) diff --git a/src/forge/Combat.java b/src/forge/Combat.java index 17784ac46b5..1127044583f 100644 --- a/src/forge/Combat.java +++ b/src/forge/Combat.java @@ -145,7 +145,7 @@ public class Combat public void addAttackingDamage(int n) {attackingDamage += n;} public int getAttackingDamage() {return attackingDamage;} - public void addAttacker(Card c) {map.put(c, new CardList()); declaredAttackers++;} + public void addAttacker(Card c) {map.put(c, new CardList()); declaredAttackers++; } public void resetAttackers() {map.clear();} public Card[] getAttackers() { diff --git a/src/forge/CombatUtil.java b/src/forge/CombatUtil.java index 7b593de5b5e..b8888b7dcad 100644 --- a/src/forge/CombatUtil.java +++ b/src/forge/CombatUtil.java @@ -17,11 +17,6 @@ public class CombatUtil && blocker.getKeyword().contains("This creature can block creatures with shadow as though they didn't have shadow.")) return false; - /* - if(attacker.getKeyword().contains("Shadow")) - return blocker.getKeyword().contains("Shadow") || - blocker.getKeyword().contains("This creature can block creatures with shadow as though they didn't have shadow."); - */ if (attacker.getKeyword().contains("Shadow") && !blocker.getKeyword().contains("Shadow") && !blocker.getKeyword().contains("This creature can block creatures with shadow as though they didn't have shadow.") ) return false; @@ -194,13 +189,14 @@ public class CombatUtil public static boolean canAttack(Card c) { + if (isPeaceKeeperInPlay()) + return false; + boolean moatPrevented = false; if (isMoatInPlay() || isMagusMoatInPlay()) { if (!c.getKeyword().contains("Flying")) - { moatPrevented = true; - } } PlayerZone play = AllZone.getZone(Constant.Zone.Play,AllZone.GameAction.getOpponent(c.getController())); @@ -661,6 +657,16 @@ public class CombatUtil return false; } + public static boolean isPeaceKeeperInPlay() + { + CardList all = new CardList(); + all.addAll(AllZone.Human_Play.getCards()); + all.addAll(AllZone.Computer_Play.getCards()); + + all = all.getName("Peacekeeper"); + return all.size() > 0; + } + public static boolean isMoatInPlay() { CardList all = new CardList(); @@ -687,6 +693,68 @@ public class CombatUtil return false; } + public static boolean checkPropagandaEffects(Card c) + { + if (CardFactoryUtil.getPropagandaCost(c).equals("0")) + return true; + + final Card crd = c; + final boolean[] canAttack = new boolean[1]; + canAttack[0] = false; + //if (AllZone.Phase.getPhase().equals(Constant.Phase.Combat_Declare_Attackers)) + if (AllZone.Phase.getPhase().equals("Declare Blockers") || + AllZone.Phase.getPhase().equals(Constant.Phase.Combat_Declare_Attackers_InstantAbility)) + { + //if (!c.getCreatureAttackedThisTurn()) + //{ + String cost = CardFactoryUtil.getPropagandaCost(c); + if (!cost.equals("0")) + { + final Ability ability = new Ability(c, cost) + { + public void resolve() + { + canAttack[0] = true; + } + }; + + final Command unpaidCommand = new Command() { + + private static final long serialVersionUID = -6483405139208343935L; + + public void execute() { + //AllZone.GameAction.sacrifice(c); + canAttack[0] = false; + AllZone.Combat.removeFromCombat(crd); + crd.untap(); + } + }; + + final Command paidCommand = new Command() { + private static final long serialVersionUID = -8303368287601871955L; + + public void execute() { + canAttack[0] = true; + } + }; + + if (c.getController().equals(Constant.Player.Human)) { + AllZone.InputControl.setInput(new Input_PayManaCost_Ability("Propaganda "+ c +"\r\n", ability.getManaCost(), paidCommand, unpaidCommand)); + } + else //computer + { + if (ComputerUtil.canPayCost(ability)) + ComputerUtil.playNoStack(ability); + else + canAttack[0] = false; + } + } + } + //c.setCreatureAttackedThisTurn(true); + //} + return canAttack[0]; + } + public static void checkDeclareAttackers(Card c) //this method checks triggered effects of attacking creatures, right before defending player declares blockers { //human does not have an "attackers_instantAbility" phase during his turn (yet), so triggers will happen at the beginning of declare blockers diff --git a/src/forge/GameActionUtil.java b/src/forge/GameActionUtil.java index 8b4f7e8a2f0..a4a0f55934e 100644 --- a/src/forge/GameActionUtil.java +++ b/src/forge/GameActionUtil.java @@ -4108,9 +4108,11 @@ public class GameActionUtil { if (c.getKeyword().contains("Whenever this creature deals damage to a player, that player gets a poison counter.")) - playerCombatDamage_PoisonCounter(c); + playerCombatDamage_PoisonCounter(c, 1); - if (c.getName().equals("Hypnotic Specter")) + if (c.getName().equals("Marsh Viper")) + playerCombatDamage_PoisonCounter(c, 2); + else if (c.getName().equals("Hypnotic Specter")) playerCombatDamage_Hypnotic_Specter(c); else if (c.getName().equals("Dimir Cutpurse")) playerCombatDamage_Dimir_Cutpurse(c); @@ -4207,15 +4209,15 @@ public class GameActionUtil } */ - private static void playerCombatDamage_PoisonCounter(Card c) + private static void playerCombatDamage_PoisonCounter(Card c, int n) { final String player = c.getController(); final String opponent = AllZone.GameAction.getOpponent(player); if (opponent.equals(Constant.Player.Human)) - AllZone.Human_PoisonCounter.addPoisonCounters(1); + AllZone.Human_PoisonCounter.addPoisonCounters(n); else - AllZone.Computer_PoisonCounter.addPoisonCounters(1); + AllZone.Computer_PoisonCounter.addPoisonCounters(n); } private static void playerCombatDamage_Oros(Card c) @@ -5134,6 +5136,7 @@ public class GameActionUtil + private static void upkeep_AEther_Vial() { final String player = AllZone.Phase.getActivePlayer(); diff --git a/src/forge/InputControl.java b/src/forge/InputControl.java index 3c568fb5df8..0a77118cb1f 100644 --- a/src/forge/InputControl.java +++ b/src/forge/InputControl.java @@ -89,10 +89,12 @@ import java.util.*; CardList list = new CardList(); list.addAll(AllZone.Combat.getAttackers()); list.addAll(AllZone.pwCombat.getAttackers()); + + for (Card c : list) + CombatUtil.checkPropagandaEffects(c); + for (Card c : list) - { CombatUtil.checkDeclareAttackers(c); - } return new Input_Attack_Instant(); } @@ -296,6 +298,10 @@ import java.util.*; CardList list = new CardList(); list.addAll(AllZone.Combat.getAttackers()); list.addAll(AllZone.pwCombat.getAttackers()); + for (Card c : list) + { + CombatUtil.checkPropagandaEffects(c); + } for (Card c : list) { CombatUtil.checkDeclareAttackers(c); diff --git a/src/forge/Input_Attack.java b/src/forge/Input_Attack.java index 212bc422c50..de16bc7ecf7 100644 --- a/src/forge/Input_Attack.java +++ b/src/forge/Input_Attack.java @@ -17,6 +17,7 @@ public void showMessage() Card c = creats.get(i); if (CombatUtil.canAttack(c) && c.getKeyword().contains("This card attacks each turn if able.")) { + AllZone.Combat.addAttacker(c); if (!c.getKeyword().contains("Vigilance")) c.tap(); @@ -62,15 +63,15 @@ public void showMessage() CombatUtil.canAttack(card) ) { + if(! card.getKeyword().contains("Vigilance")) { card.tap(); - //otherwise cards stay untapped, not sure why this is needed but it works AllZone.Human_Play.updateObservers(); } AllZone.Combat.addAttacker(card); - + //for Castle Raptors, since it gets a bonus if untapped for (String effect : AllZone.StateBasedEffects.getStateBasedMap().keySet() ) { Command com = GameActionUtil.commands.get(effect); diff --git a/src/forge/Input_PayManaCost_Ability.java b/src/forge/Input_PayManaCost_Ability.java index b541bc76ed5..ebdaf781085 100644 --- a/src/forge/Input_PayManaCost_Ability.java +++ b/src/forge/Input_PayManaCost_Ability.java @@ -65,9 +65,11 @@ public class Input_PayManaCost_Ability extends Input //tappedLand.clear(); AllZone.ManaPool.paid(); - //Command MUST BE AFTER, for Urborg Syphon-Mage - tap, mana, discard abilit - stopSetNext(new ComputerAI_StackNotEmpty()); + //Command MUST BE AFTER, for Urborg Syphon-Mage - tap, mana, discard ability + //does it really have to be after? paidCommand.execute(); + stopSetNext(new ComputerAI_StackNotEmpty()); + //paidCommand.execute(); } }