diff --git a/forge-ai/src/main/java/forge/ai/AiBlockController.java b/forge-ai/src/main/java/forge/ai/AiBlockController.java
index 16473ed204a..59f16a3a75d 100644
--- a/forge-ai/src/main/java/forge/ai/AiBlockController.java
+++ b/forge-ai/src/main/java/forge/ai/AiBlockController.java
@@ -570,7 +570,7 @@ public class AiBlockController {
// Try to block a Menace attacker with two blockers, neither of which will die
for (final Card attacker : attackersLeft) {
- if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) <= 1) {
+ if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) != 2) {
continue;
}
@@ -741,16 +741,14 @@ public class AiBlockController {
combat.addBlocker(attacker, blocker);
usedBlockers.add(blocker);
if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size(), combat)) {
+ attackersLeft.remove(attacker);
+ usedBlockers.clear();
break;
}
}
}
- if (CombatUtil.canAttackerBeBlockedWithAmount(attacker, usedBlockers.size(), combat)) {
- attackersLeft.remove(attacker);
- } else {
- for (Card blocker : usedBlockers) {
- combat.removeBlockAssignment(attacker, blocker);
- }
+ for (Card blocker : usedBlockers) {
+ combat.removeBlockAssignment(attacker, blocker);
}
}
}
@@ -767,10 +765,7 @@ public class AiBlockController {
tramplingAttackers = CardLists.filter(tramplingAttackers, Predicates.not(changesPTWhenBlocked(true)));
for (final Card attacker : tramplingAttackers) {
- boolean staticAssignCombatDamageAsUnblocked = StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker);
-
- if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > combat.getBlockers(attacker).size()
- || attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
+ if (CombatUtil.getMinNumBlockersForAttacker(attacker, combat.getDefenderPlayerByAttacker(attacker)) > combat.getBlockers(attacker).size()) {
continue;
}
@@ -800,7 +795,7 @@ public class AiBlockController {
}
}
- if (staticAssignCombatDamageAsUnblocked) {
+ if (!needsMoreChumpBlockers || StaticAbilityAssignCombatDamageAsUnblocked.assignCombatDamageAsUnblocked(attacker)) {
continue;
}
@@ -935,21 +930,21 @@ public class AiBlockController {
CardLists.sortByPowerAsc(chumpPWDefenders);
if (!chumpPWDefenders.isEmpty()) {
for (final Card attacker : attackers) {
+ if (attacker.hasKeyword(Keyword.TRAMPLE)) {
+ // don't bother trying to chump a trampling creature
+ continue;
+ }
+ if (!combat.getBlockers(attacker).isEmpty()) {
+ // already blocked by something, no need to chump
+ continue;
+ }
GameEntity def = combat.getDefenderByAttacker(attacker);
if (def instanceof Card && threatenedPWs.contains(def)) {
- if (attacker.hasKeyword(Keyword.TRAMPLE)) {
- // don't bother trying to chump a trampling creature
- continue;
- }
- if (!combat.getBlockers(attacker).isEmpty()) {
- // already blocked by something, no need to chump
- continue;
- }
Card blockerDecided = null;
for (final Card blocker : chumpPWDefenders) {
if (CombatUtil.canBlock(attacker, blocker, combat)) {
combat.addBlocker(attacker, blocker);
- pwsWithChumpBlocks.add((Card) combat.getDefenderByAttacker(attacker));
+ pwsWithChumpBlocks.add((Card) def);
chosenChumpBlockers.add(blocker);
blockerDecided = blocker;
blockersLeft.remove(blocker);
@@ -962,9 +957,9 @@ public class AiBlockController {
// check to see if we managed to cover all the blockers of the planeswalker; if not, bail
for (final Card pw : pwsWithChumpBlocks) {
CardCollection pwAttackers = combat.getAttackersOf(pw);
- CardCollection pwDefenders = new CardCollection();
- boolean isFullyBlocked = true;
if (!pwAttackers.isEmpty()) {
+ CardCollection pwDefenders = new CardCollection();
+ boolean isFullyBlocked = true;
int damageToPW = 0;
for (Card pwAtk : pwAttackers) {
if (!combat.getBlockers(pwAtk).isEmpty()) {
diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
index 5b2e1834c62..5a949ebac44 100644
--- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
+++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java
@@ -23,7 +23,10 @@ import forge.game.ability.effects.CharmEffect;
import forge.game.card.*;
import forge.game.card.CardPredicates.Presets;
import forge.game.combat.Combat;
-import forge.game.cost.*;
+import forge.game.cost.Cost;
+import forge.game.cost.CostEnlist;
+import forge.game.cost.CostPart;
+import forge.game.cost.CostPartMana;
import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordInterface;
import forge.game.mana.Mana;
@@ -257,9 +260,6 @@ public class PlayerControllerAi extends PlayerController {
public boolean confirmTrigger(WrappedAbility wrapper) {
final SpellAbility sa = wrapper.getWrappedAbility();
//final Trigger regtrig = wrapper.getTrigger();
- if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Deathmist Raptor")) {
- return true;
- }
if (wrapper.isMandatory()) {
return true;
}
diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java
index dee1be9b1bc..9edb682eabc 100644
--- a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java
+++ b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java
@@ -75,6 +75,9 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
}
return true; // perhaps the opponent(s) had Sigarda, Heron's Grace or another effect giving hexproof in play, still play the creature as 6/6
}
+ if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Deathmist Raptor")) {
+ return true;
+ }
return super.doTriggerAINoCost(aiPlayer, sa, mandatory);
}
diff --git a/forge-ai/src/main/java/forge/ai/ability/FightAi.java b/forge-ai/src/main/java/forge/ai/ability/FightAi.java
index 3d1eb307a79..265b74cc85f 100644
--- a/forge-ai/src/main/java/forge/ai/ability/FightAi.java
+++ b/forge-ai/src/main/java/forge/ai/ability/FightAi.java
@@ -1,7 +1,5 @@
package forge.ai.ability;
-import java.util.List;
-
import forge.ai.*;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
@@ -17,6 +15,8 @@ import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerType;
import forge.util.MyRandom;
+import java.util.List;
+
public class FightAi extends SpellAbilityAi {
@Override
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
@@ -31,10 +31,6 @@ public class FightAi extends SpellAbilityAi {
// everything is defined or targeted above, can't do anything there unless a specific logic is set
if (sa.hasParam("Defined") && !sa.usesTargeting()) {
// TODO extend Logic for cards like Arena
- if ("Grothama".equals(sa.getParam("AILogic"))) { // Grothama, All-Devouring
- return SpecialCardAi.GrothamaAllDevouring.consider(ai, sa);
- }
-
return true;
}
@@ -120,6 +116,11 @@ public class FightAi extends SpellAbilityAi {
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
+ final String aiLogic = sa.getParamOrDefault("AILogic", "");
+ if (aiLogic.equals("Grothama")) {
+ return mandatory ? true : SpecialCardAi.GrothamaAllDevouring.consider(ai, sa);
+ }
+
if (checkApiLogic(ai, sa)) {
return true;
}
diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java
index d6918135c25..dec1b885798 100644
--- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java
+++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java
@@ -394,7 +394,7 @@ public abstract class SpellAbilityEffect {
public static void addForgetOnMovedTrigger(final Card card, final String zone) {
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ " + zone + " | ExcludedDestinations$ Stack,Exile | Destination$ Any | TriggerZones$ Command | Static$ True";
- String trig2 = "Mode$ Exiled | ValidCard$ Card.IsRemembered | TriggerZones$ Command | Static$ True";
+ String trig2 = "Mode$ Exiled | ValidCard$ Card.IsRemembered | ValidCause$ SpellAbility.!EffectSource | TriggerZones$ Command | Static$ True";
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
final Trigger parsedTrigger2 = TriggerHandler.parseTrigger(trig2, card, true);
diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java b/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java
index 5cde2db1c35..0e952545a8c 100644
--- a/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java
+++ b/forge-game/src/main/java/forge/game/trigger/TriggerExiled.java
@@ -73,19 +73,8 @@ public class TriggerExiled extends Trigger {
return false;
}
- if (hasParam("ValidCause")) {
- if (!runParams.containsKey(AbilityKey.Cause)) {
- return false;
- }
- SpellAbility cause = (SpellAbility) runParams.get(AbilityKey.Cause);
- if (cause == null) {
- return false;
- }
- if (!matchesValid(cause, getParam("ValidCause").split(","))) {
- if (!matchesValid(cause.getHostCard(), getParam("ValidCause").split(","))) {
- return false;
- }
- }
+ if (!matchesValidParam("ValidCause", runParams.get(AbilityKey.Cause))) {
+ return false;
}
return true;
diff --git a/forge-gui/res/adventure/common/custom_card_pics/Hallowed Sigil.fullborder.jpg b/forge-gui/res/adventure/common/custom_card_pics/Hallowed Sigil.fullborder.jpg
new file mode 100644
index 00000000000..73264fca8e8
Binary files /dev/null and b/forge-gui/res/adventure/common/custom_card_pics/Hallowed Sigil.fullborder.jpg differ
diff --git a/forge-gui/res/adventure/common/custom_card_pics/Power of Valyx.fullborder.jpg b/forge-gui/res/adventure/common/custom_card_pics/Power of Valyx.fullborder.jpg
new file mode 100644
index 00000000000..555669cb6c7
Binary files /dev/null and b/forge-gui/res/adventure/common/custom_card_pics/Power of Valyx.fullborder.jpg differ
diff --git a/forge-gui/res/adventure/common/custom_card_pics/Sigil of Torment.fullborder.jpg b/forge-gui/res/adventure/common/custom_card_pics/Sigil of Torment.fullborder.jpg
new file mode 100644
index 00000000000..2f2c0f9579e
Binary files /dev/null and b/forge-gui/res/adventure/common/custom_card_pics/Sigil of Torment.fullborder.jpg differ
diff --git a/forge-gui/res/adventure/common/custom_cards/hallowed_sigil.txt b/forge-gui/res/adventure/common/custom_cards/hallowed_sigil.txt
new file mode 100644
index 00000000000..f5e6d9e9700
--- /dev/null
+++ b/forge-gui/res/adventure/common/custom_cards/hallowed_sigil.txt
@@ -0,0 +1,7 @@
+Name:Hallowed Sigil
+ManaCost:no cost
+Colors:white
+Types:Enchantment
+A:AB$ Pump | Cost$ PayShards<4> T | ValidTgts$ Creature.YouCtrl | KW$ Hexproof | SubAbility$ Eject | ActivationLimit$ 1 | SpellDescription$ Target creature you control gains hexproof until end of turn. Exile Hallowed Sigil.
+SVar:Eject:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile
+Oracle:{M},{T}:Target creature you control gains hexproof until end of turn. Exile Hallowed Sigil.
diff --git a/forge-gui/res/adventure/common/custom_cards/sigil_of_torment.txt b/forge-gui/res/adventure/common/custom_cards/sigil_of_torment.txt
new file mode 100644
index 00000000000..22e6086f09e
--- /dev/null
+++ b/forge-gui/res/adventure/common/custom_cards/sigil_of_torment.txt
@@ -0,0 +1,8 @@
+Name:Sigil of Torment
+ManaCost:no cost
+Colors:black
+Types:Enchantment
+A:AB$ Destroy | Cost$ 4 B T PayShards<5> | ValidTgts$ Creature | TgtPrompt$ Select target creature | ActivationLimit$ 1 | SubAbility$ DBLifeGain | SpellDescription$ Destroy target creature. You gain 3 life. Exile Sigil of Torment.
+SVar:DBLifeGain:DB$ GainLife | Defined$ You | LifeAmount$ 3 | SubAbility$ Eject
+SVar:Eject:DB$ ChangeZone | Defined$ Self | Origin$ Battlefield | Destination$ Exile
+Oracle:{M},{T}: Destroy target creature. You gain 3 life. Exile Sigil of Torment.
diff --git a/forge-gui/res/adventure/common/custom_cards/valyx_boss_effect.txt b/forge-gui/res/adventure/common/custom_cards/valyx_boss_effect.txt
new file mode 100644
index 00000000000..ce73b9bb2ae
--- /dev/null
+++ b/forge-gui/res/adventure/common/custom_cards/valyx_boss_effect.txt
@@ -0,0 +1,8 @@
+Name:Power of Valyx
+ManaCost:no cost
+Colors:black
+Types:Enchantment
+K:Hexproof
+A:AB$ Destroy | Cost$ 4 B T Sac<1/Creature.YouCtrl/creature you control>| ValidTgts$ Creature | TgtPrompt$ Select target creature | SubAbility$ DBLifeGain | SpellDescription$ Destroy target creature. You gain 3 life.
+SVar:DBLifeGain:DB$ GainLife | Defined$ You | LifeAmount$ 3
+Oracle:{M},{T}, Sacrifice a creature: Destroy target creature. You gain 3 life.
diff --git a/forge-gui/res/adventure/common/decks/miniboss/valyx.dck b/forge-gui/res/adventure/common/decks/miniboss/valyx.dck
new file mode 100644
index 00000000000..71c1a9830b2
--- /dev/null
+++ b/forge-gui/res/adventure/common/decks/miniboss/valyx.dck
@@ -0,0 +1,23 @@
+[metadata]
+Name=valyx
+[Main]
+3 Damnation|MM3|1
+2 Deathrender|CNS|1
+2 Deathrender|LRW|1
+1 Doomed Dissenter|DBL|1
+2 Doomed Dissenter|GN3|1
+1 Ecstatic Awakener|DBL|1
+2 Ecstatic Awakener|MID|1
+3 Indulgent Tormentor|PM15|1
+3 Lord of the Void|GTC|1
+4 Mark of the Oni|BOK|1
+3 Murder|CMR|1
+1 Phyrexian Reclamation|C15|1
+1 Phyrexian Reclamation|J22|1
+2 Reaper from the Abyss|J22|1
+3 Sign in Blood|SCD|1
+1 Skirsdag High Priest|C14|1
+2 Skirsdag High Priest|J22|1
+19 Swamp|MOM|1
+1 Swamp|MOM|3
+4 Westvale Abbey|SOI|1
diff --git a/forge-gui/res/adventure/common/decks/standard/cultist.dck b/forge-gui/res/adventure/common/decks/standard/cultist.dck
new file mode 100644
index 00000000000..fb5a5e767f6
--- /dev/null
+++ b/forge-gui/res/adventure/common/decks/standard/cultist.dck
@@ -0,0 +1,48 @@
+[metadata]
+Name=cultist
+[Avatar]
+
+[Main]
+2 Bloodgift Demon|SCD|1
+2 Bloodsoaked Champion|CLB|1
+2 Bloodsoaked Champion|NCC|1
+2 Demon of Catastrophes|J22|1
+1 Doomed Dissenter|BBD|1
+1 Doomed Dissenter|MB1|1
+2 Ecstatic Awakener|DBL|1
+2 Ecstatic Awakener|MID|1
+2 Feaster of Fools|MH1|1
+1 Grave Pact|10E|1
+2 Grave Pact|CM2|1
+1 Grave Pact|COM|1
+2 Graven Archfiend|YSNC|1
+1 Grim Haruspex|C19|1
+1 Grim Haruspex|CLB|1
+2 Harvester of Souls|CN2|1
+2 Herald of Torment|BNG|1
+1 Murder|CMR|1
+2 Murder|SNC|1
+2 Sign in Blood|ARC|1
+1 Sign in Blood|STA|1
+4 Skirsdag High Priest|2XM|1
+11 Swamp|MOM|1
+2 Swamp|SHM|1
+2 Swamp|SHM|2
+5 Swamp|SHM|3
+2 Swamp|SHM|4
+[Sideboard]
+2 Culling Dais|2XM|1
+2 Furnace Celebration|CMR|1
+4 Glaring Spotlight|GTC|1
+2 Grim Return|M14|1
+1 Lord of the Void|GTC|1
+2 Ravenous Demon|DKA|1
+2 Reaper from the Abyss|C14|1
+[Planes]
+
+[Schemes]
+
+[Conspiracy]
+
+[Dungeon]
+
diff --git a/forge-gui/res/adventure/common/maps/map/desertbuildingtiles.tsx b/forge-gui/res/adventure/common/maps/map/desertbuildingtiles.tsx
deleted file mode 100644
index f36c022034a..00000000000
--- a/forge-gui/res/adventure/common/maps/map/desertbuildingtiles.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/forge-gui/res/adventure/common/maps/map/naktamun.tmx b/forge-gui/res/adventure/common/maps/map/naktamun.tmx
index 4a9104791b2..ff3edbae4ae 100644
--- a/forge-gui/res/adventure/common/maps/map/naktamun.tmx
+++ b/forge-gui/res/adventure/common/maps/map/naktamun.tmx
@@ -1,5 +1,5 @@
-