mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Compare commits
93 Commits
additional
...
forge-2.0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a29da54b2e | ||
|
|
ec63b230c8 | ||
|
|
e880a83df2 | ||
|
|
80cc7218a3 | ||
|
|
4809fb858a | ||
|
|
3af62888dc | ||
|
|
79e1d0a0f0 | ||
|
|
c447dfc888 | ||
|
|
8149966915 | ||
|
|
472f9481e8 | ||
|
|
2209ce3cee | ||
|
|
258c89e65d | ||
|
|
11913085ef | ||
|
|
53fca12a57 | ||
|
|
8e8a795f19 | ||
|
|
a4b27321ac | ||
|
|
ead83d932f | ||
|
|
900bd4327d | ||
|
|
1a2bb054f4 | ||
|
|
f908df46c8 | ||
|
|
f8c97842c4 | ||
|
|
c324b45025 | ||
|
|
5061ceda0e | ||
|
|
d1e677eb4f | ||
|
|
493a8f351b | ||
|
|
04172eead0 | ||
|
|
f0ed9288b3 | ||
|
|
03fe3d63ea | ||
|
|
83438ef72b | ||
|
|
cf18808a70 | ||
|
|
49dc2c1c42 | ||
|
|
692400db2a | ||
|
|
751c31b226 | ||
|
|
7be252c509 | ||
|
|
288eac743c | ||
|
|
d0bd80f158 | ||
|
|
7fe8154bcb | ||
|
|
87cd5c90a3 | ||
|
|
12399fca48 | ||
|
|
aaf17553c1 | ||
|
|
9dedd24d3e | ||
|
|
2443f1486d | ||
|
|
cfd1822198 | ||
|
|
7930c4949b | ||
|
|
ef6d0707ac | ||
|
|
cfd792cb69 | ||
|
|
f5352662cd | ||
|
|
bf1192f80d | ||
|
|
dee2150cf9 | ||
|
|
c44b105d9f | ||
|
|
b624fb3cf8 | ||
|
|
45396c1bf4 | ||
|
|
f562ae6fdb | ||
|
|
6615090bda | ||
|
|
eaf6f117a2 | ||
|
|
a8488502e7 | ||
|
|
309e36827c | ||
|
|
fe7883ddd8 | ||
|
|
06e5ff5174 | ||
|
|
2c31dd01dd | ||
|
|
d8a92c4879 | ||
|
|
16baeadf0c | ||
|
|
88ed81f75f | ||
|
|
70d9df1db2 | ||
|
|
aaa04570f2 | ||
|
|
9365d55964 | ||
|
|
0c61139f51 | ||
|
|
34bd623e45 | ||
|
|
0e31bb8565 | ||
|
|
0b87094f96 | ||
|
|
42e53c66f6 | ||
|
|
25a7d80146 | ||
|
|
a6170745b1 | ||
|
|
db32547a6e | ||
|
|
4df6d9998b | ||
|
|
2f42f6ca28 | ||
|
|
6617c10946 | ||
|
|
1d34e02957 | ||
|
|
132f8d3d4f | ||
|
|
d3961b1a53 | ||
|
|
2c04ef9e1f | ||
|
|
f599e3ead6 | ||
|
|
e16da84a75 | ||
|
|
0a622f5282 | ||
|
|
bb40138c52 | ||
|
|
2a7bd8bbd2 | ||
|
|
e6fc666012 | ||
|
|
a1297e593c | ||
|
|
2026c7eca0 | ||
|
|
137076f224 | ||
|
|
5538650681 | ||
|
|
0e36e6b6d9 | ||
|
|
f4c786763a |
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@@ -45,17 +45,16 @@ public class BiomeStructureDataMappingEditor extends JComponent {
|
|||||||
JList list, Object value, int index,
|
JList list, Object value, int index,
|
||||||
boolean isSelected, boolean cellHasFocus) {
|
boolean isSelected, boolean cellHasFocus) {
|
||||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
if(!(value instanceof BiomeStructureData.BiomeStructureDataMapping))
|
if(!(value instanceof BiomeStructureData.BiomeStructureDataMapping biomeData))
|
||||||
return label;
|
return label;
|
||||||
BiomeStructureData.BiomeStructureDataMapping data=(BiomeStructureData.BiomeStructureDataMapping) value;
|
|
||||||
// Get the renderer component from parent class
|
// Get the renderer component from parent class
|
||||||
|
|
||||||
label.setText(data.name);
|
label.setText(biomeData.name);
|
||||||
if(editor.data!=null)
|
if(editor.data!=null)
|
||||||
{
|
{
|
||||||
SwingAtlas itemAtlas=new SwingAtlas(Config.instance().getFile(editor.data.structureAtlasPath));
|
SwingAtlas itemAtlas=new SwingAtlas(Config.instance().getFile(editor.data.structureAtlasPath));
|
||||||
if(itemAtlas.has(data.name))
|
if(itemAtlas.has(biomeData.name))
|
||||||
label.setIcon(itemAtlas.get(data.name));
|
label.setIcon(itemAtlas.get(biomeData.name));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImageIcon img=itemAtlas.getAny();
|
ImageIcon img=itemAtlas.getAny();
|
||||||
|
|||||||
@@ -25,9 +25,8 @@ public class DialogOptionEditor extends JComponent{
|
|||||||
JList list, Object value, int index,
|
JList list, Object value, int index,
|
||||||
boolean isSelected, boolean cellHasFocus) {
|
boolean isSelected, boolean cellHasFocus) {
|
||||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
if(!(value instanceof DialogData))
|
if(!(value instanceof DialogData dialog))
|
||||||
return label;
|
return label;
|
||||||
DialogData dialog=(DialogData) value;
|
|
||||||
StringBuilder builder=new StringBuilder();
|
StringBuilder builder=new StringBuilder();
|
||||||
if(dialog.name==null||dialog.name.isEmpty())
|
if(dialog.name==null||dialog.name.isEmpty())
|
||||||
builder.append("[[Blank Option]]");
|
builder.append("[[Blank Option]]");
|
||||||
|
|||||||
@@ -27,17 +27,16 @@ public class ItemsEditor extends JComponent {
|
|||||||
JList list, Object value, int index,
|
JList list, Object value, int index,
|
||||||
boolean isSelected, boolean cellHasFocus) {
|
boolean isSelected, boolean cellHasFocus) {
|
||||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
if(!(value instanceof ItemData))
|
if(!(value instanceof ItemData item))
|
||||||
return label;
|
return label;
|
||||||
ItemData Item=(ItemData) value;
|
|
||||||
// Get the renderer component from parent class
|
// Get the renderer component from parent class
|
||||||
|
|
||||||
label.setText(Item.name);
|
label.setText(item.name);
|
||||||
if(itemAtlas==null)
|
if(itemAtlas==null)
|
||||||
itemAtlas=new SwingAtlas(Config.instance().getFile(Paths.ITEMS_ATLAS));
|
itemAtlas=new SwingAtlas(Config.instance().getFile(Paths.ITEMS_ATLAS));
|
||||||
|
|
||||||
if(itemAtlas.has(Item.iconName))
|
if(itemAtlas.has(item.iconName))
|
||||||
label.setIcon(itemAtlas.get(Item.iconName));
|
label.setIcon(itemAtlas.get(item.iconName));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImageIcon img=itemAtlas.getAny();
|
ImageIcon img=itemAtlas.getAny();
|
||||||
|
|||||||
@@ -26,9 +26,8 @@ public class QuestEditor extends JComponent {
|
|||||||
JList list, Object value, int index,
|
JList list, Object value, int index,
|
||||||
boolean isSelected, boolean cellHasFocus) {
|
boolean isSelected, boolean cellHasFocus) {
|
||||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
if(!(value instanceof AdventureQuestData))
|
if(!(value instanceof AdventureQuestData quest))
|
||||||
return label;
|
return label;
|
||||||
AdventureQuestData quest=(AdventureQuestData) value;
|
|
||||||
// Get the renderer component from parent class
|
// Get the renderer component from parent class
|
||||||
|
|
||||||
label.setText(quest.name);
|
label.setText(quest.name);
|
||||||
|
|||||||
@@ -26,9 +26,8 @@ public class QuestStageEditor extends JComponent{
|
|||||||
JList list, Object value, int index,
|
JList list, Object value, int index,
|
||||||
boolean isSelected, boolean cellHasFocus) {
|
boolean isSelected, boolean cellHasFocus) {
|
||||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
if(!(value instanceof AdventureQuestStage))
|
if(!(value instanceof AdventureQuestStage stageData))
|
||||||
return label;
|
return label;
|
||||||
AdventureQuestStage stageData=(AdventureQuestStage) value;
|
|
||||||
label.setText(stageData.name);
|
label.setText(stageData.name);
|
||||||
//label.setIcon(new ImageIcon(Config.instance().getFilePath(stageData.sourcePath))); //Type icon eventually?
|
//label.setIcon(new ImageIcon(Config.instance().getFilePath(stageData.sourcePath))); //Type icon eventually?
|
||||||
return label;
|
return label;
|
||||||
|
|||||||
@@ -43,9 +43,8 @@ public class WorldEditor extends JComponent {
|
|||||||
JList list, Object value, int index,
|
JList list, Object value, int index,
|
||||||
boolean isSelected, boolean cellHasFocus) {
|
boolean isSelected, boolean cellHasFocus) {
|
||||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
if(!(value instanceof BiomeData))
|
if(!(value instanceof BiomeData biome))
|
||||||
return label;
|
return label;
|
||||||
BiomeData biome=(BiomeData) value;
|
|
||||||
// Get the renderer component from parent class
|
// Get the renderer component from parent class
|
||||||
|
|
||||||
label.setText(biome.name);
|
label.setText(biome.name);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-ai</artifactId>
|
<artifactId>forge-ai</artifactId>
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ public class AiAttackController {
|
|||||||
} // overloaded constructor to evaluate single specified attacker
|
} // overloaded constructor to evaluate single specified attacker
|
||||||
|
|
||||||
private void refreshCombatants(GameEntity defender) {
|
private void refreshCombatants(GameEntity defender) {
|
||||||
if (defender instanceof Card && ((Card) defender).isBattle()) {
|
if (defender instanceof Card card && card.isBattle()) {
|
||||||
this.oppList = getOpponentCreatures(((Card) defender).getProtectingPlayer());
|
this.oppList = getOpponentCreatures(card.getProtectingPlayer());
|
||||||
} else {
|
} else {
|
||||||
this.oppList = getOpponentCreatures(defendingOpponent);
|
this.oppList = getOpponentCreatures(defendingOpponent);
|
||||||
}
|
}
|
||||||
@@ -312,7 +312,8 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Poison opponent if unblocked
|
// Poison opponent if unblocked
|
||||||
if (defender instanceof Player && ComputerUtilCombat.poisonIfUnblocked(attacker, (Player) defender) > 0) {
|
if (defender instanceof Player player
|
||||||
|
&& ComputerUtilCombat.poisonIfUnblocked(attacker, player) > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,10 +850,9 @@ public class AiAttackController {
|
|||||||
// decided to attack another defender so related lists need to be updated
|
// decided to attack another defender so related lists need to be updated
|
||||||
// (though usually rather try to avoid this situation for performance reasons)
|
// (though usually rather try to avoid this situation for performance reasons)
|
||||||
if (defender != defendingOpponent) {
|
if (defender != defendingOpponent) {
|
||||||
if (defender instanceof Player) {
|
if (defender instanceof Player p) {
|
||||||
defendingOpponent = (Player) defender;
|
defendingOpponent = p;
|
||||||
} else if (defender instanceof Card) {
|
} else if (defender instanceof Card defCard) {
|
||||||
Card defCard = (Card) defender;
|
|
||||||
if (defCard.isBattle()) {
|
if (defCard.isBattle()) {
|
||||||
defendingOpponent = defCard.getProtectingPlayer();
|
defendingOpponent = defCard.getProtectingPlayer();
|
||||||
} else {
|
} else {
|
||||||
@@ -946,8 +946,8 @@ public class AiAttackController {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// or weakest player
|
// or weakest player
|
||||||
if (r1.getKey() instanceof Player && r2.getKey() instanceof Player) {
|
if (r1.getKey() instanceof Player p1 && r2.getKey() instanceof Player p2) {
|
||||||
return ((Player) r1.getKey()).getLife() - ((Player) r2.getKey()).getLife();
|
return p1.getLife() - p2.getLife();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r2.getValue() - r1.getValue();
|
return r2.getValue() - r1.getValue();
|
||||||
@@ -1314,7 +1314,7 @@ public class AiAttackController {
|
|||||||
attackersAssigned.add(attacker);
|
attackersAssigned.add(attacker);
|
||||||
|
|
||||||
// check if attackers are enough to finish the attacked planeswalker
|
// check if attackers are enough to finish the attacked planeswalker
|
||||||
if (i < left.size() - 1 && defender instanceof Card) {
|
if (i < left.size() - 1 && defender instanceof Card card) {
|
||||||
final int blockNum = this.blockers.size();
|
final int blockNum = this.blockers.size();
|
||||||
int attackNum = 0;
|
int attackNum = 0;
|
||||||
int damage = 0;
|
int damage = 0;
|
||||||
@@ -1328,7 +1328,7 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if enough damage: switch to next planeswalker
|
// if enough damage: switch to next planeswalker
|
||||||
if (damage >= ComputerUtilCombat.getDamageToKill((Card) defender, true)) {
|
if (damage >= ComputerUtilCombat.getDamageToKill(card, true)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1754,10 +1754,12 @@ public class AiAttackController {
|
|||||||
private boolean doRevengeOfRavensAttackLogic(final GameEntity defender, final Queue<Card> attackersLeft, int numForcedAttackers, int maxAttack) {
|
private boolean doRevengeOfRavensAttackLogic(final GameEntity defender, final Queue<Card> attackersLeft, int numForcedAttackers, int maxAttack) {
|
||||||
// TODO: detect Revenge of Ravens by the trigger instead of by name
|
// TODO: detect Revenge of Ravens by the trigger instead of by name
|
||||||
boolean revengeOfRavens = false;
|
boolean revengeOfRavens = false;
|
||||||
if (defender instanceof Player) {
|
if (defender instanceof Player player) {
|
||||||
revengeOfRavens = !CardLists.filter(((Player)defender).getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Revenge of Ravens")).isEmpty();
|
revengeOfRavens = !CardLists.filter(player.getCardsIn(ZoneType.Battlefield),
|
||||||
} else if (defender instanceof Card) {
|
CardPredicates.nameEquals("Revenge of Ravens")).isEmpty();
|
||||||
revengeOfRavens = !CardLists.filter(((Card)defender).getController().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Revenge of Ravens")).isEmpty();
|
} else if (defender instanceof Card card) {
|
||||||
|
revengeOfRavens = !CardLists.filter(card.getController().getCardsIn(ZoneType.Battlefield),
|
||||||
|
CardPredicates.nameEquals("Revenge of Ravens")).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!revengeOfRavens) {
|
if (!revengeOfRavens) {
|
||||||
|
|||||||
@@ -161,12 +161,12 @@ public class AiBlockController {
|
|||||||
// defend battles with fewer defense counters before battles with more defense counters,
|
// defend battles with fewer defense counters before battles with more defense counters,
|
||||||
// if planeswalker/battle will be too difficult to defend don't even bother
|
// if planeswalker/battle will be too difficult to defend don't even bother
|
||||||
for (GameEntity defender : defenders) {
|
for (GameEntity defender : defenders) {
|
||||||
if ((defender instanceof Card && ((Card) defender).getController().equals(ai))
|
if ((defender instanceof Card card1 && card1.getController().equals(ai))
|
||||||
|| (defender instanceof Card && ((Card) defender).isBattle() && ((Card) defender).getProtectingPlayer().equals(ai))) {
|
|| (defender instanceof Card card2 && card2.isBattle() && card2.getProtectingPlayer().equals(ai))) {
|
||||||
final CardCollection attackers = combat.getAttackersOf(defender);
|
final CardCollection ccAttackers = combat.getAttackersOf(defender);
|
||||||
// Begin with the attackers that pose the biggest threat
|
// Begin with the attackers that pose the biggest threat
|
||||||
CardLists.sortByPowerDesc(attackers);
|
CardLists.sortByPowerDesc(ccAttackers);
|
||||||
sortedAttackers.addAll(attackers);
|
sortedAttackers.addAll(ccAttackers);
|
||||||
} else if (defender instanceof Player && defender.equals(ai)) {
|
} else if (defender instanceof Player && defender.equals(ai)) {
|
||||||
firstAttacker = combat.getAttackersOf(defender);
|
firstAttacker = combat.getAttackersOf(defender);
|
||||||
CardLists.sortByPowerDesc(firstAttacker);
|
CardLists.sortByPowerDesc(firstAttacker);
|
||||||
@@ -872,9 +872,9 @@ public class AiBlockController {
|
|||||||
CardCollection threatenedPWs = new CardCollection();
|
CardCollection threatenedPWs = new CardCollection();
|
||||||
for (final Card attacker : attackers) {
|
for (final Card attacker : attackers) {
|
||||||
GameEntity def = combat.getDefenderByAttacker(attacker);
|
GameEntity def = combat.getDefenderByAttacker(attacker);
|
||||||
if (def instanceof Card) {
|
if (def instanceof Card card) {
|
||||||
if (!onlyIfLethal) {
|
if (!onlyIfLethal) {
|
||||||
threatenedPWs.add((Card) def);
|
threatenedPWs.add(card);
|
||||||
} else {
|
} else {
|
||||||
int damageToPW = 0;
|
int damageToPW = 0;
|
||||||
for (final Card pwatkr : combat.getAttackersOf(def)) {
|
for (final Card pwatkr : combat.getAttackersOf(def)) {
|
||||||
@@ -906,12 +906,12 @@ public class AiBlockController {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
GameEntity def = combat.getDefenderByAttacker(attacker);
|
GameEntity def = combat.getDefenderByAttacker(attacker);
|
||||||
if (def instanceof Card && threatenedPWs.contains(def)) {
|
if (def instanceof Card card && threatenedPWs.contains(def)) {
|
||||||
Card blockerDecided = null;
|
Card blockerDecided = null;
|
||||||
for (final Card blocker : chumpPWDefenders) {
|
for (final Card blocker : chumpPWDefenders) {
|
||||||
if (CombatUtil.canBlock(attacker, blocker, combat)) {
|
if (CombatUtil.canBlock(attacker, blocker, combat)) {
|
||||||
combat.addBlocker(attacker, blocker);
|
combat.addBlocker(attacker, blocker);
|
||||||
pwsWithChumpBlocks.add((Card) def);
|
pwsWithChumpBlocks.add(card);
|
||||||
chosenChumpBlockers.add(blocker);
|
chosenChumpBlockers.add(blocker);
|
||||||
blockerDecided = blocker;
|
blockerDecided = blocker;
|
||||||
blockersLeft.remove(blocker);
|
blockersLeft.remove(blocker);
|
||||||
@@ -1346,8 +1346,8 @@ public class AiBlockController {
|
|||||||
&& ai.getZone(ZoneType.Hand).contains(CardPredicates.CREATURES)
|
&& ai.getZone(ZoneType.Hand).contains(CardPredicates.CREATURES)
|
||||||
&& aiCreatureCount + maxCreatDiffWithRepl >= oppCreatureCount;
|
&& aiCreatureCount + maxCreatDiffWithRepl >= oppCreatureCount;
|
||||||
boolean wantToSavePlaneswalker = MyRandom.percentTrue(chanceToSavePW)
|
boolean wantToSavePlaneswalker = MyRandom.percentTrue(chanceToSavePW)
|
||||||
&& combat.getDefenderByAttacker(attacker) instanceof Card
|
&& combat.getDefenderByAttacker(attacker) instanceof Card card
|
||||||
&& ((Card) combat.getDefenderByAttacker(attacker)).isPlaneswalker();
|
&& card.isPlaneswalker();
|
||||||
boolean wantToTradeDownToSavePW = chanceToTradeDownToSaveWalker > 0;
|
boolean wantToTradeDownToSavePW = chanceToTradeDownToSaveWalker > 0;
|
||||||
|
|
||||||
return ((evalBlk <= evalAtk + 1) || (wantToSavePlaneswalker && wantToTradeDownToSavePW)) // "1" accounts for tapped.
|
return ((evalBlk <= evalAtk + 1) || (wantToSavePlaneswalker && wantToTradeDownToSavePW)) // "1" accounts for tapped.
|
||||||
|
|||||||
@@ -1408,9 +1408,7 @@ public class ComputerUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (final CostPart part : abCost.getCostParts()) {
|
for (final CostPart part : abCost.getCostParts()) {
|
||||||
if (part instanceof CostSacrifice) {
|
if (part instanceof CostSacrifice sac) {
|
||||||
final CostSacrifice sac = (CostSacrifice) part;
|
|
||||||
|
|
||||||
final String type = sac.getType();
|
final String type = sac.getType();
|
||||||
|
|
||||||
if (type.equals("CARDNAME")) {
|
if (type.equals("CARDNAME")) {
|
||||||
@@ -1776,9 +1774,7 @@ public class ComputerUtil {
|
|||||||
noRegen = true;
|
noRegen = true;
|
||||||
}
|
}
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
|
|
||||||
// indestructible
|
// indestructible
|
||||||
if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1842,9 +1838,7 @@ public class ComputerUtil {
|
|||||||
if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c, false)) {
|
if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c, false)) {
|
||||||
threatened.add(c);
|
threatened.add(c);
|
||||||
}
|
}
|
||||||
} else if (o instanceof Player) {
|
} else if (o instanceof Player p) {
|
||||||
final Player p = (Player) o;
|
|
||||||
|
|
||||||
if (source.hasKeyword(Keyword.INFECT)) {
|
if (source.hasKeyword(Keyword.INFECT)) {
|
||||||
if (p.canReceiveCounters(CounterEnumType.POISON) && ComputerUtilCombat.predictDamageTo(p, dmg, source, false) >= 10 - p.getPoisonCounters()) {
|
if (p.canReceiveCounters(CounterEnumType.POISON) && ComputerUtilCombat.predictDamageTo(p, dmg, source, false) >= 10 - p.getPoisonCounters()) {
|
||||||
threatened.add(p);
|
threatened.add(p);
|
||||||
@@ -1862,8 +1856,7 @@ public class ComputerUtil {
|
|||||||
|| saviourApi == null)) {
|
|| saviourApi == null)) {
|
||||||
final int dmg = -AbilityUtils.calculateAmount(source, topStack.getParam("NumDef"), topStack);
|
final int dmg = -AbilityUtils.calculateAmount(source, topStack.getParam("NumDef"), topStack);
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
final boolean canRemove = (c.getNetToughness() <= dmg)
|
final boolean canRemove = (c.getNetToughness() <= dmg)
|
||||||
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && dmg >= ComputerUtilCombat.getDamageToKill(c, false));
|
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && dmg >= ComputerUtilCombat.getDamageToKill(c, false));
|
||||||
if (!canRemove) {
|
if (!canRemove) {
|
||||||
@@ -1909,9 +1902,7 @@ public class ComputerUtil {
|
|||||||
|| saviourApi == ApiType.Protection || saviourApi == null
|
|| saviourApi == ApiType.Protection || saviourApi == null
|
||||||
|| saviorWithSubsApi == ApiType.Pump || saviorWithSubsApi == ApiType.PumpAll)) {
|
|| saviorWithSubsApi == ApiType.Pump || saviorWithSubsApi == ApiType.PumpAll)) {
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
// indestructible
|
|
||||||
if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1960,8 +1951,7 @@ public class ComputerUtil {
|
|||||||
&& topStack.hasParam("Destination")
|
&& topStack.hasParam("Destination")
|
||||||
&& topStack.getParam("Destination").equals("Exile")) {
|
&& topStack.getParam("Destination").equals("Exile")) {
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
// give Shroud to targeted creatures
|
// give Shroud to targeted creatures
|
||||||
if ((saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) && (!topStack.usesTargeting() || !grantShroud)) {
|
if ((saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) && (!topStack.usesTargeting() || !grantShroud)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1988,8 +1978,7 @@ public class ComputerUtil {
|
|||||||
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|
||||||
|| saviourApi == ApiType.Protection || saviourApi == null)) {
|
|| saviourApi == ApiType.Protection || saviourApi == null)) {
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
// give Shroud to targeted creatures
|
// give Shroud to targeted creatures
|
||||||
if ((saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) && (!topStack.usesTargeting() || !grantShroud)) {
|
if ((saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) && (!topStack.usesTargeting() || !grantShroud)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -2011,8 +2000,7 @@ public class ComputerUtil {
|
|||||||
boolean enableCurseAuraRemoval = aic != null ? aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE) : false;
|
boolean enableCurseAuraRemoval = aic != null ? aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE) : false;
|
||||||
if (enableCurseAuraRemoval) {
|
if (enableCurseAuraRemoval) {
|
||||||
for (final Object o : objects) {
|
for (final Object o : objects) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
// give Shroud to targeted creatures
|
// give Shroud to targeted creatures
|
||||||
if ((saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) && (!topStack.usesTargeting() || !grantShroud)) {
|
if ((saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) && (!topStack.usesTargeting() || !grantShroud)) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ public class ComputerUtilCombat {
|
|||||||
public static int damageIfUnblocked(final Card attacker, final GameEntity attacked, final Combat combat, boolean withoutAbilities) {
|
public static int damageIfUnblocked(final Card attacker, final GameEntity attacked, final Combat combat, boolean withoutAbilities) {
|
||||||
int damage = attacker.getNetCombatDamage();
|
int damage = attacker.getNetCombatDamage();
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
if (attacked instanceof Player && !((Player) attacked).canLoseLife()) {
|
if (attacked instanceof Player player && !player.canLoseLife()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2539,20 +2539,20 @@ public class ComputerUtilCombat {
|
|||||||
if (combat != null) {
|
if (combat != null) {
|
||||||
GameEntity def = combat.getDefenderByAttacker(sa.getHostCard());
|
GameEntity def = combat.getDefenderByAttacker(sa.getHostCard());
|
||||||
// 1. If the card that spawned the attacker was sent at a card, attack the same. Consider improving.
|
// 1. If the card that spawned the attacker was sent at a card, attack the same. Consider improving.
|
||||||
if (def instanceof Card && Iterables.contains(defenders, def)) {
|
if (def instanceof Card card && Iterables.contains(defenders, def)) {
|
||||||
if (((Card) def).isPlaneswalker()) {
|
if (card.isPlaneswalker()) {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
if (((Card) def).isBattle()) {
|
if (card.isBattle()) {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2. Otherwise, go through the list of options one by one, choose the first one that can't be blocked profitably.
|
// 2. Otherwise, go through the list of options one by one, choose the first one that can't be blocked profitably.
|
||||||
for (GameEntity p : defenders) {
|
for (GameEntity p : defenders) {
|
||||||
if (p instanceof Player && !ComputerUtilCard.canBeBlockedProfitably((Player)p, attacker, true)) {
|
if (p instanceof Player p1 && !ComputerUtilCard.canBeBlockedProfitably(p1, attacker, true)) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
if (p instanceof Card && !ComputerUtilCard.canBeBlockedProfitably(((Card)p).getController(), attacker, true)) {
|
if (p instanceof Card card && !ComputerUtilCard.canBeBlockedProfitably(card.getController(), attacker, true)) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ public class ComputerUtilCost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostPutCounter) {
|
if (part instanceof CostPutCounter addCounter) {
|
||||||
final CostPutCounter addCounter = (CostPutCounter) part;
|
|
||||||
final CounterType type = addCounter.getCounter();
|
final CounterType type = addCounter.getCounter();
|
||||||
|
|
||||||
if (type.is(CounterEnumType.M1M1)) {
|
if (type.is(CounterEnumType.M1M1)) {
|
||||||
@@ -77,9 +76,7 @@ public class ComputerUtilCost {
|
|||||||
}
|
}
|
||||||
final AiCostDecision decision = new AiCostDecision(sa.getActivatingPlayer(), sa, false);
|
final AiCostDecision decision = new AiCostDecision(sa.getActivatingPlayer(), sa, false);
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostRemoveCounter) {
|
if (part instanceof CostRemoveCounter remCounter) {
|
||||||
final CostRemoveCounter remCounter = (CostRemoveCounter) part;
|
|
||||||
|
|
||||||
final CounterType type = remCounter.counter;
|
final CounterType type = remCounter.counter;
|
||||||
if (!part.payCostFromSource()) {
|
if (!part.payCostFromSource()) {
|
||||||
if (type.is(CounterEnumType.P1P1)) {
|
if (type.is(CounterEnumType.P1P1)) {
|
||||||
@@ -106,9 +103,7 @@ public class ComputerUtilCost {
|
|||||||
&& !source.hasKeyword(Keyword.UNDYING)) {
|
&& !source.hasKeyword(Keyword.UNDYING)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (part instanceof CostRemoveAnyCounter) {
|
} else if (part instanceof CostRemoveAnyCounter remCounter) {
|
||||||
final CostRemoveAnyCounter remCounter = (CostRemoveAnyCounter) part;
|
|
||||||
|
|
||||||
PaymentDecision pay = decision.visit(remCounter);
|
PaymentDecision pay = decision.visit(remCounter);
|
||||||
return pay != null;
|
return pay != null;
|
||||||
}
|
}
|
||||||
@@ -133,9 +128,7 @@ public class ComputerUtilCost {
|
|||||||
CardCollection hand = new CardCollection(ai.getCardsIn(ZoneType.Hand));
|
CardCollection hand = new CardCollection(ai.getCardsIn(ZoneType.Hand));
|
||||||
|
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostDiscard) {
|
if (part instanceof CostDiscard disc) {
|
||||||
final CostDiscard disc = (CostDiscard) part;
|
|
||||||
|
|
||||||
final String type = disc.getType();
|
final String type = disc.getType();
|
||||||
final CardCollection typeList;
|
final CardCollection typeList;
|
||||||
int num;
|
int num;
|
||||||
@@ -187,8 +180,7 @@ public class ComputerUtilCost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostDamage) {
|
if (part instanceof CostDamage pay) {
|
||||||
final CostDamage pay = (CostDamage) part;
|
|
||||||
int realDamage = ComputerUtilCombat.predictDamageTo(ai, pay.getAbilityAmount(sa), source, false);
|
int realDamage = ComputerUtilCombat.predictDamageTo(ai, pay.getAbilityAmount(sa), source, false);
|
||||||
if (ai.getLife() - realDamage < remainingLife
|
if (ai.getLife() - realDamage < remainingLife
|
||||||
&& realDamage > 0 && !ai.cantLoseForZeroOrLessLife()
|
&& realDamage > 0 && !ai.cantLoseForZeroOrLessLife()
|
||||||
@@ -220,9 +212,7 @@ public class ComputerUtilCost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostPayLife) {
|
if (part instanceof CostPayLife payLife) {
|
||||||
final CostPayLife payLife = (CostPayLife) part;
|
|
||||||
|
|
||||||
int amount = payLife.getAbilityAmount(sourceAbility);
|
int amount = payLife.getAbilityAmount(sourceAbility);
|
||||||
|
|
||||||
// check if there's override for the remainingLife threshold
|
// check if there's override for the remainingLife threshold
|
||||||
@@ -296,8 +286,7 @@ public class ComputerUtilCost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostSacrifice) {
|
if (part instanceof CostSacrifice sac) {
|
||||||
final CostSacrifice sac = (CostSacrifice) part;
|
|
||||||
final int amount = AbilityUtils.calculateAmount(source, sac.getAmount(), sourceAbility);
|
final int amount = AbilityUtils.calculateAmount(source, sac.getAmount(), sourceAbility);
|
||||||
|
|
||||||
if (sac.payCostFromSource() && source.isCreature()) {
|
if (sac.payCostFromSource() && source.isCreature()) {
|
||||||
@@ -346,12 +335,11 @@ public class ComputerUtilCost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (final CostPart part : cost.getCostParts()) {
|
for (final CostPart part : cost.getCostParts()) {
|
||||||
if (part instanceof CostSacrifice) {
|
if (part instanceof CostSacrifice sac) {
|
||||||
if (suppressRecursiveSacCostCheck) {
|
if (suppressRecursiveSacCostCheck) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CostSacrifice sac = (CostSacrifice) part;
|
|
||||||
final int amount = AbilityUtils.calculateAmount(source, sac.getAmount(), sourceAbility);
|
final int amount = AbilityUtils.calculateAmount(source, sac.getAmount(), sourceAbility);
|
||||||
|
|
||||||
String type = sac.getType();
|
String type = sac.getType();
|
||||||
|
|||||||
@@ -1326,7 +1326,9 @@ public class ComputerUtilMana {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CostAdjustment.adjust(manaCost, sa, null, test);
|
if (!effect) {
|
||||||
|
CostAdjustment.adjust(manaCost, sa, null, test);
|
||||||
|
}
|
||||||
|
|
||||||
if ("NumTimes".equals(sa.getParam("Announce"))) { // e.g. the Adversary cycle
|
if ("NumTimes".equals(sa.getParam("Announce"))) { // e.g. the Adversary cycle
|
||||||
ManaCost mkCost = sa.getPayCosts().getTotalMana();
|
ManaCost mkCost = sa.getPayCosts().getTotalMana();
|
||||||
|
|||||||
@@ -1265,8 +1265,7 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
public boolean playSaFromPlayEffect(SpellAbility tgtSA) {
|
public boolean playSaFromPlayEffect(SpellAbility tgtSA) {
|
||||||
boolean optional = !tgtSA.getPayCosts().isMandatory();
|
boolean optional = !tgtSA.getPayCosts().isMandatory();
|
||||||
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
|
boolean noManaCost = tgtSA.hasParam("WithoutManaCost");
|
||||||
if (tgtSA instanceof Spell) { // Isn't it ALWAYS a spell?
|
if (tgtSA instanceof Spell spell) { // Isn't it ALWAYS a spell?
|
||||||
Spell spell = (Spell) tgtSA;
|
|
||||||
// TODO if mandatory AI is only forced to use mana when it's already in the pool
|
// TODO if mandatory AI is only forced to use mana when it's already in the pool
|
||||||
if (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional) {
|
if (brains.canPlayFromEffectAI(spell, !optional, noManaCost) == AiPlayDecision.WillPlay || !optional) {
|
||||||
return ComputerUtil.playStack(tgtSA, player, getGame());
|
return ComputerUtil.playStack(tgtSA, player, getGame());
|
||||||
|
|||||||
@@ -342,9 +342,9 @@ public abstract class SpellAbilityAi {
|
|||||||
for (T ent : options) {
|
for (T ent : options) {
|
||||||
if (ent instanceof Player) {
|
if (ent instanceof Player) {
|
||||||
hasPlayer = true;
|
hasPlayer = true;
|
||||||
} else if (ent instanceof Card) {
|
} else if (ent instanceof Card card) {
|
||||||
hasCard = true;
|
hasCard = true;
|
||||||
if (((Card)ent).isPlaneswalker() || ((Card)ent).isBattle()) {
|
if (card.isPlaneswalker() || card.isBattle()) {
|
||||||
hasAttackableCard = true;
|
hasAttackableCard = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import forge.game.player.Player;
|
|||||||
import forge.game.player.PlayerActionConfirmMode;
|
import forge.game.player.PlayerActionConfirmMode;
|
||||||
import forge.game.spellability.AbilitySub;
|
import forge.game.spellability.AbilitySub;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.spellability.SpellAbilityStackInstance;
|
|
||||||
import forge.game.spellability.TargetRestrictions;
|
import forge.game.spellability.TargetRestrictions;
|
||||||
import forge.game.staticability.StaticAbilityMustTarget;
|
import forge.game.staticability.StaticAbilityMustTarget;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
@@ -138,8 +137,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
if (aiLogic != null) {
|
if (aiLogic != null) {
|
||||||
if (aiLogic.equals("Always")) {
|
if (aiLogic.equals("Always")) {
|
||||||
return true;
|
return true;
|
||||||
} else if (aiLogic.startsWith("ExileSpell")) {
|
|
||||||
return doExileSpellLogic(aiPlayer, sa);
|
|
||||||
} else if (aiLogic.startsWith("SacAndUpgrade")) { // Birthing Pod, Natural Order, etc.
|
} else if (aiLogic.startsWith("SacAndUpgrade")) { // Birthing Pod, Natural Order, etc.
|
||||||
return doSacAndUpgradeLogic(aiPlayer, sa);
|
return doSacAndUpgradeLogic(aiPlayer, sa);
|
||||||
} else if (aiLogic.startsWith("SacAndRetFromGrave")) { // Recurring Nightmare, etc.
|
} else if (aiLogic.startsWith("SacAndRetFromGrave")) { // Recurring Nightmare, etc.
|
||||||
@@ -878,6 +875,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
origin.addAll(ZoneType.listValueOf(sa.getParam("TgtZone")));
|
origin.addAll(ZoneType.listValueOf(sa.getParam("TgtZone")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (origin.contains(ZoneType.Stack) && doExileSpellLogic(ai, sa, mandatory)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
|
|
||||||
@@ -902,7 +903,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(origin), sa);
|
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(origin), sa);
|
||||||
|
|
||||||
// Filter AI-specific targets if provided
|
|
||||||
list = ComputerUtil.filterAITgts(sa, ai, list, true);
|
list = ComputerUtil.filterAITgts(sa, ai, list, true);
|
||||||
if (sa.hasParam("AITgtsOnlyBetterThanSelf")) {
|
if (sa.hasParam("AITgtsOnlyBetterThanSelf")) {
|
||||||
list = CardLists.filter(list, card -> ComputerUtilCard.evaluateCreature(card) > ComputerUtilCard.evaluateCreature(source) + 30);
|
list = CardLists.filter(list, card -> ComputerUtilCard.evaluateCreature(card) > ComputerUtilCard.evaluateCreature(source) + 30);
|
||||||
@@ -2061,31 +2061,24 @@ public class ChangeZoneAi extends SpellAbilityAi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doExileSpellLogic(final Player aiPlayer, final SpellAbility sa) {
|
private static boolean doExileSpellLogic(final Player ai, final SpellAbility sa, final boolean mandatory) {
|
||||||
String aiLogic = sa.getParamOrDefault("AILogic", "");
|
List<ApiType> dangerousApi = null;
|
||||||
SpellAbilityStackInstance top = aiPlayer.getGame().getStack().peek();
|
CardCollection spells = new CardCollection(ai.getGame().getStackZone().getCards());
|
||||||
List<ApiType> dangerousApi = Arrays.asList(ApiType.DealDamage, ApiType.DamageAll, ApiType.Destroy, ApiType.DestroyAll, ApiType.Sacrifice, ApiType.SacrificeAll);
|
Collections.reverse(spells);
|
||||||
int manaCost = 0;
|
if (!mandatory && !spells.isEmpty()) {
|
||||||
int minCost = 0;
|
spells = spells.subList(0, 1);
|
||||||
|
spells = ComputerUtil.filterAITgts(sa, ai, spells, true);
|
||||||
if (aiLogic.contains(".")) {
|
dangerousApi = Arrays.asList(ApiType.DealDamage, ApiType.DamageAll, ApiType.Destroy, ApiType.DestroyAll, ApiType.Sacrifice, ApiType.SacrificeAll);
|
||||||
minCost = Integer.parseInt(aiLogic.substring(aiLogic.indexOf(".") + 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top != null) {
|
for (Card c : spells) {
|
||||||
SpellAbility topSA = top.getSpellAbility();
|
SpellAbility topSA = ai.getGame().getStack().getSpellMatchingHost(c);
|
||||||
if (topSA != null) {
|
if (topSA != null && (dangerousApi == null ||
|
||||||
if (topSA.getPayCosts().hasManaCost()) {
|
(dangerousApi.contains(topSA.getApi()) && topSA.getActivatingPlayer().isOpponentOf(ai)))
|
||||||
manaCost = topSA.getPayCosts().getTotalMana().getCMC();
|
&& sa.canTarget(topSA)) {
|
||||||
}
|
sa.resetTargets();
|
||||||
|
sa.getTargets().add(topSA);
|
||||||
if ((manaCost >= minCost || dangerousApi.contains(topSA.getApi()))
|
return sa.isTargetNumberValid();
|
||||||
&& topSA.getActivatingPlayer().isOpponentOf(aiPlayer)
|
|
||||||
&& sa.canTargetSpellAbility(topSA)) {
|
|
||||||
sa.resetTargets();
|
|
||||||
sa.getTargets().add(topSA);
|
|
||||||
return sa.isTargetNumberValid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-core</artifactId>
|
<artifactId>forge-core</artifactId>
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ final class CardFace implements ICardFace, Cloneable {
|
|||||||
else variant.replacements.addAll(0, this.replacements);
|
else variant.replacements.addAll(0, this.replacements);
|
||||||
|
|
||||||
if(variant.variables == null) variant.variables = this.variables;
|
if(variant.variables == null) variant.variables = this.variables;
|
||||||
else variant.variables.putAll(this.variables);
|
else this.variables.forEach((k, v) -> variant.variables.putIfAbsent(k, v));
|
||||||
|
|
||||||
if(variant.nonAbilityText == null) variant.nonAbilityText = this.nonAbilityText;
|
if(variant.nonAbilityText == null) variant.nonAbilityText = this.nonAbilityText;
|
||||||
if(variant.draftActions == null) variant.draftActions = this.draftActions;
|
if(variant.draftActions == null) variant.draftActions = this.draftActions;
|
||||||
|
|||||||
@@ -645,9 +645,8 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
|||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object o) {
|
public boolean equals(final Object o) {
|
||||||
if (o instanceof Deck) {
|
if (o instanceof DeckBase deckBase) {
|
||||||
final DeckBase dbase = (DeckBase) o;
|
boolean deckBaseEquals = super.equals(deckBase);
|
||||||
boolean deckBaseEquals = super.equals(dbase);
|
|
||||||
if (!deckBaseEquals)
|
if (!deckBaseEquals)
|
||||||
return false;
|
return false;
|
||||||
// ok so far we made sure they do have the same name. Now onto comparing parts
|
// ok so far we made sure they do have the same name. Now onto comparing parts
|
||||||
|
|||||||
@@ -472,7 +472,8 @@ public class DeckRecognizer {
|
|||||||
"side", "sideboard", "sb",
|
"side", "sideboard", "sb",
|
||||||
"main", "card", "mainboard",
|
"main", "card", "mainboard",
|
||||||
"avatar", "commander", "schemes",
|
"avatar", "commander", "schemes",
|
||||||
"conspiracy", "planes", "deck", "dungeon"};
|
"conspiracy", "planes", "deck", "dungeon",
|
||||||
|
"attractions", "contraptions"};
|
||||||
|
|
||||||
private static CharSequence[] allCardTypes(){
|
private static CharSequence[] allCardTypes(){
|
||||||
List<String> cardTypesList = new ArrayList<>();
|
List<String> cardTypesList = new ArrayList<>();
|
||||||
@@ -670,6 +671,9 @@ public class DeckRecognizer {
|
|||||||
// ok so the card has been found - let's see if there's any restriction on the set
|
// ok so the card has been found - let's see if there's any restriction on the set
|
||||||
return checkAndSetCardToken(pc, edition, cardCount, deckSecFromCardLine,
|
return checkAndSetCardToken(pc, edition, cardCount, deckSecFromCardLine,
|
||||||
currentDeckSection, true);
|
currentDeckSection, true);
|
||||||
|
// On the off chance we accidentally interpreted part of the card's name as a set code, e.g. "Tyrranax Rex"
|
||||||
|
if (data.isMTGCard(cardName + " " + setCode))
|
||||||
|
continue;
|
||||||
// UNKNOWN card as in the Counterspell|FEM case
|
// UNKNOWN card as in the Counterspell|FEM case
|
||||||
return Token.UnknownCard(cardName, setCode, cardCount);
|
return Token.UnknownCard(cardName, setCode, cardCount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ public class BoosterGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static List<PaperCard> getBoosterPack(SealedTemplate template) {
|
public static List<PaperCard> getBoosterPack(SealedTemplate template) {
|
||||||
if (template instanceof SealedTemplateWithSlots) {
|
if (template instanceof SealedTemplateWithSlots slots) {
|
||||||
return BoosterGenerator.getBoosterPack((SealedTemplateWithSlots) template);
|
return BoosterGenerator.getBoosterPack(slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PaperCard> result = new ArrayList<>();
|
List<PaperCard> result = new ArrayList<>();
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ public class ItemPool<T extends InventoryItem> implements Iterable<Entry<T, Inte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
return (obj instanceof ItemPool) &&
|
return (obj instanceof ItemPool ip) &&
|
||||||
(this.items.equals(((ItemPool)obj).items));
|
(this.items.equals(ip.items));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-game</artifactId>
|
<artifactId>forge-game</artifactId>
|
||||||
|
|||||||
@@ -337,9 +337,6 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView,
|
|||||||
if (params.containsKey("Blessing")) {
|
if (params.containsKey("Blessing")) {
|
||||||
if ("True".equalsIgnoreCase(params.get("Blessing")) != hostController.hasBlessing()) return false;
|
if ("True".equalsIgnoreCase(params.get("Blessing")) != hostController.hasBlessing()) return false;
|
||||||
}
|
}
|
||||||
if (params.containsKey("MaxSpeed")) {
|
|
||||||
if ("True".equalsIgnoreCase(params.get("MaxSpeed")) != hostController.maxSpeed()) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.containsKey("DayTime")) {
|
if (params.containsKey("DayTime")) {
|
||||||
if ("Day".equalsIgnoreCase(params.get("DayTime"))) {
|
if ("Day".equalsIgnoreCase(params.get("DayTime"))) {
|
||||||
|
|||||||
@@ -1185,6 +1185,12 @@ public class Game {
|
|||||||
for (Player player : getRegisteredPlayers()) {
|
for (Player player : getRegisteredPlayers()) {
|
||||||
player.onCleanupPhase();
|
player.onCleanupPhase();
|
||||||
}
|
}
|
||||||
|
for (final Card c : getCardsIncludePhasingIn(ZoneType.Battlefield)) {
|
||||||
|
c.onCleanupPhase(getPhaseHandler().getPlayerTurn());
|
||||||
|
}
|
||||||
|
for (final Card card : getCardsInGame()) {
|
||||||
|
card.resetActivationsPerTurn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addCounterAddedThisTurn(Player putter, CounterType cType, Card card, Integer value) {
|
public void addCounterAddedThisTurn(Player putter, CounterType cType, Card card, Integer value) {
|
||||||
|
|||||||
@@ -82,12 +82,6 @@ public class GameAction {
|
|||||||
game = game0;
|
game = game0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void resetActivationsPerTurn() {
|
|
||||||
for (final Card card : game.getCardsInGame()) {
|
|
||||||
card.resetActivationsPerTurn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause) {
|
public Card changeZone(final Zone zoneFrom, Zone zoneTo, final Card c, Integer position, SpellAbility cause) {
|
||||||
return changeZone(zoneFrom, zoneTo, c, position, cause, null);
|
return changeZone(zoneFrom, zoneTo, c, position, cause, null);
|
||||||
}
|
}
|
||||||
@@ -107,6 +101,8 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dev mode
|
||||||
if (zoneFrom == null && !c.isToken()) {
|
if (zoneFrom == null && !c.isToken()) {
|
||||||
zoneTo.add(c, position, CardCopyService.getLKICopy(c));
|
zoneTo.add(c, position, CardCopyService.getLKICopy(c));
|
||||||
checkStaticAbilities();
|
checkStaticAbilities();
|
||||||
@@ -314,37 +310,34 @@ public class GameAction {
|
|||||||
c.getOwner().setCommanderReplacementSuppressed(true);
|
c.getOwner().setCommanderReplacementSuppressed(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// in addition to actual tokens, cards "made" by digital-only mechanics
|
|
||||||
// are also added to inbound tokens so their etb replacements will work
|
|
||||||
if (zoneFrom == null || zoneFrom.is(ZoneType.None)) {
|
|
||||||
copied.getOwner().addInboundToken(copied);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(copied);
|
Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(copied);
|
||||||
repParams.put(AbilityKey.CardLKI, lastKnownInfo);
|
repParams.put(AbilityKey.CardLKI, lastKnownInfo);
|
||||||
repParams.put(AbilityKey.Cause, cause);
|
repParams.put(AbilityKey.Cause, cause);
|
||||||
repParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType() : null);
|
repParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType() : null);
|
||||||
repParams.put(AbilityKey.Destination, zoneTo.getZoneType());
|
repParams.put(AbilityKey.Destination, zoneTo.getZoneType());
|
||||||
|
|
||||||
if (toBattlefield) {
|
if (toBattlefield) {
|
||||||
repParams.put(AbilityKey.EffectOnly, true);
|
repParams.put(AbilityKey.EffectOnly, true);
|
||||||
repParams.put(AbilityKey.CounterTable, table);
|
repParams.put(AbilityKey.CounterTable, table);
|
||||||
repParams.put(AbilityKey.CounterMap, table.column(copied));
|
repParams.put(AbilityKey.CounterMap, table.column(copied));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
repParams.putAll(params);
|
repParams.putAll(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in addition to actual tokens, cards "made" by digital-only mechanics
|
||||||
|
// are also added to inbound tokens so their etb replacements will work
|
||||||
|
if (zoneFrom == null || zoneFrom.is(ZoneType.None)) {
|
||||||
|
copied.getOwner().addInboundToken(copied);
|
||||||
|
}
|
||||||
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
|
ReplacementResult repres = game.getReplacementHandler().run(ReplacementType.Moved, repParams);
|
||||||
|
copied.getOwner().removeInboundToken(copied);
|
||||||
|
|
||||||
if (repres != ReplacementResult.NotReplaced && repres != ReplacementResult.Updated) {
|
if (repres != ReplacementResult.NotReplaced && repres != ReplacementResult.Updated) {
|
||||||
// reset failed manifested Cards back to original
|
// reset failed manifested Cards back to original
|
||||||
if ((c.isManifested() || c.isCloaked()) && !c.isInPlay()) {
|
if ((c.isManifested() || c.isCloaked()) && !c.isInPlay()) {
|
||||||
c.forceTurnFaceUp();
|
c.forceTurnFaceUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
copied.getOwner().removeInboundToken(copied);
|
|
||||||
|
|
||||||
if (repres == ReplacementResult.Prevented) {
|
if (repres == ReplacementResult.Prevented) {
|
||||||
c.clearControllers();
|
c.clearControllers();
|
||||||
cleanStaticEffect(staticEff, copied);
|
cleanStaticEffect(staticEff, copied);
|
||||||
@@ -359,10 +352,6 @@ public class GameAction {
|
|||||||
if (c.isInZone(ZoneType.Stack) && !zoneTo.is(ZoneType.Graveyard)) {
|
if (c.isInZone(ZoneType.Stack) && !zoneTo.is(ZoneType.Graveyard)) {
|
||||||
return moveToGraveyard(c, cause, params);
|
return moveToGraveyard(c, cause, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
copied.clearDevoured();
|
|
||||||
copied.clearDelved();
|
|
||||||
copied.clearExploited();
|
|
||||||
} else if (toBattlefield && !c.isInPlay()) {
|
} else if (toBattlefield && !c.isInPlay()) {
|
||||||
// was replaced with another Zone Change
|
// was replaced with another Zone Change
|
||||||
if (c.removeChangedState()) {
|
if (c.removeChangedState()) {
|
||||||
@@ -379,8 +368,6 @@ public class GameAction {
|
|||||||
copied.setGameTimestamp(game.getNextTimestamp());
|
copied.setGameTimestamp(game.getNextTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
copied.getOwner().removeInboundToken(copied);
|
|
||||||
|
|
||||||
// Aura entering as Copy from stack
|
// Aura entering as Copy from stack
|
||||||
// without targets it is sent to graveyard
|
// without targets it is sent to graveyard
|
||||||
if (copied.isAura() && !copied.isAttachedToEntity() && toBattlefield) {
|
if (copied.isAura() && !copied.isAttachedToEntity() && toBattlefield) {
|
||||||
@@ -432,10 +419,6 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suppress) {
|
|
||||||
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zoneFrom != null) {
|
if (zoneFrom != null) {
|
||||||
if (fromBattlefield && game.getCombat() != null) {
|
if (fromBattlefield && game.getCombat() != null) {
|
||||||
if (!toBattlefield) {
|
if (!toBattlefield) {
|
||||||
@@ -549,29 +532,25 @@ public class GameAction {
|
|||||||
// order here is important so it doesn't unattach cards that might have returned from UntilHostLeavesPlay
|
// order here is important so it doesn't unattach cards that might have returned from UntilHostLeavesPlay
|
||||||
unattachCardLeavingBattlefield(copied, c);
|
unattachCardLeavingBattlefield(copied, c);
|
||||||
c.runLeavesPlayCommands();
|
c.runLeavesPlayCommands();
|
||||||
|
|
||||||
|
if (copied.isTapped()) {
|
||||||
|
copied.setTapped(false); //untap card after it leaves the battlefield if needed
|
||||||
|
game.fireEvent(new GameEventCardTapped(c, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (fromGraveyard) {
|
if (fromGraveyard) {
|
||||||
game.addLeftGraveyardThisTurn(lastKnownInfo);
|
game.addLeftGraveyardThisTurn(lastKnownInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// do ETB counters after zone add
|
|
||||||
if (!suppress && toBattlefield && !table.isEmpty()) {
|
|
||||||
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c.hasChosenColorSpire()) {
|
if (c.hasChosenColorSpire()) {
|
||||||
copied.setChosenColorID(ImmutableSet.copyOf(c.getChosenColorID()));
|
copied.setChosenColorID(ImmutableSet.copyOf(c.getChosenColorID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
copied.updateStateForView();
|
copied.updateStateForView();
|
||||||
|
|
||||||
if (fromBattlefield) {
|
// needed for counters + ascend
|
||||||
copied.setDamage(0); //clear damage after a card leaves the battlefield
|
if (!suppress && toBattlefield) {
|
||||||
copied.setHasBeenDealtDeathtouchDamage(false);
|
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
||||||
if (copied.isTapped()) {
|
|
||||||
copied.setTapped(false); //untap card after it leaves the battlefield if needed
|
|
||||||
game.fireEvent(new GameEventCardTapped(c, false));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!table.isEmpty()) {
|
if (!table.isEmpty()) {
|
||||||
@@ -579,12 +558,12 @@ public class GameAction {
|
|||||||
game.getTriggerHandler().suppressMode(TriggerType.Always);
|
game.getTriggerHandler().suppressMode(TriggerType.Always);
|
||||||
// Need to apply any static effects to produce correct triggers
|
// Need to apply any static effects to produce correct triggers
|
||||||
checkStaticAbilities();
|
checkStaticAbilities();
|
||||||
|
// do ETB counters after zone add
|
||||||
|
table.replaceCounterEffect(game, null, true, true, params);
|
||||||
|
game.getTriggerHandler().clearSuppression(TriggerType.Always);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.replaceCounterEffect(game, null, true, true, params);
|
|
||||||
|
|
||||||
// update static abilities after etb counters have been placed
|
// update static abilities after etb counters have been placed
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.Always);
|
|
||||||
checkStaticAbilities();
|
checkStaticAbilities();
|
||||||
|
|
||||||
// 400.7g try adding keyword back into card if it doesn't already have it
|
// 400.7g try adding keyword back into card if it doesn't already have it
|
||||||
@@ -607,25 +586,26 @@ public class GameAction {
|
|||||||
c.cleanupExiledWith();
|
c.cleanupExiledWith();
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getTriggerHandler().clearActiveTriggers(copied, null);
|
|
||||||
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
|
||||||
|
|
||||||
// play the change zone sound
|
// play the change zone sound
|
||||||
game.fireEvent(new GameEventCardChangeZone(c, zoneFrom, zoneTo));
|
game.fireEvent(new GameEventCardChangeZone(c, zoneFrom, zoneTo));
|
||||||
|
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(copied);
|
game.getTriggerHandler().clearActiveTriggers(copied, null);
|
||||||
runParams.put(AbilityKey.CardLKI, lastKnownInfo);
|
game.getTriggerHandler().registerActiveTrigger(copied, false);
|
||||||
runParams.put(AbilityKey.Cause, cause);
|
|
||||||
runParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType().name() : null);
|
|
||||||
runParams.put(AbilityKey.Destination, zoneTo.getZoneType().name());
|
|
||||||
runParams.put(AbilityKey.IndividualCostPaymentInstance, game.costPaymentStack.peek());
|
|
||||||
runParams.put(AbilityKey.MergedCards, mergedCards);
|
|
||||||
|
|
||||||
if (params != null) {
|
if (!suppress) {
|
||||||
runParams.putAll(params);
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromCard(copied);
|
||||||
|
runParams.put(AbilityKey.CardLKI, lastKnownInfo);
|
||||||
|
runParams.put(AbilityKey.Cause, cause);
|
||||||
|
runParams.put(AbilityKey.Origin, zoneFrom != null ? zoneFrom.getZoneType().name() : null);
|
||||||
|
runParams.put(AbilityKey.Destination, zoneTo.getZoneType().name());
|
||||||
|
runParams.put(AbilityKey.IndividualCostPaymentInstance, game.costPaymentStack.peek());
|
||||||
|
runParams.put(AbilityKey.MergedCards, mergedCards);
|
||||||
|
if (params != null) {
|
||||||
|
runParams.putAll(params);
|
||||||
|
}
|
||||||
|
game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesZone, runParams, true);
|
|
||||||
if (fromBattlefield && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) {
|
if (fromBattlefield && !zoneFrom.getPlayer().equals(zoneTo.getPlayer())) {
|
||||||
final Map<AbilityKey, Object> runParams2 = AbilityKey.mapFromCard(lastKnownInfo);
|
final Map<AbilityKey, Object> runParams2 = AbilityKey.mapFromCard(lastKnownInfo);
|
||||||
runParams2.put(AbilityKey.OriginalController, zoneFrom.getPlayer());
|
runParams2.put(AbilityKey.OriginalController, zoneFrom.getPlayer());
|
||||||
@@ -635,31 +615,18 @@ public class GameAction {
|
|||||||
game.getTriggerHandler().runTrigger(TriggerType.ChangesController, runParams2, false);
|
game.getTriggerHandler().runTrigger(TriggerType.ChangesController, runParams2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suppress) {
|
|
||||||
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zoneFrom == null) {
|
if (zoneFrom == null) {
|
||||||
return copied;
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c.isRealToken() && !toBattlefield) {
|
// CR 708.9 reveal face-down card leaving
|
||||||
copied.clearDevoured();
|
if (wasFacedown && (fromBattlefield || (zoneFrom.is(ZoneType.Stack) && !toBattlefield))) {
|
||||||
copied.clearDelved();
|
|
||||||
copied.clearExploited();
|
|
||||||
}
|
|
||||||
|
|
||||||
// rule 504.6: reveal a face-down card leaving the stack
|
|
||||||
if (zoneFrom != null && zoneTo != null && zoneFrom.is(ZoneType.Stack) && !zoneTo.is(ZoneType.Battlefield) && wasFacedown) {
|
|
||||||
Card revealLKI = CardCopyService.getLKICopy(c);
|
Card revealLKI = CardCopyService.getLKICopy(c);
|
||||||
revealLKI.forceTurnFaceUp();
|
revealLKI.forceTurnFaceUp();
|
||||||
reveal(new CardCollection(revealLKI), revealLKI.getOwner(), true, "Face-down card moves from the stack: ");
|
reveal(new CardCollection(revealLKI), revealLKI.getOwner(), true, "Face-down card leaves the " + zoneFrom.toString() + ": ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromBattlefield) {
|
if (fromBattlefield) {
|
||||||
if (!c.isRealToken() && !c.isSpecialized()) {
|
|
||||||
copied.setState(CardStateName.Original, true);
|
|
||||||
}
|
|
||||||
// Soulbond unpairing
|
// Soulbond unpairing
|
||||||
if (c.isPaired()) {
|
if (c.isPaired()) {
|
||||||
c.getPairedWith().setPairedWith(null);
|
c.getPairedWith().setPairedWith(null);
|
||||||
@@ -680,27 +647,12 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
changeZone(null, zoneTo, unmeld, position, cause, params);
|
changeZone(null, zoneTo, unmeld, position, cause, params);
|
||||||
}
|
}
|
||||||
// Reveal if face-down
|
|
||||||
if (wasFacedown) {
|
|
||||||
Card revealLKI = CardCopyService.getLKICopy(c);
|
|
||||||
revealLKI.forceTurnFaceUp();
|
|
||||||
reveal(new CardCollection(revealLKI), revealLKI.getOwner(), true, "Face-down card leaves the battlefield: ");
|
|
||||||
|
|
||||||
copied.setState(CardStateName.Original, true);
|
|
||||||
}
|
|
||||||
} else if (toBattlefield) {
|
} else if (toBattlefield) {
|
||||||
for (Player p : game.getPlayers()) {
|
for (Player p : game.getPlayers()) {
|
||||||
copied.getDamageHistory().setNotAttackedSinceLastUpkeepOf(p);
|
copied.getDamageHistory().setNotAttackedSinceLastUpkeepOf(p);
|
||||||
copied.getDamageHistory().setNotBlockedSinceLastUpkeepOf(p);
|
copied.getDamageHistory().setNotBlockedSinceLastUpkeepOf(p);
|
||||||
copied.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(p);
|
copied.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(p);
|
||||||
}
|
}
|
||||||
} else if (zoneTo.is(ZoneType.Graveyard)
|
|
||||||
|| zoneTo.is(ZoneType.Hand)
|
|
||||||
|| zoneTo.is(ZoneType.Library)
|
|
||||||
|| zoneTo.is(ZoneType.Exile)) {
|
|
||||||
if (copied.isFaceDown()) {
|
|
||||||
copied.setState(CardStateName.Original, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cards not on the battlefield / stack should not have controller
|
// Cards not on the battlefield / stack should not have controller
|
||||||
@@ -748,12 +700,12 @@ public class GameAction {
|
|||||||
eff.setLayerTimestamp(timestamp);
|
eff.setLayerTimestamp(timestamp);
|
||||||
} else {
|
} else {
|
||||||
// otherwise create effect first
|
// otherwise create effect first
|
||||||
eff = SpellAbilityEffect.createEffect(cause, cause.getActivatingPlayer(), name, source.getImageKey(), timestamp);
|
eff = SpellAbilityEffect.createEffect(cause, cause.getHostCard(), cause.getActivatingPlayer(), name, source.getImageKey(), timestamp);
|
||||||
eff.setRenderForUI(false);
|
eff.setRenderForUI(false);
|
||||||
StaticAbility stAb = eff.addStaticAbility(AbilityUtils.getSVar(cause, cause.getParam("StaticEffect")));
|
StaticAbility stAb = eff.addStaticAbility(AbilityUtils.getSVar(cause, cause.getParam("StaticEffect")));
|
||||||
stAb.setActiveZone(EnumSet.of(ZoneType.Command));
|
stAb.setActiveZone(EnumSet.of(ZoneType.Command));
|
||||||
// needed for ETB lookahead like Bronzehide Lion
|
// needed for ETB lookahead like Bronzehide Lion
|
||||||
stAb.putParam("AffectedZone", "Battlefield,Hand,Graveyard,Exile,Stack,Library,Command");
|
stAb.putParam("AffectedZone", "All");
|
||||||
SpellAbilityEffect.addForgetOnMovedTrigger(eff, "Battlefield");
|
SpellAbilityEffect.addForgetOnMovedTrigger(eff, "Battlefield");
|
||||||
game.getAction().moveToCommand(eff, cause);
|
game.getAction().moveToCommand(eff, cause);
|
||||||
}
|
}
|
||||||
@@ -1038,7 +990,8 @@ public class GameAction {
|
|||||||
lki = CardCopyService.getLKICopy(c);
|
lki = CardCopyService.getLKICopy(c);
|
||||||
}
|
}
|
||||||
game.addChangeZoneLKIInfo(lki);
|
game.addChangeZoneLKIInfo(lki);
|
||||||
if (lki.isInPlay()) {
|
// CR 702.26k
|
||||||
|
if (lki.isInPlay() && !lki.isPhasedOut()) {
|
||||||
if (game.getCombat() != null) {
|
if (game.getCombat() != null) {
|
||||||
game.getCombat().saveLKI(lki);
|
game.getCombat().saveLKI(lki);
|
||||||
game.getCombat().removeFromCombat(c);
|
game.getCombat().removeFromCombat(c);
|
||||||
@@ -1282,9 +1235,9 @@ public class GameAction {
|
|||||||
// Update P/T and type in the view only once after all the cards have been processed, to avoid flickering
|
// Update P/T and type in the view only once after all the cards have been processed, to avoid flickering
|
||||||
for (Card c : affectedCards) {
|
for (Card c : affectedCards) {
|
||||||
c.updateNameforView();
|
c.updateNameforView();
|
||||||
c.updatePowerToughnessForView();
|
c.updatePTforView();
|
||||||
c.updateTypesForView();
|
c.updateTypesForView();
|
||||||
c.updateAbilityTextForView(); // only update keywords and text for view to avoid flickering
|
c.updateKeywords();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO filter out old copies from zone change
|
// TODO filter out old copies from zone change
|
||||||
@@ -1569,6 +1522,12 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 704.5z If a player controls a permanent with start your engines! and that player has no speed, that player’s speed becomes 1. See rule 702.179, “Start Your Engines!”
|
||||||
|
if (p.getSpeed() == 0 && p.getCardsIn(ZoneType.Battlefield).anyMatch(c -> c.hasKeyword(Keyword.START_YOUR_ENGINES))) {
|
||||||
|
p.increaseSpeed();
|
||||||
|
checkAgain = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (handlePlaneswalkerRule(p, noRegCreats)) {
|
if (handlePlaneswalkerRule(p, noRegCreats)) {
|
||||||
checkAgain = true;
|
checkAgain = true;
|
||||||
}
|
}
|
||||||
@@ -2684,8 +2643,8 @@ public class GameAction {
|
|||||||
if (isCombat) {
|
if (isCombat) {
|
||||||
for (Map.Entry<GameEntity, Map<Card, Integer>> et : damageMap.columnMap().entrySet()) {
|
for (Map.Entry<GameEntity, Map<Card, Integer>> et : damageMap.columnMap().entrySet()) {
|
||||||
final GameEntity ge = et.getKey();
|
final GameEntity ge = et.getKey();
|
||||||
if (ge instanceof Card) {
|
if (ge instanceof Card c) {
|
||||||
((Card) ge).clearAssignedDamage();
|
c.clearAssignedDamage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2705,8 +2664,7 @@ public class GameAction {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.getKey() instanceof Card && !lethalDamage.containsKey(e.getKey())) {
|
if (e.getKey() instanceof Card c && !lethalDamage.containsKey(c)) {
|
||||||
Card c = (Card) e.getKey();
|
|
||||||
lethalDamage.put(c, c.getExcessDamageValue(false));
|
lethalDamage.put(c, c.getExcessDamageValue(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -273,8 +273,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String controllerName;
|
String controllerName;
|
||||||
if (defender instanceof Card) {
|
if (defender instanceof Card c) {
|
||||||
Card c = ((Card)defender);
|
|
||||||
controllerName = c.isBattle() ? c.getProtectingPlayer().getName() : c.getController().getName();
|
controllerName = c.isBattle() ? c.getProtectingPlayer().getName() : c.getController().getName();
|
||||||
} else {
|
} else {
|
||||||
controllerName = defender.getName();
|
controllerName = defender.getName();
|
||||||
|
|||||||
@@ -219,7 +219,9 @@ public class StaticEffect {
|
|||||||
|
|
||||||
if (layers.contains(StaticAbilityLayer.TEXT)) {
|
if (layers.contains(StaticAbilityLayer.TEXT)) {
|
||||||
// Revert changed color words
|
// Revert changed color words
|
||||||
affectedCard.removeChangedTextColorWord(getTimestamp(), ability.getId());
|
if (hasParam("ChangeColorWordsTo")) {
|
||||||
|
affectedCard.removeChangedTextColorWord(getTimestamp(), ability.getId());
|
||||||
|
}
|
||||||
|
|
||||||
// remove changed name
|
// remove changed name
|
||||||
if (hasParam("SetName") || hasParam("AddNames")) {
|
if (hasParam("SetName") || hasParam("AddNames")) {
|
||||||
@@ -265,7 +267,7 @@ public class StaticEffect {
|
|||||||
if (hasParam("AddAbility") || hasParam("GainsAbilitiesOf")
|
if (hasParam("AddAbility") || hasParam("GainsAbilitiesOf")
|
||||||
|| hasParam("GainsAbilitiesOfDefined") || hasParam("GainsTriggerAbsOf")
|
|| hasParam("GainsAbilitiesOfDefined") || hasParam("GainsTriggerAbsOf")
|
||||||
|| hasParam("AddTrigger") || hasParam("AddStaticAbility")
|
|| hasParam("AddTrigger") || hasParam("AddStaticAbility")
|
||||||
|| hasParam("AddReplacementEffects") || hasParam("RemoveAllAbilities")
|
|| hasParam("AddReplacementEffect") || hasParam("RemoveAllAbilities")
|
||||||
|| hasParam("RemoveLandTypes")) {
|
|| hasParam("RemoveLandTypes")) {
|
||||||
affectedCard.removeChangedCardTraits(getTimestamp(), ability.getId());
|
affectedCard.removeChangedCardTraits(getTimestamp(), ability.getId());
|
||||||
}
|
}
|
||||||
@@ -275,11 +277,14 @@ public class StaticEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
affectedCard.removeChangedSVars(getTimestamp(), ability.getId());
|
affectedCard.removeChangedSVars(getTimestamp(), ability.getId());
|
||||||
|
|
||||||
|
// need update for clean reapply
|
||||||
|
affectedCard.updateKeywordsCache(affectedCard.getCurrentState());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layers.contains(StaticAbilityLayer.SETPT)) {
|
if (layers.contains(StaticAbilityLayer.CHARACTERISTIC) || layers.contains(StaticAbilityLayer.SETPT)) {
|
||||||
if (hasParam("SetPower") || hasParam("SetToughness")) {
|
if (hasParam("SetPower") || hasParam("SetToughness")) {
|
||||||
affectedCard.removeNewPT(getTimestamp(), ability.getId());
|
affectedCard.removeNewPT(getTimestamp(), ability.getId(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,8 +316,6 @@ public class StaticEffect {
|
|||||||
affectedCard.removeCanBlockAdditional(getTimestamp());
|
affectedCard.removeCanBlockAdditional(getTimestamp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
affectedCard.updateAbilityTextForView(); // need to update keyword cache for clean reapply
|
|
||||||
}
|
}
|
||||||
return affectedCards;
|
return affectedCards;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import forge.game.*;
|
|||||||
import forge.game.ability.AbilityFactory.AbilityRecordType;
|
import forge.game.ability.AbilityFactory.AbilityRecordType;
|
||||||
import forge.game.card.*;
|
import forge.game.card.*;
|
||||||
import forge.game.cost.Cost;
|
import forge.game.cost.Cost;
|
||||||
|
import forge.game.cost.IndividualCostPaymentInstance;
|
||||||
import forge.game.keyword.Keyword;
|
import forge.game.keyword.Keyword;
|
||||||
import forge.game.keyword.KeywordInterface;
|
import forge.game.keyword.KeywordInterface;
|
||||||
import forge.game.mana.Mana;
|
import forge.game.mana.Mana;
|
||||||
@@ -1362,10 +1363,8 @@ public class AbilityUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// do blessing there before condition checks
|
// do blessing there before condition checks
|
||||||
if (source.hasKeyword(Keyword.ASCEND)) {
|
if (source.hasKeyword(Keyword.ASCEND) && controller.getZone(ZoneType.Battlefield).size() >= 10) {
|
||||||
if (controller.getZone(ZoneType.Battlefield).size() >= 10) {
|
controller.setBlessing(true);
|
||||||
controller.setBlessing(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source.hasKeyword(Keyword.GIFT) && sa.isGiftPromised()) {
|
if (source.hasKeyword(Keyword.GIFT) && sa.isGiftPromised()) {
|
||||||
@@ -2839,7 +2838,13 @@ public class AbilityUtils {
|
|||||||
final String[] workingCopy = paidparts[0].split("_");
|
final String[] workingCopy = paidparts[0].split("_");
|
||||||
final String validFilter = workingCopy[1];
|
final String validFilter = workingCopy[1];
|
||||||
// use objectXCount ?
|
// use objectXCount ?
|
||||||
return CardUtil.getThisTurnActivated(validFilter, c, ctb, player).size();
|
int activated = CardUtil.getThisTurnActivated(validFilter, c, ctb, player).size();
|
||||||
|
for (IndividualCostPaymentInstance i : game.costPaymentStack) {
|
||||||
|
if (i.getPayment().getAbility().isValid(validFilter, player, c, ctb)) {
|
||||||
|
activated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count$ThisTurnEntered <ZoneDestination> [from <ZoneOrigin>] <Valid>
|
// Count$ThisTurnEntered <ZoneDestination> [from <ZoneOrigin>] <Valid>
|
||||||
@@ -3699,6 +3704,10 @@ public class AbilityUtils {
|
|||||||
return doXMath(amount, m, source, ctb);
|
return doXMath(amount, m, source, ctb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value.equals("AttractionsVisitedThisTurn")) {
|
||||||
|
return doXMath(player.getAttractionsVisitedThisTurn(), m, source, ctb);
|
||||||
|
}
|
||||||
|
|
||||||
if (value.startsWith("PlaneswalkedToThisTurn")) {
|
if (value.startsWith("PlaneswalkedToThisTurn")) {
|
||||||
int found = 0;
|
int found = 0;
|
||||||
String name = value.split(" ")[1];
|
String name = value.split(" ")[1];
|
||||||
|
|||||||
@@ -588,11 +588,10 @@ public abstract class SpellAbilityEffect {
|
|||||||
|
|
||||||
// create a basic template for Effect to be used somewhere els
|
// create a basic template for Effect to be used somewhere els
|
||||||
public static Card createEffect(final SpellAbility sa, final Player controller, final String name, final String image) {
|
public static Card createEffect(final SpellAbility sa, final Player controller, final String name, final String image) {
|
||||||
return createEffect(sa, controller, name, image, controller.getGame().getNextTimestamp());
|
return createEffect(sa, sa.getHostCard(), controller, name, image, controller.getGame().getNextTimestamp());
|
||||||
}
|
}
|
||||||
public static Card createEffect(final SpellAbility sa, final Player controller, final String name, final String image, final long timestamp) {
|
public static Card createEffect(final SpellAbility sa, final Card hostCard, final Player controller, final String name, final String image, final long timestamp) {
|
||||||
final Card hostCard = sa.getHostCard();
|
final Game game = controller.getGame();
|
||||||
final Game game = hostCard.getGame();
|
|
||||||
final Card eff = new Card(game.nextCardId(), game);
|
final Card eff = new Card(game.nextCardId(), game);
|
||||||
|
|
||||||
eff.setGameTimestamp(timestamp);
|
eff.setGameTimestamp(timestamp);
|
||||||
@@ -608,12 +607,7 @@ public abstract class SpellAbilityEffect {
|
|||||||
eff.setRarity(hostCard.getRarity());
|
eff.setRarity(hostCard.getRarity());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("Boon")) {
|
|
||||||
eff.setBoon(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
eff.setOwner(controller);
|
eff.setOwner(controller);
|
||||||
eff.setSVars(sa.getSVars());
|
|
||||||
|
|
||||||
eff.setSetCode(hostCard.getSetCode());
|
eff.setSetCode(hostCard.getSetCode());
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
@@ -621,7 +615,12 @@ public abstract class SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
eff.setGamePieceType(GamePieceType.EFFECT);
|
eff.setGamePieceType(GamePieceType.EFFECT);
|
||||||
eff.setEffectSource(sa);
|
if (sa != null) {
|
||||||
|
eff.setEffectSource(sa);
|
||||||
|
eff.setSVars(sa.getSVars());
|
||||||
|
} else {
|
||||||
|
eff.setEffectSource(hostCard);
|
||||||
|
}
|
||||||
|
|
||||||
return eff;
|
return eff;
|
||||||
}
|
}
|
||||||
@@ -1041,7 +1040,9 @@ public abstract class SpellAbilityEffect {
|
|||||||
exilingSource = cause.getOriginalHost();
|
exilingSource = cause.getOriginalHost();
|
||||||
}
|
}
|
||||||
movedCard.setExiledWith(exilingSource);
|
movedCard.setExiledWith(exilingSource);
|
||||||
movedCard.setExiledBy(cause.getActivatingPlayer());
|
Player exiler = cause.hasParam("DefinedExiler") ?
|
||||||
|
getDefinedPlayersOrTargeted(cause, "DefinedExiler").get(0) : cause.getActivatingPlayer();
|
||||||
|
movedCard.setExiledBy(exiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GameCommand exileEffectCommand(final Game game, final Card effect) {
|
public static GameCommand exileEffectCommand(final Game game, final Card effect) {
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ public class AnimateEffect extends AnimateEffectBase {
|
|||||||
|
|
||||||
if (sa.isCrew()) {
|
if (sa.isCrew()) {
|
||||||
gameCard.becomesCrewed(sa);
|
gameCard.becomesCrewed(sa);
|
||||||
gameCard.updatePowerToughnessForView();
|
gameCard.updatePTforView();
|
||||||
}
|
}
|
||||||
|
|
||||||
game.fireEvent(new GameEventCardStatsChanged(gameCard));
|
game.fireEvent(new GameEventCardStatsChanged(gameCard));
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ public class ChangeTargetsEffect extends SpellAbilityEffect {
|
|||||||
source = changingTgtSA.getTargetCard();
|
source = changingTgtSA.getTargetCard();
|
||||||
}
|
}
|
||||||
Predicate<GameObject> filter = sa.hasParam("TargetRestriction") ? GameObjectPredicates.restriction(sa.getParam("TargetRestriction").split(","), activator, source, sa) : null;
|
Predicate<GameObject> filter = sa.hasParam("TargetRestriction") ? GameObjectPredicates.restriction(sa.getParam("TargetRestriction").split(","), activator, source, sa) : null;
|
||||||
// TODO Creature.Other might not work yet as it should
|
|
||||||
TargetChoices newTarget = chooser.getController().chooseNewTargetsFor(changingTgtSA, filter, false);
|
TargetChoices newTarget = chooser.getController().chooseNewTargetsFor(changingTgtSA, filter, false);
|
||||||
changingTgtSI.updateTarget(newTarget, sa.getHostCard());
|
changingTgtSI.updateTarget(newTarget, sa.getHostCard());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,10 +107,8 @@ public class ChangeZoneAllEffect extends SpellAbilityEffect {
|
|||||||
final Zone originZone = game.getZoneOf(c);
|
final Zone originZone = game.getZoneOf(c);
|
||||||
|
|
||||||
// Fizzle spells so that they are removed from stack (e.g. Summary Dismissal)
|
// Fizzle spells so that they are removed from stack (e.g. Summary Dismissal)
|
||||||
if (sa.hasParam("Fizzle")) {
|
if (originZone.is(ZoneType.Stack)) {
|
||||||
if (originZone.is(ZoneType.Exile) || originZone.is(ZoneType.Hand) || originZone.is(ZoneType.Stack)) {
|
game.getStack().remove(c);
|
||||||
game.getStack().remove(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remLKI) {
|
if (remLKI) {
|
||||||
|
|||||||
@@ -558,22 +558,15 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (originZone.is(ZoneType.Stack)) {
|
||||||
|
game.getStack().remove(gameCard);
|
||||||
|
}
|
||||||
|
|
||||||
Card movedCard = null;
|
Card movedCard = null;
|
||||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||||
AbilityKey.addCardZoneTableParams(moveParams, triggerList);
|
AbilityKey.addCardZoneTableParams(moveParams, triggerList);
|
||||||
|
|
||||||
if (destination.equals(ZoneType.Library)) {
|
if (destination.equals(ZoneType.Battlefield)) {
|
||||||
// If a card is moved to library from the stack, remove its spells from the stack
|
|
||||||
if (sa.hasParam("Fizzle")) {
|
|
||||||
// TODO only AI still targets as card, try to remove it
|
|
||||||
if (gameCard.isInZone(ZoneType.Exile) || gameCard.isInZone(ZoneType.Hand) || gameCard.isInZone(ZoneType.Stack)) {
|
|
||||||
// This only fizzles spells, not anything else.
|
|
||||||
game.getStack().remove(gameCard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
movedCard = game.getAction().moveToLibrary(gameCard, libraryPosition, sa, moveParams);
|
|
||||||
} else if (destination.equals(ZoneType.Battlefield)) {
|
|
||||||
moveParams.put(AbilityKey.SimultaneousETB, tgtCards);
|
moveParams.put(AbilityKey.SimultaneousETB, tgtCards);
|
||||||
if (sa.isReplacementAbility()) {
|
if (sa.isReplacementAbility()) {
|
||||||
ReplacementEffect re = sa.getReplacementEffect();
|
ReplacementEffect re = sa.getReplacementEffect();
|
||||||
@@ -725,15 +718,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
commandCards.add(movedCard); //add to list to reveal the commandzone cards
|
commandCards.add(movedCard); //add to list to reveal the commandzone cards
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a card is Exiled from the stack, remove its spells from the stack
|
|
||||||
if (sa.hasParam("Fizzle")) {
|
|
||||||
if (gameCard.isInZone(ZoneType.Exile) || gameCard.isInZone(ZoneType.Hand)
|
|
||||||
|| gameCard.isInZone(ZoneType.Stack) || gameCard.isInZone(ZoneType.Command)) {
|
|
||||||
// This only fizzles spells, not anything else.
|
|
||||||
game.getStack().remove(gameCard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa.hasParam("WithCountersType")) {
|
if (sa.hasParam("WithCountersType")) {
|
||||||
CounterType cType = CounterType.getType(sa.getParam("WithCountersType"));
|
CounterType cType = CounterType.getType(sa.getParam("WithCountersType"));
|
||||||
int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa);
|
int cAmount = AbilityUtils.calculateAmount(hostCard, sa.getParamOrDefault("WithCountersAmount", "1"), sa);
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ public class ControlSpellEffect extends SpellAbilityEffect {
|
|||||||
// Expand this area as it becomes needed
|
// Expand this area as it becomes needed
|
||||||
// Use "DefinedExchange" to Reference Object that is Exchanging the other direction
|
// Use "DefinedExchange" to Reference Object that is Exchanging the other direction
|
||||||
GameObject obj = Iterables.getFirst(getDefinedOrTargeted(sa, "DefinedExchange"), null);
|
GameObject obj = Iterables.getFirst(getDefinedOrTargeted(sa, "DefinedExchange"), null);
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card c) {
|
||||||
Card c = (Card)obj;
|
|
||||||
if (!c.isInPlay() || si == null) {
|
if (!c.isInPlay() || si == null) {
|
||||||
// Exchanging object isn't available, continue
|
// Exchanging object isn't available, continue
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ public class CounterEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
params.put(AbilityKey.StackSa, tgtSA);
|
params.put(AbilityKey.StackSa, tgtSA);
|
||||||
|
|
||||||
String destination = srcSA.hasParam("Destination") ? srcSA.getParam("Destination") : tgtSA.isAftermath() ? "Exile" : "Graveyard";
|
String destination = srcSA.getParamOrDefault("Destination", "Graveyard");
|
||||||
if (srcSA.hasParam("DestinationChoice")) { //Hinder
|
if (srcSA.hasParam("DestinationChoice")) { //Hinder
|
||||||
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
|
List<String> pos = Arrays.asList(srcSA.getParam("DestinationChoice").split(","));
|
||||||
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos);
|
destination = srcSA.getActivatingPlayer().getController().chooseSomeType(Localizer.getInstance().getMessage("lblRemoveDestination"), tgtSA, pos);
|
||||||
|
|||||||
@@ -252,8 +252,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
if (sa.hasParam("DividedRandomly")) {
|
if (sa.hasParam("DividedRandomly")) {
|
||||||
CardCollection targets = new CardCollection();
|
CardCollection targets = new CardCollection();
|
||||||
for (final GameEntity obj : tgtObjects) { // check if each target is still OK
|
for (final GameEntity obj : tgtObjects) { // check if each target is still OK
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card tgtCard) {
|
||||||
Card tgtCard = (Card) obj;
|
|
||||||
Card gameCard = game.getCardState(tgtCard, null);
|
Card gameCard = game.getCardState(tgtCard, null);
|
||||||
if (gameCard == null || !tgtCard.equalsWithGameTimestamp(gameCard)) {
|
if (gameCard == null || !tgtCard.equalsWithGameTimestamp(gameCard)) {
|
||||||
tgtObjects.remove(obj);
|
tgtObjects.remove(obj);
|
||||||
@@ -284,8 +283,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
for (final GameEntity obj : tgtObjects) {
|
for (final GameEntity obj : tgtObjects) {
|
||||||
// check if the object is still in game or if it was moved
|
// check if the object is still in game or if it was moved
|
||||||
Card gameCard = null;
|
Card gameCard = null;
|
||||||
if (obj instanceof Card) {
|
if (obj instanceof Card tgtCard) {
|
||||||
Card tgtCard = (Card) obj;
|
|
||||||
gameCard = game.getCardState(tgtCard, null);
|
gameCard = game.getCardState(tgtCard, null);
|
||||||
// gameCard is LKI in that case, the card is not in game anymore
|
// gameCard is LKI in that case, the card is not in game anymore
|
||||||
// or the timestamp did change
|
// or the timestamp did change
|
||||||
@@ -572,9 +570,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
|
|||||||
if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) {
|
if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) {
|
||||||
counterRemain = counterRemain - counterAmount;
|
counterRemain = counterRemain - counterAmount;
|
||||||
}
|
}
|
||||||
} else if (obj instanceof Player) {
|
} else if (obj instanceof Player pl) {
|
||||||
// Add Counters to players!
|
// Add Counters to players!
|
||||||
Player pl = (Player) obj;
|
|
||||||
pl.addCounter(counterType, counterAmount, placer, table);
|
pl.addCounter(counterType, counterAmount, placer, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,8 +248,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
|
|||||||
if (chosenAmount > 0) {
|
if (chosenAmount > 0) {
|
||||||
removed += chosenAmount;
|
removed += chosenAmount;
|
||||||
entity.subtractCounter(chosenType, chosenAmount, activator);
|
entity.subtractCounter(chosenType, chosenAmount, activator);
|
||||||
if (entity instanceof Card) {
|
if (entity instanceof Card gameCard) {
|
||||||
Card gameCard = (Card) entity;
|
|
||||||
game.updateLastStateForCard(gameCard);
|
game.updateLastStateForCard(gameCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -252,16 +252,14 @@ public class DamageDealEffect extends DamageBaseEffect {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
final Card gc = game.getCardState(c, null);
|
final Card gc = game.getCardState(c, null);
|
||||||
if (gc == null || !c.equalsWithGameTimestamp(gc) || !gc.isInPlay() || gc.isPhasedOut()) {
|
if (gc == null || !c.equalsWithGameTimestamp(gc) || !gc.isInPlay() || gc.isPhasedOut()) {
|
||||||
// timestamp different or not in play
|
// timestamp different or not in play
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
internalDamageDeal(sa, sourceLKI, gc, dmg, damageMap);
|
internalDamageDeal(sa, sourceLKI, gc, dmg, damageMap);
|
||||||
} else if (o instanceof Player) {
|
} else if (o instanceof Player p) {
|
||||||
final Player p = (Player) o;
|
|
||||||
damageMap.put(sourceLKI, p, dmg);
|
damageMap.put(sourceLKI, p, dmg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,8 +95,7 @@ public class DamageEachEffect extends DamageBaseEffect {
|
|||||||
}
|
}
|
||||||
} else for (GameEntity ge : getTargetEntities(sa)) {
|
} else for (GameEntity ge : getTargetEntities(sa)) {
|
||||||
// check before checking sources
|
// check before checking sources
|
||||||
if (ge instanceof Card) {
|
if (ge instanceof Card c) {
|
||||||
final Card c = (Card) ge;
|
|
||||||
if (!c.isInPlay() || c.isPhasedOut()) {
|
if (!c.isInPlay() || c.isPhasedOut()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ public class DamagePreventEffect extends SpellAbilityEffect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Object o = tgts.get(i);
|
final Object o = tgts.get(i);
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card tgtC) {
|
||||||
final Card tgtC = (Card) o;
|
|
||||||
if (tgtC.isFaceDown()) {
|
if (tgtC.isFaceDown()) {
|
||||||
sb.append("Morph");
|
sb.append("Morph");
|
||||||
} else {
|
} else {
|
||||||
@@ -104,8 +103,7 @@ public class DamagePreventEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
for (final GameEntity o : tgts) {
|
for (final GameEntity o : tgts) {
|
||||||
numDam = sa.usesTargeting() && sa.isDividedAsYouChoose() ? sa.getDividedValue(o) : numDam;
|
numDam = sa.usesTargeting() && sa.isDividedAsYouChoose() ? sa.getDividedValue(o) : numDam;
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
final Card c = (Card) o;
|
|
||||||
if (c.isInPlay()) {
|
if (c.isInPlay()) {
|
||||||
addPreventNextDamage(sa, o, numDam);
|
addPreventNextDamage(sa, o, numDam);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
final boolean skipReorder = sa.hasParam("SkipReorder");
|
final boolean skipReorder = sa.hasParam("SkipReorder");
|
||||||
|
|
||||||
// A hack for cards like Explorer's Scope that need to ensure that a card is revealed to the player activating the ability
|
// A hack for cards like Explorer's Scope that need to ensure that a card is revealed to the player activating the ability
|
||||||
final boolean forceRevealToController = sa.hasParam("ForceRevealToController");
|
final boolean forceReveal = sa.hasParam("ForceRevealToController") ||
|
||||||
|
sa.hasParam("ForceReveal");
|
||||||
|
|
||||||
// These parameters are used to indicate that a dialog box must be show to the player asking if the player wants to proceed
|
// These parameters are used to indicate that a dialog box must be show to the player asking if the player wants to proceed
|
||||||
// with an optional ability, otherwise the optional ability is skipped.
|
// with an optional ability, otherwise the optional ability is skipped.
|
||||||
@@ -236,9 +237,12 @@ public class DigEffect extends SpellAbilityEffect {
|
|||||||
valid = top;
|
valid = top;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forceRevealToController) {
|
if (forceReveal) {
|
||||||
// Force revealing the card to the player activating the ability (e.g. Explorer's Scope)
|
// Force revealing the card to defined (e.g. Gonti, Night Minister) or the player activating the
|
||||||
game.getAction().revealTo(top, activator);
|
// ability (e.g. Explorer's Scope)
|
||||||
|
Player revealTo = sa.hasParam("ForceReveal") ?
|
||||||
|
getDefinedPlayersOrTargeted(sa, "ForceReveal").get(0) : activator;
|
||||||
|
game.getAction().revealTo(top, revealTo);
|
||||||
delayedReveal = null; // top is already seen by the player, do not reveal twice
|
delayedReveal = null; // top is already seen by the player, do not reveal twice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,9 @@ public class EffectEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
for (Player controller : effectOwner) {
|
for (Player controller : effectOwner) {
|
||||||
final Card eff = createEffect(sa, controller, name, image);
|
final Card eff = createEffect(sa, controller, name, image);
|
||||||
|
if (sa.hasParam("Boon")) {
|
||||||
|
eff.setBoon(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Abilities and triggers work the same as they do for Token
|
// Abilities and triggers work the same as they do for Token
|
||||||
// Grant abilities
|
// Grant abilities
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card card = sa.getHostCard();
|
final Card card = sa.getHostCard();
|
||||||
|
final Game game = card.getGame();
|
||||||
final AbilityManaPart abMana = sa.getManaPart();
|
final AbilityManaPart abMana = sa.getManaPart();
|
||||||
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
|
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
|
||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
@@ -39,10 +40,7 @@ public class ManaEffect extends SpellAbilityEffect {
|
|||||||
// Spells are not undoable
|
// Spells are not undoable
|
||||||
sa.setUndoable(sa.isAbility() && sa.isUndoable() && tgtPlayers.size() < 2 && !sa.hasParam("ActivationLimit"));
|
sa.setUndoable(sa.isAbility() && sa.isUndoable() && tgtPlayers.size() < 2 && !sa.hasParam("ActivationLimit"));
|
||||||
|
|
||||||
final boolean optional = sa.hasParam("Optional");
|
if (sa.hasParam("Optional") && !activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantAddMana"), null)) {
|
||||||
final Game game = activator.getGame();
|
|
||||||
|
|
||||||
if (optional && !activator.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantAddMana"), null)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,24 +17,25 @@ public class ManifestDreadEffect extends ManifestEffect {
|
|||||||
final Game game = p.getGame();
|
final Game game = p.getGame();
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
CardCollection tgtCards = p.getTopXCardsFromLibrary(2);
|
CardCollection tgtCards = p.getTopXCardsFromLibrary(2);
|
||||||
Card manifest = null;
|
CardCollection toGrave = new CardCollection();
|
||||||
Card toGrave = null;
|
|
||||||
if (!tgtCards.isEmpty()) {
|
if (!tgtCards.isEmpty()) {
|
||||||
manifest = p.getController().chooseSingleEntityForEffect(tgtCards, sa, getDefaultMessage(), null);
|
Card manifest = p.getController().chooseSingleEntityForEffect(tgtCards, sa, getDefaultMessage(), null);
|
||||||
tgtCards.remove(manifest);
|
tgtCards.remove(manifest);
|
||||||
toGrave = tgtCards.isEmpty() ? null : tgtCards.getFirst();
|
|
||||||
|
|
||||||
// CR 701.34d If an effect instructs a player to manifest multiple cards from their library, those cards are manifested one at a time.
|
|
||||||
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
|
||||||
CardZoneTable triggerList = AbilityKey.addCardZoneTableParams(moveParams, sa);
|
CardZoneTable triggerList = AbilityKey.addCardZoneTableParams(moveParams, sa);
|
||||||
internalEffect(manifest, p, sa, moveParams);
|
manifest = internalEffect(manifest, p, sa, moveParams);
|
||||||
if (toGrave != null) {
|
// CR 701.60a
|
||||||
toGrave = game.getAction().moveToGraveyard(toGrave, sa, moveParams);
|
if (!manifest.isManifested()) {
|
||||||
|
tgtCards.add(manifest);
|
||||||
|
}
|
||||||
|
for (Card c : tgtCards) {
|
||||||
|
toGrave.add(game.getAction().moveToGraveyard(c, sa, moveParams));
|
||||||
}
|
}
|
||||||
triggerList.triggerChangesZoneAll(game, sa);
|
triggerList.triggerChangesZoneAll(game, sa);
|
||||||
}
|
}
|
||||||
Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(p);
|
Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(p);
|
||||||
runParams.put(AbilityKey.Card, toGrave);
|
runParams.put(AbilityKey.Cards, toGrave);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.ManifestDread, runParams, true);
|
game.getTriggerHandler().runTrigger(TriggerType.ManifestDread, runParams, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
|||||||
tgtC.addChangedCardKeywords(kws, null, false, timestamp, null);
|
tgtC.addChangedCardKeywords(kws, null, false, timestamp, null);
|
||||||
}
|
}
|
||||||
if (redrawPT) {
|
if (redrawPT) {
|
||||||
tgtC.updatePowerToughnessForView();
|
tgtC.updatePTforView();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hiddenkws.isEmpty()) {
|
if (!hiddenkws.isEmpty()) {
|
||||||
@@ -93,7 +93,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
|
|||||||
tgtC.removeChangedCardKeywords(timestamp, 0);
|
tgtC.removeChangedCardKeywords(timestamp, 0);
|
||||||
tgtC.removeHiddenExtrinsicKeywords(timestamp, 0);
|
tgtC.removeHiddenExtrinsicKeywords(timestamp, 0);
|
||||||
|
|
||||||
tgtC.updatePowerToughnessForView();
|
tgtC.updatePTforView();
|
||||||
|
|
||||||
game.fireEvent(new GameEventCardStatsChanged(tgtC));
|
game.fireEvent(new GameEventCardStatsChanged(tgtC));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
gameCard.addHiddenExtrinsicKeywords(timestamp, 0, hiddenKws);
|
gameCard.addHiddenExtrinsicKeywords(timestamp, 0, hiddenKws);
|
||||||
}
|
}
|
||||||
if (redrawPT) {
|
if (redrawPT) {
|
||||||
gameCard.updatePowerToughnessForView();
|
gameCard.updatePTforView();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sa.hasParam("CanBlockAny")) {
|
if (sa.hasParam("CanBlockAny")) {
|
||||||
@@ -120,7 +120,7 @@ public class PumpEffect extends SpellAbilityEffect {
|
|||||||
gameCard.removeHiddenExtrinsicKeywords(timestamp, 0);
|
gameCard.removeHiddenExtrinsicKeywords(timestamp, 0);
|
||||||
gameCard.removeChangedCardKeywords(timestamp, 0);
|
gameCard.removeChangedCardKeywords(timestamp, 0);
|
||||||
}
|
}
|
||||||
gameCard.updatePowerToughnessForView();
|
gameCard.updatePTforView();
|
||||||
if (updateText) {
|
if (updateText) {
|
||||||
gameCard.updateAbilityTextForView();
|
gameCard.updateAbilityTextForView();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ public class UnattachAllEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
// If Cast Targets will be checked on the Stack
|
// If Cast Targets will be checked on the Stack
|
||||||
for (GameEntity ge : getTargetEntities(sa)) {
|
for (GameEntity ge : getTargetEntities(sa)) {
|
||||||
if (ge instanceof Card) {
|
if (ge instanceof Card gc) {
|
||||||
Card gc = (Card) ge;
|
|
||||||
// check if the object is still in game or if it was moved
|
// check if the object is still in game or if it was moved
|
||||||
Card gameCard = game.getCardState(gc, null);
|
Card gameCard = game.getCardState(gc, null);
|
||||||
// gameCard is LKI in that case, the card is not in game anymore
|
// gameCard is LKI in that case, the card is not in game anymore
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -214,8 +214,8 @@ public class CardCopyService {
|
|||||||
if (cachedCard != null) {
|
if (cachedCard != null) {
|
||||||
return cachedCard;
|
return cachedCard;
|
||||||
}
|
}
|
||||||
String msg = "CardUtil:getLKICopy copy object";
|
|
||||||
|
|
||||||
|
String msg = "CardUtil:getLKICopy copy object";
|
||||||
Breadcrumb bread = new Breadcrumb(msg);
|
Breadcrumb bread = new Breadcrumb(msg);
|
||||||
bread.setData("Card", copyFrom.getName());
|
bread.setData("Card", copyFrom.getName());
|
||||||
bread.setData("CardState", copyFrom.getCurrentStateName().toString());
|
bread.setData("CardState", copyFrom.getCurrentStateName().toString());
|
||||||
@@ -304,19 +304,19 @@ public class CardCopyService {
|
|||||||
|
|
||||||
newCopy.setCounters(Maps.newHashMap(copyFrom.getCounters()));
|
newCopy.setCounters(Maps.newHashMap(copyFrom.getCounters()));
|
||||||
|
|
||||||
|
newCopy.setColor(copyFrom.getColor().getColor());
|
||||||
|
newCopy.setPhasedOut(copyFrom.getPhasedOut());
|
||||||
|
newCopy.setTapped(copyFrom.isTapped());
|
||||||
newCopy.setTributed(copyFrom.isTributed());
|
newCopy.setTributed(copyFrom.isTributed());
|
||||||
newCopy.setMonstrous(copyFrom.isMonstrous());
|
newCopy.setMonstrous(copyFrom.isMonstrous());
|
||||||
newCopy.setRenowned(copyFrom.isRenowned());
|
newCopy.setRenowned(copyFrom.isRenowned());
|
||||||
newCopy.setSolved(copyFrom.isSolved());
|
newCopy.setSolved(copyFrom.isSolved());
|
||||||
newCopy.setSaddled(copyFrom.isSaddled());
|
|
||||||
newCopy.setPromisedGift(copyFrom.getPromisedGift());
|
newCopy.setPromisedGift(copyFrom.getPromisedGift());
|
||||||
|
newCopy.setSaddled(copyFrom.isSaddled());
|
||||||
if (newCopy.isSaddled()) newCopy.setSaddledByThisTurn(copyFrom.getSaddledByThisTurn());
|
if (newCopy.isSaddled()) newCopy.setSaddledByThisTurn(copyFrom.getSaddledByThisTurn());
|
||||||
newCopy.setSuspectedTimestamp(copyFrom.getSuspectedTimestamp());
|
if (copyFrom.isSuspected()) {
|
||||||
|
newCopy.setSuspectedEffect(getLKICopy(copyFrom.getSuspectedEffect(), cachedMap));
|
||||||
newCopy.setColor(copyFrom.getColor().getColor());
|
}
|
||||||
newCopy.setPhasedOut(copyFrom.getPhasedOut());
|
|
||||||
|
|
||||||
newCopy.setTapped(copyFrom.isTapped());
|
|
||||||
|
|
||||||
newCopy.setDamageHistory(copyFrom.getDamageHistory());
|
newCopy.setDamageHistory(copyFrom.getDamageHistory());
|
||||||
newCopy.setDamageReceivedThisTurn(copyFrom.getDamageReceivedThisTurn());
|
newCopy.setDamageReceivedThisTurn(copyFrom.getDamageReceivedThisTurn());
|
||||||
@@ -352,8 +352,6 @@ public class CardCopyService {
|
|||||||
}
|
}
|
||||||
newCopy.setChosenEvenOdd(copyFrom.getChosenEvenOdd());
|
newCopy.setChosenEvenOdd(copyFrom.getChosenEvenOdd());
|
||||||
|
|
||||||
//newCopy.getEtbCounters().putAll(copyFrom.getEtbCounters());
|
|
||||||
|
|
||||||
newCopy.setUnearthed(copyFrom.isUnearthed());
|
newCopy.setUnearthed(copyFrom.isUnearthed());
|
||||||
|
|
||||||
newCopy.copyFrom(copyFrom);
|
newCopy.copyFrom(copyFrom);
|
||||||
|
|||||||
@@ -1835,15 +1835,6 @@ public class CardFactoryUtil {
|
|||||||
squadTrigger.setOverridingAbility(squadAbility);
|
squadTrigger.setOverridingAbility(squadAbility);
|
||||||
squadTrigger.setSVar("SquadAmount", "Count$OptionalKeywordAmount");
|
squadTrigger.setSVar("SquadAmount", "Count$OptionalKeywordAmount");
|
||||||
inst.addTrigger(squadTrigger);
|
inst.addTrigger(squadTrigger);
|
||||||
} else if (keyword.equals("Start your engines")) {
|
|
||||||
final String trig = "Mode$ Always | TriggerZones$ Battlefield | Static$ True | CheckDefinedPlayer$ " +
|
|
||||||
"You.NoSpeed | TriggerDescription$ Start your engines! (" + inst.getReminderText() + ")";
|
|
||||||
final String effect = "DB$ ChangeSpeed";
|
|
||||||
|
|
||||||
final Trigger trigger = TriggerHandler.parseTrigger(trig, card, intrinsic);
|
|
||||||
trigger.setOverridingAbility(AbilityFactory.getAbility(effect, card));
|
|
||||||
|
|
||||||
inst.addTrigger(trigger);
|
|
||||||
} else if (keyword.equals("Storm")) {
|
} else if (keyword.equals("Storm")) {
|
||||||
final String actualTrigger = "Mode$ SpellCast | ValidCard$ Card.Self | TriggerZones$ Stack | Secondary$ True"
|
final String actualTrigger = "Mode$ SpellCast | ValidCard$ Card.Self | TriggerZones$ Stack | Secondary$ True"
|
||||||
+ "| TriggerDescription$ Storm (" + inst.getReminderText() + ")";
|
+ "| TriggerDescription$ Storm (" + inst.getReminderText() + ")";
|
||||||
@@ -3773,6 +3764,8 @@ public class CardFactoryUtil {
|
|||||||
desc = "Basic land";
|
desc = "Basic land";
|
||||||
} else if (type.equals("Land.Artifact")) {
|
} else if (type.equals("Land.Artifact")) {
|
||||||
desc = "Artifact land";
|
desc = "Artifact land";
|
||||||
|
} else if (type.startsWith("Card.with")) {
|
||||||
|
desc = type.substring(9);
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(" Discard<1/CARDNAME> | ActivationZone$ Hand | PrecostDesc$ ").append(desc).append("cycling ");
|
sb.append(" Discard<1/CARDNAME> | ActivationZone$ Hand | PrecostDesc$ ").append(desc).append("cycling ");
|
||||||
@@ -3793,17 +3786,20 @@ public class CardFactoryUtil {
|
|||||||
if (keyword.startsWith("Affinity")) {
|
if (keyword.startsWith("Affinity")) {
|
||||||
final String[] k = keyword.split(":");
|
final String[] k = keyword.split(":");
|
||||||
final String t = k[1];
|
final String t = k[1];
|
||||||
String d = "";
|
|
||||||
if (k.length > 2) {
|
|
||||||
final StringBuilder s = new StringBuilder();
|
|
||||||
s.append(k[2]).append("s");
|
|
||||||
d = s.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String desc = "Artifact".equals(t) ? "artifacts" : CardType.getPluralType(t);
|
String desc;
|
||||||
if (!d.isEmpty()) {
|
if(k.length > 2) {
|
||||||
desc = d;
|
String typeText = k[2];
|
||||||
|
if(typeText.contains(" with "))
|
||||||
|
desc = typeText.substring(typeText.indexOf(" with ") + 6);
|
||||||
|
else
|
||||||
|
desc = typeText + "s";
|
||||||
}
|
}
|
||||||
|
else if ("Artifact".equals(t))
|
||||||
|
desc = "artifacts";
|
||||||
|
else
|
||||||
|
desc = CardType.getPluralType(t);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ AffinityX | EffectZone$ All");
|
sb.append("Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ AffinityX | EffectZone$ All");
|
||||||
sb.append("| Description$ Affinity for ").append(desc);
|
sb.append("| Description$ Affinity for ").append(desc);
|
||||||
|
|||||||
@@ -491,6 +491,7 @@ public class CardView extends GameEntityView {
|
|||||||
}
|
}
|
||||||
void updateCurrentRoom(Card c) {
|
void updateCurrentRoom(Card c) {
|
||||||
set(TrackableProperty.CurrentRoom, c.getCurrentRoom());
|
set(TrackableProperty.CurrentRoom, c.getCurrentRoom());
|
||||||
|
updateMarkerText(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIntensity() {
|
public int getIntensity() {
|
||||||
@@ -514,6 +515,7 @@ public class CardView extends GameEntityView {
|
|||||||
}
|
}
|
||||||
void updateClassLevel(Card c) {
|
void updateClassLevel(Card c) {
|
||||||
set(TrackableProperty.ClassLevel, c.getClassLevel());
|
set(TrackableProperty.ClassLevel, c.getClassLevel());
|
||||||
|
updateMarkerText(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRingLevel() {
|
public int getRingLevel() {
|
||||||
@@ -525,6 +527,41 @@ public class CardView extends GameEntityView {
|
|||||||
set(TrackableProperty.RingLevel, p.getNumRingTemptedYou());
|
set(TrackableProperty.RingLevel, p.getNumRingTemptedYou());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOverlayText() {
|
||||||
|
return get(TrackableProperty.OverlayText);
|
||||||
|
}
|
||||||
|
public List<String> getMarkerText() {
|
||||||
|
return get(TrackableProperty.MarkerText);
|
||||||
|
}
|
||||||
|
void updateMarkerText(Card c) {
|
||||||
|
List<String> markerItems = new ArrayList<>();
|
||||||
|
if(c.getCurrentRoom() != null && !c.getCurrentRoom().isEmpty()) {
|
||||||
|
markerItems.add("In Room:");
|
||||||
|
markerItems.add(c.getCurrentRoom());
|
||||||
|
}
|
||||||
|
if(c.isClassCard() && c.isInZone(ZoneType.Battlefield)) {
|
||||||
|
markerItems.add("CL:" + c.getClassLevel());
|
||||||
|
}
|
||||||
|
if(getRingLevel() > 0) {
|
||||||
|
markerItems.add("RL:" + getRingLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(StringUtils.isNotEmpty(c.getOverlayText())) {
|
||||||
|
set(TrackableProperty.OverlayText, c.getOverlayText());
|
||||||
|
markerItems.add(c.getOverlayText());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//Overlay text is any custom string. It gets mixed in with the other marker lines, but it also needs its
|
||||||
|
//own property so that it can display in the card detail text.
|
||||||
|
set(TrackableProperty.OverlayText, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(markerItems.isEmpty())
|
||||||
|
set(TrackableProperty.MarkerText, null);
|
||||||
|
else
|
||||||
|
set(TrackableProperty.MarkerText, markerItems);
|
||||||
|
}
|
||||||
|
|
||||||
private String getRemembered() {
|
private String getRemembered() {
|
||||||
return get(TrackableProperty.Remembered);
|
return get(TrackableProperty.Remembered);
|
||||||
}
|
}
|
||||||
@@ -974,6 +1011,7 @@ public class CardView extends GameEntityView {
|
|||||||
updateDamage(c);
|
updateDamage(c);
|
||||||
updateSpecialize(c);
|
updateSpecialize(c);
|
||||||
updateRingLevel(c);
|
updateRingLevel(c);
|
||||||
|
updateMarkerText(c);
|
||||||
|
|
||||||
if (c.getIntensity(false) > 0) {
|
if (c.getIntensity(false) > 0) {
|
||||||
updateIntensity(c);
|
updateIntensity(c);
|
||||||
@@ -1603,7 +1641,9 @@ public class CardView extends GameEntityView {
|
|||||||
}
|
}
|
||||||
void updateKeywords(Card c, CardState state) {
|
void updateKeywords(Card c, CardState state) {
|
||||||
c.updateKeywordsCache(state);
|
c.updateKeywordsCache(state);
|
||||||
|
// deeper check for Idris
|
||||||
set(TrackableProperty.HasAnnihilator, c.hasKeyword(Keyword.ANNIHILATOR, state) || state.getTriggers().anyMatch(t -> t.isKeyword(Keyword.ANNIHILATOR)));
|
set(TrackableProperty.HasAnnihilator, c.hasKeyword(Keyword.ANNIHILATOR, state) || state.getTriggers().anyMatch(t -> t.isKeyword(Keyword.ANNIHILATOR)));
|
||||||
|
set(TrackableProperty.HasWard, c.hasKeyword(Keyword.WARD, state) || state.getTriggers().anyMatch(t -> t.isKeyword(Keyword.WARD)));
|
||||||
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
|
set(TrackableProperty.HasDeathtouch, c.hasKeyword(Keyword.DEATHTOUCH, state));
|
||||||
set(TrackableProperty.HasToxic, c.hasKeyword(Keyword.TOXIC, state));
|
set(TrackableProperty.HasToxic, c.hasKeyword(Keyword.TOXIC, state));
|
||||||
set(TrackableProperty.HasDevoid, c.hasKeyword(Keyword.DEVOID, state));
|
set(TrackableProperty.HasDevoid, c.hasKeyword(Keyword.DEVOID, state));
|
||||||
@@ -1617,7 +1657,6 @@ public class CardView extends GameEntityView {
|
|||||||
set(TrackableProperty.HasFear, c.hasKeyword(Keyword.FEAR, state));
|
set(TrackableProperty.HasFear, c.hasKeyword(Keyword.FEAR, state));
|
||||||
set(TrackableProperty.HasHexproof, c.hasKeyword(Keyword.HEXPROOF, state));
|
set(TrackableProperty.HasHexproof, c.hasKeyword(Keyword.HEXPROOF, state));
|
||||||
set(TrackableProperty.HasHorsemanship, c.hasKeyword(Keyword.HORSEMANSHIP, state));
|
set(TrackableProperty.HasHorsemanship, c.hasKeyword(Keyword.HORSEMANSHIP, state));
|
||||||
set(TrackableProperty.HasWard, c.hasKeyword(Keyword.WARD, state) || state.getTriggers().anyMatch(t -> t.isKeyword(Keyword.WARD)));
|
|
||||||
set(TrackableProperty.HasWither, c.hasKeyword(Keyword.WITHER, state));
|
set(TrackableProperty.HasWither, c.hasKeyword(Keyword.WITHER, state));
|
||||||
set(TrackableProperty.HasIndestructible, c.hasKeyword(Keyword.INDESTRUCTIBLE, state));
|
set(TrackableProperty.HasIndestructible, c.hasKeyword(Keyword.INDESTRUCTIBLE, state));
|
||||||
set(TrackableProperty.HasIntimidate, c.hasKeyword(Keyword.INTIMIDATE, state));
|
set(TrackableProperty.HasIntimidate, c.hasKeyword(Keyword.INTIMIDATE, state));
|
||||||
|
|||||||
@@ -95,8 +95,6 @@ public enum CounterEnumType {
|
|||||||
|
|
||||||
CORRUPTION("CRPTN", 210, 121, 210),
|
CORRUPTION("CRPTN", 210, 121, 210),
|
||||||
|
|
||||||
CRANK("CRANK!", 181, 148, 15),
|
|
||||||
|
|
||||||
CROAK("CROAK", 155, 255, 5),
|
CROAK("CROAK", 155, 255, 5),
|
||||||
|
|
||||||
CREDIT("CRDIT", 188, 197, 234),
|
CREDIT("CRDIT", 188, 197, 234),
|
||||||
|
|||||||
@@ -648,8 +648,7 @@ public class Combat {
|
|||||||
missingCombatants.add(c);
|
missingCombatants.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ee.getKey() instanceof Card) {
|
if (ee.getKey() instanceof Card c) {
|
||||||
Card c = (Card) ee.getKey();
|
|
||||||
if (!c.isBattle() && !c.isPlaneswalker()) {
|
if (!c.isBattle() && !c.isPlaneswalker()) {
|
||||||
missingCombatants.add(c);
|
missingCombatants.add(c);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -374,7 +374,6 @@ public class CombatUtil {
|
|||||||
final GameEntity defender = combat.getDefenderByAttacker(c);
|
final GameEntity defender = combat.getDefenderByAttacker(c);
|
||||||
final List<Card> otherAttackers = combat.getAttackers();
|
final List<Card> otherAttackers = combat.getAttackers();
|
||||||
|
|
||||||
// Run triggers
|
|
||||||
if (triggers) {
|
if (triggers) {
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
final Map<AbilityKey, Object> runParams = AbilityKey.newMap();
|
||||||
runParams.put(AbilityKey.Attacker, c);
|
runParams.put(AbilityKey.Attacker, c);
|
||||||
|
|||||||
@@ -547,18 +547,6 @@ public class CostAdjustment {
|
|||||||
if (!sa.isActivatedAbility() || sa.isReplacementAbility()) {
|
if (!sa.isActivatedAbility() || sa.isReplacementAbility()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (st.hasParam("OnlyFirstActivation")) {
|
|
||||||
int times = 0;
|
|
||||||
for (IndividualCostPaymentInstance i : game.costPaymentStack) {
|
|
||||||
SpellAbility paymentSa = i.getPayment().getAbility();
|
|
||||||
if (paymentSa.isActivatedAbility() && st.matchesValidParam("ValidCard", paymentSa.getHostCard())) {
|
|
||||||
times++;
|
|
||||||
if (times > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case "Foretell" -> {
|
case "Foretell" -> {
|
||||||
if (!sa.isForetelling()) {
|
if (!sa.isForetelling()) {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package forge.game.event;
|
||||||
|
|
||||||
|
import forge.game.player.Player;
|
||||||
|
|
||||||
|
public class GameEventSpeedChanged extends GameEvent {
|
||||||
|
|
||||||
|
public final Player player;
|
||||||
|
public final int oldValue;
|
||||||
|
public final int newValue;
|
||||||
|
|
||||||
|
public GameEventSpeedChanged(Player affected, int oldValue, int newValue) {
|
||||||
|
player = affected;
|
||||||
|
this.oldValue = oldValue;
|
||||||
|
this.newValue = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package forge.game.event;
|
|
||||||
|
|
||||||
public class GameEventSpeedUp extends GameEvent {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T visit(IGameEventVisitor<T> visitor) {
|
|
||||||
return visitor.visit(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -44,7 +44,7 @@ public interface IGameEventVisitor<T> {
|
|||||||
T visit(GameEventRollDie event);
|
T visit(GameEventRollDie event);
|
||||||
T visit(GameEventScry event);
|
T visit(GameEventScry event);
|
||||||
T visit(GameEventShuffle event);
|
T visit(GameEventShuffle event);
|
||||||
T visit(GameEventSpeedUp event);
|
T visit(GameEventSpeedChanged event);
|
||||||
T visit(GameEventSpellAbilityCast event);
|
T visit(GameEventSpellAbilityCast event);
|
||||||
T visit(GameEventSpellResolved event);
|
T visit(GameEventSpellResolved event);
|
||||||
T visit(GameEventSpellRemovedFromStack event);
|
T visit(GameEventSpellRemovedFromStack event);
|
||||||
@@ -101,7 +101,7 @@ public interface IGameEventVisitor<T> {
|
|||||||
public T visit(GameEventRollDie event) { return null; }
|
public T visit(GameEventRollDie event) { return null; }
|
||||||
public T visit(GameEventScry event) { return null; }
|
public T visit(GameEventScry event) { return null; }
|
||||||
public T visit(GameEventShuffle event) { return null; }
|
public T visit(GameEventShuffle event) { return null; }
|
||||||
public T visit(GameEventSpeedUp event) { return null; }
|
public T visit(GameEventSpeedChanged event) { return null; }
|
||||||
public T visit(GameEventSpellResolved event) { return null; }
|
public T visit(GameEventSpellResolved event) { return null; }
|
||||||
public T visit(GameEventSpellAbilityCast event) { return null; }
|
public T visit(GameEventSpellAbilityCast event) { return null; }
|
||||||
public T visit(GameEventSpellRemovedFromStack event) { return null; }
|
public T visit(GameEventSpellRemovedFromStack event) { return null; }
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ public class KeywordWithCostAndType extends KeywordInstance<KeywordWithCostAndTy
|
|||||||
n[i] = "basic land";
|
n[i] = "basic land";
|
||||||
} else if (n[i].equals("Land.Artifact")) {
|
} else if (n[i].equals("Land.Artifact")) {
|
||||||
n[i] = "artifact land";
|
n[i] = "artifact land";
|
||||||
|
} else if (n[i].startsWith("Card.with")) {
|
||||||
|
n[i] = n[i].substring(9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import forge.game.replacement.ReplacementResult;
|
|||||||
import forge.game.replacement.ReplacementType;
|
import forge.game.replacement.ReplacementType;
|
||||||
|
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
import forge.game.staticability.StaticAbilityNoCleanupDamage;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
import forge.game.zone.Zone;
|
import forge.game.zone.Zone;
|
||||||
@@ -180,8 +181,6 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
}
|
}
|
||||||
playerTurn.incrementTurn();
|
playerTurn.incrementTurn();
|
||||||
|
|
||||||
game.getAction().resetActivationsPerTurn();
|
|
||||||
|
|
||||||
final int lands = CardLists.count(playerTurn.getLandsInPlay(), CardPredicates.UNTAPPED);
|
final int lands = CardLists.count(playerTurn.getLandsInPlay(), CardPredicates.UNTAPPED);
|
||||||
playerTurn.setNumPowerSurgeLands(lands);
|
playerTurn.setNumPowerSurgeLands(lands);
|
||||||
}
|
}
|
||||||
@@ -395,7 +394,10 @@ public class PhaseHandler implements java.io.Serializable {
|
|||||||
// Rule 514.2
|
// Rule 514.2
|
||||||
// Reset Damage received map
|
// Reset Damage received map
|
||||||
for (final Card c : game.getCardsIncludePhasingIn(ZoneType.Battlefield)) {
|
for (final Card c : game.getCardsIncludePhasingIn(ZoneType.Battlefield)) {
|
||||||
c.onCleanupPhase(playerTurn);
|
if (!StaticAbilityNoCleanupDamage.damageNotRemoved(c)) {
|
||||||
|
c.setDamage(0);
|
||||||
|
}
|
||||||
|
c.setHasBeenDealtDeathtouchDamage(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
game.getEndOfTurn().executeUntil();
|
game.getEndOfTurn().executeUntil();
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
private int expentThisTurn;
|
private int expentThisTurn;
|
||||||
private int numLibrarySearchedOwn; //The number of times this player has searched his library
|
private int numLibrarySearchedOwn; //The number of times this player has searched his library
|
||||||
private int venturedThisTurn;
|
private int venturedThisTurn;
|
||||||
|
private int attractionsVisitedThisTurn;
|
||||||
private int descended;
|
private int descended;
|
||||||
private int numRingTemptedYou;
|
private int numRingTemptedYou;
|
||||||
private int devotionMod;
|
private int devotionMod;
|
||||||
@@ -189,7 +190,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
private final Map<Card, Integer> commanderCast = Maps.newHashMap();
|
private final Map<Card, Integer> commanderCast = Maps.newHashMap();
|
||||||
private final Map<Card, Integer> commanderDamage = Maps.newHashMap();
|
private final Map<Card, Integer> commanderDamage = Maps.newHashMap();
|
||||||
private DetachedCardEffect commanderEffect = null;
|
private DetachedCardEffect commanderEffect = null;
|
||||||
private DetachedCardEffect speedEffect;
|
|
||||||
|
|
||||||
private Card monarchEffect;
|
private Card monarchEffect;
|
||||||
private Card initiativeEffect;
|
private Card initiativeEffect;
|
||||||
@@ -197,6 +197,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
private Card contraptionSprocketEffect;
|
private Card contraptionSprocketEffect;
|
||||||
private Card radiationEffect;
|
private Card radiationEffect;
|
||||||
private Card keywordEffect;
|
private Card keywordEffect;
|
||||||
|
private Card speedEffect;
|
||||||
|
|
||||||
private Map<Long, Integer> additionalVotes = Maps.newHashMap();
|
private Map<Long, Integer> additionalVotes = Maps.newHashMap();
|
||||||
private Map<Long, Integer> additionalOptionalVotes = Maps.newHashMap();
|
private Map<Long, Integer> additionalOptionalVotes = Maps.newHashMap();
|
||||||
@@ -237,10 +238,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return achievementTracker;
|
return achievementTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final PlayerOutcome getOutcome() {
|
|
||||||
return stats.getOutcome();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String chooseName(String originalName) {
|
private String chooseName(String originalName) {
|
||||||
String nameCandidate = originalName;
|
String nameCandidate = originalName;
|
||||||
for (int i = 2; i <= 8; i++) { // several tries, not matter how many
|
for (int i = 2; i <= 8; i++) { // several tries, not matter how many
|
||||||
@@ -1333,6 +1330,25 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return drawn;
|
return drawn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void resetNumDrawnThisDrawStep() {
|
||||||
|
numDrawnThisDrawStep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void resetNumDrawnThisTurn() {
|
||||||
|
numDrawnThisTurn = 0;
|
||||||
|
view.updateNumDrawnThisTurn(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getNumDrawnThisTurn() {
|
||||||
|
return numDrawnThisTurn;
|
||||||
|
}
|
||||||
|
public final int getNumDrawnLastTurn() {
|
||||||
|
return numDrawnLastTurn;
|
||||||
|
}
|
||||||
|
public final int numDrawnThisDrawStep() {
|
||||||
|
return numDrawnThisDrawStep;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns PlayerZone corresponding to the given zone of game.
|
* Returns PlayerZone corresponding to the given zone of game.
|
||||||
*/
|
*/
|
||||||
@@ -1360,7 +1376,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public final CardCollectionView getCardsIn(final ZoneType zoneType) {
|
public final CardCollectionView getCardsIn(final ZoneType zoneType) {
|
||||||
return getCardsIn(zoneType, true);
|
return getCardsIn(zoneType, true);
|
||||||
}
|
}
|
||||||
@@ -1448,35 +1463,12 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return CardCollection.combine(getCardsIn(Player.ALL_ZONES), getCardsIn(ZoneType.Stack), inboundTokens);
|
return CardCollection.combine(getCardsIn(Player.ALL_ZONES), getCardsIn(ZoneType.Stack), inboundTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void resetNumDrawnThisDrawStep() {
|
|
||||||
numDrawnThisDrawStep = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void resetNumDrawnThisTurn() {
|
|
||||||
numDrawnThisTurn = 0;
|
|
||||||
view.updateNumDrawnThisTurn(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getNumDrawnThisTurn() {
|
|
||||||
return numDrawnThisTurn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getNumDrawnLastTurn() {
|
|
||||||
return numDrawnLastTurn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int numDrawnThisDrawStep() {
|
|
||||||
return numDrawnThisDrawStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void resetNumRollsThisTurn() {
|
public final void resetNumRollsThisTurn() {
|
||||||
numRollsThisTurn = 0;
|
numRollsThisTurn = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getNumRollsThisTurn() {
|
public final int getNumRollsThisTurn() {
|
||||||
return numRollsThisTurn;
|
return numRollsThisTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void roll() {
|
public void roll() {
|
||||||
numRollsThisTurn++;
|
numRollsThisTurn++;
|
||||||
}
|
}
|
||||||
@@ -1717,15 +1709,16 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, cause);
|
final Card c = game.getAction().moveTo(getZone(ZoneType.Battlefield), land, cause);
|
||||||
game.updateLastStateForCard(c);
|
game.updateLastStateForCard(c);
|
||||||
|
|
||||||
// play a sound
|
|
||||||
game.fireEvent(new GameEventLandPlayed(this, land));
|
|
||||||
|
|
||||||
// Run triggers
|
// Run triggers
|
||||||
runParams.put(AbilityKey.SpellAbility, cause);
|
runParams.put(AbilityKey.SpellAbility, cause);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.LandPlayed, runParams, false);
|
||||||
|
|
||||||
game.getStack().unfreezeStack();
|
game.getStack().unfreezeStack();
|
||||||
addLandPlayedThisTurn();
|
addLandPlayedThisTurn();
|
||||||
|
|
||||||
|
// play a sound
|
||||||
|
game.fireEvent(new GameEventLandPlayed(this, land));
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1878,6 +1871,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
stats.nextTurn();
|
stats.nextTurn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final int getLastTurnNr() {
|
||||||
|
return this.lastTurnNr;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasTappedLandForManaThisTurn() {
|
public boolean hasTappedLandForManaThisTurn() {
|
||||||
return tappedLandForManaThisTurn;
|
return tappedLandForManaThisTurn;
|
||||||
}
|
}
|
||||||
@@ -1949,35 +1946,23 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
completedDungeons.clear();
|
completedDungeons.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getNumRingTemptedYou() {
|
|
||||||
return numRingTemptedYou;
|
|
||||||
}
|
|
||||||
public final void incrementRingTemptedYou() {
|
|
||||||
numRingTemptedYou++;
|
|
||||||
}
|
|
||||||
public final void setNumRingTemptedYou(int value) {
|
|
||||||
numRingTemptedYou = value;
|
|
||||||
}
|
|
||||||
public final void resetRingTemptedYou() {
|
|
||||||
numRingTemptedYou = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int getSpeed() {
|
public final int getSpeed() {
|
||||||
return speed;
|
return speed;
|
||||||
}
|
}
|
||||||
public final void increaseSpeed() {
|
public final void increaseSpeed() {
|
||||||
if (speedEffect == null) createSpeedEffect();
|
|
||||||
if (!maxSpeed()) { // can't increase past 4
|
if (!maxSpeed()) { // can't increase past 4
|
||||||
|
int old = speed;
|
||||||
speed++;
|
speed++;
|
||||||
view.updateSpeed(this);
|
getGame().fireEvent(new GameEventSpeedChanged(this, old, speed)); //play sound effect
|
||||||
game.fireEvent(new GameEventSpeedUp()); //play sound effect
|
updateSpeedEffect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public final void decreaseSpeed() {
|
public final void decreaseSpeed() {
|
||||||
if (speed > 1) { // can't decrease speed below 1
|
if (speed > 1) { // can't decrease speed below 1
|
||||||
|
int old = speed;
|
||||||
speed--;
|
speed--;
|
||||||
view.updateSpeed(this);
|
game.fireEvent(new GameEventSpeedChanged(this, old, speed));
|
||||||
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
|
updateSpeedEffect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public final boolean noSpeed() {
|
public final boolean noSpeed() {
|
||||||
@@ -1988,24 +1973,62 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
public final void setSpeed(int i) { //just used for copy/save
|
public final void setSpeed(int i) { //just used for copy/save
|
||||||
speed = i;
|
speed = i;
|
||||||
if (speed > 0) view.updateSpeed(this);
|
if(this.speedEffect != null)
|
||||||
|
updateSpeedEffect();
|
||||||
}
|
}
|
||||||
public final void createSpeedEffect() {
|
public final void createSpeedEffect() {
|
||||||
final PlayerZone com = getZone(ZoneType.Command);
|
if(this.speedEffect != null || this.noSpeed())
|
||||||
DetachedCardEffect eff = new DetachedCardEffect(this, "Speed Effect");
|
return;
|
||||||
String trigger = "Mode$ LifeLost | ValidPlayer$ Opponent | TriggerZones$ Command | ActivationLimit$ 1 | " +
|
|
||||||
"PlayerTurn$ True | TriggerDescription$ Your speed increases once on each of your turns when an " +
|
|
||||||
"opponent loses life.";
|
|
||||||
String speedUp = "DB$ ChangeSpeed";
|
|
||||||
Trigger lifeLostTrigger = TriggerHandler.parseTrigger(trigger, eff, true);
|
|
||||||
lifeLostTrigger.setOverridingAbility(AbilityFactory.getAbility(speedUp, eff));
|
|
||||||
eff.addTrigger(lifeLostTrigger);
|
|
||||||
this.speedEffect = eff;
|
|
||||||
com.add(eff);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final List<Card> getPlaneswalkedToThisTurn() {
|
speedEffect = new Card(game.nextCardId(), null, game);
|
||||||
return planeswalkedToThisTurn;
|
speedEffect.setOwner(this);
|
||||||
|
speedEffect.setGamePieceType(GamePieceType.EFFECT);
|
||||||
|
|
||||||
|
speedEffect.addAlternateState(CardStateName.Flipped, false);
|
||||||
|
CardState speedFront = speedEffect.getState(CardStateName.Original);
|
||||||
|
CardState speedBack = speedEffect.getState(CardStateName.Flipped);
|
||||||
|
|
||||||
|
speedFront.setImageKey("t:speed");
|
||||||
|
speedFront.setName("Start Your Engines!");
|
||||||
|
|
||||||
|
speedBack.setImageKey("t:max_speed");
|
||||||
|
speedBack.setName("Max Speed!");
|
||||||
|
|
||||||
|
String label = Localizer.getInstance().getMessage("lblSpeed", this.speed);
|
||||||
|
speedEffect.setOverlayText(label);
|
||||||
|
|
||||||
|
// 702.179d There is an inherent triggered ability associated with a player having 1 or more speed. This ability has no source and is controlled by that player.
|
||||||
|
// That ability is “Whenever one or more opponents lose life during your turn, if your speed is less than 4, your speed increases by 1. This ability triggers only once each turn.”
|
||||||
|
String trigger = "Mode$ LifeLostAll | ValidPlayer$ Opponent | TriggerZones$ Command | ActivationLimit$ 1 | " +
|
||||||
|
"PlayerTurn$ True | CheckSVar$ Count$YourSpeed | SVarCompare$ LT4 | "
|
||||||
|
+ "TriggerDescription$ Whenever one or more opponents lose life during your turn, if your speed is less than 4, your speed increases by 1. This ability triggers only once each turn.";
|
||||||
|
String speedUp = "DB$ ChangeSpeed";
|
||||||
|
Trigger lifeLostTrigger = TriggerHandler.parseTrigger(trigger, speedEffect, true);
|
||||||
|
lifeLostTrigger.setOverridingAbility(AbilityFactory.getAbility(speedUp, speedEffect));
|
||||||
|
speedFront.addTrigger(lifeLostTrigger);
|
||||||
|
|
||||||
|
speedEffect.updateStateForView();
|
||||||
|
|
||||||
|
if(this.maxSpeed())
|
||||||
|
speedEffect.setState(CardStateName.Flipped, true);
|
||||||
|
|
||||||
|
final PlayerZone com = getZone(ZoneType.Command);
|
||||||
|
com.add(speedEffect);
|
||||||
|
this.updateZoneForView(com);
|
||||||
|
}
|
||||||
|
protected void updateSpeedEffect() {
|
||||||
|
if(this.speedEffect == null) {
|
||||||
|
if(this.noSpeed())
|
||||||
|
return;
|
||||||
|
createSpeedEffect();
|
||||||
|
}
|
||||||
|
Localizer localizer = Localizer.getInstance();
|
||||||
|
String label = this.maxSpeed() ? localizer.getMessage("lblMaxSpeed") : localizer.getMessage("lblSpeed", this.speed);
|
||||||
|
speedEffect.setOverlayText(label);
|
||||||
|
if(maxSpeed() && speedEffect.getCurrentStateName() == CardStateName.Original)
|
||||||
|
speedEffect.setState(CardStateName.Flipped, true);
|
||||||
|
else if(!maxSpeed() && speedEffect.getCurrentStateName() == CardStateName.Flipped)
|
||||||
|
speedEffect.setState(CardStateName.Original, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void altWinBySpellEffect(final String sourceName) {
|
public final void altWinBySpellEffect(final String sourceName) {
|
||||||
@@ -2285,6 +2308,13 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
view.updateUnlimitedHandSize(this);
|
view.updateUnlimitedHandSize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getStartingHandSize() {
|
||||||
|
return startingHandSize;
|
||||||
|
}
|
||||||
|
public void setStartingHandSize(int shs) {
|
||||||
|
startingHandSize = shs;
|
||||||
|
}
|
||||||
|
|
||||||
public final int getLandsPlayedThisTurn() {
|
public final int getLandsPlayedThisTurn() {
|
||||||
return landsPlayedThisTurn;
|
return landsPlayedThisTurn;
|
||||||
}
|
}
|
||||||
@@ -2461,6 +2491,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return game.getMatch().getPlayers().get(game.getRegisteredPlayers().indexOf(this));
|
return game.getMatch().getPlayers().get(game.getRegisteredPlayers().indexOf(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final PlayerOutcome getOutcome() {
|
||||||
|
return stats.getOutcome();
|
||||||
|
}
|
||||||
private void setOutcome(PlayerOutcome outcome) {
|
private void setOutcome(PlayerOutcome outcome) {
|
||||||
stats.setOutcome(outcome);
|
stats.setOutcome(outcome);
|
||||||
}
|
}
|
||||||
@@ -2519,10 +2552,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return keywords.getAmount(k);
|
return keywords.getAmount(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getLastTurnNr() {
|
|
||||||
return this.lastTurnNr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCleanupPhase() {
|
public void onCleanupPhase() {
|
||||||
for (Card c : getCardsIn(ZoneType.Hand)) {
|
for (Card c : getCardsIn(ZoneType.Hand)) {
|
||||||
c.setDrawnThisTurn(false);
|
c.setDrawnThisTurn(false);
|
||||||
@@ -2561,6 +2590,7 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
setCommitedCrimeThisTurn(0);
|
setCommitedCrimeThisTurn(0);
|
||||||
diceRollsThisTurn = Lists.newArrayList();
|
diceRollsThisTurn = Lists.newArrayList();
|
||||||
setExpentThisTurn(0);
|
setExpentThisTurn(0);
|
||||||
|
attractionsVisitedThisTurn = 0;
|
||||||
|
|
||||||
damageReceivedThisTurn.clear();
|
damageReceivedThisTurn.clear();
|
||||||
planeswalkedToThisTurn.clear();
|
planeswalkedToThisTurn.clear();
|
||||||
@@ -2693,11 +2723,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return !isInGame();
|
return !isInGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStartingHandSize() {
|
public final List<Card> getPlaneswalkedToThisTurn() {
|
||||||
return startingHandSize;
|
return planeswalkedToThisTurn;
|
||||||
}
|
|
||||||
public void setStartingHandSize(int shs) {
|
|
||||||
startingHandSize = shs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3386,6 +3413,19 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
getTheRing().updateStateForView();
|
getTheRing().updateStateForView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final int getNumRingTemptedYou() {
|
||||||
|
return numRingTemptedYou;
|
||||||
|
}
|
||||||
|
public final void incrementRingTemptedYou() {
|
||||||
|
numRingTemptedYou++;
|
||||||
|
}
|
||||||
|
public final void setNumRingTemptedYou(int value) {
|
||||||
|
numRingTemptedYou = value;
|
||||||
|
}
|
||||||
|
public final void resetRingTemptedYou() {
|
||||||
|
numRingTemptedYou = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public void changeOwnership(Card card) {
|
public void changeOwnership(Card card) {
|
||||||
// If lost then gained, just clear out of lost.
|
// If lost then gained, just clear out of lost.
|
||||||
// If gained then lost, just clear out of gained.
|
// If gained then lost, just clear out of gained.
|
||||||
@@ -3610,12 +3650,31 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return radiationEffect != null;
|
return radiationEffect != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Card getKeywordCard() {
|
||||||
|
if (keywordEffect != null) {
|
||||||
|
return keywordEffect;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PlayerZone com = getZone(ZoneType.Command);
|
||||||
|
|
||||||
|
keywordEffect = new Card(game.nextCardId(), null, game);
|
||||||
|
keywordEffect.setGamePieceType(GamePieceType.EFFECT);
|
||||||
|
keywordEffect.setOwner(this);
|
||||||
|
keywordEffect.setName("Keyword Effects");
|
||||||
|
keywordEffect.setImageKey(ImageKeys.HIDDEN_CARD);
|
||||||
|
|
||||||
|
keywordEffect.updateStateForView();
|
||||||
|
|
||||||
|
com.add(keywordEffect);
|
||||||
|
|
||||||
|
this.updateZoneForView(com);
|
||||||
|
return keywordEffect;
|
||||||
|
}
|
||||||
public void updateKeywordCardAbilityText() {
|
public void updateKeywordCardAbilityText() {
|
||||||
if (getKeywordCard() == null)
|
if (getKeywordCard() == null)
|
||||||
return;
|
return;
|
||||||
final PlayerZone com = getZone(ZoneType.Command);
|
final PlayerZone com = getZone(ZoneType.Command);
|
||||||
keywordEffect.setText("");
|
keywordEffect.setText("");
|
||||||
keywordEffect.updateAbilityTextForView();
|
|
||||||
boolean headerAdded = false;
|
boolean headerAdded = false;
|
||||||
StringBuilder kw = new StringBuilder();
|
StringBuilder kw = new StringBuilder();
|
||||||
for (KeywordInterface k : keywords) {
|
for (KeywordInterface k : keywords) {
|
||||||
@@ -3627,8 +3686,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
}
|
}
|
||||||
if (!kw.toString().isEmpty()) {
|
if (!kw.toString().isEmpty()) {
|
||||||
keywordEffect.setText(trimKeywords(kw.toString()));
|
keywordEffect.setText(trimKeywords(kw.toString()));
|
||||||
keywordEffect.updateAbilityTextForView();
|
|
||||||
}
|
}
|
||||||
|
keywordEffect.updateAbilityTextForView();
|
||||||
this.updateZoneForView(com);
|
this.updateZoneForView(com);
|
||||||
}
|
}
|
||||||
public String trimKeywords(String keywordTexts) {
|
public String trimKeywords(String keywordTexts) {
|
||||||
@@ -3686,6 +3745,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
blessingEffect.updateStateForView();
|
blessingEffect.updateStateForView();
|
||||||
|
|
||||||
com.add(blessingEffect);
|
com.add(blessingEffect);
|
||||||
|
|
||||||
|
// 702.131d. After a player gets the city's blessing, continuous effects are reapplied
|
||||||
|
game.getAction().checkStaticAbilities();
|
||||||
} else {
|
} else {
|
||||||
com.remove(blessingEffect);
|
com.remove(blessingEffect);
|
||||||
blessingEffect = null;
|
blessingEffect = null;
|
||||||
@@ -3732,27 +3794,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|| !hasKeyword("Spells and abilities you control can't cause you to search your library.");
|
|| !hasKeyword("Spells and abilities you control can't cause you to search your library.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Card getKeywordCard() {
|
|
||||||
if (keywordEffect != null) {
|
|
||||||
return keywordEffect;
|
|
||||||
}
|
|
||||||
|
|
||||||
final PlayerZone com = getZone(ZoneType.Command);
|
|
||||||
|
|
||||||
keywordEffect = new Card(game.nextCardId(), null, game);
|
|
||||||
keywordEffect.setGamePieceType(GamePieceType.EFFECT);
|
|
||||||
keywordEffect.setOwner(this);
|
|
||||||
keywordEffect.setName("Keyword Effects");
|
|
||||||
keywordEffect.setImageKey(ImageKeys.HIDDEN_CARD);
|
|
||||||
|
|
||||||
keywordEffect.updateStateForView();
|
|
||||||
|
|
||||||
com.add(keywordEffect);
|
|
||||||
|
|
||||||
this.updateZoneForView(com);
|
|
||||||
return keywordEffect;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAdditionalVote(long timestamp, int value) {
|
public void addAdditionalVote(long timestamp, int value) {
|
||||||
additionalVotes.put(timestamp, value);
|
additionalVotes.put(timestamp, value);
|
||||||
getView().updateAdditionalVote(this);
|
getView().updateAdditionalVote(this);
|
||||||
@@ -3943,7 +3984,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
|
|
||||||
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
final Map<AbilityKey, Object> runParams = AbilityKey.mapFromPlayer(this);
|
||||||
game.getTriggerHandler().runTrigger(TriggerType.CommitCrime, runParams, false);
|
game.getTriggerHandler().runTrigger(TriggerType.CommitCrime, runParams, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCommittedCrimeThisTurn() {
|
public int getCommittedCrimeThisTurn() {
|
||||||
@@ -3970,12 +4010,17 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
public void visitAttractions(int light) {
|
public void visitAttractions(int light) {
|
||||||
CardCollection attractions = CardLists.filter(getCardsIn(ZoneType.Battlefield), CardPredicates.isAttractionWithLight(light));
|
CardCollection attractions = CardLists.filter(getCardsIn(ZoneType.Battlefield), CardPredicates.isAttractionWithLight(light));
|
||||||
for (Card c : attractions) {
|
for (Card c : attractions) {
|
||||||
|
if(!c.wasVisitedThisTurn())
|
||||||
|
this.attractionsVisitedThisTurn++;
|
||||||
c.visitAttraction(this);
|
c.visitAttraction(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void rollToVisitAttractions() {
|
public void rollToVisitAttractions() {
|
||||||
this.visitAttractions(RollDiceEffect.rollDiceForPlayerToVisitAttractions(this));
|
this.visitAttractions(RollDiceEffect.rollDiceForPlayerToVisitAttractions(this));
|
||||||
}
|
}
|
||||||
|
public int getAttractionsVisitedThisTurn() {
|
||||||
|
return this.attractionsVisitedThisTurn;
|
||||||
|
}
|
||||||
|
|
||||||
public int getCrankCounter() {
|
public int getCrankCounter() {
|
||||||
return this.crankCounter;
|
return this.crankCounter;
|
||||||
@@ -3983,8 +4028,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
public void setCrankCounter(int counters) {
|
public void setCrankCounter(int counters) {
|
||||||
this.crankCounter = counters;
|
this.crankCounter = counters;
|
||||||
if (this.contraptionSprocketEffect != null) {
|
if (this.contraptionSprocketEffect != null) {
|
||||||
Map<CounterType, Integer> counterMap = Map.of(CounterType.get(CounterEnumType.CRANK), this.crankCounter);
|
String label = Localizer.getInstance().getMessage("lblCrank", this.crankCounter);
|
||||||
contraptionSprocketEffect.setCounters(counterMap);
|
contraptionSprocketEffect.setOverlayText(label);
|
||||||
}
|
}
|
||||||
else if (this.getCardsIn(ZoneType.Battlefield).anyMatch(CardPredicates.CONTRAPTIONS)) {
|
else if (this.getCardsIn(ZoneType.Battlefield).anyMatch(CardPredicates.CONTRAPTIONS)) {
|
||||||
this.createContraptionSprockets();
|
this.createContraptionSprockets();
|
||||||
@@ -4010,12 +4055,8 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
contraptionSprocketEffect.setName("Contraption Sprockets");
|
contraptionSprocketEffect.setName("Contraption Sprockets");
|
||||||
contraptionSprocketEffect.setGamePieceType(GamePieceType.EFFECT);
|
contraptionSprocketEffect.setGamePieceType(GamePieceType.EFFECT);
|
||||||
|
|
||||||
//Add "counters" on the effect to represent the current CRANK counter position.
|
String label = Localizer.getInstance().getMessage("lblCrank", this.crankCounter);
|
||||||
//This and some other un-cards could benefit from a distinct system for positional counters or markers,
|
contraptionSprocketEffect.setOverlayText(label);
|
||||||
//see for instance Baron von Count or B-I-N-G-O. For now this is sufficient to display the current sprocket
|
|
||||||
//on the counter overlay, and I don't think any existing effect will notice it.
|
|
||||||
Map<CounterType, Integer> counterMap = Map.of(CounterType.get(CounterEnumType.CRANK), this.crankCounter);
|
|
||||||
contraptionSprocketEffect.setCounters(counterMap);
|
|
||||||
contraptionSprocketEffect.setText("At the beginning of your upkeep, if you control a Contraption, move the CRANK! counter to the next sprocket and crank any number of that sprocket's Contraptions.");
|
contraptionSprocketEffect.setText("At the beginning of your upkeep, if you control a Contraption, move the CRANK! counter to the next sprocket and crank any number of that sprocket's Contraptions.");
|
||||||
|
|
||||||
contraptionSprocketEffect.updateStateForView();
|
contraptionSprocketEffect.updateStateForView();
|
||||||
@@ -4028,11 +4069,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
public void addDeclaresAttackers(long ts, Player p) {
|
public void addDeclaresAttackers(long ts, Player p) {
|
||||||
this.declaresAttackers.put(ts, p);
|
this.declaresAttackers.put(ts, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDeclaresAttackers(long ts) {
|
public void removeDeclaresAttackers(long ts) {
|
||||||
this.declaresAttackers.remove(ts);
|
this.declaresAttackers.remove(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getDeclaresAttackers() {
|
public Player getDeclaresAttackers() {
|
||||||
Map.Entry<Long, Player> e = declaresAttackers.lastEntry();
|
Map.Entry<Long, Player> e = declaresAttackers.lastEntry();
|
||||||
return e == null ? null : e.getValue();
|
return e == null ? null : e.getValue();
|
||||||
@@ -4041,11 +4080,9 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
public void addDeclaresBlockers(long ts, Player p) {
|
public void addDeclaresBlockers(long ts, Player p) {
|
||||||
this.declaresBlockers.put(ts, p);
|
this.declaresBlockers.put(ts, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDeclaresBlockers(long ts) {
|
public void removeDeclaresBlockers(long ts) {
|
||||||
this.declaresBlockers.remove(ts);
|
this.declaresBlockers.remove(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getDeclaresBlockers() {
|
public Player getDeclaresBlockers() {
|
||||||
Map.Entry<Long, Player> e = declaresBlockers.lastEntry();
|
Map.Entry<Long, Player> e = declaresBlockers.lastEntry();
|
||||||
return e == null ? null : e.getValue();
|
return e == null ? null : e.getValue();
|
||||||
|
|||||||
@@ -329,13 +329,6 @@ public class PlayerView extends GameEntityView {
|
|||||||
set(TrackableProperty.ControlVotes, val);
|
set(TrackableProperty.ControlVotes, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSpeed() {
|
|
||||||
return get(TrackableProperty.Speed);
|
|
||||||
}
|
|
||||||
void updateSpeed(Player p) {
|
|
||||||
set(TrackableProperty.Speed, p.getSpeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAdditionalVillainousChoices() {
|
public int getAdditionalVillainousChoices() {
|
||||||
return get(TrackableProperty.AdditionalVillainousChoices);
|
return get(TrackableProperty.AdditionalVillainousChoices);
|
||||||
}
|
}
|
||||||
@@ -604,10 +597,6 @@ public class PlayerView extends GameEntityView {
|
|||||||
}
|
}
|
||||||
details.add(Localizer.getInstance().getMessage("lblExtraTurnCountHas", String.valueOf(getExtraTurnCount())));
|
details.add(Localizer.getInstance().getMessage("lblExtraTurnCountHas", String.valueOf(getExtraTurnCount())));
|
||||||
|
|
||||||
if (getSpeed() > 0) {
|
|
||||||
details.add(Localizer.getInstance().getMessage("lblSpeed", String.valueOf(getSpeed())));
|
|
||||||
}
|
|
||||||
|
|
||||||
final String keywords = Lang.joinHomogenous(getDisplayableKeywords());
|
final String keywords = Lang.joinHomogenous(getDisplayableKeywords());
|
||||||
if (!keywords.isEmpty()) {
|
if (!keywords.isEmpty()) {
|
||||||
details.add(keywords);
|
details.add(keywords);
|
||||||
|
|||||||
@@ -322,16 +322,13 @@ public class AbilityManaPart implements java.io.Serializable {
|
|||||||
return this.triggersWhenSpent != null;
|
return this.triggersWhenSpent != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTriggersWhenSpent(SpellAbility saBeingPaid, Card card) {
|
public void addTriggersWhenSpent(SpellAbility saBeingPaid) {
|
||||||
if (this.triggersWhenSpent == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
TriggerHandler handler = card.getGame().getTriggerHandler();
|
|
||||||
Trigger trig = TriggerHandler.parseTrigger(sVarHolder.getSVar(this.triggersWhenSpent), sourceCard, false, sVarHolder);
|
Trigger trig = TriggerHandler.parseTrigger(sVarHolder.getSVar(this.triggersWhenSpent), sourceCard, false, sVarHolder);
|
||||||
if (sVarHolder instanceof SpellAbility) {
|
trig.addRemembered(saBeingPaid);
|
||||||
trig.setSpawningAbility((SpellAbility) sVarHolder);
|
if (getSourceSA() != null) {
|
||||||
|
trig.setSpawningAbility(getSourceSA());
|
||||||
}
|
}
|
||||||
handler.registerOneTrigger(trig);
|
saBeingPaid.getHostCard().getGame().getTriggerHandler().registerThisTurnDelayedTrigger(trig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpellAbility getSourceSA() {
|
public SpellAbility getSourceSA() {
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ import forge.game.staticability.StaticAbilityCastWithFlash;
|
|||||||
import forge.game.staticability.StaticAbilityMustTarget;
|
import forge.game.staticability.StaticAbilityMustTarget;
|
||||||
import forge.game.trigger.Trigger;
|
import forge.game.trigger.Trigger;
|
||||||
import forge.game.trigger.TriggerType;
|
import forge.game.trigger.TriggerType;
|
||||||
|
import forge.game.trigger.WrappedAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
//only SpellAbility can go on the stack
|
//only SpellAbility can go on the stack
|
||||||
@@ -837,7 +838,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
|
|
||||||
for (Mana mana : getPayingMana()) {
|
for (Mana mana : getPayingMana()) {
|
||||||
if (mana.triggersWhenSpent()) {
|
if (mana.triggersWhenSpent()) {
|
||||||
mana.getManaAbility().addTriggersWhenSpent(this, host);
|
mana.getManaAbility().addTriggersWhenSpent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mana.addsCounters(this)) {
|
if (mana.addsCounters(this)) {
|
||||||
@@ -1072,11 +1073,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void rebuiltDescription() {
|
public void rebuiltDescription() {
|
||||||
|
|
||||||
// SubAbilities don't have Costs or Cost descriptors
|
// SubAbilities don't have Costs or Cost descriptors
|
||||||
|
|
||||||
String sb = getCostDescription() +
|
String sb = getCostDescription() + getParam("SpellDescription");
|
||||||
getParam("SpellDescription");
|
|
||||||
setDescription(sb);
|
setDescription(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1536,8 +1535,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity instanceof GameEntity) {
|
if (entity instanceof GameEntity e) {
|
||||||
GameEntity e = (GameEntity)entity;
|
|
||||||
if (!e.isValid(tr.getValidTgts(), getActivatingPlayer(), getHostCard(), this)) {
|
if (!e.isValid(tr.getValidTgts(), getActivatingPlayer(), getHostCard(), this)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1546,8 +1544,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity instanceof Card) {
|
if (entity instanceof Card c) {
|
||||||
final Card c = (Card) entity;
|
|
||||||
if (c.getZone() != null && !tr.getZone().contains(c.getZone().getZoneType())) {
|
if (c.getZone() != null && !tr.getZone().contains(c.getZone().getZoneType())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1991,7 +1988,11 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
|
|||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canTargetSpellAbility(final SpellAbility topSA) {
|
public boolean canTargetSpellAbility(SpellAbility topSA) {
|
||||||
|
if (topSA.isWrapper()) {
|
||||||
|
topSA = ((WrappedAbility) topSA).getWrappedAbility();
|
||||||
|
}
|
||||||
|
|
||||||
final TargetRestrictions tgt = getTargetRestrictions();
|
final TargetRestrictions tgt = getTargetRestrictions();
|
||||||
|
|
||||||
if (this.equals(topSA)) {
|
if (this.equals(topSA)) {
|
||||||
|
|||||||
@@ -271,7 +271,6 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
|
|||||||
if (this.isRevolt() && !activator.hasRevolt()) return false;
|
if (this.isRevolt() && !activator.hasRevolt()) return false;
|
||||||
if (this.isDesert() && !activator.hasDesert()) return false;
|
if (this.isDesert() && !activator.hasDesert()) return false;
|
||||||
if (this.isBlessing() && !activator.hasBlessing()) return false;
|
if (this.isBlessing() && !activator.hasBlessing()) return false;
|
||||||
if (this.isMaxSpeed() && !activator.maxSpeed()) return false;
|
|
||||||
|
|
||||||
if (this.kicked && !sa.isKicked()) return false;
|
if (this.kicked && !sa.isKicked()) return false;
|
||||||
if (this.kicked1 && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) return false;
|
if (this.kicked1 && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) return false;
|
||||||
|
|||||||
@@ -98,9 +98,6 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
|||||||
if (value.equals("Solved")) {
|
if (value.equals("Solved")) {
|
||||||
this.setSolved(true);
|
this.setSolved(true);
|
||||||
}
|
}
|
||||||
if (value.equals("MaxSpeed")) {
|
|
||||||
this.setMaxSpeed(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("ActivationZone")) {
|
if (params.containsKey("ActivationZone")) {
|
||||||
@@ -443,9 +440,6 @@ public class SpellAbilityRestriction extends SpellAbilityVariables {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isMaxSpeed()) {
|
|
||||||
if (!activator.maxSpeed()) return false;
|
|
||||||
}
|
|
||||||
if (isBlessing()) {
|
if (isBlessing()) {
|
||||||
if (!activator.hasBlessing()) {
|
if (!activator.hasBlessing()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ public class SpellAbilityVariables implements Cloneable {
|
|||||||
private boolean desert = false;
|
private boolean desert = false;
|
||||||
private boolean blessing = false;
|
private boolean blessing = false;
|
||||||
private boolean solved = false;
|
private boolean solved = false;
|
||||||
private boolean maxSpeed = false;
|
|
||||||
|
|
||||||
/** The s is present. */
|
/** The s is present. */
|
||||||
private String isPresent = null;
|
private String isPresent = null;
|
||||||
@@ -347,7 +346,6 @@ public class SpellAbilityVariables implements Cloneable {
|
|||||||
public void setDesert(final boolean bDesert) { desert = bDesert; }
|
public void setDesert(final boolean bDesert) { desert = bDesert; }
|
||||||
public void setBlessing(final boolean bBlessing) { blessing = bBlessing; }
|
public void setBlessing(final boolean bBlessing) { blessing = bBlessing; }
|
||||||
public void setSolved(final boolean bSolved) { solved = bSolved; }
|
public void setSolved(final boolean bSolved) { solved = bSolved; }
|
||||||
public void setMaxSpeed(final boolean b) { maxSpeed = b; }
|
|
||||||
|
|
||||||
/** Optional Costs */
|
/** Optional Costs */
|
||||||
protected boolean kicked = false;
|
protected boolean kicked = false;
|
||||||
@@ -540,8 +538,6 @@ public class SpellAbilityVariables implements Cloneable {
|
|||||||
|
|
||||||
public final boolean isSolved() { return this.solved; }
|
public final boolean isSolved() { return this.solved; }
|
||||||
|
|
||||||
public final boolean isMaxSpeed() { return this.maxSpeed; }
|
|
||||||
|
|
||||||
public String getNoDifferentColors() {
|
public String getNoDifferentColors() {
|
||||||
return noDifferentColors;
|
return noDifferentColors;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ public class TargetChoices extends ForwardingList<GameObject> implements Cloneab
|
|||||||
|
|
||||||
public final boolean add(final GameObject o) {
|
public final boolean add(final GameObject o) {
|
||||||
if (o instanceof Player || o instanceof Card || o instanceof SpellAbility) {
|
if (o instanceof Player || o instanceof Card || o instanceof SpellAbility) {
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card c) {
|
||||||
Card c = (Card) o;
|
|
||||||
cardControllers.put(c, c.getController());
|
cardControllers.put(c, c.getController());
|
||||||
}
|
}
|
||||||
return super.add(o);
|
return super.add(o);
|
||||||
|
|||||||
@@ -344,10 +344,8 @@ public class StaticAbility extends CardTraitBase implements IIdentifiable, Clone
|
|||||||
if (zone == null || !this.validHostZones.contains(zone.getZoneType())) {
|
if (zone == null || !this.validHostZones.contains(zone.getZoneType())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!getHostCard().isInPlay()) { // default
|
||||||
if (!getHostCard().isInPlay()) { // default
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -452,29 +452,11 @@ public final class StaticAbilityContinuous {
|
|||||||
|
|
||||||
if (layer == StaticAbilityLayer.COLOR) {
|
if (layer == StaticAbilityLayer.COLOR) {
|
||||||
if (params.containsKey("AddColor")) {
|
if (params.containsKey("AddColor")) {
|
||||||
final String colors = params.get("AddColor");
|
addColors = getColorsFromParam(stAb, params.get("AddColor"));
|
||||||
if (colors.equals("ChosenColor")) {
|
|
||||||
if (hostCard.hasChosenColor()) {
|
|
||||||
addColors = ColorSet.fromNames(hostCard.getChosenColors());
|
|
||||||
}
|
|
||||||
} else if (colors.equals("All")) {
|
|
||||||
addColors = ColorSet.ALL_COLORS;
|
|
||||||
} else {
|
|
||||||
addColors = ColorSet.fromNames(colors.split(" & "));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.containsKey("SetColor")) {
|
if (params.containsKey("SetColor")) {
|
||||||
final String colors = params.get("SetColor");
|
addColors = getColorsFromParam(stAb, params.get("SetColor"));
|
||||||
if (colors.equals("ChosenColor")) {
|
|
||||||
if (hostCard.hasChosenColor()) {
|
|
||||||
addColors = ColorSet.fromNames(hostCard.getChosenColors());
|
|
||||||
}
|
|
||||||
} else if (colors.equals("All")) {
|
|
||||||
addColors = ColorSet.ALL_COLORS;
|
|
||||||
} else {
|
|
||||||
addColors = ColorSet.fromNames(colors.split(" & "));
|
|
||||||
}
|
|
||||||
overwriteColors = true;
|
overwriteColors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -704,7 +686,7 @@ public final class StaticAbilityContinuous {
|
|||||||
setToughness = AbilityUtils.calculateAmount(affectedCard, setT, stAb, true);
|
setToughness = AbilityUtils.calculateAmount(affectedCard, setT, stAb, true);
|
||||||
}
|
}
|
||||||
affectedCard.addNewPT(setPower, setToughness,
|
affectedCard.addNewPT(setPower, setToughness,
|
||||||
se.getTimestamp(), stAb.getId(), layer == StaticAbilityLayer.CHARACTERISTIC);
|
se.getTimestamp(), stAb.getId(), layer == StaticAbilityLayer.CHARACTERISTIC, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +734,8 @@ public final class StaticAbilityContinuous {
|
|||||||
}
|
}
|
||||||
|
|
||||||
affectedCard.addChangedCardKeywords(newKeywords, removeKeywords,
|
affectedCard.addChangedCardKeywords(newKeywords, removeKeywords,
|
||||||
removeAllAbilities, se.getTimestamp(), stAb, true);
|
removeAllAbilities, se.getTimestamp(), stAb, false);
|
||||||
|
affectedCard.updateKeywordsCache(affectedCard.getCurrentState());
|
||||||
}
|
}
|
||||||
|
|
||||||
// add HIDDEN keywords
|
// add HIDDEN keywords
|
||||||
@@ -862,7 +845,7 @@ public final class StaticAbilityContinuous {
|
|||||||
|| removeAllAbilities) {
|
|| removeAllAbilities) {
|
||||||
affectedCard.addChangedCardTraits(
|
affectedCard.addChangedCardTraits(
|
||||||
addedAbilities, null, addedTrigger, addedReplacementEffects, addedStaticAbility, removeAllAbilities, removeNonMana,
|
addedAbilities, null, addedTrigger, addedReplacementEffects, addedStaticAbility, removeAllAbilities, removeNonMana,
|
||||||
se.getTimestamp(), stAb.getId()
|
se.getTimestamp(), stAb.getId(), false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,7 +857,7 @@ public final class StaticAbilityContinuous {
|
|||||||
// add Types
|
// add Types
|
||||||
if ((addTypes != null && !addTypes.isEmpty()) || (removeTypes != null && !removeTypes.isEmpty()) || addAllCreatureTypes || !remove.isEmpty()) {
|
if ((addTypes != null && !addTypes.isEmpty()) || (removeTypes != null && !removeTypes.isEmpty()) || addAllCreatureTypes || !remove.isEmpty()) {
|
||||||
affectedCard.addChangedCardTypes(addTypes, removeTypes, addAllCreatureTypes, remove,
|
affectedCard.addChangedCardTypes(addTypes, removeTypes, addAllCreatureTypes, remove,
|
||||||
se.getTimestamp(), stAb.getId(), true, stAb.isCharacteristicDefining());
|
se.getTimestamp(), stAb.getId(), false, stAb.isCharacteristicDefining());
|
||||||
}
|
}
|
||||||
|
|
||||||
// add colors
|
// add colors
|
||||||
@@ -932,6 +915,21 @@ public final class StaticAbilityContinuous {
|
|||||||
return affectedCards;
|
return affectedCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ColorSet getColorsFromParam(StaticAbility stAb, final String colors) {
|
||||||
|
final Card hostCard = stAb.getHostCard();
|
||||||
|
ColorSet addColors = null;
|
||||||
|
if (colors.equals("ChosenColor")) {
|
||||||
|
if (hostCard.hasChosenColor()) {
|
||||||
|
addColors = ColorSet.fromNames(hostCard.getChosenColors());
|
||||||
|
}
|
||||||
|
} else if (colors.equals("All")) {
|
||||||
|
addColors = ColorSet.ALL_COLORS;
|
||||||
|
} else {
|
||||||
|
addColors = ColorSet.fromNames(colors.split(" & "));
|
||||||
|
}
|
||||||
|
return addColors;
|
||||||
|
}
|
||||||
|
|
||||||
private static void buildIgnorEffectAbility(final StaticAbility stAb, final String costString, final List<Player> players, final CardCollectionView cards) {
|
private static void buildIgnorEffectAbility(final StaticAbility stAb, final String costString, final List<Player> players, final CardCollectionView cards) {
|
||||||
final List<Player> validActivator = new ArrayList<>(players);
|
final List<Player> validActivator = new ArrayList<>(players);
|
||||||
for (final Card c : cards) {
|
for (final Card c : cards) {
|
||||||
@@ -1031,10 +1029,18 @@ public final class StaticAbilityContinuous {
|
|||||||
// non - CharacteristicDefining
|
// non - CharacteristicDefining
|
||||||
CardCollection affectedCards = new CardCollection();
|
CardCollection affectedCards = new CardCollection();
|
||||||
|
|
||||||
|
CardCollection definedCards = null;
|
||||||
|
if (stAb.hasParam("AffectedDefined")) {
|
||||||
|
definedCards = AbilityUtils.getDefinedCards(hostCard, stAb.getParam("AffectedDefined"), stAb).filter(CardPredicates.phasedIn());
|
||||||
|
}
|
||||||
|
|
||||||
// add preList in addition to the normal affected cards
|
// add preList in addition to the normal affected cards
|
||||||
// need to add before game cards to have preference over them
|
// need to add before game cards to have preference over them
|
||||||
if (!preList.isEmpty()) {
|
if (!preList.isEmpty()) {
|
||||||
if (stAb.hasParam("AffectedZone")) {
|
if (stAb.hasParam("AffectedDefined")) {
|
||||||
|
affectedCards.addAll(preList);
|
||||||
|
affectedCards.retainAll(definedCards);
|
||||||
|
} else if (stAb.hasParam("AffectedZone")) {
|
||||||
affectedCards.addAll(CardLists.filter(preList, CardPredicates.inZone(
|
affectedCards.addAll(CardLists.filter(preList, CardPredicates.inZone(
|
||||||
ZoneType.listValueOf(stAb.getParam("AffectedZone")))));
|
ZoneType.listValueOf(stAb.getParam("AffectedZone")))));
|
||||||
} else {
|
} else {
|
||||||
@@ -1042,7 +1048,9 @@ public final class StaticAbilityContinuous {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stAb.hasParam("AffectedZone")) {
|
if (stAb.hasParam("AffectedDefined")) {
|
||||||
|
affectedCards.addAll(definedCards);
|
||||||
|
} else if (stAb.hasParam("AffectedZone")) {
|
||||||
affectedCards.addAll(game.getCardsIn(ZoneType.listValueOf(stAb.getParam("AffectedZone"))));
|
affectedCards.addAll(game.getCardsIn(ZoneType.listValueOf(stAb.getParam("AffectedZone"))));
|
||||||
} else {
|
} else {
|
||||||
affectedCards.addAll(game.getCardsIn(ZoneType.Battlefield));
|
affectedCards.addAll(game.getCardsIn(ZoneType.Battlefield));
|
||||||
|
|||||||
@@ -30,13 +30,11 @@ public class StaticAbilityMustAttack {
|
|||||||
if (stAb.hasParam("MustAttack")) {
|
if (stAb.hasParam("MustAttack")) {
|
||||||
List<GameEntity> def = AbilityUtils.getDefinedEntities(stAb.getHostCard(), stAb.getParam("MustAttack"), stAb);
|
List<GameEntity> def = AbilityUtils.getDefinedEntities(stAb.getHostCard(), stAb.getParam("MustAttack"), stAb);
|
||||||
for (GameEntity e : def) {
|
for (GameEntity e : def) {
|
||||||
if (e instanceof Player) {
|
if (e instanceof Player attackPl) {
|
||||||
Player attackPl = (Player) e;
|
|
||||||
if (!game.getPhaseHandler().isPlayerTurn(attackPl)) { // CR 506.2
|
if (!game.getPhaseHandler().isPlayerTurn(attackPl)) { // CR 506.2
|
||||||
entityList.add(e);
|
entityList.add(e);
|
||||||
}
|
}
|
||||||
} else if (e instanceof Card) {
|
} else if (e instanceof Card attackPW) {
|
||||||
Card attackPW = (Card) e;
|
|
||||||
if (!game.getPhaseHandler().isPlayerTurn(attackPW.getController())) { // CR 506.2
|
if (!game.getPhaseHandler().isPlayerTurn(attackPW.getController())) { // CR 506.2
|
||||||
entityList.add(e);
|
entityList.add(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,6 @@ public class TriggerAttackerBlockedByCreature extends Trigger {
|
|||||||
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
public final boolean performTest(final Map<AbilityKey, Object> runParams) {
|
||||||
final Object a = runParams.get(AbilityKey.Attacker),
|
final Object a = runParams.get(AbilityKey.Attacker),
|
||||||
b = runParams.get(AbilityKey.Blocker);
|
b = runParams.get(AbilityKey.Blocker);
|
||||||
if (!(a instanceof Card && b instanceof Card)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Card attacker = (Card) a,
|
final Card attacker = (Card) a,
|
||||||
blocker = (Card) b;
|
blocker = (Card) b;
|
||||||
|
|||||||
@@ -113,8 +113,7 @@ public class TriggerDamageDone extends Trigger {
|
|||||||
final Object target = runParams.get(AbilityKey.DamageTarget);
|
final Object target = runParams.get(AbilityKey.DamageTarget);
|
||||||
final Card source = (Card) runParams.get(AbilityKey.DamageSource);
|
final Card source = (Card) runParams.get(AbilityKey.DamageSource);
|
||||||
|
|
||||||
if (target instanceof Player) {
|
if (target instanceof Player trigTgt) {
|
||||||
final Player trigTgt = (Player) target;
|
|
||||||
if (!Expressions.compare(trigTgt.getAssignedDamage(null, source), operator, operand)) {
|
if (!Expressions.compare(trigTgt.getAssignedDamage(null, source), operator, operand)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -384,17 +384,15 @@ public class TriggerHandler {
|
|||||||
return false; // It's not the right phase to go off.
|
return false; // It's not the right phase to go off.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TriggerType.Always.equals(regtrig.getMode())) {
|
|
||||||
if (game.getStack().hasStateTrigger(regtrig.getId())) {
|
|
||||||
return false; // State triggers that are already on the stack
|
|
||||||
// don't trigger again.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regtrig.isSuppressed()) {
|
if (regtrig.isSuppressed()) {
|
||||||
return false; // Trigger removed by effect
|
return false; // Trigger removed by effect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TriggerType.Always.equals(regtrig.getMode()) && game.getStack().hasStateTrigger(regtrig.getId())) {
|
||||||
|
return false; // State triggers that are already on the stack
|
||||||
|
// don't trigger again.
|
||||||
|
}
|
||||||
|
|
||||||
// do not check delayed
|
// do not check delayed
|
||||||
if (regtrig.getSpawningAbility() == null && !regtrig.zonesCheck(game.getZoneOf(regtrig.getHostCard()))) {
|
if (regtrig.getSpawningAbility() == null && !regtrig.zonesCheck(game.getZoneOf(regtrig.getHostCard()))) {
|
||||||
return false; // Host card isn't where it needs to be.
|
return false; // Host card isn't where it needs to be.
|
||||||
@@ -415,6 +413,10 @@ public class TriggerHandler {
|
|||||||
return false; // Not the right mode.
|
return false; // Not the right mode.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (regtrig.isSuppressed()) {
|
||||||
|
return false; // Trigger removed by effect
|
||||||
|
}
|
||||||
|
|
||||||
/* this trigger can only be activated once per turn, verify it hasn't already run */
|
/* this trigger can only be activated once per turn, verify it hasn't already run */
|
||||||
if (!regtrig.checkActivationLimit()) {
|
if (!regtrig.checkActivationLimit()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -431,21 +433,17 @@ public class TriggerHandler {
|
|||||||
if (!regtrig.performTest(runParams)) {
|
if (!regtrig.performTest(runParams)) {
|
||||||
return false; // Test failed.
|
return false; // Test failed.
|
||||||
}
|
}
|
||||||
if (regtrig.isSuppressed()) {
|
|
||||||
return false; // Trigger removed by effect
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TriggerType.Always.equals(regtrig.getMode())) {
|
if (TriggerType.Always.equals(regtrig.getMode()) && game.getStack().hasStateTrigger(regtrig.getId())) {
|
||||||
if (game.getStack().hasStateTrigger(regtrig.getId())) {
|
return false; // State triggers that are already on the stack
|
||||||
return false; // State triggers that are already on the stack
|
// don't trigger again.
|
||||||
// don't trigger again.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if any static abilities are disabling the trigger (Torpor Orb and the like)
|
// check if any static abilities are disabling the trigger (Torpor Orb and the like)
|
||||||
if (!regtrig.isStatic() && StaticAbilityDisableTriggers.disabled(game, regtrig, runParams)) {
|
if (!regtrig.isStatic() && StaticAbilityDisableTriggers.disabled(game, regtrig, runParams)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class TriggerManifestDread extends Trigger {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
|
||||||
sa.setTriggeringObject(AbilityKey.NewCard, runParams.get(AbilityKey.Card));
|
sa.setTriggeringObject(AbilityKey.Cards, runParams.get(AbilityKey.Cards));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,13 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getSpawningAbility() != null && getSpawningAbility().hasParam("TriggersWhenSpent")) {
|
||||||
|
if (!getTriggerRemembered().contains(spellAbility)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ package forge.game.trigger;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.TreeBasedTable;
|
import com.google.common.collect.TreeBasedTable;
|
||||||
|
|
||||||
@@ -39,141 +36,6 @@ import forge.game.spellability.TargetRestrictions;
|
|||||||
// use of any of the methods)
|
// use of any of the methods)
|
||||||
public class WrappedAbility extends Ability {
|
public class WrappedAbility extends Ability {
|
||||||
|
|
||||||
static Set<ApiType> noTimestampCheck = ImmutableSet.of(
|
|
||||||
ApiType.Abandon, // no Triggered
|
|
||||||
ApiType.ActivateAbility, // no Triggered
|
|
||||||
ApiType.AddPhase, // only player
|
|
||||||
ApiType.AddTurn, // only player
|
|
||||||
ApiType.SkipPhase, // only player
|
|
||||||
ApiType.SkipTurn, // only player
|
|
||||||
ApiType.AlterAttribute, // updated
|
|
||||||
|
|
||||||
ApiType.Amass, // no Triggered only you
|
|
||||||
ApiType.Ascend, // only player (you)
|
|
||||||
|
|
||||||
ApiType.Animate, // updated
|
|
||||||
ApiType.AnimateAll, // no triggered
|
|
||||||
|
|
||||||
ApiType.Attach, // checked
|
|
||||||
|
|
||||||
ApiType.Balance, // only player
|
|
||||||
|
|
||||||
ApiType.BecomesBlocked, // no triggered
|
|
||||||
ApiType.MustBlock, // updated
|
|
||||||
ApiType.SwitchBlock, // no triggered
|
|
||||||
|
|
||||||
ApiType.BecomeMonarch, // only player
|
|
||||||
ApiType.Bond, // updated
|
|
||||||
|
|
||||||
ApiType.BidLife, // only player
|
|
||||||
ApiType.Clash, // only player
|
|
||||||
|
|
||||||
ApiType.Clone, // checked
|
|
||||||
|
|
||||||
ApiType.GenericChoice, // only player
|
|
||||||
|
|
||||||
ApiType.Connive, // no triggered
|
|
||||||
ApiType.Encode, // no triggered
|
|
||||||
ApiType.Meld, // no triggered
|
|
||||||
ApiType.Haunt, // has own logic
|
|
||||||
|
|
||||||
ApiType.PutCounter,
|
|
||||||
ApiType.PutCounterAll, // no triggered
|
|
||||||
ApiType.MoveCounter,
|
|
||||||
ApiType.MultiplyCounter,
|
|
||||||
ApiType.MoveCounter,
|
|
||||||
ApiType.RemoveCounter,
|
|
||||||
ApiType.AddOrRemoveCounter,
|
|
||||||
ApiType.MoveCounter,
|
|
||||||
|
|
||||||
ApiType.Draw, // only player
|
|
||||||
ApiType.GainLife, // only player
|
|
||||||
ApiType.SetLife, // only player
|
|
||||||
ApiType.LoseLife, // only player
|
|
||||||
ApiType.ChangeZone,
|
|
||||||
ApiType.Destroy,
|
|
||||||
ApiType.Token,
|
|
||||||
ApiType.SetState,
|
|
||||||
ApiType.Play,
|
|
||||||
ApiType.Sacrifice, // no triggered
|
|
||||||
ApiType.SacrificeAll,
|
|
||||||
ApiType.Pump,
|
|
||||||
|
|
||||||
ApiType.Phases, // updated
|
|
||||||
|
|
||||||
ApiType.DealDamage, // checked
|
|
||||||
|
|
||||||
ApiType.DelayedTrigger,
|
|
||||||
ApiType.ImmediateTrigger,
|
|
||||||
|
|
||||||
ApiType.Goad, // updated
|
|
||||||
|
|
||||||
ApiType.EachDamage,
|
|
||||||
ApiType.WinsGame, // only player
|
|
||||||
ApiType.LosesGame, // only player
|
|
||||||
ApiType.Incubate, // only player
|
|
||||||
ApiType.Mill, // only player
|
|
||||||
|
|
||||||
ApiType.Explore,
|
|
||||||
ApiType.Protection, // should not care about triggered
|
|
||||||
ApiType.ProtectionAll, // No Triggered
|
|
||||||
ApiType.Proliferate, // only player no triggered interaction
|
|
||||||
ApiType.TimeTravel, // only player no triggered interaction
|
|
||||||
ApiType.CopyPermanent,
|
|
||||||
ApiType.Token,
|
|
||||||
ApiType.Debuff, // updated
|
|
||||||
ApiType.Manifest, // no triggered
|
|
||||||
ApiType.Scry, // only player
|
|
||||||
ApiType.SetInMotion, // No Triggered
|
|
||||||
ApiType.Shuffle, // only player
|
|
||||||
ApiType.Surveil, // only player
|
|
||||||
ApiType.Tap, // Done
|
|
||||||
ApiType.TapAll, // uses filterListByType
|
|
||||||
ApiType.TapOrUntap, // No TriggeredCard
|
|
||||||
ApiType.TapOrUntapAll, // No TriggeredCard
|
|
||||||
ApiType.Untap, // Done
|
|
||||||
ApiType.UntapAll, // only player
|
|
||||||
ApiType.Unattach, // No Triggered
|
|
||||||
ApiType.UnattachAll, // No Triggered
|
|
||||||
|
|
||||||
ApiType.Regenerate, // Updated
|
|
||||||
ApiType.Regeneration, // Replacement Effect only
|
|
||||||
|
|
||||||
ApiType.RemoveFromCombat, // Done
|
|
||||||
|
|
||||||
// only Replacement Effects, no Trigger
|
|
||||||
ApiType.ReplaceCounter,
|
|
||||||
ApiType.ReplaceDamage,
|
|
||||||
ApiType.ReplaceEffect,
|
|
||||||
ApiType.ReplaceMana,
|
|
||||||
ApiType.ReplaceSplitDamage,
|
|
||||||
ApiType.ReplaceToken,
|
|
||||||
|
|
||||||
ApiType.Repeat,
|
|
||||||
ApiType.RepeatEach,
|
|
||||||
|
|
||||||
ApiType.RollDice, // only player
|
|
||||||
ApiType.RollPlanarDice, // only player
|
|
||||||
ApiType.Seek, // only player
|
|
||||||
ApiType.Heist, // only player
|
|
||||||
|
|
||||||
ApiType.RingTemptsYou, // only player
|
|
||||||
ApiType.TakeInitiative, // only player
|
|
||||||
|
|
||||||
ApiType.UnlockDoor, // no triggered
|
|
||||||
|
|
||||||
ApiType.Poison, // only player
|
|
||||||
ApiType.Venture, // only player
|
|
||||||
ApiType.VillainousChoice, // only player
|
|
||||||
ApiType.Vote, // only player
|
|
||||||
// internal
|
|
||||||
ApiType.BlankLine,
|
|
||||||
ApiType.DamageResolve,
|
|
||||||
ApiType.ChangeZoneResolve,
|
|
||||||
ApiType.InternalLegendaryRule,
|
|
||||||
ApiType.InternalIgnoreEffect
|
|
||||||
);
|
|
||||||
|
|
||||||
private final SpellAbility sa;
|
private final SpellAbility sa;
|
||||||
private Player decider;
|
private Player decider;
|
||||||
|
|
||||||
@@ -323,7 +185,7 @@ public class WrappedAbility extends Ability {
|
|||||||
// a real solution would include only the triggering information that actually is used, but that's a major change
|
// a real solution would include only the triggering information that actually is used, but that's a major change
|
||||||
@Override
|
@Override
|
||||||
public String toUnsuppressedString() {
|
public String toUnsuppressedString() {
|
||||||
String desc = this.getStackDescription(); /* use augmented stack description as string for wrapped things */
|
String desc = this.getStackDescription(false); /* use augmented stack description as string for wrapped things */
|
||||||
String card = getHostCard().toString();
|
String card = getHostCard().toString();
|
||||||
if (!desc.contains(card) && desc.contains(" this ")) { /* a hack for Evolve and similar that don't have CARDNAME */
|
if (!desc.contains(card) && desc.contains(" this ")) { /* a hack for Evolve and similar that don't have CARDNAME */
|
||||||
return card + ": " + desc;
|
return card + ": " + desc;
|
||||||
@@ -332,15 +194,23 @@ public class WrappedAbility extends Ability {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStackDescription() {
|
public String getStackDescription() {
|
||||||
|
return getStackDescription(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStackDescription(boolean withTargets) {
|
||||||
final Trigger regtrig = getTrigger();
|
final Trigger regtrig = getTrigger();
|
||||||
if (regtrig == null) return "";
|
if (regtrig == null) return "";
|
||||||
final StringBuilder sb =
|
final StringBuilder sb =
|
||||||
new StringBuilder(regtrig.replaceAbilityText(regtrig.toString(true), this, true));
|
new StringBuilder(regtrig.replaceAbilityText(regtrig.toString(true), this, true));
|
||||||
List<TargetChoices> allTargets = sa.getAllTargetChoices();
|
|
||||||
if (!allTargets.isEmpty() && !ApiType.Charm.equals(sa.getApi())) {
|
// prevent text growing too long when SA target other in a chain and also potential StackOverflow
|
||||||
sb.append(" (Targeting: ");
|
if (withTargets) {
|
||||||
sb.append(allTargets);
|
List<TargetChoices> allTargets = sa.getAllTargetChoices();
|
||||||
sb.append(")");
|
if (!allTargets.isEmpty() && !ApiType.Charm.equals(sa.getApi())) {
|
||||||
|
sb.append(" (Targeting: ");
|
||||||
|
sb.append(allTargets);
|
||||||
|
sb.append(")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String important = regtrig.getImportantStackObjects(this);
|
String important = regtrig.getImportantStackObjects(this);
|
||||||
@@ -577,35 +447,9 @@ public class WrappedAbility extends Ability {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timestampCheck();
|
|
||||||
|
|
||||||
getActivatingPlayer().getController().playSpellAbilityNoStack(sa, false);
|
getActivatingPlayer().getController().playSpellAbilityNoStack(sa, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO remove this function after the Effects are updated
|
|
||||||
*/
|
|
||||||
protected void timestampCheck() {
|
|
||||||
final Game game = sa.getActivatingPlayer().getGame();
|
|
||||||
|
|
||||||
if (noTimestampCheck.contains(sa.getApi())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<AbilityKey, Object> triggerMap = AbilityKey.newMap(sa.getTriggeringObjects());
|
|
||||||
for (Entry<AbilityKey, Object> ev : triggerMap.entrySet()) {
|
|
||||||
if (ev.getValue() instanceof Card) {
|
|
||||||
Card card = (Card) ev.getValue();
|
|
||||||
Card current = game.getCardState(card);
|
|
||||||
if (card.isInPlay() && current.isInPlay() && !current.equalsWithGameTimestamp(card)) {
|
|
||||||
// TODO: figure out if NoTimestampCheck should be the default for ChangesZone triggers
|
|
||||||
sa.getTriggeringObjects().remove(ev.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: CardCollection
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CardDamageMap getDamageMap() {
|
public CardDamageMap getDamageMap() {
|
||||||
return sa.getDamageMap();
|
return sa.getDamageMap();
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ public enum TrackableProperty {
|
|||||||
RingLevel(TrackableTypes.IntegerType),
|
RingLevel(TrackableTypes.IntegerType),
|
||||||
CurrentRoom(TrackableTypes.StringType),
|
CurrentRoom(TrackableTypes.StringType),
|
||||||
Intensity(TrackableTypes.IntegerType),
|
Intensity(TrackableTypes.IntegerType),
|
||||||
|
OverlayText(TrackableTypes.StringType),
|
||||||
|
MarkerText(TrackableTypes.StringListType),
|
||||||
Remembered(TrackableTypes.StringType),
|
Remembered(TrackableTypes.StringType),
|
||||||
NamedCard(TrackableTypes.StringListType),
|
NamedCard(TrackableTypes.StringListType),
|
||||||
PlayerMayLook(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze),
|
PlayerMayLook(TrackableTypes.PlayerViewCollectionType, FreezeMode.IgnoresFreeze),
|
||||||
@@ -217,7 +219,6 @@ public enum TrackableProperty {
|
|||||||
CommanderCast(TrackableTypes.IntegerMapType),
|
CommanderCast(TrackableTypes.IntegerMapType),
|
||||||
CommanderDamage(TrackableTypes.IntegerMapType),
|
CommanderDamage(TrackableTypes.IntegerMapType),
|
||||||
MindSlaveMaster(TrackableTypes.PlayerViewType),
|
MindSlaveMaster(TrackableTypes.PlayerViewType),
|
||||||
Speed(TrackableTypes.IntegerType),
|
|
||||||
|
|
||||||
Ante(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
|
Ante(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze),
|
||||||
Battlefield(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), //zones can't respect freeze, otherwise cards that die from state based effects won't have that reflected in the UI
|
Battlefield(TrackableTypes.CardViewCollectionType, FreezeMode.IgnoresFreeze), //zones can't respect freeze, otherwise cards that die from state based effects won't have that reflected in the UI
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-android</artifactId>
|
<artifactId>forge-gui-android</artifactId>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<!-- generate versionName from revision property to snapshot-version property -->
|
<!-- generate versionName from revision property to snapshot-version property -->
|
||||||
<name>snapshot-version</name>
|
<name>snapshot-version</name>
|
||||||
<value>${revision}</value>
|
<value>2.0.02-SNAPSHOT</value>
|
||||||
<regex>-SNAPSHOT</regex>
|
<regex>-SNAPSHOT</regex>
|
||||||
<replacement>-SNAPSHOT-${month.date}</replacement>
|
<replacement>-SNAPSHOT-${month.date}</replacement>
|
||||||
<failIfNoMatch>false</failIfNoMatch>
|
<failIfNoMatch>false</failIfNoMatch>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-desktop</artifactId>
|
<artifactId>forge-gui-desktop</artifactId>
|
||||||
@@ -50,7 +49,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<!-- generate versionName from revision property to snapshot-version property -->
|
<!-- generate versionName from revision property to snapshot-version property -->
|
||||||
<name>snapshot-version</name>
|
<name>snapshot-version</name>
|
||||||
<value>${revision}</value>
|
<value>2.0.02-SNAPSHOT</value>
|
||||||
<regex>-SNAPSHOT</regex>
|
<regex>-SNAPSHOT</regex>
|
||||||
<replacement>-SNAPSHOT-${month.date}</replacement>
|
<replacement>-SNAPSHOT-${month.date}</replacement>
|
||||||
<failIfNoMatch>false</failIfNoMatch>
|
<failIfNoMatch>false</failIfNoMatch>
|
||||||
@@ -486,63 +485,42 @@
|
|||||||
<phase>pre-integration-test</phase>
|
<phase>pre-integration-test</phase>
|
||||||
<configuration>
|
<configuration>
|
||||||
<target>
|
<target>
|
||||||
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx"/>
|
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx" />
|
||||||
<copy todir="${project.build.directory}/${project.build.finalName}-osx">
|
<copy todir="${project.build.directory}/${project.build.finalName}-osx">
|
||||||
<fileset dir="${basedir}/../forge-gui/" includes="LICENSE.txt"/>
|
<fileset dir="${basedir}/../forge-gui/" includes="LICENSE.txt" />
|
||||||
<fileset dir="${basedir}/../forge-gui/release-files/"
|
<fileset dir="${basedir}/../forge-gui/release-files/" includes="CHANGES.txt" />
|
||||||
includes="CHANGES.txt"/>
|
<fileset dir="${basedir}/../forge-gui/release-files/" includes="CONTRIBUTORS.txt" />
|
||||||
<fileset dir="${basedir}/../forge-gui/release-files/"
|
<fileset dir="${basedir}/../forge-gui/release-files/" includes="ISSUES.txt" />
|
||||||
includes="CONTRIBUTORS.txt"/>
|
<fileset dir="${basedir}/../forge-gui/release-files/" includes="INSTALLATION.txt" />
|
||||||
<fileset dir="${basedir}/../forge-gui/release-files/"
|
<fileset dir="${basedir}/../forge-gui/release-files/" includes="GAMEPAD_README.txt" />
|
||||||
includes="ISSUES.txt"/>
|
<fileset dir="${basedir}/../forge-gui/" includes="MANUAL.txt" />
|
||||||
<fileset dir="${basedir}/../forge-gui/release-files/"
|
<fileset dir="${basedir}/" includes="sentry.properties" />
|
||||||
includes="INSTALLATION.txt"/>
|
|
||||||
<fileset dir="${basedir}/../forge-gui/release-files/"
|
|
||||||
includes="GAMEPAD_README.txt"/>
|
|
||||||
<fileset dir="${basedir}/../forge-gui/" includes="MANUAL.txt"/>
|
|
||||||
<fileset dir="${basedir}/" includes="sentry.properties"/>
|
|
||||||
</copy>
|
</copy>
|
||||||
<taskdef name="bundleapp"
|
<taskdef name="bundleapp" classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar" classname="com.oracle.appbundler.AppBundlerTask" />
|
||||||
classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar"
|
<bundleapp outputdirectory="${project.build.directory}/${project.build.finalName}-osx" name="${project.name}" displayname="${project.name}" shortversion="${project.version}" identifier="forge.view.Main" icon="${basedir}/${configSourceDirectory}/Forge.icns" applicationCategory="public.app-category.games" mainclassname="forge.view.Main">
|
||||||
classname="com.oracle.appbundler.AppBundlerTask"/>
|
<classpath file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar" />
|
||||||
<bundleapp
|
<classpath file="${basedir}/../forge-gui/forge.profile.properties.example" />
|
||||||
outputdirectory="${project.build.directory}/${project.build.finalName}-osx"
|
<option value="-Dapple.laf.useScreenMenuBar=true" />
|
||||||
name="${project.name}" displayname="${project.name}"
|
<option value="-Dcom.apple.macos.use-file-dialog-packages=true" />
|
||||||
shortversion="${project.version}" identifier="forge.view.Main"
|
<option value="-Dcom.apple.macos.useScreenMenuBar=true" />
|
||||||
icon="${basedir}/${configSourceDirectory}/Forge.icns"
|
<option value="-Dcom.apple.mrj.application.apple.menu.about.name=Forge" />
|
||||||
applicationCategory="public.app-category.games"
|
<option value="-Dcom.apple.smallTabs=true" />
|
||||||
mainclassname="forge.view.Main">
|
<option value="-Xmx4096M" />
|
||||||
<classpath
|
<option value="-Dapp.dir=$APP_ROOT/Contents/Resources/" />
|
||||||
file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar"/>
|
|
||||||
<classpath file="${basedir}/../forge-gui/forge.profile.properties.example"/>
|
|
||||||
<option value="-Dapple.laf.useScreenMenuBar=true"/>
|
|
||||||
<option value="-Dcom.apple.macos.use-file-dialog-packages=true"/>
|
|
||||||
<option value="-Dcom.apple.macos.useScreenMenuBar=true"/>
|
|
||||||
<option value="-Dcom.apple.mrj.application.apple.menu.about.name=Forge"/>
|
|
||||||
<option value="-Dcom.apple.smallTabs=true"/>
|
|
||||||
<option value="-Xmx4096M"/>
|
|
||||||
<option value="-Dapp.dir=$APP_ROOT/Contents/Resources/"/>
|
|
||||||
</bundleapp>
|
</bundleapp>
|
||||||
<copy todir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res">
|
<copy todir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res">
|
||||||
<fileset dir="${basedir}/../forge-gui/res" excludes="**/cardsfolder/**"/>
|
<fileset dir="${basedir}/../forge-gui/res" excludes="**/cardsfolder/**" />
|
||||||
</copy>
|
</copy>
|
||||||
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder"/>
|
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder" />
|
||||||
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip"
|
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip" basedir="${basedir}/../forge-gui/res/cardsfolder" level="1" />
|
||||||
basedir="${basedir}/../forge-gui/res/cardsfolder" level="1"/>
|
<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" resource="/Applications" />
|
||||||
<symlink
|
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg" failonerror="false">
|
||||||
link="${project.build.directory}/${project.build.finalName}-osx/Applications"
|
<arg line="--volname ${project.name}-${project.version} --background ${basedir}/../forge-gui/${configSourceDirectory}/backgroundImage.jpg --window-size 700 419 --icon-size 64 --icon ${forge.file.name} 141 283 --icon ${applications.file.name} 452 283 --icon ${changes.file.name} 645 80 --icon ${license.file.name} 645 200 --icon ${readme.file.name} 645 320 ${project.build.directory}/${project.build.finalName}.dmg ${project.build.directory}/${project.build.finalName}-osx" />
|
||||||
resource="/Applications"/>
|
|
||||||
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg"
|
|
||||||
failonerror="false">
|
|
||||||
<arg line="--volname ${project.name}-${project.version} --background ${basedir}/../forge-gui/${configSourceDirectory}/backgroundImage.jpg --window-size 700 419 --icon-size 64 --icon ${forge.file.name} 141 283 --icon ${applications.file.name} 452 283 --icon ${changes.file.name} 645 80 --icon ${license.file.name} 645 200 --icon ${readme.file.name} 645 320 ${project.build.directory}/${project.build.finalName}.dmg ${project.build.directory}/${project.build.finalName}-osx"/>
|
|
||||||
</exec>
|
</exec>
|
||||||
<tar basedir="${project.build.directory}"
|
<tar basedir="${project.build.directory}" includes="${project.build.finalName}.dmg" destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2" compression="bzip2" />
|
||||||
includes="${project.build.finalName}.dmg"
|
|
||||||
destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2"
|
|
||||||
compression="bzip2"/>
|
|
||||||
<!--<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" action="delete" /> -->
|
<!--<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" action="delete" /> -->
|
||||||
<exec executable="rm" failonerror="false">
|
<exec executable="rm" failonerror="false">
|
||||||
<arg line="-f ${project.build.directory}/${project.build.finalName}-osx/Applications"/>
|
<arg line="-f ${project.build.directory}/${project.build.finalName}-osx/Applications" />
|
||||||
</exec>
|
</exec>
|
||||||
</target>
|
</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -622,54 +600,38 @@
|
|||||||
<phase>pre-integration-test</phase>
|
<phase>pre-integration-test</phase>
|
||||||
<configuration>
|
<configuration>
|
||||||
<target>
|
<target>
|
||||||
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx"/>
|
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx" />
|
||||||
<copy todir="${project.build.directory}/${project.build.finalName}-osx">
|
<copy todir="${project.build.directory}/${project.build.finalName}-osx">
|
||||||
<fileset dir="${basedir}/../forge-gui/" includes="LICENSE.txt"/>
|
<fileset dir="${basedir}/../forge-gui/" includes="LICENSE.txt" />
|
||||||
<fileset dir="${basedir}/../forge-gui/" includes="README.txt"/>
|
<fileset dir="${basedir}/../forge-gui/" includes="README.txt" />
|
||||||
<fileset dir="${basedir}/../forge-gui/" includes="MANUAL.txt"/>
|
<fileset dir="${basedir}/../forge-gui/" includes="MANUAL.txt" />
|
||||||
<fileset dir="${basedir}/" includes="sentry.properties"/>
|
<fileset dir="${basedir}/" includes="sentry.properties" />
|
||||||
</copy>
|
</copy>
|
||||||
<taskdef name="bundleapp"
|
<taskdef name="bundleapp" classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar" classname="com.oracle.appbundler.AppBundlerTask" />
|
||||||
classpath="${basedir}/../forge-gui/${configSourceDirectory}/appbundler-1.0-custom.jar"
|
<bundleapp outputdirectory="${project.build.directory}/${project.build.finalName}-osx" name="${project.name}" displayname="${project.name}" shortversion="${project.version}" identifier="forge.view.Main" icon="${basedir}/${configSourceDirectory}/Forge.icns" applicationCategory="public.app-category.games" mainclassname="forge.view.Main">
|
||||||
classname="com.oracle.appbundler.AppBundlerTask"/>
|
<classpath file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar" />
|
||||||
<bundleapp
|
<classpath file="${basedir}/../forge-gui/forge.profile.properties.example" />
|
||||||
outputdirectory="${project.build.directory}/${project.build.finalName}-osx"
|
<option value="-Dapple.laf.useScreenMenuBar=true" />
|
||||||
name="${project.name}" displayname="${project.name}"
|
<option value="-Dcom.apple.macos.use-file-dialog-packages=true" />
|
||||||
shortversion="${project.version}" identifier="forge.view.Main"
|
<option value="-Dcom.apple.macos.useScreenMenuBar=true" />
|
||||||
icon="${basedir}/${configSourceDirectory}/Forge.icns"
|
<option value="-Dcom.apple.mrj.application.apple.menu.about.name=Forge" />
|
||||||
applicationCategory="public.app-category.games"
|
<option value="-Dcom.apple.smallTabs=true" />
|
||||||
mainclassname="forge.view.Main">
|
<option value="-Xmx4096M" />
|
||||||
<classpath
|
<option value="-Dapp.dir=$APP_ROOT/Contents/Resources/" />
|
||||||
file="${project.build.directory}/${project.build.finalName}-jar-with-dependencies.jar"/>
|
|
||||||
<classpath file="${basedir}/../forge-gui/forge.profile.properties.example"/>
|
|
||||||
<option value="-Dapple.laf.useScreenMenuBar=true"/>
|
|
||||||
<option value="-Dcom.apple.macos.use-file-dialog-packages=true"/>
|
|
||||||
<option value="-Dcom.apple.macos.useScreenMenuBar=true"/>
|
|
||||||
<option value="-Dcom.apple.mrj.application.apple.menu.about.name=Forge"/>
|
|
||||||
<option value="-Dcom.apple.smallTabs=true"/>
|
|
||||||
<option value="-Xmx4096M"/>
|
|
||||||
<option value="-Dapp.dir=$APP_ROOT/Contents/Resources/"/>
|
|
||||||
</bundleapp>
|
</bundleapp>
|
||||||
<copy todir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res">
|
<copy todir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res">
|
||||||
<fileset dir="${basedir}/../forge-gui/res" excludes="**/cardsfolder/**"/>
|
<fileset dir="${basedir}/../forge-gui/res" excludes="**/cardsfolder/**" />
|
||||||
</copy>
|
</copy>
|
||||||
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder"/>
|
<mkdir dir="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder" />
|
||||||
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip"
|
<zip destfile="${project.build.directory}/${project.build.finalName}-osx/Forge.app/Contents/Resources/res/cardsfolder/cardsfolder.zip" basedir="${basedir}/../forge-gui/res/cardsfolder" level="1" />
|
||||||
basedir="${basedir}/../forge-gui/res/cardsfolder" level="1"/>
|
<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" resource="/Applications" />
|
||||||
<symlink
|
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg" failonerror="false">
|
||||||
link="${project.build.directory}/${project.build.finalName}-osx/Applications"
|
<arg line="--volname ${project.name}-${project.version} --background ${basedir}/../forge-gui/${configSourceDirectory}/backgroundImage.jpg --window-size 700 419 --icon-size 64 --icon ${forge.file.name} 141 283 --icon ${applications.file.name} 452 283 --icon ${changes.file.name} 645 80 --icon ${license.file.name} 645 200 --icon ${readme.file.name} 645 320 ${project.build.directory}/${project.build.finalName}.dmg ${project.build.directory}/${project.build.finalName}-osx" />
|
||||||
resource="/Applications"/>
|
|
||||||
<exec executable="${basedir}/../forge-gui/${configSourceDirectory}/create-dmg"
|
|
||||||
failonerror="false">
|
|
||||||
<arg line="--volname ${project.name}-${project.version} --background ${basedir}/../forge-gui/${configSourceDirectory}/backgroundImage.jpg --window-size 700 419 --icon-size 64 --icon ${forge.file.name} 141 283 --icon ${applications.file.name} 452 283 --icon ${changes.file.name} 645 80 --icon ${license.file.name} 645 200 --icon ${readme.file.name} 645 320 ${project.build.directory}/${project.build.finalName}.dmg ${project.build.directory}/${project.build.finalName}-osx"/>
|
|
||||||
</exec>
|
</exec>
|
||||||
<tar basedir="${project.build.directory}"
|
<tar basedir="${project.build.directory}" includes="${project.build.finalName}.dmg" destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2" compression="bzip2" />
|
||||||
includes="${project.build.finalName}.dmg"
|
|
||||||
destfile="${project.build.directory}/${project.build.finalName}-osx.tar.bz2"
|
|
||||||
compression="bzip2"/>
|
|
||||||
<!--<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" action="delete" /> -->
|
<!--<symlink link="${project.build.directory}/${project.build.finalName}-osx/Applications" action="delete" /> -->
|
||||||
<exec executable="rm" failonerror="false">
|
<exec executable="rm" failonerror="false">
|
||||||
<arg line="-f ${project.build.directory}/${project.build.finalName}-osx/Applications"/>
|
<arg line="-f ${project.build.directory}/${project.build.finalName}-osx/Applications" />
|
||||||
</exec>
|
</exec>
|
||||||
</target>
|
</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -533,11 +533,8 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card.getCurrentRoom() != null && !card.getCurrentRoom().isEmpty()) {
|
if(card.getMarkerText() != null) {
|
||||||
List<String> markers = new ArrayList<>();
|
drawMarkersTabs(g, card.getMarkerText());
|
||||||
markers.add("In Room:");
|
|
||||||
markers.add(card.getCurrentRoom());
|
|
||||||
drawMarkersTabs(g, markers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final int combatXSymbols = (cardXOffset + (cardWidth / 4)) - 16;
|
final int combatXSymbols = (cardXOffset + (cardWidth / 4)) - 16;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-ios</artifactId>
|
<artifactId>forge-gui-ios</artifactId>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<filtering>true</filtering>
|
<filtering>true</filtering>
|
||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
<finalName>forge-ios-${revision}</finalName>
|
<finalName>forge-ios-2.0.02-SNAPSHOT</finalName>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-mobile-dev</artifactId>
|
<artifactId>forge-gui-mobile-dev</artifactId>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<!-- generate versionName from revision property to snapshot-version property -->
|
<!-- generate versionName from revision property to snapshot-version property -->
|
||||||
<name>snapshot-version</name>
|
<name>snapshot-version</name>
|
||||||
<value>${revision}</value>
|
<value>2.0.02-SNAPSHOT</value>
|
||||||
<regex>-SNAPSHOT</regex>
|
<regex>-SNAPSHOT</regex>
|
||||||
<replacement>-SNAPSHOT-${month.date}</replacement>
|
<replacement>-SNAPSHOT-${month.date}</replacement>
|
||||||
<failIfNoMatch>false</failIfNoMatch>
|
<failIfNoMatch>false</failIfNoMatch>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui-mobile</artifactId>
|
<artifactId>forge-gui-mobile</artifactId>
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ public class AdventureEventData implements Serializable {
|
|||||||
description += "\n";
|
description += "\n";
|
||||||
}
|
}
|
||||||
description += "Prizes\n3 round wins: 500 gold\n2 round wins: 200 gold\n1 round win: 100 gold\n";
|
description += "Prizes\n3 round wins: 500 gold\n2 round wins: 200 gold\n1 round win: 100 gold\n";
|
||||||
description += "Finishing event will award an unsellable copy of each card in your Jumpstart deck.";
|
description += "Participating in this event will award a valueless copy of each card in your Jumpstart deck.";
|
||||||
}
|
}
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -908,14 +908,45 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int cardSellPrice(PaperCard card) {
|
public int cardSellPrice(PaperCard card) {
|
||||||
|
int valuable = cards.count(card) - noSellCards.count(card);
|
||||||
|
if (valuable == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int basePrice = (int) (CardUtil.getCardPrice(card) * difficultyData.sellFactor);
|
||||||
|
|
||||||
float townPriceModifier = currentLocationChanges == null ? 1f : currentLocationChanges.getTownPriceModifier();
|
float townPriceModifier = currentLocationChanges == null ? 1f : currentLocationChanges.getTownPriceModifier();
|
||||||
return (int) (CardUtil.getCardPrice(card) * difficultyData.sellFactor * (2.0f - townPriceModifier));
|
return (int) (basePrice * (2.0f - townPriceModifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sellCard(PaperCard card, Integer result) {
|
public int sellCard(PaperCard card, Integer result, boolean addGold) {
|
||||||
float price = cardSellPrice(card) * result;
|
// When selling cards, always try to sell cards worth something before selling cards that aren't worth anything
|
||||||
cards.remove(card, result);
|
if (result == null || result < 1) return 0;
|
||||||
addGold((int) price);
|
|
||||||
|
float earned = 0;
|
||||||
|
|
||||||
|
int valuableCount = cards.count(card) - noSellCards.count(card);
|
||||||
|
int noValueToSell = result - valuableCount;
|
||||||
|
int amountValuableToSell = Math.min(result, valuableCount);
|
||||||
|
|
||||||
|
if (amountValuableToSell > 0) {
|
||||||
|
earned += cardSellPrice(card) * amountValuableToSell;
|
||||||
|
cards.remove(card, amountValuableToSell);
|
||||||
|
}
|
||||||
|
if (noValueToSell > 0) {
|
||||||
|
cards.remove(card, noValueToSell);
|
||||||
|
noSellCards.remove(card, noValueToSell);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addGold) {
|
||||||
|
addGold((int) earned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) earned;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sellOneCard(PaperCard card) {
|
||||||
|
return sellCard(card, 1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeItem(String name) {
|
public void removeItem(String name) {
|
||||||
@@ -1166,8 +1197,8 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
|||||||
ItemPool<PaperCard> sellableCards = new ItemPool<>(PaperCard.class);
|
ItemPool<PaperCard> sellableCards = new ItemPool<>(PaperCard.class);
|
||||||
sellableCards.addAllFlat(cards.toFlatList());
|
sellableCards.addAllFlat(cards.toFlatList());
|
||||||
|
|
||||||
// 1. Remove cards you can't sell
|
// Nosell cards used to be filtered out here. Instead we're going to replace their value with 0
|
||||||
sellableCards.removeAll(noSellCards);
|
|
||||||
// 1a. Potentially return here if we want to give config option to sell cards from decks
|
// 1a. Potentially return here if we want to give config option to sell cards from decks
|
||||||
// but would need to update the decks on sell, not just the catalog
|
// but would need to update the decks on sell, not just the catalog
|
||||||
|
|
||||||
@@ -1175,11 +1206,7 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
|||||||
Map<PaperCard, Integer> maxCardCounts = new HashMap<>();
|
Map<PaperCard, Integer> maxCardCounts = new HashMap<>();
|
||||||
for (int i = 0; i < NUMBER_OF_DECKS; i++) {
|
for (int i = 0; i < NUMBER_OF_DECKS; i++) {
|
||||||
for (final Map.Entry<PaperCard, Integer> cp : decks[i].getAllCardsInASinglePool()) {
|
for (final Map.Entry<PaperCard, Integer> cp : decks[i].getAllCardsInASinglePool()) {
|
||||||
|
int count = cp.getValue();
|
||||||
int count = cp.getValue() - noSellCards.count(cp.getKey());
|
|
||||||
|
|
||||||
if (count <= 0) continue;
|
|
||||||
|
|
||||||
if (count > maxCardCounts.getOrDefault(cp.getKey(), 0)) {
|
if (count > maxCardCounts.getOrDefault(cp.getKey(), 0)) {
|
||||||
maxCardCounts.put(cp.getKey(), cp.getValue());
|
maxCardCounts.put(cp.getKey(), cp.getValue());
|
||||||
}
|
}
|
||||||
@@ -1213,9 +1240,8 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
|
|||||||
public void doAutosell() {
|
public void doAutosell() {
|
||||||
int profit = 0;
|
int profit = 0;
|
||||||
for (PaperCard cardToSell : autoSellCards.toFlatList()) {
|
for (PaperCard cardToSell : autoSellCards.toFlatList()) {
|
||||||
profit += AdventurePlayer.current().cardSellPrice(cardToSell);
|
profit += AdventurePlayer.current().sellOneCard(cardToSell);
|
||||||
autoSellCards.remove(cardToSell);
|
autoSellCards.remove(cardToSell);
|
||||||
cards.remove(cardToSell, 1);
|
|
||||||
}
|
}
|
||||||
addGold(profit); //do this as one transaction so as not to get multiple copies of sound effect
|
addGold(profit); //do this as one transaction so as not to get multiple copies of sound effect
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
|||||||
if (!cardManager.isInfinite()) {
|
if (!cardManager.isInfinite()) {
|
||||||
removeCard(card, result);
|
removeCard(card, result);
|
||||||
}
|
}
|
||||||
AdventurePlayer.current().sellCard(card, result);
|
AdventurePlayer.current().sellCard(card, result, true);
|
||||||
lblGold.setText(String.valueOf(AdventurePlayer.current().getGold()));
|
lblGold.setText(String.valueOf(AdventurePlayer.current().getGold()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -601,7 +601,7 @@ public class AdventureDeckEditor extends TabPageScreen<AdventureDeckEditor> {
|
|||||||
public void run(Boolean result) {
|
public void run(Boolean result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
for (Map.Entry<PaperCard, Integer> entry : catalogPage.cardManager.getFilteredItems()) {
|
for (Map.Entry<PaperCard, Integer> entry : catalogPage.cardManager.getFilteredItems()) {
|
||||||
AdventurePlayer.current().sellCard(entry.getKey(), entry.getValue());
|
AdventurePlayer.current().sellCard(entry.getKey(), entry.getValue(), true);
|
||||||
}
|
}
|
||||||
catalogPage.refresh();
|
catalogPage.refresh();
|
||||||
lblGold.setText(String.valueOf(AdventurePlayer.current().getGold()));
|
lblGold.setText(String.valueOf(AdventurePlayer.current().getGold()));
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
private transient boolean enterSpawnPOI = false;
|
private transient boolean enterSpawnPOI = false;
|
||||||
|
|
||||||
NavArrowActor navArrow;
|
NavArrowActor navArrow;
|
||||||
|
final Rectangle tempBoundingRect = new Rectangle();
|
||||||
|
final Vector2 enemyMoveVector = new Vector2();
|
||||||
|
boolean collided = false;
|
||||||
|
|
||||||
public WorldStage() {
|
public WorldStage() {
|
||||||
super();
|
super();
|
||||||
background = new WorldBackground(this);
|
background = new WorldBackground(this);
|
||||||
@@ -61,10 +65,6 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
return instance == null ? instance = new WorldStage() : instance;
|
return instance == null ? instance = new WorldStage() : instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Rectangle tempBoundingRect = new Rectangle();
|
|
||||||
final Vector2 enemyMoveVector = new Vector2();
|
|
||||||
|
|
||||||
boolean collided = false;
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActing(float delta) {
|
protected void onActing(float delta) {
|
||||||
if (isPaused() || MapStage.getInstance().isDialogOnlyInput() || Forge.advFreezePlayerControls)
|
if (isPaused() || MapStage.getInstance().isDialogOnlyInput() || Forge.advFreezePlayerControls)
|
||||||
@@ -72,7 +72,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
drawNavigationArrow();
|
drawNavigationArrow();
|
||||||
if (player.isMoving()) {
|
if (player.isMoving()) {
|
||||||
handleMonsterSpawn(delta);
|
handleMonsterSpawn(delta);
|
||||||
handlePointsOfInterestCollision();
|
collided = collided || handlePointsOfInterestCollision();
|
||||||
globalTimer += delta;
|
globalTimer += delta;
|
||||||
Iterator<Pair<Float, EnemySprite>> it = enemies.iterator();
|
Iterator<Pair<Float, EnemySprite>> it = enemies.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
@@ -146,6 +146,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
pair.getValue().setAnimation(CharacterSprite.AnimationTypes.Idle);
|
pair.getValue().setAnimation(CharacterSprite.AnimationTypes.Idle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
collided = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeEnemy(EnemySprite currentMob) {
|
private void removeEnemy(EnemySprite currentMob) {
|
||||||
@@ -200,7 +201,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handlePointsOfInterestCollision() {
|
public boolean handlePointsOfInterestCollision() {
|
||||||
for (Actor actor : foregroundSprites.getChildren()) {
|
for (Actor actor : foregroundSprites.getChildren()) {
|
||||||
if (actor.getClass() == PointOfInterestMapSprite.class) {
|
if (actor.getClass() == PointOfInterestMapSprite.class) {
|
||||||
PointOfInterestMapSprite point = (PointOfInterestMapSprite) actor;
|
PointOfInterestMapSprite point = (PointOfInterestMapSprite) actor;
|
||||||
@@ -215,6 +216,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
WorldSave.getCurrentSave().autoSave();
|
WorldSave.getCurrentSave().autoSave();
|
||||||
loadPOI(point.getPointOfInterest());
|
loadPOI(point.getPointOfInterest());
|
||||||
point.getMapSprite().checkOut();
|
point.getMapSprite().checkOut();
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (point == collidingPoint) {
|
if (point == collidingPoint) {
|
||||||
collidingPoint = null;
|
collidingPoint = null;
|
||||||
@@ -222,6 +224,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadPOI(PointOfInterest poi) {
|
public void loadPOI(PointOfInterest poi) {
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ public class AdventureEventController implements Serializable {
|
|||||||
|
|
||||||
AdventureEventData e;
|
AdventureEventData e;
|
||||||
|
|
||||||
|
// TODO After a certain amount of wins, stop offering jump start events
|
||||||
if (random.nextInt(10) <= 2) {
|
if (random.nextInt(10) <= 2) {
|
||||||
e = new AdventureEventData(eventSeed, EventFormat.Jumpstart);
|
e = new AdventureEventData(eventSeed, EventFormat.Jumpstart);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -336,11 +336,11 @@ public class MapDialog {
|
|||||||
else Current.player().takeGold(-E.addShards);
|
else Current.player().takeGold(-E.addShards);
|
||||||
}
|
}
|
||||||
if (E.addMapReputation != 0) {
|
if (E.addMapReputation != 0) {
|
||||||
if (!E.POIReference.isEmpty() && !E.POIReference.contains("$")) {
|
if (E.POIReference != null && !E.POIReference.isEmpty() && !E.POIReference.contains("$")) {
|
||||||
WorldSave.getCurrentSave().getPointOfInterestChanges(E.POIReference).addMapReputation(E.addMapReputation);
|
WorldSave.getCurrentSave().getPointOfInterestChanges(E.POIReference).addMapReputation(E.addMapReputation);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
stage.getChanges().addMapReputation(E.addMapReputation);
|
stage.getChanges().addMapReputation(E.addMapReputation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (E.deleteMapObject != 0) { //Removes a dummy object from the map.
|
if (E.deleteMapObject != 0) { //Removes a dummy object from the map.
|
||||||
if (E.deleteMapObject < 0) stage.deleteObject(parentID);
|
if (E.deleteMapObject < 0) stage.deleteObject(parentID);
|
||||||
|
|||||||
@@ -378,8 +378,7 @@ public class TextRenderer {
|
|||||||
lastPieceIdx = pieces.size() - 1; //don't re-wrap anything if reached previous line
|
lastPieceIdx = pieces.size() - 1; //don't re-wrap anything if reached previous line
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (lastPiece instanceof TextPiece) {
|
if (lastPiece instanceof TextPiece textPiece) {
|
||||||
TextPiece textPiece = (TextPiece)lastPiece;
|
|
||||||
int index = textPiece.text.lastIndexOf(' ');
|
int index = textPiece.text.lastIndexOf(' ');
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
|
|||||||
@@ -781,23 +781,12 @@ public class CardRenderer {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card.getCurrentRoom() != null && !card.getCurrentRoom().isEmpty()) {
|
if(card.getMarkerText() != null) {
|
||||||
List<String> markers = new ArrayList<>();
|
List<String> markers = card.getMarkerText();
|
||||||
markers.add("In Room:");
|
if(markers.size() > 1) //Use smaller text for multi-line strings.
|
||||||
markers.add(card.getCurrentRoom());
|
drawMarkersTabs(markers, g, x, y, w, h, false);
|
||||||
drawMarkersTabs(markers, g, x, y, w, h, false);
|
else
|
||||||
}
|
drawMarkersTabs(markers, g, x, y - markersHeight, w, h, true);
|
||||||
//Class level
|
|
||||||
if (card.getCurrentState().getType().hasStringType("Class") && ZoneType.Battlefield.equals(card.getZone())) {
|
|
||||||
List<String> markers = new ArrayList<>();
|
|
||||||
markers.add("CL:" + card.getClassLevel());
|
|
||||||
drawMarkersTabs(markers, g, x, y - markersHeight, w, h, true);
|
|
||||||
}
|
|
||||||
//Ring level
|
|
||||||
if (card.getRingLevel() > 0) {
|
|
||||||
List<String> markers = new ArrayList<>();
|
|
||||||
markers.add("RL:" + card.getRingLevel());
|
|
||||||
drawMarkersTabs(markers, g, x, y - markersHeight, w, h, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float otherSymbolsSize = w / 4f;
|
float otherSymbolsSize = w / 4f;
|
||||||
@@ -1528,7 +1517,7 @@ public class CardRenderer {
|
|||||||
int pageSize = 128;
|
int pageSize = 128;
|
||||||
|
|
||||||
//only generate images for characters that could be used by Forge
|
//only generate images for characters that could be used by Forge
|
||||||
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/-+:'";
|
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890/-+:'!—";
|
||||||
|
|
||||||
final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
|
final PixmapPacker packer = new PixmapPacker(pageSize, pageSize, Pixmap.Format.RGBA8888, 2, false);
|
||||||
final FreeTypeFontParameter parameter = new FreeTypeFontParameter();
|
final FreeTypeFontParameter parameter = new FreeTypeFontParameter();
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ public abstract class FScreen extends FContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setHeaderCaption(String headerCaption) {
|
public void setHeaderCaption(String headerCaption) {
|
||||||
if (header instanceof DefaultHeader) {
|
if (header instanceof DefaultHeader dh) {
|
||||||
((DefaultHeader)header).lblCaption.setText(headerCaption);
|
dh.lblCaption.setText(headerCaption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ public abstract class FScreen extends FContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showMenu() {
|
public void showMenu() {
|
||||||
if (header instanceof MenuHeader) {
|
if (header instanceof MenuHeader mh) {
|
||||||
((MenuHeader)header).btnMenu.trigger();
|
mh.btnMenu.trigger();
|
||||||
}
|
}
|
||||||
else { //just so settings screen if no menu header
|
else { //just so settings screen if no menu header
|
||||||
SettingsScreen.show(false);
|
SettingsScreen.show(false);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>forge</artifactId>
|
<artifactId>forge</artifactId>
|
||||||
<groupId>forge</groupId>
|
<groupId>forge</groupId>
|
||||||
<version>${revision}</version>
|
<version>2.0.02</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>forge-gui</artifactId>
|
<artifactId>forge-gui</artifactId>
|
||||||
|
|||||||
@@ -49,7 +49,25 @@
|
|||||||
"Mishra's Workshop",
|
"Mishra's Workshop",
|
||||||
"Yawgmoth's Bargain",
|
"Yawgmoth's Bargain",
|
||||||
"Gaea's Cradle",
|
"Gaea's Cradle",
|
||||||
"Commander's Sphere"
|
"Commander's Sphere",
|
||||||
|
"Drake Stone",
|
||||||
|
"Wrenn and One",
|
||||||
|
"Under-Construction Skyscraper",
|
||||||
|
"Temur Elevator",
|
||||||
|
"Slumbering Waterways",
|
||||||
|
"Omenpath to Naya",
|
||||||
|
"The Heron Moon",
|
||||||
|
"Gobland",
|
||||||
|
"Fetching Garden",
|
||||||
|
"Mox Poison",
|
||||||
|
"Wisedrafter's Will",
|
||||||
|
"New Master of Arms",
|
||||||
|
"Halving Season",
|
||||||
|
"Questing Cosplayer",
|
||||||
|
"Teferi, Druid of Argoth",
|
||||||
|
"Anax and Cymede & Kynaios and Tiro",
|
||||||
|
"Call from the Grave"
|
||||||
|
|
||||||
],
|
],
|
||||||
"restrictedEditions": [
|
"restrictedEditions": [
|
||||||
"HTR",
|
"HTR",
|
||||||
@@ -58,7 +76,6 @@
|
|||||||
"HTR19",
|
"HTR19",
|
||||||
"HTR20",
|
"HTR20",
|
||||||
"PCEL",
|
"PCEL",
|
||||||
"DS0",
|
|
||||||
"HHO",
|
"HHO",
|
||||||
"CMB1",
|
"CMB1",
|
||||||
"UST",
|
"UST",
|
||||||
@@ -67,14 +84,9 @@
|
|||||||
"PPC1",
|
"PPC1",
|
||||||
"UND",
|
"UND",
|
||||||
"PUST",
|
"PUST",
|
||||||
"UEPHI23",
|
"DA1",
|
||||||
"UEMIN23",
|
"UST",
|
||||||
"UELAS23",
|
"UNF"
|
||||||
"UEIND23",
|
|
||||||
"UEBAR23",
|
|
||||||
"MB2",
|
|
||||||
"UNF",
|
|
||||||
"DA1"
|
|
||||||
],
|
],
|
||||||
"difficulties": [
|
"difficulties": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ SVar:DBChoose:DB$ ChooseCard | AtRandom$ True | Choices$ Creature.nonToken+OppCt
|
|||||||
SVar:DBCopy:DB$ CopyPermanent | Defined$ ChosenCard | AddTypes$ Sliver | SubAbility$ DBCleanup
|
SVar:DBCopy:DB$ CopyPermanent | Defined$ ChosenCard | AddTypes$ Sliver | SubAbility$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
||||||
SVar:DBSeek:DB$ Seek | Type$ Card.Sliver | SpellDescription$ Seek a Sliver card.
|
SVar:DBSeek:DB$ Seek | Type$ Card.Sliver | SpellDescription$ Seek a Sliver card.
|
||||||
Oracle:{4}: Create a 1/1 colorless Sliver creature token. Every player may activate this ability but only once each turn. \n At the gebinning of your upkeep, choose one at random\n• Create a 1/1 colorless Sliver creature token.\nCreate a token of a random nontoken creature your opponent controls. That creature becomes a Sliver in addition to its other types.\n• Seek a Sliver card.
|
Oracle:{4}: Create a 1/1 colorless Sliver creature token. Every player may activate this ability but only once each turn. \n At the beginning of your upkeep, choose one at random\n• Create a 1/1 colorless Sliver creature token.\nCreate a token of a random nontoken creature your opponent controls. That creature becomes a Sliver in addition to its other types.\n• Seek a Sliver card.
|
||||||
|
|||||||
@@ -15,17 +15,16 @@ Name=eldrazilarge
|
|||||||
3 Eye of Ugin|J20|1
|
3 Eye of Ugin|J20|1
|
||||||
4 Fellwar Stone|C15|1
|
4 Fellwar Stone|C15|1
|
||||||
2 Flayer of Loyalties|CMM|1
|
2 Flayer of Loyalties|CMM|1
|
||||||
1 It That Betrays|CMM|1
|
2 It That Betrays|CMM|1
|
||||||
1 Kozilek, Butcher of Truth|UMA|1
|
1 Kozilek, Butcher of Truth|UMA|1
|
||||||
4 Matter Reshaper|OGW|1
|
4 Matter Reshaper|OGW|1
|
||||||
1 Not of This World|CMM|1
|
1 Not of This World|CMM|1
|
||||||
4 Reality Smasher|SLD|1
|
4 Reality Smasher|SLD|1
|
||||||
1 Rise of the Eldrazi|CMM|1
|
1 Rise of the Eldrazi|CMM|1
|
||||||
2 Sol Ring|CM2|1
|
|
||||||
4 Thought-Knot Seer|PLIST|1
|
4 Thought-Knot Seer|PLIST|1
|
||||||
4 Thran Dynamo|MB1|1
|
4 Thran Dynamo|MB1|1
|
||||||
1 Ulamog, the Ceaseless Hunger|CMM|1
|
1 Ulamog, the Ceaseless Hunger|CMM|1
|
||||||
1 Ulamog, the Infinite Gyre|2X2|2
|
2 Ulamog, the Infinite Gyre|2X2|2
|
||||||
2 Void Winnower|BFZ|1
|
2 Void Winnower|BFZ|1
|
||||||
23 Wastes|SLD|1
|
23 Wastes|SLD|1
|
||||||
[Sideboard]
|
[Sideboard]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="54">
|
<map version="1.10" tiledversion="1.10.1" orientation="orthogonal" renderorder="right-down" width="30" height="17" tilewidth="16" tileheight="16" infinite="0" nextlayerid="6" nextobjectid="54">
|
||||||
<editorsettings>
|
<editorsettings>
|
||||||
<export target="wastetown..tmx" format="tmx"/>
|
<export target="wastetown..tmx" format="tmx"/>
|
||||||
</editorsettings>
|
</editorsettings>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<object id="53" template="../../obj/enemy.tx" x="197.5" y="138.75">
|
<object id="53" template="../../obj/enemy.tx" x="197.5" y="138.75">
|
||||||
<properties>
|
<properties>
|
||||||
<property name="enemy" value="Mimic"/>
|
<property name="enemy" value="Fog Trap"/>
|
||||||
</properties>
|
</properties>
|
||||||
</object>
|
</object>
|
||||||
</objectgroup>
|
</objectgroup>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user