mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Ensure creatures that must attack do so even if you End Turn before combat
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user