This commit is contained in:
jjayers99
2023-03-09 22:56:08 -05:00
21 changed files with 94 additions and 123 deletions

View File

@@ -1090,7 +1090,6 @@ public class ChangeZoneAi extends SpellAbilityAi {
// Exile and bounce opponents stuff
if (destination.equals(ZoneType.Exile) || origin.contains(ZoneType.Battlefield)) {
// don't rush bouncing stuff when not going to attack
if (!immediately && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
&& game.getPhaseHandler().isPlayerTurn(ai)
@@ -1433,17 +1432,14 @@ public class ChangeZoneAi extends SpellAbilityAi {
final ZoneType destination = ZoneType.smartValueOf(sa.getParam("Destination"));
final TargetRestrictions tgt = sa.getTargetRestrictions();
CardCollection list = CardLists.getValidCards(ai.getGame().getCardsIn(tgt.getZone()), tgt.getValidTgts(), ai, source, sa);
list = CardLists.getTargetableCards(list, sa);
list.removeAll(sa.getTargets().getTargetCards());
CardCollection list = new CardCollection(CardUtil.getValidCardsToTarget(tgt, sa));
if (list.isEmpty()) {
return false;
}
// target loop
while (sa.getTargets().size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
while (!sa.isMinTargetChosen()) {
// AI Targeting
Card choice = null;

View File

@@ -757,7 +757,7 @@ public class DamageDealAi extends DamageAiBase {
return false;
}
// TODO: Improve Damage, we shouldn't just target the player just because we can
if (sa.canTarget(enemy) && tcs.size() < tgt.getMaxTargets(source, sa)) {
if (sa.canTarget(enemy) && sa.canAddMoreTarget()) {
if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai))
|| (SpellAbilityAi.isSorcerySpeed(sa, ai) && phase.is(PhaseType.MAIN2))
|| ("PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai))

View File

@@ -130,7 +130,7 @@ public class DamagePreventAi extends SpellAbilityAi {
ComputerUtilCard.sortByEvaluateCreature(combatants);
for (final Card c : combatants) {
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && tcs.size() < tgt.getMaxTargets(hostCard, sa)) {
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && sa.canAddMoreTarget()) {
tcs.add(c);
chance = true;
}

View File

@@ -17,6 +17,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.combat.Combat;
import forge.game.cost.Cost;
import forge.game.phase.PhaseHandler;
@@ -24,7 +25,6 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
public class DebuffAi extends SpellAbilityAi {
@@ -195,16 +195,13 @@ public class DebuffAi extends SpellAbilityAi {
*/
private boolean debuffMandatoryTarget(final Player ai, final SpellAbility sa, final boolean mandatory) {
final TargetRestrictions tgt = sa.getTargetRestrictions();
CardCollection list = CardLists.getTargetableCards(ai.getGame().getCardsIn(ZoneType.Battlefield), sa);
List<Card> list = CardUtil.getValidCardsToTarget(tgt, sa);
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
sa.resetTargets();
return false;
}
// Remove anything that's already been targeted
list.removeAll(sa.getTargets().getTargetCards());
final CardCollection pref = CardLists.filterControlledBy(list, ai.getOpponents());
final CardCollection forced = CardLists.filterControlledBy(list, ai);
final Card source = sa.getHostCard();
@@ -219,7 +216,7 @@ public class DebuffAi extends SpellAbilityAi {
sa.getTargets().add(c);
}
while (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
while (!sa.isMinTargetChosen()) {
if (forced.isEmpty()) {
break;
}
@@ -237,13 +234,13 @@ public class DebuffAi extends SpellAbilityAi {
sa.getTargets().add(c);
}
if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
if (!sa.isMinTargetChosen()) {
sa.resetTargets();
return false;
}
return true;
} // pumpMandatoryTarget()
}
@Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {

View File

@@ -20,13 +20,13 @@ import forge.game.ability.effects.ProtectEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.combat.Combat;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import forge.util.MyRandom;
public class ProtectAi extends SpellAbilityAi {
@@ -168,23 +168,23 @@ public class ProtectAi extends SpellAbilityAi {
@Override
protected boolean checkApiLogic(final Player ai, final SpellAbility sa) {
if (!sa.usesTargeting()) {
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
if (cards.size() == 0) {
return false;
} else if (cards.size() == 1) {
// Affecting single card
return getProtectCreatures(ai, sa).contains(cards.get(0));
}
/*
* when this happens we need to expand AI to consider if its ok
* for everything? for (Card card : cards) { // TODO if AI doesn't
* control Card and Pump is a Curse, than maybe use?
* }
*/
} else {
if (sa.usesTargeting()) {
return protectTgtAI(ai, sa, false);
}
final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
if (cards.size() == 0) {
return false;
} else if (cards.size() == 1) {
// Affecting single card
return getProtectCreatures(ai, sa).contains(cards.get(0));
}
/*
* when this happens we need to expand AI to consider if its ok
* for everything? for (Card card : cards) { // TODO if AI doesn't
* control Card and Pump is a Curse, than maybe use?
* }
*/
return false;
}
@@ -256,21 +256,15 @@ public class ProtectAi extends SpellAbilityAi {
} // protectTgtAI()
private static boolean protectMandatoryTarget(final Player ai, final SpellAbility sa) {
final Game game = ai.getGame();
final TargetRestrictions tgt = sa.getTargetRestrictions();
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
final Card source = sa.getHostCard();
final List<Card> list = CardUtil.getValidCardsToTarget(tgt, sa);
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
if (list.size() < tgt.getMinTargets(source, sa)) {
sa.resetTargets();
return false;
}
// Remove anything that's already been targeted
for (final Card c : sa.getTargets().getTargetCards()) {
list.remove(c);
}
CardCollection pref = CardLists.filterControlledBy(list, ai);
pref = CardLists.filter(pref, new Predicate<Card>() {
@Override
@@ -286,7 +280,6 @@ public class ProtectAi extends SpellAbilityAi {
}
});
final List<Card> forced = CardLists.filterControlledBy(list, ai);
final Card source = sa.getHostCard();
while (sa.canAddMoreTarget()) {
if (pref.isEmpty()) {
@@ -308,13 +301,13 @@ public class ProtectAi extends SpellAbilityAi {
sa.getTargets().add(c);
}
while (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
while (!sa.isMinTargetChosen()) {
if (forced.isEmpty()) {
break;
}
Card c;
if (CardLists.getNotType(forced, "Creature").size() == 0) {
if (CardLists.getNotType(forced, "Creature").isEmpty()) {
c = ComputerUtilCard.getWorstCreatureAI(forced);
} else {
c = ComputerUtilCard.getCheapestPermanentAI(forced, sa, false);
@@ -323,7 +316,7 @@ public class ProtectAi extends SpellAbilityAi {
sa.getTargets().add(c);
}
if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
if (!sa.isMinTargetChosen()) {
sa.resetTargets();
return false;
}

View File

@@ -616,23 +616,16 @@ public class PumpAi extends PumpAiBase {
}
private boolean pumpMandatoryTarget(final Player ai, final SpellAbility sa) {
final Game game = ai.getGame();
final TargetRestrictions tgt = sa.getTargetRestrictions();
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
List<Card> list = CardUtil.getValidCardsToTarget(tgt, sa);
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
sa.resetTargets();
return false;
}
// Remove anything that's already been targeted
for (final Card c : sa.getTargets().getTargetCards()) {
list.remove(c);
}
CardCollection pref;
CardCollection forced;
final Card source = sa.getHostCard();
if (sa.isCurse()) {
pref = CardLists.filterControlledBy(list, ai.getOpponents());
@@ -652,7 +645,7 @@ public class PumpAi extends PumpAiBase {
sa.getTargets().add(c);
}
while (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
while (!sa.isMinTargetChosen()) {
if (forced.isEmpty()) {
break;
}
@@ -668,7 +661,7 @@ public class PumpAi extends PumpAiBase {
sa.getTargets().add(c);
}
if (sa.getTargets().size() < tgt.getMinTargets(source, sa)) {
if (!sa.isMinTargetChosen()) {
sa.resetTargets();
return false;
}

View File

@@ -97,13 +97,13 @@ public class SetStateAi extends SpellAbilityAi {
for (final Card c : list) {
if (shouldTransformCard(c, ai, ph) || "Always".equals(logic)) {
sa.getTargets().add(c);
if (sa.getTargets().size() == tgt.getMaxTargets(source, sa)) {
if (sa.isMaxTargetChosen()) {
break;
}
}
}
return sa.getTargets().size() >= tgt.getMinTargets(source, sa);
return sa.isMinTargetChosen();
}
} else if ("TurnFace".equals(mode)) {
if (!sa.usesTargeting()) {

View File

@@ -96,6 +96,13 @@ public class AbilityUtils {
final Game game = hostCard.getGame();
Card c = null;
Player player = null;
if (sa instanceof SpellAbility) {
player = ((SpellAbility)sa).getActivatingPlayer();
}
if (player == null) {
player = hostCard.getController();
}
if (defined.equals("Self")) {
c = hostCard;
@@ -143,7 +150,7 @@ public class AbilityUtils {
}
}
} else if (defined.equals("TopOfGraveyard")) {
final CardCollectionView grave = hostCard.getController().getCardsIn(ZoneType.Graveyard);
final CardCollectionView grave = player.getCardsIn(ZoneType.Graveyard);
if (grave.size() > 0) {
c = grave.getLast();
@@ -153,7 +160,7 @@ public class AbilityUtils {
}
}
else if (defined.endsWith("OfLibrary")) {
final CardCollectionView lib = hostCard.getController().getCardsIn(ZoneType.Library);
final CardCollectionView lib = player.getCardsIn(ZoneType.Library);
int libSize = lib.size();
if (libSize > 0) { // TopOfLibrary or BottomOfLibrary
if (defined.startsWith("TopThird")) {
@@ -354,7 +361,7 @@ public class AbilityUtils {
candidates = game.getCardsIn(ZoneType.smartValueOf(zone));
validDefined = s[1];
}
cards.addAll(CardLists.getValidCards(candidates, validDefined, hostCard.getController(), hostCard, sa));
cards.addAll(CardLists.getValidCards(candidates, validDefined, player, hostCard, sa));
return cards;
} else if (defined.startsWith("ExiledWith")) {
cards.addAll(hostCard.getExiledCards());
@@ -375,7 +382,7 @@ public class AbilityUtils {
for (int i = 0; i < valids.length; i++) {
valids[i] = "Card." + valids[i];
}
cards = CardLists.getValidCards(cards, valids, hostCard.getController(), hostCard, sa);
cards = CardLists.getValidCards(cards, valids, player, hostCard, sa);
}
return cards;

View File

@@ -115,7 +115,7 @@ public class PumpAllEffect extends SpellAbilityEffect {
sb.append(desc);
return sb.toString();
} // pumpAllStackDescription()
}
@Override
public void resolve(final SpellAbility sa) {

View File

@@ -415,10 +415,7 @@ public class PumpEffect extends SpellAbilityEffect {
final ZoneType pumpZone = sa.hasParam("PumpZone") ? ZoneType.smartValueOf(sa.getParam("PumpZone"))
: ZoneType.Battlefield;
final int size = tgtCards.size();
for (int j = 0; j < size; j++) {
final Card tgtC = tgtCards.get(j);
for (Card tgtC : tgtCards) {
// CR 702.26e
if (tgtC.isPhasedOut()) {
continue;

View File

@@ -32,14 +32,6 @@ public class RepeatEachEffect extends SpellAbilityEffect {
final SpellAbility repeat = sa.getAdditionalAbility("RepeatSubAbility");
if (repeat != null && !repeat.getHostCard().equalsWithTimestamp(source)) {
// TODO: for some reason, the host card of the original additional SA is set to the cloned card when
// the ability is copied (e.g. Clone Legion + Swarm Intelligence). Couldn't figure out why this happens,
// so this hack is necessary for now to work around this issue.
System.out.println("Warning: RepeatSubAbility had the wrong host set (potentially after cloning the root SA or changing zones), attempting to correct...");
repeat.setHostCard(source);
}
final Player player = sa.getActivatingPlayer();
final Game game = player.getGame();
if (sa.hasParam("Optional") && sa.hasParam("OptionPrompt") && //for now, OptionPrompt is needed
@@ -74,10 +66,6 @@ public class RepeatEachEffect extends SpellAbilityEffect {
}
else if (sa.hasParam("DefinedCards")) {
repeatCards = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa);
if (sa.hasParam("AdditionalRestriction")) { // lki cards might not be in game
repeatCards = CardLists.getValidCards(repeatCards,
sa.getParam("AdditionalRestriction"), source.getController(), source, sa);
}
}
boolean loopOverCards = repeatCards != null && !repeatCards.isEmpty();
@@ -125,13 +113,10 @@ public class RepeatEachEffect extends SpellAbilityEffect {
// for a mixed list of target permanents and players, e.g. Soulfire Eruption
if (sa.hasParam("RepeatTargeted")) {
final List <GameObject> tgts = getTargets(sa);
if (tgts != null) {
for (final Object o : tgts) {
source.addRemembered(o);
AbilityUtils.resolve(repeat);
source.removeRemembered(o);
}
for (final GameObject o : getTargets(sa)) {
source.addRemembered(o);
AbilityUtils.resolve(repeat);
source.removeRemembered(o);
}
}

View File

@@ -26,17 +26,9 @@ public class RepeatEffect extends SpellAbilityEffect {
// setup subability to repeat
SpellAbility repeat = sa.getAdditionalAbility("RepeatSubAbility");
if (repeat != null && !repeat.getHostCard().equals(source)) {
// TODO: for some reason, the host card of the original additional SA is set to the cloned card when
// the ability is copied (e.g. Clone Legion + Swarm Intelligence). Couldn't figure out why this happens,
// so this hack is necessary for now to work around this issue.
System.out.println("Warning: RepeatSubAbility had the wrong host set (potentially after cloning the root SA), attempting to correct...");
repeat.setHostCard(source);
}
Integer maxRepeat = null;
if (sa.hasParam("MaxRepeat")) {
maxRepeat = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("MaxRepeat"), sa);
maxRepeat = AbilityUtils.calculateAmount(source, sa.getParam("MaxRepeat"), sa);
if (maxRepeat.intValue() == 0) return; // do nothing if maxRepeat is 0. the next loop will execute at least once
}
@@ -50,7 +42,7 @@ public class RepeatEffect extends SpellAbilityEffect {
// Helm of Obedience vs Graveyard to Library replacement effect
if (source.getName().equals("Helm of Obedience")) {
StringBuilder infLoop = new StringBuilder(sa.getHostCard().toString());
StringBuilder infLoop = new StringBuilder(source.toString());
infLoop.append(" - To avoid an infinite loop, this repeat has been broken ");
infLoop.append(" and the game will now continue in the current state, ending the loop early. ");
infLoop.append("Once Draws are available this probably should change to a Draw.");
@@ -84,7 +76,7 @@ public class RepeatEffect extends SpellAbilityEffect {
} else {
list = game.getCardsIn(ZoneType.Battlefield);
}
list = CardLists.getValidCards(list, repeatPresent, sa.getActivatingPlayer(), sa.getHostCard(), sa);
list = CardLists.getValidCards(list, repeatPresent, activator, sa.getHostCard(), sa);
final String rightString = repeatCompare.substring(2);
int right = AbilityUtils.calculateAmount(sa.getHostCard(), rightString, sa);
@@ -114,7 +106,7 @@ public class RepeatEffect extends SpellAbilityEffect {
if (sa.hasParam("RepeatOptional")) {
Player decider = sa.hasParam("RepeatOptionalDecider")
? AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("RepeatOptionalDecider"), sa).get(0)
: sa.getActivatingPlayer();
: activator;
return decider.getController().confirmAction(sa, null, Localizer.getInstance().getMessage("lblDoYouWantRepeatProcessAgain"), null);
}

View File

@@ -63,6 +63,7 @@ public class MapActor extends Actor {
{
ParticleEffect effect = new ParticleEffect();
effect.load(Config.instance().getFile(path),Config.instance().getFile(path).parent());
effect.setPosition(getCenterX(), getCenterY());
effects.add(new CurrentEffect(path, effect, offset, overlay));
if(duration!=0)//ParticleEffect.setDuration uses an integer for some reason
{
@@ -147,16 +148,29 @@ public class MapActor extends Actor {
effect.effect.draw(batch);
}
}
float getCenterX() {
float scale = 1f;
if (this instanceof EnemySprite) {
scale = ((EnemySprite) this).getData().scale;
}
return getX()+(getWidth()*scale)/2;
}
float getCenterY() {
float scale = 1f;
if (this instanceof EnemySprite) {
scale = ((EnemySprite) this).getData().scale;
}
return getY()+(getHeight()*scale)/2;
}
@Override
public void act(float delta) {
super.act(delta);
for(int i=0;i<effects.size;i++)
{
CurrentEffect effect=effects.get(i);
effect.effect.update(delta);
effect.effect.setPosition(getX()+getHeight()/2+effect.offset.x,getY()+getWidth()/2+effect.offset.y);
effect.effect.setPosition(getCenterX()+effect.offset.x,getCenterY()+effect.offset.y);
if(effect.effect.isComplete())
{
effects.removeIndex(i);

View File

@@ -798,9 +798,7 @@ public class MapStage extends GameStage {
Current.player().win();
player.setAnimation(CharacterSprite.AnimationTypes.Attack);
float vx = currentMob.getData().scale == 1f ? 0f : -((currentMob.getWidth()*currentMob.getData().scale)/2);
float vy = currentMob.getData().scale == 1f ? 0f : -((currentMob.getHeight()*currentMob.getData().scale)/2);
currentMob.playEffect(Paths.EFFECT_BLOOD, 0.5f, true, new Vector2(vx, vy));
currentMob.playEffect(Paths.EFFECT_BLOOD, 0.5f);
Timer.schedule(new Timer.Task() {
@Override
public void run() {

View File

@@ -156,9 +156,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
if (playerIsWinner) {
Current.player().win();
player.setAnimation(CharacterSprite.AnimationTypes.Attack);
float vx = currentMob.getData().scale == 1f ? 0f : -((currentMob.getWidth()*currentMob.getData().scale)/2);
float vy = currentMob.getData().scale == 1f ? 0f : -((currentMob.getHeight()*currentMob.getData().scale)/2);
currentMob.playEffect(Paths.EFFECT_BLOOD, 0.5f, true, new Vector2(vx, vy));
currentMob.playEffect(Paths.EFFECT_BLOOD, 0.5f);
Timer.schedule(new Timer.Task() {
@Override
public void run() {

View File

@@ -3,5 +3,6 @@ ManaCost:no cost
Types:Artifact
S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Activated.Equip | Activator$ You | Amount$ 1 | EffectZone$ Command | Description$ Equip costs you pay cost {1} less.
T:Mode$ AttackersDeclared | ValidAttackers$ Creature.modified+YouCtrl | TriggerZones$ Command | Execute$ TrigConjure | TriggerDescription$ Whenever a modified creature you control attacks, you may pay {M}{M}, if you do conjure a random card from Nahiri's Armory's Spellbook into your hand.
SVar:TrigConjure:AB$ MakeCard | Cost$ PayShards<2> | Conjure$ True | AtRandom$ True | Zone$ Hand | Spellbook$ Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn; Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior; Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer AxeSword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan; Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel; Seraph of Steel,Auriok Steelshaper
SVar:TrigConjure:AB$ MakeCard | Cost$ PayShards<2> | Conjure$ True | AtRandom$ True | Zone$ Hand | Spellbook$
Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn; Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior; Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer Axe,Sword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan; Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel; Seraph of Steel,Auriok Steelshaper
Oracle:Equip costs you pay cost {1} less.\nWhenever a modified creature you control attacks, you may pay {M}{M}, if you do conjure a random card from Nahiri's Armory's Spellbook into your hand.

View File

@@ -4,7 +4,7 @@ Types:Enchantment
S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Activated.Equip | Activator$ You | Amount$ 1 | EffectZone$ Command | Description$ Equip costs you pay cost {1} less.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Command | Secondary$ True | Execute $ TrigConjure | TriggerDescription$ At the beginning of your upkeep or when an equipped creature you control attacks, conjure a card from Nahiri's Spellbook into your hand.
T:Mode$ Attacks | ValidCard$ Creature.YouCtrl+equipped | TriggerZones$ Command | Execute$ TrigConjure | TriggerDescription$ At the beginning of your upkeep or when an equipped creature you control attacks, conjure a card from Nahiri's Spellbook into your hand.
SVar:TrigConjure:DB$ MakeCard | Zone$ Hand | Conjure$ True | AtRandom$ True | Spellbook$ Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn, Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior;Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer Axe,Sword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan, Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel, Seraph of Steel,Auriok Steelshaper
SVar:TrigConjure:DB$ MakeCard | Zone$ Hand | Conjure$ True | AtRandom$ True | Spellbook$ Stoneforge Mystic,Danitha; Benalia's Hope,Ardenn, Intrepid Archaeologist,Open the Armory,Stone Haven Outfitter,Argentum Armor,Sword of the Animist,Masterwork of Ingenuity,Kaldra Compleat,Armored Skyhunter,Lion Sash,Relic Seeker,Esper Sentinel,Forgeborn Phoenix,Foundry Beetle,Inchblade Companion,Komainu Battle Armor,Luxior;Giada's Gift,Mace of Disruption,Nettlecyst,Shadowspear,Seraphic Greatsword,Soulstealer Axe,Sword of Body and Mind,Sword of Fire and Ice,Junkyard Scrapper,Soulstealer Axe,Sword of Feast and Famine,Expedition Supplier,Foundry Beetle,Armory Automaton,Bladegraft Aspirant,Dancing Sword,Jor Kadeen; First Goldwarden,Akiri; Fearless Voyager,Acclaimed Contender,Embercleave,Puresteel Paladin,Champion of the Flame,Tiana; Ship's Caretaker,Reyav; Master Smith,Sigarda's Aid,Armored Skyhunter,Bruenor Battlehammer,Halvar; God of Battle,Wyleth; Soul of Steel,Koll; the Forgemaster,Valduk; Keeper of the Flame,Fervent Champion,Cloudsteel Kirin,Leonin Shikari,Balan; Wandering Knight,Kor Duelist,Leonin Abunas,Zamriel;Seraph of Steel,Auriok Steelshaper
T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Command | Execute$ TrigToken | TriggerDescription$ At the beginning of your end step, create a 1/1 white Kor Soldier creature token
SVar:TrigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_kor_soldier
Oracle:Equip costs you pay cost {1} less.\nAt the beginning of your upkeep or whenever an equipped creature you control attacks, conjure a card from Nahiri's Spellbook into your hand\nAt the beginning of your end step, create a 1/1 white Kor Soldier creature token.

View File

@@ -40,7 +40,7 @@
"scale": 3,
"selectedLayer": 4,
"viewCenter": {
"x": 240,
"x": 239.83333333333331,
"y": 136.16666666666663
}
},
@@ -51,7 +51,7 @@
"scale": 2,
"selectedLayer": 4,
"viewCenter": {
"x": 240,
"x": 239.75,
"y": 136.25
}
},
@@ -62,7 +62,7 @@
"scale": 2,
"selectedLayer": 4,
"viewCenter": {
"x": 400,
"x": 399.75,
"y": 319.75
}
},
@@ -2438,7 +2438,7 @@
"scale": 1.5,
"selectedLayer": 5,
"viewCenter": {
"x": 384,
"x": 383.66666666666663,
"y": 383.66666666666663
}
},
@@ -2449,7 +2449,7 @@
"scale": 3,
"selectedLayer": 4,
"viewCenter": {
"x": 240,
"x": 239.83333333333331,
"y": 239.83333333333331
}
},
@@ -2613,7 +2613,7 @@
"scale": 3,
"selectedLayer": 4,
"viewCenter": {
"x": 106,
"x": 106.16666666666669,
"y": 253.83333333333331
}
},
@@ -2624,7 +2624,7 @@
"scale": 1,
"selectedLayer": 4,
"viewCenter": {
"x": 368,
"x": 368.5,
"y": 200.5
}
},
@@ -2632,8 +2632,8 @@
"scale": 1,
"selectedLayer": 1,
"viewCenter": {
"x": 509,
"y": 282.5
"x": 400.5,
"y": 216.5
}
},
"map/nest_white_1.tmx": {
@@ -2894,7 +2894,7 @@
"scale": 3,
"selectedLayer": 4,
"viewCenter": {
"x": 263.3333333333333,
"x": 263.8333333333333,
"y": 191.83333333333331
}
},

View File

@@ -7,12 +7,12 @@
<tileset firstgid="10113" source="../tileset/buildings.tsx"/>
<layer id="1" name="Background" width="30" height="17">
<data encoding="base64" compression="zlib">
eJy9lb9OwzAQhw+J2kUQBiQegwHRgVeAp6mgLEwdgYmpa2f+SOQN8gDMwNhn4AUQd4p/snOxHUdFnPSpapK7z+ezkn1LdMBUzCFT/xNPzDPzwrwW5twYovs9ogfmjrk2472rCdFnhFxOLP7aG/4viW29OYa84bNL21KyZ+hPZjjkOVY1Ul59bnNe+RV36v6YfvW5TeUiX9dJcbLj895s33sxJbqcdr2xvUr5Uvfg1dfD+V6Zfr9yvoTG7ePCdPM3fP3H5Syq7nyxbtRcRvqNnStZ65o8X5O21ofyxuaa6zEk551bzznnn1beJV64hSMb3/d34/tduTWsKe/93u32DWqXK7Vmpn1+pt6NG1dnbr0LdWP9l8SZOg+Yp36fwofaj2oG8kxD5V4d4r2t/HwRtfJiz/X3bhtvGHCH/cAphN+7Mfs85A0DXsy5of4shF8B8frL
eJy9lTtOxDAQhgeJtRdBKJA4BgViC64Ap1nB0lBtCVRU227NQyI3yAGogXLPwAUQM4p/2ZnYjqNFjPQriu2Zz/NQsm+JDlgV65BV/5OeWM+sF9Zroc+NIbrfI3pg3bGuzXjuakL0GVHOJ2Z/zQ3fS2xbbk5D3PDs0rYqqRnykx4OcY5VjBRXz22OK09hp/bH5KvnNuULfx0npZMd7/dm+9yLKdHltMuN1SrFS+2Bq9fD/l6Zfr4yX6LG1XFhuv4bXv9xPouq21/cGzGXkXxjcyV3XZPX16SN9aG4sb7mcgyV486t1zn7n1aeJVyw5fyRjdf93fh8V+4Oa8pzv3e7eUO185VYM9Oen6lv48bFmVvPQtxY/iV2puYB/dTfU/AQ+9H2Z7Khcq424d5Wvr+wWnFRc/2/24YbGthhPmCKwv/dmDoPcUMDF31uqN8L0S8+a/oe
</data>
</layer>
<layer id="2" name="Ground" width="30" height="17">
<data encoding="base64" compression="zlib">
eJytlL9OwzAQxq8tidNMFMRaGACxABJi4BE6ULpXwFPACyAUmBgrAc8AiDdAwBqJAcTKDO+AuE+OZcdxwDE96VMT1+ffxfdnElGQXc0QLbWJFtvN/LYK3iSQe9HVamIvAdyjmOg4LjOVNlKizdT/LJO7mxA9WPcG1h6vX/O9Zl0tFxt68/yO7ViyIOQrM3xfI/n+lZSZ0+AuiPJ5PaH/mxVVns0e0HS4tp+Ljdjmhft7fev7Ly5sJyrrW8i843m/Q3TA6rNOW0QnrDtRPeM37lrqd0+oaewfJrLmoDHX3a3wY8JwX2Aj5sOOXMs45jPWeau6/z2S+5WQ30ERf8+TCUMfrVvnL3OOVlirjlzZXMQ7J6SaGLifls8j855YzzU10g9k2VzkRhnmri3T0O9jay3ERomu4/uafr0Rko+ZhV7Jix66/Adf1TNqc+SYS64ZlRu9CzZ6Cgrhmvog/aueTeXWzFD7fHuojmsybS7ec4cP+E24P6PWfGc=
eJytlMsuBEEUhs9ceqqnV4bYYoHYIBELjzALzF7wFLyASLOynATPgHgDwbYTC2JrzTuI86e60tWnq011ZU7yp29V9Z0+t3FEQXbTJVpqEy22m+3bynnjQO5Vv1ATewvgnvSITntlptFGQrSZ+J9lc3djoicRN7D2+f0txzXtF3KxoQ/P/9juaRaEfKXW3vdIP//EZeY0uPOqfN5AFd9mVJUn2UOaDlfuc7Hh25xy/69vfU/iwnaisn6VzjvuDztER6wF1nmL6Iz1oKpn/MddS/zihJrG+r1Y1xx0wHV3r/yYMMQLbPh83NHvUvb5gnXZqq7/jPR6I+R3mPs/8GTC0Efr4vxlztEKa9WRK8mFv7NKq4mB+y32PDPvhfVaUyPwM4QluciNMcxdKdvQ7zI+ITaKizp+rOnXO6X5mFnolSzvoevu5PPrzNQzanPkmEuuGZVZvQs2egoK4dr6ouJq7m1lYmaYdb49VMe1mZKL58yxB/wm3D9fB3xE
</data>
</layer>
<layer id="3" name="Clutter" width="30" height="17">
@@ -20,7 +20,7 @@
<property name="spriteLayer" type="bool" value="true"/>
</properties>
<data encoding="base64" compression="zlib">
eJxjYGBgKOdloBuooJNdn1hoZ3YVHj9MYCXOjIXM5NtDSXzZI4VLCdScSizmVdMxTcAANn89YyDNLcSqhakrGQB/4gOv2MnXixx+rESkL3R7saUDUsFiPPZSK6yxhZEjEfmdXmUPDFASl5SYQy170QEbN4SmZ31BbH7GVyajA0rSIS6/w8SfUWAmstmkhDE56RoAwkMY7Q==
eJxjYGBgKOdloBuooJNdn1hoZ3YVHj9MYCXOjIXM5NtDSXzZI4VLCdScSizmVdMxTcAANn89YyDNLcSqhakrGQB/4gOv2MnXixx+rESkL3R7saUDUsFiPPZSK6yxhZEjEfmdXmUPDGBzpy4jdcwhRz05diMDNm4ITc/6gtj8jK9MRgeUpENcfoeJP6PATGSzSQljctI1AJToGUk=
</data>
</layer>
<objectgroup id="4" name="Objects">

View File

@@ -3,7 +3,7 @@ ManaCost:1 B B
Types:Enchantment
T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of each end step, each player who tapped a land for mana this turn sacrifices a land. Desolation deals 2 damage to each player who sacrificed a Plains this way.
SVar:TrigSac:DB$ Sacrifice | SacValid$ Land | Defined$ Player.TappedLandForManaThisTurn | RememberSacrificed$ True | SubAbility$ DBRepeat
SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ Remembered | AdditionalRestriction$ Plains | UseImprinted$ True | RepeatSubAbility$ DBDamage | SubAbility$ DBCleanup
SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ Remembered.Plains | UseImprinted$ True | RepeatSubAbility$ DBDamage | SubAbility$ DBCleanup
SVar:DBDamage:DB$ DealDamage | Defined$ ImprintedController | NumDmg$ 2
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
AI:RemoveDeck:All

View File

@@ -2,7 +2,7 @@ Name:Wave of Vitriol
ManaCost:5 G G
Types:Sorcery
A:SP$ SacrificeAll | Cost$ 5 G G | ValidCards$ Artifact,Enchantment,Land.nonBasic | RememberSacrificed$ True | SubAbility$ DBRepeat | SpellDescription$ Each player sacrifices all artifacts, enchantments, and nonbasic lands they control. For each land sacrificed this way, its controller may search their library for a basic land card and put it onto the battlefield tapped. Then each player who searched their library this way shuffles.
SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ DirectRemembered | AdditionalRestriction$ Land | UseImprinted$ True | RepeatSubAbility$ DBSearch | ClearRemembered$ True | SubAbility$ DBShuffle
SVar:DBRepeat:DB$ RepeatEach | DefinedCards$ DirectRemembered.Land | UseImprinted$ True | RepeatSubAbility$ DBSearch | ClearRemembered$ True | SubAbility$ DBShuffle
SVar:DBSearch:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Land.Basic | ChangeNum$ 1 | Tapped$ True | RememberChanged$ True | DefinedPlayer$ ImprintedController | Chooser$ ImprintedController | NoShuffle$ True | Optional$ True
SVar:DBShuffle:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ ShuffleSearched | SubAbility$ DBCleanup
SVar:ShuffleSearched:DB$ Shuffle | Defined$ Player.IsRemembered | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1