Support selecting cards further back in stack if tapped card not selectable

This commit is contained in:
drdev
2014-05-17 21:38:41 +00:00
parent 1baf8009b0
commit 91d0c89b8a
16 changed files with 132 additions and 85 deletions

View File

@@ -173,12 +173,28 @@ public abstract class VCardDisplayArea extends VDisplayArea {
ThreadUtil.invokeInGameThread(new Runnable() { //must invoke in game thread in case a dialog needs to be shown
@Override
public void run() {
FControl.getInputProxy().selectCard(getCard(), null);
if (!selectCard()) {
//if no cards in stack can be selected, just show zoom/details for card
CardZoom.show(getCard());
}
}
});
return true;
}
public boolean selectCard() {
if (FControl.getInputProxy().selectCard(getCard(), null)) {
return true;
}
//if panel can't do anything with card selection, try selecting an attached panel
for (CardAreaPanel panel : attachedPanels) {
if (panel.selectCard()) {
return true;
}
}
return false;
}
@Override
public boolean longPress(float x, float y) {
CardZoom.show(getCard());

View File

@@ -9,7 +9,7 @@ import forge.util.ITriggerEvent;
public interface Input {
void showMessageInitial();
void selectCard(Card c, ITriggerEvent triggerEvent);
boolean selectCard(Card c, ITriggerEvent triggerEvent);
void selectAbility(SpellAbility ab);

View File

@@ -69,7 +69,7 @@ public class InputAttack extends InputSyncronizedBase {
setCurrentDefender(defenders.isEmpty() ? null : defenders.get(0));
if ( null == currentDefender ) {
if (null == currentDefender) {
System.err.println("InputAttack has no potential defenders!");
return; // should even throw here!
}
@@ -77,8 +77,8 @@ public class InputAttack extends InputSyncronizedBase {
List<Card> possibleAttackers = playerAttacks.getCardsIn(ZoneType.Battlefield);
for (Card c : Iterables.filter(possibleAttackers, CardPredicates.Presets.CREATURES)) {
if (c.hasKeyword("CARDNAME attacks each turn if able.")) {
for(GameEntity def : defenders ) {
if( CombatUtil.canAttack(c, def, combat) ) {
for(GameEntity def : defenders) {
if(CombatUtil.canAttack(c, def, combat)) {
combat.addAttacker(c, currentDefender);
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(c, currentDefender));
break;
@@ -123,7 +123,7 @@ public class InputAttack extends InputSyncronizedBase {
/** {@inheritDoc} */
@Override
protected final void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
protected final boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
final List<Card> att = combat.getAttackers();
if (triggerEvent != null && triggerEvent.getButton() == 3 && att.contains(card) && !card.hasKeyword("CARDNAME attacks each turn if able.")
&& !card.hasStartOfKeyword("CARDNAME attacks specific player each combat if able")) {
@@ -135,32 +135,37 @@ public class InputAttack extends InputSyncronizedBase {
this.activateBand(null);
GuiBase.getInterface().fireEvent(new UiEventAttackerDeclared(card, null));
return;
return true;
}
if (combat.isAttacking(card, currentDefender)) {
// Activate band by selecting/deselecting a band member
boolean validAction = true;
if (this.activeBand == null) {
this.activateBand(combat.getBandOfAttacker(card));
} else if (this.activeBand.getAttackers().contains(card)) {
}
else if (this.activeBand.getAttackers().contains(card)) {
this.activateBand(null);
} else { // Join a band by selecting a non-active band member after activating a band
}
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 {
flashIncorrectAction();
validAction = false;
}
}
updateMessage();
return;
return validAction;
}
if ( card.getController().isOpponentOf(playerAttacks) ) {
if ( defenders.contains(card) ) { // planeswalker?
if (card.getController().isOpponentOf(playerAttacks)) {
if (defenders.contains(card)) { // planeswalker?
setCurrentDefender(card);
return;
return true;
}
}
@@ -169,7 +174,7 @@ public class InputAttack extends InputSyncronizedBase {
this.activateBand(null);
updateMessage();
flashIncorrectAction();
return;
return false;
}
if(combat.isAttacking(card)) {
@@ -178,12 +183,12 @@ public class InputAttack extends InputSyncronizedBase {
declareAttacker(card);
showCombat();
return true;
}
else {
flashIncorrectAction();
}
} // selectCard()
flashIncorrectAction();
return false;
}
/**
* TODO: Write javadoc for this method.
@@ -199,8 +204,8 @@ public class InputAttack extends InputSyncronizedBase {
private final void setCurrentDefender(GameEntity def) {
currentDefender = def;
for( GameEntity ge: defenders ) {
if ( ge instanceof Card) {
for(GameEntity ge: defenders) {
if (ge instanceof Card) {
GuiBase.getInterface().setUsedToPay((Card)ge, ge == def);
}
else if (ge instanceof Player) {

View File

@@ -71,12 +71,14 @@ public abstract class InputBase implements java.io.Serializable, Input {
}
@Override
public final void selectCard(final Card c, final ITriggerEvent triggerEvent) {
if (isFinished()) { return; }
onCardSelected(c, triggerEvent);
public final boolean selectCard(final Card c, final ITriggerEvent triggerEvent) {
if (isFinished()) { return false; }
return onCardSelected(c, triggerEvent);
}
protected void onCardSelected(final Card c, final ITriggerEvent triggerEvent) {}
protected boolean onCardSelected(final Card c, final ITriggerEvent triggerEvent) {
return false;
}
protected void onPlayerSelected(final Player p, final ITriggerEvent triggerEvent) {}
protected void onCancel() {}
protected void onOk() {}

View File

@@ -97,22 +97,24 @@ public class InputBlock extends InputSyncronizedBase {
/** {@inheritDoc} */
@Override
public final void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
public final boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
boolean isCorrectAction = false;
if (triggerEvent != null && triggerEvent.getButton() == 3 && card.getController() == defender) {
combat.removeFromCombat(card);
GuiBase.getInterface().fireEvent(new UiEventBlockerAssigned(card, (Card)null));
} else {
isCorrectAction = true;
}
else {
// is attacking?
boolean isCorrectAction = false;
if (combat.isAttacking(card)) {
setCurrentAttacker(card);
isCorrectAction = true;
} else {
}
else {
// Make sure this card is valid to even be a blocker
if (this.currentAttacker != null && card.isCreature() && defender.getZone(ZoneType.Battlefield).contains(card)) {
isCorrectAction = CombatUtil.canBlock(this.currentAttacker, card, combat);
if ( isCorrectAction ) {
if (isCorrectAction) {
combat.addBlocker(this.currentAttacker, card);
GuiBase.getInterface().fireEvent(new UiEventBlockerAssigned(card, currentAttacker));
}
@@ -124,8 +126,8 @@ public class InputBlock extends InputSyncronizedBase {
}
}
this.showMessage();
} // selectCard()
return isCorrectAction;
}
private void setCurrentAttacker(Card card) {
currentAttacker = card;

View File

@@ -111,13 +111,13 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
volatile boolean cardSelectLocked = false;
@Override
protected void onCardSelected(final Card c0, final ITriggerEvent triggerEvent) { // the only place that would cause troubles - input is supposed only to confirm, not to fire abilities
protected boolean onCardSelected(final Card c0, final ITriggerEvent triggerEvent) { // the only place that would cause troubles - input is supposed only to confirm, not to fire abilities
boolean fromHand = player.getZone(ZoneType.Hand).contains(c0);
boolean isSerumPowder = c0.getName().equals("Serum Powder");
boolean isLegalChoice = fromHand && (isCommander || isSerumPowder);
if (!isLegalChoice || cardSelectLocked) {
flashIncorrectAction();
return;
return false;
}
if (isSerumPowder && SGuiDialog.confirm(c0, "Use " + c0.getName() + "'s ability?")) {
@@ -132,7 +132,7 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
cardSelectLocked = false;
}
});
return;
return true;
}
if (isCommander) { // allow to choose cards for partial paris
@@ -151,6 +151,7 @@ public class InputConfirmMulligan extends InputSyncronizedBase {
ButtonUtil.enableAllFocusOk();
}
}
return true;
}
public final boolean isKeepHand() {

View File

@@ -59,10 +59,20 @@ public class InputLockUI implements Input {
GuiBase.getInterface().showPromptMessage(message);
}
@Override public void selectCard(Card c, ITriggerEvent triggerEvent) {}
@Override public void selectAbility(SpellAbility ab) {}
@Override public void selectPlayer(Player player, ITriggerEvent triggerEvent) {}
@Override public void selectButtonOK() {}
@Override public void selectButtonCancel() {}
@Override
public boolean selectCard(Card c, ITriggerEvent triggerEvent) {
return false;
}
@Override
public void selectAbility(SpellAbility ab) {
}
@Override
public void selectPlayer(Player player, ITriggerEvent triggerEvent) {
}
@Override
public void selectButtonOK() {
}
@Override
public void selectButtonCancel() {
}
}

View File

@@ -60,15 +60,16 @@ public class InputPassPriority extends InputSyncronizedBase {
public SpellAbility getChosenSa() { return chosenSa; }
@Override
protected void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
protected boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
//remove unplayable unless triggerEvent specified, in which case unplayable may be shown as disabled options
List<SpellAbility> abilities = card.getAllPossibleAbilities(player, triggerEvent == null);
if (abilities.isEmpty()) {
flashIncorrectAction();
return;
return false;
}
selectAbility(player.getController().getAbilityToPlay(abilities, triggerEvent));
return true;
}
@Override

View File

@@ -49,13 +49,13 @@ public abstract class InputPayMana extends InputSyncronizedBase {
}
@Override
protected void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
protected boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
if (card.getManaAbility().isEmpty()) {
flashIncorrectAction();
return;
return false;
}
// only tap card if the mana is needed
activateManaAbility(card, this.manaCost);
return activateManaAbility(card, this.manaCost);
}
@Override
@@ -123,18 +123,18 @@ public abstract class InputPayMana extends InputSyncronizedBase {
* a {@link forge.game.mana.ManaCostBeingPaid} object.
* @return a {@link forge.game.mana.ManaCostBeingPaid} object.
*/
protected void activateManaAbility(final Card card, ManaCostBeingPaid manaCost) {
activateManaAbility(card, manaCost, null);
protected boolean activateManaAbility(final Card card, ManaCostBeingPaid manaCost) {
return activateManaAbility(card, manaCost, null);
}
protected void activateManaAbility(final Card card, ManaCostBeingPaid manaCost, SpellAbility chosenAbility) {
if ( locked ) {
protected boolean activateManaAbility(final Card card, ManaCostBeingPaid manaCost, SpellAbility chosenAbility) {
if (locked) {
System.err.print("Should wait till previous call to playAbility finishes.");
return;
return false;
}
// make sure computer's lands aren't selected
if (card.getController() != player) {
return;
return false;
}
byte colorCanUse = 0;
@@ -149,7 +149,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
}
if (colorCanUse == 0) { // no mana cost or something
return;
return false;
}
List<SpellAbility> abilities = new ArrayList<SpellAbility>();
@@ -157,7 +157,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
final String typeRes = manaCost.getSourceRestriction();
if (StringUtils.isNotBlank(typeRes) && !card.isType(typeRes)) {
return;
return false;
}
boolean guessAbilityWithRequiredColors = true;
@@ -179,7 +179,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
}
if (abilities.isEmpty() || (chosenAbility != null && !abilities.contains(chosenAbility))) {
return;
return false;
}
// Store some information about color costs to help with any mana choices
@@ -251,6 +251,7 @@ public abstract class InputPayMana extends InputSyncronizedBase {
};
locked = true;
game.getAction().invoke(proc);
return true;
}
/**

View File

@@ -92,9 +92,9 @@ public class InputPayManaX extends InputPayMana {
}
@Override
protected void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
protected boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
// don't allow here the cards that produce only wrong colors
activateManaAbility(card, this.manaCost);
return activateManaAbility(card, this.manaCost);
}
@Override

View File

@@ -42,9 +42,9 @@ public final class InputProliferate extends InputSelectManyBase<GameEntity> {
}
@Override
protected void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
protected boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
if (!card.hasCounters()) {
return;
return false;
}
boolean entityWasSelected = chosenCounters.containsKey(card);
@@ -64,6 +64,7 @@ public final class InputProliferate extends InputSelectManyBase<GameEntity> {
}
refresh();
return true;
}
@Override

View File

@@ -136,11 +136,12 @@ public class InputProxy implements Observer {
* a {@link forge.game.card.Card} object.
* @param triggerEvent
*/
public final void selectCard(final Card card, final ITriggerEvent triggerEvent) {
public final boolean selectCard(final Card card, final ITriggerEvent triggerEvent) {
Input inp = getInput();
if (inp != null) {
inp.selectCard(card, triggerEvent);
return inp.selectCard(card, triggerEvent);
}
return false;
}
public final void selectAbility(SpellAbility ab) {

View File

@@ -38,11 +38,11 @@ public final class InputSelectCardsForConvoke extends InputSelectManyBase<Card>
}
@Override
protected void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
protected boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
if (!availableCreatures.contains(card)) {
// Not in untapped creatures list provided. Not a legal Convoke selection.
flashIncorrectAction();
return;
return false;
}
boolean entityWasSelected = chosenCards.containsKey(card);
@@ -57,11 +57,12 @@ public final class InputSelectCardsForConvoke extends InputSelectManyBase<Card>
if (remainingCost.getColorlessManaAmount() > 0 && (chosenColor == 0 || !remainingCost.needsColor(chosenColor, player.getManaPool()))) {
registerConvoked(card, ManaCostShard.COLORLESS, chosenColor);
} else {
}
else {
for (ManaCostShard shard : remainingCost.getDistinctShards()) {
if (shard.canBePaidWithManaOfColor(chosenColor)) {
registerConvoked(card, shard, chosenColor);
return;
return true;
}
}
showMessage("The colors provided by " + card.toString() + " you've chosen cannot be used to decrease the manacost of " + remainingCost.toString());
@@ -70,6 +71,7 @@ public final class InputSelectCardsForConvoke extends InputSelectManyBase<Card>
}
refresh();
return true;
}
private void registerConvoked(Card card, ManaCostShard shard, byte chosenColor) {

View File

@@ -25,11 +25,12 @@ public class InputSelectEntitiesFromList<T extends GameEntity> extends InputSele
}
@Override
protected void onCardSelected(final Card c, final ITriggerEvent triggerEvent) {
protected boolean onCardSelected(final Card c, final ITriggerEvent triggerEvent) {
if (!selectEntity(c)) {
return;
return false;
}
refresh();
return true;
}
@Override

View File

@@ -104,33 +104,33 @@ public final class InputSelectTargets extends InputSyncronizedBase {
}
@Override
protected final void onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
protected final boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) {
if (!tgt.isUniqueTargets() && targetDepth.containsKey(card)) {
return;
return false;
}
// leave this in temporarily, there some seriously wrong things going on here
// Can be targeted doesn't check if the target is a valid type, only if a card is generally "targetable"
if (!card.canBeTargetedBy(sa)) {
showMessage(sa.getHostCard() + " - Cannot target this card (Shroud? Protection? Restrictions).");
return;
return false;
}
// If all cards must be from the same zone
if (tgt.isSingleZone() && lastTarget != null && !card.getController().equals(lastTarget.getController())) {
showMessage(sa.getHostCard() + " - Cannot target this card (not in the same zone)");
return;
return false;
}
// If all cards must be from different zones
if (tgt.isDifferentZone() && lastTarget != null && !card.getController().equals(lastTarget.getController().getOpponent())) {
showMessage(sa.getHostCard() + " - Cannot target this card (not in different zones)");
return;
return false;
}
// If the cards can't share a creature type
if (tgt.isWithoutSameCreatureType() && lastTarget != null && card.sharesCreatureTypeWith(lastTarget)) {
showMessage(sa.getHostCard() + " - Cannot target this card (should not share a creature type)");
return;
return false;
}
// If all cards must have different controllers
@@ -144,17 +144,18 @@ public final class InputSelectTargets extends InputSyncronizedBase {
}
if (targetedControllers.contains(card.getController())) {
showMessage(sa.getHostCard() + " - Cannot target this card (must have different controllers)");
return;
return false;
}
}
if (!choices.contains(card)) {
if (card.isPlaneswalker() && sa.getApi() == ApiType.DealDamage) {
showMessage(sa.getHostCard() + " - To deal an opposing Planeswalker direct damage, target its controller and then redirect the damage on resolution.");
} else {
}
else {
showMessage(sa.getHostCard() + " - The selected card is not a valid choice to be targeted.");
}
return;
return false;
}
if (tgt.isDividedAsYouChoose()) {
@@ -179,8 +180,8 @@ public final class InputSelectTargets extends InputSyncronizedBase {
sb.append(apiBasedMessage);
sb.append(card.toString());
Integer chosen = SGuiChoose.oneOrNone(sb.toString(), choices);
if (null == chosen) {
return;
if (chosen == null) {
return true; //still return true since there was a valid choice
}
allocatedPortion = chosen;
} else { // otherwise assign the rest of the damage/protection
@@ -190,6 +191,7 @@ public final class InputSelectTargets extends InputSyncronizedBase {
tgt.addDividedAllocation(card, allocatedPortion);
}
addTarget(card);
return true;
} // selectCard()
@Override

View File

@@ -727,11 +727,12 @@ public class HumanCostDecision extends CostDecisionMakerBase {
private static final long serialVersionUID = 8338626212893374798L;
@Override
protected void onCardSelected(Card c, ITriggerEvent triggerEvent) {
protected boolean onCardSelected(Card c, ITriggerEvent triggerEvent) {
Card firstCard = Iterables.getFirst(this.selected, null);
if(firstCard != null && !CardPredicates.sharesColorWith(firstCard).apply(c))
return;
super.onCardSelected(c, triggerEvent);
if (firstCard != null && !CardPredicates.sharesColorWith(firstCard).apply(c)) {
return false;
}
return super.onCardSelected(c, triggerEvent);
}
};
inp.setMessage("Select " + Lang.nounWithAmount(num, "card" ) + " of same color to reveal.");
@@ -817,9 +818,9 @@ public class HumanCostDecision extends CostDecisionMakerBase {
}
@Override
protected void onCardSelected(Card c, ITriggerEvent triggerEvent) {
protected boolean onCardSelected(Card c, ITriggerEvent triggerEvent) {
if (!isValidChoice(c) || c.getCounters(counterType) <= getTimesSelected(c)) {
return;
return false;
}
int tc = getTimesSelected(c);
@@ -827,6 +828,7 @@ public class HumanCostDecision extends CostDecisionMakerBase {
onSelectStateChanged(c, true);
refresh();
return true;
};
@Override