diff --git a/forge-core/src/main/java/forge/card/DeckHints.java b/forge-core/src/main/java/forge/card/DeckHints.java index e6ce28f9fea..6af5a15dfad 100644 --- a/forge-core/src/main/java/forge/card/DeckHints.java +++ b/forge-core/src/main/java/forge/card/DeckHints.java @@ -201,7 +201,11 @@ public class DeckHints { Predicate op; if (t.contains(".")) { String[] typeParts = t.split("\\."); - op = Predicates.and(CardRulesPredicates.coreType(true, typeParts[0]), CardRulesPredicates.subType(typeParts[1])); + if (CardType.isASupertype(typeParts[0])) { + op = Predicates.and(CardRulesPredicates.superType(true, typeParts[0]), CardRulesPredicates.coreType(true, typeParts[1])); + } else { + op = Predicates.and(CardRulesPredicates.coreType(true, typeParts[0]), CardRulesPredicates.subType(typeParts[1])); + } } else { op = CardRulesPredicates.joinedType(StringOp.CONTAINS_IC, t); } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index caf567a5066..7d80f295144 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -566,7 +566,10 @@ public class AbilityUtils { } else if (calcX[0].equals("LastStateBattlefield") && ability instanceof SpellAbility) { Card c = ((SpellAbility) ability).getLastStateBattlefield().get(card); val = c == null ? 0 : xCount(c, calcX[1], ability); - } else if (calcX[0].startsWith("Remembered")) { + } else if (calcX[0].startsWith("ExiledWith")) { + val = handlePaid(card.getExiledCards(), calcX[1], card, ability); + } + else if (calcX[0].startsWith("Remembered")) { // Add whole Remembered list to handlePaid final CardCollection list = new CardCollection(); Card newCard = card; diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java index 263df8d86f3..c40b0273535 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java @@ -7,7 +7,6 @@ import com.google.common.collect.Sets; import forge.game.player.DelayedReveal; import forge.game.player.PlayerView; import forge.util.CardTranslation; -import org.apache.commons.lang3.StringUtils; import forge.card.CardType; import forge.game.Game; @@ -86,9 +85,7 @@ public class ChooseCardEffect extends SpellAbilityEffect { final String amountValue = sa.getParamOrDefault("Amount", "1"); int validAmount; - if (StringUtils.isNumeric(amountValue)) { - validAmount = Integer.parseInt(amountValue); - } else if (amountValue.equals("Random")) { + if (amountValue.equals("Random")) { validAmount = Aggregates.randomInt(0, choices.size()); } else { validAmount = AbilityUtils.calculateAmount(host, amountValue, sa); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java index 5f3ea984730..27668d41797 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java @@ -2,8 +2,6 @@ package forge.game.ability.effects; import java.util.List; -import org.apache.commons.lang3.StringUtils; - import forge.game.Game; import forge.game.ability.AbilityUtils; import forge.game.ability.SpellAbilityEffect; @@ -125,8 +123,7 @@ public class ChooseSourceEffect extends SpellAbilityEffect { return; } - final String numericAmount = sa.getParamOrDefault("Amount", "1"); - final int validAmount = StringUtils.isNumeric(numericAmount) ? Integer.parseInt(numericAmount) : AbilityUtils.calculateAmount(host, numericAmount, sa); + final int validAmount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Amount", "1"), sa); for (final Player p : tgtPlayers) { if (!p.isInGame()) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java index f0c5f9cb494..ad1085d9abc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java @@ -18,7 +18,6 @@ import forge.game.zone.ZoneType; import forge.util.Lang; import forge.util.Localizer; import forge.util.TextUtil; -import org.apache.commons.lang3.StringUtils; import java.util.Map; @@ -63,11 +62,8 @@ public class SetStateEffect extends SpellAbilityEffect { CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield); choices = CardLists.getValidCards(choices, sa.getParam("Choices"), p, host, sa); - final String numericAmount = sa.getParamOrDefault("Amount", "1"); - final int validAmount = StringUtils.isNumeric(numericAmount) ? Integer.parseInt(numericAmount) : - AbilityUtils.calculateAmount(host, numericAmount, sa); - final int minAmount = sa.hasParam("MinAmount") ? Integer.parseInt(sa.getParam("MinAmount")) : - validAmount; + final int validAmount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Amount", "1"), sa); + final int minAmount = sa.hasParam("MinAmount") ? Integer.parseInt(sa.getParam("MinAmount")) : validAmount; if (validAmount <= 0) { return; diff --git a/forge-game/src/main/java/forge/game/cost/CostExile.java b/forge-game/src/main/java/forge/game/cost/CostExile.java index cb1347ac665..d1ae6869344 100644 --- a/forge-game/src/main/java/forge/game/cost/CostExile.java +++ b/forge-game/src/main/java/forge/game/cost/CostExile.java @@ -123,11 +123,6 @@ public class CostExile extends CostPartWithList { return String.format ("Exile any number of %s from your %s", desc, origin); } - //for very specific situations like Timothar - if (desc.startsWith("the")) { - return String.format("Exile %s from your %s", desc, origin); - } - return String.format("Exile %s from your %s", Cost.convertAmountTypeToWords(i, this.getAmount(), desc), origin); } diff --git a/forge-gui-mobile/pom.xml b/forge-gui-mobile/pom.xml index 59fb125d1d2..4e506c14ca6 100644 --- a/forge-gui-mobile/pom.xml +++ b/forge-gui-mobile/pom.xml @@ -57,7 +57,7 @@ com.github.tommyettinger textratypist - 0.8.1 + 0.8.2 com.badlogicgames.gdx-controllers diff --git a/forge-gui-mobile/src/forge/Graphics.java b/forge-gui-mobile/src/forge/Graphics.java index 80df838870a..11b8edf4c13 100644 --- a/forge-gui-mobile/src/forge/Graphics.java +++ b/forge-gui-mobile/src/forge/Graphics.java @@ -81,6 +81,10 @@ public class Graphics { return shaderUnderwater; } + + public ShaderProgram getShaderNightDay() { + return shaderNightDay; + } public void begin(float regionWidth0, float regionHeight0) { batch.begin(); bounds = new Rectangle(0, 0, regionWidth0, regionHeight0); diff --git a/forge-gui-mobile/src/forge/adventure/character/MapActor.java b/forge-gui-mobile/src/forge/adventure/character/MapActor.java index c8fe9219ac0..f6354045dd5 100644 --- a/forge-gui-mobile/src/forge/adventure/character/MapActor.java +++ b/forge-gui-mobile/src/forge/adventure/character/MapActor.java @@ -223,4 +223,12 @@ public class MapActor extends Actor { public float getCollisionHeight() { return collisionHeight; } + + public void resetCollisionHeight() { + collisionHeight = 0.4f; + } + + public void clearCollisionHeight() { + collisionHeight = 0f; + } } diff --git a/forge-gui-mobile/src/forge/adventure/data/SettingData.java b/forge-gui-mobile/src/forge/adventure/data/SettingData.java index f656a46d107..048e608b8a6 100644 --- a/forge-gui-mobile/src/forge/adventure/data/SettingData.java +++ b/forge-gui-mobile/src/forge/adventure/data/SettingData.java @@ -18,4 +18,5 @@ public class SettingData { public Float cardTooltipAdj; public Float rewardCardAdjLandscape; public Float cardTooltipAdjLandscape; + public boolean dayNightBG; } diff --git a/forge-gui-mobile/src/forge/adventure/pointofintrest/PointOfInterestChanges.java b/forge-gui-mobile/src/forge/adventure/pointofintrest/PointOfInterestChanges.java index 61c79b43f9a..715930e707b 100644 --- a/forge-gui-mobile/src/forge/adventure/pointofintrest/PointOfInterestChanges.java +++ b/forge-gui-mobile/src/forge/adventure/pointofintrest/PointOfInterestChanges.java @@ -137,5 +137,8 @@ public class PointOfInterestChanges implements SaveFileContent { public void setTownModifier(float mod){ shopModifiers.put(0, mod); } + public boolean hasDeletedObjects() { + return deletedObjects != null && !deletedObjects.isEmpty(); + } } diff --git a/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java b/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java index e4e30c64f9f..5bfdcc8119e 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/PlayerStatisticScene.java @@ -28,6 +28,7 @@ import forge.card.ColorSet; import forge.game.GameType; import forge.localinstance.achievements.Achievement; import forge.localinstance.achievements.AchievementCollection; +import forge.localinstance.achievements.PlaneswalkerAchievements; import forge.model.FModel; import forge.player.GamePlayerUtil; import org.apache.commons.lang3.tuple.Pair; @@ -35,9 +36,9 @@ import org.apache.commons.lang3.tuple.Pair; import java.util.Map; public class PlayerStatisticScene extends UIScene { - Image avatar, avatarBorder, lifeIcon, goldIcon; + Image avatar, avatarBorder; Image colorFrame; - TextraLabel money, life; + TextraLabel money, life, shards; TextraLabel wins, totalWins; TextraLabel loss, totalLoss; TextraLabel winloss, lossWinRatio; @@ -48,7 +49,6 @@ public class PlayerStatisticScene extends UIScene { ScrollPane scroller; Table root; boolean toggle = false; - AchievementCollection achievements; private PlayerStatisticScene() { super(Forge.isLandscapeMode() ? "ui/statistic.json" : "ui/statistic_portrait.json"); @@ -65,8 +65,7 @@ public class PlayerStatisticScene extends UIScene { playerName = ui.findActor("playerName"); life = ui.findActor("lifePoints"); money = ui.findActor("money"); - lifeIcon = ui.findActor("lifeIcon"); - goldIcon = ui.findActor("goldIcon"); + shards = ui.findActor("shards"); wins = ui.findActor("wins"); colorFrame = ui.findActor("colorFrame"); totalWins = ui.findActor("totalWins"); @@ -154,27 +153,8 @@ public class PlayerStatisticScene extends UIScene { @Override public void enter() { super.enter(); - achievements = FModel.getAchievements(GameType.Constructed); - achievementContainer.clear(); - for (Achievement a : achievements) { - GameType g = GameType.smartValueOf(a.getKey()); - if (g != null) //skip variants - continue; - TextureRegion textureRegion = new TextureRegion(((FBufferedImage) a.getImage()).getTexture()); - textureRegion.flip(true, true); - Image image = new Image(textureRegion); - float alpha = a.isActive() ? 1f : 0.25f; - image.getColor().a = alpha; - achievementContainer.add(image).height(50).width(40).center().pad(5); - String value = "[%105]" + a.getDisplayName() + "[%98]"; - String subTitle = a.getSubTitle(true); - if (subTitle != null) - value += "\n" + subTitle; - TextraLabel label = Controls.newTextraLabel(value); - label.getColor().a = alpha; - achievementContainer.add(label).left().pad(5); - achievementContainer.row(); - } + updateAchievements(PlaneswalkerAchievements.instance, true); + updateAchievements(FModel.getAchievements(GameType.Constructed), false); scrollContainer.clear(); if (playerName != null) { @@ -184,10 +164,13 @@ public class PlayerStatisticScene extends UIScene { avatar.setDrawable(new TextureRegionDrawable(Current.player().avatar())); } if (life != null) { - AdventurePlayer.current().onLifeChange(() -> life.setText(AdventurePlayer.current().getLife() + "/" + AdventurePlayer.current().getMaxLife())); + AdventurePlayer.current().onLifeChange(() -> life.setText("[+Life] [BLACK]" + AdventurePlayer.current().getLife() + "/" + AdventurePlayer.current().getMaxLife())); } if (money != null) { - WorldSave.getCurrentSave().getPlayer().onGoldChange(() -> money.setText(String.valueOf(AdventurePlayer.current().getGold()))); + WorldSave.getCurrentSave().getPlayer().onGoldChange(() -> money.setText("[+Gold] [BLACK]" + AdventurePlayer.current().getGold())); + } + if (shards != null) { + WorldSave.getCurrentSave().getPlayer().onShardsChange(() -> shards.setText("[+Shards] [BLACK]" + AdventurePlayer.current().getShards())); } if (totalWins != null) { totalWins.setText(String.valueOf(Current.player().getStatistic().totalWins())); @@ -222,4 +205,31 @@ public class PlayerStatisticScene extends UIScene { } performTouch(scrollPaneOfActor(scrollContainer)); //can use mouse wheel if available to scroll } + void updateAchievements(AchievementCollection achievementCollection, boolean isPW) { + achievementContainer.clear(); + for (Achievement a : achievementCollection) { + if (isPW) { + if (!a.isActive()) //skip inactive + continue; + } else { + GameType g = GameType.smartValueOf(a.getKey()); + if (g != null) //skip variants + continue; + } + TextureRegion textureRegion = new TextureRegion(((FBufferedImage) a.getImage()).getTexture()); + textureRegion.flip(true, true); + Image image = new Image(textureRegion); + float alpha = a.isActive() ? 1f : 0.25f; + image.getColor().a = alpha; + achievementContainer.add(image).height(50).width(40).center().pad(5); + String value = "[%105]" + a.getDisplayName() + "[%98]"; + String subTitle = a.getSubTitle(true); + if (subTitle != null) + value += "\n" + subTitle; + TextraLabel label = Controls.newTextraLabel(value); + label.getColor().a = alpha; + achievementContainer.add(label).left().pad(5); + achievementContainer.row(); + } + } } diff --git a/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java b/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java index f9141425840..7be5f2f6895 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/SettingsScene.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.LocalTime; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; @@ -38,43 +39,42 @@ public class SettingsScene extends UIScene { SelectBox selectSourcePlane; TextField newPlaneName; + private void copyNewPlane() { - String plane=(String) selectSourcePlane.getSelected(); - Path source= Paths.get(Config.instance().getPlanePath(plane)); - Path destination= Paths.get(Config.instance().getPlanePath(""+newPlaneName.getText())); - AtomicBoolean somethingWentWrong= new AtomicBoolean(false); - try (Stream stream = Files.walk(source)) - { + String plane = (String) selectSourcePlane.getSelected(); + Path source = Paths.get(Config.instance().getPlanePath(plane)); + Path destination = Paths.get(Config.instance().getPlanePath("" + newPlaneName.getText())); + AtomicBoolean somethingWentWrong = new AtomicBoolean(false); + try (Stream stream = Files.walk(source)) { Files.createDirectories(destination); stream.forEach(s -> { - try { Files.copy(s, destination.resolve(source.relativize(s)), REPLACE_EXISTING); } - catch (IOException e) { + try { + Files.copy(s, destination.resolve(source.relativize(s)), REPLACE_EXISTING); + } catch (IOException e) { somethingWentWrong.set(true); } }); } catch (IOException e) { somethingWentWrong.set(true); } - if(somethingWentWrong.get()) - { - Dialog dialog=prepareDialog("Something went wrong", ButtonOk|ButtonAbort,null); + if (somethingWentWrong.get()) { + Dialog dialog = prepareDialog("Something went wrong", ButtonOk | ButtonAbort, null); dialog.text("Copy was not successful check your access right\n and if the folder is in use"); showDialog(dialog); - } - else - { - Dialog dialog=prepareDialog("Copied plane", ButtonOk|ButtonAbort,null); - dialog.text("New plane "+newPlaneName.getText()+" was created\nYou can now start the editor to change the plane\n" + + } else { + Dialog dialog = prepareDialog("Copied plane", ButtonOk | ButtonAbort, null); + dialog.text("New plane " + newPlaneName.getText() + " was created\nYou can now start the editor to change the plane\n" + "or edit it manually from the folder\n" + - Config.instance().getPlanePath(""+newPlaneName.getText())); - Config.instance().getSettingData().plane = ""+newPlaneName.getText(); + Config.instance().getPlanePath("" + newPlaneName.getText())); + Config.instance().getSettingData().plane = "" + newPlaneName.getText(); Config.instance().saveSettings(); showDialog(dialog); } } + private void createNewPlane() { - Dialog dialog=prepareDialog("Create your own Plane", ButtonOk|ButtonAbort,()->copyNewPlane()); + Dialog dialog = prepareDialog("Create your own Plane", ButtonOk | ButtonAbort, () -> copyNewPlane()); dialog.text("Select a plane to copy"); dialog.getContentTable().row(); dialog.getContentTable().add(selectSourcePlane); @@ -82,7 +82,7 @@ public class SettingsScene extends UIScene { dialog.text("Set new plane name"); dialog.getContentTable().row(); dialog.getContentTable().add(newPlaneName); - newPlaneName.setText(selectSourcePlane.getSelected().toString()+"_copy"); + newPlaneName.setText(selectSourcePlane.getSelected().toString() + "_copy"); dialog.show(stage); } @@ -101,7 +101,7 @@ public class SettingsScene extends UIScene { Config.instance().saveSettings(); return null; }); - newPlane=Controls.newTextButton("Create own plane"); + newPlane = Controls.newTextButton("Create own plane"); newPlane.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { @@ -110,7 +110,7 @@ public class SettingsScene extends UIScene { }); addLabel(Forge.getLocalizer().getMessage("lblWorld")); settingGroup.add(plane).align(Align.right).pad(2); - addLabel(Forge.getLocalizer().getMessage("lblCreate")+Forge.getLocalizer().getMessage("lblWorld")); + addLabel(Forge.getLocalizer().getMessage("lblCreate") + Forge.getLocalizer().getMessage("lblWorld")); settingGroup.add(newPlane).align(Align.right).pad(2); if (!GuiBase.isAndroid()) { @@ -145,7 +145,7 @@ public class SettingsScene extends UIScene { } if (Forge.isLandscapeMode()) { //different adjustment to landscape - SelectBox rewardCardAdjLandscape = Controls.newComboBox(new Float[]{0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 1f, 1.05f, 1.1f, 1.15f, 1.2f, 1.25f, 1.3f, 1.35f, 1.4f, 1.45f, 1.5f, 1.55f, 1.6f}, Config.instance().getSettingData().rewardCardAdjLandscape, o -> { + SelectBox rewardCardAdjLandscape = Controls.newComboBox(new Float[]{0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1f, 1.05f, 1.1f, 1.15f, 1.2f, 1.25f, 1.3f, 1.35f, 1.4f, 1.45f, 1.5f, 1.55f, 1.6f}, Config.instance().getSettingData().rewardCardAdjLandscape, o -> { Float val = (Float) o; if (val == null || val == 0f) val = 1f; @@ -155,7 +155,7 @@ public class SettingsScene extends UIScene { }); addLabel("Reward/Shop Card Display Ratio"); settingGroup.add(rewardCardAdjLandscape).align(Align.right).pad(2); - SelectBox tooltipAdjLandscape = Controls.newComboBox(new Float[]{0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 1f, 1.05f, 1.1f, 1.15f, 1.2f, 1.25f, 1.3f, 1.35f, 1.4f, 1.45f, 1.5f, 1.55f, 1.6f}, Config.instance().getSettingData().cardTooltipAdjLandscape, o -> { + SelectBox tooltipAdjLandscape = Controls.newComboBox(new Float[]{0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1f, 1.05f, 1.1f, 1.15f, 1.2f, 1.25f, 1.3f, 1.35f, 1.4f, 1.45f, 1.5f, 1.55f, 1.6f}, Config.instance().getSettingData().cardTooltipAdjLandscape, o -> { Float val = (Float) o; if (val == null || val == 0f) val = 1f; @@ -195,6 +195,7 @@ public class SettingsScene extends UIScene { boolean value = ((CheckBox) actor).isChecked(); Config.instance().getSettingData().fullScreen = value; Config.instance().saveSettings(); + setTargetTime(LocalTime.now().getHour()); //update if (FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.UI_FULLSCREEN_MODE) != value) { FModel.getPreferences().setPref(ForgePreferences.FPref.UI_LANDSCAPE_MODE, value); @@ -203,6 +204,17 @@ public class SettingsScene extends UIScene { } }); } + addSettingField(Forge.getLocalizer().getMessage("lblDay") + " | " + Forge.getLocalizer().getMessage("lblNight") + " " + Forge.getLocalizer().getMessage("lblBackgroundImage"), Config.instance().getSettingData().dayNightBG, new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + boolean value = ((CheckBox) actor).isChecked(); + Config.instance().getSettingData().dayNightBG = value; + Config.instance().saveSettings(); + if (value) { + updateBG(true); + } + } + }); addCheckBox(Forge.getLocalizer().getMessage("lblCardName"), ForgePreferences.FPref.UI_OVERLAY_CARD_NAME); addSettingSlider(Forge.getLocalizer().getMessage("cbAdjustMusicVolume"), ForgePreferences.FPref.UI_VOL_MUSIC, 0, 100); addSettingSlider(Forge.getLocalizer().getMessage("cbAdjustSoundsVolume"), ForgePreferences.FPref.UI_VOL_SOUNDS, 0, 100); @@ -233,14 +245,12 @@ public class SettingsScene extends UIScene { backButton = ui.findActor("return"); ui.onButtonPress("return", SettingsScene.this::back); - ScrollPane scrollPane = ui.findActor("settings"); + scrollPane = ui.findActor("settings"); scrollPane.setActor(settingGroup); addToSelectable(settingGroup); } - - public boolean back() { Forge.switchToLast(); return true; @@ -318,13 +328,12 @@ public class SettingsScene extends UIScene { private static SettingsScene object; public static SettingsScene instance() { - if(object==null) - object=new SettingsScene(); + if (object == null) + object = new SettingsScene(); return object; } - @Override public void dispose() { if (stage != null) diff --git a/forge-gui-mobile/src/forge/adventure/scene/UIScene.java b/forge-gui-mobile/src/forge/adventure/scene/UIScene.java index 0e8fe6f184b..29c71ca69e2 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/UIScene.java +++ b/forge-gui-mobile/src/forge/adventure/scene/UIScene.java @@ -22,6 +22,8 @@ import forge.adventure.util.*; import forge.sound.SoundEffectType; import forge.sound.SoundSystem; +import java.time.LocalTime; + /** * Base class for an GUI scene where the elements are loaded from a json file */ @@ -268,6 +270,21 @@ public class UIScene extends Scene { } } + static float timeOfDay = 8.0f; + float targetTime = 8.0f; + + public static float getTimeOfDay() { + return timeOfDay; + } + + public void setTargetTime(float val) { + targetTime = val; + } + + public void setTimeOfDay(float val) { + timeOfDay = val; + } + @Override public void dispose() { if (stage != null) @@ -277,6 +294,15 @@ public class UIScene extends Scene { @Override public void act(float delta) { stage.act(delta); + if (timeOfDay < targetTime) { + timeOfDay += (delta * 1.5f); + if (timeOfDay > targetTime) + timeOfDay = targetTime; + } else if (timeOfDay > targetTime) { + timeOfDay -= (delta * 1.5f); + if (timeOfDay < targetTime) + timeOfDay = targetTime; + } } @Override @@ -306,7 +332,6 @@ public class UIScene extends Scene { } public boolean keyReleased(int keycode) { - ui.pressUp(keycode); if (!dialogShowing()) { Button pressedButton = ui.buttonPressed(keycode); @@ -537,7 +562,6 @@ public class UIScene extends Scene { public void selectNextUp() { - if (getSelected() == null) { selectFirst(); } else { @@ -573,7 +597,6 @@ public class UIScene extends Scene { } private void selectFirst() { - Selectable result = null; for (Selectable candidate : getPossibleSelection()) { if (result == null || candidate.getY() > result.getY()) { @@ -594,8 +617,6 @@ public class UIScene extends Scene { } public void selectActor(Selectable actor) { - - unselectActors(); if (actor == null) return; stage.setKeyboardFocus(actor.actor); @@ -628,9 +649,68 @@ public class UIScene extends Scene { } } Gdx.input.setInputProcessor(stage); + updateBG(false); super.enter(); } + void updateBG(boolean animate) { + if (Config.instance().getSettingData().dayNightBG) { + int hour = LocalTime.now().getHour(); + if (animate) + setTimeOfDay(hour + 3 > 23 ? hour + 3 - 23 : hour + 3); + switch (hour) { + case 8: + case 9: + setTargetTime(6f); + break; + case 10: + case 11: + setTargetTime(7f); + break; + case 12: + case 13: + setTargetTime(8f); + break; + case 14: + case 15: + setTargetTime(11f); + break; + case 16: + case 17: + setTargetTime(12f); + break; + case 18: + case 19: + setTargetTime(14f); + break; + case 20: + case 21: + setTargetTime(15f); + break; + case 22: + case 23: + setTargetTime(16f); + break; + case 0: + case 1: + setTargetTime(17f); + break; + case 2: + case 3: + setTargetTime(20f); + break; + case 4: + case 5: + setTargetTime(23f); + break; + case 6: + case 7: + setTargetTime(28f); + break; + } + } + } + public TextureRegion getUIBackground() { try { Actor a = ui.getChild(0); diff --git a/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java b/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java index 5efa7f9a3bc..53f6355c93c 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java +++ b/forge-gui-mobile/src/forge/adventure/stage/GameHUD.java @@ -364,38 +364,31 @@ public class GameHUD extends Stage { } } - 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); + @Override + public void act(float delta) { + super.act(delta); + if (fade < targetfade) { + fade += (delta/2); + if (fade > targetfade) + fade = targetfade; + fadeAudio(fade); + } else if (fade > targetfade) { + fade -= (delta/2); + if (fade < targetfade) + fade = targetfade; + fadeAudio(fade); } } - 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); - } + float fade = 1f; + float targetfade = 1f; + + public void fadeIn() { + targetfade = 1f; + } + + public void fadeOut() { + targetfade = 0.1f; } public void stopAudio() { diff --git a/forge-gui-mobile/src/forge/adventure/stage/MapSprite.java b/forge-gui-mobile/src/forge/adventure/stage/MapSprite.java index 87252921090..0b3a690be23 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/MapSprite.java +++ b/forge-gui-mobile/src/forge/adventure/stage/MapSprite.java @@ -5,8 +5,10 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.utils.Array; +import com.github.tommyettinger.textra.TextraLabel; import forge.adventure.data.BiomeSpriteData; import forge.adventure.pointofintrest.PointOfInterest; +import forge.adventure.util.Controls; import forge.adventure.world.WorldSave; import org.apache.commons.lang3.tuple.Pair; @@ -20,14 +22,23 @@ public class MapSprite extends Actor { static public int BackgroundLayer = -1; static public int SpriteLayer = 0; TextureRegion texture; + TextraLabel searchPost; + boolean isCaveDungeon, isOldorVisited; + PointOfInterest poi; - public MapSprite(Vector2 pos, TextureRegion sprite) { - + public MapSprite(Vector2 pos, TextureRegion sprite, PointOfInterest point) { + poi = point; + isCaveDungeon = point != null && (point.getData().type.equalsIgnoreCase("cave") || point.getData().type.equalsIgnoreCase("dungeon")); + isOldorVisited = poi != null && WorldSave.getCurrentSave().getPointOfInterestChanges(poi.getID() + poi.getData().map).hasDeletedObjects(); + searchPost = Controls.newTextraLabel("[%80][+SearchPost]"); texture = sprite; setPosition(pos.x, pos.y); setHeight(texture.getRegionHeight()); setWidth(texture.getRegionWidth()); } + public void checkOut() { + isOldorVisited = true; + } public static Array GetMapSprites(int chunkX, int chunkY) { Array actorGroup = new Array<>(); @@ -44,7 +55,7 @@ public class MapSprite extends Actor { BiomeSpriteData data = WorldSave.getCurrentSave().getWorld().getObject(entry.getValue()); if (data.layer != SpriteLayer) continue; - Actor sprite = new MapSprite(entry.getKey(), WorldSave.getCurrentSave().getWorld().getData().GetBiomeSprites().getSprite(data.name, (int) entry.getKey().x + (int) entry.getKey().y * 11483)); + Actor sprite = new MapSprite(entry.getKey(), WorldSave.getCurrentSave().getWorld().getData().GetBiomeSprites().getSprite(data.name, (int) entry.getKey().x + (int) entry.getKey().y * 11483), null); actorGroup.add(sprite); } return actorGroup; @@ -57,7 +68,7 @@ public class MapSprite extends Actor { BiomeSpriteData data = WorldSave.getCurrentSave().getWorld().getObject(entry.getValue()); if (data.layer != BackgroundLayer) continue; - Actor sprite = new MapSprite(entry.getKey(), WorldSave.getCurrentSave().getWorld().getData().GetBiomeSprites().getSprite(data.name, (int) entry.getKey().x + (int) entry.getKey().y * 11483)); + Actor sprite = new MapSprite(entry.getKey(), WorldSave.getCurrentSave().getWorld().getData().GetBiomeSprites().getSprite(data.name, (int) entry.getKey().x + (int) entry.getKey().y * 11483), null); actorGroup.add(sprite); } return actorGroup; @@ -69,6 +80,10 @@ public class MapSprite extends Actor { if (texture == null) return; batch.draw(texture, getX(), getY()); + if (isCaveDungeon && !isOldorVisited) { + searchPost.setPosition(getX() - 7, getY() + 7); + searchPost.draw(batch, parentAlpha); + } //font.draw(batch,String.valueOf(getZIndex()),getX()-(getWidth()/2),getY()); } diff --git a/forge-gui-mobile/src/forge/adventure/stage/MapStage.java b/forge-gui-mobile/src/forge/adventure/stage/MapStage.java index d6b53cbc300..06404859bb8 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/MapStage.java +++ b/forge-gui-mobile/src/forge/adventure/stage/MapStage.java @@ -797,7 +797,7 @@ public class MapStage extends GameStage { public void setWinner(boolean playerWins) { isLoadingMatch = false; if (playerWins) { - + currentMob.clearCollisionHeight(); Current.player().win(); player.setAnimation(CharacterSprite.AnimationTypes.Attack); currentMob.playEffect(Paths.EFFECT_BLOOD, 0.5f); @@ -805,16 +805,18 @@ public class MapStage extends GameStage { @Override public void run() { currentMob.setAnimation(CharacterSprite.AnimationTypes.Death); + currentMob.resetCollisionHeight(); startPause(0.3f, MapStage.this::getReward); } }, 1f); } else { + currentMob.clearCollisionHeight(); player.setAnimation(CharacterSprite.AnimationTypes.Hit); currentMob.setAnimation(CharacterSprite.AnimationTypes.Attack); - startPause(0.3f, () -> { player.setAnimation(CharacterSprite.AnimationTypes.Idle); currentMob.setAnimation(CharacterSprite.AnimationTypes.Idle); + currentMob.resetCollisionHeight(); player.setPosition(oldPosition4); currentMob.freezeMovement(); boolean defeated = Current.player().defeated(); diff --git a/forge-gui-mobile/src/forge/adventure/stage/PointOfInterestMapSprite.java b/forge-gui-mobile/src/forge/adventure/stage/PointOfInterestMapSprite.java index 5651ced85ea..145833dfd49 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/PointOfInterestMapSprite.java +++ b/forge-gui-mobile/src/forge/adventure/stage/PointOfInterestMapSprite.java @@ -14,10 +14,12 @@ public class PointOfInterestMapSprite extends MapSprite { PointOfInterest pointOfInterest; Texture debugTexture; Rectangle boundingRect; + MapSprite mapSprite; public PointOfInterestMapSprite(PointOfInterest point) { - super(point.getPosition(), point.getSprite()); + super(point.getPosition(), point.getSprite(), point); pointOfInterest = point; + mapSprite = this; boundingRect = new Rectangle(getX(), getY(), texture.getRegionWidth(), texture.getRegionHeight()); } @@ -25,6 +27,10 @@ public class PointOfInterestMapSprite extends MapSprite { return pointOfInterest; } + public MapSprite getMapSprite() { + return mapSprite; + } + private Texture getDebugTexture() { if (debugTexture == null) { Pixmap pixmap = new Pixmap(texture.getRegionWidth(), texture.getRegionHeight(), Pixmap.Format.RGBA8888); diff --git a/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java b/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java index 047e4b9804b..e0c796d14a1 100644 --- a/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java +++ b/forge-gui-mobile/src/forge/adventure/stage/WorldStage.java @@ -154,6 +154,7 @@ public class WorldStage extends GameStage implements SaveFileContent { @Override public void setWinner(boolean playerIsWinner) { if (playerIsWinner) { + currentMob.clearCollisionHeight(); Current.player().win(); player.setAnimation(CharacterSprite.AnimationTypes.Attack); currentMob.playEffect(Paths.EFFECT_BLOOD, 0.5f); @@ -161,6 +162,7 @@ public class WorldStage extends GameStage implements SaveFileContent { @Override public void run() { currentMob.setAnimation(CharacterSprite.AnimationTypes.Death); + currentMob.resetCollisionHeight(); startPause(0.3f, () -> { RewardScene.instance().loadRewards(currentMob.getRewards(), RewardScene.Type.Loot, null); WorldStage.this.removeEnemy(currentMob); @@ -170,9 +172,11 @@ public class WorldStage extends GameStage implements SaveFileContent { } }, 1f); } else { + currentMob.clearCollisionHeight(); player.setAnimation(CharacterSprite.AnimationTypes.Hit); currentMob.setAnimation(CharacterSprite.AnimationTypes.Attack); startPause(0.5f, () -> { + currentMob.resetCollisionHeight(); Current.player().defeated(); WorldStage.this.removeEnemy(currentMob); currentMob = null; diff --git a/forge-gui-mobile/src/forge/adventure/util/MapDialog.java b/forge-gui-mobile/src/forge/adventure/util/MapDialog.java index 1cc22adcd1d..e9ba36c237e 100644 --- a/forge-gui-mobile/src/forge/adventure/util/MapDialog.java +++ b/forge-gui-mobile/src/forge/adventure/util/MapDialog.java @@ -64,7 +64,6 @@ public class MapDialog { } Pair audio = null; - float fade = 1f; void unload() { if (audio != null) { @@ -186,35 +185,11 @@ public class MapDialog { void fadeIn() { disposeAudio(true); - 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; - GameHUD.getInstance().fadeAudio(fade); - } - }, delay); - } + GameHUD.getInstance().fadeIn(); } 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; - GameHUD.getInstance().fadeAudio(fade); - } - }, delay); - } + GameHUD.getInstance().fadeOut(); } public void activate() { //Method for actors to show their dialogues. diff --git a/forge-gui-mobile/src/forge/adventure/util/UIActor.java b/forge-gui-mobile/src/forge/adventure/util/UIActor.java index f1deec90ac1..31ad2116752 100644 --- a/forge-gui-mobile/src/forge/adventure/util/UIActor.java +++ b/forge-gui-mobile/src/forge/adventure/util/UIActor.java @@ -3,7 +3,9 @@ package forge.adventure.util; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.InputEvent; @@ -287,9 +289,41 @@ public class UIActor extends Group { for (ObjectMap.Entry property : entries) { switch (property.key.toString()) { case "image": - Texture t = Forge.getAssets().getTexture(Config.instance().getFile(property.value.toString())); + boolean is2D = property.value.toString().startsWith("ui") && + (property.value.toString().contains("minimap") || property.value.toString().contains("hud")); + Texture t = Forge.getAssets().getTexture(Config.instance().getFile(property.value.toString()), is2D, false); TextureRegion tr = new TextureRegion(t); - newActor.setDrawable(new TextureRegionDrawable(tr)); + if (property.value.toString().contains("title_bg")) { + ShaderProgram shaderNightDay = Forge.getGraphics().getShaderNightDay(); + newActor.setDrawable(new TextureRegionDrawable(tr) { + @Override + public void draw(Batch batch, float x, float y, float width, float height) { + try { + if (Config.instance().getSettingData().dayNightBG) { + batch.end(); + shaderNightDay.bind(); + shaderNightDay.setUniformf("u_timeOfDay", UIScene.getTimeOfDay()); + shaderNightDay.setUniformf("u_time", 0f); + shaderNightDay.setUniformf("u_bias", 0.9f); + batch.setShader(shaderNightDay); + batch.begin(); + //draw + batch.draw(this.getRegion(), x, y, width, height); + //reset + batch.end(); + batch.setShader(null); + batch.begin(); + } else { + batch.draw(this.getRegion(), x, y, width, height); + } + } catch (Exception e) { + //e.printStackTrace(); + } + } + }); + } else { + newActor.setDrawable(new TextureRegionDrawable(tr)); + } break; } } diff --git a/forge-gui-mobile/src/forge/assets/Assets.java b/forge-gui-mobile/src/forge/assets/Assets.java index e78dfa80284..8c1db14dd04 100644 --- a/forge-gui-mobile/src/forge/assets/Assets.java +++ b/forge-gui-mobile/src/forge/assets/Assets.java @@ -251,11 +251,16 @@ public class Assets implements Disposable { public Texture getTexture(FileHandle file) { return getTexture(file, true); } + public Texture getTexture(FileHandle file, boolean required) { + return getTexture(file, false, required); + } + + public Texture getTexture(FileHandle file, boolean is2D, boolean required) { if (file == null || !file.exists()) { if (!required) return null; - System.err.println("Failed to load: " + file +"!. Creating dummy texture."); + System.err.println("Failed to load: " + file + "!. Creating dummy texture."); return getDummy(); } //internal path can be inside apk or jar.. @@ -269,15 +274,19 @@ public class Assets implements Disposable { } Texture t = manager().get(file.path(), Texture.class, false); if (t == null) { - manager().load(file.path(), Texture.class, getTextureFilter()); + if (is2D) + manager().load(file.path(), Texture.class, new TextureParameter()); + else + manager().load(file.path(), Texture.class, getTextureFilter()); manager().finishLoadingAsset(file.path()); t = manager().get(file.path(), Texture.class); } return t; } + public ParticleEffect getEffect(FileHandle file) { if (file == null || !file.exists() || !FileType.Absolute.equals(file.type())) { - System.err.println("Failed to load: " + file +"!."); + System.err.println("Failed to load: " + file + "!."); return null; } ParticleEffect effect = manager().get(file.path(), ParticleEffect.class, false); @@ -310,6 +319,7 @@ public class Assets implements Disposable { public void loadTexture(FileHandle file) { loadTexture(file, getTextureFilter()); } + public void loadTexture(FileHandle file, TextureParameter parameter) { try { if (file == null || !file.exists()) @@ -339,7 +349,7 @@ public class Assets implements Disposable { textrafonts = new ObjectMap<>(); if (!textrafonts.containsKey("textrafont")) { Font font = new Font(bitmapFont, 0f, 2f, 0f, 0f); - font.addAtlas(item_atlas, 0f, 4f, 0f); + font.addAtlas(item_atlas, 0f, 6f, 0f); //problematic atlas since some buttons are small, and this is too big for some buttons, need a way to enable //this via property //font.addAtlas(pixelmana_atlas, -90f, 20f, 0f); @@ -354,7 +364,7 @@ public class Assets implements Disposable { textrafonts = new ObjectMap<>(); if (!textrafonts.containsKey("keysfont")) { Font font = new Font(bitmapFont); - font.addAtlas(keys_atlas, 0f, 4f, 0f); + font.addAtlas(keys_atlas, 0f, 6f, 0f); font.integerPosition = false; textrafonts.put("keysfont", font); } @@ -366,7 +376,7 @@ public class Assets implements Disposable { textrafonts = new ObjectMap<>(); if (!textrafonts.containsKey(name)) { Font font = new Font(bitmapFont); - font.addAtlas(items_atlas, 0f, 4f, 0f); + font.addAtlas(items_atlas, 0f, 6f, 0f); font.integerPosition = false; textrafonts.put(name, font); } @@ -375,7 +385,7 @@ public class Assets implements Disposable { public Music getMusic(FileHandle file) { if (file == null || !file.exists() || !FileType.Absolute.equals(file.type())) { - System.err.println("Failed to load: " + file +"!."); + System.err.println("Failed to load: " + file + "!."); return null; } Music music = manager().get(file.path(), Music.class, false); @@ -389,7 +399,7 @@ public class Assets implements Disposable { public Sound getSound(FileHandle file) { if (file == null || !file.exists() || !FileType.Absolute.equals(file.type())) { - System.err.println("Failed to load: " + file +"!."); + System.err.println("Failed to load: " + file + "!."); return null; } Sound sound = manager().get(file.path(), Sound.class, false); diff --git a/forge-gui-mobile/src/forge/assets/FSkin.java b/forge-gui-mobile/src/forge/assets/FSkin.java index 8f5ff88bbdd..e209547ea4f 100644 --- a/forge-gui-mobile/src/forge/assets/FSkin.java +++ b/forge-gui-mobile/src/forge/assets/FSkin.java @@ -34,7 +34,7 @@ public class FSkin { public static Texture getLogo() { if (Forge.isMobileAdventureMode) - return Forge.getAssets().getTexture(getDefaultSkinFile("adv_logo.png")); + return Forge.getAssets().getTexture(getDefaultSkinFile("adv_logo.png"), true, false); return Forge.getAssets().getTexture(getSkinFile("hd_logo.png"), false); } @@ -113,7 +113,7 @@ public class FSkin { //load theme logo while changing skins Forge.getAssets().loadTexture(getSkinFile("hd_logo.png")); - Forge.getAssets().loadTexture(getDefaultSkinFile("adv_logo.png")); + Forge.getAssets().loadTexture(getDefaultSkinFile("adv_logo.png"), new TextureLoader.TextureParameter()); Forge.getAssets().loadTexture(getDefaultSkinFile("overlay_alpha.png")); Forge.getAssets().loadTexture(getDefaultSkinFile("splatter.png")); @@ -157,14 +157,14 @@ public class FSkin { //override splashscreen startup if (Forge.selector.equals("Adventure")) { if (f3.exists()) { - Texture advSplash = Forge.getAssets().getTexture(f3); + Texture advSplash = Forge.getAssets().getTexture(f3, true, false); w = advSplash.getWidth(); h = advSplash.getHeight(); splashScreen.setSplashTexture(new TextureRegion(advSplash, 0, 0, w, h - 100)); pxSplash = new Pixmap(f3); } if (f4.exists()) { - Texture advBG = Forge.getAssets().getTexture(f4); + Texture advBG = Forge.getAssets().getTexture(f4, true, false); advBG.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat); splashScreen.setSplashBGTexture(advBG); } diff --git a/forge-gui-mobile/src/forge/assets/FSkinImage.java b/forge-gui-mobile/src/forge/assets/FSkinImage.java index 8905122cd05..786b8e62f8e 100644 --- a/forge-gui-mobile/src/forge/assets/FSkinImage.java +++ b/forge-gui-mobile/src/forge/assets/FSkinImage.java @@ -522,12 +522,13 @@ public enum FSkinImage implements FImage { public void load(Pixmap preferredIcons) { String filename = sourceFile.getFilename(); + boolean is2D = sourceFile == SourceFile.ADVENTURE; FileHandle preferredFile = FSkin.getSkinFile(filename); - Texture texture = Forge.getAssets().getTexture(preferredFile, false); + Texture texture = Forge.getAssets().getTexture(preferredFile, is2D, false); if (texture == null) { if (preferredFile.exists()) { try { - texture = Forge.getAssets().getTexture(preferredFile, false); + texture = Forge.getAssets().getTexture(preferredFile, is2D, false); } catch (final Exception e) { System.err.println("Failed to load skin file: " + preferredFile); @@ -594,11 +595,11 @@ public enum FSkinImage implements FImage { //use default file if can't use preferred file FileHandle defaultFile = FSkin.getDefaultSkinFile(filename); - texture = Forge.getAssets().getTexture(defaultFile, false); + texture = Forge.getAssets().getTexture(defaultFile, is2D, false); if (texture == null) { if (defaultFile.exists()) { try { - texture = Forge.getAssets().getTexture(defaultFile, false); + texture = Forge.getAssets().getTexture(defaultFile, is2D, false); } catch (final Exception e) { System.err.println("Failed to load skin file: " + defaultFile); diff --git a/forge-gui-mobile/src/forge/screens/ClosingScreen.java b/forge-gui-mobile/src/forge/screens/ClosingScreen.java index 9e0a55d3767..52ae8ae09bc 100644 --- a/forge-gui-mobile/src/forge/screens/ClosingScreen.java +++ b/forge-gui-mobile/src/forge/screens/ClosingScreen.java @@ -21,7 +21,7 @@ public class ClosingScreen extends FContainer { private boolean drawStatic = false; private FileHandle adv_logo = getSkinFile("adv_logo.png"); private FileHandle existingLogo = adv_logo.exists() ? adv_logo : getDefaultSkinFile("adv_logo.png"); - private Texture logo = existingLogo.exists() && Forge.advStartup ? Forge.getAssets().getTexture(existingLogo) : FSkin.getLogo(); + private Texture logo = existingLogo.exists() && Forge.advStartup ? Forge.getAssets().getTexture(existingLogo, true, false) : FSkin.getLogo(); public ClosingScreen(boolean restart0) { bgAnimation = new BGAnimation(); diff --git a/forge-gui-mobile/src/forge/screens/home/AdventureScreen.java b/forge-gui-mobile/src/forge/screens/home/AdventureScreen.java index 826d434b82c..9e7f7bbd770 100644 --- a/forge-gui-mobile/src/forge/screens/home/AdventureScreen.java +++ b/forge-gui-mobile/src/forge/screens/home/AdventureScreen.java @@ -56,6 +56,7 @@ public class AdventureScreen extends LaunchScreen { @Override protected void startMatch() { + Forge.isMobileAdventureMode = true; //set early for the transition logo Forge.switchToAdventure(); } public static void preload() { diff --git a/forge-gui/res/adventure/Shandalar/custom_cards/tibalt_boss_effect.txt b/forge-gui/res/adventure/Shandalar/custom_cards/tibalt_boss_effect.txt index 2fd7bec2202..f75e52aa142 100644 --- a/forge-gui/res/adventure/Shandalar/custom_cards/tibalt_boss_effect.txt +++ b/forge-gui/res/adventure/Shandalar/custom_cards/tibalt_boss_effect.txt @@ -5,15 +5,17 @@ Types:Enchantment T:Mode$ LifeGained | TriggerZones$ Command | ValidPlayer$ Opponent | Execute$ TrigPutCounter | TriggerDescription$ Whenever an opponent gains life, put a Flame counter on Tibalt SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ FLAME | CounterNum$ 1 T:Mode$ Phase | Phase$ Upkeep | TriggerZones$ Command | ValidPlayer$ You | Execute$ TrigRollDice| TriggerDescription$ Roll a D20, then add the amount of Flame counters on Tibalt's Boss Effect to the roll. -SVar:TrigRollDice:DB$ RollDice | Sides$ 20 | ResultSubAbilities$ 1-5:DBConjureSmall,6-10:DBRandomLoot:,11-15:DBDamage,16:DBCast,17-18:DBWheel,Else:DBConjureBig | Modifier$ X | SpellDescription$ Roll a D20 +SVar:TrigRollDice:DB$ RollDice | Modifier$ X | Sides$ 20 | ResultSubAbilities$ 1-5:DBConjureSmall,6-10:DBRandomLoot:,11-15:DBDamage,16:DBCast,17-18:DBWheel,19-20:DBConjureBig | SpellDescription$ Roll a D20 SVar:DBConjureSmall:DB$ MakeCard | Conjure$ True | AtRandom$ True | Spellbook$ Chained Brute,Big Spender,Devilish Valet,Gibbering Fiend,Hobblefiend,Footlight Fiend,Forge Devil,Festival Crasher,Hulking Devil,Mayhem Devil,Spiteful Prankster,Vexing Devil,Wildfire Devil | Zone$ Battlefield | SpellDescription$ Conjure a random Devil unto the battlefield. SVar:DBRandomLoot:DB$ Draw | Defined$ Player | NumCards$ 3 | SubAbility$ DBDiscard3 | SpellDescription$ Each player draws three cards, then discards three cards at random. SVar:DBDiscard3:DB$ Discard | Defined$ Player | Mode$ Random | NumCards$ 3 -SVar:DBDamage:DB$ DealDamage | NumDmg$ 7 | ValidTgts$ Creature,Player,Planeswalker | TargetsAtRandom$ True | SubAbility$ DBChangeZone | SpellDescription$ Tibalt deals seven damage to a creature chosen at random. Then Tibalt returns a random creature card to the battlefield -SVar:DBChangeZone:DB$ ChangeZone | ChangeType$ Creature | ChangeNum$ 1 | GainControl$ True | Hidden$ True | Origin$ Graveyard | AtRandom$ True | Destination$ Battlefield +SVar:DBDamage:DB$ ChooseCard | AtRandom$ True | Choices$ Creature | SubAbility$ DBDamageBis | SpellDescription$ Tibalt deals seven damage to a creature chosen at random. Then Tibalt returns a random creature card to the battlefield +SVar:DBDamageBis:DB$ DealDamage | NumDmg$ 7 | Defined$ ChosenCard | SubAbility$ DBChangeZone +SVar:DBChangeZone:DB$ ChangeZone | ChangeType$ Creature | ChangeNum$ 1 | GainControl$ True | Hidden$ True | Origin$ Graveyard | AtRandom$ True | Destination$ Battlefield | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True SVar:DBCast:DB$ Play | AnySupportedCard$ Names:Hellion Eruption,Insurrection,Warp World,Shahrazad,Possibility Storm,Scrambleverse | RandomCopied$ True | CopyCard$ True | WithoutManaCost$ True | SpellDescription$ Cast a copy of one of the following cards chosen at random—Hellion Eruption, Insurrection, Warp World, Shahrazad, Possibility Storm, Scrambleverse. SVar:DBWheel:DB$ Discard | Mode$ Hand | Defined$ Player | SubAbility$ DBEachDraw | SpellDescription$ Each player discards their hand, then draws seven cards. SVar:DBEachDraw:DB$ Draw | Defined$ Player | NumCards$ 7 -SVar:DBConjureBig:DB$ MakeCard | Conjure$ True | AtRandom$ True | SpellBook$ Kardur; Doomscourge,Harvester of Souls,Archfiend of Depravity,Bloodgift Demon,Ob Nixilis, Unshackled,Nightmare Shepherd,Archfiend of Despair,Lord of the Void,Sire of Insanity, Reaper from the Abyss,Archfiend of Spite,Abyssal Persecutor | Zone$ Battlefield | SpellDescription$ Conjure a random Demon unto the battlefield. +SVar:DBConjureBig:DB$ MakeCard | Conjure$ True | AtRandom$ True | Spellbook$ GriselbrandHarvester of Souls,Archfiend of Depravity,Bloodgift Demon,Ob Nixilis, Unshackled,Nightmare Shepherd,Archfiend of Despair,Lord of the Void,Sire of Insanity, Reaper from the Abyss,Archfiend of Spite,Abyssal Persecutor | Zone$ Battlefield | SpellDescription$ Conjure a random Demon unto the battlefield. SVar:X:Count$CardCounters.FLAME Oracle:Whenever an opponent gains life, put a Flame counter on Tibalt\nAt the beginning of your upkeep roll a D20, Then add the amount of Flame counters on Tibalt's Boss Effect to the roll. \n1—5 |Conjure a random Devil unto the battlefield. \n6—10 |Each player draws three cards, then discards three cards at random.\n11—15 |Tibalt deals seven damage to a creature chosen at random.\n16 |Cast a copy of one of the following cards chosen at random—Hellion Eruption, Insurrection, Warp World,Shahrazad,Possibility Storm,Scrambleverse.\n17—18 |Each player discards their hand, then draws seven cards.\n19—20 |Conjure a random Demon unto the battlefield. diff --git a/forge-gui/res/adventure/Shandalar/maps/main.tiled-session b/forge-gui/res/adventure/Shandalar/maps/main.tiled-session index 5eeb002317e..a53a20b34d9 100644 --- a/forge-gui/res/adventure/Shandalar/maps/main.tiled-session +++ b/forge-gui/res/adventure/Shandalar/maps/main.tiled-session @@ -3,13 +3,13 @@ "height": 4300, "width": 2 }, - "activeFile": "map/tibalt.tmx", + "activeFile": "map/cave_6.tmx", "automapping.whileDrawing": false, "expandedProjectPaths": [ "map", - "map/main_story", + "tileset", "obj", - "tileset" + "map/main_story" ], "file.lastUsedOpenFilter": "All Files (*)", "fileStates": { @@ -2469,14 +2469,14 @@ }, "map/main_story/templeofchandra.tmx": { "expandedObjectLayers": [ - 7, - 2 + 2, + 7 ], "scale": 2, "selectedLayer": 5, "viewCenter": { - "x": 480, - "y": 487.75 + "x": 849, + "y": 624.75 } }, "map/main_story/templeofliliana.tmx": { @@ -3085,7 +3085,7 @@ "scale": 3, "selectedLayer": 4, "viewCenter": { - "x": 321, + "x": 345, "y": 181.83333333333331 } }, @@ -3096,8 +3096,8 @@ "scale": 2, "selectedLayer": 5, "viewCenter": { - "x": 243.5, - "y": 171.75 + "x": 225, + "y": 192.75 } }, "map/tibalt_f1.tmx": { @@ -3105,10 +3105,10 @@ 12 ], "scale": 3, - "selectedLayer": 0, + "selectedLayer": 5, "viewCenter": { - "x": 368, - "y": 223.83333333333331 + "x": 518.6666666666666, + "y": 69.16666666666667 } }, "map/tibalt_f2.tmx": { @@ -3118,8 +3118,8 @@ "scale": 3, "selectedLayer": 4, "viewCenter": { - "x": 176, - "y": 167.83333333333334 + "x": 174.33333333333331, + "y": 162.5 } }, "map/tileset/buildings.tsx": { @@ -3267,27 +3267,30 @@ "map.width": 90, "openFiles": [ "map/xira.tmx", - "map/tibalt_f1.tmx", - "map/tibalt_f2.tmx", - "map/tibalt.tmx", - "map/main_story/templeofchandra.tmx", - "map/teferi.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/xira.tmx", - "map/tibalt_f1.tmx", - "map/tibalt_f2.tmx", - "map/teferi.tmx", - "map/main_story/templeofchandra.tmx", - "map/tibalt.tmx", "map/aerie_1B.tmx", "map/aerie_1C.tmx", "map/cave_6.tmx", "map/cave_5.tmx", "map/cave_4.tmx", - "map/cave_3.tmx" + "map/cave_3.tmx", + "map/cave_2.tmx", + "map/castle_plains_3.tmx", + "map/castle_plains_2.tmx", + "map/castle_plains_1.tmx", + "map/tibalt.tmx", + "map/magetower_1.tmx" ], "resizeMap.removeObjects": true, "textEdit.monospace": true diff --git a/forge-gui/res/adventure/Shandalar/maps/map/tibalt.tmx b/forge-gui/res/adventure/Shandalar/maps/map/tibalt.tmx index a1a661608bc..c7670d176a4 100644 --- a/forge-gui/res/adventure/Shandalar/maps/map/tibalt.tmx +++ b/forge-gui/res/adventure/Shandalar/maps/map/tibalt.tmx @@ -7,7 +7,7 @@ - eJxjYMAEbpwQDGMTAqs5EBjGHylgFdCvu3gQ/HhB+tgHwv5iCHEQfw0Nwx1k724eVDE/JPt3EmH/cyLSEgwgpztZoNlPgXxGBgh+zIlp105omNwQRYhxCBBn13Uc7gLZCfIzI5SPbD8hQKzdtAC0spuRsBKcdoPCGIbpDZDtRrafHm5Bt5ecMNgkSlgNIfuxsbHl2Y1odh0VJs9eWpZFxNqPXt4MRNojKhyEBth+GgKY/dwDWB6OgqEPACg9MIk= + eJxjYMAEbpwQDGMTAqs5EBjGHylgFdCvu3gQ/HhB+tgHwv5iCHEQfw0Nwx1k724e3PI7ibD/ORFpCQaQ050s0OynQD4jAwTjsh8UBjdEEWIcAsTZdR2Hu0B2gvwMsxOf/eiAWLtpAWhlNzF+x2U3KIxhmN4A2W5k++nhFnR7yQmDTaKE1RCyHxsbBNDz7EY0u44Kk2cvLcsiYu1HL28GIu0RFQ5CA2w/DQHMfu4BLA9HwdAHAHu4LNQ= @@ -30,7 +30,7 @@ - eJydlrtuE0EUhscTb6zVAmFNbMEbQEGRgksDvEEkEuQyQJAIUCIKBO8QLg3gUIB4gVDBO4TSQTR+AhrboVhgucyvmZ85O9mLxZF+ebM7e775z5mZzShWaitS/yJz19sL1TrQVt+M3vaUeiOEv+WYOl3VRXZVSPYzp+dGl/pKXTZ6Z5ja6ErfjnniVMdGzMOeai/OYSNV6rrTmdhfU4jJf/ie6vJ7TwVTde39m469aX53un78H8efx3fW4H+mLRfMX6llgvXa6Iup97Br+e97Rf6NtNgHCB4Qdw3zU7ueS3aeWi702/kClwJ/JxD4y+bZmrZr5ED7usKvrPvDY7YXVHLc1edENRvP+BsqF2ORa1v4Zt0fGWZvSan+kr23Ens2hDy7Pc9mP2XIGnAukj1qKbXvhNzgoe5n217sweCIUqcSz5Y13QzYLeX9S/ZQrIn7hvfdsH5Glo0YG4+j2F4/Pup7AO/gn0yKc4AQWFdUyGe9GPT8YlGpV4u2/+tG17RnsweIr20rxIekWH9E6B21DdkUPV/sWDb6jjWHNQzv3Gdkc72VsclncL/VseH5pfPNs2omzpJxXPRdxib/dHxYnBfGsM6fW9VsrPnlBTtmHHxfVuJqdp4We07f5IKHWj7QvtdkD7Tfa7c6lo8x4TkL75LNyEW/cc6BzT3E/v4QyiPPnonzhfw9U+d7kZ3Hatt7JxuxQaY4vxEhj6xQki1jS8xD8j8mfsywe/g9eJY9rVOZb7KzgD9vgH3OvHPe6EKn+hr5yZb7vOyM4/Mm4f29mu9SFnlN3DsTt94HJblQh/AbWCWuW+ReC/KE0ZQTuW67+sta3BG1qfI3cTx6mjbMZd5o+j8rc7Uiu0x/AckTJ+A= + eJyllr1uE0EUha8n3lirBYJNbMEbQEGRgp8G8gZIBOQyQJAIUCIKBO8QfhqIQ0HEC0AF7xBKg2j8BDS2Q2HA/MzRzGHuTnbXFhzpyBt7dr45987Opp+KbCbyVxN/vbVQ7n3j/NV6ty3yShl/6zFVvmzy7DJp9lPvZ9YXOiIXrV9bprFe7bgxj72r2NA87JEJ5hrWmyLXvE+l4ZqGhv+Qe2SKv3uimNJy39/w7A37udMK4397/v/mhsbGccH82XRMsF5af7b17rUc/207z7/ezPcBRgbojmV/qM/HnjYdF/7lc4FLg78TGfxl+9uacXtk34S6Ym/r7A+OuF7Q2VFfn2PlbPzGz9hTNRZzbancrPtDy2wviXSW3HcraWDDmOdNO7DZTy1dA65Fs/s1kY/emBs81P10PZg96B4SOZEFtq7pRsSuSciv2T21J+5Z3jfL+pE4NjSwGfupu350OPQA2cE/nuXXAEPYV3TMZ70oZn6+KLK96Pp/xfqqCWz2APpSd4beZfn6Q3F21DZm08x8vuHY6Dv2HPYwsvNMJZv7rYhNPsXnrYqNzC98bp5VY3WWDNJ87iI2+SfTg+a6MIZ1/lQrZ2PPLy+4MYPo/bKSlrOnzXzPmZtc8FDL+yb0muyuCc/azYbjY0x81iG7ZlNT1W+cc2DzGWJ/vytPk8Aeq/OF/D1b57uJW8eleshONrROpjq/oZhHVmzN1tpU69D891kY02sdvA+ZdU+rXJSb7EnEn1dgn7H3nLU+1yi/xvxk6+e86Izj77OM+/cq3kuTJHjo7xn6/d4tmAt1iN+BZea+xdxr0TyxZs2JuW75+uta3Fa1Kcs39DxmGs1Yy7ya9f/GxNeK7CL/ATakJuM= @@ -53,18 +53,18 @@ - + - + - + @@ -122,8 +122,8 @@ - - + + @@ -131,7 +131,7 @@ - + diff --git a/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f1.tmx b/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f1.tmx index 425f3be0984..c7624b4546a 100644 --- a/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f1.tmx +++ b/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f1.tmx @@ -1,5 +1,5 @@ - + @@ -7,7 +7,7 @@ - eJzVlVEOwjAIhhvffFlM1Jt4Bu/hYb3PjmDIJKkEKKwFOxKyWij7JH9ZKePseS7ldNcdciyO9bT4SG4rE+Z7z0Zxv26/zuVway97Bnf9W+OWamZyw3q5bI77lNX6/kxuZAYHOwo3stZrLv+x6B6hE64e1bd2R4FLs39wW2w2bs/5NYDb+y3wzlzgyua2mna2vn8ccy83NU0L2D+pNt2XuDE2itvbc5pr5R7Rb/D31a9lLb5+41H9hvrc3emdRTWbtN/ibvUlktvLQ/OkmhZuWkd6Su8Aq/XS6nUPN+hd4m7t16bN79n7Tec4PkdwU/49+vbMo159lzLuXmZze2MzcLfORjB5uC1sWr/3/vce7j3W6nckcyR3hk6OyP0BqjbOBA== + eJzVllEOwjAIhhvffFlM1Jt4Bu/hYb3PjmDIJKkEKKxQOxKyWij7/EPRUuLseS7ldNcdciyO9bR4JLeVCfO9Z7O4X7df53K4tZd9BHf9WeOWao7khvVy2Rz3Kav1/SO5kRkc7CjcyFqvufzHontGn3D1aH9rdxS4NPsHt8Vm4/acXxO4vb8F3pkLXKO5raadre8fx9zLTU3rBdRPqk33JW6MRXF7Nae5Vu4IvcHfV38va/H1G8/SG+pzd6d3FtVs0n6Lu6VLJreXh+ZJNS3ctI70lN4BVvdLS+sebuh3iRv/S1m4tfk9u950juMzgpvy7+lvzzzq7e9S4u7laG5vbAbu1tkMJg+3hU3Te+937+HeYy29M5kzuUf0yRG5P3f9ziI= @@ -43,10 +43,10 @@ - + - + [ @@ -59,7 +59,7 @@ ] - + @@ -85,7 +85,7 @@ - + @@ -93,11 +93,6 @@ - - - - - @@ -151,10 +146,10 @@ - - + + - + [{ "text":"This door is locked", @@ -182,5 +177,13 @@ + + + + + + + + diff --git a/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f2.tmx b/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f2.tmx index 3af93ecfc3d..eba66e84fc5 100644 --- a/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f2.tmx +++ b/forge-gui/res/adventure/Shandalar/maps/map/tibalt_f2.tmx @@ -1,5 +1,5 @@ - + @@ -7,7 +7,7 @@ - eJxjYBhY4MaJwJTqJ4RJNdeEjzAmx1xK/EWpuaTqHWhzsakjZC6yPIyNLkZO+OJSR0k6QzcHn93UNpeQHDnm0jIcCMkNBnOR/U6Nsg2fHdQ2FxufEnPx2UMNcylNZ+h2kJKPAc8THhk= + eJxjYBhY4MaJwJTqJ4RJNdeEjzAmx1xK/EWpuaTqHWhzsakjZC6yPIyNLkZO+OJSR0k6QzcHn93UNpeQHDnm0jIcCMkNBnOR/U6Nsg2fHdQ2FxufEnPx2UMNc6mRzsitLwAC3x8G @@ -63,12 +63,6 @@ - - - - - - @@ -95,7 +89,7 @@ - + [{ "text":"This door is locked", @@ -111,5 +105,12 @@ }] + + + + + + + diff --git a/forge-gui/res/adventure/Shandalar/sprites/items.atlas b/forge-gui/res/adventure/Shandalar/sprites/items.atlas index a23a5f9136a..880da0e083f 100644 --- a/forge-gui/res/adventure/Shandalar/sprites/items.atlas +++ b/forge-gui/res/adventure/Shandalar/sprites/items.atlas @@ -392,4 +392,13 @@ FrogSkin size: 18, 18 BurningBook xy: 239,815 - size: 18, 18 \ No newline at end of file + size: 18, 18 +GreenLeaf + xy: 48, 496 + size: 16, 16 +GreenLeaf2 + xy: 112, 608 + size: 16, 16 +SearchPost + xy: 384, 63 + size: 16, 16 \ No newline at end of file diff --git a/forge-gui/res/adventure/Shandalar/sprites/items.png b/forge-gui/res/adventure/Shandalar/sprites/items.png index b5a19653401..a2194cbf234 100644 Binary files a/forge-gui/res/adventure/Shandalar/sprites/items.png and b/forge-gui/res/adventure/Shandalar/sprites/items.png differ diff --git a/forge-gui/res/adventure/Shandalar/ui/statistic.json b/forge-gui/res/adventure/Shandalar/ui/statistic.json index 2a0886907b2..0e901284a71 100644 --- a/forge-gui/res/adventure/Shandalar/ui/statistic.json +++ b/forge-gui/res/adventure/Shandalar/ui/statistic.json @@ -136,24 +136,6 @@ "x": 315, "y": 224 }, - { - "type": "Image", - "name": "lifeIcon", - "image": "ui/life.png", - "x": 392, - "y": 40, - "width": 16, - "height": 16 - }, - { - "type": "Image", - "name": "goldIcon", - "image": "ui/money.png", - "x": 392, - "y": 60, - "width": 16, - "height": 16 - }, { "type": "Label", "name": "playerName", @@ -166,19 +148,25 @@ { "type": "Label", "name": "lifePoints", - "fontColor":"black", "width": 64, "height": 16, - "x": 410, + "x": 394, "y": 40 }, { "type": "Label", "name": "money", - "fontColor":"black", "width": 64, "height": 16, - "x": 410, + "x": 394, + "y": 80 + }, + { + "type": "Label", + "name": "shards", + "width": 64, + "height": 16, + "x": 394, "y": 60 }, { diff --git a/forge-gui/res/adventure/Shandalar/ui/statistic_portrait.json b/forge-gui/res/adventure/Shandalar/ui/statistic_portrait.json index ba4ec5ebe02..9f01117d94d 100644 --- a/forge-gui/res/adventure/Shandalar/ui/statistic_portrait.json +++ b/forge-gui/res/adventure/Shandalar/ui/statistic_portrait.json @@ -136,40 +136,28 @@ "height": 24, "fontColor":"black" }, - { - "type": "Image", - "name": "lifeIcon", - "image": "ui/life.png", - "x": 98, - "y": 22, - "width": 16, - "height": 16 - }, - { - "type": "Image", - "name": "goldIcon", - "image": "ui/money.png", - "x": 98, - "y": 42, - "width": 16, - "height": 16 - }, { "type": "Label", "name": "lifePoints", - "fontColor":"black", "width": 64, "height": 16, - "x": 118, + "x": 98, "y": 22 }, { "type": "Label", "name": "money", - "fontColor":"black", "width": 64, "height": 16, - "x": 118, + "x": 98, + "y": 42 + }, + { + "type": "Label", + "name": "shards", + "width": 64, + "height": 16, + "x": 170, "y": 42 }, { diff --git a/forge-gui/res/adventure/Shandalar/world/enemies.json b/forge-gui/res/adventure/Shandalar/world/enemies.json index c093de893d5..709217697e1 100644 --- a/forge-gui/res/adventure/Shandalar/world/enemies.json +++ b/forge-gui/res/adventure/Shandalar/world/enemies.json @@ -10543,7 +10543,7 @@ "decks/torturer.dck" ], "ai": "", - "flying": false, + "flying": true, "spawnRate": 1, "scale":0.5, "difficulty": 0.1, diff --git a/forge-gui/res/cardsfolder/a/academy_rector.txt b/forge-gui/res/cardsfolder/a/academy_rector.txt index b1520725d1e..42d4cc90466 100644 --- a/forge-gui/res/cardsfolder/a/academy_rector.txt +++ b/forge-gui/res/cardsfolder/a/academy_rector.txt @@ -2,9 +2,8 @@ Name:Academy Rector ManaCost:3 W Types:Creature Human Cleric PT:1/2 -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigExile | OptionalDecider$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, search your library for an enchantment card and put that card onto the battlefield, then shuffle. -SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ TriggeredNewCardLKICopy | SubAbility$ DBReturn -SVar:DBReturn:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Enchantment.YouOwn | ChangeNum$ 1 +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigExile | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, search your library for an enchantment card and put that card onto the battlefield, then shuffle. +SVar:TrigReturn:AB$ ChangeZone | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Origin$ Library | Destination$ Battlefield | ChangeType$ Enchantment.YouOwn | ChangeNum$ 1 SVar:SacMe:4 AI:RemoveDeck:Random DeckNeeds:Type$Enchantment diff --git a/forge-gui/res/cardsfolder/a/arena_rector.txt b/forge-gui/res/cardsfolder/a/arena_rector.txt index 07fc3ee3885..6352b57e0ac 100644 --- a/forge-gui/res/cardsfolder/a/arena_rector.txt +++ b/forge-gui/res/cardsfolder/a/arena_rector.txt @@ -2,9 +2,8 @@ Name:Arena Rector ManaCost:3 W Types:Creature Human Cleric PT:1/2 -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigExile | OptionalDecider$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, search your library for a planeswalker card, put it onto the battlefield, then shuffle. -SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ TriggeredNewCardLKICopy | SubAbility$ DBReturn -SVar:DBReturn:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Planeswalker.YouOwn | ChangeNum$ 1 +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigExile | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, search your library for a planeswalker card, put it onto the battlefield, then shuffle. +SVar:TrigExile:AB$ ChangeZone | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Origin$ Library | Destination$ Battlefield | ChangeType$ Planeswalker.YouOwn | ChangeNum$ 1 SVar:SacMe:4 AI:RemoveDeck:Random DeckNeeds:Type$Planeswalker diff --git a/forge-gui/res/cardsfolder/c/cavalier_of_thorns.txt b/forge-gui/res/cardsfolder/c/cavalier_of_thorns.txt index 46e1a8d3fb0..5d48d3f1356 100644 --- a/forge-gui/res/cardsfolder/c/cavalier_of_thorns.txt +++ b/forge-gui/res/cardsfolder/c/cavalier_of_thorns.txt @@ -5,7 +5,6 @@ PT:5/6 K:Reach T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, reveal the top five cards of your library. Put a land card from among them onto the battlefield and the rest into your graveyard. SVar:TrigDig:DB$ Dig | DigNum$ 5 | ChangeNum$ 1 | ChangeValid$ Land | Optional$ True | DestinationZone$ Battlefield | DestinationZone2$ Graveyard -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | OptionalDecider$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, put another target card from your graveyard on top of your library. -SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ TriggeredNewCardLKICopy | SubAbility$ DBChange -SVar:DBChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Library | ValidTgts$ Card.YouOwn+Other | TgtPrompt$ Select another target card from your graveyard | AITgts$ Card.Other | ChangeNum$ 1 +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile| TriggerDescription$ When CARDNAME dies, you may exile it. If you do, put another target card from your graveyard on top of your library. +SVar:TrigExile:AB$ ChangeZone | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Origin$ Graveyard | Destination$ Library | ValidTgts$ Card.YouOwn+Other | TgtPrompt$ Select another target card from your graveyard | AITgts$ Card.Other | ChangeNum$ 1 Oracle:Reach\nWhen Cavalier of Thorns enters the battlefield, reveal the top five cards of your library. Put a land card from among them onto the battlefield and the rest into your graveyard.\nWhen Cavalier of Thorns dies, you may exile it. If you do, put another target card from your graveyard on top of your library. diff --git a/forge-gui/res/cardsfolder/c/colfenors_urn.txt b/forge-gui/res/cardsfolder/c/colfenors_urn.txt index 922a4ef549c..bdb6a88a793 100644 --- a/forge-gui/res/cardsfolder/c/colfenors_urn.txt +++ b/forge-gui/res/cardsfolder/c/colfenors_urn.txt @@ -2,9 +2,8 @@ Name:Colfenor's Urn ManaCost:3 Types:Artifact T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Battlefield | ValidCard$ Creature.toughnessGE4+YouOwn | OptionalDecider$ You | Execute$ TrigExile | TriggerDescription$ Whenever a creature with toughness 4 or greater is put into your graveyard from the battlefield, you may exile it. -SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ TriggeredNewCardLKICopy | RememberChanged$ True +SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ TriggeredNewCardLKICopy T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | CheckSVar$ X | SVarCompare$ GE3 | Execute$ TrigReturnAll | TriggerDescription$ At the beginning of the end step, if three or more cards have been exiled with CARDNAME, sacrifice it. If you do, return those cards to the battlefield under their owner's control. -SVar:TrigReturnAll:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBSacSelf -SVar:DBSacSelf:DB$ Sacrifice -SVar:X:Remembered$Amount +SVar:TrigReturnAll:AB$ ChangeZone | Cost$ Mandatory Sac<1/CARDNAME> | Defined$ ExiledWith | Origin$ Exile | Destination$ Battlefield +SVar:X:ExiledWith$Amount Oracle:Whenever a creature with toughness 4 or greater is put into your graveyard from the battlefield, you may exile it.\nAt the beginning of the end step, if three or more cards have been exiled with Colfenor's Urn, sacrifice it. If you do, return those cards to the battlefield under their owner's control. diff --git a/forge-gui/res/cardsfolder/c/creeping_chill.txt b/forge-gui/res/cardsfolder/c/creeping_chill.txt index 6afe9895037..29edfe7bbe8 100644 --- a/forge-gui/res/cardsfolder/c/creeping_chill.txt +++ b/forge-gui/res/cardsfolder/c/creeping_chill.txt @@ -3,7 +3,6 @@ ManaCost:3 B Types:Sorcery A:SP$ DamageAll | StackDescription$ CARDNAME deals 3 damage to each opponent and | Cost$ 3 B | ValidPlayers$ Player.Opponent | NumDmg$ 3 | SubAbility$ DBGainLife | SpellDescription$ CARDNAME deals 3 damage to each opponent and you gain 3 life. T:Mode$ ChangesZone | Origin$ Library | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | OptionalDecider$ You | TriggerDescription$ When CARDNAME is put into your graveyard from your library, you may exile it. If you do, CARDNAME deals 3 damage to each opponent and you gain 3 life. -SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredCardLKICopy | Origin$ Graveyard | Destination$ Exile | SubAbility$ DBDamage -SVar:DBDamage:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 3 | SubAbility$ DBGainLife +SVar:TrigExile:AB$ DealDamage | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Defined$ Player.Opponent | NumDmg$ 3 | SubAbility$ DBGainLife SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ 3 Oracle:Creeping Chill deals 3 damage to each opponent and you gain 3 life.\nWhen Creeping Chill is put into your graveyard from your library, you may exile it. If you do, Creeping Chill deals 3 damage to each opponent and you gain 3 life. diff --git a/forge-gui/res/cardsfolder/g/gamekeeper.txt b/forge-gui/res/cardsfolder/g/gamekeeper.txt index f4d4ee2260a..6ad8af5e234 100644 --- a/forge-gui/res/cardsfolder/g/gamekeeper.txt +++ b/forge-gui/res/cardsfolder/g/gamekeeper.txt @@ -2,7 +2,6 @@ Name:Gamekeeper ManaCost:3 G Types:Creature Elf PT:2/2 -T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | OptionalDecider$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and put all other cards revealed this way into your graveyard. -SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Exile | SubAbility$ DBDigUntil -SVar:DBDigUntil:DB$ DigUntil | Valid$ Creature | ValidDescription$ Creature | FoundDestination$ Battlefield | RevealedDestination$ Graveyard +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and put all other cards revealed this way into your graveyard. +SVar:TrigExile:AB$ DigUntil | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Valid$ Creature | ValidDescription$ Creature | FoundDestination$ Battlefield | RevealedDestination$ Graveyard Oracle:When Gamekeeper dies, you may exile it. If you do, reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and put all other cards revealed this way into your graveyard. diff --git a/forge-gui/res/cardsfolder/g/greenbelt_rampager.txt b/forge-gui/res/cardsfolder/g/greenbelt_rampager.txt index b6a707642fd..85ea5e2e4bc 100644 --- a/forge-gui/res/cardsfolder/g/greenbelt_rampager.txt +++ b/forge-gui/res/cardsfolder/g/greenbelt_rampager.txt @@ -2,7 +2,7 @@ Name:Greenbelt Rampager ManaCost:G Types:Creature Elephant PT:3/4 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ PaymentCheck | TriggerDescription$ When CARDNAME enters the battlefield, pay {E}{E} (two energy counters). If you can't, return Greenbelt Rampager to its owner's hand and you get {E}. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ PaymentCheck | TriggerDescription$ When CARDNAME enters the battlefield, pay {E}{E} (two energy counters). If you can't, return CARDNAME to its owner's hand and you get {E}. SVar:PaymentCheck:DB$ Pump | UnlessCost$ Mandatory PayEnergy<2> | UnlessPayer$ You | UnlessResolveSubs$ WhenNotPaid | SubAbility$ DBReturnToHand SVar:DBReturnToHand:DB$ ChangeZone | Origin$ Battlefield | Destination$ Hand | Defined$ Self | SubAbility$ GetEnergy SVar:GetEnergy:DB$ PutCounter | Defined$ You | CounterType$ ENERGY | CounterNum$ 1 diff --git a/forge-gui/res/cardsfolder/g/greenwarden_of_murasa.txt b/forge-gui/res/cardsfolder/g/greenwarden_of_murasa.txt index c37072ba62d..68f0961563c 100644 --- a/forge-gui/res/cardsfolder/g/greenwarden_of_murasa.txt +++ b/forge-gui/res/cardsfolder/g/greenwarden_of_murasa.txt @@ -4,7 +4,6 @@ Types:Creature Elemental PT:5/4 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChangeZone | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, you may return target card from your graveyard to your hand. SVar:TrigChangeZone:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Card.YouCtrl -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigExile | OptionalDecider$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, return target card from your graveyard to your hand. -SVar:TrigExile:DB$ ChangeZone | Destination$ Exile | Defined$ TriggeredNewCardLKICopy | SubAbility$ DBReturn -SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Card.YouCtrl +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigExile | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, return target card from your graveyard to your hand. +SVar:TrigExile:AB$ ChangeZone | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Origin$ Graveyard | Destination$ Hand | ValidTgts$ Card.YouCtrl Oracle:When Greenwarden of Murasa enters the battlefield, you may return target card from your graveyard to your hand.\nWhen Greenwarden of Murasa dies, you may exile it. If you do, return target card from your graveyard to your hand. diff --git a/forge-gui/res/cardsfolder/i/iname_as_one.txt b/forge-gui/res/cardsfolder/i/iname_as_one.txt index 88ac2e52b1f..cc4dd17e20d 100644 --- a/forge-gui/res/cardsfolder/i/iname_as_one.txt +++ b/forge-gui/res/cardsfolder/i/iname_as_one.txt @@ -5,6 +5,5 @@ PT:8/8 T:Mode$ ChangesZone | ValidCard$ Card.wasCastFromYourHandByYou+Self | Destination$ Battlefield | Execute$ TrigSearch | OptionalDecider$ You | TriggerDescription$ When CARDNAME enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle. SVar:TrigSearch:DB$ ChangeZone | Origin$ Library | Destination$ Battlefield | ChangeType$ Spirit.YouCtrl | ChangeNum$ 1 | ShuffleNonMandatory$ True T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigExile | OptionalDecider$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, return target Spirit permanent card from your graveyard to the battlefield. -SVar:TrigExile:DB$ ChangeZone | Origin$ Graveyard | Destination$ Exile | Defined$ TriggeredNewCardLKICopy | SubAbility$ DBReturn -SVar:DBReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Hidden$ True | ChangeType$ Spirit.YouCtrl | ChangeNum$ 1 +SVar:TrigExile:AB$ ChangeZone | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | Origin$ Graveyard | Destination$ Battlefield | ValidTgts$ Spirit.YouCtrl Oracle:When Iname as One enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle.\nWhen Iname as One dies, you may exile it. If you do, return target Spirit permanent card from your graveyard to the battlefield. diff --git a/forge-gui/res/cardsfolder/k/kinzu_of_the_bleak_coven.txt b/forge-gui/res/cardsfolder/k/kinzu_of_the_bleak_coven.txt index d512fda3610..00ced35171a 100644 --- a/forge-gui/res/cardsfolder/k/kinzu_of_the_bleak_coven.txt +++ b/forge-gui/res/cardsfolder/k/kinzu_of_the_bleak_coven.txt @@ -4,6 +4,6 @@ Types:Legendary Creature Phyrexian Vampire PT:5/4 K:Flying T:Mode$ ChangesZone | ValidCard$ Creature.nonToken+Other+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | TriggerZones$ Battlefield | Execute$ TrigExile | OptionalDecider$ You | TriggerDescription$ Whenever another nontoken creature you control dies, you may pay 2 life and exile it. If you do, create a token that's a copy of that creature, except it's 1/1 and has toxic 1. (Players dealt combat damage by it also get a poison counter.) -SVar:TrigExile:AB$ CopyPermanent | Cost$ PayLife<2> ExileAnyGrave<1/Card.TriggeredCard/Exile nontoken creature that just died> | AddKeywords$ Toxic:1 | Defined$ TriggeredCardLKICopy | SetPower$ 1 | SetToughness$ 1 +SVar:TrigExile:AB$ CopyPermanent | Cost$ PayLife<2> ExileAnyGrave<1/Card.TriggeredNewCard> | AddKeywords$ Toxic:1 | Defined$ TriggeredCardLKICopy | SetPower$ 1 | SetToughness$ 1 DeckHas:Ability$Token Oracle:Flying\nWhenever another nontoken creature you control dies, you may pay 2 life and exile it. If you do, create a token that's a copy of that creature, except it's 1/1 and has toxic 1. (Players dealt combat damage by it also get a poison counter.) diff --git a/forge-gui/res/cardsfolder/k/kozileks_return.txt b/forge-gui/res/cardsfolder/k/kozileks_return.txt index 143e2660c38..c27edd5c8e5 100644 --- a/forge-gui/res/cardsfolder/k/kozileks_return.txt +++ b/forge-gui/res/cardsfolder/k/kozileks_return.txt @@ -4,5 +4,5 @@ Types:Instant K:Devoid A:SP$ DamageAll | Cost$ 2 R | NumDmg$ 2 | ValidCards$ Creature | ValidDescription$ each creature. | SpellDescription$ CARDNAME deals 2 damage to each creature. T:Mode$ SpellCast | ValidCard$ Creature.Eldrazi+cmcGE7+YouCtrl | TriggerZones$ Graveyard | Execute$ DBDamageAll | TriggerDescription$ Whenever you cast an Eldrazi creature spell with mana value 7 or greater, you may exile CARDNAME from your graveyard. If you do, CARDNAME deals 5 damage to each creature. -SVar:DBDamageAll:AB$ DamageAll | Cost$ ExileFromGrave<1/Card.Self> | ValidCards$ Creature | NumDmg$ 5 | ValidDescription$ each creature. +SVar:DBDamageAll:AB$ DamageAll | Cost$ ExileFromGrave<1/CARDNAME> | ValidCards$ Creature | NumDmg$ 5 | ValidDescription$ each creature. Oracle:Devoid (This card has no color.)\nKozilek's Return deals 2 damage to each creature.\nWhenever you cast an Eldrazi creature spell with mana value 7 or greater, you may exile Kozilek's Return from your graveyard. If you do, Kozilek's Return deals 5 damage to each creature. diff --git a/forge-gui/res/cardsfolder/r/rooting_kavu.txt b/forge-gui/res/cardsfolder/r/rooting_kavu.txt index c2fbfba6017..3819999ee8a 100644 --- a/forge-gui/res/cardsfolder/r/rooting_kavu.txt +++ b/forge-gui/res/cardsfolder/r/rooting_kavu.txt @@ -3,6 +3,5 @@ ManaCost:2 G G Types:Creature Kavu PT:4/3 T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ TrigExile | OptionalDecider$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, you may exile it. If you do, shuffle all creature cards from your graveyard into your library. -SVar:TrigExile:DB$ ChangeZone | Defined$ TriggeredNewCardLKICopy | Origin$ Graveyard | Destination$ Exile | SubAbility$ DBShuffle -SVar:DBShuffle:DB$ ChangeZoneAll | ChangeType$ Creature.YouCtrl | Origin$ Graveyard | Destination$ Library | Shuffle$ True +SVar:TrigExile:AB$ ChangeZoneAll | Cost$ ExileAnyGrave<1/Card.TriggeredNewCard> | ChangeType$ Creature.YouCtrl | Origin$ Graveyard | Destination$ Library | Shuffle$ True Oracle:When Rooting Kavu dies, you may exile it. If you do, shuffle all creature cards from your graveyard into your library. diff --git a/forge-gui/res/cardsfolder/t/timothar_baron_of_bats.txt b/forge-gui/res/cardsfolder/t/timothar_baron_of_bats.txt index c77b7f40f12..8ebdd33e1ff 100644 --- a/forge-gui/res/cardsfolder/t/timothar_baron_of_bats.txt +++ b/forge-gui/res/cardsfolder/t/timothar_baron_of_bats.txt @@ -4,7 +4,7 @@ Types:Legendary Creature Vampire Noble PT:4/4 K:Ward:Discard<1/Card> T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Vampire.Other+nonToken+YouCtrl | TriggerZones$ Battlefield | Execute$ TrigToken | TriggerDescription$ Whenever another nontoken Vampire you control dies, you may pay {1} and exile it. If you do, create a 1/1 black Bat creature token with flying. It gains "When this creature deals combat damage to a player, sacrifice it and return the exiled card to the battlefield tapped." -SVar:TrigToken:AB$ Token | Cost$ 1 ExileAnyGrave<1/Card.TriggeredNewCard/the Vampire card> | TokenRemembered$ ExiledCards | TokenScript$ b_1_1_bat_flying | ImprintTokens$ True | SubAbility$ DBAnimate +SVar:TrigToken:AB$ Token | Cost$ 1 ExileAnyGrave<1/Card.TriggeredNewCard> | TokenRemembered$ ExiledCards | TokenScript$ b_1_1_bat_flying | ImprintTokens$ True | SubAbility$ DBAnimate SVar:DBAnimate:DB$ Animate | Defined$ Imprinted | Duration$ Permanent | Triggers$ CDTrigger SVar:CDTrigger:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | CombatDamage$ True | Execute$ TrigSac | TriggerZones$ Battlefield | TriggerDescription$ When this creature deals combat damage to a player, sacrifice it and return the exiled card to the battlefield tapped. SVar:TrigSac:DB$ Sacrifice | SubAbility$ DBReturn diff --git a/forge-gui/res/cardsfolder/t/tyvar_jubilant_brawler.txt b/forge-gui/res/cardsfolder/t/tyvar_jubilant_brawler.txt index 58afcd1bf85..d2377628c35 100644 --- a/forge-gui/res/cardsfolder/t/tyvar_jubilant_brawler.txt +++ b/forge-gui/res/cardsfolder/t/tyvar_jubilant_brawler.txt @@ -4,7 +4,7 @@ Types:Legendary Planeswalker Tyvar Loyalty:3 S:Mode$ ActivateAbilityAsIfHaste | ValidCard$ Creature.YouCtrl | Description$ You may activate abilities of creatures you control as though those creatures had haste. A:AB$ Untap | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TargetMin$ 0 | TargetMax$ 1 | SpellDescription$ Untap up to one target creature. -A:AB$ Mill | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 3 | Defined$ You | SubAbility$ DBChange | SpellDescription$ Mill three cards, then you may return a creature card with mana value 2 or less from your graveyard to the battlefield. +A:AB$ Mill | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | NumCards$ 3 | Defined$ You | SubAbility$ DBChange | SpellDescription$ Mill three cards, then you may return a creature card with mana value 2 or less from your graveyard to the battlefield. SVar:DBChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | SelectPrompt$ Choose a creature card with mana value 2 or less | Hidden$ True | ChangeType$ Creature.YouOwn+cmcLE2 DeckHas:Ability$Graveyard|Mill DeckHints:Ability$Graveyard diff --git a/forge-gui/res/cardsfolder/upcoming/mount_doom.txt b/forge-gui/res/cardsfolder/upcoming/mount_doom.txt new file mode 100644 index 00000000000..1a9924c7ce7 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/mount_doom.txt @@ -0,0 +1,9 @@ +Name:Mount Doom +ManaCost:no cost +Types:Legendary Land +A:AB$ Mana | Cost$ T PayLife<1> | Produced$ Combo B R | SpellDescription$ Add {B} or {R}. +A:AB$ DealDamage | Cost$ 1 B R T | Defined$ Opponent | NumDmg$ 1 | SpellDescription$ CARDNAME deals 1 damage to each opponent. +A:AB$ ChooseCard | Cost$ 5 B R T Sac<1/CARDNAME> Sac<1/Artifact.Legendary/legendary artifact> | Defined$ You | Amount$ 2 | Choices$ Creature | ChoiceTitle$ Choose up to two creatures | SorcerySpeed$ True | AILogic$ Duneblast | SubAbility$ DBDestroyAll | StackDescription$ {p:You} chooses up to two creatures. Destroy the rest. | SpellDescription$ Choose up to two creatures, then destroy the rest. Activate only as a sorcery. +SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Creature.nonChosenCard | StackDescription$ None +DeckNeeds:Type$Legendary.Artifact +Oracle:{T}, Pay 1 life: Add {B} or {R}.\n{1}{B}{R}, {T}: Mount Doom deals 1 damage to each opponent.\n{5}{B}{R}, {T}, Sacrifice Mount Doom and a legendary artifact: Choose up to two creatures, then destroy the rest. Activate only as a sorcery. diff --git a/forge-gui/res/cardsfolder/upcoming/reprieve.txt b/forge-gui/res/cardsfolder/upcoming/reprieve.txt new file mode 100644 index 00000000000..0589dd77e1b --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/reprieve.txt @@ -0,0 +1,6 @@ +Name:Reprieve +ManaCost:1 W +Types:Instant +A:SP$ ChangeZone | ValidTgts$ Card.inZoneStack | TgtZone$ Stack | Origin$ Stack | Fizzle$ True | Destination$ Hand | SubAbility$ DBDraw | SpellDescription$ Return target spell to its owner's hand. +SVar:DBDraw:DB$ Draw | NumCards$ 1 +Oracle:Return target spell to its owner's hand.\nDraw a card. diff --git a/forge-gui/res/cardsfolder/upcoming/you_cannot_pass!.txt b/forge-gui/res/cardsfolder/upcoming/you_cannot_pass!.txt new file mode 100644 index 00000000000..976fe3979ad --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/you_cannot_pass!.txt @@ -0,0 +1,6 @@ +Name:You Cannot Pass! +ManaCost:W +Types:Instant +A:SP$ Destroy | ValidTgts$ Creature.blockedValidThisTurn Creature.Legendary,Creature.blockedByValidThisTurn Creature.Legendary | TgtPrompt$ Select target creature that blocked or was blocked by a legendary creature this turn | SpellDescription$ Destroy target creature that blocked or was blocked by a legendary creature this turn. +DeckNeeds:Type$Legendary.Creature +Oracle:Destroy target creature that blocked or was blocked by a legendary creature this turn. diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 165c10c5f92..69f0dcaf8b8 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -269,9 +269,9 @@ public class HumanCostDecision extends CostDecisionMakerBase { return inp.hasCancelled() ? null : PaymentDecision.card(inp.getSelected()); } - if (cost.from == ZoneType.Library) { return exileFromTop(cost, ability, player, c); } - if (fromTopGrave) { return exileFromTopGraveType(ability, c, list); } - if (cost.zoneRestriction != 0) { return exileFromMiscZone(cost, ability, c, list); } + if (cost.from == ZoneType.Library) { return exileFromTop(cost, c); } + if (fromTopGrave) { return exileFromTopGraveType(c, list); } + if (cost.zoneRestriction != 0) { return exileFromMiscZone(cost, c, list); } final FCollectionView players = game.getPlayers(); final List payableZone = new ArrayList<>(); @@ -363,22 +363,27 @@ public class HumanCostDecision extends CostDecisionMakerBase { return PaymentDecision.spellabilities(exiled); } - private PaymentDecision exileFromTop(final CostExile cost, final SpellAbility sa, final Player player, final int nNeeded) { + private PaymentDecision exileFromTop(final CostExile cost, final int nNeeded) { final CardCollectionView list = player.getCardsIn(ZoneType.Library, nNeeded); - - if (list.size() > nNeeded || !confirmAction(cost, Localizer.getInstance().getMessage("lblExileNCardFromYourTopLibraryConfirm"))) { + if (!confirmAction(cost, Localizer.getInstance().getMessage("lblExileNCardFromYourTopLibraryConfirm"))) { return null; } return PaymentDecision.card(list); } - private PaymentDecision exileFromMiscZone(final CostExile cost, final SpellAbility sa, final int nNeeded, final CardCollection typeList) { - if (typeList.size() < nNeeded) { return null; } + private PaymentDecision exileFromMiscZone(final CostExile cost, final int nNeeded, final CardCollection typeList) { + // when it's always a single triggered card getting exiled don't act like it might be different by offering the zone for choice + if (cost.zoneRestriction == -1 && ability.isTrigger() && nNeeded == 1 && typeList.size() == 1) { + if (confirmAction(cost, Localizer.getInstance().getMessage("lblExileConfirm", CardTranslation.getTranslatedName(typeList.getFirst().getName())))) { + return PaymentDecision.card(typeList.getFirst()); + } + return null; + } final List origin = Lists.newArrayList(cost.from); final CardCollection exiled = new CardCollection(); - final List chosen = controller.chooseCardsForZoneChange(ZoneType.Exile, origin, sa, typeList, mandatory ? nNeeded : 0, + final List chosen = controller.chooseCardsForZoneChange(ZoneType.Exile, origin, ability, typeList, mandatory ? nNeeded : 0, nNeeded, null, cost.toString(), null); exiled.addAll(chosen); @@ -388,9 +393,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { return PaymentDecision.card(exiled); } - private PaymentDecision exileFromTopGraveType(final SpellAbility sa, final int nNeeded, final CardCollection typeList) { - if (typeList.size() < nNeeded) { return null; } - + private PaymentDecision exileFromTopGraveType(final int nNeeded, final CardCollection typeList) { Collections.reverse(typeList); return PaymentDecision.card(Iterables.limit(typeList, nNeeded)); } diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index 31566a9ebdd..8adf2994cb7 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -1,13 +1,10 @@ package forge.player; - import java.util.ArrayList; import java.util.List; import forge.ImageKeys; import forge.game.cost.*; -import forge.game.spellability.SpellAbilityStackInstance; -import org.apache.commons.lang3.StringUtils; import com.google.common.collect.Iterables; @@ -198,24 +195,6 @@ public class HumanPlay { req.playAbility(!useOldTargets, false, true); } - private static int getAmountFromPart(CostPart part, Card source, SpellAbility sourceAbility) { - String amountString = part.getAmount(); - return StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : AbilityUtils.calculateAmount(source, amountString, sourceAbility); - } - - /** - * TODO: Write javadoc for this method. - * @param part - * @param source - * @param sourceAbility - * @return - */ - private static int getAmountFromPartX(CostPart part, Card source, SpellAbility sourceAbility) { - String amountString = part.getAmount(); - // Probably should just be - - return AbilityUtils.calculateAmount(source, amountString, sourceAbility); - } - /** *

* payCostDuringAbilityResolve. @@ -282,6 +261,7 @@ public class HumanPlay { || part instanceof CostRollDice || part instanceof CostDamage || part instanceof CostEnlist + || part instanceof CostExileFromStack || part instanceof CostPutCounter || part instanceof CostRemoveCounter || part instanceof CostRemoveAnyCounter @@ -327,7 +307,7 @@ public class HumanPlay { list = new CardCollection(p.getCardsIn(from)); } list = CardLists.getValidCards(list, part.getType().split(";"), p, source, sourceAbility); - final int nNeeded = getAmountFromPart(part, source, sourceAbility); + final int nNeeded = part.getAbilityAmount(sourceAbility); if (list.size() < nNeeded) { return false; } @@ -357,53 +337,6 @@ public class HumanPlay { } } } - else if (part instanceof CostExileFromStack) { - CostExileFromStack costExile = (CostExileFromStack) part; - - final List saList = new ArrayList<>(); - final List descList = new ArrayList<>(); - - for (final SpellAbilityStackInstance si : p.getGame().getStack()) { - final Card stC = si.getSourceCard(); - final SpellAbility stSA = si.getSpellAbility(true).getRootAbility(); - if (stC.isValid(part.getType().split(";"), p, source, sourceAbility) && stSA.isSpell()) { - saList.add(stSA); - if (stC.isCopiedSpell()) { - descList.add(stSA.getStackDescription() + " (Copied Spell)"); - } else { - descList.add(stSA.getStackDescription()); - } - } - } - - List payList = new ArrayList<>(); - if (part.getType().equals("All")) { - payList.addAll(saList); - } else { - final int c = getAmountFromPart(part, source, sourceAbility); - - if (saList.size() < c) { - return false; - } - - for (int i = 0; i < c; i++) { - //Have to use the stack descriptions here because some copied spells have no description otherwise - final String o = controller.getGui().oneOrNone(Localizer.getInstance().getMessage("lblExileFromStack"), descList); - - if (o == null) { - return false; - } - final SpellAbility toExile = saList.get(descList.indexOf(o)); - - saList.remove(toExile); - descList.remove(o); - - payList.add(toExile); - } - } - - costExile.payAsDecided(p, PaymentDecision.spellabilities(payList), sourceAbility, hcd.isEffect()); - } else if (part instanceof CostPutCardToLib) { int amount = Integer.parseInt(part.getAmount()); final ZoneType from = ((CostPutCardToLib) part).getFrom(); @@ -466,12 +399,12 @@ public class HumanPlay { } else if (part instanceof CostReturn) { CardCollectionView list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source, sourceAbility); - int amount = getAmountFromPartX(part, source, sourceAbility); + int amount = part.getAbilityAmount(sourceAbility); boolean hasPaid = payCostPart(controller, p, sourceAbility, hcd.isEffect(), (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReturnToHand") + orString); if (!hasPaid) { return false; } } else if (part instanceof CostDiscard) { - int amount = getAmountFromPartX(part, source, sourceAbility); + int amount = part.getAbilityAmount(sourceAbility); if ("Hand".equals(part.getType())) { if (!p.getController().confirmPayment(part, Localizer.getInstance().getMessage("lblDoYouWantDiscardYourHand"), sourceAbility)) { return false; @@ -493,14 +426,14 @@ public class HumanPlay { else if (part instanceof CostReveal) { CostReveal costReveal = (CostReveal) part; CardCollectionView list = CardLists.getValidCards(p.getCardsIn(costReveal.getRevealFrom()), part.getType(), p, source, sourceAbility); - int amount = getAmountFromPartX(part, source, sourceAbility); + int amount = part.getAbilityAmount(sourceAbility); boolean hasPaid = payCostPart(controller, p, sourceAbility, hcd.isEffect(), (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblReveal") + orString); if (!hasPaid) { return false; } } else if (part instanceof CostTapType) { CardCollectionView list = CardLists.getValidCards(p.getCardsIn(ZoneType.Battlefield), part.getType(), p, source, sourceAbility); list = CardLists.filter(list, Presets.UNTAPPED); - int amount = getAmountFromPartX(part, source, sourceAbility); + int amount = part.getAbilityAmount(sourceAbility); boolean hasPaid = payCostPart(controller, p, sourceAbility, hcd.isEffect(), (CostPartWithList)part, amount, list, Localizer.getInstance().getMessage("lblTap") + orString); if (!hasPaid) { return false; } } @@ -511,7 +444,7 @@ public class HumanPlay { } else if (part instanceof CostPayEnergy) { CounterType counterType = CounterType.get(CounterEnumType.ENERGY); - int amount = getAmountFromPartX(part, source, sourceAbility); + int amount = part.getAbilityAmount(sourceAbility); if (!mandatory && !p.getController().confirmPayment(part, Localizer.getInstance().getMessage("lblDoYouWantSpendNTargetTypeCounter", String.valueOf(amount), counterType.getName()), sourceAbility)) { return false; @@ -525,7 +458,7 @@ public class HumanPlay { else if (part instanceof CostPayShards) { CounterType counterType = CounterType.get(CounterEnumType.MANASHARDS); - int amount = getAmountFromPartX(part, source, sourceAbility); + int amount = part.getAbilityAmount(sourceAbility); if (!mandatory && !p.getController().confirmPayment(part, Localizer.getInstance().getMessage("lblDoYouWantSpendNTargetTypeCounter", String.valueOf(amount), counterType.getName()), sourceAbility)) { return false; diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 0389e02ccaf..01c3339e071 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -1850,8 +1850,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } @Override - public ReplacementEffect chooseSingleReplacementEffect(final String prompt, - final List possibleReplacers) { + public ReplacementEffect chooseSingleReplacementEffect(final String prompt, final List possibleReplacers) { final ReplacementEffect first = possibleReplacers.get(0); if (possibleReplacers.size() == 1) { return first;