diff --git a/.gitattributes b/.gitattributes index b62aecc4c84..5c8c142d36a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -456,6 +456,7 @@ res/cardsfolder/benthic_behemoth.txt -text svneol=native#text/plain res/cardsfolder/benthic_djinn.txt -text svneol=native#text/plain res/cardsfolder/bereavement.txt -text svneol=native#text/plain res/cardsfolder/berserk.txt -text svneol=native#text/plain +res/cardsfolder/berserk_murlodont.txt -text svneol=native#text/plain res/cardsfolder/berserkers_of_blood_ridge.txt -text svneol=native#text/plain res/cardsfolder/bestial_menace.txt -text svneol=native#text/plain res/cardsfolder/bewilder.txt -text svneol=native#text/plain @@ -1405,6 +1406,7 @@ res/cardsfolder/elves_of_deep_shadow.txt -text svneol=native#text/plain res/cardsfolder/elvish_aberration.txt -text svneol=native#text/plain res/cardsfolder/elvish_archdruid.txt -text svneol=native#text/plain res/cardsfolder/elvish_archers.txt -text svneol=native#text/plain +res/cardsfolder/elvish_berserker.txt -text svneol=native#text/plain res/cardsfolder/elvish_champion.txt -text svneol=native#text/plain res/cardsfolder/elvish_farmer.txt -text svneol=native#text/plain res/cardsfolder/elvish_fury.txt -text svneol=native#text/plain @@ -4358,6 +4360,7 @@ res/cardsfolder/spark_spray.txt -text svneol=native#text/plain res/cardsfolder/sparkmage_apprentice.txt -text svneol=native#text/plain res/cardsfolder/sparksmith.txt -text svneol=native#text/plain res/cardsfolder/sparkspitter.txt -text svneol=native#text/plain +res/cardsfolder/sparring_golem.txt -text svneol=native#text/plain res/cardsfolder/spawning_pool.txt -text svneol=native#text/plain res/cardsfolder/spawnwrithe.txt -text svneol=native#text/plain res/cardsfolder/specters_wail.txt -text svneol=native#text/plain @@ -4398,6 +4401,7 @@ res/cardsfolder/spinal_villain.txt -text svneol=native#text/plain res/cardsfolder/spincrusher.txt -text svneol=native#text/plain res/cardsfolder/spindrift_drake.txt -text svneol=native#text/plain res/cardsfolder/spined_basher.txt -text svneol=native#text/plain +res/cardsfolder/spined_sliver.txt -text svneol=native#text/plain res/cardsfolder/spined_wurm.txt -text svneol=native#text/plain res/cardsfolder/spineless_thug.txt -text svneol=native#text/plain res/cardsfolder/spinneret_sliver.txt -text svneol=native#text/plain diff --git a/res/cardsfolder/berserk_murlodont.txt b/res/cardsfolder/berserk_murlodont.txt new file mode 100644 index 00000000000..f527fb30863 --- /dev/null +++ b/res/cardsfolder/berserk_murlodont.txt @@ -0,0 +1,9 @@ +Name:Berserk Murlodont +ManaCost:4 G +Types:Creature Beast +Text:no text +PT:3/3 +K:stPumpAll:Beast:0/0/HIDDEN Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it.:no Condition:Whenever a Beast becomes blocked, it gets +1/+1 until end of turn for each creature blocking it. +SVar:Rarity:Uncommon +SVar:Picture:http://www.wizards.com/global/images/magic/general/berserk_murlodont.jpg +End diff --git a/res/cardsfolder/deaths_shadow.txt b/res/cardsfolder/deaths_shadow.txt index e42cb8e913b..00e2b7f4957 100644 --- a/res/cardsfolder/deaths_shadow.txt +++ b/res/cardsfolder/deaths_shadow.txt @@ -5,6 +5,7 @@ Text:no text PT:13/13 K:stPumpSelf:Creature:X/X:no Condition:CARDNAME gets -X/-X, where X is your life total. SVar:X:Count$YourLifeTotal/Times.-1 +SVar:RemAIDeck:True SVar:Rarity:Uncommon SVar:Picture:http://www.wizards.com/global/images/magic/general/deaths_shadow.jpg SetInfo:WWK|Rare|http://magiccards.info/scans/en/wwk/57.jpg diff --git a/res/cardsfolder/elvish_berserker.txt b/res/cardsfolder/elvish_berserker.txt new file mode 100644 index 00000000000..ade642f0588 --- /dev/null +++ b/res/cardsfolder/elvish_berserker.txt @@ -0,0 +1,9 @@ +Name:Elvish Berserker +ManaCost:G +Types:Creature Elf Berserker +Text:no text +PT:1/1 +K:Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it. +SVar:Rarity:Common +SVar:Picture:http://www.wizards.com/global/images/magic/general/elvish_berserker.jpg +End diff --git a/res/cardsfolder/sparring_golem.txt b/res/cardsfolder/sparring_golem.txt new file mode 100644 index 00000000000..c99cd6243ae --- /dev/null +++ b/res/cardsfolder/sparring_golem.txt @@ -0,0 +1,9 @@ +Name:Sparring Golem +ManaCost:3 +Types:Artifact Creature Golem +Text:no text +PT:2/2 +K:Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it. +SVar:Rarity:Uncommon +SVar:Picture:http://www.wizards.com/global/images/magic/general/sparring_golem.jpg +End diff --git a/res/cardsfolder/spined_sliver.txt b/res/cardsfolder/spined_sliver.txt new file mode 100644 index 00000000000..a37e5572504 --- /dev/null +++ b/res/cardsfolder/spined_sliver.txt @@ -0,0 +1,9 @@ +Name:Spined Sliver +ManaCost:R G +Types:Creature Sliver +Text:no text +PT:2/2 +K:stPumpAll:Sliver:0/0/HIDDEN Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it.:no Condition:Whenever a Sliver becomes blocked, that Sliver gets +1/+1 until end of turn for each creature blocking it. +SVar:Rarity:Uncommon +SVar:Picture:http://www.wizards.com/global/images/magic/general/spined_sliver.jpg +End diff --git a/src/forge/CombatUtil.java b/src/forge/CombatUtil.java index c8b53b9aaec..96a2467563c 100644 --- a/src/forge/CombatUtil.java +++ b/src/forge/CombatUtil.java @@ -2224,7 +2224,7 @@ public class CombatUtil { }//checkDeclareBlockers - public static void checkBlockedAttackers(Card a, Card b) { + public static void checkBlockedAttackers(final Card a, Card b) { //System.out.println(a.getName() + " got blocked by " + b.getName()); if(!a.getCreatureGotBlockedThisCombat()) AllZone.GameAction.checkWheneverKeyword(a,"BecomesBlocked",null); @@ -2232,6 +2232,41 @@ public class CombatUtil { if(!a.getCreatureGotBlockedThisCombat()) { for(Ability ab:CardFactoryUtil.getBushidoEffects(a)) AllZone.Stack.add(ab); + + final int blockers = AllZone.Combat.getBlockers(a).size(); + + if(a.getKeyword().contains("Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it.")) { + Ability ability = new Ability(a, "0") { + @Override + public void resolve() { + final Command untilEOT = new Command() { + private static final long serialVersionUID = -5476584542164560128L; + + public void execute() { + if(AllZone.GameAction.isCardInPlay(a)) { + a.addTempAttackBoost(-blockers); + a.addTempDefenseBoost(-blockers); + } + } + };//Command + + if(AllZone.GameAction.isCardInPlay(a)) { + a.addTempAttackBoost(blockers); + a.addTempDefenseBoost(blockers); + + AllZone.EndOfTurn.addUntil(untilEOT); + } + }//resolve + }; + + StringBuilder sb = new StringBuilder(); + sb.append(a.getName()+" - gains +1/+1 for each blocker"); + ability.setStackDescription(sb.toString()); + int amount = a.getAmountOfKeyword("Whenever CARDNAME becomes blocked, it gets +1/+1 until end of turn for each creature blocking it."); + + for(int i=0 ; i < amount ; i++) + AllZone.Stack.add(ability); + } } //Rampage diff --git a/src/forge/ComputerUtil_Attack2.java b/src/forge/ComputerUtil_Attack2.java index 021364f1d0b..41798fdc3cb 100644 --- a/src/forge/ComputerUtil_Attack2.java +++ b/src/forge/ComputerUtil_Attack2.java @@ -1,49 +1,49 @@ package forge; import java.util.*; - //doesHumanAttackAndWin() uses the global variable AllZone.ComputerPlayer - public class ComputerUtil_Attack2 - { - //possible attackers and blockers - private CardList attackers; - private CardList blockers; - private int blockerLife; +//doesHumanAttackAndWin() uses the global variable AllZone.ComputerPlayer +public class ComputerUtil_Attack2 { + +//possible attackers and blockers + private CardList attackers; + private CardList blockers; + private int blockerLife; + + private Random random = new Random(); + private final int randomInt = random.nextInt(); + + private CardList humanList; //holds human player creatures + private CardList computerList;//holds computer creatures + + public ComputerUtil_Attack2(Card[] possibleAttackers, Card[] possibleBlockers, int blockerLife) + { + this(new CardList(possibleAttackers), new CardList(possibleBlockers), blockerLife); + } - private Random random = new Random(); - private final int randomInt = random.nextInt(); - - private CardList humanList; //holds human player creatures - private CardList computerList;//holds computer creatures - - public ComputerUtil_Attack2(Card[] possibleAttackers, Card[] possibleBlockers, int blockerLife) - { - this(new CardList(possibleAttackers), new CardList(possibleBlockers), blockerLife); - } - - public ComputerUtil_Attack2(CardList possibleAttackers, CardList possibleBlockers, int blockerLife) - { - humanList = new CardList(possibleBlockers.toArray()); - humanList = humanList.getType("Creature"); - - computerList = new CardList(possibleAttackers.toArray()); - computerList = computerList.getType("Creature"); - - attackers = getUntappedCreatures(possibleAttackers, true); - blockers = getUntappedCreatures(possibleBlockers , false); - this.blockerLife = blockerLife; - - final ArrayList valuable = new ArrayList(); - valuable.add("Kamahl, Pit Fighter"); - valuable.add("Elvish Piper"); - - attackers = attackers.filter(new CardListFilter() - { - public boolean addCard(Card c) - { - return (0 < getAttack(c) || c.getName().equals("Guiltfeeder")) && ! valuable.contains(c.getName()); - } - }); - }//constructor + public ComputerUtil_Attack2(CardList possibleAttackers, CardList possibleBlockers, int blockerLife) + { + humanList = new CardList(possibleBlockers.toArray()); + humanList = humanList.getType("Creature"); + + computerList = new CardList(possibleAttackers.toArray()); + computerList = computerList.getType("Creature"); + + attackers = getUntappedCreatures(possibleAttackers, true); + blockers = getUntappedCreatures(possibleBlockers , false); + this.blockerLife = blockerLife; + + final ArrayList valuable = new ArrayList(); + valuable.add("Kamahl, Pit Fighter"); + valuable.add("Elvish Piper"); + + attackers = attackers.filter(new CardListFilter() + { + public boolean addCard(Card c) + { + return (0 < getAttack(c) || c.getName().equals("Guiltfeeder")) && ! valuable.contains(c.getName()); + } + }); + }//constructor public CardList getUntappedCreatures(CardList in, final boolean checkCanAttack) { @@ -54,10 +54,8 @@ import java.util.*; { boolean b = c.isCreature() && c.isUntapped(); - if(checkCanAttack) - return b && CombatUtil.canAttack(c); - - return b; + if(checkCanAttack) return b && CombatUtil.canAttack(c); + else return b && CombatUtil.canBlock(c); } }); return list; @@ -158,7 +156,8 @@ import java.util.*; random.setSeed(AllZone.Phase.getTurn() + randomInt); Combat combat = new Combat(); - + + //Atackers that don't really have a choice for (int i=0; i= 3 || AllZoneUtil.isCardInPlay("Rafiq of the Many", AllZone.ComputerPlayer) || AllZoneUtil.getPlayerCardsInPlay(AllZone.ComputerPlayer, "Battlegrace Angel").size() >= 2 || @@ -187,6 +187,7 @@ import java.util.*; if (att!= null) combat.addAttacker(att); } + //do assault (all creatures attack) if the computer would win the game //or if the computer has 4 creatures and the player has 1 else if(doAssault() || (humanList.size() == 1 && 3 < attackers.size())) @@ -194,6 +195,7 @@ import java.util.*; for(int i = 0; i < attackers.size(); i++) combat.addAttacker(attackers.get(i)); } + else { //should the computer randomly not attack with one attacker? @@ -226,8 +228,8 @@ import java.util.*; for(; i < attackers.size(); i++) { - int totalFirstStrikeBlockPower = 0; - if (!attackers.get(i).hasFirstStrike() && !attackers.get(i).hasDoubleStrike()) + int totalFirstStrikeBlockPower = 0; + if (!attackers.get(i).hasFirstStrike() && !attackers.get(i).hasDoubleStrike()) totalFirstStrikeBlockPower = CombatUtil.getTotalFirstStrikeBlockPower(attackers.get(i), AllZone.HumanPlayer); if ( shouldAttack(attackers.get(i),blockers) && totalFirstStrikeBlockPower < attackers.get(i).getKillDamage() ) @@ -250,43 +252,43 @@ import java.util.*; return null; } - //returns null if no blockers found - public Card getBiggestDefense(Card attack) - { - CardListUtil.sortDefense(blockers); - for(int i = 0; i < blockers.size(); i++) - if(CombatUtil.canBlock(attack, blockers.get(i))) - return blockers.get(i); + //returns null if no blockers found + public Card getBiggestDefense(Card attack) + { + CardListUtil.sortDefense(blockers); + for(int i = 0; i < blockers.size(); i++) + if(CombatUtil.canBlock(attack, blockers.get(i))) + return blockers.get(i); + + return null; + } - return null; + public int countExaltedBonus(Player player) + { + PlayerZone play = AllZone.getZone(Constant.Zone.Play, player); + CardList list = new CardList(); + list.addAll(play.getCards()); + list = list.filter(new CardListFilter(){ + public boolean addCard(Card c) { + return c.getKeyword().contains("Exalted"); + } + }); + + return list.size(); } - - public int countExaltedBonus(Player player) - { - PlayerZone play = AllZone.getZone(Constant.Zone.Play, player); - CardList list = new CardList(); - list.addAll(play.getCards()); - list = list.filter(new CardListFilter(){ - public boolean addCard(Card c) { - return c.getKeyword().contains("Exalted"); - } - }); - - return list.size(); - } - public int getAttack(Card c) - { - int n = c.getNetAttack(); + public int getAttack(Card c) + { + int n = c.getNetAttack(); - if(CombatUtil.isDoranInPlay()) - n = c.getNetDefense(); + if(CombatUtil.isDoranInPlay()) + n = c.getNetDefense(); - if(c.getKeyword().contains("Double Strike")) - n *= 2; + if(c.getKeyword().contains("Double Strike")) + n *= 2; - return n; - } + return n; + } public boolean shouldAttack(Card attacker, CardList defenders) { @@ -300,87 +302,23 @@ import java.util.*; } } return (canKillAll || !canBeKilled); // A creature should attack if it can't be killed or can kill any blocker - } + } + + // + public static Combat getAttackers(CardList attackerPermanents, CardList defenderPermanents) { + + Combat combat = new Combat(); + CardList attackersLeft = new CardList(); //keeps track of all undecided attackers + CardList humanBlockers = new CardList(); + + for(Card c:defenderPermanents) { + if(c.isCreature() && CombatUtil.canBlock(c)) humanBlockers.add(c); + } + + for(Card c:attackerPermanents) { + if(c.isCreature() && CombatUtil.canAttack(c)) attackersLeft.add(c); + } + + return combat; + } } - - /* - //this returns the attacking power that is needed to destroy - //Card c and takes into account first and double strike - // - //Doran, the Siege Tower doesn't change a card's defense - //used by sortDefense() - private int getDefense(Card c) - { - int n = c.getNetDefense(); - - //is the defense is less than attack and the card has - //first or double strike? - if(hasStrike(c) && n < getAttack_FirstStrike(c)) - n = getAttack_FirstStrike(c); - - return n; - } - - //does this card have first or double strike? - private boolean hasStrike(Card c) - { - return c.getKeyword.contains("First Strike") || - c.getKeyword.contains("Double Strike") - } - - //the higher the defense the better - @SuppressWarnings("unchecked") // Comparator needs - private void sortDefense(CardList list) - { - Comparator com = new Comparator() - { - public int compare(Object a1, Object b1) - { - Card a = (Card)a1; - Card b = (Card)b1; - - return getDefense(b) - getDefense(a); - } - }; - list.sort(com); - }//sortDefense() - - //use this method if you need to know about first strike - public int getAttack_FirstStrike(Card c) - { - int n = getAttack(c); - - //adding 1 is a little bit hacky bit it shows - //that first strike is a little better - //than a average creature - if(c.getKeyword().contains("First Strike")) - n += 1; - - return n; - } - - //returns lowest attack first - private void sortAttackLowFirst(CardList list) - { - sortAttack(list); - list.reverse(); - return list; - } - - //the higher the attack the better - @SuppressWarnings("unchecked") // Comparator needs type - private void sortAttack(CardList list) - { - Comparator com = new Comparator() - { - public int compare(Object a1, Object b1) - { - Card a = (Card)a1; - Card b = (Card)b1; - - return getAttack_FirstStrike(b) - getAttack_FirstStrike(a); - } - }; - list.sort(com); - }//sortAttack() - */