mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
Improve InputAttack to make tapping undeclare attacker if banding not possible, simplify message, and allow Alpha Strike and Cancelling attackers from right prompt button
This commit is contained in:
@@ -208,8 +208,12 @@ public class FButton extends FDisplayObject implements IButton {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(text)) {
|
String displayText = text;
|
||||||
g.drawText(text, font, FORE_COLOR, x, y, w, h, false, HAlignment.CENTER, true);
|
if (!StringUtils.isEmpty(displayText)) {
|
||||||
|
if (corner != Corner.None) {
|
||||||
|
displayText = displayText.replaceFirst(" ", "\n"); //allow second word to wrap if corner button
|
||||||
|
}
|
||||||
|
g.drawText(displayText, font, FORE_COLOR, x, y, w, h, false, HAlignment.CENTER, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ import forge.events.UiEventAttackerDeclared;
|
|||||||
import forge.game.GameEntity;
|
import forge.game.GameEntity;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates;
|
import forge.game.card.CardPredicates;
|
||||||
|
import forge.game.card.CardPredicates.Presets;
|
||||||
import forge.game.combat.AttackingBand;
|
import forge.game.combat.AttackingBand;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
import forge.game.combat.CombatUtil;
|
import forge.game.combat.CombatUtil;
|
||||||
@@ -32,6 +34,7 @@ import forge.game.player.Player;
|
|||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.util.ITriggerEvent;
|
import forge.util.ITriggerEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,41 +53,38 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
private final List<GameEntity> defenders;
|
private final List<GameEntity> defenders;
|
||||||
private GameEntity currentDefender;
|
private GameEntity currentDefender;
|
||||||
private final Player playerAttacks;
|
private final Player playerAttacks;
|
||||||
private final Player playerDeclares;
|
|
||||||
private AttackingBand activeBand = null;
|
private AttackingBand activeBand = null;
|
||||||
|
|
||||||
public InputAttack(Player attacks, Player declares, Combat combat) {
|
public InputAttack(Player attacks0, Combat combat0) {
|
||||||
this.playerAttacks = attacks;
|
playerAttacks = attacks0;
|
||||||
this.playerDeclares = declares;
|
combat = combat0;
|
||||||
this.combat = combat;
|
defenders = combat.getDefenders();
|
||||||
this.defenders = combat.getDefenders();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public final void showMessage() {
|
public final void showMessage() {
|
||||||
// TODO still seems to have some issues with multiple planeswalkers
|
// TODO still seems to have some issues with multiple planeswalkers
|
||||||
|
|
||||||
ButtonUtil.enableOnlyOk();
|
|
||||||
|
|
||||||
setCurrentDefender(defenders.isEmpty() ? null : defenders.get(0));
|
setCurrentDefender(defenders.isEmpty() ? null : defenders.get(0));
|
||||||
|
|
||||||
if (null == currentDefender) {
|
if (null == currentDefender) {
|
||||||
System.err.println("InputAttack has no potential defenders!");
|
System.err.println("InputAttack has no potential defenders!");
|
||||||
|
updatePrompt();
|
||||||
return; // should even throw here!
|
return; // should even throw here!
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Card> possibleAttackers = playerAttacks.getCardsIn(ZoneType.Battlefield);
|
List<Card> possibleAttackers = playerAttacks.getCardsIn(ZoneType.Battlefield);
|
||||||
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) {
|
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) {
|
||||||
if (c.hasKeyword("CARDNAME attacks each turn if able.")) {
|
if (c.hasKeyword("CARDNAME attacks each turn if able.")) {
|
||||||
for(GameEntity def : defenders) {
|
for (GameEntity def : defenders) {
|
||||||
if(CombatUtil.canAttack(c, def, combat)) {
|
if (CombatUtil.canAttack(c, def, combat)) {
|
||||||
combat.addAttacker(c, currentDefender);
|
combat.addAttacker(c, def);
|
||||||
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(c, currentDefender));
|
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(c, currentDefender));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (c.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
|
}
|
||||||
|
else 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 int i = c.getKeywordPosition("CARDNAME attacks specific player each combat if able");
|
||||||
final String defined = c.getKeyword().get(i).split(":")[1];
|
final String defined = c.getKeyword().get(i).split(":")[1];
|
||||||
final Player player = AbilityUtils.getDefinedPlayers(c, defined, null).get(0);
|
final Player player = AbilityUtils.getDefinedPlayers(c, defined, null).get(0);
|
||||||
@@ -94,11 +94,17 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showCombat() {
|
private void updatePrompt() {
|
||||||
// redraw sword icons
|
if (combat.getAttackers().isEmpty()) {
|
||||||
GuiBase.getInterface().showCombat(combat);
|
ButtonUtil.setButtonText("OK", "Alpha Strike");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ButtonUtil.setButtonText("OK", "Cancel");
|
||||||
|
}
|
||||||
|
ButtonUtil.enableAllFocusOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@@ -111,9 +117,40 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
protected final void onCancel() {
|
||||||
|
//either alpha strike or undeclare all attackers based on whether any attackers have been declared
|
||||||
|
if (combat.getAttackers().isEmpty()) {
|
||||||
|
//alpha strike
|
||||||
|
List<Player> defenders = playerAttacks.getOpponents();
|
||||||
|
|
||||||
|
for (Card c : CardLists.filter(playerAttacks.getCardsIn(ZoneType.Battlefield), Presets.CREATURES)) {
|
||||||
|
if (combat.isAttacking(c)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Player defender : defenders) {
|
||||||
|
if (CombatUtil.canAttack(c, defender, combat)) {
|
||||||
|
combat.addAttacker(c, defender);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//undeclare all attackers
|
||||||
|
List<Card> attackers = new ArrayList<Card>(combat.getAttackers()); //must copy list since it will be modified
|
||||||
|
for (Card c : attackers) {
|
||||||
|
undeclareAttacker(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateMessage();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void onPlayerSelected(Player selected, final ITriggerEvent triggerEvent) {
|
protected final void onPlayerSelected(Player selected, final ITriggerEvent triggerEvent) {
|
||||||
if(defenders.contains(selected)) {
|
if (defenders.contains(selected)) {
|
||||||
setCurrentDefender(selected);
|
setCurrentDefender(selected);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -125,37 +162,37 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
@Override
|
@Override
|
||||||
protected final boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
|
protected final boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
|
||||||
final List<Card> att = combat.getAttackers();
|
final List<Card> att = combat.getAttackers();
|
||||||
if (triggerEvent != null && triggerEvent.getButton() == 3 && att.contains(card) && !card.hasKeyword("CARDNAME attacks each turn if able.")
|
if (triggerEvent != null && triggerEvent.getButton() == 3 && att.contains(card)) {
|
||||||
&& !card.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
|
if (undeclareAttacker(card)) {
|
||||||
// TODO Is there no way to attacks each turn cards to attack Planeswalkers?
|
updateMessage();
|
||||||
combat.removeFromCombat(card);
|
return true;
|
||||||
GuiBase.getInterface().setUsedToPay(card, false);
|
}
|
||||||
showCombat();
|
|
||||||
// When removing an attacker clear the attacking band
|
|
||||||
this.activateBand(null);
|
|
||||||
|
|
||||||
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(card, null));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (combat.isAttacking(card, currentDefender)) {
|
if (combat.isAttacking(card, currentDefender)) {
|
||||||
// Activate band by selecting/deselecting a band member
|
|
||||||
boolean validAction = true;
|
boolean validAction = true;
|
||||||
if (this.activeBand == null) {
|
if (isBandingPossible()) {
|
||||||
this.activateBand(combat.getBandOfAttacker(card));
|
// Activate band by selecting/deselecting a band member
|
||||||
}
|
if (activeBand == null) {
|
||||||
else if (this.activeBand.getAttackers().contains(card)) {
|
activateBand(combat.getBandOfAttacker(card));
|
||||||
this.activateBand(null);
|
|
||||||
}
|
|
||||||
else { // Join a band by selecting a non-active band member after activating a band
|
|
||||||
if (this.activeBand.canJoinBand(card)) {
|
|
||||||
combat.removeFromCombat(card);
|
|
||||||
declareAttacker(card);
|
|
||||||
}
|
}
|
||||||
else {
|
else if (activeBand.getAttackers().contains(card)) {
|
||||||
flashIncorrectAction();
|
activateBand(null);
|
||||||
validAction = false;
|
|
||||||
}
|
}
|
||||||
|
else { // Join a band by selecting a non-active band member after activating a band
|
||||||
|
if (activeBand.canJoinBand(card)) {
|
||||||
|
combat.removeFromCombat(card);
|
||||||
|
declareAttacker(card);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
flashIncorrectAction();
|
||||||
|
validAction = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//if banding not possible, just undeclare attacker
|
||||||
|
undeclareAttacker(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMessage();
|
updateMessage();
|
||||||
@@ -170,19 +207,19 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (playerAttacks.getZone(ZoneType.Battlefield).contains(card) && CombatUtil.canAttack(card, currentDefender, combat)) {
|
if (playerAttacks.getZone(ZoneType.Battlefield).contains(card) && CombatUtil.canAttack(card, currentDefender, combat)) {
|
||||||
if (this.activeBand != null && !this.activeBand.canJoinBand(card)) {
|
if (activeBand != null && !activeBand.canJoinBand(card)) {
|
||||||
this.activateBand(null);
|
activateBand(null);
|
||||||
updateMessage();
|
updateMessage();
|
||||||
flashIncorrectAction();
|
flashIncorrectAction();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(combat.isAttacking(card)) {
|
if (combat.isAttacking(card)) {
|
||||||
combat.removeFromCombat(card);
|
combat.removeFromCombat(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
declareAttacker(card);
|
declareAttacker(card);
|
||||||
showCombat();
|
updateMessage();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,18 +227,29 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Write javadoc for this method.
|
|
||||||
* @param card
|
|
||||||
*/
|
|
||||||
private void declareAttacker(final Card card) {
|
private void declareAttacker(final Card card) {
|
||||||
combat.addAttacker(card, currentDefender, this.activeBand);
|
combat.addAttacker(card, currentDefender, activeBand);
|
||||||
this.activateBand(this.activeBand);
|
activateBand(activeBand);
|
||||||
updateMessage();
|
|
||||||
|
|
||||||
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(card, currentDefender));
|
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(card, currentDefender));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean undeclareAttacker(Card card) {
|
||||||
|
if (card.hasKeyword("CARDNAME attacks each turn if able.") ||
|
||||||
|
card.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Is there no way to attacks each turn cards to attack Planeswalkers?
|
||||||
|
combat.removeFromCombat(card);
|
||||||
|
GuiBase.getInterface().setUsedToPay(card, false);
|
||||||
|
// When removing an attacker clear the attacking band
|
||||||
|
activateBand(null);
|
||||||
|
|
||||||
|
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(card, null));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private final void setCurrentDefender(GameEntity def) {
|
private final void setCurrentDefender(GameEntity def) {
|
||||||
currentDefender = def;
|
currentDefender = def;
|
||||||
for(GameEntity ge: defenders) {
|
for(GameEntity ge: defenders) {
|
||||||
@@ -214,35 +262,42 @@ public class InputAttack extends InputSyncronizedBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateMessage();
|
updateMessage();
|
||||||
|
|
||||||
// update UI
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final void activateBand(AttackingBand band) {
|
private final void activateBand(AttackingBand band) {
|
||||||
if (this.activeBand != null) {
|
if (activeBand != null) {
|
||||||
for(Card card : this.activeBand.getAttackers()) {
|
for(Card card : activeBand.getAttackers()) {
|
||||||
GuiBase.getInterface().setUsedToPay(card, false);
|
GuiBase.getInterface().setUsedToPay(card, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.activeBand = band;
|
activeBand = band;
|
||||||
|
|
||||||
if (this.activeBand != null) {
|
if (activeBand != null) {
|
||||||
for(Card card : this.activeBand.getAttackers()) {
|
for(Card card : activeBand.getAttackers()) {
|
||||||
GuiBase.getInterface().setUsedToPay(card, true);
|
GuiBase.getInterface().setUsedToPay(card, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update UI
|
//only enable banding message and actions if a creature that can attack has banding
|
||||||
|
private boolean isBandingPossible() {
|
||||||
|
List<Card> possibleAttackers = playerAttacks.getCardsIn(ZoneType.Battlefield);
|
||||||
|
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.hasKeyword("Banding"))) {
|
||||||
|
if (c.isCreature() && CombatUtil.canAttack(c, currentDefender, combat)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMessage() {
|
private void updateMessage() {
|
||||||
StringBuilder sb = new StringBuilder();
|
String message = "Select creatures to attack " + currentDefender + " or select player/planeswalker you wish to attack.";
|
||||||
sb.append(playerDeclares.getName()).append(", ");
|
if (isBandingPossible()) {
|
||||||
sb.append(playerAttacks == playerDeclares ? "declare attackers." : "declare attackers for " + playerAttacks.getName()).append("\n");
|
message += " To attack as a band, select an attacking creature to activate its 'band' then select another to join it.";
|
||||||
sb.append("Selecting Creatures to Attack ").append(currentDefender).append("\n\n");
|
}
|
||||||
sb.append("To change the current defender, click on the player or planeswalker you wish to attack.\n");
|
showMessage(message);
|
||||||
sb.append("To attack as a band, click an attacking creature to activate its 'band', select another to join the band.");
|
|
||||||
|
|
||||||
showMessage(sb.toString());
|
updatePrompt();
|
||||||
|
GuiBase.getInterface().showCombat(combat); // redraw sword icons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -595,7 +595,7 @@ public class PlayerControllerHuman extends PlayerController {
|
|||||||
@Override
|
@Override
|
||||||
public void declareAttackers(Player attacker, Combat combat) {
|
public void declareAttackers(Player attacker, Combat combat) {
|
||||||
// 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, player, combat);
|
InputAttack inpAttack = new InputAttack(attacker, combat);
|
||||||
inpAttack.showAndWait();
|
inpAttack.showAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user