mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
payCostDuringAbilityResolve moved to HumanPlay
MagicStack is Iterable<SpellAbilityStackInstance>, get(i) methods removed, many invocations of size() replaced with isEmpty or iteration when appropriate/ GameActionUtil - some other methods inlined for being 2-lines long or used only once
This commit is contained in:
@@ -53,6 +53,7 @@ import forge.card.mana.ManaCost;
|
||||
import forge.card.replacement.ReplaceMoved;
|
||||
import forge.card.replacement.ReplacementEffect;
|
||||
import forge.card.replacement.ReplacementResult;
|
||||
import forge.card.spellability.Ability;
|
||||
import forge.card.spellability.AbilityTriggered;
|
||||
import forge.card.spellability.OptionalCost;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
@@ -62,9 +63,9 @@ import forge.card.staticability.StaticAbility;
|
||||
import forge.card.trigger.Trigger;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.card.trigger.ZCTrigger;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.event.CardDamagedEvent;
|
||||
import forge.game.event.CardEquippedEvent;
|
||||
import forge.game.event.CounterAddedEvent;
|
||||
import forge.game.event.CounterRemovedEvent;
|
||||
@@ -7406,7 +7407,9 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
this.addReceivedDamageFromThisTurn(source, damageToAdd);
|
||||
source.addDealtDamageToThisTurn(this, damageToAdd);
|
||||
|
||||
GameActionUtil.executeDamageDealingEffects(source, damageToAdd);
|
||||
if (source.hasKeyword("Lifelink")) {
|
||||
source.getController().gainLife(damageToAdd, source);
|
||||
}
|
||||
|
||||
// Run triggers
|
||||
final Map<String, Object> runParams = new TreeMap<String, Object>();
|
||||
@@ -7421,25 +7424,57 @@ public class Card extends GameEntity implements Comparable<Card> {
|
||||
this.subtractCounter(CounterType.LOYALTY, damageToAdd);
|
||||
additionalLog = String.format("(Removing %d Loyalty Counters)", damageToAdd);
|
||||
} else {
|
||||
|
||||
final GameState game = source.getGame();
|
||||
|
||||
final String s = this + " - destroy";
|
||||
|
||||
final int amount = this.getAmountOfKeyword("When CARDNAME is dealt damage, destroy it.");
|
||||
if(amount > 0) {
|
||||
final Ability abDestroy = new Ability(source, ManaCost.ZERO){
|
||||
@Override public void resolve() { game.getAction().destroy(Card.this, this); }
|
||||
};
|
||||
abDestroy.setStackDescription(s + ", it cannot be regenerated.");
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
game.getStack().addSimultaneousStackEntry(abDestroy);
|
||||
}
|
||||
}
|
||||
|
||||
final int amount2 = this.getAmountOfKeyword("When CARDNAME is dealt damage, destroy it. It can't be regenerated.");
|
||||
if( amount2 > 0 ) {
|
||||
final Ability abDestoryNoRegen = new Ability(source, ManaCost.ZERO){
|
||||
@Override public void resolve() { game.getAction().destroyNoRegeneration(Card.this, this); }
|
||||
};
|
||||
abDestoryNoRegen.setStackDescription(s);
|
||||
|
||||
for (int i = 0; i < amount2; i++) {
|
||||
game.getStack().addSimultaneousStackEntry(abDestoryNoRegen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean wither = (getGame().getStaticEffects().getGlobalRuleChange(GlobalRuleChange.alwaysWither)
|
||||
|| source.hasKeyword("Wither") || source.hasKeyword("Infect"));
|
||||
|
||||
GameActionUtil.executeDamageToCreatureEffects(source, this, damageToAdd);
|
||||
|
||||
if (this.isInPlay() && wither) {
|
||||
this.addCounter(CounterType.M1M1, damageToAdd, true);
|
||||
additionalLog = "(As -1/-1 Counters)";
|
||||
if (this.isInPlay()) {
|
||||
if (wither) {
|
||||
this.addCounter(CounterType.M1M1, damageToAdd, true);
|
||||
additionalLog = "(As -1/-1 Counters)";
|
||||
} else
|
||||
this.damage += damageToAdd;
|
||||
}
|
||||
|
||||
if (source.hasKeyword("Deathtouch") && this.isCreature()) {
|
||||
getGame().getAction().destroy(this, null);
|
||||
additionalLog = "(Deathtouch)";
|
||||
} else if (this.isInPlay() && !wither) {
|
||||
this.damage += damageToAdd;
|
||||
}
|
||||
}
|
||||
|
||||
// Play the Damage sound
|
||||
game.getEvents().post(new CardDamagedEvent());
|
||||
}
|
||||
|
||||
getGame().getGameLog().add("Damage", String.format("Dealing %d damage to %s. %s",
|
||||
damageToAdd, this.getName(), additionalLog), 3);
|
||||
getGame().getGameLog().add("Damage", String.format("Dealing %d damage to %s. %s", damageToAdd, this.getName(), additionalLog), 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ import forge.card.spellability.Ability;
|
||||
import forge.card.spellability.AbilityStatic;
|
||||
import forge.card.spellability.AbilitySub;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.player.HumanPlay;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.Expressions;
|
||||
@@ -1107,13 +1107,13 @@ public class AbilityUtils {
|
||||
for (Player payer : payers) {
|
||||
ability.setActivatingPlayer(payer);
|
||||
if (payer.isComputer()) {
|
||||
if (ComputerUtilCost.willPayUnlessCost(sa, payer, ability, paid, payers)) {
|
||||
if (ComputerUtilCost.willPayUnlessCost(sa, payer, ability, paid, payers) && ComputerUtilCost.canPayCost(ability, payer)) {
|
||||
ComputerUtil.playNoStack(payer, ability, game); // Unless cost was payed - no resolve
|
||||
paid = true;
|
||||
}
|
||||
} else {
|
||||
// if it's paid by the AI already the human can pay, but it won't change anything
|
||||
paid |= GameActionUtil.payCostDuringAbilityResolve(ability, cost, sa, game);
|
||||
paid |= HumanPlay.payCostDuringAbilityResolve(ability, cost, sa, game);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -588,7 +588,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
// in general this should only be used to protect from Imminent Harm
|
||||
// (dying or losing control of)
|
||||
if (origin.equals(ZoneType.Battlefield)) {
|
||||
if (ai.getGame().getStack().size() == 0) {
|
||||
if (ai.getGame().getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -720,7 +720,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
|
||||
// check stack for something on the stack that will kill
|
||||
// anything i control
|
||||
if (ai.getGame().getStack().size() > 0) {
|
||||
if (!ai.getGame().getStack().isEmpty()) {
|
||||
final ArrayList<Object> objects = ComputerUtil.predictThreatenedObjects(ai, sa);
|
||||
|
||||
final List<Card> threatenedTargets = new ArrayList<Card>();
|
||||
|
||||
@@ -56,7 +56,7 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
sa.getParam("Defined"), sa);
|
||||
|
||||
// react to threats on the stack
|
||||
if (game.getStack().size() > 0) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
final ArrayList<Object> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
||||
for (final Object o : objects) {
|
||||
if (threatenedObjects.contains(o)) {
|
||||
@@ -90,7 +90,7 @@ public class DamagePreventAi extends SpellAbilityAi {
|
||||
} // targeted
|
||||
|
||||
// react to threats on the stack
|
||||
else if (game.getStack().size() > 0) {
|
||||
else if (!game.getStack().isEmpty()) {
|
||||
tgt.resetTargets();
|
||||
// check stack for something on the stack will kill anything i
|
||||
// control
|
||||
|
||||
@@ -38,7 +38,7 @@ public class DamagePreventAllAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ai.getGame().getStack().size() > 0) {
|
||||
if (!ai.getGame().getStack().isEmpty()) {
|
||||
// TODO check stack for something on the stack will kill anything i
|
||||
// control
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public class EffectAi extends SpellAbilityAi {
|
||||
if (!game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY)) {
|
||||
return false;
|
||||
}
|
||||
if (game.getStack().size() != 0) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (game.getPhaseHandler().isPreventCombatDamageThisTurn()) {
|
||||
|
||||
@@ -137,13 +137,13 @@ public class ProtectAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
// Phase Restrictions
|
||||
if ((game.getStack().size() == 0) && game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE)) {
|
||||
if (game.getStack().isEmpty() && game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_FIRST_STRIKE_DAMAGE)) {
|
||||
// Instant-speed protections should not be cast outside of combat
|
||||
// when the stack is empty
|
||||
if (!SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||
return false;
|
||||
}
|
||||
} else if (game.getStack().size() > 0) {
|
||||
} else if (!game.getStack().isEmpty()) {
|
||||
// TODO protection something only if the top thing on the stack will
|
||||
// kill it via damage or destroy
|
||||
return false;
|
||||
|
||||
@@ -83,13 +83,13 @@ public class PumpAi extends PumpAiBase {
|
||||
}
|
||||
|
||||
// Phase Restrictions
|
||||
if ((game.getStack().size() == 0) && ph.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) {
|
||||
if (game.getStack().isEmpty() && ph.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) {
|
||||
// Instant-speed pumps should not be cast outside of combat when the
|
||||
// stack is empty
|
||||
if (!sa.isCurse() && !SpellAbilityAi.isSorcerySpeed(sa)) {
|
||||
return false;
|
||||
}
|
||||
} else if (game.getStack().size() > 0) {
|
||||
} else if (!game.getStack().isEmpty()) {
|
||||
if (!keywords.contains("Shroud") && !keywords.contains("Hexproof")) {
|
||||
return false;
|
||||
}
|
||||
@@ -228,7 +228,7 @@ public class PumpAi extends PumpAiBase {
|
||||
}
|
||||
|
||||
list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, sa.getSourceCard());
|
||||
if (game.getStack().size() == 0) {
|
||||
if (game.getStack().isEmpty()) {
|
||||
// If the cost is tapping, don't activate before declare
|
||||
// attack/block
|
||||
if ((sa.getPayCosts() != null) && sa.getPayCosts().hasTapCost()) {
|
||||
|
||||
@@ -83,7 +83,7 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
// them
|
||||
final List<Card> list = AbilityUtils.getDefinedCards(hostCard, sa.getParam("Defined"), sa);
|
||||
|
||||
if (game.getStack().size() > 0) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
final List<Object> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
||||
|
||||
for (final Card c : list) {
|
||||
@@ -117,7 +117,7 @@ public class RegenerateAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (game.getStack().size() > 0) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
// check stack for something on the stack will kill anything i
|
||||
// control
|
||||
final ArrayList<Object> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
||||
|
||||
@@ -56,7 +56,7 @@ public class RegenerateAllAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
int numSaved = 0;
|
||||
if (game.getStack().size() > 0) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
final ArrayList<Object> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa);
|
||||
|
||||
for (final Card c : list) {
|
||||
|
||||
@@ -178,34 +178,35 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
|
||||
}
|
||||
|
||||
private Card ChooseCardOnStack(SpellAbility sa, Player ai, GameState game) {
|
||||
int size = game.getStack().size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final SpellAbility topStack = game.getStack().peekAbility(i);
|
||||
if (sa.hasParam("Choices") && !topStack.getSourceCard().isValid(sa.getParam("Choices"), ai, sa.getSourceCard())) {
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
final Card source = si.getSourceCard();
|
||||
final SpellAbility abilityOnStack = si.getSpellAbility();
|
||||
|
||||
if (sa.hasParam("Choices") && !abilityOnStack.getSourceCard().isValid(sa.getParam("Choices"), ai, sa.getSourceCard())) {
|
||||
continue;
|
||||
}
|
||||
final ApiType threatApi = topStack.getApi();
|
||||
final ApiType threatApi = abilityOnStack.getApi();
|
||||
if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Card source = topStack.getSourceCard();
|
||||
|
||||
ArrayList<Object> objects = new ArrayList<Object>();
|
||||
final Target threatTgt = topStack.getTarget();
|
||||
final Target threatTgt = abilityOnStack.getTarget();
|
||||
|
||||
if (threatTgt == null) {
|
||||
if (topStack.hasParam("Defined")) {
|
||||
objects = AbilityUtils.getDefinedObjects(source, topStack.getParam("Defined"), topStack);
|
||||
} else if (topStack.hasParam("ValidPlayers")) {
|
||||
objects.addAll(AbilityUtils.getDefinedPlayers(source, topStack.getParam("ValidPlayers"), topStack));
|
||||
if (abilityOnStack.hasParam("Defined")) {
|
||||
objects = AbilityUtils.getDefinedObjects(source, abilityOnStack.getParam("Defined"), abilityOnStack);
|
||||
} else if (abilityOnStack.hasParam("ValidPlayers")) {
|
||||
objects.addAll(AbilityUtils.getDefinedPlayers(source, abilityOnStack.getParam("ValidPlayers"), abilityOnStack));
|
||||
}
|
||||
} else {
|
||||
objects.addAll(threatTgt.getTargetPlayers());
|
||||
}
|
||||
if (!objects.contains(ai) || topStack.hasParam("NoPrevention")) {
|
||||
if (!objects.contains(ai) || abilityOnStack.hasParam("NoPrevention")) {
|
||||
continue;
|
||||
}
|
||||
int dmg = AbilityUtils.calculateAmount(source, topStack.getParam("NumDmg"), topStack);
|
||||
int dmg = AbilityUtils.calculateAmount(source, abilityOnStack.getParam("NumDmg"), abilityOnStack);
|
||||
if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false) <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
|
||||
if (sa.hasParam("AllType")) {
|
||||
sas = new ArrayList<SpellAbility>();
|
||||
for (int i = 0; i < game.getStack().size(); i++) {
|
||||
SpellAbility spell = game.getStack().peekAbility(i);
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
SpellAbility spell = si.getSpellAbility();
|
||||
if (sa.getParam("AllType").equals("Spell") && !spell.isSpell()) {
|
||||
continue;
|
||||
}
|
||||
@@ -69,8 +69,8 @@ public class CounterEffect extends SpellAbilityEffect {
|
||||
|
||||
if (sa.hasParam("AllType")) {
|
||||
sas = new ArrayList<SpellAbility>();
|
||||
for (int i = 0; i < game.getStack().size(); i++) {
|
||||
SpellAbility spell = game.getStack().peekAbility(i);
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
SpellAbility spell = si.getSpellAbility();
|
||||
if (sa.getParam("AllType").equals("Spell") && !spell.isSpell()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import forge.control.input.InputSelectCardsFromList;
|
||||
import forge.game.GameState;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.MagicStack;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.GuiDialog;
|
||||
@@ -162,8 +161,8 @@ public class CostExile extends CostPartWithList {
|
||||
return true; // this will always work
|
||||
}
|
||||
if (this.getFrom().equals(ZoneType.Stack)) {
|
||||
for (int i = 0; i < game.getStack().size(); i++) {
|
||||
typeList.add(game.getStack().peekAbility(i).getSourceCard());
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
typeList.add(si.getSourceCard());
|
||||
}
|
||||
} else {
|
||||
if (this.sameZone) {
|
||||
@@ -336,13 +335,13 @@ public class CostExile extends CostPartWithList {
|
||||
return true;
|
||||
}
|
||||
|
||||
final GameState game = sa.getActivatingPlayer().getGame();
|
||||
ArrayList<SpellAbility> saList = new ArrayList<SpellAbility>();
|
||||
ArrayList<String> descList = new ArrayList<String>();
|
||||
final MagicStack stack = sa.getActivatingPlayer().getGame().getStack();
|
||||
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
final Card stC = stack.peekAbility(i).getSourceCard();
|
||||
final SpellAbility stSA = stack.peekAbility(i).getRootAbility();
|
||||
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
final Card stC = si.getSourceCard();
|
||||
final SpellAbility stSA = si.getSpellAbility().getRootAbility();
|
||||
if (stC.isValid(getType().split(";"), sa.getActivatingPlayer(), sa.getSourceCard()) && stSA.isSpell()) {
|
||||
saList.add(stSA);
|
||||
if (stC.isCopiedSpell()) {
|
||||
@@ -373,8 +372,8 @@ public class CostExile extends CostPartWithList {
|
||||
} else {
|
||||
addToList(c);
|
||||
}
|
||||
final SpellAbilityStackInstance si = stack.getInstanceFromSpellAbility(toExile);
|
||||
stack.remove(si);
|
||||
final SpellAbilityStackInstance si = game.getStack().getInstanceFromSpellAbility(toExile);
|
||||
game.getStack().remove(si);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -51,9 +51,6 @@ public class SpellAbilityStackInstance {
|
||||
private TargetChoices tc = null;
|
||||
private List<Card> splicedCards = null;
|
||||
|
||||
/** The activating player. */
|
||||
private Player activatingPlayer = null;
|
||||
|
||||
/** The stack description. */
|
||||
private String stackDescription = null;
|
||||
|
||||
@@ -89,7 +86,6 @@ public class SpellAbilityStackInstance {
|
||||
// Base SA info
|
||||
this.ability = sa;
|
||||
this.stackDescription = this.ability.getStackDescription();
|
||||
this.activatingPlayer = sa.getActivatingPlayer();
|
||||
|
||||
// Payment info
|
||||
this.paidHash = this.ability.getPaidHash();
|
||||
@@ -140,7 +136,6 @@ public class SpellAbilityStackInstance {
|
||||
this.ability.getTarget().resetTargets();
|
||||
this.ability.getTarget().setTargetChoices(this.tc);
|
||||
}
|
||||
this.ability.setActivatingPlayer(this.activatingPlayer);
|
||||
|
||||
// Saved sub-SA needs to be reset on the way out
|
||||
if (this.subInstace != null) {
|
||||
@@ -191,17 +186,6 @@ public class SpellAbilityStackInstance {
|
||||
return this.ability.getSourceCard();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Getter for the field <code>activatingPlayer</code>.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.game.player.Player} object.
|
||||
*/
|
||||
public final Player getActivatingPlayer() {
|
||||
return this.activatingPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* isSpell.
|
||||
|
||||
@@ -310,14 +310,14 @@ public class TargetSelection {
|
||||
final List<Object> selectOptions = new ArrayList<Object>();
|
||||
|
||||
final GameState game = ability.getActivatingPlayer().getGame();
|
||||
for (int i = 0; i < game.getStack().size(); i++) {
|
||||
SpellAbility stackItem = game.getStack().peekAbility(i);
|
||||
if (ability.equals(stackItem)) {
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
SpellAbility abilityOnStack = si.getSpellAbility();
|
||||
if (ability.equals(abilityOnStack)) {
|
||||
// By peeking at stack item, target is set to its SI state. So set it back before adding targets
|
||||
tgt.resetTargets();
|
||||
}
|
||||
else if (ability.canTargetSpellAbility(stackItem)) {
|
||||
selectOptions.add(stackItem);
|
||||
else if (ability.canTargetSpellAbility(abilityOnStack)) {
|
||||
selectOptions.add(abilityOnStack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public class TriggerSpellAbilityCast extends Trigger {
|
||||
}
|
||||
|
||||
if (this.mapParams.containsKey("ValidActivatingPlayer")) {
|
||||
if (si == null || !matchesValid(si.getActivatingPlayer(), this.mapParams.get("ValidActivatingPlayer")
|
||||
if (si == null || !matchesValid(si.getSpellAbility().getActivatingPlayer(), this.mapParams.get("ValidActivatingPlayer")
|
||||
.split(","), this.getHostCard())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public class InputPassPriority extends InputBase {
|
||||
sb.append("Turn : ").append(ph.getPlayerTurn()).append("\n");
|
||||
sb.append("Phase: ").append(ph.getPhase().Name).append("\n");
|
||||
sb.append("Stack: ");
|
||||
if (player.getGame().getStack().size() != 0) {
|
||||
if (!player.getGame().getStack().isEmpty()) {
|
||||
sb.append(player.getGame().getStack().size()).append(" to Resolve.");
|
||||
} else {
|
||||
sb.append("Empty");
|
||||
|
||||
@@ -522,7 +522,7 @@ public class GameAction {
|
||||
Player p = recoverable.getController();
|
||||
|
||||
if (p.isHuman()) {
|
||||
if ( GameActionUtil.payCostDuringAbilityResolve(abRecover, abRecover.getPayCosts(), null, game) )
|
||||
if ( HumanPlay.payCostDuringAbilityResolve(abRecover, abRecover.getPayCosts(), null, game) )
|
||||
moveToHand(recoverable);
|
||||
else
|
||||
exile(recoverable);
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
@@ -36,27 +35,12 @@ import forge.CardUtil;
|
||||
import forge.Command;
|
||||
import forge.Constant;
|
||||
import forge.CounterType;
|
||||
import forge.FThreads;
|
||||
import forge.card.ability.AbilityFactory;
|
||||
import forge.card.ability.AbilityFactory.AbilityRecordType;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
import forge.card.ability.ApiType;
|
||||
import forge.card.cardfactory.CardFactoryUtil;
|
||||
import forge.card.cost.Cost;
|
||||
import forge.card.cost.CostDamage;
|
||||
import forge.card.cost.CostDiscard;
|
||||
import forge.card.cost.CostExile;
|
||||
import forge.card.cost.CostMill;
|
||||
import forge.card.cost.CostPart;
|
||||
import forge.card.cost.CostPartMana;
|
||||
import forge.card.cost.CostPartWithList;
|
||||
import forge.card.cost.CostPayLife;
|
||||
import forge.card.cost.CostPutCounter;
|
||||
import forge.card.cost.CostRemoveCounter;
|
||||
import forge.card.cost.CostReturn;
|
||||
import forge.card.cost.CostReveal;
|
||||
import forge.card.cost.CostSacrifice;
|
||||
import forge.card.cost.CostTapType;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostBeingPaid;
|
||||
import forge.card.spellability.Ability;
|
||||
@@ -66,13 +50,7 @@ import forge.card.spellability.AbilitySub;
|
||||
import forge.card.spellability.OptionalCost;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.SpellAbilityRestriction;
|
||||
import forge.control.input.InputPayManaExecuteCommands;
|
||||
import forge.control.input.InputPayment;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.control.input.InputSelectCardsFromList;
|
||||
import forge.game.ai.AiController;
|
||||
import forge.game.event.CardDamagedEvent;
|
||||
import forge.game.event.LifeLossEvent;
|
||||
import forge.game.player.HumanPlay;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerControllerAi;
|
||||
@@ -92,32 +70,7 @@ import forge.util.TextUtil;
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class GameActionUtil {
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
*
|
||||
*/
|
||||
private static final class AbilityDestroy extends Ability {
|
||||
private final Card affected;
|
||||
private final boolean canRegenerate;
|
||||
|
||||
public AbilityDestroy(Card sourceCard, Card affected, boolean canRegenerate) {
|
||||
super(sourceCard, ManaCost.ZERO);
|
||||
this.affected = affected;
|
||||
this.canRegenerate = canRegenerate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resolve() {
|
||||
final GameState game = affected.getGame();
|
||||
if ( canRegenerate )
|
||||
game.getAction().destroy(affected, this);
|
||||
else
|
||||
game.getAction().destroyNoRegeneration(affected, this);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CascadeAbility extends Ability {
|
||||
private final Player controller;
|
||||
private final Card cascCard;
|
||||
@@ -368,7 +321,7 @@ public final class GameActionUtil {
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
*/
|
||||
public static void executePlayCardEffects(final SpellAbility sa) {
|
||||
// (called in MagicStack.java)
|
||||
// (called from MagicStack.java)
|
||||
|
||||
final GameState game = sa.getActivatingPlayer().getGame();
|
||||
final Command cascade = new CascadeExecutor(sa.getActivatingPlayer(), sa.getSourceCard(), game);
|
||||
@@ -377,307 +330,6 @@ public final class GameActionUtil {
|
||||
ripple.run();
|
||||
}
|
||||
|
||||
private static int getAmountFromPart(CostPart part, Card source, SpellAbility sourceAbility) {
|
||||
String amountString = part.getAmount();
|
||||
return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : AbilityUtils.calculateAmount(source, amountString, sourceAbility);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param part
|
||||
* @param source
|
||||
* @param sourceAbility
|
||||
* @return
|
||||
*/
|
||||
private static int getAmountFromPartX(CostPart part, Card source, SpellAbility sourceAbility) {
|
||||
String amountString = part.getAmount();
|
||||
return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payCostDuringAbilityResolve.
|
||||
* </p>
|
||||
*
|
||||
* @param ability
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param cost
|
||||
* a {@link forge.card.cost.Cost} object.
|
||||
* @param paid
|
||||
* a {@link forge.Command} object.
|
||||
* @param unpaid
|
||||
* a {@link forge.Command} object.
|
||||
* @param sourceAbility TODO
|
||||
*/
|
||||
public static boolean payCostDuringAbilityResolve(final SpellAbility ability, final Cost cost, SpellAbility sourceAbility, final GameState game) {
|
||||
|
||||
// Only human player pays this way
|
||||
final Player p = ability.getActivatingPlayer();
|
||||
final Card source = ability.getSourceCard();
|
||||
Card current = null; // Used in spells with RepeatEach effect to distinguish cards, Cut the Tethers
|
||||
if (!source.getRemembered().isEmpty()) {
|
||||
if (source.getRemembered().get(0) instanceof Card) {
|
||||
current = (Card) source.getRemembered().get(0);
|
||||
}
|
||||
}
|
||||
if (!source.getImprinted().isEmpty()) {
|
||||
current = source.getImprinted().get(0);
|
||||
}
|
||||
|
||||
final List<CostPart> parts = cost.getCostParts();
|
||||
ArrayList<CostPart> remainingParts = new ArrayList<CostPart>(cost.getCostParts());
|
||||
CostPart costPart = null;
|
||||
if (!parts.isEmpty()) {
|
||||
costPart = parts.get(0);
|
||||
}
|
||||
final String orString = sourceAbility == null ? "" : " (or: " + sourceAbility.getStackDescription() + ")";
|
||||
|
||||
if (parts.isEmpty() || costPart.getAmount().equals("0")) {
|
||||
return GuiDialog.confirm(source, "Do you want to pay 0?" + orString);
|
||||
}
|
||||
|
||||
//the following costs do not need inputs
|
||||
for (CostPart part : parts) {
|
||||
boolean mayRemovePart = true;
|
||||
|
||||
if (part instanceof CostPayLife) {
|
||||
final int amount = getAmountFromPart(part, source, sourceAbility);
|
||||
if (!p.canPayLife(amount))
|
||||
return false;
|
||||
|
||||
if (false == GuiDialog.confirm(source, "Do you want to pay " + amount + " life?" + orString))
|
||||
return false;
|
||||
|
||||
p.payLife(amount, null);
|
||||
}
|
||||
|
||||
else if (part instanceof CostMill) {
|
||||
final int amount = getAmountFromPart(part, source, sourceAbility);
|
||||
final List<Card> list = p.getCardsIn(ZoneType.Library);
|
||||
if (list.size() < amount) return false;
|
||||
if (!GuiDialog.confirm(source, "Do you want to mill " + amount + " card(s)?" + orString))
|
||||
return false;
|
||||
List<Card> listmill = p.getCardsIn(ZoneType.Library, amount);
|
||||
((CostMill) part).executePayment(sourceAbility, listmill);
|
||||
}
|
||||
|
||||
else if (part instanceof CostDamage) {
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
if (!p.canPayLife(amount))
|
||||
return false;
|
||||
|
||||
if (false == GuiDialog.confirm(source, "Do you want " + source + " to deal " + amount + " damage to you?"))
|
||||
return false;
|
||||
|
||||
p.addDamage(amount, source);
|
||||
}
|
||||
|
||||
else if (part instanceof CostPutCounter) {
|
||||
CounterType counterType = ((CostPutCounter) part).getCounter();
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
|
||||
if (false == source.canReceiveCounters(counterType)) {
|
||||
String message = String.format("Won't be able to pay upkeep for %s but it can't have %s counters put on it.", source, counterType.getName());
|
||||
p.getGame().getGameLog().add("ResolveStack", message, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
String plural = amount > 1 ? "s" : "";
|
||||
if (false == GuiDialog.confirm(source, "Do you want to put " + amount + " " + counterType.getName() + " counter" + plural + " on " + source + "?"))
|
||||
return false;
|
||||
|
||||
source.addCounter(counterType, amount, false);
|
||||
}
|
||||
|
||||
else if (part instanceof CostRemoveCounter) {
|
||||
CounterType counterType = ((CostRemoveCounter) part).getCounter();
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
String plural = amount > 1 ? "s" : "";
|
||||
|
||||
if (!part.canPay(sourceAbility))
|
||||
return false;
|
||||
|
||||
if ( false == GuiDialog.confirm(source, "Do you want to remove " + amount + " " + counterType.getName() + " counter" + plural + " from " + source + "?"))
|
||||
return false;
|
||||
|
||||
source.subtractCounter(counterType, amount);
|
||||
}
|
||||
|
||||
else if (part instanceof CostExile) {
|
||||
if ("All".equals(part.getType())) {
|
||||
if (false == GuiDialog.confirm(source, "Do you want to exile all cards in your graveyard?"))
|
||||
return false;
|
||||
|
||||
List<Card> cards = new ArrayList<Card>(p.getCardsIn(ZoneType.Graveyard));
|
||||
for (final Card card : cards) {
|
||||
p.getGame().getAction().exile(card);
|
||||
}
|
||||
} else {
|
||||
CostExile costExile = (CostExile) part;
|
||||
ZoneType from = costExile.getFrom();
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source);
|
||||
final int nNeeded = AbilityUtils.calculateAmount(source, part.getAmount(), ability);
|
||||
if (list.size() < nNeeded)
|
||||
return false;
|
||||
|
||||
// replace this with input
|
||||
for (int i = 0; i < nNeeded; i++) {
|
||||
final Card c = GuiChoose.oneOrNone("Exile from " + from, list);
|
||||
if (c == null)
|
||||
return false;
|
||||
|
||||
list.remove(c);
|
||||
p.getGame().getAction().exile(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (part instanceof CostSacrifice) {
|
||||
int amount = Integer.parseInt(((CostSacrifice)part).getAmount());
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "sacrifice." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostReturn) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "return to hand." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostDiscard) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "discard." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostReveal) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "reveal." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostTapType) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
list = CardLists.filter(list, Presets.UNTAPPED);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "tap." + orString);
|
||||
if(!hasPaid) return false;
|
||||
}
|
||||
|
||||
else if (part instanceof CostPartMana ) {
|
||||
if (!((CostPartMana) part).getManaToPay().isZero()) // non-zero costs require input
|
||||
mayRemovePart = false;
|
||||
} else
|
||||
throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - An unhandled type of cost was met: " + part.getClass());
|
||||
|
||||
if( mayRemovePart )
|
||||
remainingParts.remove(part);
|
||||
}
|
||||
|
||||
|
||||
if (remainingParts.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (remainingParts.size() > 1) {
|
||||
throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - Too many payment types - " + source);
|
||||
}
|
||||
costPart = remainingParts.get(0);
|
||||
// check this is a mana cost
|
||||
if (!(costPart instanceof CostPartMana ))
|
||||
throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - The remaining payment type is not Mana.");
|
||||
|
||||
InputPayment toSet = current == null
|
||||
? new InputPayManaExecuteCommands(p, source + "\r\n", cost.getCostMana().getManaToPay())
|
||||
: new InputPayManaExecuteCommands(p, source + "\r\n" + "Current Card: " + current + "\r\n" , cost.getCostMana().getManaToPay());
|
||||
FThreads.setInputAndWait(toSet);
|
||||
return toSet.isPaid();
|
||||
}
|
||||
|
||||
private static boolean payCostPart(SpellAbility sourceAbility, CostPartWithList cpl, int amount, List<Card> list, String actionName) {
|
||||
if (list.size() < amount) return false; // unable to pay (not enough cards)
|
||||
|
||||
InputSelectCards inp = new InputSelectCardsFromList(amount, amount, list);
|
||||
inp.setMessage("Select %d " + cpl.getDescriptiveType() + " card(s) to " + actionName);
|
||||
inp.setCancelAllowed(true);
|
||||
|
||||
FThreads.setInputAndWait(inp);
|
||||
if( inp.hasCancelled() || inp.getSelected().size() != amount)
|
||||
return false;
|
||||
|
||||
for(Card c : inp.getSelected()) {
|
||||
cpl.executePayment(sourceAbility, c);
|
||||
}
|
||||
if (sourceAbility != null) {
|
||||
cpl.reportPaidCardsTo(sourceAbility);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// not restricted to combat damage, not restricted to dealing damage to
|
||||
// creatures/players
|
||||
/**
|
||||
* <p>
|
||||
* executeDamageDealingEffects.
|
||||
* </p>
|
||||
*
|
||||
* @param source
|
||||
* a {@link forge.Card} object.
|
||||
* @param damage
|
||||
* a int.
|
||||
*/
|
||||
public static void executeDamageDealingEffects(final Card source, final int damage) {
|
||||
|
||||
if (damage <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.hasKeyword("Lifelink")) {
|
||||
source.getController().gainLife(damage, source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// not restricted to combat damage, restricted to dealing damage to
|
||||
// creatures
|
||||
/**
|
||||
* <p>
|
||||
* executeDamageToCreatureEffects.
|
||||
* </p>
|
||||
*
|
||||
* @param source
|
||||
* a {@link forge.Card} object.
|
||||
* @param affected
|
||||
* a {@link forge.Card} object.
|
||||
* @param damage
|
||||
* a int.
|
||||
*/
|
||||
public static void executeDamageToCreatureEffects(final Card source, final Card affected, final int damage) {
|
||||
|
||||
if (damage <= 0) {
|
||||
return;
|
||||
}
|
||||
final GameState game = source.getGame();
|
||||
|
||||
if (affected.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) {
|
||||
final Ability ability = new AbilityDestroy(source, affected, true);
|
||||
final Ability ability2 = new AbilityDestroy(source, affected, false);
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(affected).append(" - destroy");
|
||||
ability.setStackDescription(sb.toString());
|
||||
ability2.setStackDescription(sb.toString());
|
||||
final int amount = affected.getAmountOfKeyword("When CARDNAME is dealt damage, destroy it. It can't be regenerated.");
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
game.getStack().addSimultaneousStackEntry(ability2);
|
||||
}
|
||||
final int amount2 = affected.getAmountOfKeyword("When CARDNAME is dealt damage, destroy it.");
|
||||
|
||||
for (int i = 0; i < amount2; i++) {
|
||||
game.getStack().addSimultaneousStackEntry(ability);
|
||||
}
|
||||
}
|
||||
|
||||
// Play the Damage sound
|
||||
game.getEvents().post(new CardDamagedEvent());
|
||||
}
|
||||
|
||||
// this is for cards like Sengir Vampire
|
||||
/**
|
||||
* <p>
|
||||
@@ -723,30 +375,6 @@ public final class GameActionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
// not restricted to just combat damage, restricted to players
|
||||
/**
|
||||
* <p>
|
||||
* executeDamageToPlayerEffects.
|
||||
* </p>
|
||||
*
|
||||
* @param player
|
||||
* a {@link forge.game.player.Player} object.
|
||||
* @param c
|
||||
* a {@link forge.Card} object.
|
||||
* @param damage
|
||||
* a int.
|
||||
*/
|
||||
public static void executeDamageToPlayerEffects(final Player player, final Card c, final int damage) {
|
||||
if (damage <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
c.getDamageHistory().registerDamage(player);
|
||||
|
||||
// Play the Life Loss sound
|
||||
player.getGame().getEvents().post(new LifeLossEvent());
|
||||
}
|
||||
|
||||
// restricted to combat damage, restricted to players
|
||||
/**
|
||||
* <p>
|
||||
@@ -771,14 +399,11 @@ public final class GameActionUtil {
|
||||
final String parse = c.getKeyword().get(keywordPosition).toString();
|
||||
final String[] k = parse.split(" ");
|
||||
final int poison = Integer.parseInt(k[1]);
|
||||
final Card crd = c;
|
||||
|
||||
final Ability ability = new Ability(c, ManaCost.ZERO) {
|
||||
@Override
|
||||
public void resolve() {
|
||||
final Player player = crd.getController();
|
||||
final Player opponent = player.getOpponent();
|
||||
opponent.addPoisonCounters(poison, c);
|
||||
player.addPoisonCounters(poison, c);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -786,9 +411,7 @@ public final class GameActionUtil {
|
||||
sb.append(c);
|
||||
sb.append(" - Poisonous: ");
|
||||
sb.append(c.getController().getOpponent());
|
||||
sb.append(" gets ");
|
||||
sb.append(poison);
|
||||
sb.append(" poison counter");
|
||||
sb.append(" gets ").append(poison).append(" poison counter");
|
||||
if (poison != 1) {
|
||||
sb.append("s");
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ public class MatchController {
|
||||
// The UI controls should use these game data as models
|
||||
CMatchUI.SINGLETON_INSTANCE.initMatch(currentGame.getRegisteredPlayers(), localHuman);
|
||||
CDock.SINGLETON_INSTANCE.setModel(currentGame, localHuman);
|
||||
CStack.SINGLETON_INSTANCE.setModel(currentGame.getStack());
|
||||
CStack.SINGLETON_INSTANCE.setModel(currentGame.getStack(), localHuman);
|
||||
CLog.SINGLETON_INSTANCE.setModel(currentGame.getGameLog());
|
||||
CCombat.SINGLETON_INSTANCE.setModel(currentGame);
|
||||
CMessage.SINGLETON_INSTANCE.setModel(match);
|
||||
|
||||
@@ -86,7 +86,7 @@ public class AiController {
|
||||
|
||||
public final SpellAbility getSpellAbilityToPlay() {
|
||||
// if top of stack is owned by me
|
||||
if (!game.getStack().isEmpty() && game.getStack().peekInstance().getActivatingPlayer().equals(player)) {
|
||||
if (!game.getStack().isEmpty() && game.getStack().peekAbility().getActivatingPlayer().equals(player)) {
|
||||
// probably should let my stuff resolve
|
||||
return null;
|
||||
}
|
||||
@@ -792,7 +792,7 @@ public class AiController {
|
||||
public void onPriorityRecieved() {
|
||||
final PhaseType phase = game.getPhaseHandler().getPhase();
|
||||
|
||||
if (game.getStack().size() > 0) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
playSpellAbilities(game);
|
||||
} else {
|
||||
switch(phase) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import forge.card.cost.CostPayment;
|
||||
import forge.card.spellability.AbilityManaPart;
|
||||
import forge.card.spellability.AbilityStatic;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.SpellAbilityStackInstance;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.error.BugReporter;
|
||||
import forge.game.GameState;
|
||||
@@ -271,7 +272,7 @@ public class ComputerUtil {
|
||||
final SpellAbility newSA = sa.copyWithNoManaCost();
|
||||
newSA.setActivatingPlayer(ai);
|
||||
|
||||
if (!ComputerUtilCost.canPayAdditionalCosts(newSA, ai)) {
|
||||
if (!CostPayment.canPayAdditionalCosts(newSA.getPayCosts(), newSA)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -462,13 +463,14 @@ public class ComputerUtil {
|
||||
final GameState game = ai.getGame();
|
||||
List<Card> typeList = new ArrayList<Card>();
|
||||
if (zone.equals(ZoneType.Stack)) {
|
||||
for (int i = 0; i < game.getStack().size(); i++) {
|
||||
typeList.add(game.getStack().peekAbility(i).getSourceCard());
|
||||
typeList = CardLists.getValidCards(typeList, type.split(","), activate.getController(), activate);
|
||||
for (SpellAbilityStackInstance si : game.getStack()) {
|
||||
typeList.add(si.getSourceCard());
|
||||
}
|
||||
} else {
|
||||
typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(","), activate.getController(), activate);
|
||||
typeList = ai.getCardsIn(zone);
|
||||
}
|
||||
typeList = CardLists.getValidCards(typeList, type.split(","), activate.getController(), activate);
|
||||
|
||||
if ((target != null) && target.getController() == ai && typeList.contains(target)) {
|
||||
typeList.remove(target); // don't exile the card we're pumping
|
||||
}
|
||||
|
||||
@@ -364,30 +364,8 @@ public class ComputerUtilCost {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ComputerUtilCost.canPayAdditionalCosts(sa, player);
|
||||
} // canPayCost()
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* canPayAdditionalCosts.
|
||||
* </p>
|
||||
*
|
||||
* @param sa
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param player
|
||||
* a {@link forge.game.player.Player} object.
|
||||
* @return a boolean.
|
||||
*/
|
||||
public static boolean canPayAdditionalCosts(final SpellAbility sa, final Player player) {
|
||||
if (sa.getActivatingPlayer() == null) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(sa.getSourceCard());
|
||||
sb.append(" in ComputerUtil.canPayAdditionalCosts() without an activating player");
|
||||
System.out.println(sb.toString());
|
||||
sa.setActivatingPlayer(player);
|
||||
}
|
||||
return CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
|
||||
}
|
||||
} // canPayCost()
|
||||
|
||||
public static boolean willPayUnlessCost(SpellAbility sa, Player payer, SpellAbility ability, boolean alreadyPaid, List<Player> payers) {
|
||||
final Card source = sa.getSourceCard();
|
||||
@@ -426,20 +404,17 @@ public class ComputerUtilCost {
|
||||
if (alreadyPaid || (payers.size() > 1 && (isMine && !payForOwnOnly))) {
|
||||
return false;
|
||||
}
|
||||
if (canPayCost(ability, payer)
|
||||
&& checkLifeCost(payer, ability.getPayCosts(), source, 4, sa)
|
||||
&& checkDamageCost(payer, ability.getPayCosts(), source, 4)
|
||||
&& (isMine || checkDiscardCost(payer, ability.getPayCosts(), source))
|
||||
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
|
||||
&& (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2)
|
||||
&& (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)
|
||||
&& (!source.getName().equals("Chain of Vapor")
|
||||
|| (payer.getOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3))) {
|
||||
// AI was crashing because the blank ability used to pay costs
|
||||
// Didn't have any of the data on the original SA to pay dependant costs
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// AI was crashing because the blank ability used to pay costs
|
||||
// Didn't have any of the data on the original SA to pay dependant costs
|
||||
|
||||
return checkLifeCost(payer, ability.getPayCosts(), source, 4, sa)
|
||||
&& checkDamageCost(payer, ability.getPayCosts(), source, 4)
|
||||
&& (isMine || checkDiscardCost(payer, ability.getPayCosts(), source))
|
||||
&& (!source.getName().equals("Tyrannize") || payer.getCardsIn(ZoneType.Hand).size() > 2)
|
||||
&& (!source.getName().equals("Perplex") || payer.getCardsIn(ZoneType.Hand).size() < 2)
|
||||
&& (!source.getName().equals("Breaking Point") || payer.getCreaturesInPlay().size() > 1)
|
||||
&& (!source.getName().equals("Chain of Vapor") || (payer.getOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ public class ComputerUtilMana {
|
||||
ma.setActivatingPlayer(ai);
|
||||
// if the AI can't pay the additional costs skip the mana ability
|
||||
if (ma.getPayCosts() != null && checkPlayable) {
|
||||
if (!ComputerUtilCost.canPayAdditionalCosts(ma, ai)) {
|
||||
if (!CostPayment.canPayAdditionalCosts(ma.getPayCosts(), ma)) {
|
||||
continue;
|
||||
}
|
||||
} else if (sourceCard.isTapped() && checkPlayable) {
|
||||
@@ -535,7 +535,7 @@ public class ComputerUtilMana {
|
||||
// ability
|
||||
m.setActivatingPlayer(ai);
|
||||
if (cost != null) {
|
||||
if (!ComputerUtilCost.canPayAdditionalCosts(m, ai)) {
|
||||
if (!CostPayment.canPayAdditionalCosts(m.getPayCosts(), m)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +46,12 @@ import forge.card.spellability.Ability;
|
||||
import forge.card.spellability.AbilityStatic;
|
||||
import forge.card.staticability.StaticAbility;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.GlobalRuleChange;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.player.HumanPlay;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -1078,7 +1078,7 @@ public class CombatUtil {
|
||||
ability.setActivatingPlayer(c.getController());
|
||||
|
||||
if (c.getController().isHuman()) {
|
||||
hasPaid = GameActionUtil.payCostDuringAbilityResolve(ability, attackCost, null, game);
|
||||
hasPaid = HumanPlay.payCostDuringAbilityResolve(ability, attackCost, null, game);
|
||||
} else { // computer
|
||||
if (ComputerUtilCost.canPayCost(ability, c.getController())) {
|
||||
ComputerUtil.playNoStack(c.getController(), ability, game);
|
||||
|
||||
@@ -429,7 +429,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
|
||||
this.setPlayersPriorityPermission(true); // PlayerPriorityAllowed = false;
|
||||
|
||||
// If the Stack isn't empty why is nextPhase being called?
|
||||
if (game.getStack().size() != 0) {
|
||||
if (!game.getStack().isEmpty()) {
|
||||
Log.debug("Phase.nextPhase() is called, but Stack isn't empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ import forge.card.spellability.Ability;
|
||||
import forge.card.spellability.AbilityStatic;
|
||||
import forge.card.staticability.StaticAbility;
|
||||
import forge.card.trigger.TriggerType;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.player.HumanPlay;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.match.CMatchUI;
|
||||
@@ -225,7 +225,7 @@ public class PhaseUtil {
|
||||
ability.setActivatingPlayer(blocker.getController());
|
||||
|
||||
if (blocker.getController().isHuman()) {
|
||||
hasPaid = GameActionUtil.payCostDuringAbilityResolve(ability, blockCost, null, game);
|
||||
hasPaid = HumanPlay.payCostDuringAbilityResolve(ability, blockCost, null, game);
|
||||
} else { // computer
|
||||
if (ComputerUtilCost.canPayCost(ability, blocker.getController())) {
|
||||
ComputerUtil.playNoStack(blocker.getController(), ability, game);
|
||||
|
||||
@@ -41,13 +41,13 @@ import forge.control.input.InputPayManaExecuteCommands;
|
||||
import forge.control.input.InputPayment;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.control.input.InputSelectCardsFromList;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.ai.ComputerUtil;
|
||||
import forge.game.ai.ComputerUtilCard;
|
||||
import forge.game.ai.ComputerUtilCombat;
|
||||
import forge.game.ai.ComputerUtilCost;
|
||||
import forge.game.ai.ComputerUtilMana;
|
||||
import forge.game.player.HumanPlay;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.PlayerZone;
|
||||
import forge.game.zone.ZoneType;
|
||||
@@ -182,7 +182,7 @@ public class Upkeep extends Phase {
|
||||
Player controller = c.getController();
|
||||
if (controller.isHuman()) {
|
||||
Cost cost = new Cost(c.getEchoCost().trim(), true);
|
||||
if ( !GameActionUtil.payCostDuringAbilityResolve(blankAbility, cost, null, game) )
|
||||
if ( !HumanPlay.payCostDuringAbilityResolve(blankAbility, cost, null, game) )
|
||||
game.getAction().sacrifice(c, null);;
|
||||
|
||||
} else { // computer
|
||||
@@ -324,7 +324,7 @@ public class Upkeep extends Phase {
|
||||
@Override
|
||||
public void resolve() {
|
||||
if (controller.isHuman()) {
|
||||
if ( !GameActionUtil.payCostDuringAbilityResolve(blankAbility, blankAbility.getPayCosts(), this, game))
|
||||
if ( !HumanPlay.payCostDuringAbilityResolve(blankAbility, blankAbility.getPayCosts(), this, game))
|
||||
game.getAction().sacrifice(c, null);
|
||||
} else { // computer
|
||||
if (ComputerUtilCost.shouldPayCost(controller, c, upkeepCost) && ComputerUtilCost.canPayCost(blankAbility, controller)) {
|
||||
|
||||
@@ -1,14 +1,35 @@
|
||||
package forge.game.player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import forge.Card;
|
||||
import forge.CardLists;
|
||||
import forge.CardPredicates.Presets;
|
||||
import forge.CounterType;
|
||||
import forge.FThreads;
|
||||
import forge.card.ability.AbilityUtils;
|
||||
import forge.card.ability.ApiType;
|
||||
import forge.card.ability.effects.CharmEffect;
|
||||
import forge.card.cardfactory.CardFactoryUtil;
|
||||
import forge.card.cost.Cost;
|
||||
import forge.card.cost.CostDamage;
|
||||
import forge.card.cost.CostDiscard;
|
||||
import forge.card.cost.CostExile;
|
||||
import forge.card.cost.CostMill;
|
||||
import forge.card.cost.CostPart;
|
||||
import forge.card.cost.CostPartMana;
|
||||
import forge.card.cost.CostPartWithList;
|
||||
import forge.card.cost.CostPayLife;
|
||||
import forge.card.cost.CostPayment;
|
||||
import forge.card.cost.CostPutCounter;
|
||||
import forge.card.cost.CostRemoveCounter;
|
||||
import forge.card.cost.CostReturn;
|
||||
import forge.card.cost.CostReveal;
|
||||
import forge.card.cost.CostSacrifice;
|
||||
import forge.card.cost.CostTapType;
|
||||
import forge.card.mana.ManaCost;
|
||||
import forge.card.mana.ManaCostBeingPaid;
|
||||
import forge.card.mana.ManaCostShard;
|
||||
@@ -17,8 +38,16 @@ import forge.card.spellability.HumanPlaySpellAbility;
|
||||
import forge.card.spellability.SpellAbility;
|
||||
import forge.card.spellability.Target;
|
||||
import forge.control.input.InputPayManaBase;
|
||||
import forge.control.input.InputPayManaExecuteCommands;
|
||||
import forge.control.input.InputPayManaSimple;
|
||||
import forge.control.input.InputPayment;
|
||||
import forge.control.input.InputSelectCards;
|
||||
import forge.control.input.InputSelectCardsFromList;
|
||||
import forge.game.GameActionUtil;
|
||||
import forge.game.GameState;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.GuiDialog;
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this type.
|
||||
@@ -223,4 +252,238 @@ public class HumanPlay {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
private static int getAmountFromPart(CostPart part, Card source, SpellAbility sourceAbility) {
|
||||
String amountString = part.getAmount();
|
||||
return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : AbilityUtils.calculateAmount(source, amountString, sourceAbility);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param part
|
||||
* @param source
|
||||
* @param sourceAbility
|
||||
* @return
|
||||
*/
|
||||
private static int getAmountFromPartX(CostPart part, Card source, SpellAbility sourceAbility) {
|
||||
String amountString = part.getAmount();
|
||||
return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString));
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* payCostDuringAbilityResolve.
|
||||
* </p>
|
||||
*
|
||||
* @param ability
|
||||
* a {@link forge.card.spellability.SpellAbility} object.
|
||||
* @param cost
|
||||
* a {@link forge.card.cost.Cost} object.
|
||||
* @param paid
|
||||
* a {@link forge.Command} object.
|
||||
* @param unpaid
|
||||
* a {@link forge.Command} object.
|
||||
* @param sourceAbility TODO
|
||||
*/
|
||||
public static boolean payCostDuringAbilityResolve(final SpellAbility ability, final Cost cost, SpellAbility sourceAbility, final GameState game) {
|
||||
|
||||
// Only human player pays this way
|
||||
final Player p = ability.getActivatingPlayer();
|
||||
final Card source = ability.getSourceCard();
|
||||
Card current = null; // Used in spells with RepeatEach effect to distinguish cards, Cut the Tethers
|
||||
if (!source.getRemembered().isEmpty()) {
|
||||
if (source.getRemembered().get(0) instanceof Card) {
|
||||
current = (Card) source.getRemembered().get(0);
|
||||
}
|
||||
}
|
||||
if (!source.getImprinted().isEmpty()) {
|
||||
current = source.getImprinted().get(0);
|
||||
}
|
||||
|
||||
final List<CostPart> parts = cost.getCostParts();
|
||||
ArrayList<CostPart> remainingParts = new ArrayList<CostPart>(cost.getCostParts());
|
||||
CostPart costPart = null;
|
||||
if (!parts.isEmpty()) {
|
||||
costPart = parts.get(0);
|
||||
}
|
||||
final String orString = sourceAbility == null ? "" : " (or: " + sourceAbility.getStackDescription() + ")";
|
||||
|
||||
if (parts.isEmpty() || costPart.getAmount().equals("0")) {
|
||||
return GuiDialog.confirm(source, "Do you want to pay 0?" + orString);
|
||||
}
|
||||
|
||||
//the following costs do not need inputs
|
||||
for (CostPart part : parts) {
|
||||
boolean mayRemovePart = true;
|
||||
|
||||
if (part instanceof CostPayLife) {
|
||||
final int amount = getAmountFromPart(part, source, sourceAbility);
|
||||
if (!p.canPayLife(amount))
|
||||
return false;
|
||||
|
||||
if (false == GuiDialog.confirm(source, "Do you want to pay " + amount + " life?" + orString))
|
||||
return false;
|
||||
|
||||
p.payLife(amount, null);
|
||||
}
|
||||
|
||||
else if (part instanceof CostMill) {
|
||||
final int amount = getAmountFromPart(part, source, sourceAbility);
|
||||
final List<Card> list = p.getCardsIn(ZoneType.Library);
|
||||
if (list.size() < amount) return false;
|
||||
if (!GuiDialog.confirm(source, "Do you want to mill " + amount + " card(s)?" + orString))
|
||||
return false;
|
||||
List<Card> listmill = p.getCardsIn(ZoneType.Library, amount);
|
||||
((CostMill) part).executePayment(sourceAbility, listmill);
|
||||
}
|
||||
|
||||
else if (part instanceof CostDamage) {
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
if (!p.canPayLife(amount))
|
||||
return false;
|
||||
|
||||
if (false == GuiDialog.confirm(source, "Do you want " + source + " to deal " + amount + " damage to you?"))
|
||||
return false;
|
||||
|
||||
p.addDamage(amount, source);
|
||||
}
|
||||
|
||||
else if (part instanceof CostPutCounter) {
|
||||
CounterType counterType = ((CostPutCounter) part).getCounter();
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
|
||||
if (false == source.canReceiveCounters(counterType)) {
|
||||
String message = String.format("Won't be able to pay upkeep for %s but it can't have %s counters put on it.", source, counterType.getName());
|
||||
p.getGame().getGameLog().add("ResolveStack", message, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
String plural = amount > 1 ? "s" : "";
|
||||
if (false == GuiDialog.confirm(source, "Do you want to put " + amount + " " + counterType.getName() + " counter" + plural + " on " + source + "?"))
|
||||
return false;
|
||||
|
||||
source.addCounter(counterType, amount, false);
|
||||
}
|
||||
|
||||
else if (part instanceof CostRemoveCounter) {
|
||||
CounterType counterType = ((CostRemoveCounter) part).getCounter();
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
String plural = amount > 1 ? "s" : "";
|
||||
|
||||
if (!part.canPay(sourceAbility))
|
||||
return false;
|
||||
|
||||
if ( false == GuiDialog.confirm(source, "Do you want to remove " + amount + " " + counterType.getName() + " counter" + plural + " from " + source + "?"))
|
||||
return false;
|
||||
|
||||
source.subtractCounter(counterType, amount);
|
||||
}
|
||||
|
||||
else if (part instanceof CostExile) {
|
||||
if ("All".equals(part.getType())) {
|
||||
if (false == GuiDialog.confirm(source, "Do you want to exile all cards in your graveyard?"))
|
||||
return false;
|
||||
|
||||
List<Card> cards = new ArrayList<Card>(p.getCardsIn(ZoneType.Graveyard));
|
||||
for (final Card card : cards) {
|
||||
p.getGame().getAction().exile(card);
|
||||
}
|
||||
} else {
|
||||
CostExile costExile = (CostExile) part;
|
||||
ZoneType from = costExile.getFrom();
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(from), part.getType().split(";"), p, source);
|
||||
final int nNeeded = AbilityUtils.calculateAmount(source, part.getAmount(), ability);
|
||||
if (list.size() < nNeeded)
|
||||
return false;
|
||||
|
||||
// replace this with input
|
||||
for (int i = 0; i < nNeeded; i++) {
|
||||
final Card c = GuiChoose.oneOrNone("Exile from " + from, list);
|
||||
if (c == null)
|
||||
return false;
|
||||
|
||||
list.remove(c);
|
||||
p.getGame().getAction().exile(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (part instanceof CostSacrifice) {
|
||||
int amount = Integer.parseInt(((CostSacrifice)part).getAmount());
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "sacrifice." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostReturn) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "return to hand." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostDiscard) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "discard." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostReveal) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Hand), part.getType(), p, source);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "reveal." + orString);
|
||||
if(!hasPaid) return false;
|
||||
} else if (part instanceof CostTapType) {
|
||||
List<Card> list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source);
|
||||
list = CardLists.filter(list, Presets.UNTAPPED);
|
||||
int amount = getAmountFromPartX(part, source, sourceAbility);
|
||||
boolean hasPaid = payCostPart(sourceAbility, (CostPartWithList)part, amount, list, "tap." + orString);
|
||||
if(!hasPaid) return false;
|
||||
}
|
||||
|
||||
else if (part instanceof CostPartMana ) {
|
||||
if (!((CostPartMana) part).getManaToPay().isZero()) // non-zero costs require input
|
||||
mayRemovePart = false;
|
||||
} else
|
||||
throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - An unhandled type of cost was met: " + part.getClass());
|
||||
|
||||
if( mayRemovePart )
|
||||
remainingParts.remove(part);
|
||||
}
|
||||
|
||||
|
||||
if (remainingParts.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (remainingParts.size() > 1) {
|
||||
throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - Too many payment types - " + source);
|
||||
}
|
||||
costPart = remainingParts.get(0);
|
||||
// check this is a mana cost
|
||||
if (!(costPart instanceof CostPartMana ))
|
||||
throw new RuntimeException("GameActionUtil.payCostDuringAbilityResolve - The remaining payment type is not Mana.");
|
||||
|
||||
InputPayment toSet = current == null
|
||||
? new InputPayManaExecuteCommands(p, source + "\r\n", cost.getCostMana().getManaToPay())
|
||||
: new InputPayManaExecuteCommands(p, source + "\r\n" + "Current Card: " + current + "\r\n" , cost.getCostMana().getManaToPay());
|
||||
FThreads.setInputAndWait(toSet);
|
||||
return toSet.isPaid();
|
||||
}
|
||||
|
||||
private static boolean payCostPart(SpellAbility sourceAbility, CostPartWithList cpl, int amount, List<Card> list, String actionName) {
|
||||
if (list.size() < amount) return false; // unable to pay (not enough cards)
|
||||
|
||||
InputSelectCards inp = new InputSelectCardsFromList(amount, amount, list);
|
||||
inp.setMessage("Select %d " + cpl.getDescriptiveType() + " card(s) to " + actionName);
|
||||
inp.setCancelAllowed(true);
|
||||
|
||||
FThreads.setInputAndWait(inp);
|
||||
if( inp.hasCancelled() || inp.getSelected().size() != amount)
|
||||
return false;
|
||||
|
||||
for(Card c : inp.getSelected()) {
|
||||
cpl.executePayment(sourceAbility, c);
|
||||
}
|
||||
if (sourceAbility != null) {
|
||||
cpl.reportPaidCardsTo(sourceAbility);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -612,7 +612,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
public final boolean addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat) {
|
||||
final int damageToDo = damage;
|
||||
|
||||
if (damageToDo == 0) {
|
||||
if (damageToDo <= 0) {
|
||||
return false;
|
||||
}
|
||||
String additionalLog = "";
|
||||
@@ -639,8 +639,11 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
|
||||
this.assignedDamage.put(source, damageToDo);
|
||||
GameActionUtil.executeDamageDealingEffects(source, damageToDo);
|
||||
GameActionUtil.executeDamageToPlayerEffects(this, source, damageToDo);
|
||||
if (source.hasKeyword("Lifelink")) {
|
||||
source.getController().gainLife(damageToDo, source);
|
||||
}
|
||||
source.getDamageHistory().registerDamage(this);
|
||||
this.getGame().getEvents().post(new LifeLossEvent());
|
||||
|
||||
if (isCombat) {
|
||||
final ArrayList<String> types = source.getType();
|
||||
@@ -2822,7 +2825,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
public boolean canCastSorcery() {
|
||||
PhaseHandler now = game.getPhaseHandler();
|
||||
return now.isPlayerTurn(this) && now.getPhase().isMain() && game.getStack().size() == 0;
|
||||
return now.isPlayerTurn(this) && now.getPhase().isMain() && game.getStack().isEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package forge.game.zone;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
@@ -66,7 +67,7 @@ import forge.util.MyObservable;
|
||||
* @author Forge
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MagicStack extends MyObservable {
|
||||
public class MagicStack extends MyObservable implements Iterable<SpellAbilityStackInstance> {
|
||||
private final List<SpellAbility> simultaneousStackEntryList = new ArrayList<SpellAbility>();
|
||||
|
||||
private final Stack<SpellAbilityStackInstance> stack = new Stack<SpellAbilityStackInstance>();
|
||||
@@ -539,7 +540,7 @@ public class MagicStack extends MyObservable {
|
||||
* @return a boolean.
|
||||
*/
|
||||
public final boolean isEmpty() {
|
||||
return this.getStack().size() == 0;
|
||||
return this.getStack().isEmpty();
|
||||
}
|
||||
|
||||
// Push should only be used by add.
|
||||
@@ -854,53 +855,12 @@ public class MagicStack extends MyObservable {
|
||||
}
|
||||
}
|
||||
|
||||
public final SpellAbility top() {
|
||||
private final SpellAbility top() {
|
||||
final SpellAbilityStackInstance si = this.getStack().peek();
|
||||
final SpellAbility sa = si.getSpellAbility();
|
||||
return sa;
|
||||
}
|
||||
|
||||
// CAREFUL! Peeking while an SAs Targets are being choosen may cause issues
|
||||
// index = 0 is the top, index = 1 is the next to top, etc...
|
||||
/**
|
||||
* <p>
|
||||
* peekInstance.
|
||||
* </p>
|
||||
*
|
||||
* @param index
|
||||
* a int.
|
||||
* @return a {@link forge.card.spellability.SpellAbilityStackInstance}
|
||||
* object.
|
||||
*/
|
||||
public final SpellAbilityStackInstance peekInstance(final int index) {
|
||||
return this.getStack().get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* peekAbility.
|
||||
* </p>
|
||||
*
|
||||
* @param index
|
||||
* a int.
|
||||
* @return a {@link forge.card.spellability.SpellAbility} object.
|
||||
*/
|
||||
public final SpellAbility peekAbility(final int index) {
|
||||
return this.getStack().get(index).getSpellAbility();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* peekInstance.
|
||||
* </p>
|
||||
*
|
||||
* @return a {@link forge.card.spellability.SpellAbilityStackInstance}
|
||||
* object.
|
||||
*/
|
||||
public final SpellAbilityStackInstance peekInstance() {
|
||||
return this.getStack().peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* peekAbility.
|
||||
@@ -1168,4 +1128,13 @@ public class MagicStack extends MyObservable {
|
||||
|
||||
return c.equals(this.curResolvingCard);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<SpellAbilityStackInstance> iterator() {
|
||||
// TODO Auto-generated method stub
|
||||
return stack.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.Observer;
|
||||
|
||||
import forge.Command;
|
||||
import forge.FThreads;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.zone.MagicStack;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.ICDoc;
|
||||
@@ -22,6 +23,7 @@ public enum CStack implements ICDoc, Observer {
|
||||
SINGLETON_INSTANCE;
|
||||
|
||||
private MagicStack model;
|
||||
private Player viewer;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.gui.framework.ICDoc#getCommandOnSelect()
|
||||
@@ -40,7 +42,7 @@ public enum CStack implements ICDoc, Observer {
|
||||
|
||||
private final Runnable upd = new Runnable() { @Override public void run() {
|
||||
SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
|
||||
VStack.SINGLETON_INSTANCE.updateStack(model);
|
||||
VStack.SINGLETON_INSTANCE.updateStack(model, viewer);
|
||||
} };
|
||||
|
||||
/* (non-Javadoc)
|
||||
@@ -59,6 +61,9 @@ public enum CStack implements ICDoc, Observer {
|
||||
FThreads.invokeInEdtNowOrLater(upd);
|
||||
}
|
||||
|
||||
public void setModel(MagicStack model) { this.model = model; }
|
||||
public void setModel(MagicStack model, Player guiPlayer) {
|
||||
this.model = model;
|
||||
this.viewer = guiPlayer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
@@ -35,6 +36,7 @@ import net.miginfocom.swing.MigLayout;
|
||||
import forge.CardUtil;
|
||||
import forge.card.spellability.SpellAbilityStackInstance;
|
||||
import forge.control.FControl;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.player.PlayerController;
|
||||
import forge.game.zone.MagicStack;
|
||||
import forge.gui.framework.DragCell;
|
||||
@@ -115,14 +117,15 @@ public enum VStack implements IVDoc<CStack> {
|
||||
//========== Observer update methods
|
||||
|
||||
/**
|
||||
* @param stack */
|
||||
public void updateStack(final MagicStack stack) {
|
||||
* @param stack
|
||||
* @param viewer */
|
||||
public void updateStack(final MagicStack stack, Player viewer) {
|
||||
// No need to update this unless it's showing
|
||||
if (!parentCell.getSelected().equals(this)) { return; }
|
||||
|
||||
int count = 1;
|
||||
JTextArea tar;
|
||||
String txt, isOptional;
|
||||
|
||||
List<JTextArea> list = new ArrayList<JTextArea>();
|
||||
|
||||
parentCell.getBody().removeAll();
|
||||
parentCell.getBody().setLayout(new MigLayout("insets 1%, gap 1%, wrap"));
|
||||
@@ -133,15 +136,13 @@ public enum VStack implements IVDoc<CStack> {
|
||||
Color[] scheme;
|
||||
|
||||
stackTARs.clear();
|
||||
for (int i = stack.size() - 1; 0 <= i; i--) {
|
||||
final SpellAbilityStackInstance spell = stack.peekInstance(i);
|
||||
|
||||
boolean isFirst = true;
|
||||
for (final SpellAbilityStackInstance spell : stack) {
|
||||
scheme = getSpellColor(spell);
|
||||
|
||||
isOptional = stack.peekAbility(i).isOptionalTrigger()
|
||||
&& stack.peekAbility(i).getSourceCard().getController().isHuman() ? "(OPTIONAL) " : "";
|
||||
txt = (count++) + ". " + isOptional + spell.getStackDescription();
|
||||
tar = new JTextArea(txt);
|
||||
String isOptional = spell.getSpellAbility().isOptionalTrigger() && spell.getSourceCard().getController().equals(viewer) ? "(OPTIONAL) " : "";
|
||||
String txt = (count++) + ". " + isOptional + spell.getStackDescription();
|
||||
JTextArea tar = new JTextArea(txt);
|
||||
tar.setToolTipText(txt);
|
||||
tar.setOpaque(true);
|
||||
tar.setBorder(border);
|
||||
@@ -183,17 +184,23 @@ public enum VStack implements IVDoc<CStack> {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
list.add(tar);
|
||||
|
||||
/*
|
||||
* This updates the Card Picture/Detail when the spell is added to
|
||||
* the stack. This functionality was not present in v 1.1.8.
|
||||
*
|
||||
* Problem is described in TODO right above this.
|
||||
*/
|
||||
if (i == 0 && !spell.getStackDescription().startsWith("Morph ")) {
|
||||
if (isFirst && !spell.getStackDescription().startsWith("Morph ")) {
|
||||
CMatchUI.SINGLETON_INSTANCE.setCard(spell.getSourceCard());
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
Collections.reverse(list);
|
||||
|
||||
for(JTextArea tar : list) {
|
||||
parentCell.getBody().add(tar, "w 98%!");
|
||||
stackTARs.add(tar);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user