Ensure creatures that must attack do so even if you End Turn before combat

This commit is contained in:
drdev
2014-08-22 04:23:22 +00:00
parent 7a98346356
commit 1b26d6677b
3 changed files with 57 additions and 26 deletions

View File

@@ -17,7 +17,9 @@
*/ */
package forge.game.combat; package forge.game.combat;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.card.CardType; import forge.card.CardType;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
@@ -29,6 +31,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardFactoryUtil; import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.phase.Untap; import forge.game.phase.Untap;
@@ -41,7 +44,9 @@ import forge.game.zone.ZoneType;
import forge.util.Expressions; import forge.util.Expressions;
import forge.util.Lang; import forge.util.Lang;
import forge.util.TextUtil; import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -1039,4 +1044,31 @@ public class CombatUtil {
} }
} }
public static List<Pair<Card, GameEntity>> getMandatoryAttackers(Player attackingPlayer, Combat combat, List<GameEntity> defenders) {
List<Pair<Card, GameEntity>> attackers = new ArrayList<Pair<Card, GameEntity>>();
List<Card> possibleAttackers = attackingPlayer.getCardsIn(ZoneType.Battlefield);
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) {
GameEntity mustAttack = c.getController().getMustAttackEntity() ;
if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
final int i = c.getKeywordPosition("CARDNAME attacks specific player each combat if able");
final String defined = c.getKeyword().get(i).split(":")[1];
mustAttack = AbilityUtils.getDefinedPlayers(c, defined, null).get(0);
}
if (mustAttack != null && CombatUtil.canAttack(c, mustAttack, combat)) {
attackers.add(Pair.of(c, mustAttack));
continue;
}
if (c.hasKeyword("CARDNAME attacks each combat if able.") ||
(c.hasKeyword("CARDNAME attacks each turn if able.") && !c.getDamageHistory().getCreatureAttackedThisTurn())) {
for (GameEntity def : defenders) {
if (CombatUtil.canAttack(c, def, combat)) {
attackers.add(Pair.of(c, def));
break;
}
}
}
}
return attackers;
}
} // end class CombatUtil } // end class CombatUtil

View File

@@ -22,7 +22,6 @@ import com.google.common.collect.Iterables;
import forge.GuiBase; import forge.GuiBase;
import forge.events.UiEventAttackerDeclared; import forge.events.UiEventAttackerDeclared;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardPredicates; import forge.game.card.CardPredicates;
@@ -37,6 +36,8 @@ import forge.util.ITriggerEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
/** /**
* <p> * <p>
* InputAttack class. * InputAttack class.
@@ -73,29 +74,10 @@ public class InputAttack extends InputSyncronizedBase {
return; // should even throw here! return; // should even throw here!
} }
List<Card> possibleAttackers = playerAttacks.getCardsIn(ZoneType.Battlefield); List<Pair<Card, GameEntity>> mandatoryAttackers = CombatUtil.getMandatoryAttackers(playerAttacks, combat, defenders);
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) { for (Pair<Card, GameEntity> attacker : mandatoryAttackers) {
GameEntity mustAttack = c.getController().getMustAttackEntity() ; combat.addAttacker(attacker.getLeft(), attacker.getRight());
if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) { GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(attacker.getLeft(), attacker.getRight()));
final int i = c.getKeywordPosition("CARDNAME attacks specific player each combat if able");
final String defined = c.getKeyword().get(i).split(":")[1];
mustAttack = AbilityUtils.getDefinedPlayers(c, defined, null).get(0);
}
if (mustAttack != null && CombatUtil.canAttack(c, mustAttack, combat)) {
combat.addAttacker(c, mustAttack);
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(c, mustAttack));
continue;
}
if (c.hasKeyword("CARDNAME attacks each combat if able.") ||
(c.hasKeyword("CARDNAME attacks each turn if able.") && !c.getDamageHistory().getCreatureAttackedThisTurn())) {
for (GameEntity def : defenders) {
if (CombatUtil.canAttack(c, def, combat)) {
combat.addAttacker(c, def);
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(c, currentDefender));
break;
}
}
}
} }
updateMessage(); updateMessage();
} }

View File

@@ -17,6 +17,7 @@ import forge.control.FControlGamePlayback;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.deck.Deck; import forge.deck.Deck;
import forge.deck.DeckSection; import forge.deck.DeckSection;
import forge.events.UiEventAttackerDeclared;
import forge.game.Game; import forge.game.Game;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.GameLogEntryType; import forge.game.GameLogEntryType;
@@ -27,6 +28,7 @@ import forge.game.card.Card;
import forge.game.card.CardShields; import forge.game.card.CardShields;
import forge.game.card.CounterType; import forge.game.card.CounterType;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.combat.CombatUtil;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostPart; import forge.game.cost.CostPart;
import forge.game.cost.CostPartMana; import forge.game.cost.CostPartMana;
@@ -620,13 +622,28 @@ public class PlayerControllerHuman extends PlayerController {
} }
@Override @Override
public void declareAttackers(Player attacker, Combat combat) { public void declareAttackers(Player attackingPlayer, Combat combat) {
if (mayAutoPass()) { if (mayAutoPass()) {
List<Pair<Card, GameEntity>> mandatoryAttackers = CombatUtil.getMandatoryAttackers(attackingPlayer, combat, combat.getDefenders());
if (!mandatoryAttackers.isEmpty()) {
//even if auto-passing attack phase, if there are any mandatory attackers,
//ensure they're declared and then delay slightly so user can see as much
for (Pair<Card, GameEntity> attacker : mandatoryAttackers) {
combat.addAttacker(attacker.getLeft(), attacker.getRight());
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(attacker.getLeft(), attacker.getRight()));
}
try {
Thread.sleep(FControlGamePlayback.combatDelay);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
return; //don't prompt to declare attackers if user chose to end the turn return; //don't prompt to declare attackers if user chose to end the turn
} }
// This input should not modify combat object itself, but should return user choice // This input should not modify combat object itself, but should return user choice
InputAttack inpAttack = new InputAttack(attacker, combat); InputAttack inpAttack = new InputAttack(attackingPlayer, combat);
inpAttack.showAndWait(); inpAttack.showAndWait();
} }