mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
Merge remote-tracking branch 'upstream/master' into collector-number-in-card-list-and-card-db-refactoring
This commit is contained in:
@@ -253,7 +253,10 @@ public class AiController {
|
||||
}
|
||||
boolean rightapi = false;
|
||||
Player activatingPlayer = sa.getActivatingPlayer();
|
||||
|
||||
|
||||
// for xPaid stuff
|
||||
card.setCastSA(sa);
|
||||
|
||||
// Trigger play improvements
|
||||
for (final Trigger tr : card.getTriggers()) {
|
||||
// These triggers all care for ETB effects
|
||||
@@ -743,7 +746,7 @@ public class AiController {
|
||||
return AiPlayDecision.CantPlaySa;
|
||||
}
|
||||
|
||||
boolean xCost = sa.getPayCosts().hasXInAnyCostPart();
|
||||
boolean xCost = sa.getPayCosts().hasXInAnyCostPart() || sa.getHostCard().hasStartOfKeyword("Strive");
|
||||
if (!xCost && !ComputerUtilCost.canPayCost(sa, player)) {
|
||||
// for most costs, it's OK to check if they can be paid early in order to avoid running a heavy API check
|
||||
// when the AI won't even be able to play the spell in the first place (even if it could afford it)
|
||||
@@ -751,11 +754,11 @@ public class AiController {
|
||||
}
|
||||
|
||||
// state needs to be switched here so API checks evaluate the right face
|
||||
if (sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||
if (sa.getCardState() != null && !sa.getHostCard().isInPlay() && sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||
sa.getHostCard().setState(CardStateName.Modal, false);
|
||||
}
|
||||
AiPlayDecision canPlay = canPlaySa(sa); // this is the "heaviest" check, which also sets up targets, defines X, etc.
|
||||
if (sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||
if (sa.getCardState() != null && !sa.getHostCard().isInPlay() && sa.getCardState().getStateName() == CardStateName.Modal) {
|
||||
sa.getHostCard().setState(CardStateName.Original, false);
|
||||
}
|
||||
|
||||
@@ -1754,10 +1757,10 @@ public class AiController {
|
||||
}
|
||||
|
||||
public boolean doTrigger(SpellAbility spell, boolean mandatory) {
|
||||
if (spell.getApi() != null)
|
||||
return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
|
||||
if (spell instanceof WrappedAbility)
|
||||
return doTrigger(((WrappedAbility)spell).getWrappedAbility(), mandatory);
|
||||
if (spell.getApi() != null)
|
||||
return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
|
||||
if (spell.getPayCosts() == Cost.Zero && spell.getTargetRestrictions() == null) {
|
||||
// For non-converted triggers (such as Cumulative Upkeep) that don't have costs or targets to worry about
|
||||
return true;
|
||||
|
||||
@@ -695,8 +695,9 @@ public class ComputerUtilCost {
|
||||
|
||||
public static int getMaxXValue(SpellAbility sa, Player ai) {
|
||||
final Card source = sa.getHostCard();
|
||||
final SpellAbility root = sa.getRootAbility();
|
||||
SpellAbility root = sa.getRootAbility();
|
||||
final Cost abCost = root.getPayCosts();
|
||||
|
||||
if (abCost == null || !abCost.hasXInAnyCostPart()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -827,8 +827,7 @@ public class ComputerUtilMana {
|
||||
|
||||
// remove from available lists
|
||||
Iterables.removeIf(sourcesForShards.values(), CardTraitPredicates.isHostCard(saPayment.getHostCard()));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
final CostPayment pay = new CostPayment(saPayment.getPayCosts(), saPayment);
|
||||
if (!pay.payComputerCosts(new AiCostDecision(ai, saPayment))) {
|
||||
saList.remove(saPayment);
|
||||
@@ -863,8 +862,7 @@ public class ComputerUtilMana {
|
||||
if (test) {
|
||||
resetPayment(paymentList);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
System.out.println("ComputerUtil : payManaCost() cost was not paid for " + sa.getHostCard().getName() + ". Didn't find what to pay for " + toPay);
|
||||
return false;
|
||||
}
|
||||
@@ -1233,8 +1231,7 @@ public class ComputerUtilMana {
|
||||
if (!(ai.getGame().getPhaseHandler().isPlayerTurn(ai))) {
|
||||
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK);
|
||||
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT);
|
||||
}
|
||||
else
|
||||
} else
|
||||
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK);
|
||||
} else {
|
||||
if ((AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK)) ||
|
||||
@@ -1255,8 +1252,7 @@ public class ComputerUtilMana {
|
||||
|
||||
if (curPhase == PhaseType.MAIN2 || curPhase == PhaseType.CLEANUP) {
|
||||
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2)) {
|
||||
// This mana source is held elsewhere for a Main Phase 2 spell.
|
||||
return true;
|
||||
@@ -1266,7 +1262,6 @@ public class ComputerUtilMana {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost) {
|
||||
// mind the priorities
|
||||
// * Pay mono-colored first,curPhase == PhaseType.CLEANUP
|
||||
@@ -1352,8 +1347,7 @@ public class ComputerUtilMana {
|
||||
String commonColor = ComputerUtilCard.getMostProminentColor(ai.getCardsIn(ZoneType.Hand));
|
||||
if (!commonColor.isEmpty() && satisfiesColorChoice(abMana, choiceString, MagicColor.toShortString(commonColor)) && abMana.getComboColors().contains(MagicColor.toShortString(commonColor))) {
|
||||
choice = MagicColor.toShortString(commonColor);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// default to first available color
|
||||
for (String c : comboColors) {
|
||||
if (satisfiesColorChoice(abMana, choiceString, c)) {
|
||||
@@ -1398,8 +1392,7 @@ public class ComputerUtilMana {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
String color = MagicColor.toShortString(manaPart);
|
||||
boolean wasNeeded = testCost.ai_payMana(color, p.getManaPool());
|
||||
if (!wasNeeded) {
|
||||
|
||||
@@ -434,7 +434,7 @@ public abstract class GameState {
|
||||
boolean first = true;
|
||||
StringBuilder counterString = new StringBuilder();
|
||||
|
||||
for(Entry<CounterType, Integer> kv : counters.entrySet()) {
|
||||
for (Entry<CounterType, Integer> kv : counters.entrySet()) {
|
||||
if (!first) {
|
||||
counterString.append(",");
|
||||
}
|
||||
@@ -470,7 +470,7 @@ public abstract class GameState {
|
||||
}
|
||||
|
||||
public void parse(List<String> lines) {
|
||||
for(String line : lines) {
|
||||
for (String line : lines) {
|
||||
parseLine(line);
|
||||
}
|
||||
}
|
||||
@@ -1110,13 +1110,13 @@ public abstract class GameState {
|
||||
|
||||
private void handleCardAttachments() {
|
||||
// Unattach all permanents first
|
||||
for(Entry<Card, Integer> entry : cardToAttachId.entrySet()) {
|
||||
for (Entry<Card, Integer> entry : cardToAttachId.entrySet()) {
|
||||
Card attachedTo = idToCard.get(entry.getValue());
|
||||
attachedTo.unAttachAllCards();
|
||||
}
|
||||
|
||||
// Attach permanents by ID
|
||||
for(Entry<Card, Integer> entry : cardToAttachId.entrySet()) {
|
||||
for (Entry<Card, Integer> entry : cardToAttachId.entrySet()) {
|
||||
Card attachedTo = idToCard.get(entry.getValue());
|
||||
Card attacher = entry.getKey();
|
||||
if (attacher.isAttachment()) {
|
||||
@@ -1125,7 +1125,7 @@ public abstract class GameState {
|
||||
}
|
||||
|
||||
// Enchant players by ID
|
||||
for(Entry<Card, Integer> entry : cardToEnchantPlayerId.entrySet()) {
|
||||
for (Entry<Card, Integer> entry : cardToEnchantPlayerId.entrySet()) {
|
||||
// TODO: improve this for game states with more than two players
|
||||
Card attacher = entry.getKey();
|
||||
Game game = attacher.getGame();
|
||||
@@ -1136,9 +1136,9 @@ public abstract class GameState {
|
||||
}
|
||||
|
||||
private void handleMergedCards() {
|
||||
for(Entry<Card, List<String>> entry : cardToMergedCards.entrySet()) {
|
||||
for (Entry<Card, List<String>> entry : cardToMergedCards.entrySet()) {
|
||||
Card mergedTo = entry.getKey();
|
||||
for(String mergedCardName : entry.getValue()) {
|
||||
for (String mergedCardName : entry.getValue()) {
|
||||
Card c;
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(mergedCardName. replace("^", ","));
|
||||
if (pc == null) {
|
||||
|
||||
@@ -1064,7 +1064,7 @@ public class PlayerControllerAi extends PlayerController {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean prepareSingleSa(final Card host, final SpellAbility sa, boolean isMandatory){
|
||||
private boolean prepareSingleSa(final Card host, final SpellAbility sa, boolean isMandatory) {
|
||||
if (sa.hasParam("TargetingPlayer")) {
|
||||
Player targetingPlayer = AbilityUtils.getDefinedPlayers(host, sa.getParam("TargetingPlayer"), sa).get(0);
|
||||
sa.setTargetingPlayer(targetingPlayer);
|
||||
|
||||
@@ -115,8 +115,7 @@ public class AttachAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if (abCost.getTotalMana().countX() > 0 && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value. (Endless Scream and Venarian
|
||||
// Gold)
|
||||
// Set PayX here to maximum value. (Endless Scream and Venarian Gold)
|
||||
final int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
|
||||
if (xPay == 0) {
|
||||
|
||||
@@ -407,6 +407,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
||||
if (num.contains("X") && sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
int xPay = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
if (xPay == 0) return false;
|
||||
xPay = Math.min(xPay, list.size());
|
||||
sa.setXManaCostPaid(xPay);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
||||
}
|
||||
|
||||
if ("MomirAvatar".equals(aiLogic)) {
|
||||
return SpecialCardAi.MomirVigAvatar.consider(aiPlayer, sa);
|
||||
return SpecialCardAi.MomirVigAvatar.consider(aiPlayer, sa);
|
||||
} else if ("MimicVat".equals(aiLogic)) {
|
||||
return SpecialCardAi.MimicVat.considerCopy(aiPlayer, sa);
|
||||
} else if ("AtEOT".equals(aiLogic)) {
|
||||
@@ -59,7 +59,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("AtEOT") && !aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1)) {
|
||||
if (sa.hasParam("AtEOT") && !ph.is(PhaseType.MAIN1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,6 @@ public class CopyPermanentAi extends SpellAbilityAi {
|
||||
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
|
||||
final boolean canCopyLegendary = sa.hasParam("NonLegendary");
|
||||
|
||||
|
||||
// ////
|
||||
// Targeting
|
||||
if (sa.usesTargeting()) {
|
||||
|
||||
@@ -163,8 +163,6 @@ public class DiscardAi extends SpellAbilityAi {
|
||||
return false;
|
||||
} // discardTargetAI()
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
@@ -211,9 +209,8 @@ public class DiscardAi extends SpellAbilityAi {
|
||||
return true;
|
||||
} // discardCheckDrawbackAI()
|
||||
|
||||
|
||||
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
if ( mode == PlayerActionConfirmMode.Random ) { //
|
||||
if ( mode == PlayerActionConfirmMode.Random ) {
|
||||
// TODO For now AI will always discard Random used currently with: Balduvian Horde and similar cards
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CounterEnumType;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
@@ -23,15 +25,17 @@ public class LegendaryRuleAi extends SpellAbilityAi {
|
||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||
return false; // should not get here
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// Choose a single legendary/planeswalker card to keep
|
||||
Card firstOption = Iterables.getFirst(options, null);
|
||||
CardCollection legends = new CardCollection(options);
|
||||
CardCollection badOptions = ComputerUtil.choosePermanentsToSacrifice(ai, legends, legends.size() -1, sa, false, false);
|
||||
legends.removeAll(badOptions);
|
||||
Card firstOption = Iterables.getFirst(legends, null);
|
||||
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();
|
||||
|
||||
if ( choosingFromPlanewalkers ) {
|
||||
if (choosingFromPlanewalkers) {
|
||||
// AI decision making - should AI compare counters?
|
||||
} else {
|
||||
// AI decision making - should AI compare damage and debuffs?
|
||||
|
||||
@@ -250,9 +250,6 @@ public class TokenAi extends SpellAbilityAi {
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
String tokenAmount = sa.getParamOrDefault("TokenAmount", "1");
|
||||
|
||||
final Card source = sa.getHostCard();
|
||||
final TargetRestrictions tgt = sa.getTargetRestrictions();
|
||||
if (tgt != null) {
|
||||
sa.resetTargets();
|
||||
@@ -262,16 +259,21 @@ public class TokenAi extends SpellAbilityAi {
|
||||
sa.getTargets().add(ai);
|
||||
}
|
||||
}
|
||||
|
||||
Card actualToken = spawnToken(ai, sa);
|
||||
String tokenPower = sa.getParamOrDefault("TokenPower", actualToken.getBasePowerString());
|
||||
String tokenToughness = sa.getParamOrDefault("TokenToughness", actualToken.getBaseToughnessString());
|
||||
String tokenAmount = sa.getParamOrDefault("TokenAmount", "1");
|
||||
final Card source = sa.getHostCard();
|
||||
|
||||
if ("X".equals(tokenAmount) || "X".equals(tokenPower) || "X".equals(tokenToughness)) {
|
||||
int x = AbilityUtils.calculateAmount(source, tokenAmount, sa);
|
||||
if (sa.getSVar("X").equals("Count$xPaid")) {
|
||||
// Set PayX here to maximum value.
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
sa.setXManaCostPaid(x);
|
||||
if (x == 0) { // already paid outside trigger
|
||||
// Set PayX here to maximum value.
|
||||
x = ComputerUtilCost.getMaxXValue(sa, ai);
|
||||
sa.setXManaCostPaid(x);
|
||||
}
|
||||
}
|
||||
if (x <= 0) {
|
||||
return false;
|
||||
|
||||
@@ -414,7 +414,7 @@ public final class CardRules implements ICardCharacteristics {
|
||||
String key = colonPos > 0 ? line.substring(0, colonPos) : line;
|
||||
String value = colonPos > 0 ? line.substring(1+colonPos).trim() : null;
|
||||
|
||||
switch(key.charAt(0)) {
|
||||
switch (key.charAt(0)) {
|
||||
case 'A':
|
||||
if ("A".equals(key)) {
|
||||
this.faces[curFace].addAbility(value);
|
||||
|
||||
@@ -759,7 +759,6 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
///////// Utility methods
|
||||
public static boolean isACardType(final String cardType) {
|
||||
return CoreType.isValidEnum(cardType);
|
||||
|
||||
@@ -40,7 +40,6 @@ public class PrintSheet {
|
||||
|
||||
private final ItemPool<PaperCard> cardsWithWeights;
|
||||
|
||||
|
||||
private final String name;
|
||||
public PrintSheet(String name0) {
|
||||
this(name0, null);
|
||||
|
||||
@@ -43,9 +43,8 @@ public class DeckGenPool implements IDeckGenPool {
|
||||
Iterable<PaperCard> editionCards=Iterables.filter(cards.values(), filter);
|
||||
if (editionCards.iterator().hasNext()){
|
||||
return editionCards.iterator().next();
|
||||
}else {
|
||||
return getCard(name);
|
||||
}
|
||||
return getCard(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,9 +3,6 @@ package forge.item;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Filtering conditions for miscellaneous InventoryItems.
|
||||
*/
|
||||
|
||||
@@ -351,4 +351,3 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
|| (this.getName().equals("Mountain"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ public class PaperToken implements InventoryItemFromSet, IPaperCard {
|
||||
build.add(subtypes);
|
||||
|
||||
// Are these keywords sorted?
|
||||
for(String keyword : rules.getMainPart().getKeywords()) {
|
||||
for (String keyword : rules.getMainPart().getKeywords()) {
|
||||
build.add(keyword);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public class ImageUtil {
|
||||
}
|
||||
|
||||
public static PaperCard getPaperCardFromImageKey(String key) {
|
||||
if ( key == null ) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ public class ImageUtil {
|
||||
String edition = key.substring(index + 1);
|
||||
if (script.startsWith("emblem"))
|
||||
return null;
|
||||
if (null == StaticData.instance().getCardEdition(edition)) {
|
||||
script = key;
|
||||
edition = "???";
|
||||
}
|
||||
script = script.replaceAll("[0-9]*$", "");
|
||||
return StaticData.instance().getAllTokens().getToken(script, edition);
|
||||
}
|
||||
|
||||
@@ -366,7 +366,6 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
||||
if (!Expressions.compare(left, presentCompare, right)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (params.containsKey("IsPresent2")) {
|
||||
|
||||
@@ -1633,7 +1633,7 @@ public class GameAction {
|
||||
|
||||
recheck = true;
|
||||
|
||||
Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new SpellAbility.EmptySa(ApiType.InternalLegendaryRule, null, p),
|
||||
Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new SpellAbility.EmptySa(ApiType.InternalLegendaryRule, new Card(-1, game), p),
|
||||
"You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)", null);
|
||||
for (Card c: cc) {
|
||||
if (c != toKeep) {
|
||||
@@ -1905,7 +1905,6 @@ public class GameAction {
|
||||
game.getTriggerHandler().runTrigger(TriggerType.NewGame, AbilityKey.newMap(), true);
|
||||
//</THIS CODE WILL WORK WITH PHASE = NULL>
|
||||
|
||||
|
||||
game.getPhaseHandler().startFirstTurn(first, startGameHook);
|
||||
//after game ends, ensure Auto-Pass canceled for all players so it doesn't apply to next game
|
||||
for (Player p : game.getRegisteredPlayers()) {
|
||||
@@ -1980,7 +1979,6 @@ public class GameAction {
|
||||
private void runPreOpeningHandActions(final Player first) {
|
||||
Player takesAction = first;
|
||||
do {
|
||||
//
|
||||
List<Card> ploys = CardLists.filter(takesAction.getCardsIn(ZoneType.Command), new Predicate<Card>() {
|
||||
@Override
|
||||
public boolean apply(Card input) {
|
||||
@@ -2054,8 +2052,7 @@ public class GameAction {
|
||||
public void invoke(final Runnable proc) {
|
||||
if (ThreadUtil.isGameThread()) {
|
||||
proc.run();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ThreadUtil.invokeInGameThread(proc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,8 +274,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
||||
blockers = att.getValue();
|
||||
if (blockers.isEmpty()) {
|
||||
sb.append(Localizer.getInstance().getMessage("lblLogPlayerDidntBlockAttacker", controllerName, att.getKey()));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sb.append(Localizer.getInstance().getMessage("lblLogPlayerAssignedBlockerToBlockAttacker", controllerName, Lang.joinHomogenous(blockers), att.getKey()));
|
||||
}
|
||||
firstAttacker = false;
|
||||
|
||||
@@ -178,7 +178,6 @@ public final class AbilityFactory {
|
||||
return getAbility(type, type.getApiTypeOf(mapParams), mapParams, parseAbilityCost(state, mapParams, type), state, sVarHolder);
|
||||
}
|
||||
|
||||
|
||||
public static Cost parseAbilityCost(final CardState state, Map<String, String> mapParams, AbilityRecordType type) {
|
||||
Cost abCost = null;
|
||||
if (type != AbilityRecordType.SubAbility) {
|
||||
|
||||
@@ -1282,7 +1282,8 @@ public class AbilityUtils {
|
||||
}
|
||||
else if (defined.equals("Opponent")) {
|
||||
players.addAll(player.getOpponents());
|
||||
} else if (defined.startsWith("NextPlayerToYour")) {
|
||||
}
|
||||
else if (defined.startsWith("NextPlayerToYour")) {
|
||||
Direction dir = defined.substring(16).equals("Left") ? Direction.Left : Direction.Right;
|
||||
players.add(game.getNextPlayerAfter(player, dir));
|
||||
}
|
||||
@@ -1806,6 +1807,19 @@ public class AbilityUtils {
|
||||
}
|
||||
return count;
|
||||
}
|
||||
// Count$TriggeredManaCostDevotion.<Color>
|
||||
if (sq[0].startsWith("TriggeredManaCostDevotion")) {
|
||||
final SpellAbility root = sa.getRootAbility();
|
||||
Card triggeringObject = (Card) root.getTriggeringObject(AbilityKey.Card);
|
||||
int count = 0;
|
||||
byte colorCode = ManaAtom.fromName(sq[1]);
|
||||
for (ManaCostShard sh : triggeringObject.getManaCost()) {
|
||||
if (sh.isColor(colorCode)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
// Count$TriggeredPayingMana.<Color1>.<Color2>
|
||||
if (sq[0].startsWith("TriggeredPayingMana")) {
|
||||
final SpellAbility root = sa.getRootAbility();
|
||||
@@ -1927,7 +1941,7 @@ public class AbilityUtils {
|
||||
// Count$DevotionDual.<color name>.<color name>
|
||||
// Count$Devotion.<color name>
|
||||
if (sq[0].contains("Devotion")) {
|
||||
int colorOcurrencices = 0;
|
||||
int colorOccurrences = 0;
|
||||
String colorName = sq[1];
|
||||
if (colorName.contains("Chosen")) {
|
||||
colorName = MagicColor.toShortString(c.getChosenColor());
|
||||
@@ -1939,12 +1953,12 @@ public class AbilityUtils {
|
||||
for (Card c0 : player.getCardsIn(ZoneType.Battlefield)) {
|
||||
for (ManaCostShard sh : c0.getManaCost()) {
|
||||
if (sh.isColor(colorCode)) {
|
||||
colorOcurrencices++;
|
||||
colorOccurrences++;
|
||||
}
|
||||
}
|
||||
colorOcurrencices += c0.getAmountOfKeyword("Your devotion to each color and each combination of colors is increased by one.");
|
||||
colorOccurrences += c0.getAmountOfKeyword("Your devotion to each color and each combination of colors is increased by one.");
|
||||
}
|
||||
return doXMath(colorOcurrencices, expr, c, ctb);
|
||||
return doXMath(colorOccurrences, expr, c, ctb);
|
||||
}
|
||||
|
||||
} // end ctb != null
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
package forge.game.ability.effects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.StaticData;
|
||||
import forge.card.CardFacePredicates;
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.CardSplitType;
|
||||
import forge.card.ICardFace;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
@@ -26,9 +20,7 @@ import forge.game.card.CardLists;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.TargetRestrictions;
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.Aggregates;
|
||||
import forge.util.ComparableOp;
|
||||
import forge.util.Localizer;
|
||||
|
||||
public class ChooseCardNameEffect extends SpellAbilityEffect {
|
||||
|
||||
@@ -327,11 +327,10 @@ public class EffectEffect extends SpellAbilityEffect {
|
||||
hostCard.addImprintedCard(eff);
|
||||
}
|
||||
|
||||
eff.updateStateForView();
|
||||
|
||||
// TODO: Add targeting to the effect so it knows who it's dealing with
|
||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
||||
game.getAction().moveTo(ZoneType.Command, eff, sa);
|
||||
eff.updateStateForView();
|
||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
||||
//if (effectTriggers != null) {
|
||||
// game.getTriggerHandler().registerActiveTrigger(cmdEffect, false);
|
||||
|
||||
@@ -36,7 +36,7 @@ public class RegenerateAllEffect extends RegenerateBaseEffect {
|
||||
list = CardLists.getValidCards(list, valid.split(","), hostCard.getController(), hostCard, sa);
|
||||
|
||||
// create Effect for Regeneration
|
||||
createRengenerationEffect(sa, list);
|
||||
createRegenerationEffect(sa, list);
|
||||
} // regenerateAllResolve
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import forge.game.zone.ZoneType;
|
||||
|
||||
public abstract class RegenerateBaseEffect extends SpellAbilityEffect {
|
||||
|
||||
public void createRengenerationEffect(SpellAbility sa, final Iterable<Card> list) {
|
||||
public void createRegenerationEffect(SpellAbility sa, final Iterable<Card> list) {
|
||||
final Card hostCard = sa.getHostCard();
|
||||
final Game game = hostCard.getGame();
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class RegenerateEffect extends RegenerateBaseEffect {
|
||||
@Override
|
||||
public void resolve(SpellAbility sa) {
|
||||
// create Effect for Regeneration
|
||||
createRengenerationEffect(sa, getTargetCards(sa));
|
||||
} // regenerateResolve
|
||||
createRegenerationEffect(sa, getTargetCards(sa));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public class RegenerationEffect extends SpellAbilityEffect {
|
||||
c.addRegeneratedThisTurn();
|
||||
|
||||
if (game.getCombat() != null) {
|
||||
game.getCombat().saveLKI(c);
|
||||
game.getCombat().removeFromCombat(c);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public class RemoveFromCombatEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
|
||||
game.getCombat().saveLKI(c);
|
||||
combat.removeFromCombat(c);
|
||||
|
||||
if (rem) {
|
||||
|
||||
@@ -49,6 +49,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
|
||||
game.clearCounterAddedThisTurn();
|
||||
game.resetPlayersAttackedOnNextTurn();
|
||||
game.resetPlayersAttackedOnNextTurn();
|
||||
game.setMonarch(null);
|
||||
GameAction action = game.getAction();
|
||||
|
||||
for (Player p: players) {
|
||||
@@ -59,6 +60,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
|
||||
p.setLandsPlayedLastTurn(0);
|
||||
p.resetCommanderStats();
|
||||
p.resetCompletedDungeons();
|
||||
p.setBlessing(false);
|
||||
|
||||
CardCollection newLibrary = new CardCollection(p.getCardsIn(restartZones, false));
|
||||
List<Card> filteredCards = null;
|
||||
@@ -111,4 +113,3 @@ public class RestartGameEffect extends SpellAbilityEffect {
|
||||
return TextUtil.fastReplace(desc, "CARDNAME", sa.getHostCard().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import forge.game.ability.AbilityKey;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.ability.ApiType;
|
||||
import forge.game.combat.Combat;
|
||||
import forge.game.combat.CombatLki;
|
||||
import forge.game.cost.Cost;
|
||||
import forge.game.cost.CostSacrifice;
|
||||
import forge.game.event.*;
|
||||
@@ -295,6 +296,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesTurnStatic = HashBasedTable.create();
|
||||
private final Table<SpellAbility, StaticAbility, List<String>> chosenModesGameStatic = HashBasedTable.create();
|
||||
|
||||
private CombatLki combatLKI = null;
|
||||
|
||||
// Enumeration for CMC request types
|
||||
public enum SplitCMCMode {
|
||||
CurrentSideCMC,
|
||||
@@ -1354,7 +1357,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
@Override
|
||||
public int addCounter(final CounterType counterType, final int n, final Player source, final SpellAbility cause, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) {
|
||||
int addAmount = n;
|
||||
if(addAmount <= 0 || !canReceiveCounters(counterType)) {
|
||||
if (addAmount <= 0 || !canReceiveCounters(counterType)) {
|
||||
// As per rule 107.1b
|
||||
return 0;
|
||||
}
|
||||
@@ -1974,6 +1977,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
sbx.append(" (").append(inst.getReminderText()).append(")");
|
||||
sbLong.append(sbx).append("\r\n");
|
||||
}
|
||||
} else if (keyword.startsWith("Trample:")) {
|
||||
sbLong.append("Trample over planeswalkers").append(" (").append(inst.getReminderText()).append(")").append("\r\n");
|
||||
} else if (keyword.startsWith("Hexproof:")) {
|
||||
final String[] k = keyword.split(":");
|
||||
sbLong.append("Hexproof from ").append(k[2])
|
||||
@@ -2000,7 +2005,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
|| keyword.equals("Suspend") // for the ones without amount
|
||||
|| keyword.equals("Foretell") // for the ones without cost
|
||||
|| keyword.equals("Hideaway") || keyword.equals("Ascend")
|
||||
|| keyword.equals("Trample over planeswalkers")
|
||||
|| keyword.equals("Totem armor") || keyword.equals("Battle cry")
|
||||
|| keyword.equals("Devoid") || keyword.equals("Riot")){
|
||||
sbLong.append(keyword).append(" (").append(inst.getReminderText()).append(")");
|
||||
@@ -4670,6 +4674,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
setPhasedOut(!phasedOut);
|
||||
final Combat combat = getGame().getPhaseHandler().getCombat();
|
||||
if (combat != null && phasedOut) {
|
||||
combat.saveLKI(this);
|
||||
combat.removeFromCombat(this);
|
||||
}
|
||||
|
||||
@@ -6167,7 +6172,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source == null){
|
||||
if (source == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6924,4 +6929,18 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
||||
public final void clearUntilLeavesBattlefield() {
|
||||
untilLeavesBattlefield = view.clearCards(untilLeavesBattlefield, TrackableProperty.UntilLeavesBattlefield);
|
||||
}
|
||||
|
||||
public CombatLki getCombatLKI() {
|
||||
return combatLKI;
|
||||
}
|
||||
public void setCombatLKI(CombatLki combatLKI) {
|
||||
this.combatLKI = combatLKI;
|
||||
}
|
||||
|
||||
public boolean isAttacking() {
|
||||
if (getCombatLKI() != null) {
|
||||
return getCombatLKI().isAttacker;
|
||||
}
|
||||
return getGame().getCombat().isAttacking(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,6 +518,7 @@ public class CardFactoryUtil {
|
||||
final Set<String> protectionkw = Sets.newHashSet();
|
||||
final Set<String> protectionColorkw = Sets.newHashSet();
|
||||
final Set<String> hexproofkw = Sets.newHashSet();
|
||||
final Set<String> tramplekw = Sets.newHashSet();
|
||||
final Set<String> allkw = Sets.newHashSet();
|
||||
|
||||
for (Card c : CardLists.getValidCards(cardlist, restrictions, p, host, null)) {
|
||||
@@ -535,6 +536,8 @@ public class CardFactoryUtil {
|
||||
}
|
||||
} else if (k.startsWith("Hexproof")) {
|
||||
hexproofkw.add(k);
|
||||
} else if (k.startsWith("Trample")) {
|
||||
tramplekw.add(k);
|
||||
}
|
||||
allkw.add(k);
|
||||
}
|
||||
@@ -548,6 +551,8 @@ public class CardFactoryUtil {
|
||||
filteredkw.addAll(landkw);
|
||||
} else if (keyword.equals("Hexproof")) {
|
||||
filteredkw.addAll(hexproofkw);
|
||||
} else if (keyword.equals("Trample")) {
|
||||
filteredkw.addAll(tramplekw);
|
||||
} else if (allkw.contains(keyword)) {
|
||||
filteredkw.add(keyword);
|
||||
}
|
||||
@@ -781,7 +786,7 @@ public class CardFactoryUtil {
|
||||
+ "TriggerZones$ Battlefield | Secondary$ True | TriggerDescription$ "
|
||||
+ "Annihilator " + n + " (" + inst.getReminderText() + ")";
|
||||
|
||||
final String effect = "DB$ Sacrifice | Defined$ DefendingPlayer | SacValid$ Permanent | Amount$ " + k[1];
|
||||
final String effect = "DB$ Sacrifice | Defined$ TriggeredDefendingPlayer | SacValid$ Permanent | Amount$ " + k[1];
|
||||
|
||||
final Trigger trigger = TriggerHandler.parseTrigger(trig, card, intrinsic);
|
||||
trigger.setOverridingAbility(AbilityFactory.getAbility(effect, card));
|
||||
@@ -1516,7 +1521,7 @@ public class CardFactoryUtil {
|
||||
} else if (keyword.equals("Provoke")) {
|
||||
final String actualTrigger = "Mode$ Attacks | ValidCard$ Card.Self | OptionalDecider$ You | Secondary$ True"
|
||||
+ " | TriggerDescription$ Provoke (" + inst.getReminderText() + ")";
|
||||
final String blockStr = "DB$ MustBlock | ValidTgts$ Creature.DefenderCtrl | TgtPrompt$ Select target creature defending player controls";
|
||||
final String blockStr = "DB$ MustBlock | ValidTgts$ Creature.ControlledBy TriggeredDefendingPlayer | TgtPrompt$ Select target creature defending player controls";
|
||||
final String untapStr = "DB$ Untap | Defined$ Targeted";
|
||||
|
||||
SpellAbility blockSA = AbilityFactory.getAbility(blockStr, card);
|
||||
|
||||
@@ -1412,8 +1412,7 @@ public class CardProperty {
|
||||
// These predicated refer to ongoing combat. If no combat happens, they'll return false (meaning not attacking/blocking ATM)
|
||||
else if (property.startsWith("attacking")) {
|
||||
if (null == combat) return false;
|
||||
if (property.equals("attacking")) return combat.isAttacking(card);
|
||||
if (property.equals("attackingLKI")) return combat.isLKIAttacking(card);
|
||||
if (property.equals("attacking")) return card.isAttacking();
|
||||
if (property.equals("attackingYou")) return combat.isAttacking(card, sourceController);
|
||||
if (property.equals("attackingSame")) {
|
||||
final GameEntity attacked = combat.getDefenderByAttacker(source);
|
||||
@@ -1514,12 +1513,10 @@ public class CardProperty {
|
||||
return false;
|
||||
}
|
||||
String valid = property.split(" ")[1];
|
||||
for(Card c : blocked) {
|
||||
if (c.isValid(valid, card.getController(), source, spellAbility)) {
|
||||
return true;
|
||||
}
|
||||
if (Iterables.any(blocked, CardPredicates.restriction(valid, card.getController(), source, spellAbility))) {
|
||||
return true;
|
||||
}
|
||||
for(Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) {
|
||||
for (Card c : AbilityUtils.getDefinedCards(source, valid, spellAbility)) {
|
||||
if (blocked.contains(c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -323,6 +323,10 @@ public final class CardUtil {
|
||||
|
||||
newCopy.setExiledWith(getLKICopy(in.getExiledWith(), cachedMap));
|
||||
|
||||
if (in.getGame().getCombat() != null) {
|
||||
newCopy.setCombatLKI(in.getGame().getCombat().saveLKI(newCopy));
|
||||
}
|
||||
|
||||
return newCopy;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,9 @@ public class CounterType implements Comparable<CounterType>, Serializable {
|
||||
final String[] k = sVal.split(":");
|
||||
return "Hexproof from " + k[2];
|
||||
}
|
||||
if (sVal.startsWith("Trample:")) {
|
||||
return "Trample over Planeswalkers";
|
||||
}
|
||||
return sVal;
|
||||
}
|
||||
|
||||
@@ -119,6 +122,9 @@ public class CounterType implements Comparable<CounterType>, Serializable {
|
||||
if (sVal.startsWith("Hexproof:")) {
|
||||
return true;
|
||||
}
|
||||
if (sVal.startsWith("Trample:")) {
|
||||
return true;
|
||||
}
|
||||
return keywordCounter.contains(sVal);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import forge.game.card.Card;
|
||||
import forge.game.card.CardCollection;
|
||||
import forge.game.card.CardCollectionView;
|
||||
import forge.game.card.CardDamageMap;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.player.Player;
|
||||
import forge.game.replacement.ReplacementType;
|
||||
@@ -75,7 +76,7 @@ public class Combat {
|
||||
|
||||
private Map<Card, CardCollection> attackersOrderedForDamageAssignment = Maps.newHashMap();
|
||||
private Map<Card, CardCollection> blockersOrderedForDamageAssignment = Maps.newHashMap();
|
||||
private Map<GameEntity, CombatLki> lkiCache = Maps.newHashMap();
|
||||
private CardCollection lkiCache = new CardCollection();
|
||||
private CardDamageMap damageMap = new CardDamageMap();
|
||||
|
||||
// List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW)
|
||||
@@ -300,7 +301,7 @@ public class Combat {
|
||||
return ab;
|
||||
}
|
||||
}
|
||||
CombatLki lki = lkiCache.get(c);
|
||||
CombatLki lki = lkiCache.get(c).getCombatLKI();
|
||||
return lki == null || !lki.isAttacker ? null : lki.getFirstBand();
|
||||
}
|
||||
|
||||
@@ -316,14 +317,6 @@ public class Combat {
|
||||
return Lists.newArrayList(attackedByBands.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a card is attacking, returns true if the card was attacking when it left the battlefield
|
||||
*/
|
||||
public final boolean isLKIAttacking(final Card c) {
|
||||
AttackingBand ab = getBandOfAttacker(c);
|
||||
return ab != null;
|
||||
}
|
||||
|
||||
public boolean isAttacking(Card card, GameEntity defender) {
|
||||
AttackingBand ab = getBandOfAttacker(card);
|
||||
for (Entry<GameEntity, AttackingBand> ee : attackedByBands.entries()) {
|
||||
@@ -786,7 +779,7 @@ public class Combat {
|
||||
assignedDamage = true;
|
||||
GameEntity defender = getDefenderByAttacker(band);
|
||||
// If the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender
|
||||
if (defender instanceof Card && attacker.hasKeyword("Trample over planeswalkers")) {
|
||||
if (defender instanceof Card && attacker.hasKeyword("Trample:Planeswalker")) {
|
||||
if (orderedBlockers == null || orderedBlockers.isEmpty()) {
|
||||
CardCollection cc = new CardCollection();
|
||||
cc.add((Card)defender);
|
||||
@@ -918,7 +911,7 @@ public class Combat {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
CombatLki lki = lkiCache.get(blocker).getCombatLKI();
|
||||
return null != lki && !lki.isAttacker; // was blocking something anyway
|
||||
}
|
||||
|
||||
@@ -933,21 +926,36 @@ public class Combat {
|
||||
return false;
|
||||
}
|
||||
|
||||
CombatLki lki = lkiCache.get(blocker);
|
||||
CombatLki lki = lkiCache.get(blocker).getCombatLKI();
|
||||
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
|
||||
}
|
||||
|
||||
public void saveLKI(Card lastKnownInfo) {
|
||||
public CombatLki saveLKI(Card lki) {
|
||||
if (!lki.isLKI()) {
|
||||
lki = CardUtil.getLKICopy(lki);
|
||||
}
|
||||
FCollectionView<AttackingBand> attackersBlocked = null;
|
||||
final AttackingBand attackingBand = getBandOfAttacker(lastKnownInfo);
|
||||
final AttackingBand attackingBand = getBandOfAttacker(lki);
|
||||
final boolean isAttacker = attackingBand != null;
|
||||
if (!isAttacker) {
|
||||
attackersBlocked = getAttackingBandsBlockedBy(lastKnownInfo);
|
||||
if (isAttacker) {
|
||||
boolean found = false;
|
||||
for (AttackingBand ab : attackedByBands.values()) {
|
||||
if (ab.contains(lki)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
attackersBlocked = getAttackingBandsBlockedBy(lki);
|
||||
if (attackersBlocked.isEmpty()) {
|
||||
return; // card was not even in combat
|
||||
return null; // card was not even in combat
|
||||
}
|
||||
}
|
||||
lkiCache.add(lki);
|
||||
final FCollectionView<AttackingBand> relatedBands = isAttacker ? new FCollection<>(attackingBand) : attackersBlocked;
|
||||
lkiCache.put(lastKnownInfo, new CombatLki(isAttacker, relatedBands));
|
||||
return new CombatLki(isAttacker, relatedBands);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,8 +240,7 @@ public class Cost implements Serializable {
|
||||
xCantBe0 = true;
|
||||
} else if ("Mandatory".equals(part)) {
|
||||
this.isMandatory = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
CostPart cp = parseCostPart(part, tapCost, untapCost);
|
||||
if (null != cp )
|
||||
if (cp instanceof CostPartMana ) {
|
||||
@@ -268,7 +267,6 @@ public class Cost implements Serializable {
|
||||
}
|
||||
|
||||
private static CostPart parseCostPart(String parse, boolean tapCost, boolean untapCost) {
|
||||
|
||||
if (parse.startsWith("Mana<")) {
|
||||
final String[] splitStr = TextUtil.split(abCostParse(parse, 1)[0], '\\');
|
||||
final String restriction = splitStr.length > 1 ? splitStr[1] : null;
|
||||
|
||||
@@ -10,8 +10,7 @@ public class GameEventAnteCardsSelected extends GameEvent {
|
||||
public GameEventAnteCardsSelected(Multimap<Player, Card> list) {
|
||||
cards = list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -14,7 +14,7 @@ public class GameEventAttackersDeclared extends GameEvent {
|
||||
|
||||
public final Player player;
|
||||
public final Multimap<GameEntity, Card> attackersMap;
|
||||
|
||||
|
||||
public GameEventAttackersDeclared(Player playerTurn, Multimap<GameEntity, Card> attackersMap) {
|
||||
this.player = playerTurn;
|
||||
this.attackersMap = attackersMap;
|
||||
|
||||
@@ -20,7 +20,7 @@ public class GameEventBlockersDeclared extends GameEvent {
|
||||
|
||||
public final Map<GameEntity, MapOfLists<Card, Card>> blockers;
|
||||
public final Player defendingPlayer;
|
||||
|
||||
|
||||
public GameEventBlockersDeclared(Player who, Map<GameEntity, MapOfLists<Card, Card>> blockers) {
|
||||
this.blockers = blockers;
|
||||
defendingPlayer = who;
|
||||
|
||||
@@ -8,13 +8,13 @@ public class GameEventCardAttachment extends GameEvent {
|
||||
public final Card equipment;
|
||||
public final GameEntity newTarget; // can enchant player, I'm ssaving a class to enchants - it could be incorrect.
|
||||
public final GameEntity oldEntiy;
|
||||
|
||||
|
||||
public GameEventCardAttachment(Card attachment, GameEntity formerEntity, GameEntity newEntity) {
|
||||
this.equipment = attachment;
|
||||
this.newTarget = newEntity;
|
||||
this.oldEntiy = formerEntity;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -6,11 +6,10 @@ import forge.util.TextUtil;
|
||||
|
||||
public class GameEventCardChangeZone extends GameEvent {
|
||||
|
||||
public final Card card;
|
||||
public final Zone from;
|
||||
public final Zone to;
|
||||
|
||||
public final Card card;
|
||||
public final Zone from;
|
||||
public final Zone to;
|
||||
|
||||
public GameEventCardChangeZone(Card c, Zone zoneFrom, Zone zoneTo) {
|
||||
card = c;
|
||||
from = zoneFrom;
|
||||
@@ -21,7 +20,7 @@ public class GameEventCardChangeZone extends GameEvent {
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -15,7 +15,7 @@ public class GameEventCardCounters extends GameEvent {
|
||||
this.oldValue = old;
|
||||
this.newValue = newValue;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -3,7 +3,7 @@ package forge.game.event;
|
||||
import forge.game.card.Card;
|
||||
|
||||
public class GameEventCardDamaged extends GameEvent {
|
||||
|
||||
|
||||
public enum DamageType {
|
||||
Normal,
|
||||
M1M1Counters,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package forge.game.event;
|
||||
|
||||
public class GameEventCardDestroyed extends GameEvent {
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -3,12 +3,12 @@ package forge.game.event;
|
||||
import forge.game.player.Player;
|
||||
|
||||
public class GameEventCardModeChosen extends GameEvent {
|
||||
|
||||
|
||||
public final Player player;
|
||||
public final String cardName;
|
||||
public final String mode;
|
||||
public final boolean log;
|
||||
|
||||
|
||||
public GameEventCardModeChosen(Player player, String cardName, String mode, boolean log) {
|
||||
this.player = player;
|
||||
this.cardName = cardName;
|
||||
@@ -21,5 +21,3 @@ public class GameEventCardModeChosen extends GameEvent {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public class GameEventCardPhased extends GameEvent {
|
||||
|
||||
public final Card card;
|
||||
public final boolean phaseState;
|
||||
|
||||
|
||||
public GameEventCardPhased(Card card, boolean state) {
|
||||
this.card = card;
|
||||
phaseState = state;
|
||||
@@ -20,7 +20,7 @@ public class GameEventCardPhased extends GameEvent {
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package forge.game.event;
|
||||
|
||||
public class GameEventCardRegenerated extends GameEvent {
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package forge.game.event;
|
||||
|
||||
public class GameEventCardSacrificed extends GameEvent {
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -18,7 +18,7 @@ public class GameEventCardStatsChanged extends GameEvent {
|
||||
public GameEventCardStatsChanged(Card affected) {
|
||||
cards = Arrays.asList(affected);
|
||||
}
|
||||
|
||||
|
||||
public GameEventCardStatsChanged(Collection<Card> affected) {
|
||||
cards = affected;
|
||||
}
|
||||
@@ -31,13 +31,13 @@ public class GameEventCardStatsChanged extends GameEvent {
|
||||
// TODO Auto-generated method stub
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Card card = Iterables.getFirst(cards, null);
|
||||
if ( null == card )
|
||||
if (null == card)
|
||||
return "Card state changes: (empty list)";
|
||||
if( cards.size() == 1)
|
||||
if (cards.size() == 1)
|
||||
return "Card state changes: " + card.getName() +
|
||||
" (" + StringUtils.join(card.getType(), ' ') + ") " +
|
||||
card.getNetPower() + "/" + card.getNetToughness();
|
||||
|
||||
@@ -10,8 +10,7 @@ public class GameEventCardTapped extends GameEvent {
|
||||
this.tapped = tapped;
|
||||
this.card = card;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -8,7 +8,7 @@ public class GameEventCombatEnded extends GameEvent {
|
||||
|
||||
public final List<Card> attackers;
|
||||
public final List<Card> blockers;
|
||||
|
||||
|
||||
public GameEventCombatEnded(List<Card> attackers, List<Card> blockers) {
|
||||
this.attackers = attackers;
|
||||
this.blockers = blockers;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package forge.game.event;
|
||||
|
||||
public class GameEventFlipCoin extends GameEvent {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -12,8 +12,7 @@ public class GameEventGameOutcome extends GameEvent {
|
||||
this.result = lastOne;
|
||||
this.history = history;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -9,12 +9,11 @@ import forge.game.player.Player;
|
||||
public class GameEventGameRestarted extends GameEvent {
|
||||
|
||||
public final Player whoRestarted;
|
||||
|
||||
|
||||
public GameEventGameRestarted(Player playerTurn) {
|
||||
whoRestarted = playerTurn;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -22,12 +22,11 @@ public class GameEventGameStarted extends GameEvent {
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,6 @@ public class GameEventLandPlayed extends GameEvent {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -4,11 +4,11 @@ import forge.game.player.Player;
|
||||
|
||||
// This special event denotes loss of mana due to phase end
|
||||
public class GameEventManaBurn extends GameEvent {
|
||||
|
||||
|
||||
public final Player player;
|
||||
public final boolean causedLifeLoss;
|
||||
public final int amount;
|
||||
|
||||
|
||||
/**
|
||||
* TODO: Write javadoc for Constructor.
|
||||
* @param dealDamage
|
||||
@@ -19,7 +19,7 @@ public class GameEventManaBurn extends GameEvent {
|
||||
amount = burn;
|
||||
causedLifeLoss = dealDamage;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -24,7 +24,7 @@ public class GameEventManaPool extends GameEvent {
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -12,8 +12,7 @@ public class GameEventMulligan extends GameEvent {
|
||||
public GameEventMulligan(Player p) {
|
||||
player = p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -8,18 +8,18 @@ public class GameEventPlayerLivesChanged extends GameEvent {
|
||||
public final Player player;
|
||||
public final int oldLives;
|
||||
public final int newLives;
|
||||
|
||||
|
||||
public GameEventPlayerLivesChanged(Player who, int oldValue, int newValue) {
|
||||
player = who;
|
||||
oldLives = oldValue;
|
||||
newLives = newValue;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TextUtil.concatWithSpace(Lang.getInstance().getPossesive(player.getName()),"lives changed:", String.valueOf(oldLives),"->", String.valueOf(newLives));
|
||||
|
||||
@@ -19,8 +19,7 @@ public class GameEventPlayerPoisoned extends GameEvent {
|
||||
oldValue = old;
|
||||
amount = num;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -13,7 +13,7 @@ public class GameEventPlayerPriority extends GameEvent {
|
||||
public final Player turn;
|
||||
public final PhaseType phase;
|
||||
public final Player priority;
|
||||
|
||||
|
||||
public GameEventPlayerPriority(Player playerTurn, PhaseType phase, Player priorityPlayer) {
|
||||
turn = playerTurn;
|
||||
this.phase = phase;
|
||||
@@ -25,7 +25,6 @@ public class GameEventPlayerPriority extends GameEvent {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -3,10 +3,10 @@ package forge.game.event;
|
||||
import forge.game.player.Player;
|
||||
|
||||
public class GameEventScry extends GameEvent {
|
||||
|
||||
|
||||
public final Player player;
|
||||
public final int toTop, toBottom;
|
||||
|
||||
|
||||
public GameEventScry(Player player, int toTop, int toBottom) {
|
||||
this.player = player;
|
||||
this.toTop = toTop;
|
||||
@@ -18,4 +18,3 @@ public class GameEventScry extends GameEvent {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import forge.util.Lang;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
public class GameEventShuffle extends GameEvent {
|
||||
|
||||
|
||||
public final Player player;
|
||||
|
||||
|
||||
public GameEventShuffle(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class GameEventShuffle extends GameEvent {
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ public class GameEventSpellAbilityCast extends GameEvent {
|
||||
public final SpellAbilityStackInstance si;
|
||||
public final boolean replicate;
|
||||
public final int stackIndex;
|
||||
|
||||
|
||||
public GameEventSpellAbilityCast(SpellAbility sp, SpellAbilityStackInstance si, int stackIndex, boolean replicate) {
|
||||
sa = sp;
|
||||
this.si = si;
|
||||
|
||||
@@ -23,8 +23,6 @@ public class GameEventSpellResolved extends GameEvent {
|
||||
this.hasFizzled = hasFizzled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -3,10 +3,10 @@ package forge.game.event;
|
||||
import forge.game.player.Player;
|
||||
|
||||
public class GameEventSurveil extends GameEvent {
|
||||
|
||||
|
||||
public final Player player;
|
||||
public final int toLibrary, toGraveyard;
|
||||
|
||||
|
||||
public GameEventSurveil(Player player, int toLibrary, int toGraveyard) {
|
||||
this.player = player;
|
||||
this.toLibrary = toLibrary;
|
||||
@@ -18,4 +18,3 @@ public class GameEventSurveil extends GameEvent {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package forge.game.event;
|
||||
|
||||
public class GameEventTokenCreated extends GameEvent {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -4,10 +4,10 @@ import forge.game.player.Player;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
public class GameEventTurnBegan extends GameEvent {
|
||||
|
||||
|
||||
public final Player turnOwner;
|
||||
public final int turnNumber;
|
||||
|
||||
|
||||
public GameEventTurnBegan(Player turnOwner, int turnNumber) {
|
||||
super();
|
||||
this.turnOwner = turnOwner;
|
||||
@@ -18,7 +18,7 @@ public class GameEventTurnBegan extends GameEvent {
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package forge.game.event;
|
||||
|
||||
public class GameEventTurnEnded extends GameEvent {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||
return visitor.visit(this);
|
||||
|
||||
@@ -105,4 +105,3 @@ public interface IGameEventVisitor<T> {
|
||||
public T visit(GameEventZone event) { return null; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,8 +152,7 @@ public enum Keyword {
|
||||
SURGE("Surge", KeywordWithCost.class, false, "You may cast this spell for its surge cost if you or a teammate has cast another spell this turn."),
|
||||
SUSPEND("Suspend", Suspend.class, false, "Rather than cast this card from your hand, you may pay %s and exile it with {%d:time counter} on it. At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost."),
|
||||
TOTEM_ARMOR("Totem armor", SimpleKeyword.class, true, "If enchanted permanent would be destroyed, instead remove all damage marked on it and destroy this Aura."),
|
||||
TRAMPLE("Trample", SimpleKeyword.class, true, "This creature can deal excess combat damage to the player or planeswalker it's attacking."),
|
||||
TRAMPLE_OVER_PLANESWALKERS("Trample over planeswalkers", SimpleKeyword.class, true, "This creature can deal excess combat damage to the controller of the planeswalker it’s attacking."),
|
||||
TRAMPLE("Trample", Trample.class, true, "This creature can deal excess combat damage to the player or planeswalker it's attacking."),
|
||||
TRANSFIGURE("Transfigure", KeywordWithCost.class, false, "%s, Sacrifice this creature: Search your library for a creature card with the same mana value as this creature and put that card onto the battlefield, then shuffle. Transfigure only as a sorcery."),
|
||||
TRANSMUTE("Transmute", KeywordWithCost.class, false, "%s, Discard this card: Search your library for a card with the same mana value as this card, reveal it, and put it into your hand, then shuffle. Transmute only as a sorcery."),
|
||||
TRIBUTE("Tribute", KeywordWithAmount.class, false, "As this creature enters the battlefield, an opponent of your choice may put {%d:+1/+1 counter} on it."),
|
||||
|
||||
35
forge-game/src/main/java/forge/game/keyword/Trample.java
Normal file
35
forge-game/src/main/java/forge/game/keyword/Trample.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package forge.game.keyword;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class Trample extends KeywordInstance<Trample> {
|
||||
private String type = "";
|
||||
|
||||
@Override
|
||||
protected void parse(String details) {
|
||||
if (!details.isEmpty()) {
|
||||
type = details.split(":")[0];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatReminderText(String reminderText) {
|
||||
if (!type.isEmpty()) {
|
||||
return "This creature can deal excess combat damage to the controller of the planeswalker it’s attacking.";
|
||||
}
|
||||
return reminderText;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see forge.game.keyword.KeywordInstance#redundant(java.util.Collection)
|
||||
*/
|
||||
@Override
|
||||
public boolean redundant(Collection<KeywordInterface> list) {
|
||||
for (KeywordInterface i : list) {
|
||||
if (i.getOriginal().equals(getOriginal())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,10 @@ package forge.game.mana;
|
||||
import forge.card.MagicColor;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardUtil;
|
||||
import forge.game.spellability.AbilityManaPart;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.zone.ZoneType;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -71,7 +73,7 @@ public class Mana {
|
||||
public Mana(final byte color, final Card source, final AbilityManaPart manaAbility) {
|
||||
this.color = color;
|
||||
this.manaAbility = manaAbility;
|
||||
this.sourceCard = source;
|
||||
this.sourceCard = source.isInZone(ZoneType.Battlefield) ? CardUtil.getLKICopy(source) : source.getGame().getChangeZoneLKIInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3221,21 +3221,21 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
|
||||
public void updateKeywordCardAbilityText() {
|
||||
if(getKeywordCard() == null)
|
||||
if (getKeywordCard() == null)
|
||||
return;
|
||||
final PlayerZone com = getZone(ZoneType.Command);
|
||||
keywordEffect.setText("");
|
||||
keywordEffect.updateAbilityTextForView();
|
||||
boolean headerAdded = false;
|
||||
StringBuilder kw = new StringBuilder();
|
||||
for(KeywordInterface k : keywords) {
|
||||
if(!headerAdded) {
|
||||
for (KeywordInterface k : keywords) {
|
||||
if (!headerAdded) {
|
||||
headerAdded = true;
|
||||
kw.append(this.getName()).append(" has: \n");
|
||||
}
|
||||
kw.append(k).append("\n");
|
||||
}
|
||||
if(!kw.toString().isEmpty()) {
|
||||
if (!kw.toString().isEmpty()) {
|
||||
keywordEffect.setText(trimKeywords(kw.toString()));
|
||||
keywordEffect.updateAbilityTextForView();
|
||||
}
|
||||
@@ -3275,7 +3275,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
|
||||
final PlayerZone com = getZone(ZoneType.Command);
|
||||
|
||||
if(bless) {
|
||||
if (bless) {
|
||||
blessingEffect = new Card(game.nextCardId(), null, game);
|
||||
blessingEffect.setOwner(this);
|
||||
blessingEffect.setImageKey("t:blessing");
|
||||
@@ -3337,7 +3337,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
||||
}
|
||||
return targetPlayer == null || !targetPlayer.equals(sa.getActivatingPlayer())
|
||||
|| !hasKeyword("Spells and abilities you control can't cause you to search your library.");
|
||||
|
||||
}
|
||||
|
||||
public Card getKeywordCard() {
|
||||
|
||||
@@ -65,7 +65,7 @@ public abstract class AbilityActivated extends SpellAbility implements Cloneable
|
||||
this.setTargetRestrictions(tgt);
|
||||
}
|
||||
|
||||
public boolean isActivatedAbility() { return true; }
|
||||
public boolean isActivatedAbility() { return !isTrigger(); }
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.google.common.collect.Sets;
|
||||
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameStage;
|
||||
import forge.game.IHasSVars;
|
||||
import forge.game.TriggerReplacementBase;
|
||||
import forge.game.ability.AbilityFactory;
|
||||
@@ -347,8 +348,9 @@ public abstract class Trigger extends TriggerReplacementBase {
|
||||
}
|
||||
|
||||
// host controller will be null when adding card in a simulation game
|
||||
if (this.getHostCard().getController() == null || !meetsCommonRequirements(this.mapParams))
|
||||
if (this.getHostCard().getController() == null || game.getAge() != GameStage.Play || !meetsCommonRequirements(this.mapParams)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ public class TriggerAbandoned extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Scheme))) {
|
||||
return false;
|
||||
}
|
||||
@@ -60,7 +59,6 @@ public class TriggerAbandoned extends Trigger {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
@@ -72,4 +70,3 @@ public class TriggerAbandoned extends Trigger {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ public class TriggerAttached extends Trigger {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
|
||||
@@ -54,7 +54,6 @@ public class TriggerAttackerUnblocked extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Attacker))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ public class TriggerAttacks extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Attacker))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ public class TriggerChampioned extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Championed))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ public class TriggerDamageAll extends Trigger {
|
||||
|
||||
@Override
|
||||
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (hasParam("CombatDamage")) {
|
||||
if (getParam("CombatDamage").equals("True")) {
|
||||
if (!((Boolean) runParams.get(AbilityKey.IsCombatDamage))) {
|
||||
|
||||
@@ -59,7 +59,6 @@ public class TriggerDamageDealtOnce extends Trigger {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (hasParam("CombatDamage")) {
|
||||
if (getParam("CombatDamage").equals("True")) {
|
||||
if (!((Boolean) runParams.get(AbilityKey.IsCombatDamage))) {
|
||||
|
||||
@@ -21,7 +21,6 @@ public class TriggerDamageDoneOnce extends Trigger {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean performTest(Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (hasParam("CombatDamage")) {
|
||||
if (getParam("CombatDamage").equals("True")) {
|
||||
if (!((Boolean) runParams.get(AbilityKey.IsCombatDamage))) {
|
||||
@@ -54,7 +53,11 @@ public class TriggerDamageDoneOnce extends Trigger {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<Card, Integer> damageMap = (Map<Card, Integer>) runParams.get(AbilityKey.DamageMap);
|
||||
|
||||
sa.setTriggeringObject(AbilityKey.Target, CardUtil.getLKICopy((Card)runParams.get(AbilityKey.DamageTarget)));
|
||||
Object target = runParams.get(AbilityKey.DamageTarget);
|
||||
if (target instanceof Card) {
|
||||
target = CardUtil.getLKICopy((Card)runParams.get(AbilityKey.DamageTarget));
|
||||
}
|
||||
sa.setTriggeringObject(AbilityKey.Target, target);
|
||||
sa.setTriggeringObject(AbilityKey.Sources, getDamageSources(damageMap));
|
||||
sa.setTriggeringObject(AbilityKey.DamageAmount, getDamageAmount(damageMap));
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ public class TriggerDamagePrevented extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidSource", runParams.get(AbilityKey.DamageSource))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ public class TriggerDamagePreventedOnce extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidTarget", runParams.get(AbilityKey.DamageTarget))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -82,4 +82,3 @@ public class TriggerExcessDamage extends Trigger {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ public class TriggerExploited extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Exploited))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -73,4 +73,3 @@ public class TriggerExplores extends Trigger {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,4 +70,3 @@ public class TriggerFightOnce extends Trigger {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -579,7 +579,7 @@ public class TriggerHandler {
|
||||
sa.setOptionalTrigger(true);
|
||||
decider = AbilityUtils.getDefinedPlayers(host, regtrig.getParam("OptionalDecider"), sa).get(0);
|
||||
}
|
||||
else if (sa instanceof AbilitySub || !sa.hasParam("Cost") || sa.getParam("Cost").equals("0")) {
|
||||
else if (sa instanceof AbilitySub || !sa.hasParam("Cost") || (sa.getPayCosts() != null && sa.getPayCosts().isMandatory()) || sa.getParam("Cost").equals("0")) {
|
||||
isMandatory = true;
|
||||
} else { // triggers with a cost can't be mandatory
|
||||
sa.setOptionalTrigger(true);
|
||||
|
||||
@@ -67,7 +67,6 @@ public class TriggerPayCumulativeUpkeep extends Trigger {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
|
||||
@@ -66,7 +66,6 @@ public class TriggerPayEcho extends Trigger {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
|
||||
@@ -17,7 +17,6 @@ public class TriggerPhaseOut extends Trigger {
|
||||
* @param runParams*/
|
||||
@Override
|
||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||
|
||||
if (!matchesValidParam("ValidCard", runParams.get(AbilityKey.Card))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,6 @@ public class TriggerSacrificed extends Trigger {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
|
||||
@@ -61,7 +61,6 @@ public class TriggerScry extends Trigger {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void setTriggeringObjects(final SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user