...
 
Commits (120)
......@@ -1601,8 +1601,8 @@ public class ComputerUtilCard {
}
pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp);
pumped.setPTBoost(c.getPTBoostMap());
pumped.addPTBoost(power + berserkPower, toughness, timestamp);
pumped.setPTBoost(c.getPTBoostTable());
pumped.addPTBoost(power + berserkPower, toughness, timestamp, null);
pumped.addChangedCardKeywords(kws, null, false, false, timestamp);
Set<CounterType> types = c.getCounters().keySet();
for(CounterType ct : types) {
......@@ -1649,8 +1649,8 @@ public class ComputerUtilCard {
list.add(vCard); // account for the static abilities that may be present on the card itself
for (final Card c : list) {
// remove old boost that might be copied
vCard.removePTBoost(c.getTimestamp());
for (final StaticAbility stAb : c.getStaticAbilities()) {
vCard.removePTBoost(c.getTimestamp(), stAb.getId());
final Map<String, String> params = stAb.getMapParams();
if (!params.get("Mode").equals("Continuous")) {
continue;
......@@ -1683,7 +1683,7 @@ public class ComputerUtilCard {
def = AbilityUtils.calculateAmount(c, addT, stAb);
}
}
vCard.addPTBoost(att, def, c.getTimestamp());
vCard.addPTBoost(att, def, c.getTimestamp(), stAb.getId());
}
}
}
......
......@@ -156,7 +156,7 @@ public class DamageDealAi extends DamageAiBase {
}
}
}
if (ai.getAttackedWithCreatureThisTurn()) {
if (!ai.getCreaturesAttackedThisTurn().isEmpty()) {
dmg = Integer.parseInt(logic.substring(logic.indexOf(".") + 1));
}
} else if ("WildHunt".equals(logic)) {
......
......@@ -63,7 +63,7 @@ public class DestroyAi extends SpellAbilityAi {
}
} else if ("AtEOTIfNotAttacking".equals(sa.getParam("AILogic"))) {
PhaseHandler ph = ai.getGame().getPhaseHandler();
if (!ph.is(PhaseType.END_OF_TURN) || ai.getAttackedWithCreatureThisTurn()) {
if (!ph.is(PhaseType.END_OF_TURN) || !ai.getCreaturesAttackedThisTurn().isEmpty()) {
return false;
}
}
......
......@@ -246,7 +246,7 @@ public class GameCopier {
newCard.addType(type);
}
for (StaticAbility stAb : c.getStaticAbilities()) {
newCard.addStaticAbility(stAb);
newCard.addStaticAbility(stAb.copy(newCard, true));
}
for (SpellAbility sa : c.getSpellAbilities()) {
SpellAbility saCopy = sa.copy(newCard, true);
......@@ -277,7 +277,7 @@ public class GameCopier {
// TODO: Copy the full list with timestamps.
newCard.addNewPT(setPower, setToughness, newGame.getNextTimestamp());
}
newCard.setPTBoost(c.getPTBoostMap());
newCard.setPTBoost(c.getPTBoostTable());
newCard.setDamage(c.getDamage());
newCard.setChangedCardTypes(c.getChangedCardTypesMap());
......
......@@ -8,7 +8,8 @@ public enum CardSplitType
Transform(FaceSelectionMethod.USE_ACTIVE_FACE, CardStateName.Transformed),
Meld(FaceSelectionMethod.USE_ACTIVE_FACE, CardStateName.Meld),
Split(FaceSelectionMethod.COMBINE, CardStateName.RightSplit),
Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Flipped);
Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Flipped),
Adventure(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Adventure);
CardSplitType(FaceSelectionMethod calcMode, CardStateName stateName) {
method = calcMode;
......
......@@ -9,6 +9,7 @@ public enum CardStateName {
Meld,
LeftSplit,
RightSplit,
Adventure,
;
......
......@@ -259,7 +259,19 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView {
return false;
}
final String payingMana = StringUtils.join(hostCard.getCastSA().getPayingMana());
if (StringUtils.countMatches(payingMana, MagicColor.toShortString(params.get("Adamant"))) < 3) {
final String color = params.get("Adamant");
if ("Any".equals(color)) {
boolean bFlag = false;
for (byte c : MagicColor.WUBRG) {
if (StringUtils.countMatches(payingMana, MagicColor.toShortString(c)) >= 3) {
bFlag = true;
break;
}
}
if (!bFlag) {
return false;
}
} else if (StringUtils.countMatches(payingMana, MagicColor.toShortString(color)) < 3) {
return false;
}
}
......
......@@ -54,6 +54,8 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
import java.util.*;
import static forge.util.EnumMapUtil.toStringMap;
/**
* Methods for common actions performed during a game.
*
......@@ -144,6 +146,10 @@ public class GameAction {
}
}
// if an adventureCard is put from Stack somewhere else, need to reset to Original State
if (c.isAdventureCard() && (zoneFrom.is(ZoneType.Stack) || !zoneTo.is(ZoneType.Stack))) {
c.setState(CardStateName.Original, true);
}
// Clean up the temporary Dash SVar when the Dashed card leaves the battlefield
// Clean up the temporary AtEOT SVar
......@@ -297,7 +303,7 @@ public class GameAction {
repParams.putAll(params);
}
ReplacementResult repres = game.getReplacementHandler().run(EnumMapUtil.toStringMap(repParams));
ReplacementResult repres = game.getReplacementHandler().run(toStringMap(repParams));
if (repres != ReplacementResult.NotReplaced) {
// reset failed manifested Cards back to original
if (c.isManifested()) {
......
......@@ -22,6 +22,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import forge.card.CardStateName;
import forge.card.mana.ManaCost;
import forge.card.mana.ManaCostParser;
import forge.game.ability.AbilityUtils;
......@@ -101,6 +102,45 @@ public final class GameActionUtil {
}
source.turnFaceDownNoUpdate();
lkicheck = true;
} else if (sa.isAdventure() && !source.isInZone(ZoneType.Battlefield)) {
if (!source.isLKI()) {
source = CardUtil.getLKICopy(source);
}
source.setState(CardStateName.Adventure, false);
// need to reset CMC
source.setLKICMC(-1);
source.setLKICMC(source.getCMC());
lkicheck = true;
} else if (source.isSplitCard() && (sa.isLeftSplit() || sa.isRightSplit())) {
if (!source.isLKI()) {
source = CardUtil.getLKICopy(source);
}
if (sa.isLeftSplit()) {
if (!source.hasState(CardStateName.LeftSplit)) {
source.addAlternateState(CardStateName.LeftSplit, false);
source.getState(CardStateName.LeftSplit).copyFrom(
sa.getHostCard().getState(CardStateName.LeftSplit), true);
}
source.setState(CardStateName.LeftSplit, false);
}
if (sa.isRightSplit()) {
if (!source.hasState(CardStateName.RightSplit)) {
source.addAlternateState(CardStateName.RightSplit, false);
source.getState(CardStateName.RightSplit).copyFrom(
sa.getHostCard().getState(CardStateName.RightSplit), true);
}
source.setState(CardStateName.RightSplit, false);
}
// need to reset CMC
source.setLKICMC(-1);
source.setLKICMC(source.getCMC());
lkicheck = true;
}
if (lkicheck) {
......@@ -132,20 +172,6 @@ public final class GameActionUtil {
newSA = sa.copyWithManaCostReplaced(activator, o.getAltManaCost());
newSA.setBasicSpell(false);
changedManaCost = true;
if (host.hasSVar("AsForetoldSplitCMCHack")) {
// TODO: This is a temporary workaround for As Foretold interaction with split cards, better solution needed.
if (sa.isLeftSplit()) {
int leftCMC = sa.getHostCard().getCMC(Card.SplitCMCMode.LeftSplitCMC);
if (leftCMC > host.getCounters(CounterType.TIME)) {
continue;
}
} else if (sa.isRightSplit()) {
int rightCMC = sa.getHostCard().getCMC(Card.SplitCMCMode.RightSplitCMC);
if (rightCMC > host.getCounters(CounterType.TIME)) {
continue;
}
}
}
} else {
newSA = sa.copy(activator);
}
......
......@@ -170,7 +170,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
int restDamage = damage;
// first try to replace the damage
final Map<String, Object> repParams = Maps.newHashMap();
final Map<String, Object> repParams = Maps.newHashMap();
repParams.put("Event", "DamageDone");
repParams.put("Affected", this);
repParams.put("DamageSource", source);
......
......@@ -27,6 +27,7 @@ public enum GlobalRuleChange {
manapoolsDontEmpty ("Mana pools don't empty as steps and phases end."),
noCycling ("Players can't cycle cards."),
noCreatureETBTriggers ("Creatures entering the battlefield don't cause abilities to trigger."),
noCreatureDyingTriggers ("Creatures dying don't cause abilities to trigger."),
noLegendRule ("The legend rule doesn't apply."),
noPrevention ("Damage can't be prevented."),
/* onlyOneAttackerATurn ("No more than one creature can attack each turn."), */
......
......@@ -798,7 +798,7 @@ public class StaticEffect {
}
// remove P/T bonus
affectedCard.removePTBoost(getTimestamp());
affectedCard.removePTBoost(getTimestamp(), ability.getId());
// the view is updated in GameAction#checkStaticAbilities to avoid flickering
......
......@@ -8,43 +8,91 @@ import java.util.Map;
*/
public enum AbilityKey {
AbilityMana("AbilityMana"),
Activator("Activator"),
Affected("Affected"),
AllVotes("AllVotes"),
Amount("Amount"),
Attach("Attach"),
AttachSource("AttachSource"),
AttachTarget("AttachTarget"),
Attacked("Attacked"),
Attacker("Attacker"),
Attackers("Attackers"),
AttackingPlayer("AttackingPlayer"),
AttackedTarget("AttackedTarget"),
Blocker("Blocker"),
Blockers("Blockers"),
CastSA("CastSA"),
CastSACMC("CastSACMC"),
Card("Card"),
Cards("Cards"),
CardLKI("CardLKI"),
Cause("Cause"),
Causer("Causer"),
Championed("Championed"),
CopySA("CopySA"),
Cost("Cost"),
CostStack("CostStack"),
CounterAmount("CounterAmount"),
CounteredSA("CounteredSA"),
CounterType("CounterType"),
Crew("Crew"),
CumulativeUpkeepPaid("CumulativeUpkeepPaid"),
CurrentCastSpells("CurrentCastSpells"),
CurrentStormCount("CurrentStormCount"),
DamageAmount("DamageAmount"),
DamageSource("DamageSource"),
DamageSources("DamageSources"),
DamageTarget("DamageTarget"),
DamageTargets("DamageTargets"),
Defender("Defender"),
Defenders("Defenders"),
DefendingPlayer("DefendingPlayer"),
Destination("Destination"),
Devoured("Devoured"),
EchoPaid("EchoPaid"),
Exploited("Exploited"),
Explorer("Explorer"),
Event("Event"),
Fighter("Fighter"),
FirstTime("FirstTime"),
Fizzle("Fizzle"),
IsCombatDamage("IsCombatDamage"),
Player("Player"),
IndividualCostPaymentInstance("IndividualCostPaymentInstance"),
IsMadness("IsMadness"),
LifeAmount("LifeAmount"),
MonstrosityAmount("MonstrosityAmount"),
NewCounterAmount("NewCounterAmount"),
Num("Num"), // TODO confirm that this and NumThisTurn can be merged
NumBlockers("NumBlockers"),
NumThisTurn("NumThisTurn"),
Number("Number"),
Object("Object"),
Objects("Objects"),
OtherAttackers("OtherAttackers"),
OtherVoters("OtherVoters"),
Origin("Origin"),
OriginalController("OriginalController"),
OriginalDefender("OriginalDefender"),
PayingMana("PayingMana"),
Phase("Phase"),
Player("Player"),
Produced("Produced"),
Result("Result"),
Scheme("Scheme"),
Source("Source"),
Sources("Sources"),
SourceSA("SourceSA"),
SpellAbility("SpellAbility"),
SpellAbilityStackInstance("SpellAbilityStackInstance"),
SpellAbilityTargetingCards("SpellAbilityTargetingCards"),
StackInstance("StackInstance"),
StackSa("StackSa"),
StackSi("StackSi"),
Target("Target"),
Targets("Targets"),
Transformer("Transformer"),
Vehicle("Vehicle"),
Won("Won");
......@@ -59,10 +107,204 @@ public enum AbilityKey {
return key;
}
/**
* @param s A string that would be output from toString
* @return the corresponding key if there is one or null otherwise
*/
public static AbilityKey fromString(String s) {
switch (s) {
case "AbilityMana":
return AbilityMana;
case "Activator":
return Activator;
case "Affected":
return Affected;
case "AllVotes":
return AllVotes;
case "Amount":
return Amount;
case "Attach":
return Attach;
case "AttachSource":
return AttachSource;
case "AttachTarget":
return AttachTarget;
case "Attacked":
return Attacked;
case "Attacker":
return Attacker;
case "Attackers":
return Attackers;
case "AttackingPlayer":
return AttackingPlayer;
case "AttackedTarget":
return AttackedTarget;
case "Blocker":
return Blocker;
case "Blockers":
return Blockers;
case "CastSA":
return CastSA;
case "CastSACMC":
return CastSACMC;
case "Card":
return Card;
case "Cards":
return Cards;
case "CardLKI":
return CardLKI;
case "Cause":
return Cause;
case "Causer":
return Causer;
case "Championed":
return Championed;
case "CopySA":
return CopySA;
case "Cost":
return Cost;
case "CostStack":
return CostStack;
case "CounterAmount":
return CounterAmount;
case "CounteredSA":
return CounteredSA;
case "CounterType":
return CounterType;
case "Crew":
return Crew;
case "CumulativeUpkeepPaid":
return CumulativeUpkeepPaid;
case "CurrentCastSpells":
return CurrentCastSpells;
case "CurrentStormCount":
return CurrentStormCount;
case "DamageAmount":
return DamageAmount;
case "DamageSource":
return DamageSource;
case "DamageSources":
return DamageSources;
case "DamageTarget":
return DamageTarget;
case "DamageTargets":
return DamageTargets;
case "Defender":
return Defender;
case "Defenders":
return Defenders;
case "DefendingPlayer":
return DefendingPlayer;
case "Destination":
return Destination;
case "Devoured":
return Devoured;
case "EchoPaid":
return EchoPaid;
case "Exploited":
return Exploited;
case "Explorer":
return Explorer;
case "Event":
return Event;
case "Fighter":
return Fighter;
case "FirstTime":
return FirstTime;
case "Fizzle":
return Fizzle;
case "IsCombatDamage":
return IsCombatDamage;
case "IndividualCostPaymentInstance":
return IndividualCostPaymentInstance;
case "IsMadness":
return IsMadness;
case "LifeAmount":
return LifeAmount;
case "MonstrosityAmount":
return MonstrosityAmount;
case "NewCounterAmount":
return NewCounterAmount;
case "Num":
return Num;
case "NumBlockers":
return NumBlockers;
case "NumThisTurn":
return NumThisTurn;
case "Number":
return Number;
case "Object":
return Object;
case "Objects":
return Objects;
case "OtherAttackers":
return OtherAttackers;
case "OtherVoters":
return OtherVoters;
case "Origin":
return Origin;
case "OriginalController":
return OriginalController;
case "OriginalDefender":
return OriginalDefender;
case "PayingMana":
return PayingMana;
case "Phase":
return Phase;
case "Player":
return Player;
case "Produced":
return Produced;
case "Result":
return Result;
case "Scheme":
return Scheme;
case "Source":
return Source;
case "Sources":
return Sources;
case "SourceSA":
return SourceSA;
case "SpellAbility":
return SpellAbility;
case "SpellAbilityStackInstance":
return SpellAbilityStackInstance;
case "SpellAbilityTargetingCards":
return SpellAbilityTargetingCards;
case "StackInstance":
return StackInstance;
case "StackSa":
return StackSa;
case "StackSi":
return StackSi;
case "Target":
return Target;
case "Targets":
return Targets;
case "Transformer":
return Transformer;
case "Vehicle":
return Vehicle;
case "Won":
return Won;
default:
return null;
}
}
public static <V> EnumMap<AbilityKey, V> newMap() {
return new EnumMap<>(AbilityKey.class);
}
public static <V> EnumMap<AbilityKey, V> newMap(Map<AbilityKey, V> map) {
// The EnumMap constructor throws IllegalArgumentException if the map is empty.
if (map.isEmpty()) {
return newMap();
}
return new EnumMap<>(map);
}
public static Map<AbilityKey, Object> mapFromCard(forge.game.card.Card card) {
final Map<AbilityKey, Object> runParams = newMap();
......
......@@ -2,6 +2,7 @@ package forge.game.ability.effects;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.combat.AttackingBand;
......@@ -54,12 +55,12 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect {
// retarget triggers to the new defender (e.g. Ulamog, Ceaseless Hunger + Portal Mage)
for (SpellAbilityStackInstance si : game.getStack()) {
if (si.isTrigger() && c.equals(si.getSourceCard())
&& si.getTriggeringObject("Attacker") != null) {
si.addTriggeringObject("OriginalDefender", originalDefender);
&& si.getTriggeringObject(AbilityKey.Attacker) != null) {
si.addTriggeringObject(AbilityKey.OriginalDefender, originalDefender);
if (defender instanceof Player) {
si.updateTriggeringObject("DefendingPlayer", defender);
si.updateTriggeringObject(AbilityKey.DefendingPlayer, defender);
} else if (defender instanceof Card) {
si.updateTriggeringObject("DefendingPlayer", ((Card)defender).getController());
si.updateTriggeringObject(AbilityKey.DefendingPlayer, ((Card)defender).getController());
}
}
}
......
......@@ -980,7 +980,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
c.setController(newController, game.getNextTimestamp());
}
if (sa.hasParam("WithCounters")) {
String[] parse = sa.getParam("WithCounters").split("_");
c.addEtbCounter(CounterType.getType(parse[0]), Integer.parseInt(parse[1]), player);
}
if (sa.hasParam("Transformed")) {
if (c.isDoubleFaced()) {
c.changeCardState("Transform", null);
......
......@@ -65,6 +65,11 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect {
}
}
if (sa.hasParam("RememberDefinedNumber")) {
immediateTrig.addRemembered((Integer) AbilityUtils.calculateAmount(sa.getHostCard(),
sa.getParam("RememberDefinedNumber"), sa));
}
if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) {
SpellAbility overridingSA = sa.getAdditionalAbility("Execute");
overridingSA.setActivatingPlayer(sa.getActivatingPlayer());
......
......@@ -9,15 +9,18 @@ import forge.game.GameActionUtil;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.mana.Mana;
import forge.game.player.Player;
import forge.game.spellability.AbilityManaPart;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Iterables;
import java.util.List;
public class ManaEffect extends SpellAbilityEffect {
......@@ -181,6 +184,16 @@ public class ManaEffect extends SpellAbilityEffect {
}
String cs = manaType.toString();
abMana.setExpressChoice(cs);
} else if (type.startsWith("EachColorAmong")) {
final String res = type.split("_")[1];
final CardCollection list = CardLists.getValidCards(card.getGame().getCardsIn(ZoneType.Battlefield),
res, sa.getActivatingPlayer(), card, sa);
byte colors = 0;
for (Card c : list) {
colors |= c.determineColor().getColor();
}
if (colors == 0) return;
abMana.setExpressChoice(ColorSet.fromMask(colors));
}
if (abMana.getExpressChoice().isEmpty()) {
......
......@@ -52,7 +52,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
boolean redrawPT = false;
if (a != 0 || d != 0) {
tgtC.addPTBoost(a, d, timestamp);
tgtC.addPTBoost(a, d, timestamp, 0);
redrawPT = true;
}
......@@ -76,7 +76,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
@Override
public void run() {
tgtC.removePTBoost(timestamp);
tgtC.removePTBoost(timestamp, 0);
tgtC.removeChangedCardKeywords(timestamp);
for (String kw : hiddenkws) {
......
......@@ -56,7 +56,7 @@ public class PumpEffect extends SpellAbilityEffect {
}
if (a != 0 || d != 0) {
gameCard.addPTBoost(a, d, timestamp);
gameCard.addPTBoost(a, d, timestamp, 0);
redrawPT = true;
}
......@@ -84,7 +84,7 @@ public class PumpEffect extends SpellAbilityEffect {
@Override
public void run() {
gameCard.removePTBoost(timestamp);
gameCard.removePTBoost(timestamp, 0);
boolean updateText = false;
updateText = gameCard.removeCanBlockAny(timestamp) || updateText;
updateText = gameCard.removeCanBlockAdditional(timestamp) || updateText;
......
package forge.game.ability.effects;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.event.GameEventCardRegenerated;
......@@ -9,8 +10,6 @@ import forge.game.trigger.TriggerType;
import java.util.Map;
import com.google.common.collect.Maps;
public class RegenerationEffect extends SpellAbilityEffect {
/*
......@@ -44,10 +43,9 @@ public class RegenerationEffect extends SpellAbilityEffect {
}
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", c);
runParams.put("Cause", host);
game.getTriggerHandler().runTriggerOld(TriggerType.Regenerated, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(c);
runParams.put(AbilityKey.Cause, host);
game.getTriggerHandler().runTrigger(TriggerType.Regenerated, runParams, false);
}
}
......
......@@ -70,6 +70,7 @@ public class RestartGameEffect extends SpellAbilityEffect {
player.setStartingLife(psc.getStartingLife());
player.setPoisonCounters(0, sa.getHostCard());
player.resetSpellCastThisGame();
player.setLandsPlayedLastTurn(0);
player.resetLandsPlayedThisTurn();
player.resetInvestigatedThisTurn();
......
package forge.game.ability.effects;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
......@@ -14,9 +15,7 @@ import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
public class RevealEffect extends SpellAbilityEffect {
......@@ -74,14 +73,7 @@ public class RevealEffect extends SpellAbilityEffect {
game.getAction().reveal(revealed, p);
for (final Card c : revealed) {
Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Card", c);
/*
if (sa.hasParam("MiracleCost")) {
runParams.put("Miracle", true);
}
//*/
game.getTriggerHandler().runTriggerOld(TriggerType.Revealed, runParams, false);
game.getTriggerHandler().runTrigger(TriggerType.Revealed, AbilityKey.mapFromCard(c), false);
if (sa.hasParam("RememberRevealed")) {
host.addRemembered(c);
}
......
package forge.game.ability.effects;
import com.google.common.collect.Maps;
import forge.card.mana.ManaCost;
import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.GameEntityCounterTable;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.*;
......@@ -36,10 +36,9 @@ public class SacrificeEffect extends SpellAbilityEffect {
isPaid = activator.getController().payManaOptional(card, new Cost(sa.getParam("Echo"), true),
sa, "Pay Echo", ManaPaymentPurpose.Echo);
}
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("EchoPaid", Boolean.valueOf(isPaid));
runParams.put("Card", card);
game.getTriggerHandler().runTriggerOld(TriggerType.PayEcho, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(card);
runParams.put(AbilityKey.EchoPaid, isPaid);
game.getTriggerHandler().runTrigger(TriggerType.PayEcho, runParams, false);
if (isPaid || !card.getController().equals(activator)) {
return;
}
......@@ -65,11 +64,10 @@ public class SacrificeEffect extends SpellAbilityEffect {
sb.append("Cumulative upkeep for ").append(card);
boolean isPaid = activator.getController().payManaOptional(card, payCost, sa, sb.toString(), ManaPaymentPurpose.CumulativeUpkeep);
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("CumulativeUpkeepPaid", Boolean.valueOf(isPaid));
runParams.put("Card", card);
runParams.put("PayingMana", StringUtils.join(sa.getPayingMana(), ""));
game.getTriggerHandler().runTriggerOld(TriggerType.PayCumulativeUpkeep, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(card);
runParams.put(AbilityKey.CumulativeUpkeepPaid, isPaid);
runParams.put(AbilityKey.PayingMana, StringUtils.join(sa.getPayingMana(), ""));
game.getTriggerHandler().runTrigger(TriggerType.PayCumulativeUpkeep, runParams, false);
if (isPaid || !card.getController().equals(activator)) {
return;
}
......@@ -147,16 +145,15 @@ public class SacrificeEffect extends SpellAbilityEffect {
// Run Devour Trigger
if (devour) {
card.addDevoured(lKICopy);
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Devoured", sac);
game.getTriggerHandler().runTriggerOld(TriggerType.Devoured, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Devoured, sac);
game.getTriggerHandler().runTrigger(TriggerType.Devoured, runParams, false);
}
if (exploit) {
card.addExploited(lKICopy);
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Exploited", lKICopy);
runParams.put("Card", card);
game.getTriggerHandler().runTriggerOld(TriggerType.Exploited, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(card);
runParams.put(AbilityKey.Exploited, lKICopy);
game.getTriggerHandler().runTrigger(TriggerType.Exploited, runParams, false);
}
if (wasDestroyed || wasSacrificed) {
countSacrificed++;
......
package forge.game.ability.effects;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
......@@ -46,9 +46,9 @@ public class SetInMotionEffect extends SpellAbilityEffect {
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
// Run triggers
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("Scheme", controller.getActiveScheme());
game.getTriggerHandler().runTriggerOld(TriggerType.SetInMotion, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.Scheme, controller.getActiveScheme());
game.getTriggerHandler().runTrigger(TriggerType.SetInMotion, runParams, false);
} else {
controller.setSchemeInMotion();
}
......
......@@ -5,13 +5,13 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import forge.game.ability.AbilityKey;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.ability.AbilityFactory;
......@@ -98,10 +98,10 @@ public class VoteEffect extends SpellAbilityEffect {
host.getGame().getAction().nofityOfValue(sa, p, result + "\r\nCurrent Votes:" + votes, p);
}
}
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("AllVotes", votes);
game.getTriggerHandler().runTriggerOld(TriggerType.Vote, runParams, false);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.AllVotes, votes);
game.getTriggerHandler().runTrigger(TriggerType.Vote, runParams, false);
List<String> subAbs = Lists.newArrayList();
final List<Object> mostVotes = getMostVotes(votes);
......
......@@ -7,11 +7,11 @@ import java.util.Map;
import com.google.common.collect.ForwardingTable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import forge.game.GameEntity;
import forge.game.ability.AbilityKey;
import forge.game.keyword.Keyword;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
......@@ -34,12 +34,12 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
}
if (sum > 0) {
final GameEntity ge = e.getKey();
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("DamageTarget", ge);
runParams.put("DamageAmount", sum);
runParams.put("IsCombatDamage", isCombat);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.DamageTarget, ge);
runParams.put(AbilityKey.DamageAmount, sum);
runParams.put(AbilityKey.IsCombatDamage, isCombat);
ge.getGame().getTriggerHandler().runTriggerOld(TriggerType.DamagePreventedOnce, runParams, false);
ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamagePreventedOnce, runParams, false);
}
}
}
......@@ -53,13 +53,13 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
sum += i;
}
if (sum > 0) {
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("DamageSource", sourceLKI);
runParams.put("DamageTargets", Sets.newHashSet(e.getValue().keySet()));
runParams.put("DamageAmount", sum);
runParams.put("IsCombatDamage", isCombat);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.DamageSource, sourceLKI);
runParams.put(AbilityKey.DamageTargets, Sets.newHashSet(e.getValue().keySet()));
runParams.put(AbilityKey.DamageAmount, sum);
runParams.put(AbilityKey.IsCombatDamage, isCombat);
sourceLKI.getGame().getTriggerHandler().runTriggerOld(TriggerType.DamageDealtOnce, runParams, false);
sourceLKI.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false);
if (sourceLKI.hasKeyword(Keyword.LIFELINK)) {
sourceLKI.getController().gainLife(sum, sourceLKI, sa);
......@@ -74,13 +74,13 @@ public class CardDamageMap extends ForwardingTable<Card, GameEntity, Integer> {
}
if (sum > 0) {
final GameEntity ge = e.getKey();
final Map<String, Object> runParams = Maps.newHashMap();
runParams.put("DamageTarget", ge);
runParams.put("DamageSources", Sets.newHashSet(e.getValue().keySet()));
runParams.put("DamageAmount", sum);
runParams.put("IsCombatDamage", isCombat);
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
runParams.put(AbilityKey.DamageTarget, ge);
runParams.put(AbilityKey.DamageSources, Sets.newHashSet(e.getValue().keySet()));
runParams.put(AbilityKey.DamageAmount, sum);
runParams.put(AbilityKey.IsCombatDamage, isCombat);
ge.getGame().getTriggerHandler().runTriggerOld(TriggerType.DamageDoneOnce, runParams, false);
ge.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDoneOnce, runParams, false);
}
}
}
......
......@@ -305,6 +305,11 @@ public class CardFactory {
} else if (c.isMeldable() && cp instanceof PaperCard) {
c.setState(CardStateName.Meld, false);
c.setImageKey(cp.getImageKey(true));
} else if (c.isAdventureCard()) {
c.setState(CardStateName.Adventure, false);
c.setImageKey(originalPicture);
c.setSetCode(cp.getEdition());
c.setRarity(cp.getRarity());
}
c.setSetCode(cp.getEdition());
......@@ -325,7 +330,6 @@ public class CardFactory {
// ******************************************************************
// ************** Link to different CardFactories *******************
if (state == CardStateName.LeftSplit || state == CardStateName.RightSplit) {
for (final SpellAbility sa : card.getSpellAbilities()) {
if (state == CardStateName.LeftSplit) {
......@@ -342,6 +346,9 @@ public class CardFactory {
} else if (state != CardStateName.Original){
CardFactoryUtil.setupKeywordedAbilities(card);
}
if (state == CardStateName.Adventure) {
CardFactoryUtil.setupAdventureAbility(card);
}
}
card.setState(CardStateName.Original, false);
......@@ -728,6 +735,14 @@ public class CardFactory {
final CardState ret2 = new CardState(out, CardStateName.Flipped);
ret2.copyFrom(in.getState(CardStateName.Flipped, true), false);
result.put(CardStateName.Flipped, ret2);
} else if (in.isAdventureCard()) {
final CardState ret1 = new CardState(out, CardStateName.Original);
ret1.copyFrom(in.getState(CardStateName.Original, true), false);
result.put(CardStateName.Original, ret1);
final CardState ret2 = new CardState(out, CardStateName.Adventure);
ret2.copyFrom(in.getState(CardStateName.Adventure, true), false);
result.put(CardStateName.Adventure, ret2);
} else {
// in all other cases just copy the current state to original
final CardState ret = new CardState(out, CardStateName.Original);
......
......@@ -35,6 +35,7 @@ import forge.game.GameEntity;
import forge.game.GameEntityCounterTable;
import forge.game.GameLogEntryType;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.CardPredicates.Presets;
......@@ -917,6 +918,10 @@ public class CardFactoryUtil {
return doXMath(cc.getSurveilThisTurn(), m, c);
}
if (sq[0].equals("YouCastThisGame")) {
return doXMath(cc.getSpellsCastThisGame(), m, c);
}
if (sq[0].equals("FirstSpellTotalManaSpent")) {
try{
return doXMath(c.getFirstSpellAbility().getTotalManaSpent(), m, c);
......@@ -948,7 +953,7 @@ public class CardFactoryUtil {
// TriggeringObjects
if (sq[0].startsWith("Triggered")) {
return doXMath(xCount((Card) c.getTriggeringObject("Card"), sq[0].substring(9)), m, c);
return doXMath(xCount((Card) c.getTriggeringObject(AbilityKey.Card), sq[0].substring(9)), m, c);
}
if (sq[0].contains("YourStartingLife")) {
......@@ -1102,13 +1107,11 @@ public class CardFactoryUtil {
final String restriction = l[0].substring(11);
final String[] rest = restriction.split(",");
final CardCollection list = CardLists.getValidCards(cc.getGame().getCardsInGame(), rest, cc, c, null);
int n = 0;
for (final byte col : MagicColor.WUBRG) {
if (!CardLists.getColor(list, col).isEmpty()) {
n++;
}
byte n = 0;
for (final Card card : list) {
n |= card.determineColor().getColor();
}
return doXMath(n, m, c);
return doXMath(ColorSet.fromMask(n).countColors(), m, c);
}
if (sq[0].contains("CreatureType")) {
......@@ -1331,6 +1334,13 @@ public class CardFactoryUtil {
return doXMath(cc.getAttackersDeclaredThisTurn(), m, c);
}
// Count$CardAttackedThisTurn_<Valid>
if (sq[0].contains("CreaturesAttackedThisTurn")) {
final String[] workingCopy = l[0].split("_");
final String validFilter = workingCopy[1];
return doXMath(CardLists.getType(cc.getCreaturesAttackedThisTurn(), validFilter).size(), m, c);
}
// Count$ThisTurnCast <Valid>
// Count$LastTurnCast <Valid>
if (sq[0].contains("ThisTurnCast") || sq[0].contains("LastTurnCast")) {
......@@ -4728,4 +4738,30 @@ public class CardFactoryUtil {
}
return byClause + StringUtils.join(orClauses, " or ") + ".";
}
public static void setupAdventureAbility(Card card) {
if (card.getCurrentStateName() != CardStateName.Adventure) {
return;
}
SpellAbility sa = card.getFirstSpellAbility();
if (sa == null) {
return;
}
sa.setAdventure(true);
String abExile = "DB$ ChangeZone | Defined$ Self | Origin$ Stack | Destination$ Exile | StackDescription$ None";
AbilitySub saExile = (AbilitySub)AbilityFactory.getAbility(abExile, card);
String abEffect = "DB$ Effect | RememberObjects$ Self | StaticAbilities$ Play | ExileOnMoved$ Exile | Duration$ Permanent | ConditionDefined$ Self | ConditionPresent$ Card.nonCopiedSpell";
AbilitySub saEffect = (AbilitySub)AbilityFactory.getAbility(abEffect, card);
StringBuilder sb = new StringBuilder();
sb.append("Mode$ Continuous | MayPlay$ True | EffectZone$ Command | Affected$ Card.IsRemembered+nonAdventure");
sb.append(" | AffectedZone$ Exile | Description$ You may cast the card.");
saEffect.setSVar("Play", sb.toString());
saExile.setSubAbility(saEffect);
sa.appendSubAbility(saExile);
}
}
......@@ -6,6 +6,7 @@ import forge.card.MagicColor;
import forge.game.Direction;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.card.CardPredicates.Presets;
import forge.game.combat.AttackingBand;
......@@ -106,26 +107,8 @@ public class CardProperty {
if (card.isSplitCard()) {
return false;
}