Merge branch 'Card-Forge:master' into adventureupdatee

This commit is contained in:
Simisays
2023-03-22 15:04:23 +01:00
committed by GitHub
132 changed files with 1534 additions and 851 deletions

View File

@@ -163,6 +163,7 @@ public enum SpellApiToAi {
.put(ApiType.Sacrifice, SacrificeAi.class)
.put(ApiType.SacrificeAll, SacrificeAllAi.class)
.put(ApiType.Scry, ScryAi.class)
.put(ApiType.Seek, AlwaysPlayAi.class)
.put(ApiType.SetInMotion, AlwaysPlayAi.class)
.put(ApiType.SetLife, LifeSetAi.class)
.put(ApiType.SetState, SetStateAi.class)

View File

@@ -26,7 +26,7 @@ public class CharmAi extends SpellAbilityAi {
@Override
protected boolean checkApiLogic(Player ai, SpellAbility sa) {
final Card source = sa.getHostCard();
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa);
List<AbilitySub> choices = CharmEffect.makePossibleOptions(sa, false);
final int num;
final int min;

View File

@@ -380,6 +380,8 @@ public final class GameActionUtil {
return costs;
}
sa.clearPipsToReduce();
Card source = sa.getHostCard();
final Game game = source.getGame();
boolean lkicheck = false;

View File

@@ -166,7 +166,14 @@ public class GameEntityCounterTable extends ForwardingTable<Optional<Player>, Ga
for (Map.Entry<Optional<Player>, Map<CounterType, Integer>> e : values.entrySet()) {
boolean remember = cause != null && cause.hasParam("RememberPut");
for (Map.Entry<CounterType, Integer> ec : e.getValue().entrySet()) {
gm.getKey().addCounterInternal(ec.getKey(), ec.getValue(), e.getKey().orNull(), true, result, runParams);
Integer value = ec.getValue();
if (value == null) {
continue;
}
if (cause != null && cause.hasParam("MaxFromEffect")) {
value = Math.min(value, Integer.parseInt(cause.getParam("MaxFromEffect")) - gm.getKey().getCounters(ec.getKey()));
}
gm.getKey().addCounterInternal(ec.getKey(), value, e.getKey().orNull(), true, result, runParams);
if (remember && ec.getValue() >= 1) {
cause.getHostCard().addRemembered(gm.getKey());
}

View File

@@ -166,7 +166,13 @@ public class AbilityUtils {
if (defined.startsWith("TopThird")) {
int third = defined.contains("RoundedDown") ? (int) Math.floor(libSize / 3.0)
: (int) Math.ceil(libSize / 3.0);
for (int i=0; i<third; i++) {
for (int i = 0; i < third; i++) {
cards.add(lib.get(i));
}
} else if (defined.startsWith("Top_")) {
String[] parts = defined.split("_");
int amt = AbilityUtils.calculateAmount(hostCard, parts[1], sa);
for (int i = 0; i < amt; i++) {
cards.add(lib.get(i));
}
} else {

View File

@@ -162,6 +162,7 @@ public enum ApiType {
Sacrifice (SacrificeEffect.class),
SacrificeAll (SacrificeAllEffect.class),
Scry (ScryEffect.class),
Seek (SeekEffect.class),
SetInMotion (SetInMotionEffect.class),
SetLife (LifeSetEffect.class),
SetState (SetStateEffect.class),

View File

@@ -20,7 +20,7 @@ import forge.util.collect.FCollection;
public class CharmEffect extends SpellAbilityEffect {
public static List<AbilitySub> makePossibleOptions(final SpellAbility sa) {
public static List<AbilitySub> makePossibleOptions(final SpellAbility sa, boolean forDesc) {
final Card source = sa.getHostCard();
List<String> restriction = null;
@@ -29,16 +29,19 @@ public class CharmEffect extends SpellAbilityEffect {
}
List<AbilitySub> choices = Lists.newArrayList(sa.getAdditionalAbilityList("Choices"));
List<AbilitySub> toRemove = Lists.newArrayList();
for (AbilitySub ch : choices) {
// 603.3c If one of the modes would be illegal, that mode can't be chosen.
if ((ch.usesTargeting() && ch.isTrigger() && ch.getMinTargets() > 0 &&
ch.getTargetRestrictions().getNumCandidates(ch, true) == 0) ||
(restriction != null && restriction.contains(ch.getDescription()))) {
toRemove.add(ch);
if (!forDesc) {
List<AbilitySub> toRemove = Lists.newArrayList();
for (AbilitySub ch : choices) {
// 603.3c If one of the modes would be illegal, that mode can't be chosen.
if ((ch.usesTargeting() && ch.isTrigger() && ch.getMinTargets() > 0 &&
ch.getTargetRestrictions().getNumCandidates(ch, true) == 0) ||
(restriction != null && restriction.contains(ch.getDescription()))) {
toRemove.add(ch);
}
}
choices.removeAll(toRemove);
}
choices.removeAll(toRemove);
int indx = 0;
// set CharmOrder
@@ -52,7 +55,7 @@ public class CharmEffect extends SpellAbilityEffect {
public static String makeFormatedDescription(SpellAbility sa) {
Card source = sa.getHostCard();
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa);
List<AbilitySub> list = CharmEffect.makePossibleOptions(sa, true);
final int num;
boolean additionalDesc = sa.hasParam("AdditionalDescription");
boolean optional = sa.hasParam("Optional");
@@ -166,7 +169,7 @@ public class CharmEffect extends SpellAbilityEffect {
//this resets all previous choices
sa.setSubAbility(null);
List<AbilitySub> choices = makePossibleOptions(sa);
List<AbilitySub> choices = makePossibleOptions(sa, false);
// Entwine does use all Choices
if (sa.isEntwine()) {

View File

@@ -258,7 +258,7 @@ public class CopyPermanentEffect extends TokenEffectBase {
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged());
}
} // end resolve
}
public static Card getProtoType(final SpellAbility sa, final Card original, final Player newOwner) {
final Card host = sa.getHostCard();

View File

@@ -0,0 +1,99 @@
package forge.game.ability.effects;
import com.google.common.collect.Lists;
import forge.game.Game;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.*;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class SeekEffect extends SpellAbilityEffect {
/* (non-Javadoc)
* @see forge.game.ability.SpellAbilityEffect#getStackDescription(forge.game.spellability.SpellAbility)
*/
@Override
protected String getStackDescription(SpellAbility sa) {
return sa.getDescription();
}
@Override
public void resolve(SpellAbility sa) {
final Card source = sa.getHostCard();
final Game game = source.getGame();
List<String> seekTypes = Lists.newArrayList();
if (sa.hasParam("Types")) {
seekTypes.addAll(Arrays.asList(sa.getParam("Types").split(",")));
} else {
seekTypes.add(sa.getParamOrDefault("Type", "Card"));
}
int seekNum = AbilityUtils.calculateAmount(source, sa.getParamOrDefault("Num", "1"), sa);
if (seekNum <= 0) {
return;
}
final CardZoneTable triggerList = new CardZoneTable();
CardCollectionView lastStateBattlefield = game.copyLastStateBattlefield();
CardCollectionView lastStateGraveyard = game.copyLastStateGraveyard();
for (Player seeker : getTargetPlayers(sa)) {
if (!seeker.isInGame()) {
continue;
}
CardCollection soughtCards = new CardCollection();
for (String seekType : seekTypes) {
CardCollection pool;
if (sa.hasParam("DefinedCards")) {
pool = AbilityUtils.getDefinedCards(source, sa.getParam("DefinedCards"), sa);
} else {
pool = new CardCollection(seeker.getCardsIn(ZoneType.Library));
}
if (!seekType.equals("Card")) {
pool = CardLists.getValidCards(pool, seekType, source.getController(), source, sa);
}
if (pool.isEmpty()) {
continue; // can't find if nothing to seek
}
for (final Card c : Aggregates.random(pool, seekNum)) {
Map<AbilityKey, Object> moveParams = AbilityKey.newMap();
moveParams.put(AbilityKey.LastStateBattlefield, lastStateBattlefield);
moveParams.put(AbilityKey.LastStateGraveyard, lastStateGraveyard);
Card movedCard = game.getAction().moveToHand(c, sa, moveParams);
ZoneType resultZone = movedCard.getZone().getZoneType();
if (!resultZone.equals(ZoneType.Library)) { // as long as it moved we add to triggerList
triggerList.put(ZoneType.Library, movedCard.getZone().getZoneType(), movedCard);
}
if (resultZone.equals(ZoneType.Hand)) { // if it went to hand as planned, consider it "sought"
soughtCards.add(movedCard);
}
}
}
if (!soughtCards.isEmpty()) {
if (sa.hasParam("RememberFound")) {
source.addRemembered(soughtCards);
}
if (sa.hasParam("ImprintFound")) {
source.addImprintedCards(soughtCards);
}
game.getTriggerHandler().runTrigger(TriggerType.SeekAll, AbilityKey.mapFromPlayer(seeker), false);
}
}
triggerList.triggerChangesZoneAll(game, sa);
}
}

View File

@@ -72,7 +72,7 @@ public class UntapEffect extends SpellAbilityEffect {
* whether the untapping is mandatory.
*/
private static void untapChoose(final SpellAbility sa, final boolean mandatory) {
final int num = Integer.parseInt(sa.getParam("Amount"));
final int num = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("Amount"), sa);
final String valid = sa.getParam("UntapType");
for (final Player p : AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa)) {

View File

@@ -526,7 +526,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
if (state == CardStateName.FaceDown) {
view.updateHiddenId(game.nextHiddenCardId());
}
game.fireEvent(new GameEventCardStatsChanged(this)); //ensure stats updated for new characteristics
game.fireEvent(new GameEventCardStatsChanged(this, true)); //ensure stats updated for new characteristics
}
}
return true;

View File

@@ -22,7 +22,6 @@ import java.util.Set;
import com.google.common.collect.Sets;
import forge.card.CardType;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.card.CardCollection;
@@ -106,11 +105,7 @@ public class CostDiscard extends CostPartWithList {
desc.append("card");
}
else {
if (this.getTypeDescription() == null) {
desc.append(CardType.CoreType.isValidEnum(this.getType()) ? this.getType().toLowerCase() : this.getType());
} else {
desc.append(this.getTypeDescription());
}
desc.append(this.getDescriptiveType());
desc.append(" card");
}

View File

@@ -17,7 +17,6 @@
*/
package forge.game.cost;
import forge.card.CardType;
import forge.game.Game;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
@@ -85,10 +84,7 @@ public class CostExile extends CostPartWithList {
@Override
public final String toString() {
final Integer i = this.convertAmount();
String desc = this.getTypeDescription() == null ? this.getType() : this.getTypeDescription();
if (CardType.CoreType.isValidEnum(desc)) {
desc = desc.toLowerCase();
}
String desc = this.getDescriptiveType();
String origin = this.from.name().toLowerCase();
if (this.payCostFromSource()) {

View File

@@ -15,8 +15,14 @@ import forge.game.card.Card;
public class GameEventCardStatsChanged extends GameEvent {
public final Collection<Card> cards;
public boolean transform = false;
public GameEventCardStatsChanged(Card affected) {
this(affected, false);
}
public GameEventCardStatsChanged(Card affected, boolean isTransform) {
cards = Arrays.asList(affected);
transform = isTransform;
}
public GameEventCardStatsChanged(Collection<Card> affected) {

View File

@@ -404,10 +404,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit
}
public boolean canPlayWithOptionalCost(OptionalCostValue opt) {
SpellAbility saOpt = GameActionUtil.addOptionalCosts(this, Lists.newArrayList(opt));
boolean result = saOpt.canPlay();
saOpt.clearPipsToReduce();
return result;
return GameActionUtil.addOptionalCosts(this, Lists.newArrayList(opt)).canPlay();
}
public boolean isPossible() {

View File

@@ -0,0 +1,36 @@
package forge.game.trigger;
import forge.game.ability.AbilityKey;
import forge.game.card.Card;
import forge.game.spellability.SpellAbility;
import forge.util.Localizer;
import java.util.Map;
public class TriggerSeekAll extends Trigger {
public TriggerSeekAll(Map<String, String> params, Card host, boolean intrinsic) {
super(params, host, intrinsic);
}
@Override
public boolean performTest(Map<AbilityKey, Object> runParams) {
if (!matchesValidParam("ValidPlayer", runParams.get(AbilityKey.Player))) {
return false;
}
return true;
}
@Override
public void setTriggeringObjects(SpellAbility sa, Map<AbilityKey, Object> runParams) {
sa.setTriggeringObjectsFrom(runParams, AbilityKey.Player);
}
@Override
public String getImportantStackObjects(SpellAbility sa) {
StringBuilder sb = new StringBuilder();
sb.append(Localizer.getInstance().getMessage("lblPlayer")).append(": ");
sb.append(sa.getTriggeringObject(AbilityKey.Player));
return sb.toString();
}
}

View File

@@ -104,6 +104,7 @@ public enum TriggerType {
Sacrificed(TriggerSacrificed.class),
Scry(TriggerScry.class),
SearchedLibrary(TriggerSearchedLibrary.class),
SeekAll(TriggerSeekAll.class),
SetInMotion(TriggerSetInMotion.class),
Shuffled(TriggerShuffled.class),
Specializes(TriggerSpecializes.class),

View File

@@ -17,6 +17,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Clipboard;
import forge.adventure.scene.*;
import forge.adventure.stage.GameHUD;
import forge.adventure.util.Config;
import forge.adventure.world.WorldSave;
import forge.animation.ForgeAnimation;
@@ -340,6 +341,7 @@ public class Forge implements ApplicationListener {
try {
Config.instance().loadResources();
SpellSmithScene.instance().loadEditions();
GameHUD.getInstance().stopAudio();
if (startScene) {
MusicPlaylist.invalidateMusicPlaylist();
SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS);

View File

@@ -18,6 +18,8 @@ import forge.deck.DeckProxy;
import forge.deck.DeckSection;
import forge.item.InventoryItem;
import forge.item.PaperCard;
import forge.sound.SoundEffectType;
import forge.sound.SoundSystem;
import forge.util.ItemPool;
import java.io.Serializable;
@@ -516,6 +518,8 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
public void takeGold(int price) {
gold -= price;
onGoldChangeList.emit();
//play sfx
SoundSystem.instance.play(SoundEffectType.CoinsDrop, false);
}
public void addShards(int number) {
takeShards(-number);
@@ -523,6 +527,8 @@ public class AdventurePlayer implements Serializable, SaveFileContent {
public void takeShards(int number) {
shards -= number;
onShardsChangeList.emit();
//play sfx
SoundSystem.instance.play(SoundEffectType.TakeShard, false);
}
public void setShards(int number) {

View File

@@ -13,6 +13,7 @@ import forge.adventure.data.EffectData;
import forge.adventure.data.EnemyData;
import forge.adventure.data.ItemData;
import forge.adventure.player.AdventurePlayer;
import forge.adventure.stage.GameHUD;
import forge.adventure.stage.IAfterMatch;
import forge.adventure.util.Config;
import forge.adventure.util.Current;
@@ -67,6 +68,8 @@ public class DuelScene extends ForgeScene {
Deck playerDeck;
boolean chaosBattle = false;
boolean callbackExit = false;
boolean arenaBattleChallenge = false;
boolean isArena = false;
private LoadingOverlay matchOverlay;
List<IPaperCard> playerExtras = new ArrayList<>();
List<IPaperCard> AIExtras = new ArrayList<>();
@@ -138,7 +141,13 @@ public class DuelScene extends ForgeScene {
void afterGameEnd(String enemyName, boolean winner, boolean showOverlay, boolean alternate) {
Runnable runnable = () -> Gdx.app.postRunnable(()-> {
SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS); //start background music
if (GameScene.instance().isNotInWorldMap()) {
SoundSystem.instance.pause();
GameHUD.getInstance().playAudio();
} else {
SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MENUS);
SoundSystem.instance.resume();
}
dungeonEffect = null;
callbackExit = false;
Forge.clearTransitionScreen();
@@ -192,6 +201,7 @@ public class DuelScene extends ForgeScene {
@Override
public void enter() {
GameHUD.getInstance().unloadAudio();
Set<GameType> appliedVariants = new HashSet<>();
appliedVariants.add(GameType.Constructed);
AdventurePlayer advPlayer = Current.player();
@@ -267,6 +277,7 @@ public class DuelScene extends ForgeScene {
addEffects(humanPlayer, playerEffects);
currentEnemy = enemy.getData();
boolean bossBattle = currentEnemy.boss;
for (int i = 0; i < 8 && currentEnemy != null; i++) {
Deck deck = null;
@@ -278,6 +289,8 @@ public class DuelScene extends ForgeScene {
}
this.AIExtras = aiCards;
deck = deckProxy.getDeck();
} else if (this.arenaBattleChallenge) {
deck = Aggregates.random(DeckProxy.getAllGeneticAIDecks()).getDeck();
} else {
deck = currentEnemy.copyPlayerDeck ? this.playerDeck : currentEnemy.generateDeck(Current.player().isFantasyMode(), Current.player().isUsingCustomDeck() || Current.player().getDifficulty().name.equalsIgnoreCase("Hard"));
}
@@ -332,7 +345,7 @@ public class DuelScene extends ForgeScene {
rules.setWarnAboutAICards(false);
hostedMatch.setEndGameHook(() -> DuelScene.this.GameEnd());
hostedMatch.startMatch(rules, appliedVariants, players, guiMap);
hostedMatch.startMatch(rules, appliedVariants, players, guiMap, bossBattle ? MusicPlaylist.BOSS : MusicPlaylist.MATCH);
MatchController.instance.setGameView(hostedMatch.getGameView());
boolean showMessages = enemy.getData().copyPlayerDeck && Current.player().isUsingCustomDeck();
if (chaosBattle || showMessages) {
@@ -376,8 +389,15 @@ public class DuelScene extends ForgeScene {
}
public void initDuels(PlayerSprite playerSprite, EnemySprite enemySprite) {
initDuels(playerSprite, enemySprite, false);
}
public void initDuels(PlayerSprite playerSprite, EnemySprite enemySprite, boolean isArena) {
this.player = playerSprite;
this.enemy = enemySprite;
this.isArena = isArena;
this.arenaBattleChallenge = isArena
&& (Current.player().getDifficulty().name.equalsIgnoreCase("Hard")
|| Current.player().getDifficulty().name.equalsIgnoreCase("Insane"));
this.playerDeck = (Deck) Current.player().getSelectedDeck().copyTo("PlayerDeckCopy");
this.chaosBattle = this.enemy.getData().copyPlayerDeck && Current.player().isFantasyMode();
this.AIExtras.clear();

View File

@@ -64,14 +64,13 @@ public class GameScene extends HudScene {
} else {
World world = Current.world();
//this gets the name of the layer... this shoud be based on boundaries...
int currentBiome = World.highestBiome(world.getBiome((int) stage.getPlayerSprite().getX() / world.getTileSize(), (int) stage.getPlayerSprite().getY() / world.getTileSize()));
int currentBiome = World.highestBiome(world.getBiomeMapXY((int) stage.getPlayerSprite().getX() / world.getTileSize(), (int) stage.getPlayerSprite().getY() / world.getTileSize()));
List<BiomeData> biomeData = world.getData().GetBiomes();
if (biomeData.size() <= currentBiome) //on roads....
if (biomeData.size() <= currentBiome) //shouldn't be the case but default to waste
if (skipRoads) {
location = forHeader ? "Waste Map" : "waste";
} else {
//current workaround to get the POI town name
location = WorldStage.getInstance().getBoundary();
location = "";
}
else {
BiomeData data = biomeData.get(currentBiome);

View File

@@ -24,6 +24,7 @@ import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel;
import forge.player.GamePlayerUtil;
import forge.screens.TransitionScreen;
import forge.sound.SoundSystem;
import forge.util.NameGenerator;
import java.util.Random;
@@ -190,7 +191,7 @@ public class NewGameScene extends UIScene {
}
Runnable runnable = () -> {
started = false;
FModel.getPreferences().setPref(ForgePreferences.FPref.UI_ENABLE_MUSIC, false);
//FModel.getPreferences().setPref(ForgePreferences.FPref.UI_ENABLE_MUSIC, false);
WorldSave.generateNewWorld(selectedName.getText(),
gender.getCurrentIndex() == 0,
race.getCurrentIndex(),
@@ -200,6 +201,7 @@ public class NewGameScene extends UIScene {
modes.get(mode.getCurrentIndex()), colorId.getCurrentIndex(),
editionIds[starterEdition.getCurrentIndex()], 0);//maybe replace with enum
GamePlayerUtil.getGuiPlayer().setName(selectedName.getText());
SoundSystem.instance.changeBackgroundTrack();
Forge.switchScene(GameScene.instance());
};
Forge.setTransitionScreen(new TransitionScreen(runnable, null, false, true, "Generating World..."));

View File

@@ -146,13 +146,15 @@ public class RewardScene extends UIScene {
showLootOrDone();
return true;
}
switch (type) {
case Shop:
doneButton.setText(Forge.getLocalizer().getMessage("lblLeave"));
break;
case Loot:
doneButton.setText(Forge.getLocalizer().getMessage("lblDone"));
break;
if (type != null) {
switch (type) {
case Shop:
doneButton.setText(Forge.getLocalizer().getMessage("lblLeave"));
break;
case Loot:
doneButton.setText(Forge.getLocalizer().getMessage("lblDone"));
break;
}
}
shown = false;
clearGenerated();

View File

@@ -21,6 +21,7 @@ import forge.adventure.util.Current;
import forge.adventure.world.WorldSave;
import forge.adventure.world.WorldSaveHeader;
import forge.screens.TransitionScreen;
import forge.sound.SoundSystem;
import forge.util.TextUtil;
import java.io.File;
@@ -217,6 +218,7 @@ public class SaveLoadScene extends UIScene {
try {
Forge.setTransitionScreen(new TransitionScreen(() -> {
if (WorldSave.load(currentSlot)) {
SoundSystem.instance.changeBackgroundTrack();
Forge.switchScene(GameScene.instance());
} else {
Forge.clearTransitionScreen();
@@ -236,6 +238,7 @@ public class SaveLoadScene extends UIScene {
Current.player().updateDifficulty(Config.instance().getConfigData().difficulties[difficulty.getSelectedIndex()]);
Current.player().setWorldPosY((int) (WorldSave.getCurrentSave().getWorld().getData().playerStartPosY * WorldSave.getCurrentSave().getWorld().getData().height * WorldSave.getCurrentSave().getWorld().getTileSize()));
Current.player().setWorldPosX((int) (WorldSave.getCurrentSave().getWorld().getData().playerStartPosX * WorldSave.getCurrentSave().getWorld().getData().width * WorldSave.getCurrentSave().getWorld().getTileSize()));
SoundSystem.instance.changeBackgroundTrack();
Forge.switchScene(GameScene.instance());
} else {
Forge.clearTransitionScreen();

View File

@@ -10,6 +10,7 @@ import forge.adventure.util.Config;
import forge.adventure.util.Controls;
import forge.adventure.world.WorldSave;
import forge.screens.TransitionScreen;
import forge.sound.SoundSystem;
/**
* First scene after the splash screen
@@ -20,7 +21,6 @@ public class StartScene extends UIScene {
TextraButton saveButton, resumeButton, continueButton;
public StartScene() {
super(Forge.isLandscapeMode() ? "ui/start_menu.json" : "ui/start_menu_portrait.json");
ui.onButtonPress("Start", StartScene.this::NewGame);
@@ -31,7 +31,7 @@ public class StartScene extends UIScene {
ui.onButtonPress("Continue", StartScene.this::Continue);
ui.onButtonPress("Settings", StartScene.this::settings);
ui.onButtonPress("Exit", StartScene.this::Exit);
ui.onButtonPress("Switch", Forge::switchToClassic);
ui.onButtonPress("Switch", StartScene.this::switchToClassic);
saveButton = ui.findActor("Save");
@@ -43,8 +43,8 @@ public class StartScene extends UIScene {
}
public static StartScene instance() {
if(object==null)
object=new StartScene();
if (object == null)
object = new StartScene();
return object;
}
@@ -81,6 +81,7 @@ public class StartScene extends UIScene {
try {
Forge.setTransitionScreen(new TransitionScreen(() -> {
if (WorldSave.load(WorldSave.filenameToSlot(lastActiveSave))) {
SoundSystem.instance.changeBackgroundTrack();
Forge.switchScene(GameScene.instance());
} else {
Forge.clearTransitionScreen();
@@ -100,17 +101,22 @@ public class StartScene extends UIScene {
}
public boolean Exit() {
Dialog dialog = prepareDialog(Forge.getLocalizer().getMessage("lblExitForge"), ButtonOk|ButtonAbort,()->Forge.exit(true));
dialog.text( Controls.newLabel(Forge.getLocalizer().getMessage("lblAreYouSureYouWishExitForge")));
Dialog dialog = prepareDialog(Forge.getLocalizer().getMessage("lblExitForge"), ButtonOk | ButtonAbort, () -> Forge.exit(true));
dialog.text(Controls.newLabel(Forge.getLocalizer().getMessage("lblAreYouSureYouWishExitForge")));
showDialog(dialog);
return true;
}
public void switchToClassic() {
GameHUD.getInstance().stopAudio();
Forge.switchToClassic();
}
@Override
public void enter() {
boolean hasSaveButton = WorldSave.getCurrentSave().getWorld().getData() != null;
if (hasSaveButton) {
TileMapScene scene = TileMapScene.instance();
TileMapScene scene = TileMapScene.instance();
hasSaveButton = !scene.currentMap().isInMap() || scene.isAutoHealLocation();
}
saveButton.setVisible(hasSaveButton);
@@ -130,10 +136,9 @@ public class StartScene extends UIScene {
}
if(Forge.createNewAdventureMap)
{
if (Forge.createNewAdventureMap) {
this.NewGame();
GameStage.maximumScrollDistance=4f;
GameStage.maximumScrollDistance = 4f;
}
super.enter();

View File

@@ -19,6 +19,8 @@ import com.github.tommyettinger.textra.TextraLabel;
import forge.Forge;
import forge.adventure.stage.GameHUD;
import forge.adventure.util.*;
import forge.sound.SoundEffectType;
import forge.sound.SoundSystem;
/**
* Base class for an GUI scene where the elements are loaded from a json file
@@ -27,86 +29,83 @@ public class UIScene extends Scene {
protected UIActor ui;
public static class Selectable<T extends Actor>
{
public static class Selectable<T extends Actor> {
public T actor;
public float getY()
{
Actor act=actor;
float y=0;
while (act!=null)
{
y+=act.getY();
act=act.getParent();
public float getY() {
Actor act = actor;
float y = 0;
while (act != null) {
y += act.getY();
act = act.getParent();
}
return y;
}
public float getX()
{
Actor act=actor;
float x=0;
while (act!=null)
{
x+=act.getX();
act=act.getParent();
public float getX() {
Actor act = actor;
float x = 0;
while (act != null) {
x += act.getX();
act = act.getParent();
}
return x;
}
public Selectable(T newActor) {
actor=newActor;
actor = newActor;
}
public void onSelect(UIScene scene)
{
public void onSelect(UIScene scene) {
}
public void onDeSelect()
{
public void onDeSelect() {
//actor.fire(UIScene.eventExit());
}
public void onPressDown(UIScene scene)
{
if(actor instanceof TextField)
{
scene.requestTextInput(((TextField) actor).getText(),text-> ((TextField) actor).setText(text));
public void onPressDown(UIScene scene) {
if (actor instanceof TextField) {
scene.requestTextInput(((TextField) actor).getText(), text -> ((TextField) actor).setText(text));
}
actor.fire(UIScene.eventTouchDown());
}
public void onPressUp()
{
public void onPressUp() {
actor.fire(UIScene.eventTouchUp());
}
public float yDiff(Selectable finalOne) {
return Math.abs(finalOne.getY()-getY());
return Math.abs(finalOne.getY() - getY());
}
public float xDiff(Selectable finalOne) {
return Math.abs(finalOne.getX()-getX());
return Math.abs(finalOne.getX() - getX());
}
}
static final public int ButtonYes=0x1;
static final public int ButtonNo=0x2;
static final public int ButtonOk=0x4;
static final public int ButtonAbort=0x8;
static final public int ButtonYes = 0x1;
static final public int ButtonNo = 0x2;
static final public int ButtonOk = 0x4;
static final public int ButtonAbort = 0x8;
public Dialog prepareDialog(String header, int buttons, Runnable onOkOrYes) {
Dialog dialog =new Dialog(header, Controls.getSkin())
{
protected void result(Object object)
{
if(onOkOrYes!=null&&object!=null&&object.equals(true))
Dialog dialog = new Dialog(header, Controls.getSkin()) {
protected void result(Object object) {
SoundSystem.instance.play(SoundEffectType.ButtonPress, false);
if (onOkOrYes != null && object != null && object.equals(true))
onOkOrYes.run();
this.hide();
this.hide();
removeDialog();
}
};
if((buttons&ButtonYes)!=0)
if ((buttons & ButtonYes) != 0)
dialog.button(Forge.getLocalizer().getMessage("lblYes"), true);
if((buttons&ButtonNo)!=0)
if ((buttons & ButtonNo) != 0)
dialog.button(Forge.getLocalizer().getMessage("lblNo"), false);
if((buttons&ButtonOk)!=0)
if ((buttons & ButtonOk) != 0)
dialog.button(Forge.getLocalizer().getMessage("lblOk"), true);
if((buttons&ButtonAbort)!=0)
if ((buttons & ButtonAbort) != 0)
dialog.button(Forge.getLocalizer().getMessage("lblAbort"), false);
dialog.setMovable(false);
@@ -114,16 +113,16 @@ public class UIScene extends Scene {
dialog.setResizable(false);
return dialog;
}
public void showDialog(Dialog dialog)
{
public void showDialog(Dialog dialog) {
stage.addActor(dialog);
possibleSelectionStack.add(new Array<>());
addToSelectable(dialog.getContentTable());
addToSelectable(dialog.getButtonTable());
dialog.getColor().a=0;
dialog.getColor().a = 0;
stage.setKeyboardFocus(dialog);
stage.setScrollFocus(dialog);
for(Dialog otherDialogs:dialogs)
for (Dialog otherDialogs : dialogs)
otherDialogs.hide();
dialogs.add(dialog);
selectFirst();
@@ -136,7 +135,7 @@ public class UIScene extends Scene {
{
@Override
protected void result(@Null Object object) {
removeDialog();
removeDialog();
}
};
keyboardDialog.setText(text);
@@ -145,41 +144,42 @@ public class UIScene extends Scene {
//possibleSelection=keyboardDialog.keys();
}
public Array< Array<Selectable>> possibleSelectionStack=new Array<>();
public Array< Dialog> dialogs=new Array<>();
public Array<Selectable> getPossibleSelection()
{
if(possibleSelectionStack.isEmpty())
public Array<Array<Selectable>> possibleSelectionStack = new Array<>();
public Array<Dialog> dialogs = new Array<>();
public Array<Selectable> getPossibleSelection() {
if (possibleSelectionStack.isEmpty())
possibleSelectionStack.add(ui.selectActors);
return possibleSelectionStack.get(possibleSelectionStack.size-1);
}
return possibleSelectionStack.get(possibleSelectionStack.size - 1);
}
protected Stage stage;
String uiFile;
public static InputEvent eventTouchUp()
{
public static InputEvent eventTouchUp() {
InputEvent event = new InputEvent();
event.setPointer(-1);
event.setType(InputEvent.Type.touchUp);
return event;
}
public static InputEvent eventTouchDown()
{
public static InputEvent eventTouchDown() {
InputEvent event = new InputEvent();
event.setPointer(-1);
event.setType(InputEvent.Type.touchDown);
return event;
}
public static InputEvent eventExit()
{
public static InputEvent eventExit() {
InputEvent event = new InputEvent();
event.setPointer(-1);
event.setType(InputEvent.Type.exit);
return event;
}
public static InputEvent eventEnter()
{
public static InputEvent eventEnter() {
InputEvent event = new InputEvent();
event.setPointer(-1);
event.setType(InputEvent.Type.enter);
@@ -188,26 +188,30 @@ public class UIScene extends Scene {
@Override
public boolean buttonUp(Controller controller, int keycode) {
return stage.keyUp(KeyBinding.controllerButtonToKey(controller,keycode));
return stage.keyUp(KeyBinding.controllerButtonToKey(controller, keycode));
}
@Override
public boolean buttonDown(Controller controller, int keycode) {
return stage.keyDown(KeyBinding.controllerButtonToKey(controller,keycode));
return stage.keyDown(KeyBinding.controllerButtonToKey(controller, keycode));
}
protected void addToSelectable(Table table) {
for(Cell cell:table.getCells())
{
if(cell.getActor()!=null&&cell.getActor().getClass()!=Actor.class&&!(cell.getActor()instanceof Label)&&!(cell.getActor()instanceof TextraLabel))
for (Cell cell : table.getCells()) {
if (cell.getActor() != null && cell.getActor().getClass() != Actor.class && !(cell.getActor() instanceof Label) && !(cell.getActor() instanceof TextraLabel))
getPossibleSelection().add(new Selectable(cell.getActor()));
}
}
protected void addToSelectable(Button button)//prevent to addToSelectable(Table) fallback
{
{
getPossibleSelection().add(new Selectable(button));
}
protected void addToSelectable(Actor button) {
getPossibleSelection().add(new Selectable(button));
}
protected void addToSelectable(Selectable selectable) {
getPossibleSelection().add(selectable);
}
@@ -222,14 +226,16 @@ public class UIScene extends Scene {
stage = new Stage(new ScalingViewport(Scaling.stretch, getIntendedWidth(), getIntendedHeight())) {
@Override
public boolean keyUp(int keycode) {
keyReleased(keycode);
keyReleased(keycode);
return super.keyUp(keycode);
}
@Override
public boolean keyDown(int keyCode) {
keyPressed(keyCode);
keyPressed(keyCode);
return super.keyDown(keyCode);
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
pointerMoved(screenX, screenY);
@@ -237,9 +243,8 @@ public class UIScene extends Scene {
}
};
ui = new UIActor(Config.instance().getFile(uiFile));
for(Actor actor:ui.getChildren())
{
if(actor instanceof ScrollPane)
for (Actor actor : ui.getChildren()) {
if (actor instanceof ScrollPane)
stage.setScrollFocus(actor);
}
possibleSelectionStack.add(ui.selectActors);
@@ -249,21 +254,17 @@ public class UIScene extends Scene {
private void removeDialog() {
if(!dialogs.isEmpty())
{
dialogs.get(dialogs.size-1).remove();
dialogs.removeIndex(dialogs.size-1);
if (!dialogs.isEmpty()) {
dialogs.get(dialogs.size - 1).remove();
dialogs.removeIndex(dialogs.size - 1);
if(!dialogs.isEmpty())
dialogs.get(dialogs.size-1).show(stage);
if (!dialogs.isEmpty())
dialogs.get(dialogs.size - 1).show(stage);
}
if(possibleSelectionStack.isEmpty())
{
if (possibleSelectionStack.isEmpty()) {
getPossibleSelection();
}
else
{
possibleSelectionStack.removeIndex(possibleSelectionStack.size-1);
} else {
possibleSelectionStack.removeIndex(possibleSelectionStack.size - 1);
}
}
@@ -295,92 +296,80 @@ public class UIScene extends Scene {
Forge.switchToLast();
return true;
}
public Selectable getSelected()
{
for(Selectable selectable: getPossibleSelection())
{
if(stage.getKeyboardFocus()==selectable.actor)
public Selectable getSelected() {
for (Selectable selectable : getPossibleSelection()) {
if (stage.getKeyboardFocus() == selectable.actor)
return selectable;
}
return null;
}
public boolean keyReleased(int keycode)
{
public boolean keyReleased(int keycode) {
ui.pressUp(keycode);
if(!dialogShowing())
{
Button pressedButton=ui.buttonPressed(keycode);
if(pressedButton!=null)
{
if(pressedButton.isVisible())
if (!dialogShowing()) {
Button pressedButton = ui.buttonPressed(keycode);
if (pressedButton != null) {
if (pressedButton.isVisible())
pressedButton.fire(eventTouchUp());
}
}
if(KeyBinding.Use.isPressed(keycode)){
if(getSelected()!=null)
if (KeyBinding.Use.isPressed(keycode)) {
if (getSelected() != null)
getSelected().onPressUp();//order is important, this might remove a dialog
}
return true;
}
public boolean keyPressed(int keycode) {
Selectable selection=getSelected();
Selectable selection = getSelected();
ui.pressDown(keycode);
if(stage.getKeyboardFocus() instanceof SelectBox)
{
SelectBox box=(SelectBox) stage.getKeyboardFocus();
if(box.getScrollPane().hasParent())
{
if(KeyBinding.Use.isPressed(keycode))
{
if (stage.getKeyboardFocus() instanceof SelectBox) {
SelectBox box = (SelectBox) stage.getKeyboardFocus();
if (box.getScrollPane().hasParent()) {
if (KeyBinding.Use.isPressed(keycode)) {
box.getSelection().choose(box.getList().getSelected());
box.getScrollPane().hide();
}
return false;
}
}
if(KeyBinding.Use.isPressed(keycode)){
if(selection!=null)
if (KeyBinding.Use.isPressed(keycode)) {
if (selection != null)
selection.onPressDown(this);
}
if(KeyBinding.ScrollUp.isPressed(keycode))
{
Actor focus=stage.getScrollFocus();
if(focus!=null&&focus instanceof ScrollPane)
{
ScrollPane scroll=((ScrollPane)focus);
scroll.setScrollY(scroll.getScrollY()-20);
if (KeyBinding.ScrollUp.isPressed(keycode)) {
Actor focus = stage.getScrollFocus();
if (focus != null && focus instanceof ScrollPane) {
ScrollPane scroll = ((ScrollPane) focus);
scroll.setScrollY(scroll.getScrollY() - 20);
}
}
if(KeyBinding.ScrollDown.isPressed(keycode))
{
Actor focus=stage.getScrollFocus();
if(focus!=null&&focus instanceof ScrollPane)
{
ScrollPane scroll=((ScrollPane)focus);
scroll.setScrollY(scroll.getScrollY()+20);
if (KeyBinding.ScrollDown.isPressed(keycode)) {
Actor focus = stage.getScrollFocus();
if (focus != null && focus instanceof ScrollPane) {
ScrollPane scroll = ((ScrollPane) focus);
scroll.setScrollY(scroll.getScrollY() + 20);
}
}
if(KeyBinding.Down.isPressed(keycode))
if (KeyBinding.Down.isPressed(keycode))
selectNextDown();
if(KeyBinding.Up.isPressed(keycode))
if (KeyBinding.Up.isPressed(keycode))
selectNextUp();
if(!(stage.getKeyboardFocus() instanceof Selector)&&!(stage.getKeyboardFocus() instanceof TextField)&&!(stage.getKeyboardFocus() instanceof Slider))
{
if(KeyBinding.Right.isPressed(keycode))
if (!(stage.getKeyboardFocus() instanceof Selector) && !(stage.getKeyboardFocus() instanceof TextField) && !(stage.getKeyboardFocus() instanceof Slider)) {
if (KeyBinding.Right.isPressed(keycode))
selectNextRight();
if(KeyBinding.Left.isPressed(keycode))
if (KeyBinding.Left.isPressed(keycode))
selectNextLeft();
}
if(!dialogShowing())
{
Button pressedButton=ui.buttonPressed(keycode);
if(pressedButton!=null)
{
if(pressedButton.isVisible())
if (!dialogShowing()) {
Button pressedButton = ui.buttonPressed(keycode);
if (pressedButton != null) {
if (pressedButton.isVisible())
pressedButton.fire(eventTouchDown());
}
}
@@ -396,11 +385,13 @@ public class UIScene extends Scene {
public void disconnected(final Controller controller) {
ui.controllerDisconnected();
}
@Override
public void connected(final Controller controller) {
selectFirst();
ui.controllerConnected();
}
public boolean pointerMoved(int screenX, int screenY) {
unselectActors();
return false;
@@ -417,152 +408,128 @@ public class UIScene extends Scene {
}
}, 0.10f);
}
public void unselectActors() {
for (Selectable selectable : getPossibleSelection()) {
selectable.onDeSelect();
selectable.onDeSelect();
}
}
Array<Selectable> visibleSelection()
{
Array<Selectable> selectables=new Array<>();
Array<Selectable> visibleSelection() {
Array<Selectable> selectables = new Array<>();
for (Selectable selectable : getPossibleSelection()) {
if(selectable.actor.isVisible())
{
if(selectable.actor instanceof Button)
{
if(!((Button)selectable.actor).isDisabled())
if (selectable.actor.isVisible()) {
if (selectable.actor instanceof Button) {
if (!((Button) selectable.actor).isDisabled())
selectables.add(selectable);
}
else
{
} else {
selectables.add(selectable);
}
}
}
return selectables;
}
public void selectNextDown() {
if(getSelected()==null)
{
if (getSelected() == null) {
selectFirst();
}
else
{
Selectable current =getSelected();
Array<Selectable> candidates=new Array<>();
for(Selectable selectable:visibleSelection())
{
if(selectable.xDiff(current)<0.1&&selectable!=current)
} else {
Selectable current = getSelected();
Array<Selectable> candidates = new Array<>();
for (Selectable selectable : visibleSelection()) {
if (selectable.xDiff(current) < 0.1 && selectable != current)
candidates.add(selectable);
}
if(candidates.isEmpty())
if (candidates.isEmpty())
candidates.addAll(visibleSelection());
Selectable finalOne=null;
Selectable fallback=null;
for(Selectable candidate:candidates)
{
if(fallback==null||candidate.getY()>fallback.getY())
fallback=candidate;
if(candidate.getY()<current.getY()&&(finalOne==null||current.yDiff(candidate)<current.yDiff(finalOne)))
{
finalOne=candidate;
Selectable finalOne = null;
Selectable fallback = null;
for (Selectable candidate : candidates) {
if (fallback == null || candidate.getY() > fallback.getY())
fallback = candidate;
if (candidate.getY() < current.getY() && (finalOne == null || current.yDiff(candidate) < current.yDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne==null)
for(Selectable candidate:visibleSelection())
{
if(candidate.getY()<current.getY()&&(finalOne==null||current.yDiff(candidate)<current.yDiff(finalOne)))
{
finalOne=candidate;
if (finalOne == null)
for (Selectable candidate : visibleSelection()) {
if (candidate.getY() < current.getY() && (finalOne == null || current.yDiff(candidate) < current.yDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne!=null)
if (finalOne != null)
selectActor(finalOne);
else if(fallback!=null)
else if (fallback != null)
selectActor(fallback);
}
}
private void selectNextLeft() {
if(getSelected()==null)
{
if (getSelected() == null) {
selectFirst();
}
else
{
Selectable current =getSelected();
Array<Selectable> candidates=new Array<>();
for(Selectable selectable:visibleSelection())
{
if(selectable.yDiff(current)<0.1&&selectable!=current)
} else {
Selectable current = getSelected();
Array<Selectable> candidates = new Array<>();
for (Selectable selectable : visibleSelection()) {
if (selectable.yDiff(current) < 0.1 && selectable != current)
candidates.add(selectable);
}
if(candidates.isEmpty())
if (candidates.isEmpty())
candidates.addAll(visibleSelection());
Selectable finalOne=null;
Selectable fallback=null;
for(Selectable candidate:candidates)
{
if(fallback==null||candidate.getX()>fallback.getX())
fallback=candidate;
if(candidate.getX()<current.getX()&&(finalOne==null||current.xDiff(candidate)<current.xDiff(finalOne)))
{
finalOne=candidate;
Selectable finalOne = null;
Selectable fallback = null;
for (Selectable candidate : candidates) {
if (fallback == null || candidate.getX() > fallback.getX())
fallback = candidate;
if (candidate.getX() < current.getX() && (finalOne == null || current.xDiff(candidate) < current.xDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne==null)
for(Selectable candidate:visibleSelection())
{
if(candidate.getX()<current.getX()&&(finalOne==null||current.xDiff(candidate)<current.xDiff(finalOne)))
{
finalOne=candidate;
if (finalOne == null)
for (Selectable candidate : visibleSelection()) {
if (candidate.getX() < current.getX() && (finalOne == null || current.xDiff(candidate) < current.xDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne!=null)
if (finalOne != null)
selectActor(finalOne);
else if(fallback!=null)
else if (fallback != null)
selectActor(fallback);
}
}
private void selectNextRight() {
if(getSelected()==null)
{
if (getSelected() == null) {
selectFirst();
}
else
{
Selectable current =getSelected();
Array<Selectable> candidates=new Array<>();
for(Selectable selectable:visibleSelection())
{
if(selectable.yDiff(current)<0.1&&selectable!=current)
} else {
Selectable current = getSelected();
Array<Selectable> candidates = new Array<>();
for (Selectable selectable : visibleSelection()) {
if (selectable.yDiff(current) < 0.1 && selectable != current)
candidates.add(selectable);
}
if(candidates.isEmpty())
if (candidates.isEmpty())
candidates.addAll(visibleSelection());
Selectable finalOne=null;
Selectable fallback=null;
for(Selectable candidate:candidates)
{
if(fallback==null||candidate.getX()<fallback.getX())
fallback=candidate;
if(candidate.getX()>current.getX()&&(finalOne==null||current.xDiff(candidate)<current.xDiff(finalOne)))
{
finalOne=candidate;
Selectable finalOne = null;
Selectable fallback = null;
for (Selectable candidate : candidates) {
if (fallback == null || candidate.getX() < fallback.getX())
fallback = candidate;
if (candidate.getX() > current.getX() && (finalOne == null || current.xDiff(candidate) < current.xDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne==null)
for(Selectable candidate:visibleSelection())
{
if(candidate.getX()>current.getX()&&(finalOne==null||current.xDiff(candidate)<current.xDiff(finalOne)))
{
finalOne=candidate;
if (finalOne == null)
for (Selectable candidate : visibleSelection()) {
if (candidate.getX() > current.getX() && (finalOne == null || current.xDiff(candidate) < current.xDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne!=null)
if (finalOne != null)
selectActor(finalOne);
else if(fallback!=null)
else if (fallback != null)
selectActor(fallback);
}
@@ -571,43 +538,35 @@ public class UIScene extends Scene {
public void selectNextUp() {
if(getSelected()==null)
{
if (getSelected() == null) {
selectFirst();
}
else
{
Selectable current =getSelected();
Array<Selectable> candidates=new Array<>();
for(Selectable selectable:visibleSelection())
{
if(selectable.xDiff(current)<0.1&&selectable!=current)
} else {
Selectable current = getSelected();
Array<Selectable> candidates = new Array<>();
for (Selectable selectable : visibleSelection()) {
if (selectable.xDiff(current) < 0.1 && selectable != current)
candidates.add(selectable);
}
if(candidates.isEmpty())
if (candidates.isEmpty())
candidates.addAll(visibleSelection());
Selectable finalOne=null;
Selectable fallback=null;
for(Selectable candidate:candidates)
{
if(fallback==null||candidate.getY()<fallback.getY())
fallback=candidate;
if(candidate.getY()>current.getY()&&(finalOne==null||current.yDiff(candidate)<current.yDiff(finalOne)))
{
finalOne=candidate;
Selectable finalOne = null;
Selectable fallback = null;
for (Selectable candidate : candidates) {
if (fallback == null || candidate.getY() < fallback.getY())
fallback = candidate;
if (candidate.getY() > current.getY() && (finalOne == null || current.yDiff(candidate) < current.yDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne==null)//allowAllNow
for(Selectable candidate:visibleSelection())
{
if(candidate.getY()>current.getY()&&(finalOne==null||current.yDiff(candidate)<current.yDiff(finalOne)))
{
finalOne=candidate;
if (finalOne == null)//allowAllNow
for (Selectable candidate : visibleSelection()) {
if (candidate.getY() > current.getY() && (finalOne == null || current.yDiff(candidate) < current.yDiff(finalOne))) {
finalOne = candidate;
}
}
if(finalOne!=null)
if (finalOne != null)
selectActor(finalOne);
else if(fallback!=null)
else if (fallback != null)
selectActor(fallback);
}
@@ -615,22 +574,18 @@ public class UIScene extends Scene {
private void selectFirst() {
Selectable result=null;
for(Selectable candidate: getPossibleSelection())
{
if(result==null|| candidate.getY()>result.getY())
{
result=candidate;
Selectable result = null;
for (Selectable candidate : getPossibleSelection()) {
if (result == null || candidate.getY() > result.getY()) {
result = candidate;
}
}
selectActor(result);
}
ScrollPane scrollPaneOfActor(Actor actor)
{
while (actor!=null)
{
if(actor.getParent() instanceof ScrollPane)
{
ScrollPane scrollPaneOfActor(Actor actor) {
while (actor != null) {
if (actor.getParent() instanceof ScrollPane) {
return (ScrollPane) actor.getParent();
}
actor = actor.getParent();
@@ -641,16 +596,16 @@ public class UIScene extends Scene {
public void selectActor(Selectable actor) {
unselectActors();
if(actor==null)return;
stage.setKeyboardFocus(actor.actor);
ScrollPane scrollPane=scrollPaneOfActor(actor.actor);
if(scrollPane!=null)
{
scrollPane.scrollTo(actor.actor.getX(),actor.actor.getY(),actor.actor.getWidth(),actor.actor.getHeight(),false,false);
}
actor.onSelect(this);
unselectActors();
if (actor == null) return;
stage.setKeyboardFocus(actor.actor);
ScrollPane scrollPane = scrollPaneOfActor(actor.actor);
if (scrollPane != null) {
scrollPane.scrollTo(actor.actor.getX(), actor.actor.getY(), actor.actor.getWidth(), actor.actor.getHeight(), false, false);
}
actor.onSelect(this);
}
Image screenImage;
TextureRegion backgroundTexture;
@@ -659,6 +614,7 @@ public class UIScene extends Scene {
stage.cancelTouchFocus();
return super.leave();
}
@Override
public void enter() {
if (screenImage != null) {
@@ -674,6 +630,7 @@ public class UIScene extends Scene {
Gdx.input.setInputProcessor(stage);
super.enter();
}
public TextureRegion getUIBackground() {
try {
Actor a = ui.getChild(0);

View File

@@ -2,6 +2,8 @@ package forge.adventure.stage;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
@@ -33,8 +35,11 @@ import forge.adventure.world.WorldSave;
import forge.deck.Deck;
import forge.gui.FThreads;
import forge.gui.GuiBase;
import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel;
import forge.sound.MusicPlaylist;
import forge.sound.SoundSystem;
import org.apache.commons.lang3.tuple.Pair;
/**
* Stage to handle everything rendered in the HUD
@@ -275,7 +280,10 @@ public class GameHUD extends Stage {
updatelife = false;
lifePoints.setText("[%95][+Life]" + lifepointsTextColor + " " + AdventurePlayer.current().getLife() + "/" + AdventurePlayer.current().getMaxLife());
}
updateMusic();
if (!GameScene.instance().isNotInWorldMap())
updateMusic();
else
SoundSystem.instance.pause();
}
Texture miniMapTexture;
@@ -301,7 +309,122 @@ public class GameHUD extends Stage {
} else {
deckActor.setColor(menuActor.getColor());
}
updateMusic();
if (GameScene.instance().isNotInWorldMap()) {
SoundSystem.instance.pause();
playAudio();
} else {
unloadAudio();
SoundSystem.instance.resume(); // resume World BGM
}
}
private Pair<FileHandle, Music> audio = null;
public void playAudio() {
switch (GameScene.instance().getAdventurePlayerLocation(false, false)) {
case "capital":
case "town":
setAudio(MusicPlaylist.TOWN);
break;
case "dungeon":
case "cave":
setAudio(MusicPlaylist.CAVE);
break;
case "castle":
setAudio(MusicPlaylist.CASTLE);
break;
default:
break;
}
if (audio != null) {
audio.getRight().setLooping(true);
audio.getRight().play();
audio.getRight().setVolume(FModel.getPreferences().getPrefInt(ForgePreferences.FPref.UI_VOL_MUSIC) / 100f);
}
}
public void fadeAudio(float value) {
if (audio != null) {
audio.getRight().setVolume((FModel.getPreferences().getPrefInt(ForgePreferences.FPref.UI_VOL_MUSIC) * value) / 100f);
}
}
float fade = 1f;
void fadeIn() {
if (fade >= 1f)
return;
for (int i = 10; i > 1; i--) {
float delay = i * 0.1f;
Timer.schedule(new Timer.Task() {
@Override
public void run() {
fade += 0.1f;
if (fade > 1f)
fade = 1f;
fadeAudio(fade);
}
}, delay);
}
}
void fadeOut() {
for (int i = 10; i > 1; i--) {
float delay = i * 0.1f;
Timer.schedule(new Timer.Task() {
@Override
public void run() {
fade -= 0.1f;
if (fade < 0.1f)
fade = 0.1f;
fadeAudio(fade);
}
}, delay);
}
}
public void stopAudio() {
if (audio != null) {
audio.getRight().stop();
}
}
public void pauseMusic() {
if (audio != null) {
audio.getRight().pause();
}
SoundSystem.instance.pause();
}
public void unloadAudio() {
if (audio != null) {
audio.getRight().setOnCompletionListener(null);
audio.getRight().stop();
Forge.getAssets().manager().unload(audio.getLeft().path());
}
audio = null;
currentAudioPlaylist = null;
}
private MusicPlaylist currentAudioPlaylist = null;
private void setAudio(MusicPlaylist playlist) {
if (playlist.equals(currentAudioPlaylist))
return;
unloadAudio();
audio = getMusic(playlist);
}
private Pair<FileHandle, Music> getMusic(MusicPlaylist playlist) {
FileHandle file = Gdx.files.absolute(playlist.getNewRandomFilename());
Music music = Forge.getAssets().getMusic(file);
if (music != null) {
currentAudioPlaylist = playlist;
return Pair.of(file, music);
} else {
currentAudioPlaylist = null;
return null;
}
}
private void openDeck() {
@@ -580,17 +703,6 @@ public class GameHUD extends Stage {
case "white":
changeBGM(MusicPlaylist.WHITE);
break;
case "capital":
case "town":
changeBGM(MusicPlaylist.TOWN);
break;
case "dungeon":
case "cave":
changeBGM(MusicPlaylist.CAVE);
break;
case "castle":
changeBGM(MusicPlaylist.CASTLE);
break;
case "waste":
changeBGM(MusicPlaylist.MENUS);
break;
@@ -598,7 +710,6 @@ public class GameHUD extends Stage {
break;
}
}
float fade = 1f;
void changeBGM(MusicPlaylist playlist) {
if (!playlist.equals(SoundSystem.instance.getCurrentPlaylist())) {

View File

@@ -63,30 +63,6 @@ public class MapSprite extends Actor {
return actorGroup;
}
public static String getBoundaryName(int chunkX, int chunkY) {
String boundary = "";
List<PointOfInterest> poi = WorldSave.getCurrentSave().getWorld().getPointsOfInterest(chunkX, chunkY);
for (PointOfInterest p : poi) {
if ("town".equalsIgnoreCase(p.getData().type)) {
if (p.getData().name.startsWith("Waste"))
boundary = "waste";
else if (p.getData().name.startsWith("Plains"))
boundary = "white";
else if (p.getData().name.startsWith("Forest"))
boundary = "green";
else if (p.getData().name.startsWith("Island"))
boundary = "blue";
else if (p.getData().name.startsWith("Mountain"))
boundary = "red";
else if (p.getData().name.startsWith("Swamp"))
boundary = "black";
break;
}
}
return boundary;
}
//BitmapFont font;
@Override
public void draw(Batch batch, float parentAlpha) {

View File

@@ -294,12 +294,6 @@ public class WorldStage extends GameStage implements SaveFileContent {
background.loadChunk(pos.x, pos.y);
handlePointsOfInterestCollision();
}
public String getBoundary() {
if (background == null)
return "";
GridPoint2 pos = background.translateFromWorldToChunk(player.getX(), player.getY());
return MapSprite.getBoundaryName(pos.x, pos.y);
}
@Override
public void leave() {

View File

@@ -648,7 +648,9 @@ public class CardUtil {
FileHandle handle = Config.instance().getFile(path);
if (handle.exists())
return generateDeck(json.fromJson(GeneratedDeckData.class, handle), starterEdition, discourageDuplicates);
return null;
Deck deck = DeckgenUtil.getRandomOrPreconOrThemeDeck(colors, true, false, true);
System.err.println("Error loading JSON: " + handle.path() + "\nGenerating random deck: "+deck.getName());
return deck;
}

View File

@@ -27,6 +27,8 @@ import com.github.tommyettinger.textra.TypingLabel;
import forge.Forge;
import forge.adventure.player.AdventurePlayer;
import forge.card.ColorSet;
import forge.sound.SoundEffectType;
import forge.sound.SoundSystem;
import java.util.function.Function;
@@ -52,6 +54,13 @@ public class Controls {
static class TextButtonFix extends TextraButton {
public TextButtonFix(@Null String text) {
super(text == null ? "NULL" : text, Controls.getSkin(), Controls.getTextraFont());
addListener(new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
super.clicked(event, x, y);
SoundSystem.instance.play(SoundEffectType.ButtonPress, false);
}
});
}
@Override
@@ -74,6 +83,7 @@ public class Controls {
getTextraLabel().setWidth(getTextraLabel().layout.getWidth() + (getTextraLabel().style != null && getTextraLabel().style.background != null ? getTextraLabel().style.background.getLeftWidth() + getTextraLabel().style.background.getRightWidth() : 0.0F));
layout();
}
}
static public TextraButton newTextButton(String text) {

View File

@@ -15,11 +15,11 @@ import forge.Forge;
import forge.adventure.character.EnemySprite;
import forge.adventure.data.DialogData;
import forge.adventure.player.AdventurePlayer;
import forge.adventure.stage.GameHUD;
import forge.adventure.stage.MapStage;
import forge.card.ColorSet;
import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel;
import forge.sound.SoundSystem;
import forge.util.Localizer;
import org.apache.commons.lang3.tuple.Pair;
@@ -196,7 +196,7 @@ public class MapDialog {
fade += 0.1f;
if (fade > 1f)
fade = 1f;
SoundSystem.instance.fadeModifier(fade);
GameHUD.getInstance().fadeAudio(fade);
}
}, delay);
}
@@ -211,7 +211,7 @@ public class MapDialog {
fade -= 0.1f;
if (fade < 0.1f)
fade = 0.1f;
SoundSystem.instance.fadeModifier(fade);
GameHUD.getInstance().fadeAudio(fade);
}
}, delay);
}

View File

@@ -41,6 +41,8 @@ import forge.card.CardRenderer;
import forge.game.card.CardView;
import forge.gui.GuiBase;
import forge.item.PaperCard;
import forge.sound.SoundEffectType;
import forge.sound.SoundSystem;
import forge.util.ImageFetcher;
import forge.util.ImageUtil;
import org.apache.commons.lang3.StringUtils;
@@ -593,6 +595,7 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
return;
clicked = true;
flipProcess = 0;
SoundSystem.instance.play(SoundEffectType.FlipCard, false);
}
public void sold() {
@@ -608,7 +611,7 @@ public class RewardActor extends Actor implements Disposable, ImageFetcher.Callb
super.act(delta);
if (clicked) {
if (flipProcess < 1)
flipProcess += delta * 1.5;
flipProcess += delta * 2.4;
else
flipProcess = 1;

View File

@@ -238,6 +238,14 @@ public class World implements Disposable, SaveFileContent {
}
}
public long getBiomeMapXY(int x, int y) {
try {
return biomeMap[x][height - y - 1] & (~(0b1<<data.GetBiomes().size()));
} catch (ArrayIndexOutOfBoundsException e) {
return biomeMap[biomeMap.length - 1][biomeMap[biomeMap.length - 1].length - 1];
}
}
public boolean isStructure(int x, int y) {
try {
return (terrainMap[x][height - y - 1] & ~isStructureBit) != 0;

View File

@@ -558,7 +558,8 @@ public class Assets implements Disposable {
@Override
public synchronized void unload(String fileName) {
super.unload(fileName);
if (isLoaded(fileName))
super.unload(fileName);
if (memoryPerFile.containsKey(fileName)) {
memoryPerFile.remove(fileName);
cardArtCache().clear();

View File

@@ -236,6 +236,8 @@ public class CardImageRenderer {
g.drawOutlinedText(artist, TEXT_FONT, Color.WHITE, Color.DARK_GRAY, x + (TYPE_FONT.getCapHeight() / 2), y + (TYPE_FONT.getCapHeight() / 2), w, h, false, Align.left, false);
}
private static void drawOutlineColor(Graphics g, ColorSet colors, float x, float y, float w, float h) {
if (colors == null)
return;
switch (colors.countColors()) {
case 0:
g.drawRect(BORDER_THICKNESS*2, Color.valueOf("#A0A6A4"), x, y, w, h);
@@ -264,7 +266,8 @@ public class CardImageRenderer {
fillColorBackground(g, colors, x, y, w, h);
g.setAlphaComposite(oldAlpha);
//draw outline color here
drawOutlineColor(g, state.getColors(), x, y, w, h);
if (state != null)
drawOutlineColor(g, state.getColors(), x, y, w, h);
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
float padding = h / 8;
@@ -450,7 +453,8 @@ public class CardImageRenderer {
fillColorBackground(g, colors, x, y, w, h);
g.setAlphaComposite(oldAlpha);
//draw outline color here
drawOutlineColor(g, state.getColors(), x, y, w, h);
if (state != null)
drawOutlineColor(g, state.getColors(), x, y, w, h);
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
float padding = h / 8;
@@ -619,7 +623,8 @@ public class CardImageRenderer {
}
}
//draw outline color here
drawOutlineColor(g, state.getColors(), x, y, w, h);
if (state != null)
drawOutlineColor(g, state.getColors(), x, y, w, h);
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
if (!onTop) {
@@ -738,7 +743,8 @@ public class CardImageRenderer {
fillColorBackground(g, colors, x, y, w, h);
//draw outline color here
drawOutlineColor(g, state.getColors(), x, y, w, h);
if (state != null)
drawOutlineColor(g, state.getColors(), x, y, w, h);
g.drawRect(BORDER_THICKNESS, Color.BLACK, x, y, w, h);
if (noText)

View File

@@ -30,7 +30,8 @@ public class AudioMusic implements IAudioMusic {
public void pause() {
if (music == null)
return;
music.pause();
if (music.isPlaying())
music.pause();
}
public void resume() {

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.
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

@@ -0,0 +1,12 @@
{
"name":"Fungus",
"template":
{
"count":60,
"colors":["Green","Black"],
"tribe":"Fungus",
"tribeCards":1.0,
"tribeSynergyCards":0.2,
"rares":0.2
}
}

View File

@@ -3,13 +3,13 @@
"height": 4300,
"width": 2
},
"activeFile": "",
"activeFile": "map/cave_6.tmx",
"automapping.whileDrawing": false,
"expandedProjectPaths": [
"tileset",
"map/main_story",
"map",
"obj"
"obj",
"map/main_story",
"tileset"
],
"file.lastUsedOpenFilter": "All Files (*)",
"fileStates": {
@@ -110,24 +110,24 @@
"scale": 3,
"selectedLayer": 0,
"viewCenter": {
"x": 279.5,
"y": 217.5
"x": 240,
"y": 136.16666666666663
}
},
"map/castle_plains_2.tmx": {
"scale": 1.5,
"selectedLayer": 0,
"viewCenter": {
"x": 359.66666666666663,
"y": 295.33333333333326
"x": 240,
"y": 136.33333333333334
}
},
"map/castle_plains_3.tmx": {
"scale": 2,
"selectedLayer": 4,
"selectedLayer": 3,
"viewCenter": {
"x": 240,
"y": 136
"y": 136.25
}
},
"map/catlair_1.tmx": {
@@ -174,8 +174,8 @@
"scale": 4,
"selectedLayer": 0,
"viewCenter": {
"x": 240,
"y": 239.875
"x": 187.5,
"y": 316.625
}
},
"map/cave_1.tmx": {
@@ -428,8 +428,8 @@
"scale": 2,
"selectedLayer": 4,
"viewCenter": {
"x": 240.25,
"y": 136
"x": 135,
"y": 119.75
}
},
"map/cave_20.tmx": {
@@ -1356,16 +1356,19 @@
"scale": 2,
"selectedLayer": 0,
"viewCenter": {
"x": 240.25,
"y": 136
"x": 240,
"y": 136.25
}
},
"map/cave_4.tmx": {
"scale": 1,
"selectedLayer": 2,
"expandedObjectLayers": [
4
],
"scale": 2,
"selectedLayer": 3,
"viewCenter": {
"x": 220,
"y": 135
"x": 145,
"y": 151.75
}
},
"map/cave_5.tmx": {
@@ -1373,15 +1376,15 @@
"selectedLayer": 0,
"viewCenter": {
"x": 232,
"y": 136
"y": 136.25
}
},
"map/cave_6.tmx": {
"scale": 2,
"selectedLayer": 0,
"viewCenter": {
"x": 232,
"y": 136
"x": 337,
"y": 152.75
}
},
"map/cave_6N1.tmx": {
@@ -3206,10 +3209,10 @@
4
],
"scale": 2,
"selectedLayer": 4,
"selectedLayer": 0,
"viewCenter": {
"x": 390.5,
"y": 221.75
"x": 329.5,
"y": 204.25
}
},
"map/yule_town.tmx": {
@@ -3251,22 +3254,31 @@
"map.tileWidth": 16,
"map.width": 90,
"openFiles": [
"map/xira.tmx",
"map/castle_plains_1.tmx",
"map/castle_plains_2.tmx",
"map/castle_plains_3.tmx",
"map/cave_2.tmx",
"map/cave_3.tmx",
"map/cave_4.tmx",
"map/cave_5.tmx",
"map/cave_6.tmx"
],
"project": "main.tiled-project",
"property.type": "string",
"recentFiles": [
"map/main_story/templeofchandra.tmx",
"map/tibalt.tmx",
"map/tibalt_f1.tmx",
"map/xira.tmx",
"map/nahiricave.tmx",
"map/nahiri.tmx",
"map/grolnok.tmx",
"map/tibalt_f2.tmx",
"map/cursed_swamp.tmx",
"map/slobad_factory.tmx",
"map/main_story/blue_castle_f1.tmx",
"map/kiora_island.tmx"
"map/castle_plains_1.tmx",
"map/castle_plains_2.tmx",
"map/castle_plains_3.tmx",
"map/cave_2.tmx",
"map/cave_3.tmx",
"map/cave_4.tmx",
"map/cave_5.tmx",
"map/cave_6.tmx",
"map/cave_1..tmx",
"map/main_story/templeofchandra.tmx",
"map/tibalt.tmx"
],
"resizeMap.removeObjects": true,
"textEdit.monospace": true

View File

@@ -10,7 +10,7 @@
<tileset firstgid="10113" source="../tileset/buildings.tsx"/>
<layer id="11" name="Collision" width="40" height="26">
<data encoding="base64" compression="zlib">
eJzVlUkOgCAMRdl6Me9/JFcmhkCHP2hswgp++0pbOI8xzh+tlb2lj3SIVbURRyevbtz7LGORPspJ4b+i/ZIv85P1oKK2We9UOJ+aiI+ZlZ3fynxX+Jxs816kc/KtajHzIf5Ud6c2BZ8rN0fvKQydLSbeM+7M4GBD3l12ddm6+VffPwVfplH1DlNf9M9C4nT/N3aeunF2nNFfhDJltXfNDcOInGHjZ+cqewo+5/2z86uu/8p/961w9ucuhnsxDBe0OnBV
eJzVl11uwkAMhHnMPsBtegWKckDOgNqehjOgwjHIKrLqjDz+WZUHRhopJNn1F6/3h2Pb7Y5v5NPi7+nPXZX2uq12tq2WfuYpionfg0IO65nV1sqdvh9pTr53a1sjQ+a7Ti3XZoSPSWLflbN8+NviiPgidhy/DB/jxVhz8+tAP2Pv4Liz2hQei81jZHPCm6OePD6PDd+NYjCmbL4rfJU84JhafDh/u9j6lGWTa0vn/WqMgzG99ZGpwhf18XGoxa6yZfis2rweVr+Sy1tTIr5MPN0f67vCZvWB7BHTf9tbVzSflY8Kl9SD5VG+aNwz+cvUqWbFfrJ8LHcsjrBpXabVkTAXbMxG1h3Nqpku6twYcbJ9ITN3qvJq65PUDzLgPTlXjKwrFcYMX/fXkuufactm7ZeZ+Jb6WEv/3p7LWHueZnXNpM+60XpiKbsPsxpj7YULc47nzJHcix7kO3/b9n+AZdZOz03L7DvRjO3V9sYt8hMoNu4z
</data>
</layer>
<layer id="8" name="Background" width="40" height="26">

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -130,7 +130,7 @@ WalkDown
xy: 144, 16
size: 16, 16
WalkDown
xy: 160, 0
xy: 160, 16
size: 16, 16
WalkDown
xy: 176, 16

View File

@@ -351,7 +351,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 6
"addMaxCount": 6,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -1241,12 +1245,16 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4
"addMaxCount": 4,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
"probability": 0.5,
"count": 3,
"count": 1,
"colors": [
"Green"
],
@@ -1330,7 +1338,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4
"addMaxCount": 5,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -2830,7 +2842,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4
"addMaxCount": 5,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "gold",
@@ -3366,7 +3382,7 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 4
},
{
"type": "card",
@@ -3397,7 +3413,7 @@
{
"type": "card",
"probability": 1,
"count": 3,
"count": 1,
"colors": [
"Blue"
],
@@ -4153,7 +4169,7 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 4
},
{
"type": "card",
@@ -4471,7 +4487,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 4,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -4519,7 +4539,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 5,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -4643,7 +4667,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 4,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -5087,7 +5115,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4
"addMaxCount": 8,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -6524,7 +6556,7 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 4
},
{
"type": "card",
@@ -6755,7 +6787,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4
"addMaxCount": 6,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "gold",
@@ -6794,7 +6830,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4
"addMaxCount": 4,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "gold",
@@ -6802,23 +6842,6 @@
"count": 10,
"addMaxCount": 90
},
{
"type": "card",
"probability": 0.5,
"count": 2,
"colors": [
"Green"
],
"rarity": [
"Mythic Rare"
]
},
{
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
},
{
"type": "card",
"probability": 1,
@@ -7303,7 +7326,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 8,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -7349,7 +7376,7 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 4
},
{
"type": "card",
@@ -8432,7 +8459,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 4
"addMaxCount": 4,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "gold",
@@ -9187,7 +9218,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 8,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -9866,7 +9901,11 @@
"type": "deckCard",
"probability": 1,
"count": 3,
"addMaxCount": 7
"addMaxCount": 6,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "gold",
@@ -10194,7 +10233,11 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 8,
"rarity": [
"common",
"uncommon"
]
},
{
"type": "card",
@@ -10716,7 +10759,7 @@
"type": "deckCard",
"probability": 1,
"count": 2,
"addMaxCount": 8
"addMaxCount": 4
},
{
"type": "card",
@@ -11219,18 +11262,11 @@
{
"type": "deckCard",
"probability": 1,
"count": 1,
"addMaxCount": 4,
"count": 2,
"addMaxCount": 8,
"rarity": [
"common",
"uncommon"
],
"cardTypes": [
"Creature",
"Artifact",
"Enchantment",
"Instant",
"Sorcery"
]
},
{

View File

@@ -45,7 +45,6 @@
"Fox",
"Frost Titan",
"Griffin",
"Horse",
"Horseman",
"Human",
"Human elite",

Binary file not shown.

Binary file not shown.

View File

@@ -119,3 +119,4 @@ Dominaria Remastered, 3/6/DMR, DMR
Phyrexia: All Will Be One, 3/6/ONE, ONE
Phyrexia: All Will Be One Jumpstart, -/2/ONE, Meta-Choose(S(ONE Mite-y 1)Mite-y 1;S(ONE Mite-y 2)Mite-y 2;S(ONE Progress 1)Progress 1;S(ONE Progress 2)Progress 2;S(ONE Corruption 1)Corruption 1;S(ONE Corruption 2)Corruption 2;S(ONE Rebellious 1)Rebellious 1;S(ONE Rebellious 2)Rebellious 2;S(ONE Toxic 1)Toxic 1;S(ONE Toxic 2)Toxic 2)Themes
Alchemy: Phyrexia, 3/6/ONE, YONE
Shadows over Innistrad Remastered, 3/6/SIR, SIR

View File

@@ -4,7 +4,7 @@ Types:Creature Goblin Artificer
PT:1/2
S:Mode$ Continuous | Affected$ Creature.modified+YouCtrl | AddKeyword$ Menace | Description$ Modified creatures you control have menace.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, you may discard a card. If you do, seek a card with mana value equal to that card's mana value.
SVar:TrigSeek:AB$ ChangeZone | Cost$ Discard<1/Card> | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.cmcEQX | ChangeNum$ 1
SVar:TrigSeek:AB$ Seek | Cost$ Discard<1/Card> | Type$ Card.cmcEQX
SVar:X:Discarded$CardManaCost
DeckHas:Ability$Discard & Keyword$Menace
DeckHints:Type$Equipment|Aura & Ability$Counters

View File

@@ -3,6 +3,8 @@ ManaCost:R G W
Types:Creature Elf Druid
PT:3/4
T:Mode$ ChangesZoneAll | ValidCards$ Creature.Other+YouCtrl | Destination$ Battlefield | TriggerZones$ Battlefield | ActivationLimit$ 1 | Execute$ TrigSeek | TriggerDescription$ Alliance — Whenever one or more other creatures enter the battlefield under your control, seek a land card, then put it onto the battlefield tapped. This ability triggers only once each turn.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Land | ChangeNum$ 1 | Tapped$ True
SVar:TrigSeek:DB$ Seek | Type$ Land | RememberFound$ True | SubAbility$ DBPut
SVar:DBPut:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | Defined$ Remembered | Tapped$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:BuffedBy:Creature
Oracle:Alliance — Whenever one or more other creatures enter the battlefield under your control, seek a land card, then put it onto the battlefield tapped. This ability triggers only once each turn.

View File

@@ -1,9 +1,8 @@
Name:Bounty of the Deep
ManaCost:2 U
Types:Sorcery
A:SP$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ GE1 | TrueSubAbility$ Seek2NonLand | FalseSubAbility$ Seek1Land | StackDescription$ SpellDescription | SpellDescription$ If you have no land cards in your hand, seek a land card and a nonland card. Otherwise, seek two nonland cards.
SVar:Seek1Land:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.Land | ChangeNum$ 1 | SubAbility$ Seek1NonLand
SVar:Seek1NonLand:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.nonLand | ChangeNum$ 1
SVar:Seek2NonLand:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.nonLand | ChangeNum$ 2
A:SP$ Branch | BranchConditionSVar$ X | BranchConditionSVarCompare$ GE1 | TrueSubAbility$ Seek2 | FalseSubAbility$ Seek1 | StackDescription$ SpellDescription | SpellDescription$ If you have no land cards in your hand, seek a land card and a nonland card. Otherwise, seek two nonland cards.
SVar:Seek1:DB$ Seek | Types$ Card.Land,Card.nonLand
SVar:Seek2:DB$ Seek | Type$ Card.nonLand | Num$ 2
SVar:X:Count$ValidHand Land.YouCtrl
Oracle:If you have no land cards in your hand, seek a land card and a nonland card. Otherwise, seek two nonland cards.

View File

@@ -2,7 +2,9 @@ Name:Cabaretti Revels
ManaCost:R R G
Types:Enchantment
T:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigSeek | TriggerDescription$ Whenever you cast a creature spell, seek a creature card with lesser mana value, then put it onto the battlefield.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Creature.cmcLTX
SVar:TrigSeek:DB$ Seek | Type$ Creature.cmcLTX | RememberFound$ True | SubAbility$ DBPut
SVar:DBPut:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | Defined$ Remembered | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:TriggeredCard$CardManaCost
SVar:BuffedBy:Creature
Oracle:Whenever you cast a creature spell, seek a creature card with lesser mana value, then put it onto the battlefield.

View File

@@ -5,9 +5,9 @@ PT:4/5
K:Flying
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigCharm | TriggerDescription$ Whenever CARDNAME attacks or dies, ABILITY
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigCharm | Secondary$ True | TriggerDescription$ Whenever CARDNAME attacks or dies, ABILITY
SVar:TrigCharm:DB$ Charm | CharmNum$ 2 | Choices$ DiscardSeek,DamageTreasures,DamagePump | AdditionalDescription$ Each mode must target a different player.
SVar:DiscardSeek:DB$ Discard | ValidTgts$ Player | TargetUnique$ True | TgtPrompt$ Select target player to discard hand and seek | Mode$ Hand | RememberDiscarded$ True | SubAbility$ DBSeek | SpellDescription$ Target player discards all cards in their hand, then seeks that many nonland cards.
SVar:DBSeek:DB$ ChangeZone | Defined$ ParentTarget | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ X | ChangeType$ Card.nonLand | SubAbility$ DBCleanup
SVar:TrigCharm:DB$ Charm | CharmNum$ 2 | Choices$ DiscardSeek,DamageTreasures,DamagePump | AdditionalDescription$ . Each mode must target a different player.
SVar:DiscardSeek:DB$ Discard | ValidTgts$ Player | TargetUnique$ True | Mode$ Hand | RememberDiscarded$ True | SubAbility$ DBSeek | SpellDescription$ Target player discards all cards in their hand, then seeks that many nonland cards.
SVar:DBSeek:DB$ Seek | Defined$ Targeted | Num$ X | Type$ Card.nonLand | SubAbility$ DBCleanup
SVar:DamageTreasures:DB$ DealDamage | ValidTgts$ Player | TargetUnique$ True | TgtPrompt$ Select target player to take 2 damage and create two Treasures | NumDmg$ 2 | SubAbility$ DBToken | SpellDescription$ CARDNAME deals 2 damage to target player and they create two Treasure tokens.
SVar:DBToken:DB$ Token | TokenScript$ c_a_treasure_sac | TokenAmount$ 2 | TokenOwner$ ParentTarget
SVar:DamagePump:DB$ DamageAll | ValidTgts$ Player | TargetUnique$ True | NumDmg$ 2 | RememberDamaged$ True | ValidCards$ Creature | ValidDescription$ each creature target player controls. | SubAbility$ DBEffect | SpellDescription$ CARDNAME deals 2 damage to each creature target player controls. Those creatures perpetually get +2/+0.

View File

@@ -1,9 +1,9 @@
Name:Choice of Fortunes
ManaCost:2 U
Types:Sorcery
A:SP$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card | ChangeNum$ 2 | RememberChanged$ True | SubAbility$ DBShuffle | StackDescription$ SpellDescription | SpellDescription$ Seek two cards.
A:SP$ Seek | Num$ 2 | RememberFound$ True | SubAbility$ DBShuffle | SpellDescription$ Seek two cards.
SVar:DBShuffle:DB$ ChangeZone | Optional$ True | Defined$ Remembered | Origin$ Hand | Destination$ Library | Shuffle$ True | ForgetChanged$ True | SubAbility$ DBSeek | StackDescription$ SpellDescription | SpellDescription$ You may shuffle them into your library.
SVar:DBSeek:DB$ ChangeZone | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card | ChangeNum$ 2 | SubAbility$ DBCleanup | StackDescription$ SpellDescription | SpellDescription$ If you do, seek two cards.
SVar:DBSeek:DB$ Seek | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0 | Num$ 2 | SubAbility$ DBCleanup | SpellDescription$ If you do, seek two cards.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | StaticAbilities$ STHandSize | Duration$ Permanent | SpellDescription$ You have no maximum hand size for the rest of the game.
SVar:STHandSize:Mode$ Continuous | EffectZone$ Command | Affected$ You | SetMaxHandSize$ Unlimited | Description$ You have no maximum hand size for the rest of the game.

View File

@@ -5,8 +5,8 @@ PT:3/3
T:Mode$ Phase | Phase$ End of Turn | OptionalDecider$ You | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ At the beginning of your end step, you may discard a card. If you do, create a Treasure token and choose ambitious or expedient. If you chose ambitious, seek a card with greater mana value than the discarded card. If you chose expedient, seek a card with lesser mana value than the discarded card.
SVar:TrigToken:AB$ Token | Cost$ Discard<1/Card> | TokenScript$ c_a_treasure_sac | SubAbility$ DBChoose
SVar:DBChoose:DB$ GenericChoice | Choices$ Ambitious,Expedient | Defined$ You | AILogic$ Random
SVar:Ambitious:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.cmcGTX | ChangeNum$ 1 | SpellDescription$ Ambitious - Seek a card with greater mana value than the discarded card.
SVar:Expedient:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.cmcLTX | ChangeNum$ 1 | SpellDescription$ Expedient - Seek a card with lesser mana value than the discarded card.
SVar:Ambitious:DB$ Seek | Type$ Card.cmcGTX | SpellDescription$ Ambitious - Seek a card with greater mana value than the discarded card.
SVar:Expedient:DB$ Seek | Type$ Card.cmcLTX | SpellDescription$ Expedient - Seek a card with lesser mana value than the discarded card.
DeckHas:Ability$Sacrifice|Token|Discard & Type$Treasure|Artifact
SVar:X:Discarded$CardManaCost
Oracle:At the beginning of your end step, you may discard a card. If you do, create a Treasure token and choose ambitious or expedient. If you chose ambitious, seek a card with greater mana value than the discarded card. If you chose expedient, seek a card with lesser mana value than the discarded card.

View File

@@ -8,7 +8,7 @@ T:Mode$ Drawn | ValidCard$ Card.YouOwn+Dragon | TriggerZones$ Battlefield | Exec
SVar:DBEffect:DB$ Effect | StaticAbilities$ PerpetualP1P1 | RememberObjects$ TriggeredCard | Name$ Darigaaz's Whelp's Perpetual Effect | Duration$ Permanent
SVar:PerpetualP1P1:Mode$ Continuous | Affected$ Card.IsRemembered | AddPower$ 1 | AddToughness$ 1 | EffectZone$ Command | AffectedZone$ Battlefield,Hand,Graveyard,Exile,Stack,Library,Command | Description$ This card perpetually gets +1/+1.
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self+kicked | Execute$ TrigSeek | TriggerDescription$ When this creature enters the battlefield, if it was kicked, seek a Dragon card. CARDNAME and that Dragon card each perpetually get +1/+1.
SVar:TrigSeek:DB$ ChangeZone | RememberChanged$ True | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.Dragon | SubAbility$ DBEffectBis
SVar:TrigSeek:DB$ Seek | RememberFound$ True | Type$ Card.Dragon | SubAbility$ DBEffectBis
SVar:DBEffectBis:DB$ Effect | StaticAbilities$ PerpetualP1P1Bis | RememberObjects$ Remembered | Name$ Darigaaz's Whelp's Perpetual Effect | Duration$ Permanent | SubAbility$ DBCleanup
SVar:PerpetualP1P1Bis:Mode$ Continuous | Affected$ Card.IsRemembered,Card.EffectSource | AddPower$ 1 | AddToughness$ 1 | EffectZone$ Command | AffectedZone$ Battlefield,Hand,Graveyard,Exile,Stack,Library,Command | Description$ CARDNAME and the conjured Dragon card each perpetually get +1/+1.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -5,6 +5,6 @@ PT:2/1
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigConnive | TriggerDescription$ When CARDNAME enters the battlefield, it connives. (Draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on this creature.)
SVar:TrigConnive:DB$ Connive
T:Mode$ DiscardedAll | ValidPlayer$ You | ValidCard$ Card | TriggerZones$ Battlefield | Execute$ TrigSeek | ActivationLimit$ 1 | TriggerDescription$ Whenever you discard one or more cards, seek a card that shares a card type with one of the discarded cards. This ability triggers only once each turn.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.sharesCardTypeWith TriggeredCards | ChangeNum$ 1
SVar:TrigSeek:DB$ Seek | Type$ Card.sharesCardTypeWith TriggeredCards
DeckHas:Ability$Discard|Counters
Oracle:When Diviner of Fates enters the battlefield, it connives.\nWhenever you discard one or more cards, seek a card that shares a card type with one of the discarded cards. This ability triggers only once each turn.

View File

@@ -4,5 +4,5 @@ Types:Creature Shapeshifter
PT:2/2
K:Changeling
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, seek a creature card of the most prevalent creature type in your library.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.Creature+MostProminentCreatureTypeInLibrary | ChangeNum$ 1
SVar:TrigSeek:DB$ Seek | Type$ Card.Creature+MostProminentCreatureTypeInLibrary
Oracle:Changeling\nWhen Faceless Agent enters the battlefield, seek a creature card of the most prevalent creature type in your library.

View File

@@ -2,7 +2,7 @@ Name:Faith Unbroken
ManaCost:3 W
Types:Enchantment Aura
K:Enchant creature you control
A:SP$ Attach | Cost$ 3 W | ValidTgts$ Creature | AILogic$ Pump
A:SP$ Attach | Cost$ 3 W | ValidTgts$ Creature.YouCtrl | AILogic$ Pump
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME enters the battlefield, exile target creature an opponent controls until CARDNAME leaves the battlefield.
SVar:TrigExile:DB$ ChangeZone | Origin$ Battlefield | Destination$ Exile | ValidTgts$ Creature.OppCtrl | TgtPrompt$ Select target creature an opponent controls | Duration$ UntilHostLeavesPlay
SVar:PlayMain1:TRUE

View File

@@ -3,10 +3,9 @@ ManaCost:1 R
Types:Creature Human Wizard
PT:2/2
K:Prowess
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ Z | SVarCompare$ GE20 | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, if there are twenty or more instant and/or sorcery cards among cards in your graveyard, hand, and library, you may discard a card. If you do, seek an instant or sorcery card.
SVar:TrigSeek:AB$ ChangeZone | Cost$ Discard<1/Card> | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Instant,Sorcery
SVar:X:Count$ValidGraveyard Instant.YouOwn,Sorcery.YouOwn
SVar:Y:Count$ValidLibrary Instant.YouOwn,Sorcery.YouOwn/Plus.X
SVar:Z:Count$ValidHand Instant.YouOwn,Sorcery.YouOwn/Plus.Y
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | CheckSVar$ X | SVarCompare$ GE20 | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, if there are twenty or more instant and/or sorcery cards among cards in your graveyard, hand, and library, you may discard a card. If you do, seek an instant or sorcery card.
SVar:TrigSeek:AB$ Seek | Cost$ Discard<1/Card> | Type$ Instant,Sorcery
SVar:X:Count$ValidGraveyard,Library,Hand Instant.YouOwn,Sorcery.YouOwn
DeckNeeds:Type$Instant|Sorcery
DeckHas:Ability$Discard
Oracle:When Frenzied Geistblaster enters the battlefield, if there are twenty or more instant and/or sorcery cards among cards in your graveyard, hand, and library, you may discard a card. If you do, seek an instant or sorcery card.

View File

@@ -7,7 +7,7 @@ SVar:DBRandom:DB$ ChooseCard | Defined$ You | Choices$ Elf.YouOwn | ChoiceZone$
SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | StaticAbilities$ PerpetualP1P1 | Name$ Freyalise, Skyshroud Partisan's Perpetual Effect | Duration$ Permanent | SubAbility$ DBCleanup
SVar:PerpetualP1P1:Mode$ Continuous | Affected$ Card.IsRemembered,Card.ChosenCard | AddPower$ 1 | AddToughness$ 1 | EffectZone$ Command | AffectedZone$ Battlefield,Hand,Graveyard,Exile,Stack,Library,Command | Description$ The target Elf and randomly chosen card perpetually get +1/+1.
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
A:AB$ ChangeZone | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.Elf | ChangeNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Seek an Elf card.
A:AB$ Seek | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | Type$ Card.Elf | StackDescription$ REP Seek_{p:You} seeks | SpellDescription$ Seek an Elf card.
A:AB$ MakeCard | Conjure$ True | Cost$ SubCounter<6/LOYALTY> | Planeswalker$ True | Ultimate$ True | Name$ Regal Force | Zone$ Battlefield | SpellDescription$ Conjure a Regal Force card onto the battlefield.
DeckNeeds:Type$Elf
Oracle:[+1]: Choose up to one target Elf. Untap it. It and a random Elf creature card in your hand each perpetually gets +1/+1.\n[-1]: Seek an Elf card.\n[-6]: Conjure a Regal Force card onto the battlefield.

View File

@@ -2,7 +2,7 @@ Name:Gate of the Black Dragon
ManaCost:no cost
Types:Land Swamp Gate
K:CARDNAME enters the battlefield tapped.
A:AB$ ChangeZone | Cost$ 3 B T | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ SpellDescription | SpellDescription$ Seek a nonland card. Activate only once.
A:AB$ Seek | Cost$ 3 B T | Type$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ {p:You} seeks a nonland card. | SpellDescription$ Seek a nonland card. Activate only once.
Text:{T}: Add {B}.
DeckHints:Type$Gate
Oracle:Gate of the Black Dragon enters the battlefield tapped.\n{3}{B}, {T}: Seek a nonland card. Activate only once.\n{T}: Add {B}.

View File

@@ -2,7 +2,7 @@ Name:Gate to Manorborn
ManaCost:no cost
Types:Land Forest Gate
K:CARDNAME enters the battlefield tapped.
A:AB$ ChangeZone | Cost$ 3 G T | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ SpellDescription | SpellDescription$ Seek a nonland card. Activate only once.
A:AB$ Seek | Cost$ 3 G T | Type$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ {p:You} seeks a nonland card. | SpellDescription$ Seek a nonland card. Activate only once.
Text:{T}: Add {G}.
DeckHints:Type$Gate
Oracle:Gate to Manorborn enters the battlefield tapped.\n{3}{G}, {T}: Seek a nonland card. Activate only once.\n{T}: Add {G}.

View File

@@ -2,7 +2,7 @@ Name:Gate to Seatower
ManaCost:no cost
Types:Land Island Gate
K:CARDNAME enters the battlefield tapped.
A:AB$ ChangeZone | Cost$ 3 U T | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ SpellDescription | SpellDescription$ Seek a nonland card. Activate only once.
A:AB$ Seek | Cost$ 3 U T | Type$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ {p:You} seeks a nonland card. | SpellDescription$ Seek a nonland card. Activate only once.
Text:{T}: Add {U}.
DeckHints:Type$Gate
Oracle:Gate to Seatower enters the battlefield tapped.\n{3}{U}, {T}: Seek a nonland card. Activate only once.\n{T}: Add {U}.

View File

@@ -2,7 +2,7 @@ Name:Gate to the Citadel
ManaCost:no cost
Types:Land Plains Gate
K:CARDNAME enters the battlefield tapped.
A:AB$ ChangeZone | Cost$ 3 W T | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ SpellDescription | SpellDescription$ Seek a nonland card. Activate only once.
A:AB$ Seek | Cost$ 3 W T | Type$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ {p:You} seeks a nonland card. | SpellDescription$ Seek a nonland card. Activate only once.
Text:{T}: Add {W}.
DeckHints:Type$Gate
Oracle:Gate to the Citadel enters the battlefield tapped.\n{3}{W}, {T}: Seek a nonland card. Activate only once.\n{T}: Add {W}.

View File

@@ -2,7 +2,7 @@ Name:Gate to Tumbledown
ManaCost:no cost
Types:Land Mountain Gate
K:CARDNAME enters the battlefield tapped.
A:AB$ ChangeZone | Cost$ 3 R T | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ SpellDescription | SpellDescription$ Seek a nonland card. Activate only once.
A:AB$ Seek | Cost$ 3 R T | Type$ Card.nonLand | GameActivationLimit$ 1 | StackDescription$ {p:You} seeks a nonland card. | SpellDescription$ Seek a nonland card. Activate only once.
Text:{T}: Add {R}.
DeckHints:Type$Gate
Oracle:Gate to Tumbledown enters the battlefield tapped.\n{3}{R}, {T}: Seek a nonland card. Activate only once.\n{T}: Add {R}.

View File

@@ -4,7 +4,7 @@ Types:Creature Wolf
PT:5/4
K:Trample
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ When CARDNAME dies, seek a permanent card with mana value equal to the number of lands you control.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.Permanent+cmcEQX | ChangeNum$ 1
SVar:TrigSeek:DB$ Seek | Type$ Card.Permanent+cmcEQX
SVar:X:Count$Valid Land.YouCtrl
SVar:NeedsToPlayVar:X GE4
SVar:BuffedBy:Land

View File

@@ -4,8 +4,9 @@ Types:Creature Human Wizard
PT:2/2
K:Prowess
T:Mode$ Phase | Phase$ Main1 | PreCombatMain$ True | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigSeek | TriggerDescription$ At the beginning of your precombat main phase, you may discard a card. If you do, seek a card with greater mana value and exile it. Until the end of your next turn, you may play the exiled card.
SVar:TrigSeek:AB$ ChangeZone | Cost$ Discard<1/Card> | Origin$ Library | Destination$ Exile | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.cmcGTX | ChangeNum$ 1 | RememberChanged$ True | SubAbility$ DBEffect
SVar:TrigSeek:AB$ Seek | Cost$ Discard<1/Card> | Type$ Card.cmcGTX | RememberFound$ True | SubAbility$ DBExile
SVar:X:Discarded$CardManaCost
SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | Defined$ Remembered | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | RememberObjects$ Remembered | StaticAbilities$ MayPlay | Duration$ UntilTheEndOfYourNextTurn | ForgetOnMoved$ Exile | SubAbility$ DBCleanup
SVar:MayPlay:Mode$ Continuous | Affected$ Card.IsRemembered | MayPlay$ True | EffectZone$ Command | AffectedZone$ Exile | Description$ Until the end of your next turn, you may play the exiled card.
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -4,11 +4,13 @@ Types:Legendary Creature Frog Horror
PT:6/6
K:Menace
T:Mode$ Phase | Phase$ BeginCombat | TriggerZones$ Battlefield | Execute$ TrigSac | IsPresent$ Card.Self+untapped | TriggerDescription$ At the beginning of each combat, if CARDNAME is untapped, any opponent may sacrifice a nontoken creature. If they do, tap CARDNAME, then seek a land card and put it onto the battlefield tapped.
SVar:TrigSac:DB$ Sacrifice | Defined$ Opponent | Amount$ 1 | SacValid$ Creature.nonToken | RememberSacrificed$ True | Optional$ True | AILogic$ DesecrationDemon | SubAbility$ DBTap
SVar:DBTap:DB$ Tap | Defined$ Self | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBSeek
SVar:DBSeek:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Land | ChangeNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:TrigSac:DB$ Sacrifice | Defined$ Opponent | Amount$ 1 | SacValid$ Creature.nonToken | RememberSacrificed$ True | Optional$ True | AILogic$ DesecrationDemon | SubAbility$ DBBranch
SVar:DBBranch:DB$ Branch | BranchConditionSVar$ X | TrueSubAbility$ DBTap
SVar:X:Remembered$Amount
SVar:DBTap:DB$ Tap | SubAbility$ DBSeek
SVar:DBSeek:DB$ Seek | Type$ Card.Land | ImprintFound$ True | SubAbility$ DBPut
SVar:DBPut:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | Defined$ Imprinted | Tapped$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Land.YouCtrl | TriggerZones$ Battlefield | Execute$ TrigEffect | TriggerDescription$ Whenever a land enters the battlefield under your control, it perpetually gains "{B}{G}, {T}, Sacrifice this land: Draw a card."
SVar:TrigEffect:DB$ Effect | RememberObjects$ TriggeredCard | StaticAbilities$ PerpetualStatic | Name$ Gitrog, Horror of Zhava's Perpetual Effect | Duration$ Permanent | SubAbility$ DBCleanup
SVar:PerpetualStatic:Mode$ Continuous | Affected$ Card.IsRemembered | AddAbility$ PerpetualSacDraw | EffectZone$ All | Description$ This land card perpetually gains "{B}{G}, {T}, Sacrifice this land: Draw a card."

View File

@@ -3,7 +3,7 @@ ManaCost:R
Types:Creature Goblin
PT:1/1
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ DBSeek | TriggerDescription$ When CARDNAME dies, seek a creature card with mana value 3 or less. That card perpetually gains haste, "This spell costs {1} less to cast," and "At the beginning of your end step, sacrifice this creature."
SVar:DBSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Creature.cmcLE3+YouOwn | RememberChanged$ True | SubAbility$ DBEffect
SVar:DBSeek:DB$ Seek | Type$ Creature.cmcLE3+YouOwn | RememberFound$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | StaticAbilities$ PerpetualAbility | RememberObjects$ Remembered | Triggers$ Update | Name$ Goblin Trapfinder's Perpetual Effect | Duration$ Permanent
SVar:PerpetualAbility:Mode$ Continuous | Affected$ Card.IsRemembered | AddTrigger$ TrigSac | AddStaticAbility$ PerpetualReduceCost | AddKeyword$ Haste | EffectZone$ Command | AffectedZone$ Battlefield,Hand,Graveyard,Exile,Stack,Library,Command | Description$ This card perpetually gains haste, "This spell costs {1} less to cast," and "At the beginning of your end step, sacrifice this creature."
SVar:PerpetualReduceCost:Mode$ ReduceCost | ValidCard$ Card.Self | Type$ Spell | Amount$ 1 | EffectZone$ All | Description$ This spell costs {1} less to cast.

View File

@@ -3,6 +3,7 @@ ManaCost:3 G G
Types:Creature Elemental
PT:6/6
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, seek a land card.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Land | ChangeNum$ 1
SVar:TrigSeek:DB$ Seek | Type$ Card.Land
A:AB$ MakeCard | Cost$ Discard<1/Land/land> | Conjure$ True | Name$ Hollowhenge Beast | Zone$ Hand | AdditionalActivationZone$ Graveyard | StackDescription$ Conjure a card named Hollowhenge Beast into your hand. | SpellDescription$ Conjure a card named Hollowhenge Beast into your hand. You may also activate this ability while CARDNAME is in your graveyard.
DeckHas:Ability$Discard
Oracle:When Hollowhenge Wrangler enters the battlefield, seek a land card.\nDiscard a land card: Conjure a card named Hollowhenge Beast into your hand. You may also activate this ability while Hollowhenge Wrangler is in your graveyard.

View File

@@ -3,13 +3,11 @@ ManaCost:3 W
Types:Creature Human Cleric
PT:3/3
K:Vigilance
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.wasCast+Self | CheckSVar$ Z | SVarCompare$ GE20 | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, if you cast it and there are twenty or more creature cards with mana value 3 or less among cards in your graveyard, hand, and library, seek two creature cards with mana value 3 or less. Put one of them onto the battlefield and shuffle the other into your library.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Library | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Creature.YouOwn+cmcLE3 | ChangeNum$ 2 | RememberChanged$ True | SubAbility$ DBChangeZone1
SVar:DBChangeZone1:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.IsRemembered | ChangeNum$ 1 | Mandatory$ True | NoLooking$ True | SelectPrompt$ Select a card for the battlefield | Shuffle$ True | SubAbility$ DBChangeZone2 | StackDescription$ None
SVar:DBChangeZone2:DB$ ChangeZone | Origin$ Library | Destination$ Library | ChangeType$ Creature.IsRemembered | Mandatory$ True | NoLooking$ True | StackDescription$ None | SubAbility$ DBCleanup
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.wasCast+Self | CheckSVar$ X | SVarCompare$ GE20 | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, if you cast it and there are twenty or more creature cards with mana value 3 or less among cards in your graveyard, hand, and library, seek two creature cards with mana value 3 or less. Put one of them onto the battlefield and shuffle the other into your library.
SVar:TrigSeek:DB$ Seek | Type$ Creature.YouOwn+cmcLE3 | Num$ 2 | RememberFound$ True | SubAbility$ DBChangeZone1
SVar:DBChangeZone1:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature.IsRemembered | ChangeNum$ 1 | Mandatory$ True | SelectPrompt$ Select a card for the battlefield | SubAbility$ DBChangeZone2
SVar:DBChangeZone2:DB$ ChangeZone | Origin$ Hand | Destination$ Library | ChangeType$ Creature.IsRemembered | Mandatory$ True | Shuffle$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
SVar:X:Count$ValidGraveyard Creature.YouOwn+cmcLE3
SVar:Y:Count$ValidLibrary Creature.YouOwn+cmcLE3/Plus.X
SVar:Z:Count$ValidHand Creature.YouOwn+cmcLE3/Plus.Y
SVar:X:Count$ValidGraveyard,Library,Hand Creature.YouOwn+cmcLE3
DeckNeeds:Type$Creature
Oracle:Vigilance\nWhen Inquisitor Captain enters the battlefield, if you cast it and there are twenty or more creature cards with mana value 3 or less among cards in your graveyard, hand, and library, seek two creature cards with mana value 3 or less. Put one of them onto the battlefield and shuffle the other into your library.

View File

@@ -5,6 +5,6 @@ PT:3/3
K:Ninjutsu:1 G
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigChoose | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, choose land or nonland. Seek a permanent card of the chosen type.
SVar:TrigChoose:DB$ ChooseType | Type$ Card | ValidTypes$ Land,Nonland | SubAbility$ DBSeek
SVar:DBSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.ChosenType | ChangeNum$ 1 | SubAbility$ DBCleanup
SVar:DBSeek:DB$ Seek | Type$ Card.Permanent+ChosenType | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearChosenType$ True
Oracle:Ninjutsu {1}{G}\nWhenever Jukai Liberator deals combat damage to a player, choose land or nonland. Seek a permanent card of the chosen type.

View File

@@ -5,7 +5,9 @@ PT:4/3
K:Haste
T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigCounter | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME attacks, put a flame counter on it, then seek a card with mana value equal to the number of flame counters on it and exile that card face down.
SVar:TrigCounter:DB$ PutCounter | CounterType$ FLAME | CounterNum$ 1 | ValidCards$ Self | SubAbility$ DBSeek
SVar:DBSeek:DB$ ChangeZone | Origin$ Library | Destination$ Exile | ExileFaceDown$ True | AtRandom$ True | NoShuffle$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Card.cmcEQX+YouOwn | RememberChanged$ True
SVar:DBSeek:DB$ Seek | Type$ Card.cmcEQX+YouOwn | ImprintFound$ True | SubAbility$ DBExile
SVar:DBExile:DB$ ChangeZone | Origin$ Hand | Destination$ Exile | Defined$ Imprinted | ExileFaceDown$ True | Mandatory$ True | SubAbility$ DBClearImprinted
SVar:DBClearImprinted:DB$ Cleanup | ClearImprinted$ True
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigReturn | TriggerDescription$ When NICKNAME dies, put the cards exiled with it into their owner's hand. At the beginning of the end step of your next turn, discard those cards.
SVar:TrigReturn:DB$ ChangeZoneAll | ChangeType$ Card.IsRemembered+ExiledWithSource | Origin$ Exile | Destination$ Hand | SubAbility$ DBDelay
SVar:DBDelay:DB$ DelayedTrigger | DelayedTriggerDefinedPlayer$ You | Mode$ Phase | Phase$ End of Turn | Execute$ TrigDiscardExiled | RememberObjects$ Remembered | TriggerDescription$ At the beginning of the end step of your next turn, discard those cards.
@@ -16,4 +18,5 @@ T:Mode$ ChangesZone | Origin$ Exile | Destination$ Any | Static$ True | ValidCar
SVar:DBForget:DB$ Pump | ForgetObjects$ TriggeredCard
SVar:X:Count$CardCounters.FLAME
SVar:HasAttackEffect:TRUE
DeckHas:Ability$Discard
Oracle:Haste\nWhenever Kardum, Patron of Flames attacks, put a flame counter on it, then seek a card with mana value equal to the number of flame counters on it and exile that card face down.\nWhen Kardum dies, put all cards you own exiled with it into your hand. At the beginning of the end step of your next turn, discard those cards.

View File

@@ -39,7 +39,7 @@ SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield |
SVar:DBEffect:DB$ Effect | RememberObjects$ TriggeredCard | StaticAbilities$ PerpetualStatic | Duration$ Permanent
SVar:PerpetualStatic:Mode$ Continuous | Affected$ Card.IsRemembered | AddKeyword$ CARDNAME can't block. | EffectZone$ Command | AffectedZone$ Battlefield,Hand,Graveyard,Exile,Stack,Library,Command | Description$ It perpetually gains "This creature can't block."
T:Mode$ Specializes | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ When this card specializes from any zone, seek an instant or sorcery card with mana value 3 or less. Until end of turn, you may cast that card without paying its mana cost.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 1 | ChangeType$ Instant.cmcLE3,Sorcery.cmcLE3 | RememberChanged$ True
SVar:TrigSeek:DB$ Seek | Type$ Instant.cmcLE3,Sorcery.cmcLE3 | RememberFound$ True | SubAbility$ DBEffect
SVar:DBEffect:DB$ Effect | StaticAbilities$ MayPlay | RememberObjects$ Remembered | ForgetOnMoved$ Hand | SubAbility$ DBCleanup
SVar:MayPlay:Mode$ Continuous | Affected$ Card.IsRemembered+nonLand | MayPlayWithoutManaCost$ True | EffectZone$ Command | AffectedZone$ Hand
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True

View File

@@ -2,6 +2,6 @@ Name:Kindred Denial
ManaCost:2 U U
Types:Instant
A:SP$ Counter | TargetType$ Spell | ValidTgts$ Card | TgtPrompt$ Select target spell | RememberCounteredCMC$ True | SubAbility$ DBSeek | SpellDescription$ Counter target spell.
SVar:DBSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.cmcEQX | ChangeNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Seek a card with the same mana value as that spell.
SVar:DBSeek:DB$ Seek | Type$ Card.cmcEQX | StackDescription$ REP Seek_{p:You} seeks | SpellDescription$ Seek a card with the same mana value as that spell.
SVar:X:Count$RememberedNumber
Oracle:Counter target spell. Seek a card with the same mana value as that spell.

View File

@@ -36,10 +36,10 @@ Types:Legendary Creature Tiefling Cleric
PT:3/4
K:Vigilance
T:Mode$ Specializes | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ When this creature specializes, seek three nonland permanent cards. Choose one of those cards and shuffle the rest into your library.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeNum$ 3 | ChangeType$ Permanent.nonLand | RememberChanged$ True | SubAbility$ DBChooseCard
SVar:TrigSeek:DB$ Seek | Num$ 3 | ChangeType$ Card.Permanent+nonLand | RememberFound$ True | SubAbility$ DBChooseCard
SVar:DBChooseCard:DB$ ChooseCard | ChoiceZone$ Hand | Choices$ Card.IsRemembered | ForgetChosen$ True | SubAbility$ DBShuffle
SVar:DBShuffle:DB$ ChangeZone | Origin$ Hand | Destination$ Library | Defined$ Remembered | Shuffle$ True | SubAbility$ DBClearChosen
SVar:DBClearChosen:DB$ Cleanup | ClearChosenCard$ True
SVar:DBShuffle:DB$ ChangeZone | Origin$ Hand | Destination$ Library | Defined$ Remembered | Shuffle$ True | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True
Oracle:Vigilance\nWhen this creature specializes, seek three nonland permanent cards. Choose one of those cards and shuffle the rest into your library.
SPECIALIZE:BLACK

View File

@@ -22,7 +22,7 @@ PT:3/6
K:Double Strike
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSeek | TriggerDescription$ When this creature enters the battlefield or specializes, seek a nonland permanent card with mana value 3 or less.
T:Mode$ Specializes | ValidCard$ Card.Self | Execute$ TrigSeek | Secondary$ True | TriggerDescription$ When this creature enters the battlefield or specializes, seek a nonland permanent card with mana value 3 or less.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Permanent.nonLand+cmcLE3
SVar:TrigSeek:DB$ Seek | Type$ Card.Permanent+nonLand+cmcLE3
Oracle:Double strike\nWhen this creature enters the battlefield or specializes, seek a nonland permanent card with mana value 3 or less.
SPECIALIZE:BLUE

View File

@@ -4,7 +4,7 @@ Types:Legendary Creature Human Druid
PT:2/2
K:Specialize:3:Wild Shape:Activate only if you control six or more lands.:IsPresent$ Land.YouCtrl | PresentCompare$ GE6
T:Mode$ ChangesZone | ValidCard$ Card.wasCastByYou+Self | Destination$ Battlefield | Execute$ TrigSeek | TriggerDescription$ When CARDNAME enters the battlefield, if you cast it, seek a land card with a basic land type.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Land.hasABasicLandType | ChangeNum$ 1
SVar:TrigSeek:DB$ Seek | Type$ Land.hasABasicLandType
AlternateMode:Specialize
Oracle:Wild Shape — Specialize {3}. Activate only if you control six or more lands.\nWhen Lukamina, Moon Druid enters the battlefield, if you cast it, seek a land card with a basic land type.

View File

@@ -1,7 +1,7 @@
Name:Norn's Disassembly
ManaCost:W
Types:Enchantment
A:AB$ ChangeZone | Cost$ 1 W Sac<1/Permanent.Historic/Historic permanent> | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.Historic | ChangeNum$ 1 | SpellDescription$ Seek a historic card.
A:AB$ Seek | Cost$ 1 W Sac<1/Permanent.Historic/historic permanent> | Type$ Card.Historic | StackDescription$ REP Seek_{p:You} seeks | SpellDescription$ Seek a historic card.
DeckHas:Ability$Sacrifice
DeckNeeds:Type$Legendary|Artifact|Planeswalker
Oracle:{1}{W}, Sacrifice a historic permanent: Seek a historic card.

View File

@@ -5,6 +5,6 @@ PT:4/4
K:Flying
K:Ward:2
T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | Execute$ TrigSeek | CombatDamage$ True | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, seek a card with mana value equal to the number of cards in your hand.
SVar:TrigSeek:DB$ ChangeZone | Origin$ Library | Destination$ Hand | AtRandom$ True | NoShuffle$ True | Mandatory$ True | NoLooking$ True | NoReveal$ True | ChangeType$ Card.cmcEQX | ChangeNum$ 1
SVar:TrigSeek:DB$ Seek | Type$ Card.cmcEQX
SVar:X:Count$InYourHand
Oracle:Flying\nWard {2}\nWhenever Obsessive Collector deals combat damage to a player, seek a card with mana value equal to the number of cards in your hand.

Some files were not shown because too many files have changed in this diff Show More