From 8de3157cbedf7fc3a424a1fe2bc8d23cf38c31e7 Mon Sep 17 00:00:00 2001 From: drdev Date: Sun, 6 Apr 2014 20:23:03 +0000 Subject: [PATCH] Add quest classes to mobile game --- .gitattributes | 49 ++ forge-m-base/src/forge/deck/DeckProxy.java | 6 +- forge-m-base/src/forge/deck/DeckgenUtil.java | 12 +- forge-m-base/src/forge/deck/FDeckChooser.java | 55 +- .../forge/itemmanager/views/ItemListView.java | 2 +- forge-m-base/src/forge/model/FModel.java | 29 +- .../src/forge/quest/BoosterUtils.java | 324 ++++++++ .../src/forge/quest/IQuestRewardCard.java | 22 + .../src/forge/quest/QuestController.java | 505 +++++++++++++ .../src/forge/quest/QuestDeckMap.java | 59 ++ forge-m-base/src/forge/quest/QuestEvent.java | 131 ++++ .../src/forge/quest/QuestEventChallenge.java | 315 ++++++++ .../src/forge/quest/QuestEventDifficulty.java | 35 + .../src/forge/quest/QuestEventDuel.java | 37 + .../forge/quest/QuestEventDuelManager.java | 158 ++++ forge-m-base/src/forge/quest/QuestMode.java | 34 + .../src/forge/quest/QuestRewardCard.java | 123 +++ .../forge/quest/QuestRewardCardChooser.java | 125 ++++ .../forge/quest/QuestRewardCardDuplicate.java | 72 ++ .../forge/quest/QuestRewardCardFiltered.java | 71 ++ forge-m-base/src/forge/quest/QuestUtil.java | 174 +++++ .../src/forge/quest/QuestUtilCards.java | 708 ++++++++++++++++++ .../src/forge/quest/QuestUtilUnlockSets.java | 202 +++++ forge-m-base/src/forge/quest/QuestWorld.java | 184 +++++ forge-m-base/src/forge/quest/SellRules.java | 88 +++ .../forge/quest/StartingPoolPreferences.java | 61 ++ .../src/forge/quest/StartingPoolType.java | 25 + .../forge/quest/bazaar/IQuestBazaarItem.java | 89 +++ .../quest/bazaar/QuestBazaarManager.java | 185 +++++ .../forge/quest/bazaar/QuestItemBasic.java | 194 +++++ .../quest/bazaar/QuestItemCharmOfVigor.java | 47 ++ .../forge/quest/bazaar/QuestItemElixir.java | 53 ++ .../forge/quest/bazaar/QuestItemEstates.java | 54 ++ .../quest/bazaar/QuestItemPoundFlesh.java | 66 ++ .../src/forge/quest/bazaar/QuestItemType.java | 129 ++++ .../forge/quest/bazaar/QuestItemZeppelin.java | 47 ++ .../quest/bazaar/QuestPetController.java | 277 +++++++ .../src/forge/quest/bazaar/QuestPetStats.java | 71 ++ .../forge/quest/bazaar/QuestPetStorage.java | 134 ++++ .../quest/bazaar/QuestStallDefinition.java | 115 +++ .../src/forge/quest/bazaar/package-info.java | 3 + .../src/forge/quest/data/GameFormatQuest.java | 153 ++++ .../forge/quest/data/QuestAchievements.java | 176 +++++ .../src/forge/quest/data/QuestAssets.java | 266 +++++++ .../src/forge/quest/data/QuestData.java | 217 ++++++ .../forge/quest/data/QuestItemCondition.java | 27 + .../forge/quest/data/QuestPreferences.java | 261 +++++++ .../src/forge/quest/data/package-info.java | 3 + .../forge/quest/io/QuestChallengeReader.java | 73 ++ .../src/forge/quest/io/QuestDataIO.java | 673 +++++++++++++++++ .../src/forge/quest/io/QuestDuelReader.java | 47 ++ .../src/forge/quest/io/ReadPriceList.java | 132 ++++ .../src/forge/quest/io/package-info.java | 3 + .../src/forge/quest/package-info.java | 3 + .../constructed/ConstructedScreen.java | 263 ++++--- forge-m-base/src/forge/toolbox/FComboBox.java | 16 +- forge-m-base/src/forge/toolbox/FList.java | 22 +- forge-m-base/src/forge/utils/XmlUtil.java | 54 ++ 58 files changed, 7319 insertions(+), 140 deletions(-) create mode 100644 forge-m-base/src/forge/quest/BoosterUtils.java create mode 100644 forge-m-base/src/forge/quest/IQuestRewardCard.java create mode 100644 forge-m-base/src/forge/quest/QuestController.java create mode 100644 forge-m-base/src/forge/quest/QuestDeckMap.java create mode 100644 forge-m-base/src/forge/quest/QuestEvent.java create mode 100644 forge-m-base/src/forge/quest/QuestEventChallenge.java create mode 100644 forge-m-base/src/forge/quest/QuestEventDifficulty.java create mode 100644 forge-m-base/src/forge/quest/QuestEventDuel.java create mode 100644 forge-m-base/src/forge/quest/QuestEventDuelManager.java create mode 100644 forge-m-base/src/forge/quest/QuestMode.java create mode 100644 forge-m-base/src/forge/quest/QuestRewardCard.java create mode 100644 forge-m-base/src/forge/quest/QuestRewardCardChooser.java create mode 100644 forge-m-base/src/forge/quest/QuestRewardCardDuplicate.java create mode 100644 forge-m-base/src/forge/quest/QuestRewardCardFiltered.java create mode 100644 forge-m-base/src/forge/quest/QuestUtil.java create mode 100644 forge-m-base/src/forge/quest/QuestUtilCards.java create mode 100644 forge-m-base/src/forge/quest/QuestUtilUnlockSets.java create mode 100644 forge-m-base/src/forge/quest/QuestWorld.java create mode 100644 forge-m-base/src/forge/quest/SellRules.java create mode 100644 forge-m-base/src/forge/quest/StartingPoolPreferences.java create mode 100644 forge-m-base/src/forge/quest/StartingPoolType.java create mode 100644 forge-m-base/src/forge/quest/bazaar/IQuestBazaarItem.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestBazaarManager.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestItemBasic.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestItemCharmOfVigor.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestItemElixir.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestItemEstates.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestItemPoundFlesh.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestItemType.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestItemZeppelin.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestPetController.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestPetStats.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestPetStorage.java create mode 100644 forge-m-base/src/forge/quest/bazaar/QuestStallDefinition.java create mode 100644 forge-m-base/src/forge/quest/bazaar/package-info.java create mode 100644 forge-m-base/src/forge/quest/data/GameFormatQuest.java create mode 100644 forge-m-base/src/forge/quest/data/QuestAchievements.java create mode 100644 forge-m-base/src/forge/quest/data/QuestAssets.java create mode 100644 forge-m-base/src/forge/quest/data/QuestData.java create mode 100644 forge-m-base/src/forge/quest/data/QuestItemCondition.java create mode 100644 forge-m-base/src/forge/quest/data/QuestPreferences.java create mode 100644 forge-m-base/src/forge/quest/data/package-info.java create mode 100644 forge-m-base/src/forge/quest/io/QuestChallengeReader.java create mode 100644 forge-m-base/src/forge/quest/io/QuestDataIO.java create mode 100644 forge-m-base/src/forge/quest/io/QuestDuelReader.java create mode 100644 forge-m-base/src/forge/quest/io/ReadPriceList.java create mode 100644 forge-m-base/src/forge/quest/io/package-info.java create mode 100644 forge-m-base/src/forge/quest/package-info.java create mode 100644 forge-m-base/src/forge/utils/XmlUtil.java diff --git a/.gitattributes b/.gitattributes index 2e71958a8a2..e3c1ceb5795 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16224,6 +16224,54 @@ forge-m-base/src/forge/player/HumanPlaySpellAbility.java -text forge-m-base/src/forge/player/LobbyPlayerHuman.java -text forge-m-base/src/forge/player/PlayerControllerHuman.java -text forge-m-base/src/forge/player/TargetSelection.java -text +forge-m-base/src/forge/quest/BoosterUtils.java -text +forge-m-base/src/forge/quest/IQuestRewardCard.java -text +forge-m-base/src/forge/quest/QuestController.java -text +forge-m-base/src/forge/quest/QuestDeckMap.java -text +forge-m-base/src/forge/quest/QuestEvent.java -text +forge-m-base/src/forge/quest/QuestEventChallenge.java -text +forge-m-base/src/forge/quest/QuestEventDifficulty.java -text +forge-m-base/src/forge/quest/QuestEventDuel.java -text +forge-m-base/src/forge/quest/QuestEventDuelManager.java -text +forge-m-base/src/forge/quest/QuestMode.java -text +forge-m-base/src/forge/quest/QuestRewardCard.java -text +forge-m-base/src/forge/quest/QuestRewardCardChooser.java -text +forge-m-base/src/forge/quest/QuestRewardCardDuplicate.java -text +forge-m-base/src/forge/quest/QuestRewardCardFiltered.java -text +forge-m-base/src/forge/quest/QuestUtil.java -text +forge-m-base/src/forge/quest/QuestUtilCards.java -text +forge-m-base/src/forge/quest/QuestUtilUnlockSets.java -text +forge-m-base/src/forge/quest/QuestWorld.java -text +forge-m-base/src/forge/quest/SellRules.java -text +forge-m-base/src/forge/quest/StartingPoolPreferences.java -text +forge-m-base/src/forge/quest/StartingPoolType.java -text +forge-m-base/src/forge/quest/bazaar/IQuestBazaarItem.java -text +forge-m-base/src/forge/quest/bazaar/QuestBazaarManager.java -text +forge-m-base/src/forge/quest/bazaar/QuestItemBasic.java -text +forge-m-base/src/forge/quest/bazaar/QuestItemCharmOfVigor.java -text +forge-m-base/src/forge/quest/bazaar/QuestItemElixir.java -text +forge-m-base/src/forge/quest/bazaar/QuestItemEstates.java -text +forge-m-base/src/forge/quest/bazaar/QuestItemPoundFlesh.java -text +forge-m-base/src/forge/quest/bazaar/QuestItemType.java -text +forge-m-base/src/forge/quest/bazaar/QuestItemZeppelin.java -text +forge-m-base/src/forge/quest/bazaar/QuestPetController.java -text +forge-m-base/src/forge/quest/bazaar/QuestPetStats.java -text +forge-m-base/src/forge/quest/bazaar/QuestPetStorage.java -text +forge-m-base/src/forge/quest/bazaar/QuestStallDefinition.java -text +forge-m-base/src/forge/quest/bazaar/package-info.java -text +forge-m-base/src/forge/quest/data/GameFormatQuest.java -text +forge-m-base/src/forge/quest/data/QuestAchievements.java -text +forge-m-base/src/forge/quest/data/QuestAssets.java -text +forge-m-base/src/forge/quest/data/QuestData.java -text +forge-m-base/src/forge/quest/data/QuestItemCondition.java -text +forge-m-base/src/forge/quest/data/QuestPreferences.java -text +forge-m-base/src/forge/quest/data/package-info.java -text +forge-m-base/src/forge/quest/io/QuestChallengeReader.java -text +forge-m-base/src/forge/quest/io/QuestDataIO.java -text +forge-m-base/src/forge/quest/io/QuestDuelReader.java -text +forge-m-base/src/forge/quest/io/ReadPriceList.java -text +forge-m-base/src/forge/quest/io/package-info.java -text +forge-m-base/src/forge/quest/package-info.java -text forge-m-base/src/forge/screens/FScreen.java -text forge-m-base/src/forge/screens/LaunchScreen.java -text forge-m-base/src/forge/screens/SplashScreen.java -text @@ -16322,6 +16370,7 @@ forge-m-base/src/forge/utils/PhysicsObject.java -text forge-m-base/src/forge/utils/Preferences.java -text forge-m-base/src/forge/utils/PreferencesStore.java -text forge-m-base/src/forge/utils/Utils.java -text +forge-m-base/src/forge/utils/XmlUtil.java -text forge-m-desktop/.classpath -text forge-m-desktop/.project -text forge-m-desktop/.settings/org.eclipse.jdt.core.prefs -text diff --git a/forge-m-base/src/forge/deck/DeckProxy.java b/forge-m-base/src/forge/deck/DeckProxy.java index faa350947d9..599e3ae8a07 100644 --- a/forge-m-base/src/forge/deck/DeckProxy.java +++ b/forge-m-base/src/forge/deck/DeckProxy.java @@ -19,6 +19,8 @@ import forge.item.InventoryItem; import forge.item.PaperCard; import forge.item.PreconDeck; import forge.model.FModel; +import forge.quest.QuestController; +import forge.quest.QuestEvent; import forge.util.IHasName; import forge.util.storage.IStorage; import forge.util.storage.StorageImmediatelySerialized; @@ -324,7 +326,7 @@ public class DeckProxy implements InventoryItem { return decks; } - /* public static Iterable getAllQuestEventAndChallenges() { + public static Iterable getAllQuestEventAndChallenges() { ArrayList decks = new ArrayList(); QuestController quest = FModel.getQuest(); for (QuestEvent e : quest.getDuelsManager().getAllDuels()) { @@ -334,7 +336,7 @@ public class DeckProxy implements InventoryItem { decks.add(new DeckProxy(e.getEventDeck(), "Quest Event", null, null)); } return decks; - }*/ + } @SuppressWarnings("unchecked") public static Iterable getAllSealedDecks(IStorage sealed) { diff --git a/forge-m-base/src/forge/deck/DeckgenUtil.java b/forge-m-base/src/forge/deck/DeckgenUtil.java index d5eeb9d56ab..d77790e4de5 100644 --- a/forge-m-base/src/forge/deck/DeckgenUtil.java +++ b/forge-m-base/src/forge/deck/DeckgenUtil.java @@ -15,6 +15,10 @@ import forge.deck.generation.*; import forge.item.PaperCard; import forge.itemmanager.DeckManager; import forge.model.FModel; +import forge.quest.QuestController; +import forge.quest.QuestEvent; +import forge.quest.QuestEventChallenge; +import forge.quest.QuestEventDuel; import forge.toolbox.FOptionPane; import forge.util.Aggregates; import forge.util.Lang; @@ -82,7 +86,7 @@ public class DeckgenUtil { return deck; } - /*public static QuestEvent getQuestEvent(final String name) { + public static QuestEvent getQuestEvent(final String name) { QuestController qCtrl = FModel.getQuest(); for (QuestEventChallenge challenge : qCtrl.getChallenges()) { if (challenge.getTitle().equals(name)) { @@ -94,7 +98,7 @@ public class DeckgenUtil { @Override public boolean apply(QuestEventDuel in) { return in.getName().equals(name); } }); return duel; - }*/ + } /** @return {@link forge.deck.Deck} */ public static Deck getRandomColorDeck(boolean forAi) { @@ -116,7 +120,7 @@ public class DeckgenUtil { return allDecks.get(name); } - /*public static Deck getRandomQuestDeck() { + public static Deck getRandomQuestDeck() { final List allQuestDecks = new ArrayList(); QuestController qCtrl = FModel.getQuest(); @@ -130,7 +134,7 @@ public class DeckgenUtil { final int rand = (int) (Math.floor(Math.random() * allQuestDecks.size())); return allQuestDecks.get(rand); - }*/ + } public static void randomSelectColors(final DeckManager deckManager) { final int size = deckManager.getItemCount(); diff --git a/forge-m-base/src/forge/deck/FDeckChooser.java b/forge-m-base/src/forge/deck/FDeckChooser.java index f08c73f6d18..2a4fb4084ea 100644 --- a/forge-m-base/src/forge/deck/FDeckChooser.java +++ b/forge-m-base/src/forge/deck/FDeckChooser.java @@ -6,6 +6,10 @@ import forge.game.player.RegisteredPlayer; import forge.itemmanager.DeckManager; import forge.itemmanager.ItemManagerConfig; import forge.model.FModel; +import forge.quest.QuestController; +import forge.quest.QuestEvent; +import forge.quest.QuestEventChallenge; +import forge.quest.QuestUtil; import forge.screens.FScreen; import forge.toolbox.FComboBox; import forge.toolbox.FEvent; @@ -57,6 +61,25 @@ public class FDeckChooser extends FScreen { public void initialize(FPref savedStateSetting, DeckType defaultDeckType) { stateSetting = savedStateSetting; selectedDeckType = defaultDeckType; + + if (decksComboBox == null) { //initialize components with delayed initialization the first time this is populated + decksComboBox = new FComboBox(); + restoreSavedState(); + decksComboBox.setChangedHandler(new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + refreshDecksList(decksComboBox.getSelectedItem(), false, e); + } + }); + } + else { + clear(); + restoreSavedState(); //ensure decks refreshed and state restored in case any deleted or added since last loaded + } + add(decksComboBox); + add(lstDecks); + add(btnViewDeck); + add(btnRandom); } public DeckType getSelectedDeckType() { return selectedDeckType; } @@ -125,7 +148,6 @@ public class FDeckChooser extends FScreen { public boolean isGeneratedDeck() { return true; } - } private void updateColors() { @@ -173,7 +195,7 @@ public class FDeckChooser extends FScreen { private void updatePrecons() { lstDecks.setAllowMultipleSelections(false); - //lstDecks.setPool(DeckProxy.getAllPreconstructedDecks(QuestController.getPrecons())); + lstDecks.setPool(DeckProxy.getAllPreconstructedDecks(QuestController.getPrecons())); lstDecks.setup(ItemManagerConfig.PRECON_DECKS); btnRandom.setText("Random Deck"); @@ -190,7 +212,7 @@ public class FDeckChooser extends FScreen { private void updateQuestEvents() { lstDecks.setAllowMultipleSelections(false); - //lstDecks.setPool(DeckProxy.getAllQuestEventAndChallenges()); + lstDecks.setPool(DeckProxy.getAllQuestEventAndChallenges()); lstDecks.setup(ItemManagerConfig.QUEST_EVENT_DECKS); btnRandom.setText("Random Deck"); @@ -215,39 +237,18 @@ public class FDeckChooser extends FScreen { // Special branch for quest events if (selectedDeckType == DeckType.QUEST_OPPONENT_DECK) { - /*QuestEvent event = DeckgenUtil.getQuestEvent(lstDecks.getSelectedItem().getName()); + QuestEvent event = DeckgenUtil.getQuestEvent(lstDecks.getSelectedItem().getName()); RegisteredPlayer result = new RegisteredPlayer(event.getEventDeck()); if (event instanceof QuestEventChallenge) { result.setStartingLife(((QuestEventChallenge) event).getAiLife()); } result.setCardsOnBattlefield(QuestUtil.getComputerStartingCards(event)); - return result;*/ + return result; } return new RegisteredPlayer(getDeck()); } - public void populate() { - if (decksComboBox == null) { //initialize components with delayed initialization the first time this is populated - decksComboBox = new FComboBox(); - restoreSavedState(); - decksComboBox.setChangedHandler(new FEventHandler() { - @Override - public void handleEvent(FEvent e) { - refreshDecksList(decksComboBox.getSelectedItem(), false, e); - } - }); - } - else { - clear(); - restoreSavedState(); //ensure decks refreshed and state restored in case any deleted or added since last loaded - } - add(decksComboBox); - add(lstDecks); - add(btnViewDeck); - add(btnRandom); - } - public final boolean isAi() { return isAi; } @@ -263,6 +264,8 @@ public class FDeckChooser extends FScreen { if (e == null) { decksComboBox.setSelectedItem(deckType); } + if (deckType == null) { return; } + lstDecks.setCaption(deckType.toString()); switch (deckType) { diff --git a/forge-m-base/src/forge/itemmanager/views/ItemListView.java b/forge-m-base/src/forge/itemmanager/views/ItemListView.java index 97cb1fca1bc..3021b962b3f 100644 --- a/forge-m-base/src/forge/itemmanager/views/ItemListView.java +++ b/forge-m-base/src/forge/itemmanager/views/ItemListView.java @@ -52,7 +52,7 @@ public final class ItemListView extends ItemView { private final ItemTable table = new ItemTable(); private final ItemTableModel tableModel; private boolean allowMultipleSelections; - private List selectedIndices; + private List selectedIndices = new ArrayList(); public ItemTableModel getTableModel() { return tableModel; diff --git a/forge-m-base/src/forge/model/FModel.java b/forge-m-base/src/forge/model/FModel.java index a59833527f3..61836e23308 100644 --- a/forge-m-base/src/forge/model/FModel.java +++ b/forge-m-base/src/forge/model/FModel.java @@ -25,6 +25,9 @@ import forge.game.GameFormat; import forge.game.card.CardUtil; import forge.guantlet.GauntletData; import forge.limited.GauntletMini; +import forge.quest.QuestController; +import forge.quest.QuestWorld; +import forge.quest.data.QuestPreferences; import forge.toolbox.FProgressBar; import forge.util.FileUtil; import forge.util.storage.IStorage; @@ -55,19 +58,19 @@ public class FModel { private static PrintStream oldSystemErr; private static OutputStream logFileStream; - //private final QuestPreferences questPreferences; + private static QuestPreferences questPreferences; private static ForgePreferences preferences; // Someone should take care of 2 gauntlets here private static GauntletData gauntletData; private static GauntletMini gauntlet; - //private static QuestController quest; + private static QuestController quest; private static CardCollections decks; private static IStorage blocks; private static IStorage fantasyBlocks; - //private static IStorage worlds; + private static IStorage worlds; private static GameFormat.Collection formats; public static void initialize(final FProgressBar progressBar) { @@ -142,28 +145,28 @@ public class FModel { catch (final Exception exn) { throw new RuntimeException(exn); } - + formats = new GameFormat.Collection(new GameFormat.Reader(new File(Constants.BLOCK_DATA_DIR + "formats.txt"))); blocks = new StorageBase("Block definitions", new CardBlock.Reader(Constants.BLOCK_DATA_DIR + "blocks.txt", magicDb.getEditions())); - //questPreferences = new QuestPreferences(); + questPreferences = new QuestPreferences(); gauntletData = new GauntletData(); fantasyBlocks = new StorageBase("Custom blocks", new CardBlock.Reader(Constants.BLOCK_DATA_DIR + "fantasyblocks.txt", magicDb.getEditions())); - //worlds = new StorageBase("Quest worlds", new QuestWorld.Reader(Constants.QUEST_WORLD_DIR + "worlds.txt")); + worlds = new StorageBase("Quest worlds", new QuestWorld.Reader(Constants.QUEST_WORLD_DIR + "worlds.txt")); loadDynamicGamedata(); progressBar.setDescription("Loading decks"); decks = new CardCollections(); - //quest = new QuestController(); + quest = new QuestController(); //preload AI profiles AiProfileUtil.loadAllProfiles(Constants.AI_PROFILE_DIR); } - /*public static QuestController getQuest() { + public static QuestController getQuest() { return quest; - }*/ + } private static boolean keywordsLoaded = false; @@ -282,9 +285,9 @@ public class FModel { return blocks; } - /*public static QuestPreferences getQuestPreferences() { + public static QuestPreferences getQuestPreferences() { return questPreferences; - }*/ + } public static GauntletData getGauntletData() { return gauntletData; @@ -305,9 +308,9 @@ public class FModel { return decks; } - /*public static IStorage getWorlds() { + public static IStorage getWorlds() { return worlds; - }*/ + } public static GameFormat.Collection getFormats() { return formats; diff --git a/forge-m-base/src/forge/quest/BoosterUtils.java b/forge-m-base/src/forge/quest/BoosterUtils.java new file mode 100644 index 00000000000..d8c68eb2bce --- /dev/null +++ b/forge-m-base/src/forge/quest/BoosterUtils.java @@ -0,0 +1,324 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import forge.card.CardRules; +import forge.card.CardRulesPredicates; +import forge.card.MagicColor; +import forge.card.PrintSheet; +import forge.item.*; +import forge.model.FModel; +import forge.quest.data.QuestPreferences.QPref; +import forge.util.Aggregates; +import forge.util.MyRandom; + +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +// The BoosterPack generates cards for the Card Pool in Quest Mode +/** + *

+ * QuestBoosterPack class. + *

+ * + * @author Forge + * @version $Id: BoosterUtils.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public final class BoosterUtils { + + /** + * Gets the quest starter deck. + * + * @param filter + * the filter + * @param numCommon + * the num common + * @param numUncommon + * the num uncommon + * @param numRare + * the num rare + * @param userPrefs + * the starting pool preferences + * @return the quest starter deck + */ + public static List getQuestStarterDeck(final Predicate filter, final int numCommon, + final int numUncommon, final int numRare, final StartingPoolPreferences userPrefs) { + + final ArrayList cards = new ArrayList(); + + // Each color should have around the same amount of monocolored cards + // There should be 3 Colorless cards for every 4 cards in a single color + // There should be 1 Multicolor card for every 4 cards in a single color + + final List> colorFilters = new ArrayList>(); + final boolean preferred = (userPrefs != null && userPrefs.getPreferredColor() != MagicColor.ALL_COLORS); + final boolean randomized = userPrefs != null && userPrefs.useRandomPool(); + final int colorBias = (preferred && !randomized) ? FModel.getQuestPreferences().getPrefInt(QPref.STARTING_POOL_COLOR_BIAS) : 0; + final int biasAdjustedCommons = (((colorBias * numCommon) / 25) > 0 ? numCommon - (colorBias * numCommon) / 25 : numCommon); + final int biasAdjustedUncommons = (((colorBias * numUncommon) / 25) > 0 ? numUncommon - (colorBias * numUncommon) / 25 : numUncommon); + final int biasAdjustedRares = (((colorBias * numRare) / 25) > 0 ? numRare - (colorBias * numRare) / 25 : numRare); + + if (!randomized) { + colorFilters.add(CardRulesPredicates.Presets.IS_MULTICOLOR); + + // extra filters of the preferred color if chosen + if (preferred) { + for (int i = 0; i < colorBias + (colorBias > 6 ? (2 * (colorBias - 6 + (colorBias / 10))) : 0); i++) { + colorFilters.add(CardRulesPredicates.isMonoColor(userPrefs.getPreferredColor())); + } + } + + for (int i = 0; i < (preferred ? 3 : 4); i++) { + if (i != 2) { + colorFilters.add(CardRulesPredicates.Presets.IS_COLORLESS); + } + + colorFilters.add(CardRulesPredicates.isMonoColor(MagicColor.WHITE)); + colorFilters.add(CardRulesPredicates.isMonoColor(MagicColor.RED)); + colorFilters.add(CardRulesPredicates.isMonoColor(MagicColor.BLUE)); + colorFilters.add(CardRulesPredicates.isMonoColor(MagicColor.BLACK)); + colorFilters.add(CardRulesPredicates.isMonoColor(MagicColor.GREEN)); + } + + } + + // This will save CPU time when sets are limited + final List cardpool = Lists.newArrayList(Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), filter)); + + final Predicate pCommon = IPaperCard.Predicates.Presets.IS_COMMON; + cards.addAll(BoosterUtils.generateDefinetlyColouredCards(cardpool, pCommon, biasAdjustedCommons, colorFilters)); + + final Predicate pUncommon = IPaperCard.Predicates.Presets.IS_UNCOMMON; + cards.addAll(BoosterUtils.generateDefinetlyColouredCards(cardpool, pUncommon, biasAdjustedUncommons, colorFilters)); + + int nRares = biasAdjustedRares, nMythics = 0; + final Predicate filterMythics = IPaperCard.Predicates.Presets.IS_MYTHIC_RARE; + final boolean haveMythics = Iterables.any(cardpool, filterMythics); + for (int iSlot = 0; haveMythics && (iSlot < numRare); iSlot++) { + if (MyRandom.getRandom().nextInt(10) < 1) { + // 10% chance of upgrading a Rare into a Mythic + nRares--; + nMythics++; + } + } + + final Predicate pRare = IPaperCard.Predicates.Presets.IS_RARE; + cards.addAll(BoosterUtils.generateDefinetlyColouredCards(cardpool, pRare, nRares, colorFilters)); + if (nMythics > 0) { + cards.addAll(BoosterUtils.generateDefinetlyColouredCards(cardpool, filterMythics, nMythics, colorFilters)); + } + return cards; + } + + /** + * Create the list of card names at random from the given pool. + * + * @param source + * an Iterable + * @param filter + * Predicate + * @param cntNeeded + * an int + * @param allowedColors + * a List> + * @return a list of card names + */ + private static ArrayList generateDefinetlyColouredCards(final Iterable source, + final Predicate filter, final int cntNeeded, final List> allowedColors) { + // If color is null, use colorOrder progression to grab cards + final ArrayList result = new ArrayList(); + + final int size = allowedColors == null ? 0 : allowedColors.size(); + Collections.shuffle(allowedColors); + + int cntMade = 0, iAttempt = 0; + + // This will prevent endless loop @ wh + int allowedMisses = (2 + size + 2) * cntNeeded; // lol, 2+2 is not magic + // constant! + + while ((cntMade < cntNeeded) && (allowedMisses > 0)) { + PaperCard card = null; + + if (size > 0) { + final Predicate color2 = allowedColors.get(iAttempt % size); + if (color2 != null) { + Predicate color2c = Predicates.compose(color2, PaperCard.FN_GET_RULES); + card = Aggregates.random(Iterables.filter(source, Predicates.and(filter, color2c))); + } + } + + if (card == null) { + // We can't decide on a color, so just pick a card. + card = Aggregates.random(Iterables.filter(source, filter)); + } + + if ((card != null) && !result.contains(card)) { + result.add(card); + cntMade++; + } else { + allowedMisses--; + } + iAttempt++; + } + + return result; + } + + + /** + * Parse a limitation for a reward or chosen card. + * @param input + * String, the limitation as text. + * @return Predicate the text parsed into a CardRules predicate. + * + */ + public static Predicate parseRulesLimitation(final String input) { + if (null == input || "random".equalsIgnoreCase(input)) { + return Predicates.alwaysTrue(); + } + + if (input.equalsIgnoreCase("black")) return CardRulesPredicates.Presets.IS_BLACK; + if (input.equalsIgnoreCase("blue")) return CardRulesPredicates.Presets.IS_BLUE; + if (input.equalsIgnoreCase("green")) return CardRulesPredicates.Presets.IS_GREEN; + if (input.equalsIgnoreCase("red")) return CardRulesPredicates.Presets.IS_RED; + if (input.equalsIgnoreCase("white")) return CardRulesPredicates.Presets.IS_WHITE; + if (input.equalsIgnoreCase("colorless")) return CardRulesPredicates.Presets.IS_COLORLESS; + if (input.equalsIgnoreCase("multicolor")) return CardRulesPredicates.Presets.IS_MULTICOLOR; + + if (input.equalsIgnoreCase("land")) return CardRulesPredicates.Presets.IS_LAND; + if (input.equalsIgnoreCase("creature")) return CardRulesPredicates.Presets.IS_CREATURE; + if (input.equalsIgnoreCase("artifact")) return CardRulesPredicates.Presets.IS_ARTIFACT; + if (input.equalsIgnoreCase("planeswalker")) return CardRulesPredicates.Presets.IS_PLANESWALKER; + if (input.equalsIgnoreCase("instant")) return CardRulesPredicates.Presets.IS_INSTANT; + if (input.equalsIgnoreCase("sorcery")) return CardRulesPredicates.Presets.IS_SORCERY; + if (input.equalsIgnoreCase("enchantment")) return CardRulesPredicates.Presets.IS_ENCHANTMENT; + + throw new IllegalArgumentException("No CardRules limitations could be parsed from: " + input); + } + /** + * parseReward - used internally to parse individual items in a challenge reward definition. + * @param s + * String, the reward to parse + * @return List + */ + private static List parseReward(final String s) { + + String[] temp = s.split(" "); + List rewards = new ArrayList(); + + // last word starts with 'rare' ignore case + if (temp.length > 1 && temp[temp.length - 1].regionMatches(true, 0, "rare", 0, 4)) { + // Type 1: 'n [color] rares' + final int qty = Integer.parseInt(temp[0]); + + List> preds = new ArrayList>(); + preds.add(IPaperCard.Predicates.Presets.IS_RARE_OR_MYTHIC); // Determine rarity + + if (temp.length > 2) { + Predicate cr = parseRulesLimitation(temp[1]); + if (Predicates.alwaysTrue() != (Object) cr) { // guava has a single instance for always-const predicates + preds.add(Predicates.compose(cr, PaperCard.FN_GET_RULES)); + } + } + + if (FModel.getQuest().getFormat() != null) { + preds.add(FModel.getQuest().getFormat().getFilterPrinted()); + } + + PrintSheet ps = new PrintSheet("Quest rewards"); + Predicate predicate = preds.size() == 1 ? preds.get(0) : Predicates.and(preds); + ps.addAll(Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), predicate)); + rewards.addAll(ps.random(qty, true)); + } else if (temp.length == 2 && temp[0].equalsIgnoreCase("duplicate") && temp[1].equalsIgnoreCase("card")) { + // Type 2: a duplicate card of the players choice + rewards.add(new QuestRewardCardDuplicate()); + } else if (temp.length >= 2 && temp[0].equalsIgnoreCase("chosen") && temp[1].equalsIgnoreCase("card")) { + // Type 3: a duplicate card of the players choice + rewards.add(new QuestRewardCardFiltered(temp)); + } else if (temp.length >= 3 && temp[0].equalsIgnoreCase("booster") && temp[1].equalsIgnoreCase("pack")) { + // Type 4: a predetermined extra booster pack + rewards.add(BoosterPack.FN_FROM_SET.apply(FModel.getMagicDb().getEditions().get(temp[2]))); + } else if (temp.length >= 3 && temp[0].equalsIgnoreCase("tournament") && temp[1].equalsIgnoreCase("pack")) { + // Type 5: a predetermined extra tournament ("starter") pack + rewards.add(TournamentPack.FN_FROM_SET.apply(FModel.getMagicDb().getEditions().get(temp[2]))); + } + else if (temp.length > 0) { + // default: assume we are asking for a single copy of a specific card + final PaperCard specific = FModel.getMagicDb().getCommonCards().getCard(s); + if (specific != null) { + rewards.add(specific); + } + } + // Return the duplicate, a specified card, or an empty list + return rewards; + } + + + /** + *

+ * generateCardRewardList. + *

+ * Takes a reward list string, parses, and returns list of cards rewarded. + * + * @param s + * Properties string of reward (97 multicolor rares) + * @return List + */ + public static List generateCardRewardList(final String s) { + + if (StringUtils.isBlank(s)) { + return null; + } + + final String[] items = s.split(";"); + final List rewards = new ArrayList(); + + for (final String item : items) { + + String input = null; + + if (item.contains("%")) { + String[] tmp = item.split("%"); + final int chance = Integer.parseInt(tmp[0].trim()); + if (chance > 0 && tmp.length > 1 && MyRandom.percentTrue(chance)) { + input = tmp[1].trim(); + } + } else { + input = item; + } + if (input != null) { + List reward = parseReward(input); + + if (reward != null) { + rewards.addAll(reward); + } + } + } + + return rewards; + } +} diff --git a/forge-m-base/src/forge/quest/IQuestRewardCard.java b/forge-m-base/src/forge/quest/IQuestRewardCard.java new file mode 100644 index 00000000000..9c2705b766b --- /dev/null +++ b/forge-m-base/src/forge/quest/IQuestRewardCard.java @@ -0,0 +1,22 @@ +package forge.quest; + +import forge.item.InventoryItem; +import forge.item.PaperCard; + +import java.util.List; + +/** + * Various card rewards that may be awarded during the Quest. + * Classes that implement this interface should be able to build + * and return a list of card choices for the player to choose + * from. + */ +public interface IQuestRewardCard extends InventoryItem { + + /** + * Returns an unmodifiable list of card choices. + * @return List, an umodifiable list of cards. + */ + List getChoices(); + +} diff --git a/forge-m-base/src/forge/quest/QuestController.java b/forge-m-base/src/forge/quest/QuestController.java new file mode 100644 index 00000000000..7cf0a6775d1 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestController.java @@ -0,0 +1,505 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Nate + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.eventbus.Subscribe; + +import forge.deck.Deck; +import forge.game.GameFormat; +import forge.game.event.GameEvent; +import forge.game.event.GameEventMulligan; +import forge.item.PaperCard; +import forge.item.PreconDeck; +import forge.model.FModel; +import forge.net.FServer; +import forge.quest.bazaar.QuestBazaarManager; +import forge.quest.bazaar.QuestItemType; +import forge.quest.bazaar.QuestPetStorage; +import forge.quest.data.GameFormatQuest; +import forge.quest.data.QuestAchievements; +import forge.quest.data.QuestAssets; +import forge.quest.data.QuestData; +import forge.quest.data.QuestPreferences.DifficultyPrefs; +import forge.quest.io.QuestChallengeReader; +import forge.util.storage.IStorage; +import forge.util.storage.StorageBase; +import forge.utils.Constants; + +import java.io.File; +import java.util.*; + +/** + * TODO: Write javadoc for this type. + * + */ +public class QuestController { + + private QuestData model; + // gadgets + + // Utility class to access cards, has access to private fields + // Moved some methods there that otherwise would make this class even more + // complex + private QuestUtilCards myCards; + + private GameFormatQuest questFormat; + + private QuestEvent currentEvent; + + /** The decks. */ + private transient IStorage decks; + + private QuestEventDuelManager duelManager = null; + private IStorage allChallenges = null; + + private QuestBazaarManager bazaar = null; + + private QuestPetStorage pets = null; + + // This is used by shop. Had no idea where else to place this + private static transient IStorage preconManager = null; + + /** The Constant RANK_TITLES. */ + public static final String[] RANK_TITLES = new String[] { "Level 0 - Confused Wizard", "Level 1 - Mana Mage", + "Level 2 - Death by Megrim", "Level 3 - Shattered the Competition", "Level 4 - Black Knighted", + "Level 5 - Shockingly Good", "Level 6 - Regressed into Timmy", "Level 7 - Loves Blue Control", + "Level 8 - Immobilized by Fear", "Level 9 - Lands = Friends", "Level 10 - Forging new paths", + "Level 11 - Infect-o-tron", "Level 12 - Great Balls of Fire", "Level 13 - Artifact Schmartifact", + "Level 14 - Mike Mulligan's The Name", "Level 15 - Fresh Air: Good For The Health", + "Level 16 - In It For The Love", "Level 17 - Sticks, Stones, Bones", "Level 18 - Credits For Breakfast", + "Level 19 - Millasaurus", "Level 20 - One-turn Wonder", "Teaching Gandalf a Lesson", + "What Do You Do With The Other Hand?", "Freelance Sorcerer, Works Weekends", + "Should We Hire Commentators?", "Saltblasted For Your Talent", "Serra Angel Is Your Girlfriend", }; + + /** */ + public static final int MAX_PET_SLOTS = 2; + + + /** + * + * TODO: Write javadoc for this method. + * @param slot   int + * @param name   String + */ + public void selectPet(Integer slot, String name) { + if (this.model != null) { + this.model.getPetSlots().put(slot, name); + } + } + + public void setCharmState(boolean active) { + if (this.model != null) { + this.model.setCharmActive(active); + } + } + + public boolean getCharmState() { + return this.model == null ? false : this.model.isCharmActive(); + } + + /** + * + * @param slot   int + * @return String + */ + public String getSelectedPet(Integer slot) { + return this.model == null ? null : this.model.getPetSlots().get(slot); + } + + // Cards - class uses data from here + /** + * Gets the cards. + * + * @return the cards + */ + public QuestUtilCards getCards() { + return this.myCards; + } + + /** + * Gets the my decks. + * + * @return the myDecks + */ + public IStorage getMyDecks() { + return this.decks; + } + + /** + * Gets the current format if any. + * + * @return GameFormatQuest, the game format (if persistent). + */ + public GameFormatQuest getFormat() { + + return (getWorldFormat() == null ? this.questFormat : getWorldFormat()); + } + + /** + * Gets the custom format for the main world, if any. + */ + public GameFormatQuest getMainFormat() { + return this.questFormat; + } + + /** + * Gets the current event. + * + * @return the current event + */ + public QuestEvent getCurrentEvent() { + return this.currentEvent; + } + + /** + * Sets the current event. + * + * @param currentEvent the new current event + */ + public void setCurrentEvent(final QuestEvent currentEvent) { + this.currentEvent = currentEvent; + } + + public static IStorage getPrecons() { + if (null == preconManager) { + // read with a special class, that will fill sell rules as it processes each PreconDeck + preconManager = new StorageBase("Quest shop decks", new PreconDeck.Reader(new File(Constants.QUEST_PRECON_DIR)){ + @Override + protected PreconDeck getPreconDeckFromSections(java.util.Map> sections) { + PreconDeck result = super.getPreconDeckFromSections(sections); + preconDeals.put(result.getName(), new SellRules(sections.get("shop"))); + return result; + }; + }); + } + return QuestController.preconManager; + } + private final static Map preconDeals = new TreeMap(); + public static SellRules getPreconDeals(PreconDeck deck) { + return preconDeals.get(deck.getName()); + } + + /** + * TODO: Write javadoc for this method. + * + * @param selectedQuest the selected quest + */ + public void load(final QuestData selectedQuest) { + this.model = selectedQuest; + // These are helper classes that hold no data. + this.decks = this.model == null ? null : this.model.getAssets().getDeckStorage(); + this.myCards = this.model == null ? null : new QuestUtilCards(this); + this.questFormat = this.model == null ? null : this.model.getFormat(); + this.currentEvent = null; + + this.resetDuelsManager(); + this.resetChallengesManager(); + this.getDuelsManager().randomizeOpponents(); + } + + /** + * TODO: Write javadoc for this method. + */ + public void save() { + if (this.model != null) { + this.model.saveData(); + } + } + + /** + * New game. + * + * @param name the name + * @param difficulty + * the difficulty + * @param mode the mode + * @param formatPrizes + * prize boosters format + * @param allowSetUnlocks + * allow unlocking of sets + * @param startingCards + * the starting deck + * @param formatStartingPool + * format used for the starting pool + * @param startingWorld + * starting world + * @param userPrefs + * user preferences + */ + public void newGame(final String name, final int difficulty, final QuestMode mode, + final GameFormat formatPrizes, final boolean allowSetUnlocks, + final Deck startingCards, final GameFormat formatStartingPool, + final String startingWorld, final StartingPoolPreferences userPrefs) { + + this.load(new QuestData(name, difficulty, mode, formatPrizes, allowSetUnlocks, startingWorld)); // pass awards and unlocks here + + if (startingCards != null) { + this.myCards.addDeck(startingCards); + } + else { + Predicate filter = Predicates.alwaysTrue(); + if (formatStartingPool != null) { + filter = formatStartingPool.getFilterPrinted(); + } + this.myCards.setupNewGameCardPool(filter, difficulty, userPrefs); + } + + this.getAssets().setCredits(FModel.getQuestPreferences().getPrefInt(DifficultyPrefs.STARTING_CREDITS, difficulty)); + } + + /** + * Gets the rank. + * + * @return the rank + */ + public String getRank() { + int level = this.model.getAchievements().getLevel(); + if (level >= QuestController.RANK_TITLES.length) { + level = QuestController.RANK_TITLES.length - 1; + } + return QuestController.RANK_TITLES[level]; + } + + /** + * TODO: Write javadoc for this method. + * + * @return the assets + */ + public QuestAssets getAssets() { + return this.model == null ? null : this.model.getAssets(); + } + + /** + * Gets the QuestWorld, if any. + * + * @return QuestWorld or null, if using regular duels and challenges. + */ + public QuestWorld getWorld() { + return this.model == null || this.model.getWorldId() == null ? null : FModel.getWorlds().get(this.model.getWorldId()); + } + + /** + * Sets a new QuestWorld. + * + * @param newWorld + * string, the new world id + */ + public void setWorld(final QuestWorld newWorld) { + if (this.model == null) { + return; + } + + this.model.setWorldId(newWorld == null ? null : newWorld.getName()); + } + + /** + * Gets the QuestWorld Format, if any. + * + * @return GameFormatQuest or null. + */ + public GameFormatQuest getWorldFormat() { + if (this.model == null || this.model.getWorldId() == null) { + return null; + } + + final QuestWorld curQw = FModel.getWorlds().get(this.model.getWorldId()); + + if (curQw == null) { + return null; + } + + return curQw.getFormat(); + } + + /** + * TODO: Write javadoc for this method. + * + * @return the name + */ + public String getName() { + return this.model == null ? null : this.model.getName(); + } + + /** + * TODO: Write javadoc for this method. + * + * @return the achievements + */ + public QuestAchievements getAchievements() { + return this.model == null ? null : this.model.getAchievements(); + } + + /** + * TODO: Write javadoc for this method. + * + * @return the mode + */ + public QuestMode getMode() { + return this.model.getMode(); + } + + /** + * Gets the bazaar. + * + * @return the bazaar + */ + public final QuestBazaarManager getBazaar() { + if (null == this.bazaar) { + this.bazaar = new QuestBazaarManager(new File(Constants.BAZAAR_FILE)); + } + return this.bazaar; + } + + /** + * Gets the event manager. + * + * @return the event manager + */ + public QuestEventDuelManager getDuelsManager() { + if (this.duelManager == null) { + resetDuelsManager(); + } + return this.duelManager; + } + + /** + * + * TODO: Write javadoc for this method. + * @return QuestEventManager + */ + public IStorage getChallenges() { + if (this.allChallenges == null) { + resetChallengesManager(); + } + return this.allChallenges; + } + + /** + * + * Reset the duels manager. + */ + public void resetDuelsManager() { + QuestWorld world = getWorld(); + String path = world == null || world.getDuelsDir() == null ? Constants.DEFAULT_DUELS_DIR : "res/quest/world/" + world.getDuelsDir(); + this.duelManager = new QuestEventDuelManager(new File(path)); + } + + /** + * + * Reset the challenges manager. + */ + public void resetChallengesManager() { + QuestWorld world = getWorld(); + String path = world == null || world.getChallengesDir() == null ? Constants.DEFAULT_CHALLENGES_DIR : "res/quest/world/" + world.getChallengesDir(); + this.allChallenges = new StorageBase("Quest Challenges", new QuestChallengeReader(new File(path))); + } + + /** + * + * TODO: Write javadoc for this method. + * @return QuestPetStorage + */ + public QuestPetStorage getPetsStorage() { + if (this.pets == null) { + this.pets = new QuestPetStorage(new File(Constants.BAZAAR_FILE)); + } + + return this.pets; + } + + /** + * Quest format has unlockable sets available at the moment. + * @return int number of unlockable sets. + */ + public int getUnlocksTokens() { + if (this.questFormat == null || !this.questFormat.canUnlockSets()) { + return 0; + } + + final int wins = this.model.getAchievements().getWin(); + + int cntLocked = this.questFormat.getLockedSets().size(); + int unlocksAvaliable = wins / 20; + int unlocksSpent = this.questFormat.getUnlocksUsed(); + + return unlocksAvaliable > unlocksSpent ? Math.min(unlocksAvaliable - unlocksSpent, cntLocked) : 0; + } + + @Subscribe + public void receiveGameEvent(GameEvent ev) { // Receives events only during quest games + if (ev instanceof GameEventMulligan) { + GameEventMulligan mev = (GameEventMulligan) ev; + // First mulligan is free + if (mev.player.getLobbyPlayer() == FServer.getLobby().getQuestPlayer() + && getAssets().hasItem(QuestItemType.SLEIGHT) && mev.player.getStats().getMulliganCount() == 0) { + mev.player.drawCard(); + } + } + } + + public int getTurnsToUnlockChallenge() { + if (FModel.getQuest().getAssets().hasItem(QuestItemType.ZEPPELIN)) { + return 8; + } + // User may have MAP and ZEPPELIN, so MAP must be tested second. + else if (FModel.getQuest().getAssets().hasItem(QuestItemType.MAP)) { + return 9; + } + + return 10; + } + + public final void regenerateChallenges() { + final QuestAchievements achievements = model.getAchievements(); + final List unlockedChallengeIds = new ArrayList(); + final List availableChallengeIds = achievements.getCurrentChallenges(); + + int maxChallenges = achievements.getWin() / getTurnsToUnlockChallenge() - achievements.getChallengesPlayed(); + if (maxChallenges > 5) { + maxChallenges = 5; + } + + // Generate IDs as needed. + if (achievements.getCurrentChallenges().size() < maxChallenges) { + for (final QuestEventChallenge qc : allChallenges) { + if (qc.getWinsReqd() > achievements.getWin()) { + continue; + } + if (!qc.isRepeatable() && achievements.getLockedChallenges().contains(qc.getId())) { + continue; + } + if (!availableChallengeIds.contains(qc.getId())) { + unlockedChallengeIds.add(qc.getId()); + } + } + + Collections.shuffle(unlockedChallengeIds); + + maxChallenges = Math.min(maxChallenges, unlockedChallengeIds.size()); + + for (int i = availableChallengeIds.size(); i < maxChallenges; i++) { + availableChallengeIds.add(unlockedChallengeIds.get(i)); + } + } + + achievements.setCurrentChallenges(availableChallengeIds); + save(); + } +} diff --git a/forge-m-base/src/forge/quest/QuestDeckMap.java b/forge-m-base/src/forge/quest/QuestDeckMap.java new file mode 100644 index 00000000000..b86bd857137 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestDeckMap.java @@ -0,0 +1,59 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import forge.deck.Deck; +import forge.util.storage.StorageBase; + +import java.util.Map; + +/** + * TODO: Write javadoc for this type. + * + */ +public class QuestDeckMap extends StorageBase { + + /** + * Instantiates a new quest deck map. + */ + public QuestDeckMap(Map in) { + super("Quest decks", in); + } + + + /* + * (non-Javadoc) + * + * @see forge.util.IFolderMap#add(forge.util.IHasName) + */ + @Override + public void add(final Deck deck) { + this.map.put(deck.getName(), deck); + } + + /* + * (non-Javadoc) + * + * @see forge.util.IFolderMap#delete(java.lang.String) + */ + @Override + public void delete(final String deckName) { + this.map.remove(deckName); + } + +} diff --git a/forge-m-base/src/forge/quest/QuestEvent.java b/forge-m-base/src/forge/quest/QuestEvent.java new file mode 100644 index 00000000000..973d0357c7b --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestEvent.java @@ -0,0 +1,131 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import com.google.common.base.Function; +import forge.deck.Deck; +import forge.game.player.IHasIcon; +import forge.item.InventoryItem; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + *

+ * QuestEvent. + *

+ * + * MODEL - A basic event instance in Quest mode. Can be extended for use in + * unique event types: battles, quests, and others. + */ +public abstract class QuestEvent implements IHasIcon { + // Default vals if none provided in the event file. + private Deck eventDeck = null; + private String title = "Mystery Event"; + private String description = ""; + private QuestEventDifficulty difficulty = QuestEventDifficulty.MEDIUM; + private String imageKey = ""; + private String name = "Noname"; + private String cardReward = null; + private List cardRewardList = null; + + + public static final Function FN_GET_NAME = new Function() { + @Override public final String apply(QuestEvent qe) { return qe.name; } + }; + + public final String getTitle() { + return this.title; + } + + /** + * Returns null for standard quest events, may return something different for challenges. + */ + public String getOpponent() { + return null; + } + + public final QuestEventDifficulty getDifficulty() { + return this.difficulty; + } + + public final String getDescription() { + return this.description; + } + + public final Deck getEventDeck() { + return this.eventDeck; + } + + @Override + public final String getIconImageKey() { + return this.imageKey; + } + + public final String getName() { + return this.name; + } + + public void setName(final String name0) { + this.name = name0; + } + + public void setTitle(final String title0) { + this.title = title0; + } + + public void setDifficulty(final QuestEventDifficulty difficulty0) { + this.difficulty = difficulty0; + } + + public void setDescription(final String description0) { + this.description = description0; + } + + public void setEventDeck(final Deck eventDeck0) { + this.eventDeck = eventDeck0; + } + + @Override + public void setIconImageKey(final String s0) { + this.imageKey = s0; + } + + public final List getCardRewardList() { + if (cardReward == null) { + return null; + } + if (cardRewardList == null) { + this.cardRewardList = new ArrayList(BoosterUtils.generateCardRewardList(cardReward)); + } + return this.cardRewardList; + } + + public void setCardReward(final String cardReward0) { + this.cardReward = cardReward0; + } + + public List getHumanExtraCards() { + return Collections.emptyList(); + } + + public List getAiExtraCards() { + return Collections.emptyList(); + } +} diff --git a/forge-m-base/src/forge/quest/QuestEventChallenge.java b/forge-m-base/src/forge/quest/QuestEventChallenge.java new file mode 100644 index 00000000000..c1d81df34bd --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestEventChallenge.java @@ -0,0 +1,315 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import com.google.common.base.Function; +import forge.deck.Deck; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * QuestQuest class. + *

+ * + * MODEL - A single quest event data instance, including meta, deck, and + * quest-specific properties. + * + */ +public class QuestEventChallenge extends QuestEvent { + public static final Function FN_GET_ID = new Function() { + @Override public final String apply(QuestEventChallenge qe) { return qe.id; } + }; + + // ID (default -1, should be explicitly set at later time.) + /** The id. */ + private String id = "-1"; + + // Opponent name if different from the challenge name + private String opponentName = null; + + // Default vals if none provided for this ID + /** The ai life. */ + private int aiLife = 25; + + private Integer humanLife = null; + + /** The credits reward. */ + private int creditsReward = 100; + + /** The repeatable. */ + private boolean repeatable = false; + + private boolean useBazaar = true; + private Boolean forceAnte = null; + + /** The wins reqd. */ + private int winsReqd = 20; + + // Other cards used in assignment: starting, and reward. + /** The human extra cards. */ + private List humanExtraCards = new ArrayList(); + + /** The ai extra cards. */ + private List aiExtraCards = new ArrayList(); + + private Deck humanDeck = null; + + /** + * Instantiates a new quest challenge. + */ + public QuestEventChallenge() { + super(); + setCardReward("1 colorless rare"); // Guaranteed extra reward for challenges if not specified + } + + /** + *

+ * getAILife. + *

+ * + * @return {@link java.lang.Integer}. + */ + public final int getAILife() { + return this.getAiLife(); + } + + /** + *

+ * getCreditsReward. + *

+ * + * @return {@link java.lang.Integer}. + */ + public final int getCreditsReward() { + return this.creditsReward; + } + + /** + *

+ * getId. + *

+ * + * @return {@link java.lang.Integer}. + */ + public final String getId() { + return this.id; + } + + /** + *

+ * get the opponent's name, or null if not explicitly set. + * + *

+ * + * @return {@link java.lang.String}. + */ + @Override + public final String getOpponent() { + return this.opponentName; + } + + /** + *

+ * getWinsReqd. + *

+ * + * @return {@link java.lang.Integer}. + */ + public final int getWinsReqd() { + return this.winsReqd; + } + + /** + *

+ * getHumanExtraCards. + *

+ * Retrieves list of cards human has in play at the beginning of this quest. + * + * @return the human extra cards + */ + @Override + public final List getHumanExtraCards() { + return this.humanExtraCards; + } + + /** + * Gets the ai extra cards. + * + * @return the aiExtraCards + */ + @Override + public List getAiExtraCards() { + return this.aiExtraCards; + } + + /** + * Sets the ai extra cards. + * + * @param aiExtraCards0 + * the aiExtraCards to set + */ + public void setAiExtraCards(final List aiExtraCards0) { + this.aiExtraCards = aiExtraCards0; + } + + /** + * Sets the id. + * + * @param id0 + * the id to set + */ + public void setId(final String id0) { + this.id = id0; + } + + /** + * Sets the opponent's name. + * + * @param newName + * the name to set + */ + public void setOpponent(final String newName) { + this.opponentName = newName; + } + + /** + * Checks if is repeatable. + * + * @return the repeatable + */ + public boolean isRepeatable() { + return this.repeatable; + } + + /** + * Sets the repeatable. + * + * @param repeatable0 + * the repeatable to set + */ + public void setRepeatable(final boolean repeatable0) { + this.repeatable = repeatable0; + } + + /** + * Gets the ai life. + * + * @return the aiLife + */ + public int getAiLife() { + return this.aiLife; + } + + /** + * Sets the ai life. + * + * @param aiLife0 + * the aiLife to set + */ + public void setAiLife(final int aiLife0) { + this.aiLife = aiLife0; + } + + /** + * Sets the wins reqd. + * + * @param winsReqd0 + * the winsReqd to set + */ + public void setWinsReqd(final int winsReqd0) { + this.winsReqd = winsReqd0; + } + + /** + * Sets the credits reward. + * + * @param creditsReward0 + * the creditsReward to set + */ + public void setCreditsReward(final int creditsReward0) { + this.creditsReward = creditsReward0; + } + + /** + * Sets the human extra cards. + * + * @param humanExtraCards0 + * the humanExtraCards to set + */ + public void setHumanExtraCards(final List humanExtraCards0) { + this.humanExtraCards = humanExtraCards0; + } + + /** + * @return the humanLife + */ + public Integer getHumanLife() { + return humanLife; + } + + /** + * @param humanLife the humanLife to set + */ + public void setHumanLife(Integer humanLife) { + this.humanLife = humanLife; + } + + /** + * @return the useBazaar + */ + public boolean isUseBazaar() { + return useBazaar; + } + + /** + * @param useBazaar the useBazaar to set + */ + public void setUseBazaar(boolean useBazaar) { + this.useBazaar = useBazaar; + } + + /** + * @return the forceAnte + */ + public Boolean isForceAnte() { + return forceAnte; + } + + /** + * @param forceAnte the forceAnte to set + */ + public void setForceAnte(Boolean forceAnte) { + this.forceAnte = forceAnte; + } + + /** + * @return the humanDeck + */ + public Deck getHumanDeck() { + return humanDeck; + } + + /** + * @param humanDeck the humanDeck to set + */ + public void setHumanDeck(Deck humanDeck) { + this.humanDeck = humanDeck; + } +} diff --git a/forge-m-base/src/forge/quest/QuestEventDifficulty.java b/forge-m-base/src/forge/quest/QuestEventDifficulty.java new file mode 100644 index 00000000000..804502fcf83 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestEventDifficulty.java @@ -0,0 +1,35 @@ +package forge.quest; + +import org.apache.commons.lang3.StringUtils; + +/** + * TODO: Write javadoc for this type. + * + */ +public enum QuestEventDifficulty { + EASY("easy"), + MEDIUM("medium"), + HARD("hard"), + EXPERT("very hard"); + + String inFile; + + private QuestEventDifficulty(String storedInFile) { + inFile = storedInFile; + } + + public final String getTitle() { + return inFile; + } + + public static QuestEventDifficulty fromString(String src) { + if ( StringUtils.isBlank(src) ) + return MEDIUM; // player have custom files, that didn't specify a valid difficulty + + for(QuestEventDifficulty qd : QuestEventDifficulty.values()) { + if( src.equalsIgnoreCase(qd.inFile) || src.equalsIgnoreCase(qd.name()) ) + return qd; + } + return null; + } +} diff --git a/forge-m-base/src/forge/quest/QuestEventDuel.java b/forge-m-base/src/forge/quest/QuestEventDuel.java new file mode 100644 index 00000000000..c3b48be1580 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestEventDuel.java @@ -0,0 +1,37 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + + +/** + *

+ * QuestDuel class. + *

+ * MODEL - A single duel event data instance, including meta and deck. + * + */ +public class QuestEventDuel extends QuestEvent { + + /** + * Instantiates a new quest duel. + */ + public QuestEventDuel() { + super(); + } + +} diff --git a/forge-m-base/src/forge/quest/QuestEventDuelManager.java b/forge-m-base/src/forge/quest/QuestEventDuelManager.java new file mode 100644 index 00000000000..b972065382c --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestEventDuelManager.java @@ -0,0 +1,158 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import forge.model.FModel; +import forge.quest.data.QuestPreferences; +import forge.quest.data.QuestPreferences.DifficultyPrefs; +import forge.quest.io.QuestDuelReader; +import forge.util.CollectionSuppliers; +import forge.util.maps.EnumMapOfLists; +import forge.util.maps.MapOfLists; +import forge.util.storage.IStorage; +import forge.util.storage.StorageBase; + +import java.io.File; +import java.util.*; + +/** + * QuestEventManager. + * + * @author Forge + * @version $Id: QuestEventManager.java 20404 2013-03-17 05:34:13Z myk $ + */ +public class QuestEventDuelManager { + + private final MapOfLists sortedDuels = new EnumMapOfLists(QuestEventDifficulty.class, CollectionSuppliers.arrayLists()); + private final IStorage allDuels; + + + /** Instantiate all events and difficulty lists. + * @param dir   File object */ + public QuestEventDuelManager(final File dir) { + allDuels = new StorageBase("Quest duels", new QuestDuelReader(dir)); + assembleDuelDifficultyLists(); + } // End assembleAllEvents() + + /** @return List */ + public Iterable getAllDuels() { + return allDuels; + } + + // define fallback orders if there aren't enough opponents defined for a particular difficultly level + private static List _easyOrder = Arrays.asList(QuestEventDifficulty.EASY, QuestEventDifficulty.MEDIUM, QuestEventDifficulty.HARD, QuestEventDifficulty.EXPERT); + private static List _mediumOrder = Arrays.asList(QuestEventDifficulty.MEDIUM, QuestEventDifficulty.HARD, QuestEventDifficulty.EASY, QuestEventDifficulty.EXPERT); + private static List _hardOrder = Arrays.asList(QuestEventDifficulty.HARD, QuestEventDifficulty.MEDIUM, QuestEventDifficulty.EASY, QuestEventDifficulty.EXPERT); + private static List _expertOrder = Arrays.asList(QuestEventDifficulty.EXPERT, QuestEventDifficulty.HARD, QuestEventDifficulty.MEDIUM, QuestEventDifficulty.EASY); + + private void _addDuel(List outList, QuestEventDifficulty targetDifficulty, int toAdd) { + // if there's no way we can satisfy the request, return now + if (allDuels.size() <= toAdd) { + return; + } + + final List difficultyOrder; + switch (targetDifficulty) { + case EASY: difficultyOrder = _easyOrder; break; + case MEDIUM: difficultyOrder = _mediumOrder; break; + case HARD: difficultyOrder = _hardOrder; break; + case EXPERT: difficultyOrder = _expertOrder; break; + default: + throw new RuntimeException("unhandled difficulty: " + targetDifficulty); + } + + for (QuestEventDifficulty d : difficultyOrder) { // will add duels from preferred difficulty, will use others if the former has too few options. + for( QuestEventDuel duel : sortedDuels.get(d)) { + if(toAdd <= 0) + return; + + if (!outList.contains(duel)) { + outList.add(duel); + toAdd--; + } + } + } + } + + /** Generates an array of new duel opponents based on current win conditions. + * + * @return an array of {@link java.lang.String} objects. + */ + public final List generateDuels() { + final QuestPreferences qpref = FModel.getQuestPreferences(); + if (FModel.getQuest().getAchievements() == null) { + return null; + } + + final QuestController qCtrl = FModel.getQuest(); + final int cntWins = qCtrl.getAchievements().getWin(); + + final int index = qCtrl.getAchievements().getDifficulty(); + final List duelOpponents = new ArrayList(); + + if (cntWins < qpref.getPrefInt(DifficultyPrefs.WINS_MEDIUMAI, index)) { + _addDuel(duelOpponents, QuestEventDifficulty.EASY, 3); + } else if (cntWins == qpref.getPrefInt(DifficultyPrefs.WINS_MEDIUMAI, index)) { + _addDuel(duelOpponents, QuestEventDifficulty.EASY, 1); + _addDuel(duelOpponents, QuestEventDifficulty.MEDIUM, 2); + } else if (cntWins < qpref.getPrefInt(DifficultyPrefs.WINS_HARDAI, index)) { + _addDuel(duelOpponents, QuestEventDifficulty.MEDIUM, 3); + } else if (cntWins == qpref.getPrefInt(DifficultyPrefs.WINS_HARDAI, index)) { + _addDuel(duelOpponents, QuestEventDifficulty.MEDIUM, 1); + _addDuel(duelOpponents, QuestEventDifficulty.HARD, 2); + } else if (cntWins < qpref.getPrefInt(DifficultyPrefs.WINS_EXPERTAI, index)) { + _addDuel(duelOpponents, QuestEventDifficulty.HARD, 3); + } else { + _addDuel(duelOpponents, QuestEventDifficulty.HARD, 2); + _addDuel(duelOpponents, QuestEventDifficulty.EXPERT, 1); + } + + return duelOpponents; + } + + /** + *

+ * assembleDuelDifficultyLists. + *

+ * Assemble duel deck difficulty lists + */ + private void assembleDuelDifficultyLists() { + sortedDuels.clear(); + sortedDuels.put(QuestEventDifficulty.EASY, new ArrayList()); + sortedDuels.put(QuestEventDifficulty.MEDIUM, new ArrayList()); + sortedDuels.put(QuestEventDifficulty.HARD, new ArrayList()); + sortedDuels.put(QuestEventDifficulty.EXPERT, new ArrayList()); + + + + for (final QuestEventDuel qd : allDuels) { + sortedDuels.add(qd.getDifficulty(), qd); + } + } + + /** */ + public void randomizeOpponents() { + final long seed = new Random().nextLong(); + final Random r = new Random(seed); + for(QuestEventDifficulty qd : sortedDuels.keySet()) { + List list = (List) sortedDuels.get(qd); + Collections.shuffle(list, r); + } + } + +} diff --git a/forge-m-base/src/forge/quest/QuestMode.java b/forge-m-base/src/forge/quest/QuestMode.java new file mode 100644 index 00000000000..a934d6032de --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestMode.java @@ -0,0 +1,34 @@ +package forge.quest; + +/** + * TODO: Write javadoc for this type. + * + */ + +public enum QuestMode { + // Do not apply checkstyle here, to maintain compatibility with old saves + Fantasy, + Classic, + Gauntlet; + + /** + * TODO: Write javadoc for this method. + * @param value + * @param classic2 + * @return + */ + public static QuestMode smartValueOf(String value, QuestMode defaultValue) { + if (null == value) { + return defaultValue; + } + + final String valToCompate = value.trim(); + for (final QuestMode v : QuestMode.values()) { + if (v.name().compareToIgnoreCase(valToCompate) == 0) { + return v; + } + } + + return QuestMode.Classic; + } +} diff --git a/forge-m-base/src/forge/quest/QuestRewardCard.java b/forge-m-base/src/forge/quest/QuestRewardCard.java new file mode 100644 index 00000000000..a012384a419 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestRewardCard.java @@ -0,0 +1,123 @@ +package forge.quest; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +import forge.card.CardRules; +import forge.item.IPaperCard; +import forge.item.InventoryItem; +import forge.item.PaperCard; +import forge.model.FModel; + +import java.util.ArrayList; +import java.util.List; + + +public abstract class QuestRewardCard implements InventoryItem, IQuestRewardCard { + + protected String buildDescription(final String [] input) { + final String defaultDescription = "a card"; + if (input == null || input.length < 1) { + return defaultDescription; + } + + String buildDesc = null; + + for (String s : input) { + if (s.startsWith("desc:") || s.startsWith("Desc:")) { + String[] tmp = s.split(":"); + if (tmp.length > 1) { + buildDesc = new String(tmp[1]); + } else { + buildDesc = new String(); + } + } else if (buildDesc != null) { + if (s.contains(":")) { + return buildDesc; + } else { + buildDesc = buildDesc + " " + s; + } + } + } + + if (buildDesc != null) { + return buildDesc; + } + return defaultDescription; + } + + protected Predicate buildPredicates(final String [] input) { + if (input == null || input.length < 1) { + return null; + } + + Predicate filters = FModel.getQuest().getFormat().getFilterPrinted(); + Predicate filterRules = null; + Predicate filterRarity = null; + + for (String s : input) { + if (s.startsWith("sets:") || s.startsWith("Sets:")) { + final String[] tmp = s.split(":"); + if (tmp.length > 1) { + String [] setcodes = tmp[1].split(","); + if (setcodes.length > 0) { + List sets = new ArrayList(); + for (String code : setcodes) { + if (FModel.getMagicDb().getEditions().contains(code)) { + // System.out.println("Set " + code + " was found!"); + sets.add(code); + } + // else { System.out.println("Unknown set code " + code); } + } + if (sets.size() > 0) { + filters = IPaperCard.Predicates.printedInSets(sets, true); + } + } + } + } else if (s.startsWith("rules:") || s.startsWith("Rules:")) { + final String[] tmp = s.split(":"); + if (tmp.length > 1) { + String [] ruleCodes = tmp[1].split(","); + if (ruleCodes.length > 0) { + for (String rule : ruleCodes) { + final Predicate newRule = BoosterUtils.parseRulesLimitation(rule); + if (newRule != null) { + filterRules = (filterRules == null ? newRule : Predicates.and(filterRules, newRule)); + } + } + } + } + } else if (s.startsWith("rarity:") || s.startsWith("Rarity:")) { + final String[] tmp = s.split(":"); + if (tmp.length > 1) { + String [] rarityCodes = tmp[1].split(","); + if (rarityCodes.length > 0) { + for (String rarity : rarityCodes) { + if (rarity.startsWith("C") || rarity.startsWith("c")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_COMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_COMMON)); + } else if (rarity.startsWith("U") || rarity.startsWith("u")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_UNCOMMON : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_UNCOMMON)); + } else if (rarity.startsWith("R") || rarity.startsWith("r")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_RARE)); + } else if (rarity.startsWith("M") || rarity.startsWith("m")) { + filterRarity = (filterRarity == null ? IPaperCard.Predicates.Presets.IS_MYTHIC_RARE : Predicates.or(filterRarity, IPaperCard.Predicates.Presets.IS_MYTHIC_RARE)); + } + } + } + } + } + } + + if (filterRules != null) { + final Predicate rulesPrinted = Predicates.compose(filterRules, PaperCard.FN_GET_RULES); + filters = Predicates.and(filters, rulesPrinted); + } + if (filterRarity != null) { + filters = Predicates.and(filters, filterRarity); + } + return filters; + } + + public abstract List getChoices(); + +} \ No newline at end of file diff --git a/forge-m-base/src/forge/quest/QuestRewardCardChooser.java b/forge-m-base/src/forge/quest/QuestRewardCardChooser.java new file mode 100644 index 00000000000..cb5c68c3e6e --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestRewardCardChooser.java @@ -0,0 +1,125 @@ +package forge.quest; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import forge.item.InventoryItem; +import forge.item.PaperCard; +import forge.model.FModel; +import forge.util.ItemPool; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Resolves a card chooser InventoryItem into a CardPrinted. + * The initial version includes "duplicate", other type may be added later. + * + */ +public class QuestRewardCardChooser extends QuestRewardCard implements InventoryItem { + /** + * Possible types for this object. + */ + public enum poolType { + /** The player's own cardpool (duplicate card). */ + playerCards, + /** Filtered by a predicate that will be parsed. */ + predicateFilter + } + + private poolType type; + private final String description; + private final Predicate predicates; + + /** + * The constructor. + * The parameter indicates the more specific type. + * @param setType String, the type of the choosable card. + * @param creationParameters String, used to build the predicates and description for the predicateFilter type + */ + public QuestRewardCardChooser(final poolType setType, final String[] creationParameters) { + type = setType; + if (type == poolType.playerCards) { + description = "a duplicate card"; + predicates = null; + } else { + description = buildDescription(creationParameters); + predicates = buildPredicates(creationParameters); + } + } + + /** + * The name. + * + * @return the name + */ + @Override + public String getName() { + return description; + } + + @Override + public String toString() { + return description; + } + + /** + * The item type. + * + * @return item type + */ + @Override + public String getItemType() { + switch (type) { + case playerCards: + return "duplicate card"; + case predicateFilter: default: + return "chosen card"; + } + } + + /** + * Get type as enum. + * @return enum, item type. + */ + public poolType getType() { + return type; + } + + /** + * Produces a list of options to choose from. + * + * @return a List or null if could not create a list. + */ + @Override + public final List getChoices() { + if (type == poolType.playerCards) { + final ItemPool playerCards = FModel.getQuest().getAssets().getCardPool(); + if (!playerCards.isEmpty()) { // Maybe a redundant check since it's hard to win a duel without any cards... + + List cardChoices = new ArrayList(); + for (final Map.Entry card : playerCards) { + cardChoices.add(card.getKey()); + } + Collections.sort(cardChoices); + + return Collections.unmodifiableList(cardChoices); + } + + } else if (type == poolType.predicateFilter) { + List cardChoices = new ArrayList(); + + for (final PaperCard card : Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), predicates)) { + cardChoices.add(card); + } + Collections.sort(cardChoices); + + return Collections.unmodifiableList(cardChoices); + } else { + throw new RuntimeException("Unknown QuestRewardCardType: " + type); + } + return null; + } +} diff --git a/forge-m-base/src/forge/quest/QuestRewardCardDuplicate.java b/forge-m-base/src/forge/quest/QuestRewardCardDuplicate.java new file mode 100644 index 00000000000..9deef5e9f28 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestRewardCardDuplicate.java @@ -0,0 +1,72 @@ +package forge.quest; + +import forge.item.PaperCard; +import forge.model.FModel; +import forge.util.ItemPool; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Allows the player to choose a duplicate copy of a currently owned card. + * + */ +public class QuestRewardCardDuplicate implements IQuestRewardCard { + private final String description; + + /** + * + * The constructor. No parameters. + */ + public QuestRewardCardDuplicate() { + description = "a duplicate card"; + } + + /** + * The name. + * + * @return the name + */ + @Override + public String getName() { + return description; + } + + /** + * The item type. + * + * @return item type + */ + @Override + public String getItemType() { + return "duplicate card"; + } + + @Override + public String toString() { + return description; + } + + /** + * Produces a list of options to choose from, in this case, + * the player's current cards. + * + * @return a List or null if could not create a list. + */ + public final List getChoices() { + final ItemPool playerCards = FModel.getQuest().getAssets().getCardPool(); + if (!playerCards.isEmpty()) { // Maybe a redundant check since it's hard to win a duel without any cards... + + List cardChoices = new ArrayList(); + for (final Map.Entry card : playerCards) { + cardChoices.add(card.getKey()); + } + Collections.sort(cardChoices); + + return Collections.unmodifiableList(cardChoices); + } + return null; + } +} diff --git a/forge-m-base/src/forge/quest/QuestRewardCardFiltered.java b/forge-m-base/src/forge/quest/QuestRewardCardFiltered.java new file mode 100644 index 00000000000..0c8d9dceebf --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestRewardCardFiltered.java @@ -0,0 +1,71 @@ +package forge.quest; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import forge.item.PaperCard; +import forge.model.FModel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Allows the player to choose a card from a predicate-filtered list of cards. + * + */ +public class QuestRewardCardFiltered extends QuestRewardCard implements IQuestRewardCard { + private final String description; + private final Predicate predicates; + + /** + * The constructor. + * @param creationParameters String, used to build the predicates and description for the predicateFilter type + */ + public QuestRewardCardFiltered(final String[] creationParameters) { + description = buildDescription(creationParameters); + predicates = buildPredicates(creationParameters); + + } + + /** + * The name. + * + * @return the name + */ + @Override + public String getName() { + return description; + } + + @Override + public String toString() { + return description; + } + + /** + * The item type. + * + * @return item type + */ + @Override + public String getItemType() { + return "chosen card"; + } + + /** + * Produces a list of options to choose from. + * + * @return a List or null if could not create a list. + */ + @Override + public final List getChoices() { + List cardChoices = new ArrayList(); + for (final PaperCard card : Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), predicates)) { + cardChoices.add(card); + } + Collections.sort(cardChoices); + return Collections.unmodifiableList(cardChoices); + } + +} diff --git a/forge-m-base/src/forge/quest/QuestUtil.java b/forge-m-base/src/forge/quest/QuestUtil.java new file mode 100644 index 00000000000..c2cd84be1e1 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestUtil.java @@ -0,0 +1,174 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import forge.card.CardDb.SetPreference; +import forge.card.CardEdition; +import forge.card.CardRules; +import forge.game.card.Card; +import forge.item.IPaperCard; +import forge.item.PaperToken; +import forge.model.FModel; +import forge.quest.bazaar.QuestPetController; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * QuestUtil class. + *

+ * MODEL - Static utility methods to help with minor tasks around Quest. + * + * @author Forge + * @version $Id: QuestUtil.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public class QuestUtil { + /** + *

+ * getComputerStartingCards. + *

+ * + * @param qd + * a {@link forge.quest.data.QuestData} object. + * @return a {@link forge.CardList} object. + */ + public static List getComputerStartingCards() { + return new ArrayList(); + } + + /** + *

+ * getComputerStartingCards. + *

+ * Returns new card instances of extra AI cards in play at start of event. + * + * @param qd + * a {@link forge.quest.data.QuestData} object. + * @param qe + * a {@link forge.quest.QuestEvent} object. + * @return a {@link forge.CardList} object. + */ + public static List getComputerStartingCards(final QuestEvent qe) { + final List list = new ArrayList(); + + for (final String s : qe.getAiExtraCards()) { + list.add(QuestUtil.readExtraCard(s)); + } + + return list; + } + + /** + *

+ * getHumanStartingCards. + *

+ * Returns list of current plant/pet configuration only. + * @param human + * + * @param qd + * a {@link forge.quest.data.QuestData} object. + * @return a {@link forge.CardList} object. + */ + public static List getHumanStartingCards(final QuestController qc) { + final List list = new ArrayList(); + + for (int iSlot = 0; iSlot < QuestController.MAX_PET_SLOTS; iSlot++) { + String petName = qc.getSelectedPet(iSlot); + QuestPetController pet = qc.getPetsStorage().getPet(petName); + if (pet != null) { + IPaperCard c = pet.getPetCard(qc.getAssets()); + if (c != null) { + list.add(c); + } + } + } + + return list; + } + + /** + *

+ * getHumanStartingCards. + *

+ * Returns new card instances of extra human cards, including current + * plant/pet configuration, and cards in play at start of quest. + * + * @param qd + * a {@link forge.quest.data.QuestData} object. + * @param qe + * a {@link forge.quest.QuestEvent} object. + * @return a {@link forge.CardList} object. + */ + public static List getHumanStartingCards(final QuestController qc, final QuestEvent qe) { + final List list = QuestUtil.getHumanStartingCards(qc); + for (final String s : qe.getHumanExtraCards()) { + list.add(QuestUtil.readExtraCard(s)); + } + return list; + } + + /** + *

+ * createToken. + *

+ * Creates a card instance for token defined by property string. + * + * @param s + * Properties string of token + * (TOKEN;W;1;1;sheep;type;type;type...) + * @return token Card + */ + public static PaperToken createToken(final String s) { + final String[] properties = s.split(";", 6); + + List script = new ArrayList(); + script.add("Name:" + properties[4]); + script.add("Colors:" + properties[1]); + script.add("PT:"+ properties[2] + "/" + properties[3]); + script.add("Types:" + properties[5].replace(';', ' ')); + script.add("Oracle:"); // tokens don't have texts yet + String fileName = PaperToken.makeTokenFileName(properties[1], properties[2], properties[3], properties[4]); + final PaperToken c = new PaperToken(CardRules.fromScript(script), CardEdition.UNKNOWN, fileName); + return c; + } + + /** + *

+ * readExtraCard. + *

+ * Creates single card for a string read from unique event properties. + * + * @param name + * the name + * @param owner + * the owner + * @return the card + */ + public static IPaperCard readExtraCard(final String name) { + // Token card creation + IPaperCard tempcard; + if (name.startsWith("TOKEN")) { + tempcard = QuestUtil.createToken(name); + return tempcard; + } + // Standard card creation + return FModel.getMagicDb().getCommonCards().getCardFromEdition(name, SetPreference.Latest); + } + +} // QuestUtil diff --git a/forge-m-base/src/forge/quest/QuestUtilCards.java b/forge-m-base/src/forge/quest/QuestUtilCards.java new file mode 100644 index 00000000000..a897b9041b7 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestUtilCards.java @@ -0,0 +1,708 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import forge.card.*; +import forge.deck.Deck; +import forge.deck.DeckSection; +import forge.game.GameFormat; +import forge.item.*; +import forge.model.FModel; +import forge.quest.bazaar.QuestItemType; +import forge.quest.data.GameFormatQuest; +import forge.quest.data.QuestAssets; +import forge.quest.data.QuestPreferences; +import forge.quest.data.QuestPreferences.DifficultyPrefs; +import forge.quest.data.QuestPreferences.QPref; +import forge.util.Aggregates; +import forge.util.ItemPool; +import forge.util.MyRandom; +import forge.utils.ForgePreferences.FPref; + +import org.apache.commons.lang3.tuple.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +/** + * This is a helper class to execute operations on QuestData. It has been + * created to decrease complexity of questData class + */ +public final class QuestUtilCards { + private final QuestController qc; + private final QuestPreferences qpref; + private final QuestAssets qa; + + /** + * Instantiates a new quest util cards. + * + * @param qd + * the qd + */ + public QuestUtilCards(final QuestController qd) { + this.qc = qd; + this.qa = qc.getAssets(); + this.qpref = FModel.getQuestPreferences(); + } + + /** + * Adds the basic lands (from random sets as limited by the format). + * + * @param nBasic the n basic + * @param nSnow the n snow + * @param usedFormat currently enforced game format, if any + * @return the item pool view + */ + public static ItemPool generateBasicLands(final int nBasic, final int nSnow, final GameFormatQuest usedFormat) { + final ICardDatabase db = FModel.getMagicDb().getCommonCards(); + final ItemPool pool = new ItemPool(PaperCard.class); + + List landCodes = new ArrayList(); + List snowLandCodes = new ArrayList(); + + if (usedFormat != null) { + List availableEditions = usedFormat.getAllowedSetCodes(); + + for (String edCode : availableEditions) { + CardEdition ed = FModel.getMagicDb().getEditions().get(edCode); + // Duel decks might have only 2 types of basic lands + if (CardEdition.Predicates.hasBasicLands.apply(ed)) { + landCodes.add(edCode); + } + } + if (usedFormat.isSetLegal("ICE")) { + snowLandCodes.add("ICE"); + } + if (usedFormat.isSetLegal("CSP")) { + snowLandCodes.add("CSP"); + } + } else { + Iterable allEditions = FModel.getMagicDb().getEditions(); + for (CardEdition edition : Iterables.filter(allEditions, CardEdition.Predicates.hasBasicLands)) { + landCodes.add(edition.getCode()); + } + snowLandCodes.add("ICE"); + snowLandCodes.add("CSP"); + } + + String landCode = Aggregates.random(landCodes); + if (null == landCode) { + landCode = "M10"; + } + + final boolean isZendikarSet = landCode.equals("ZEN"); // we want to generate one kind of Zendikar lands at a time only + final boolean zendikarSetMode = MyRandom.getRandom().nextBoolean(); + + for (String landName : MagicColor.Constant.BASIC_LANDS) { + int artCount = db.getArtCount(landName, landCode); + + if (FModel.getPreferences().getPrefBoolean(FPref.UI_RANDOM_ART_IN_POOLS)) { + int[] artGroups = MyRandom.splitIntoRandomGroups(nBasic, isZendikarSet ? 4 : artCount); + + for (int i = 1; i <= artGroups.length; i++) { + pool.add(db.getCard(landName, landCode, isZendikarSet ? (zendikarSetMode ? i : i + 4) : i), artGroups[i - 1]); + } + } else { + pool.add(db.getCard(landName, landCode, artCount > 1 ? MyRandom.getRandom().nextInt(artCount) + 1 : 1), nBasic); + } + } + + + if (!snowLandCodes.isEmpty()) { + String snowLandCode = Aggregates.random(snowLandCodes); + for (String landName : MagicColor.Constant.SNOW_LANDS) { + pool.add(db.getCard(landName, snowLandCode), nSnow); + } + } + + return pool; + } + + /** + *

+ * addCards. + *

+ * + * @param fSets + * the f sets + * @return the array list + */ + public List generateQuestBooster(final Predicate fSets) { + UnOpenedProduct unopened = new UnOpenedProduct(getBoosterTemplate(), fSets); + return unopened.get(); + } + + /** + * Adds the all cards. + * + * @param newCards + * the new cards + */ + public void addAllCards(final Iterable newCards) { + for (final PaperCard card : newCards) { + this.addSingleCard(card, 1); + } + } + + /** + * Adds the single card. + * + * @param card + * the card + * @param qty + * quantity + */ + public void addSingleCard(final PaperCard card, int qty) { + this.qa.getCardPool().add(card, qty); + + // register card into that list so that it would appear as a new one. + this.qa.getNewCardList().add(card, qty); + } + + private static final Predicate RARE_PREDICATE = IPaperCard.Predicates.Presets.IS_RARE_OR_MYTHIC; + + + /** + * A predicate that takes into account the Quest Format (if any). + * @param source + * the predicate to be added to the format predicate. + * @return the composite predicate. + */ + public Predicate applyFormatFilter(Predicate source) { + return qc.getFormat() == null ? source : Predicates.and(source, qc.getFormat().getFilterPrinted()); + } + + /** + * Adds the random rare. + * + * @return the card printed + */ + public PaperCard addRandomRare() { + + final Predicate myFilter = applyFormatFilter(QuestUtilCards.RARE_PREDICATE); + + final PaperCard card = Aggregates.random(Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), myFilter)); + this.addSingleCard(card, 1); + return card; + } + + /** + * Adds the random rare. + * + * @param n + * the n + * @return the list + */ + public List addRandomRare(final int n) { + final Predicate myFilter = applyFormatFilter(QuestUtilCards.RARE_PREDICATE); + + final List newCards = Aggregates.random(Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), myFilter), n); + this.addAllCards(newCards); + return newCards; + } + + /** + * Setup new game card pool. + * + * @param filter + * the filter + * @param idxDifficulty + * the idx difficulty + * @param userPrefs + * user preferences + */ + public void setupNewGameCardPool(final Predicate filter, final int idxDifficulty, final StartingPoolPreferences userPrefs) { + final int nC = this.qpref.getPrefInt(DifficultyPrefs.STARTING_COMMONS, idxDifficulty); + final int nU = this.qpref.getPrefInt(DifficultyPrefs.STARTING_UNCOMMONS, idxDifficulty); + final int nR = this.qpref.getPrefInt(DifficultyPrefs.STARTING_RARES, idxDifficulty); + + this.addAllCards(BoosterUtils.getQuestStarterDeck(filter, nC, nU, nR, userPrefs)); + } + + /** + * Buy card. + * + * @param card + * the card + * @param qty + * quantity + * @param value + * the value + */ + public void buyCard(final PaperCard card, int qty, final int value) { + int totalCost = qty * value; + if (this.qa.getCredits() >= totalCost) { + this.qa.setCredits(this.qa.getCredits() - totalCost); + this.qa.getShopList().remove(card, qty); + this.addSingleCard(card, qty); + } + } + + /** + * Buy booster. + * + * @param booster + * the booster + * @param value + * the value + */ + public void buyPack(final SealedProduct booster, final int value) { + if (this.qa.getCredits() >= value) { + this.qa.setCredits(this.qa.getCredits() - value); + this.qa.getShopList().remove(booster); + this.addAllCards(booster.getCards()); + } + } + + /** + * Buy precon deck. + * + * @param precon + * the precon + * @param value + * the value + */ + public void buyPreconDeck(final PreconDeck precon, final int value) { + if (this.qa.getCredits() >= value) { + this.qa.setCredits(this.qa.getCredits() - value); + this.qa.getShopList().remove(precon); + this.addDeck(precon.getDeck()); + } + } + + /** + * Import an existing deck. + * + * @param fromDeck + * Deck, deck to import + */ + void addDeck(final Deck fromDeck) { + if (fromDeck == null) { + return; + } + this.qc.getMyDecks().add(fromDeck); + this.addAllCards(fromDeck.getMain().toFlatList()); + if (fromDeck.has(DeckSection.Sideboard)) { + this.addAllCards(fromDeck.get(DeckSection.Sideboard).toFlatList()); + } + } + + /** + * Sell card. + * + * @param card + * the card + * @param qty + * quantity + * @param pricePerCard + * the price per card + */ + public void sellCard(final PaperCard card, int qty, final int pricePerCard) { + this.sellCard(card, qty, pricePerCard, true); + } + + /** + * lose card. + * + * @param card + * the card + * @param qty + * quantity + */ + public void loseCards(final List cards) { + for(PaperCard pc: cards) + this.sellCard(pc, 1, 0, this.qc.getAssets().getItemLevel(QuestItemType.CASH_STAKES) > 0); + } + + /** + * Sell card. + * + * @param card + * the card + * @param price + * the price + * @param addToShop + * true if this card should be added to the shop, false otherwise + */ + private void sellCard(final PaperCard card, int qty, final int pricePerCard, final boolean addToShop) { + if (pricePerCard > 0) { + this.qa.setCredits(this.qa.getCredits() + (qty * pricePerCard)); + } + this.qa.getCardPool().remove(card, qty); + if (addToShop) { + this.qa.getShopList().add(card, qty); + } + + // remove card being sold from all decks + final int leftInPool = this.qa.getCardPool().count(card); + // remove sold cards from all decks: + for (final Deck deck : this.qc.getMyDecks()) { + int cntInMain = deck.getMain().count(card); + int cntInSb = deck.has(DeckSection.Sideboard) ? deck.get(DeckSection.Sideboard).count(card) : 0; + int nToRemoveFromThisDeck = cntInMain + cntInSb - leftInPool; + if (nToRemoveFromThisDeck <= 0) { + continue; // this is not the deck you are looking for + } + + int nToRemoveFromSb = Math.min(cntInSb, nToRemoveFromThisDeck); + if (nToRemoveFromSb > 0) { + deck.get(DeckSection.Sideboard).remove(card, nToRemoveFromSb); + nToRemoveFromThisDeck -= nToRemoveFromSb; + if (0 >= nToRemoveFromThisDeck) { + continue; // done here + } + } + + deck.getMain().remove(card, nToRemoveFromThisDeck); + } + } + + /** + * Clear shop list. + */ + public void clearShopList() { + if (null != this.qa.getShopList()) { + this.qa.getShopList().clear(); + } + } + + /** + * Gets the sell mutliplier. + * + * @return the sell mutliplier + */ + public double getSellMultiplier() { + double multi = 0.20 + (0.001 * this.qc.getAchievements().getWin()); + if (multi > 0.6) { + multi = 0.6; + } + + final int lvlEstates = this.qc.getMode() == QuestMode.Fantasy ? this.qa.getItemLevel(QuestItemType.ESTATES) : 0; + switch (lvlEstates) { + case 1: + multi += 0.01; + break; + case 2: + multi += 0.0175; + break; + case 3: + multi += 0.025; + break; + default: + break; + } + + return multi; + } + + /** + * Gets the sell price limit. + * + * @return the sell price limit + */ + public int getSellPriceLimit() { + return this.qc.getAchievements().getWin() <= 50 ? 1000 : Integer.MAX_VALUE; + } + + /** + * Generate cards in shop. + */ + private final GameFormat.Collection formats = FModel.getFormats(); + private final Predicate filterExt = this.formats.getExtended().editionLegalPredicate; + + /** The filter t2booster. */ + private final Predicate filterT2booster = Predicates.and(CardEdition.Predicates.CAN_MAKE_BOOSTER, + this.formats.getStandard().editionLegalPredicate); + + /** The filter ext but t2. */ + private final Predicate filterExtButT2 = Predicates.and( + CardEdition.Predicates.CAN_MAKE_BOOSTER, + Predicates.and(this.filterExt, this.formats.getStandard().editionLegalPredicate)); + + /** The filter not ext. */ + private final Predicate filterNotExt = Predicates.and(CardEdition.Predicates.CAN_MAKE_BOOSTER, + Predicates.not(this.filterExt)); + + /** + * Helper predicate for shops: is legal in quest format. + * + * @param qFormat + * the quest format + * @return the predicate + */ + public static Predicate isLegalInQuestFormat(final GameFormatQuest qFormat) { + return GameFormatQuest.Predicates.isLegalInFormatQuest(qFormat); + } + + /** + * Generate boosters in shop. + * + * @param count + * the count + */ + private void generateBoostersInShop(final int count) { + for (int i = 0; i < count; i++) { + final int rollD100 = MyRandom.getRandom().nextInt(100); + Predicate filter = rollD100 < 40 ? this.filterT2booster + : (rollD100 < 75 ? this.filterExtButT2 : this.filterNotExt); + if (qc.getFormat() != null) { + filter = Predicates.and(CardEdition.Predicates.CAN_MAKE_BOOSTER, isLegalInQuestFormat(qc.getFormat())); + } + Iterable rightEditions = Iterables.filter(FModel.getMagicDb().getEditions(), filter); + this.qa.getShopList().add(BoosterPack.FN_FROM_SET.apply(Aggregates.random(rightEditions))); + } + } + + /** + * Generate precons in shop. + * + * @param count + * the count + */ + private void generateTournamentsInShop(final int count) { + Predicate formatFilter = CardEdition.Predicates.HAS_TOURNAMENT_PACK; + if (qc.getFormat() != null) { + formatFilter = Predicates.and(formatFilter, isLegalInQuestFormat(qc.getFormat())); + } + Iterable rightEditions = Iterables.filter(FModel.getMagicDb().getEditions(), formatFilter); + this.qa.getShopList().addAllFlat(Aggregates.random(Iterables.transform(rightEditions, TournamentPack.FN_FROM_SET), count)); + } + + /** + * Generate precons in shop. + * + * @param count + * the count + */ + private void generateFatPacksInShop(final int count) { + Predicate formatFilter = CardEdition.Predicates.HAS_FAT_PACK; + if (qc.getFormat() != null) { + formatFilter = Predicates.and(formatFilter, isLegalInQuestFormat(qc.getFormat())); + } + Iterable rightEditions = Iterables.filter(FModel.getMagicDb().getEditions(), formatFilter); + this.qa.getShopList().addAllFlat(Aggregates.random(Iterables.transform(rightEditions, FatPack.FN_FROM_SET), count)); + } + + /** + * Generate precons in shop. + * + * @param count + * the count + */ + private void generatePreconsInShop(final int count) { + final List meetRequirements = new ArrayList(); + for (final PreconDeck deck : QuestController.getPrecons()) { + if (QuestController.getPreconDeals(deck).meetsRequiremnts(this.qc.getAchievements()) + && (null == qc.getFormat() || qc.getFormat().isSetLegal(deck.getEdition()))) { + meetRequirements.add(deck); + } + } + this.qa.getShopList().addAllFlat(Aggregates.random(meetRequirements, count)); + } + + @SuppressWarnings("unchecked") + private SealedProduct.Template getShopBoosterTemplate() { + return new SealedProduct.Template(Lists.newArrayList( + Pair.of(BoosterSlots.COMMON, this.qpref.getPrefInt(QPref.SHOP_SINGLES_COMMON)), + Pair.of(BoosterSlots.UNCOMMON, this.qpref.getPrefInt(QPref.SHOP_SINGLES_UNCOMMON)), + Pair.of(BoosterSlots.RARE_MYTHIC, this.qpref.getPrefInt(QPref.SHOP_SINGLES_RARE)) + )); + } + + @SuppressWarnings("unchecked") + private SealedProduct.Template getBoosterTemplate() { + return new SealedProduct.Template(Lists.newArrayList( + Pair.of(BoosterSlots.COMMON, this.qpref.getPrefInt(QPref.BOOSTER_COMMONS)), + Pair.of(BoosterSlots.UNCOMMON, this.qpref.getPrefInt(QPref.BOOSTER_UNCOMMONS)), + Pair.of(BoosterSlots.RARE_MYTHIC, this.qpref.getPrefInt(QPref.BOOSTER_RARES)) + )); + } + + /** + * Generate cards in shop. + */ + private void generateCardsInShop() { + // Preferences + final int startPacks = this.qpref.getPrefInt(QPref.SHOP_STARTING_PACKS); + final int winsForPack = this.qpref.getPrefInt(QPref.SHOP_WINS_FOR_ADDITIONAL_PACK); + final int maxPacks = this.qpref.getPrefInt(QPref.SHOP_MAX_PACKS); + + int level = this.qc.getAchievements().getLevel(); + final int levelPacks = level > 0 ? startPacks / level : startPacks; + final int winPacks = this.qc.getAchievements().getWin() / winsForPack; + final int totalPacks = Math.min(levelPacks + winPacks, maxPacks); + + + SealedProduct.Template tpl = getShopBoosterTemplate(); + UnOpenedProduct unopened = qc.getFormat() == null ? new UnOpenedProduct(tpl) : new UnOpenedProduct(tpl, qc.getFormat().getFilterPrinted()); + + for (int i = 0; i < totalPacks; i++) { + this.qa.getShopList().addAllFlat(unopened.get()); + } + + this.generateBoostersInShop(totalPacks); + this.generatePreconsInShop(totalPacks); + this.generateTournamentsInShop(totalPacks); + this.generateFatPacksInShop(totalPacks); + int numberSnowLands = 5; + if (qc.getFormat() != null && !qc.getFormat().hasSnowLands()) { + numberSnowLands = 0; + } + this.qa.getShopList().addAll(QuestUtilCards.generateBasicLands(10, numberSnowLands, qc.getFormat())); + } + + /** + * Gets the cardpool. + * + * @return the cardpool + */ + public ItemPool getCardpool() { + return this.qa.getCardPool(); + } + + /** + * Gets the shop list. + * + * @return the shop list + */ + public ItemPool getShopList() { + if (this.qa.getShopList().isEmpty()) { + this.generateCardsInShop(); + } + return this.qa.getShopList(); + } + + /** + * Gets the new cards. + * + * @return the new cards + */ + public ItemPool getNewCards() { + return this.qa.getNewCardList(); + } + + /** + * Reset new list. + */ + public void resetNewList() { + this.qa.getNewCardList().clear(); + } + + public Function, Comparable> getFnNewCompare() { + return this.fnNewCompare; + } + + public Function, Object> getFnNewGet() { + return this.fnNewGet; + } + + // These functions provide a way to sort and compare cards in a table + // according to their new-ness + // It might be a good idea to store them in a base class for both quest-mode + // deck editors + // Maybe we should consider doing so later + /** The fn new compare. */ + private final Function, Comparable> fnNewCompare = + new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return QuestUtilCards.this.qa.getNewCardList().contains(from.getKey()) ? Integer.valueOf(1) : Integer + .valueOf(0); + } + }; + + /** The fn new get. */ + private final Function, Object> fnNewGet = + new Function, Object>() { + @Override + public Object apply(final Entry from) { + return QuestUtilCards.this.qa.getNewCardList().contains(from.getKey()) ? "NEW" : ""; + } + }; + + public Function, Comparable> getFnOwnedCompare() { + return this.fnOwnedCompare; + } + + public Function, Object> getFnOwnedGet() { + return this.fnOwnedGet; + } + + public int getCompletionPercent(String edition) { + // get all cards in the specified edition + Predicate filter = IPaperCard.Predicates.printedInSet(edition); + Iterable editionCards = Iterables.filter(FModel.getMagicDb().getCommonCards().getAllCards(), filter); + + ItemPool ownedCards = qa.getCardPool(); + // 100% means at least one of every basic land and at least 4 of every other card in the set + int completeCards = 0; + int numOwnedCards = 0; + for (PaperCard card : editionCards) { + final int target = CardRarity.BasicLand == card.getRarity() ? 1 : 4; + + completeCards += target; + numOwnedCards += Math.min(target, ownedCards.count(card)); + } + + return (numOwnedCards * 100) / completeCards; + } + + // These functions provide a way to sort and compare items in the spell shop according to how many are already owned + private final Function, Comparable> fnOwnedCompare = + new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + InventoryItem i = from.getKey(); + if (i instanceof PaperCard) { + return QuestUtilCards.this.qa.getCardPool().count((PaperCard) i); + } else if (i instanceof PreconDeck) { + PreconDeck pDeck = (PreconDeck) i; + return FModel.getQuest().getMyDecks().contains(pDeck.getName()) ? -1 : -2; + } else if (i instanceof SealedProduct) { + SealedProduct oPack = (SealedProduct) i; + return getCompletionPercent(oPack.getEdition()) - 103; + } + return null; + } + }; + + private final Function, Object> fnOwnedGet = + new Function, Object>() { + @Override + public Object apply(final Entry from) { + InventoryItem i = from.getKey(); + if (i instanceof PaperCard) { + return QuestUtilCards.this.qa.getCardPool().count((PaperCard) i); + } else if (i instanceof PreconDeck) { + PreconDeck pDeck = (PreconDeck) i; + return FModel.getQuest().getMyDecks().contains(pDeck.getName()) ? "YES" : "NO"; + } else if (i instanceof SealedProduct) { + SealedProduct oPack = (SealedProduct) i; + return String.format("%d%%", getCompletionPercent(oPack.getEdition())); + } + return null; + } + }; +} diff --git a/forge-m-base/src/forge/quest/QuestUtilUnlockSets.java b/forge-m-base/src/forge/quest/QuestUtilUnlockSets.java new file mode 100644 index 00000000000..66e9f442fe2 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestUtilUnlockSets.java @@ -0,0 +1,202 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Nate + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import forge.card.CardEdition; +import forge.card.UnOpenedProduct; +import forge.deck.Deck; +import forge.deck.FDeckViewer; +import forge.item.PaperCard; +import forge.item.SealedProduct; +import forge.model.FModel; +import forge.quest.io.ReadPriceList; +import forge.toolbox.FOptionPane; +import forge.toolbox.GuiChoose; +import forge.util.storage.IStorage; + +import org.apache.commons.lang3.tuple.ImmutablePair; + +import java.util.*; + +/** + * This is a helper class for unlocking new sets during a format-limited + * quest. + * + */ +public class QuestUtilUnlockSets { + private static int UNLOCK_COST = 4000; + + /** + * Consider unlocking a new expansion in limited quest format. + * @param qData the QuestController for the current quest + * @param freeUnlock this unlock is free (e.g., a challenge reward), NOT IMPLEMENTED YET + * @param presetChoices List a pregenerated list of options, NOT IMPLEMENTED YET + * @return CardEdition, the unlocked edition if any. + */ + public static ImmutablePair chooseSetToUnlock(final QuestController qData, final boolean freeUnlock, + List presetChoices) { + + if (qData.getFormat() == null || !qData.getFormat().canUnlockSets()) { + return null; + } + + final ReadPriceList prices = new ReadPriceList(); + final Map mapPrices = prices.getPriceList(); + final List> setPrices = new ArrayList>(); + + for (CardEdition ed : getUnlockableEditions(qData)) { + int price = UNLOCK_COST; + if (mapPrices.containsKey(ed.getName() + " Booster Pack")) { + price = Math.max(new Double(30 * Math.pow(Math.sqrt(mapPrices.get(ed.getName() + + " Booster Pack")), 1.70)).intValue(), UNLOCK_COST); + } + setPrices.add(ImmutablePair.of(ed, price)); + } + + final String setPrompt = "You have " + qData.getAssets().getCredits() + " credits. Unlock:"; + List options = new ArrayList(); + for (ImmutablePair ee : setPrices) { + options.add(String.format("%s [PRICE: %d credits]", ee.left.getName(), ee.right)); + } + + int index = options.indexOf(GuiChoose.oneOrNone(setPrompt, options)); + if (index < 0 || index >= options.size()) { + return null; + } + + ImmutablePair toBuy = setPrices.get(index); + + int price = toBuy.right; + CardEdition choosenEdition = toBuy.left; + + if (qData.getAssets().getCredits() < price) { + FOptionPane.showMessageDialog("Unfortunately, you cannot afford that set yet.\n" + + "To unlock " + choosenEdition.getName() + ", you need " + price + " credits.\n" + + "You have only " + qData.getAssets().getCredits() + " credits.", + "Failed to unlock " + choosenEdition.getName(), + null); + return null; + } + + if (!FOptionPane.showConfirmDialog( + "Unlocking " + choosenEdition.getName() + " will cost you " + price + " credits.\n" + + "You have " + qData.getAssets().getCredits() + " credits.\n\n" + + "Are you sure you want to unlock " + choosenEdition.getName() + "?", + "Confirm Unlocking " + choosenEdition.getName())) { + return null; + } + return toBuy; + } + + /** + * Helper function for unlockSet(). + * + * @return unmodifiable list, assorted sets that are not currently in the format. + */ + private static final List emptyEditions = ImmutableList.of(); + private static final EnumSet unlockableSetTypes = + EnumSet.of(CardEdition.Type.CORE, CardEdition.Type.EXPANSION, CardEdition.Type.REPRINT, CardEdition.Type.STARTER); + + private static List getUnlockableEditions(final QuestController qData) { + if (qData.getFormat() == null || !qData.getFormat().canUnlockSets()) { + return emptyEditions; + } + + if (qData.getUnlocksTokens() < 1) { // Should never happen if we made it this far but better safe than sorry... + throw new RuntimeException("BUG? Could not find unlockable sets even though we should."); + } + List options = new ArrayList(); + + // Sort current sets by date + List allowedSets = Lists.newArrayList(Iterables.transform(qData.getFormat().getAllowedSetCodes(), FModel.getMagicDb().getEditions().FN_EDITION_BY_CODE)); + Collections.sort(allowedSets); + + // Sort unlockable sets by date + List excludedSets = Lists.newArrayList(Iterables.transform(qData.getFormat().getLockedSets(), FModel.getMagicDb().getEditions().FN_EDITION_BY_CODE)); + Collections.sort(excludedSets); + + // get a number of sets between an excluded and any included set + List> excludedWithDistances = new ArrayList>(); + for (CardEdition ex : excludedSets) { + if (!unlockableSetTypes.contains(ex.getType())) // don't add non-traditional sets + continue; + long distance = Long.MAX_VALUE; + for (CardEdition in : allowedSets) { + long d = (Math.abs(ex.getDate().getTime() - in.getDate().getTime())); + if (d < distance) { + distance = d; + } + } + excludedWithDistances.add(ImmutablePair.of(ex, distance)); + } + + // sort by distance, then by code desc + Collections.sort(excludedWithDistances, new Comparator>() { + @Override + public int compare(ImmutablePair o1, ImmutablePair o2) { + long delta = o2.right - o1.right; + return delta < 0 ? -1 : delta == 0 ? 0 : 1; + } + }); + + + for (ImmutablePair set : excludedWithDistances) { + options.add(set.left); + // System.out.println("Padded with: " + fillers.get(i).getName()); + } + Collections.reverse(options); + + return options.subList(0, Math.min(options.size(), Math.min(8, 2 + ((qData.getAchievements().getWin()) / 50)))); + } + + /** + * + * Unlock a set and get some free cards, if a tournament pack or boosters are available. + * @param qData the quest controller + * @param unlockedSet the edition to unlock + */ + public static void doUnlock(QuestController qData, final CardEdition unlockedSet) { + + IStorage starters = FModel.getMagicDb().getTournamentPacks(); + IStorage boosters = FModel.getMagicDb().getBoosters(); + qData.getFormat().unlockSet(unlockedSet.getCode()); + + List cardsWon = new ArrayList(); + + if (starters.contains(unlockedSet.getCode())) { + UnOpenedProduct starter = new UnOpenedProduct(starters.get(unlockedSet.getCode())); + cardsWon.addAll(starter.get()); + } + else if (boosters.contains(unlockedSet.getCode())) { + UnOpenedProduct booster = new UnOpenedProduct(boosters.get(unlockedSet.getCode())); + cardsWon.addAll(booster.get()); + cardsWon.addAll(booster.get()); + cardsWon.addAll(booster.get()); + } + + qData.getCards().addAllCards(cardsWon); + Deck deck = new Deck(unlockedSet.getName() + " - You get the following bonus cards:"); + deck.getMain().addAllFlat(cardsWon); + FDeckViewer.show(deck); + qData.save(); + } +} diff --git a/forge-m-base/src/forge/quest/QuestWorld.java b/forge-m-base/src/forge/quest/QuestWorld.java new file mode 100644 index 00000000000..2f41afb9e17 --- /dev/null +++ b/forge-m-base/src/forge/quest/QuestWorld.java @@ -0,0 +1,184 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Nate + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import com.google.common.base.Function; +import forge.quest.data.GameFormatQuest; +import forge.util.storage.StorageReaderFile; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * This function holds the "world info" for the current quest. + * + */ +public class QuestWorld implements Comparable{ + private final String name; + private final String dir; + private final GameFormatQuest format; + + /** + * Instantiate a new quest world. + * @param useIdx int, the quest world internal identifier + * @param useName String, the display name for the world + * @param useDir String, the basedir that contains the duels and challenges for the quest world + * @param useFormat GameFormatQuest that contains the initial format for the world + */ + public QuestWorld(final String useName, final String useDir, final GameFormatQuest useFormat) { + name = useName; + dir = useDir; + format = useFormat; + } + + /** + * The quest world display name. + * @return String, the display name + */ + public String getName() { + return name; + } + + /** + * The quest world duels directory. + * @return String, the duels directory + */ + public String getDuelsDir() { + return dir == null ? null : dir + "/duels"; + } + + /** + * The quest world challenges directory. + * @return String, the challenges directory + */ + public String getChallengesDir() { + return dir == null ? null : dir + "/challenges"; + } + + /** + * The quest world format if specified. + * @return GameFormatQuest, the format + */ + public GameFormatQuest getFormat() { + return format; + } + + /** + *

+ * toString. + *

+ * + * @return a {@link java.lang.String} object. + */ + @Override + public final String toString() { + return this.getName(); + } + + /** + * FN_GET_NAME for reader. + */ + public static final Function FN_GET_NAME = new Function() { + + @Override + public String apply(QuestWorld arg1) { + return arg1.getName(); + } + }; + + /** + * Class for reading world definitions. + */ + public static class Reader extends StorageReaderFile { + + /** + * TODO: Write javadoc for Constructor. + * @param file0 + * @param keySelector0 + */ + public Reader(String file0) { + super(file0, QuestWorld.FN_GET_NAME); + } + + /* (non-Javadoc) + * @see forge.util.StorageReaderFile#read(java.lang.String) + */ + @Override + protected QuestWorld read(String line, int i) { + String useName = null; + String useDir = null; + GameFormatQuest useFormat = null; + + final List sets = new ArrayList(); + final List bannedCards = new ArrayList(); // if both empty, no format + + // This is what you need to use here => + // FileSection.parse(line, ":", "|"); + + final String[] sParts = line.trim().split("\\|"); + + for (final String sPart : sParts) { + + final String[] kv = sPart.split(":", 2); + final String key = kv[0].toLowerCase(); + if ("name".equals(key)) { + useName = kv[1]; + } else if ("dir".equals(key)) { + useDir = kv[1]; + } else if ("sets".equals(key)) { + sets.addAll(Arrays.asList(kv[1].split(", "))); + } else if ("banned".equals(key)) { + bannedCards.addAll(Arrays.asList(kv[1].split("; "))); + } + } + + if (useName == null) { + throw new RuntimeException("A Quest World must have a name! Check worlds.txt file"); + } + + if (!sets.isEmpty() || !bannedCards.isEmpty()) { + useFormat = new GameFormatQuest(useName, sets, bannedCards); + } + + // System.out.println("Creating quest world " + useName + " (index " + useIdx + ", dir: " + useDir); + // if (useFormat != null) { System.out.println("SETS: " + sets + "\nBANNED: " + bannedCards); } + + return new QuestWorld(useName, useDir, useFormat); + + } + + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(QuestWorld other) { + if (null == other) { + return 1; + } + if (name == other.name) { + return 0; + } + if (null == name) { + return -1; + } + return name.compareTo(other.name); + } +} diff --git a/forge-m-base/src/forge/quest/SellRules.java b/forge-m-base/src/forge/quest/SellRules.java new file mode 100644 index 00000000000..d796b14c0b5 --- /dev/null +++ b/forge-m-base/src/forge/quest/SellRules.java @@ -0,0 +1,88 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +import forge.quest.data.QuestAchievements; +import forge.util.FileSection; + +import java.util.List; + +/** + * TODO: Write javadoc for this type. + * + */ +public class SellRules { + + private int minWins = 0; + private int cost = 250; + private int minDifficulty = 0; + private int maxDifficulty = 5; + + /** + * Instantiates a new sell rules. + * + * @param questShop the quest shop + */ + public SellRules(List questShop) { + if (null == questShop || questShop.isEmpty()) { + return; + } + + FileSection section = FileSection.parse(questShop, "="); + minWins = section.getInt("WinsToUnlock"); + cost = section.getInt("Credits", 250); + maxDifficulty = section.getInt("MaxDifficulty", 5); + minDifficulty = section.getInt("MinDifficulty", 0); + } + + /** + * Meets requiremnts. + * + * @param quest the quest + * @return true, if successful + */ + public boolean meetsRequiremnts(QuestAchievements quest) { + if (quest.getWin() < minWins) { + return false; + } + if (quest.getDifficulty() < minDifficulty || quest.getDifficulty() > maxDifficulty) { + return false; + } + + return true; + } + + /** + * Gets the cost. + * + * @return the cost + */ + public final int getCost() { + return cost; + } + + /** + * Gets the minWins. + * + * @return the minWins + */ + public final int getMinWins() { + return minWins; + } + +} diff --git a/forge-m-base/src/forge/quest/StartingPoolPreferences.java b/forge-m-base/src/forge/quest/StartingPoolPreferences.java new file mode 100644 index 00000000000..3609c33c29a --- /dev/null +++ b/forge-m-base/src/forge/quest/StartingPoolPreferences.java @@ -0,0 +1,61 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Nate + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest; + +/** + * This class is used to store the Quest starting pool preferences. + * (It could be expanded to store other Quest starting preferences as well, + * in order to reduce the number of parameters that need to be passed to + * QuestController.newGame from CSubmenuQuestData) + * + */ +public final class StartingPoolPreferences { + + private final boolean randomPool; + private final byte preferredColor; + + /** + * The constructor. + * @param random + * true = use completely random pool without filter restrictions + * (Note that this does NOT bypass card rarity restrictions!) + * @param preference + * preferred color/COLORLESS (ALL_COLORS = no preference) + */ + public StartingPoolPreferences(final boolean random, final byte preference) { + randomPool = random; + preferredColor = preference; + } + + /** + * Is the starting pool completely random? + * @return boolean, true if the starting pool is completely random (except for rarity) + */ + public boolean useRandomPool() { + return randomPool; + } + + /** + * Get the preferred starting pool color. + * Return ALL_COLORS if no preference set. + * @return MagicColor + */ + public byte getPreferredColor() { + return preferredColor; + } +} diff --git a/forge-m-base/src/forge/quest/StartingPoolType.java b/forge-m-base/src/forge/quest/StartingPoolType.java new file mode 100644 index 00000000000..3f2bcb5d0fb --- /dev/null +++ b/forge-m-base/src/forge/quest/StartingPoolType.java @@ -0,0 +1,25 @@ +package forge.quest; + +public enum StartingPoolType { + Complete("Unrestricted"), + Rotating("Sanctioned format"), + CustomFormat("Custom format"), + Precon("Event or starter deck"), + SealedDeck("My sealed deck"), + DraftDeck("My draft deck"), + Cube("Predefined cube"); + + private final String caption; + + private StartingPoolType(String caption0) { + caption = caption0; + } + + /* (non-Javadoc) + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return caption; + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/IQuestBazaarItem.java b/forge-m-base/src/forge/quest/bazaar/IQuestBazaarItem.java new file mode 100644 index 00000000000..b097308f5e8 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/IQuestBazaarItem.java @@ -0,0 +1,89 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import forge.assets.FImage; +import forge.quest.data.QuestAssets; + +/** + * This interface defines a thing that can be sold at the Bazaar. + * + * @author Forge + * @version $Id: IQuestBazaarItem.java 23229 2013-09-16 08:18:19Z drdev $ + */ +public interface IQuestBazaarItem extends Comparable { + /** + *

+ * getPurchaseName. + *

+ * + * @return The Name of the item + */ + String getPurchaseName(); + + /** + *

+ * getPurchaseDescription. + *

+ * + * @return an HTML formatted item description + */ + String getPurchaseDescription(QuestAssets qA); + + /** + *

+ * getIcon. + *

+ * + * @return the icon that is displayed in the bazaar + */ + FImage getIcon(QuestAssets qA); + + /** + *

+ * getPrice. + *

+ * + * @return the buying cost of the item in credits + */ + int getBuyingPrice(QuestAssets qA); + + /** + *

+ * getPrice. + *

+ * + * @return the selling cost of the item in credits + */ + int getSellingPrice(QuestAssets qA); + + /** + * Returns if the item is available for purchase;. + * + * @return true if the item can be displayed in a store + * false if the item should not be displayed in store + * since, for example, prerequisites are not met + */ + boolean isAvailableForPurchase(QuestAssets questAssets); + + /** + * Executed when the item is bought. + * @param questAssets + */ + void onPurchase(QuestAssets questAssets); +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestBazaarManager.java b/forge-m-base/src/forge/quest/bazaar/QuestBazaarManager.java new file mode 100644 index 00000000000..fae28f1cde9 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestBazaarManager.java @@ -0,0 +1,185 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import com.thoughtworks.xstream.XStream; + +import forge.model.FModel; +import forge.quest.QuestController; +import forge.quest.data.QuestAssets; +import forge.utils.IgnoringXStream; +import forge.utils.XmlUtil; + +import org.w3c.dom.*; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + *

+ * QuestStallManager class. + *

+ * + * @author Forge + * @version $Id: QuestBazaarManager.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public class QuestBazaarManager { + private final File xmlFile; + + public QuestBazaarManager(File xmlFile0) { + xmlFile = xmlFile0; + } + + public void load() { + DocumentBuilder builder; + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + final Document document = builder.parse(xmlFile); + + XStream xs = new IgnoringXStream(); + xs.autodetectAnnotations(true); + + NodeList xmlStalls = document.getElementsByTagName("stalls").item(0).getChildNodes(); + for (int iN = 0; iN < xmlStalls.getLength(); iN++) { + Node n = xmlStalls.item(iN); + if (n.getNodeType() != Node.ELEMENT_NODE) { continue; } + + Attr att = document.createAttribute("resolves-to"); + att.setValue(QuestStallDefinition.class.getCanonicalName()); + n.getAttributes().setNamedItem(att); + QuestStallDefinition stall = (QuestStallDefinition) xs.fromXML(XmlUtil.nodeToString(n)); + stalls.put(stall.getName(), stall); + } + + NodeList xmlQuestItems = document.getElementsByTagName("questItems").item(0).getChildNodes(); + for (int iN = 0; iN < xmlQuestItems.getLength(); iN++) { + Node n = xmlQuestItems.item(iN); + if (n.getNodeType() != Node.ELEMENT_NODE) { continue; } + + NamedNodeMap attrs = n.getAttributes(); + String sType = attrs.getNamedItem("itemType").getTextContent(); + String name = attrs.getNamedItem("name").getTextContent(); + QuestItemType qType = QuestItemType.smartValueOf(sType); + Attr att = document.createAttribute("resolves-to"); + att.setValue(qType.getBazaarControllerClass().getCanonicalName()); + attrs.setNamedItem(att); + QuestItemBasic ctrl = (QuestItemBasic) xs.fromXML(XmlUtil.nodeToString(n)); + items.put(name, ctrl); + } + + } catch (SAXException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + } + + /** Constant stalls. */ + private final Map stalls = new TreeMap(); + /** Constant items. */ + private final Map> itemsOnStalls = new TreeMap>(String.CASE_INSENSITIVE_ORDER); + private final Map items = new TreeMap(); + + /** + *

+ * getStall. + *

+ * + * @param stallName + * a {@link java.lang.String} object. + * @return a {@link forge.quest.bazaar.QuestStallDefinition} object. + */ + public QuestStallDefinition getStall(final String stallName) { + if (stalls.isEmpty()) { + load(); + } + + return stalls.get(stallName); + } + + /** + * Retrieves all creatures and items, iterates through them, + * and maps to appropriate merchant. + */ + public void buildItems(final QuestController qCtrl) { + final Map itemSet = new HashMap(); + + for (int iSlot = 0; iSlot < QuestController.MAX_PET_SLOTS; iSlot++) { + + for (QuestPetController pet : qCtrl.getPetsStorage().getAllPets(iSlot)) { + //System.out.println("Pet: " + pet.getName()); + itemSet.put(pet.getName(), pet); + } + } + + itemSet.putAll(items); + + itemsOnStalls.clear(); + + for (QuestStallDefinition thisStall : stalls.values()) { + TreeSet set = new TreeSet(); + + for (String itemName : thisStall.getItems()) { + IQuestBazaarItem item = itemSet.get(itemName); + //System.out.println(itemName); + set.add(item); + } + itemsOnStalls.put(thisStall.getName(), set); + } + } + + /** + * Returns purchasable items available for a particular stall. + * + * @param stallName   {@link java.lang.String} + * @return {@link java.util.List}. + */ + public List getItems(final QuestController qCtrl, final String stallName) { + buildItems(qCtrl); + + final List ret = new ArrayList(); + + QuestAssets qA = FModel.getQuest().getAssets(); + for (final IQuestBazaarItem purchasable : itemsOnStalls.get(stallName)) { + if (purchasable.isAvailableForPurchase(qA)) { + ret.add(purchasable); + } + } + return ret; + } + + /** + * TODO: Write javadoc for this method. + * @return + */ + public Set getStallNames() { + if (stalls.isEmpty()) { + load(); + } + return stalls.keySet(); + } + +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestItemBasic.java b/forge-m-base/src/forge/quest/bazaar/QuestItemBasic.java new file mode 100644 index 00000000000..01ef3a5c7bc --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestItemBasic.java @@ -0,0 +1,194 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +import forge.assets.FImage; +import forge.quest.data.QuestAssets; + +import org.apache.commons.lang3.StringUtils; + +/** + *

+ * Abstract QuestItemAbstract class. + *

+ * + * @author Forge + * @version $Id: QuestItemBasic.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public class QuestItemBasic implements IQuestBazaarItem { + + @XStreamAsAttribute + private QuestItemType itemType; + + /** + * Gets the item type. + * + * @return the item type + */ + public final QuestItemType getItemType() { + return this.itemType; + } + + @XStreamAsAttribute + private int maxLevel = 1; + + @XStreamAsAttribute + private String purchaseName = null; + + private String description = "Read from XML"; + + @XStreamAsAttribute + private int basePrice = 1000; + + /** + * Gets the base price. + * + * @return the base price + */ + protected final int getBasePrice() { + return this.basePrice; + } + + @XStreamAsAttribute + private final FImage icon = null; + + /** + *

+ * Constructor for QuestItemAbstract. + *

+ * + * @param type0 the type0 + */ + protected QuestItemBasic(final QuestItemType type0) { + this.itemType = type0; + } + + /** + * This is the name shared across all item levels e.g., "Estates". + * + * @return a {@link java.lang.String} object. + */ + public final String getName() { + return this.itemType.getKey(); + } + + /** + * This is the name used in purchasing the item e.g.,"Estates Training 1". + * + * @return a {@link java.lang.String} object. + */ + @Override + public String getPurchaseName() { + return StringUtils.isBlank(this.purchaseName) ? this.getName() : this.purchaseName; + } + + /** + * This method will be invoked when an item is bought in a shop. + * + * @param qA the q a + */ + @Override + public void onPurchase(final QuestAssets qA) { + final int currentLevel = qA.getItemLevel(this.itemType); + qA.setItemLevel(this.itemType, currentLevel + 1); + } + + /** + *

+ * isAvailableForPurchase. + *

+ * + * @param qA the q a + * @return a boolean. + */ + @Override + public boolean isAvailableForPurchase(final QuestAssets qA) { + return qA.getItemLevel(this.itemType) < this.maxLevel; + } + + /** + *

+ * Getter for the field maxLevel. + *

+ * + * @return a int. + */ + public final int getMaxLevel() { + return this.maxLevel; + } + + /** + *

+ * isLeveledItem. + *

+ * + * @return a boolean. + */ + public final boolean isLeveledItem() { + return this.maxLevel == 1; + } + + /** + *

+ * getPurchaseDescription. + *

+ * + * @param qA the q a + * @return a {@link java.lang.String} object. + */ + @Override + public String getPurchaseDescription(final QuestAssets qA) { + return this.description; + } + + @Override + public FImage getIcon(QuestAssets qA) { + return this.icon; + } + + /** + * Gets the buying price. + * + * @param qA the q a + * @return a int. + */ + @Override + public int getBuyingPrice(final QuestAssets qA) { + return this.basePrice; + } + + /** + * Gets the selling price. + * + * @param qA the q a + * @return a int. + */ + @Override + public int getSellingPrice(final QuestAssets qA) { + return 0; + } + + /** {@inheritDoc} */ + @Override + public final int compareTo(final Object o) { + final IQuestBazaarItem q = (IQuestBazaarItem) o; + return this.getPurchaseName().compareTo(q.getPurchaseName()); + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestItemCharmOfVigor.java b/forge-m-base/src/forge/quest/bazaar/QuestItemCharmOfVigor.java new file mode 100644 index 00000000000..ecb7d002408 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestItemCharmOfVigor.java @@ -0,0 +1,47 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import forge.quest.data.QuestAssets; + +/** + *

+ * QuestItemAmuletOfEndurance class. + *

+ * + * @author Forge + * @version $Id: QuestItemZeppelin.java 14797 2012-03-18 18:09:02Z Max mtg $ + */ +public class QuestItemCharmOfVigor extends QuestItemBasic { + /** + *

+ * Constructor for QuestItemAmuletOfEndurance. + *

+ */ + QuestItemCharmOfVigor() { + super(QuestItemType.CHARM); // , QuestStallManager.GEAR + } + + /** {@inheritDoc} */ + @Override + public final boolean isAvailableForPurchase(QuestAssets qA) { + return super.isAvailableForPurchase(qA); + } + + +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestItemElixir.java b/forge-m-base/src/forge/quest/bazaar/QuestItemElixir.java new file mode 100644 index 00000000000..5371d01cbcf --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestItemElixir.java @@ -0,0 +1,53 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import forge.quest.data.QuestAssets; + +/** + * This item has special coding. + * + * @author Forge + * @version $Id$ + */ +public class QuestItemElixir extends QuestItemBasic { + + /** + *

+ * Constructor for QuestItemElixir. + *

+ */ + QuestItemElixir() { + super(QuestItemType.ELIXIR_OF_LIFE); // QuestStallManager.ALCHEMIST, + } + + /** {@inheritDoc} */ + @Override + public final int getBuyingPrice(QuestAssets qA) { + int level = qA.getItemLevel(this.getItemType()); + if (level < 5) { + return super.getBasePrice(); + } else if (level < 10) { + return super.getBasePrice() * 2; + } else if (level <= this.getMaxLevel()) { + return super.getBasePrice() * 3; + } else { + return 0; + } + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestItemEstates.java b/forge-m-base/src/forge/quest/bazaar/QuestItemEstates.java new file mode 100644 index 00000000000..0943882fc23 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestItemEstates.java @@ -0,0 +1,54 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import forge.quest.data.QuestAssets; + +/** + *

+ * QuestItemEstates class. + *

+ * + * @author Forge + * @version $Id: QuestItemEstates.java 14797 2012-03-18 18:09:02Z Max mtg $ + */ +public class QuestItemEstates extends QuestItemBasic { + /** + *

+ * Constructor for QuestItemEstates. + *

+ */ + QuestItemEstates() { + super(QuestItemType.ESTATES); // QuestStallManager.BANKER, + } + + /** {@inheritDoc} */ + @Override + public final String getPurchaseDescription(QuestAssets qA) { + return String.format(super.getPurchaseDescription(qA), + (10 + (qA.getItemLevel(this.getItemType()) * 5)), + (1 + (qA.getItemLevel(this.getItemType()) * 0.75))); + } + + /** {@inheritDoc} */ + @Override + public final int getBuyingPrice(QuestAssets qA) { + int level = qA.getItemLevel(this.getItemType()); + return getBasePrice() * (2 + level); + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestItemPoundFlesh.java b/forge-m-base/src/forge/quest/bazaar/QuestItemPoundFlesh.java new file mode 100644 index 00000000000..3a973db4f1c --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestItemPoundFlesh.java @@ -0,0 +1,66 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import forge.quest.QuestMode; +import forge.quest.data.QuestAssets; + +/** + * This item has special coding. + * + * @author Forge + * @version $Id: QuestItemElixir.java 13728 2012-02-01 11:13:34Z moomarc $ + */ +public class QuestItemPoundFlesh extends QuestItemBasic { + + /** + *

+ * Constructor for QuestItemElixir. + *

+ */ + QuestItemPoundFlesh() { + super(QuestItemType.POUND_FLESH); // QuestStallManager.ALCHEMIST, + } + + /** {@inheritDoc} */ + @Override + public final String getPurchaseDescription(QuestAssets qA) { + return String.format(super.getPurchaseDescription(qA), getSellingPrice(qA)); + } + + /** {@inheritDoc} */ + @Override + public final int getBuyingPrice(QuestAssets qA) { + return 0; + } + + /** {@inheritDoc} */ + @Override + public final int getSellingPrice(QuestAssets qA) { + int level = qA.getItemLevel(this.getItemType()); + if (qA.getLife(QuestMode.Fantasy) < 2) { + return 0; + } else if (level < 5) { + return this.getBasePrice(); + } else if (level < 10) { + return this.getBasePrice() * 2; + } else { + return this.getBasePrice() * 3; + } + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestItemType.java b/forge-m-base/src/forge/quest/bazaar/QuestItemType.java new file mode 100644 index 00000000000..97e91328ed5 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestItemType.java @@ -0,0 +1,129 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Nate + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import forge.quest.data.QuestItemCondition; + +/** + * TODO: Write javadoc for this type. + * + */ +@XStreamAlias(value = "forge.quest.data.item.QuestItemType") +public enum QuestItemType { + + /** The SLEIGHT. */ + SLEIGHT("Sleight", QuestItemBasic.class, QuestItemCondition.class), + /** The ESTATES. */ + ESTATES("Estates", QuestItemEstates.class, QuestItemCondition.class), + /** The LUCKY_COIN. */ + LUCKY_COIN("Lucky Coin", QuestItemBasic.class, QuestItemCondition.class), + /** The MAP. */ + MAP("Map", QuestItemBasic.class, QuestItemCondition.class), + /** The ZEPPELIN. */ + ZEPPELIN("Zeppelin", QuestItemZeppelin.class, QuestItemCondition.class), + /** The ELIXIR_OF_LIFE. */ + ELIXIR_OF_LIFE("Elixir of Life", QuestItemElixir.class, QuestItemCondition.class), + /** The POUND_FLESH. */ + POUND_FLESH("Pound of Flesh", QuestItemPoundFlesh.class, QuestItemCondition.class), + /** The AMULET. */ + CHARM("Charm of Vigor", QuestItemCharmOfVigor.class, QuestItemCondition.class), + + CASH_STAKES("Cash Stakes", QuestItemBasic.class, QuestItemCondition.class); + + private final String saveFileKey; + private final Class bazaarControllerClass; + private final Class modelClass; + + private QuestItemType(final String key, final Class controllerClass0, + final Class modelClass0) { + this.saveFileKey = key; + this.bazaarControllerClass = controllerClass0; + this.modelClass = modelClass0; + } + + /** + * TODO: Write javadoc for this method. + * + * @return the key + */ + public String getKey() { + return this.saveFileKey; + } + + /** + * Gets the bazaar controller class. + * + * @return the bazaar controller class + */ + public Class getBazaarControllerClass() { + return this.bazaarControllerClass; + } + + /** + * Gets the model class. + * + * @return the model class + */ + public Class getModelClass() { + return this.modelClass; + } + + /** + * Smart value of. + * + * @param value the value + * @return the quest item type + */ + public static QuestItemType smartValueOf(final String value) { + if (value == null) { + return null; + } + if ("All".equals(value)) { + return null; + } + final String valToCompate = value.trim(); + for (final QuestItemType v : QuestItemType.values()) { + if (v.name().compareToIgnoreCase(valToCompate) == 0) { + return v; + } + } + throw new IllegalArgumentException("No element named " + value + " in enum QuestItemType"); + } + + /** + * Value from save key. + * + * @param name the name + * @return the quest item type + */ + public static QuestItemType valueFromSaveKey(final String name) { + if (name == null) { + return null; + } + + final String valToCompate = name.trim(); + for (final QuestItemType v : QuestItemType.values()) { + if (v.getKey().compareToIgnoreCase(valToCompate) == 0) { + return v; + } + } + throw new IllegalArgumentException("No element keyed " + name + " in enum QuestItemType"); + } + +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestItemZeppelin.java b/forge-m-base/src/forge/quest/bazaar/QuestItemZeppelin.java new file mode 100644 index 00000000000..656b163d6a8 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestItemZeppelin.java @@ -0,0 +1,47 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import forge.quest.data.QuestAssets; + +/** + *

+ * QuestItemZeppelin class. + *

+ * + * @author Forge + * @version $Id: QuestItemZeppelin.java 14797 2012-03-18 18:09:02Z Max mtg $ + */ +public class QuestItemZeppelin extends QuestItemBasic { + /** + *

+ * Constructor for QuestItemZeppelin. + *

+ */ + QuestItemZeppelin() { + super(QuestItemType.ZEPPELIN); // , QuestStallManager.GEAR + } + + /** {@inheritDoc} */ + @Override + public final boolean isAvailableForPurchase(QuestAssets qA) { + return super.isAvailableForPurchase(qA) && qA.hasItem(QuestItemType.MAP); + } + + +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestPetController.java b/forge-m-base/src/forge/quest/bazaar/QuestPetController.java new file mode 100644 index 00000000000..14c995db3d9 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestPetController.java @@ -0,0 +1,277 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Texture; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +import forge.assets.FImage; +import forge.assets.FTextureImage; +import forge.item.PaperToken; +import forge.quest.data.QuestAssets; +import forge.utils.Constants; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * Abstract QuestPetAbstract class. + *

+ * It's not good to store in a single class pets properties and bazaar sellable + * - such is a tradeoff for speed of development + * + * @author Forge + * @version $Id: QuestPetController.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public class QuestPetController implements IQuestBazaarItem { + + /** The level. */ + @XStreamAsAttribute() + private final int maxLevel; + + private final List levels = new ArrayList(); + + @XStreamAsAttribute() + private final String name; + + @XStreamAlias(value = "desc") + private final String description; + @XStreamAsAttribute() + private final String saveFileKey; + @XStreamAsAttribute() + private int slot; + + /** + * + * TODO: Write javadoc for this method. + * @param qA quest assets + * @return int + */ + protected int getPetLevel(final QuestAssets qA) { + final int level = qA.getPetLevel(this.saveFileKey); + return level < 0 ? 0 : level > this.maxLevel ? this.maxLevel : level; + } + + /** + *

+ * getPetCard. + *

+ * @param qA quest assets + * @return a {@link forge.game.card.Card} object. + */ + + public PaperToken getPetCard(final QuestAssets qA) { + return this.levels.get(this.getPetLevel(qA)).getCard(); + } + + /** + *

+ * getPrice. + *

+ * @param qA quest assets + * @return a int. + */ + @Override + public final int getBuyingPrice(final QuestAssets qA) { + final int level = this.getPetLevel(qA); + // we'll buy next level + return level >= this.maxLevel ? -1 /* cannot buy */ : this.levels.get(level + 1).getCost(); + } + + /** {@inheritDoc} */ + @Override + public final int getSellingPrice(final QuestAssets qA) { + return 0; + } + + /** + *

+ * getUpgradeDescription. + *

+ * @param qA quest assets + * @return a {@link java.lang.String} object. + */ + public final String getUpgradeDescription(final QuestAssets qA) { + return this.levels.get(this.getPetLevel(qA)).getNextLevel(); + } + + /** + *

+ * getIcon. + *

+ * @param qA quest assets + */ + @Override + public final FImage getIcon(final QuestAssets qA) { + final String path = Constants.CACHE_TOKEN_PICS_DIR; + final int level = this.getPetLevel(qA); + Texture texture = new Texture(Gdx.files.absolute(path + this.levels.get(level < this.maxLevel ? level + 1 : level).getPicture() + ".jpg")); + return new FTextureImage(texture); + } + + /** + *

+ * getStats. + *

+ * @param qA quest assets + * @return a {@link java.lang.String} object. + */ + public final String getStats(final QuestAssets qA) { + return this.levels.get(this.getPetLevel(qA)).getStats(); + } + + /** + *

+ * getUpgradedStats. + *

+ * @param qA quest assets + * @return a {@link java.lang.String} object. + */ + public final String getUpgradedStats(final QuestAssets qA) { + final int level = this.getPetLevel(qA); + return level >= this.maxLevel ? "N/A" : this.levels.get(level + 1).getStats(); + } + + /** + *

+ * Getter for the field maxLevel. + *

+ * + * @return a int. + */ + public final int getMaxLevel() { + return this.maxLevel; + } + + // Never to be called, instances will be read from xml + private QuestPetController() { + this.description = null; + this.name = null; + this.maxLevel = 0; + this.saveFileKey = null; + } + + /** + *

+ * getPurchaseDescription. + *

+ * @param qA quest assets + * @return a {@link java.lang.String} object. + */ + @Override + public final String getPurchaseDescription(final QuestAssets qA) { + return this.getDescription() + "\n\nCurrent stats: " + this.getStats(qA) + "\nUpgraded stats: " + + this.getUpgradedStats(qA); + + } + + /** + *

+ * Getter for the field description. + *

+ * + * @return a {@link java.lang.String} object. + */ + public final String getDescription() { + return this.description; + } + + /** + *

+ * Getter for the field name. + *

+ * + * @return a {@link java.lang.String} object. + */ + public final String getName() { + return this.name; + } + + /** {@inheritDoc} */ + @Override + public final String toString() { + return this.name; + } + + /** {@inheritDoc} */ + @Override + public final int compareTo(final Object o) { + return this.name.compareTo(o.toString()); + } + + /** + *

+ * getPurchaseName. + *

+ * + * @return a {@link java.lang.String} object. + */ + @Override + public final String getPurchaseName() { + return this.name; + } + + // @Override + // public String getStallName() { + // return QuestStallManager.PET_SHOP; + // } + + /** + *

+ * isAvailableForPurchase. + *

+ * @param qA quest assets + * @return a boolean. + */ + @Override + public boolean isAvailableForPurchase(final QuestAssets qA) { + return this.getPetLevel(qA) < this.getMaxLevel(); + } + + /** + *

+ * onPurchase. + *

+ * @param qA quest assets + */ + @Override + public void onPurchase(final QuestAssets qA) { + qA.setPetLevel(this.saveFileKey, this.getPetLevel(qA) + 1); + } + + /** + * + * TODO: Write javadoc for this method. + * @return String + */ + public String getSaveFileKey() { + return this.saveFileKey; + } + + /** + * TODO: Write javadoc for this method. + * + * @return int + */ + public int getSlot() { + return this.slot; + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestPetStats.java b/forge-m-base/src/forge/quest/bazaar/QuestPetStats.java new file mode 100644 index 00000000000..7afd6c7b7f8 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestPetStats.java @@ -0,0 +1,71 @@ +package forge.quest.bazaar; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +import forge.card.CardEdition; +import forge.card.CardRules; +import forge.item.PaperToken; +import forge.util.FileUtil; +import forge.utils.Constants; + +import java.io.File; +import java.util.List; + + +@XStreamAlias(value = "level") +public class QuestPetStats { + + @XStreamAsAttribute() + @XStreamAlias(value = "value") + private int levelValue; + + @XStreamAsAttribute() + @XStreamAlias(value = "pic") + private String picture; + + @XStreamAsAttribute() + private String stats; + + @XStreamAsAttribute() + private String cardFile; + + @XStreamAsAttribute() + private int cost; + + @XStreamAsAttribute() + private String nextLevel; + + private transient PaperToken petCard = null; + + private QuestPetStats() { } + + public final int getLevelValue() { + return levelValue; + } + + public final String getPicture() { + return picture; + } + + public final String getStats() { + return stats; + } + + public final PaperToken getCard() { + if (null == petCard) { + List cardLines = FileUtil.readFile(new File(Constants.CARD_DATA_PETS_DIR, cardFile)); + CardRules rules = CardRules.fromScript(cardLines); + petCard = new PaperToken(rules, CardEdition.UNKNOWN, picture); + } + return petCard; + } + + public final int getCost() { + return cost; + } + + public final String getNextLevel() { + return nextLevel; + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestPetStorage.java b/forge-m-base/src/forge/quest/bazaar/QuestPetStorage.java new file mode 100644 index 00000000000..26208f9aa01 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestPetStorage.java @@ -0,0 +1,134 @@ +package forge.quest.bazaar; + +import com.thoughtworks.xstream.XStream; + +import forge.quest.data.QuestAssets; +import forge.utils.IgnoringXStream; +import forge.utils.XmlUtil; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class QuestPetStorage { + + private Map> petsBySlot = new HashMap>(); + + private Map petsByName = new HashMap(); + + /** + * TODO: Write javadoc for Constructor. + * + * @param file File + */ + public QuestPetStorage(final File file) { + DocumentBuilder builder; + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + final Document document = builder.parse(file); + + final XStream xs = new IgnoringXStream(); + xs.autodetectAnnotations(true); + + final NodeList xmlPets = document.getElementsByTagName("pets").item(0).getChildNodes(); + for (int iN = 0; iN < xmlPets.getLength(); iN++) { + final Node n = xmlPets.item(iN); + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final Attr att = document.createAttribute("resolves-to"); + att.setValue(QuestPetController.class.getCanonicalName()); + n.getAttributes().setNamedItem(att); + final String sXml = XmlUtil.nodeToString(n); + final QuestPetController petCtrl = (QuestPetController) xs.fromXML(sXml); + this.addToMap(petCtrl); + } + + } catch (final SAXException e) { + e.printStackTrace(); + } catch (final IOException e) { + e.printStackTrace(); + } catch (final ParserConfigurationException e) { + e.printStackTrace(); + } + } + + /** + * TODO: Write javadoc for this method. + * + * @param petCtrl + */ + private void addToMap(final QuestPetController petCtrl) { + final int iSlot = petCtrl.getSlot(); + List list = this.petsBySlot.get(Integer.valueOf(iSlot)); + if (null == list) { + list = new ArrayList(); + this.petsBySlot.put(Integer.valueOf(iSlot), list); + } + this.petsByName.put(petCtrl.getName(), petCtrl); + list.add(petCtrl); + } + + /** + * TODO: Write javadoc for this method. + * + * @param petName String + * @return QuestPetController + */ + public QuestPetController getPet(final String petName) { + return this.petsByName.get(petName); + } + + /** + * TODO: Write javadoc for this method. + * + * @param iSlot int + * @param qA QuestAssets + * @return List + */ + public List getAvaliablePets(final int iSlot, final QuestAssets qA) { + final List result = new ArrayList(); + final List allPossible = this.petsBySlot.get(Integer.valueOf(iSlot)); + if (null != allPossible) { + for (final QuestPetController c : allPossible) { + if (qA.getPetLevel(c.getSaveFileKey()) > 0) { + result.add(c); + } + } + } + return result; + } + + /** + * + * TODO: Write javadoc for this method. + * @param iSlot int + * @return List + */ + public List getAllPets(final int iSlot) { + final List result = new ArrayList(); + final List allPossible = this.petsBySlot.get(Integer.valueOf(iSlot)); + if (null != allPossible) { + for (final QuestPetController c : allPossible) { + result.add(c); + } + } + return result; + } + +} diff --git a/forge-m-base/src/forge/quest/bazaar/QuestStallDefinition.java b/forge-m-base/src/forge/quest/bazaar/QuestStallDefinition.java new file mode 100644 index 00000000000..0bb3dc0c02c --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/QuestStallDefinition.java @@ -0,0 +1,115 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.bazaar; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +import forge.assets.FImage; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * QuestStallDefinition class. + *

+ * + * @author Forge + * @version $Id: QuestStallDefinition.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +@XStreamAlias("stall") +public class QuestStallDefinition { + + /** The name. */ + @XStreamAsAttribute + private final String name; + + /** The display name. */ + @XStreamAsAttribute + private final String displayName; + + @XStreamAsAttribute + private final FImage icon; + + private final String description; + + private final List items; + + /** + *

+ * Constructor for QuestStallDefinition. + *

Not used anyway + * + * @param name + * a {@link java.lang.String} object. + * @param displayName + * a {@link java.lang.String} object. + * @param description + * a {@link java.lang.String} object. + * @param icon0 + * a {@link javax.swing.ImageIcon} object. + */ + private QuestStallDefinition() { + name = null; + displayName = null; + description = null; + items = new ArrayList(); + icon = null; + } + + /** + * Gets the fluff. + * + * @return the fluff + */ + public String getFluff() { + return this.description; + } + + /** + * Gets the icon. + * + * @return the icon + */ + public FImage getIcon() { + return icon; + } + + /** + * Gets the display name. + * + * @return the displayName + */ + public String getDisplayName() { + return this.displayName; + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return this.name; + } + + public List getItems() { + return items; + } +} diff --git a/forge-m-base/src/forge/quest/bazaar/package-info.java b/forge-m-base/src/forge/quest/bazaar/package-info.java new file mode 100644 index 00000000000..47e76d9c5c0 --- /dev/null +++ b/forge-m-base/src/forge/quest/bazaar/package-info.java @@ -0,0 +1,3 @@ +/** Forge Card Game. */ +package forge.quest.bazaar; + diff --git a/forge-m-base/src/forge/quest/data/GameFormatQuest.java b/forge-m-base/src/forge/quest/data/GameFormatQuest.java new file mode 100644 index 00000000000..a44a2da24e2 --- /dev/null +++ b/forge-m-base/src/forge/quest/data/GameFormatQuest.java @@ -0,0 +1,153 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.data; + +import com.google.common.base.Predicate; + +import forge.card.CardEdition; +import forge.game.GameFormat; +import forge.model.FModel; + +import java.util.ArrayList; +import java.util.List; + + +/** + * This is an alternate game format type, the main difference is that this + * is not immutable. This class is necessary because we may wish to update + * its contents in certain circumstances, and it was safer to create a new + * class than to make the preset game formats modifiable. + */ +public final class GameFormatQuest extends GameFormat { + + private final boolean allowUnlocks; + private int unlocksUsed = 0; + + /** + * Instantiates a new game format based on two lists. + * + * @param newName + * String, the name + * @param setsToAllow + * List, these are the allowed sets + * @param cardsToBan + * List, these will be the banned cards + */ + public GameFormatQuest(final String newName, final List setsToAllow, final List cardsToBan) { + super(newName, setsToAllow, cardsToBan); + allowUnlocks = false; + } + + public GameFormatQuest(final String newName, final List setsToAllow, final List cardsToBan, boolean allowSetUnlocks) { + super(newName, setsToAllow, cardsToBan); + allowUnlocks = allowSetUnlocks; + } + /** + * Instantiates a new game format based on an existing format. + * + * @param toCopy + * an existing format + * @param allowSetUnlocks + */ + public GameFormatQuest(final GameFormat toCopy, boolean allowSetUnlocks) { + super(toCopy.getName(), toCopy.getAllowedSetCodes(), toCopy.getBannedCardNames(), toCopy.getRestrictedCards(), toCopy.getIndex()); + allowUnlocks = allowSetUnlocks; + } + + + /** + * Get the list of excluded sets. + * + * @return unmodifiable list of excluded sets. + */ + public List getLockedSets() { + + List exSets = new ArrayList(); + if (this.allowedSetCodes.isEmpty()) { + return exSets; + } + + for (CardEdition ce : FModel.getMagicDb().getEditions()) { + if (!isSetLegal(ce.getCode())) { + exSets.add(ce.getCode()); + } + } + return exSets; + } + + /** + * Add a set to allowed set codes. + * + * @param setCode String, set code. + */ + public void unlockSet(final String setCode) { + if (!canUnlockSets() || this.allowedSetCodes_ro.isEmpty() || this.allowedSetCodes_ro.contains(setCode)) { + return; + } + this.allowedSetCodes.add(setCode); + unlocksUsed++; + } + + /** + * Checks if the current format contains sets with snow-land (horrible hack...). + * @return boolean, contains snow-land sets. + * + */ + public boolean hasSnowLands() { + return (this.isSetLegal("ICE") || this.isSetLegal("CSP")); + } + + public boolean canUnlockSets() { + return allowUnlocks; + } + + public int getUnlocksUsed() { + return unlocksUsed; + } + + + /** + * The Class Predicates. + */ + public abstract static class Predicates { + /** + * Checks if is legal in quest format. + * + * @param qFormat the format + * @return the predicate + */ + public static Predicate isLegalInFormatQuest(final GameFormatQuest qFormat) { + return new LegalInFormatQuest(qFormat); + } + + private static class LegalInFormatQuest implements Predicate { + private final GameFormatQuest qFormat; + + public LegalInFormatQuest(final GameFormatQuest fmt) { + this.qFormat = fmt; + } + + @Override + public boolean apply(final CardEdition subject) { + return this.qFormat.isSetLegal(subject.getCode()); + } + } + } + + +} diff --git a/forge-m-base/src/forge/quest/data/QuestAchievements.java b/forge-m-base/src/forge/quest/data/QuestAchievements.java new file mode 100644 index 00000000000..bcebb266481 --- /dev/null +++ b/forge-m-base/src/forge/quest/data/QuestAchievements.java @@ -0,0 +1,176 @@ +package forge.quest.data; + +import forge.model.FModel; +import forge.quest.data.QuestPreferences.DifficultyPrefs; + +import java.util.ArrayList; +import java.util.List; + + +public class QuestAchievements { + + // Challenge history + /** The challenges played. */ + private int challengesPlayed = 0; + + private List completedChallenges = new ArrayList(); + private List currentChallenges = new ArrayList(); + + private int win; + private int winstreakBest = 0; + private int winstreakCurrent = 0; + private int lost; + + // Difficulty - will store only index from now. + private int difficulty; + + /** + * TODO: Write javadoc for Constructor. + * @param diff   int + */ + public QuestAchievements(int diff) { + difficulty = diff; + } + + /** + * TODO: Write javadoc for Constructor. + * @param mode + */ + /** + * Adds the win. + */ + public void addWin() { // changes getRank() + this.win++; + this.winstreakCurrent++; + + if (this.winstreakCurrent > this.winstreakBest) { + this.winstreakBest = this.winstreakCurrent; + } + + } + + // Challenge performance + /** + * Gets the challenges played. + * + * @return the challenges played + */ + public int getChallengesPlayed() { + return this.challengesPlayed; + } + + /** + * Adds the challenges played. + */ + public void addChallengesPlayed() { + this.challengesPlayed++; + } + + /** + * Returns stored list of non-repeatable challenge IDs. + * + * @return List + */ + public List getLockedChallenges() { + return this.completedChallenges; + } + + /** + *

+ * addCompletedChallenge. + *

+ * Add non-repeatable challenge ID to list. + * + * @param i + * the i + */ + public void addLockedChallenge(final String i) { + this.completedChallenges.add(i); + } + + /** + * Stores a list of current challenges. + * + * @return List + */ + public List getCurrentChallenges() { + if (this.currentChallenges == null) { + this.currentChallenges = new ArrayList(); + } + + return this.currentChallenges; + } + + /** + * Returns the stored list of current challenges. + * + * @param lst0 List + */ + public void setCurrentChallenges(final List lst0) { + this.currentChallenges = lst0; + } + + /** + * Adds the lost. + */ + public void addLost() { + this.lost++; + this.winstreakCurrent = 0; + } + // Level, read-only ( note: it increments in addWin() ) + /** + * Gets the level. + * + * @return the level + */ + public int getLevel() { + final int winsToLvlUp = FModel.getQuestPreferences().getPrefInt(DifficultyPrefs.WINS_RANKUP, difficulty); + return this.win / winsToLvlUp; + } + // Wins & Losses + /** + * Gets the lost. + * + * @return the lost + */ + public int getLost() { + return this.lost; + } + + /** + * Gets the win. + * + * @return the win + */ + public int getWin() { + return win; + } + + /** + * Gets the win streak best. + * + * @return int + */ + public int getWinStreakBest() { + return winstreakBest; + } + + /** + * Gets the win streak current. + * + * @return int + */ + public int getWinStreakCurrent() { + return winstreakCurrent; + } + + /** + * Gets the difficulty index. + * + * @return the difficulty index + */ + public int getDifficulty() { + return this.difficulty; + } + +} diff --git a/forge-m-base/src/forge/quest/data/QuestAssets.java b/forge-m-base/src/forge/quest/data/QuestAssets.java new file mode 100644 index 00000000000..88dad00a87c --- /dev/null +++ b/forge-m-base/src/forge/quest/data/QuestAssets.java @@ -0,0 +1,266 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Nate + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.data; + +import forge.deck.Deck; +import forge.item.InventoryItem; +import forge.item.PaperCard; +import forge.model.FModel; +import forge.quest.QuestDeckMap; +import forge.quest.QuestMode; +import forge.quest.QuestUtilCards; +import forge.quest.bazaar.QuestItemType; +import forge.quest.data.QuestPreferences.QPref; +import forge.util.ItemPool; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +/** */ +public class QuestAssets { + + // Cards associated with quest + /** The card pool. */ + private final ItemPool cardPool = new ItemPool(PaperCard.class); // player's + /** The credits. */ + private long credits; // this money is good for all modes + // game + // with + + // Decks collected by player + /** The my decks. */ + private final HashMap myDecks = new HashMap(); + // current + // shop + // list + /** The new card list. */ + private final ItemPool newCardList = new ItemPool(InventoryItem.class); // cards + // belonging + /** The shop list. */ + private final ItemPool shopList = new ItemPool(InventoryItem.class); // the + // gadgets + + /** The inventory items. */ + private final Map inventoryItems = new EnumMap( + QuestItemType.class); + + // Much the same like other map, but keyed by string (to support a lot of custom pets) + private final Map combatPets = new HashMap(); + /** + * Checks for item. + * + * @param itemType the item type + * @return true, if successful + */ + public final boolean hasItem(final QuestItemType itemType) { + return this.inventoryItems.containsKey(itemType) && (this.inventoryItems.get(itemType).getLevel() > 0); + } + + /** + * Gets the item level. + * + * @param itemType the item type + * @return the item level + */ + public final int getItemLevel(final QuestItemType itemType) { + final QuestItemCondition state = this.inventoryItems.get(itemType); + return state == null ? 0 : state.getLevel(); + } + + /** + * Gets the item condition. + * + * @param itemType the item type + * @param extends QuestItemCondition + * @return T the item condition + */ + @SuppressWarnings("unchecked") + public final T getItemCondition(final QuestItemType itemType) { + QuestItemCondition current = this.inventoryItems.get(itemType); + if (!current.getClass().equals(itemType.getModelClass())) { + try { + QuestItemCondition modern = itemType.getModelClass().newInstance(); + modern.takeDataFrom(current); + current = modern; + inventoryItems.put(itemType, modern); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + } + return (T) current; + } + + /** + * Sets the item level. + * + * @param itemType the item type + * @param level the level + */ + public final void setItemLevel(final QuestItemType itemType, final int level) { + QuestItemCondition cond = this.inventoryItems.get(itemType); + if (null == cond) { + try { // care to set appropriate state class here + cond = itemType.getModelClass().newInstance(); + } catch (final Exception e) { + e.printStackTrace(); + cond = new QuestItemCondition(); + } + this.inventoryItems.put(itemType, cond); + } + cond.setLevel(level); + } + + /** + * @param name String + * @return int + */ + public final int getPetLevel(final String name) { + final QuestItemCondition state = this.combatPets.get(name); + return state == null ? 0 : state.getLevel(); + } + + /** + * @param name   String + * @param extends QuestItemCondition + * @return + */ + @SuppressWarnings("unchecked") + public final T getPetCondition(final String name) { + return (T) this.combatPets.get(name); + } + + /** + * @param name String + * @param level int + */ + public final void setPetLevel(final String name, final int level) { + QuestItemCondition cond = this.combatPets.get(name); + if (null == cond) { + cond = new QuestItemCondition(); // pets have only level that should be serialized for now + this.combatPets.put(name, cond); + } + cond.setLevel(level); + } + + /** + * Instantiates a new quest assets. + */ + public QuestAssets(GameFormatQuest useFormat) { + final QuestPreferences prefs = FModel.getQuestPreferences(); + int snowLands = prefs.getPrefInt(QPref.STARTING_SNOW_LANDS); + if (useFormat != null && !useFormat.hasSnowLands()) { + snowLands = 0; + } + final ItemPool lands = QuestUtilCards.generateBasicLands( + prefs.getPrefInt(QPref.STARTING_BASIC_LANDS), snowLands, useFormat); + this.getCardPool().addAll(lands); + } + + /** + * Gets the credits. + * + * @return the credits + */ + public long getCredits() { + return this.credits; + } + + // Life (only fantasy) + /** + * Gets the life. + * + * @param mode the mode + * @return the life + */ + public int getLife(final QuestMode mode) { + final int base = mode.equals(QuestMode.Fantasy) ? 15 : 20; + return (base + this.getItemLevel(QuestItemType.ELIXIR_OF_LIFE)) - this.getItemLevel(QuestItemType.POUND_FLESH); + } + + /** + * Gets the new card list. + * + * @return the newCardList + */ + public ItemPool getNewCardList() { + return this.newCardList; + } + + /** + * Gets the shop list. + * + * @return the shopList + */ + public ItemPool getShopList() { + return this.shopList; + } + + /** + * Sets the credits. + * + * @param credits0 + * the credits to set + */ + public void setCredits(final long credits0) { + this.credits = credits0; + } + + + + // Credits + /** + * Adds the credits. + * + * @param c + * the c + */ + public void addCredits(final long c) { + this.setCredits(this.getCredits() + c); + } + + /** + * Gets the card pool. + * + * @return the cardPool + */ + public ItemPool getCardPool() { + return this.cardPool; + } + + /** + * Subtract credits. + * + * @param c + * the c + */ + public void subtractCredits(final long c) { + this.setCredits(this.getCredits() > c ? this.getCredits() - c : 0); + } + + /** + * @return the deck storage + */ + public QuestDeckMap getDeckStorage() { + return new QuestDeckMap(this.myDecks); + } + +} diff --git a/forge-m-base/src/forge/quest/data/QuestData.java b/forge-m-base/src/forge/quest/data/QuestData.java new file mode 100644 index 00000000000..5f145fe8c8d --- /dev/null +++ b/forge-m-base/src/forge/quest/data/QuestData.java @@ -0,0 +1,217 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.data; + +import forge.game.GameFormat; +import forge.model.FModel; +import forge.quest.QuestMode; +import forge.quest.io.QuestDataIO; +import forge.utils.Constants; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +//when you create QuestDataOld and AFTER you copy the AI decks over +//you have to call one of these two methods below +//see Gui_QuestOptions for more details + +/** + *

+ * QuestData class. + *

+ * + * @author Forge + * @version $Id: QuestData.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public final class QuestData { + /** Holds the latest version of the Quest Data. */ + public static final int CURRENT_VERSION_NUMBER = 8; + + // This field places the version number into QD instance, + // but only when the object is created through the constructor + // DO NOT RENAME THIS FIELD + /** The version number. */ + private int versionNumber = QuestData.CURRENT_VERSION_NUMBER; + + private GameFormatQuest format; + private String name; + + // Quest mode - there should be an enum :( + /** The mode. */ + private QuestMode mode; + + // Quest world ID, if any + private String worldId; + // gadgets + + private final QuestAssets assets; + private final QuestAchievements achievements; + private final Map petSlots = new HashMap(); + private boolean isCharmActive = false; + + /** + * Instantiates a new quest data. + * @param mode2 + * quest mode + * @param diff + * achievement diff + * @param name2 + * quest name + * @param userFormat + * user-defined format, if any (null if none). + * @param allowSetUnlocks + * allow set unlocking during quest + * @param startingWorld + * starting world + */ + public QuestData(String name0, int diff, QuestMode mode0, GameFormat userFormat, + boolean allowSetUnlocks, final String startingWorld) { + this.name = name0; + + if (userFormat != null) { + this.format = new GameFormatQuest(userFormat, allowSetUnlocks); + } + this.mode = mode0; + this.achievements = new QuestAchievements(diff); + this.assets = new QuestAssets(format); + this.worldId = startingWorld; + } + + /** + * Gets the mode. + * + * @return the mode + */ + public QuestMode getMode() { + return this.mode; + } + + /** + * Gets the persistent format, null if not assigned. + * + * @return GameFormatQuest, the persistent format + */ + public GameFormatQuest getFormat() { + return this.format; + } + + // SERIALIZATION - related things + // This must be called by XML-serializer via reflection + /** + * Read resolve. + * + * @return the object + */ + public Object readResolve() { + return this; + } + + /** + * Save data. + */ + public void saveData() { + QuestDataIO.saveData(this); + } + + /** + * Gets the version number. + * + * @return the versionNumber + */ + public int getVersionNumber() { + return this.versionNumber; + } + + /** + * Sets the version number. + * + * @param versionNumber0 + * the versionNumber to set + */ + public void setVersionNumber(final int versionNumber0) { + this.versionNumber = versionNumber0; + } + + /** + * Gets the name. + * + * @return {@link java.lang.String} + */ + public String getName() { + return this.name; + } + + /** + * Rename this quest the name. + * + * @param newName + * the new name to set + */ + public void rename(final String newName) { + File newpath = new File(Constants.QUEST_SAVE_DIR, newName + ".dat"); + File oldpath = new File(Constants.QUEST_SAVE_DIR, this.name + ".dat"); + oldpath.renameTo(newpath); + + this.name = newName; + QuestDataIO.saveData(this); + } + + public QuestAssets getAssets() { + return assets; + } + + public Map getPetSlots() { + return petSlots; + } + + public QuestAchievements getAchievements() { + return achievements; + } + + public String getWorldId() { + return worldId; + } + + /** + * Sets the world id to null or a legal world id. + * @param newId + * String, the world id to set (must be null or legal). + */ + public void setWorldId(final String newId) { + if (newId != null && FModel.getWorlds().get(newId) == null) { + throw new RuntimeException("Tried to set illegal (unknown) world id: " + newId); + } + + worldId = newId; + } + + /** + * @return the isCharmActive + */ + public boolean isCharmActive() { + return isCharmActive; + } + + /** + * @param isCharmActive the isCharmActive to set + */ + public void setCharmActive(boolean isCharmActive) { + this.isCharmActive = isCharmActive; + } +} diff --git a/forge-m-base/src/forge/quest/data/QuestItemCondition.java b/forge-m-base/src/forge/quest/data/QuestItemCondition.java new file mode 100644 index 00000000000..980138dbc4b --- /dev/null +++ b/forge-m-base/src/forge/quest/data/QuestItemCondition.java @@ -0,0 +1,27 @@ +package forge.quest.data; + +/** + * This class should store the quest items' properties that are to be serialized. + * + */ +public class QuestItemCondition { + private int level; + + /** @return int */ + public int getLevel() { + return level; + } + + /** @param level int */ + public void setLevel(int level) { + this.level = level; + } + + /** + * Copy data from the parameter instance to 'this' instance. + * @param source QuestItemCondition + */ + public void takeDataFrom(QuestItemCondition source) { + this.level = source.level; + } +} diff --git a/forge-m-base/src/forge/quest/data/QuestPreferences.java b/forge-m-base/src/forge/quest/data/QuestPreferences.java new file mode 100644 index 00000000000..aa09100d912 --- /dev/null +++ b/forge-m-base/src/forge/quest/data/QuestPreferences.java @@ -0,0 +1,261 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.data; + +import java.io.Serializable; + +import forge.utils.Constants; +import forge.utils.PreferencesStore; + +@SuppressWarnings("serial") +public class QuestPreferences extends PreferencesStore implements Serializable { + /** + * Preference identifiers, and their default values. + */ + public static enum QPref { + + // How many of each rarity comes in a won booster pack + BOOSTER_COMMONS("11"), + BOOSTER_UNCOMMONS("3"), + BOOSTER_RARES("1"), + // The preferred format of the won booster pack + BOOSTER_FORMAT("Standard"), + + // How many credits are lost for losing a match + PENALTY_LOSS("15"), + + // Currently chosen quest and deck + CURRENT_QUEST("DEFAULT"), + CURRENT_DECK("DEFAULT"), + + // All of the rewards given out End of Match + // Awarded to every match winner + REWARDS_BASE("25"), + // Didn't lose a game in the match + REWARDS_UNDEFEATED("25"), + // For each of your previous wins gain a small multiplier + // This is here to award long quests with more money for buying expensive cards + REWARDS_WINS_MULTIPLIER("0.3"), + + // Winning each game by other means "Poison", "Milling" or "Alternative" Win + REWARDS_POISON("50"), + REWARDS_MILLED("40"), + REWARDS_ALTERNATIVE("100"), + + // If you Mulligan to 0 to start a game + REWARDS_MULLIGAN0("500"), + + // How many turns it took you to win the game + REWARDS_TURN15("5"), + REWARDS_TURN10("50"), + REWARDS_TURN5("250"), + REWARDS_TURN1("1500"), + + // How many basic your starting pool has (if appropriate) + STARTING_BASIC_LANDS("20"), + STARTING_SNOW_LANDS("5"), + + // Starting pool color bias effect + STARTING_POOL_COLOR_BIAS("6"), + + // Commons in your starting pool, by difficulty + STARTING_COMMONS_EASY("82"), + STARTING_COMMONS_MEDIUM("80"), + STARTING_COMMONS_HARD("78"), + STARTING_COMMONS_EXPERT("76"), + + // Uncommons in your starting pool, by difficulty + STARTING_UNCOMMONS_EASY("40"), + STARTING_UNCOMMONS_MEDIUM("36"), + STARTING_UNCOMMONS_HARD("32"), + STARTING_UNCOMMONS_EXPERT("28"), + + // Rares in your starting pool, by difficulty + STARTING_RARES_EASY("20"), + STARTING_RARES_MEDIUM("18"), + STARTING_RARES_HARD("16"), + STARTING_RARES_EXPERT("15"), + + // Credits you start the quest with, by difficulty + STARTING_CREDITS_EASY("250"), + STARTING_CREDITS_MEDIUM("200"), + STARTING_CREDITS_HARD("150"), + STARTING_CREDITS_EXPERT("100"), + + // Matches won per booster award, by difficulty + WINS_BOOSTER_EASY("1"), + WINS_BOOSTER_MEDIUM("1"), + WINS_BOOSTER_HARD("2"), + WINS_BOOSTER_EXPERT("2"), + + // Matches won per increased rank, by difficulty + // Rank affects how many packs are opened for singles in the spell shop + WINS_RANKUP_EASY("3"), + WINS_RANKUP_MEDIUM("4"), + WINS_RANKUP_HARD("5"), + WINS_RANKUP_EXPERT("6"), + + // Matches won to unlock Medium Opponents, by difficulty + WINS_MEDIUMAI_EASY("10"), + WINS_MEDIUMAI_MEDIUM("9"), + WINS_MEDIUMAI_HARD("8"), + WINS_MEDIUMAI_EXPERT("7"), + + // Matches won to unlock Hard Opponents, by difficulty + WINS_HARDAI_EASY("20"), + WINS_HARDAI_MEDIUM("18"), + WINS_HARDAI_HARD("16"), + WINS_HARDAI_EXPERT("14"), + + // Matches won to unlock Expert Opponents, by difficulty + WINS_EXPERTAI_EASY("40"), + WINS_EXPERTAI_MEDIUM("36"), + WINS_EXPERTAI_HARD("32"), + WINS_EXPERTAI_EXPERT("28"), + + // Maximum amount of "Packs" opened by the Shop and available as singles + SHOP_MAX_PACKS("6"), + + // Rarity distribution of Singles in an Opened Shop Pack + SHOP_SINGLES_COMMON("7"), + SHOP_SINGLES_UNCOMMON("3"), + SHOP_SINGLES_RARE("1"), + + // How many wins it takes to open an additional pack in the shop + SHOP_WINS_FOR_ADDITIONAL_PACK("10"), + // How many packs the shop start with. + SHOP_STARTING_PACKS("4"); + + private final String strDefaultVal; + + /** + * Instantiates a new q pref. + * + * @param s0 + *   {@link java.lang.String} + */ + QPref(final String s0) { + this.strDefaultVal = s0; + } + + /** + * Gets the default. + * + * @return {@link java.lang.String} + */ + public String getDefault() { + return this.strDefaultVal; + } + } + + public static enum DifficultyPrefs { + STARTING_COMMONS, + STARTING_UNCOMMONS, + STARTING_RARES, + STARTING_CREDITS, + WINS_BOOSTER, + WINS_RANKUP, + WINS_MEDIUMAI, + WINS_HARDAI, + WINS_EXPERTAI + } + + /** Instantiates a QuestPreferences object. */ + public QuestPreferences() { + super(Constants.QUEST_PREFS_FILE, QPref.class); + } + + protected QPref[] getEnumValues() { + return QPref.values(); + } + + protected QPref valueOf(String name) { + try { + return QPref.valueOf(name); + } + catch (Exception e) { + return null; + } + } + + protected String getPrefDefault(QPref key) { + return key.getDefault(); + } + + /** + * Returns a preference value according to a difficulty index. + */ + public String getPref(DifficultyPrefs pref, int difficultyIndex) { + String newQPref = pref.toString(); + + switch (difficultyIndex) { + case 0: + newQPref += "_EASY"; + break; + case 1: + newQPref += "_MEDIUM"; + break; + case 2: + newQPref += "_HARD"; + break; + case 3: + newQPref += "_EXPERT"; + break; + default: + try { + throw new Exception(); + } catch (final Exception e1) { + System.err.println("Difficulty index out of bounds: " + difficultyIndex); + e1.printStackTrace(); + } + } + + return getPref(QPref.valueOf(newQPref)); + } + + /** + * Returns a difficulty-indexed preference value, as an int. + */ + public int getPrefInt(DifficultyPrefs pref, int difficultyIndex) { + return Integer.parseInt(this.getPref(pref, difficultyIndex)); + } + + /** + * Gets the difficulty. + */ + public static String getDifficulty(int difficultyIndex) { + String s; + switch (difficultyIndex) { + case 1: + s = "EASY"; + break; + case 2: + s = "MEDIUM"; + break; + case 3: + s = "HARD"; + break; + case 4: + s = "EXPERT"; + break; + default: + s = "UNKNOWN"; + } + return s; + } +} diff --git a/forge-m-base/src/forge/quest/data/package-info.java b/forge-m-base/src/forge/quest/data/package-info.java new file mode 100644 index 00000000000..7b66f98289f --- /dev/null +++ b/forge-m-base/src/forge/quest/data/package-info.java @@ -0,0 +1,3 @@ +/** Forge Card Game. */ +package forge.quest.data; + diff --git a/forge-m-base/src/forge/quest/io/QuestChallengeReader.java b/forge-m-base/src/forge/quest/io/QuestChallengeReader.java new file mode 100644 index 00000000000..ef69878e168 --- /dev/null +++ b/forge-m-base/src/forge/quest/io/QuestChallengeReader.java @@ -0,0 +1,73 @@ +package forge.quest.io; + +import forge.ImageKeys; +import forge.deck.io.DeckSerializer; +import forge.deck.io.DeckStorage; +import forge.quest.QuestEventChallenge; +import forge.quest.QuestEventDifficulty; +import forge.util.FileSection; +import forge.util.FileUtil; +import forge.util.TextUtil; +import forge.util.storage.StorageReaderFolder; +import forge.utils.Constants; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class QuestChallengeReader extends StorageReaderFolder { + public QuestChallengeReader(File deckDir0) { + super(deckDir0, QuestEventChallenge.FN_GET_ID); + // TODO Auto-generated constructor stub + } + + @Override + protected QuestEventChallenge read(File file) { + final Map> contents = FileSection.parseSections(FileUtil.readFile(file)); + final QuestEventChallenge qc = new QuestEventChallenge(); + + // Unique properties + FileSection sectionQuest = FileSection.parse(contents.get("quest"), "="); + qc.setId(sectionQuest.get("ID", "-1")); + qc.setOpponent(sectionQuest.get("OpponentName")); + qc.setRepeatable(sectionQuest.getBoolean("Repeat", false)); + qc.setAiLife(sectionQuest.getInt("AILife", 25)); + qc.setWinsReqd(sectionQuest.getInt("Wins", 20)); + qc.setCreditsReward(sectionQuest.getInt("Credit Reward", 100)); + qc.setCardReward(sectionQuest.get("Card Reward")); + qc.setHumanExtraCards(Arrays.asList(TextUtil.split(sectionQuest.get("HumanExtras", ""), '|'))); + qc.setAiExtraCards(Arrays.asList(TextUtil.split(sectionQuest.get("AIExtras", ""), '|'))); + // Less common properties + int humanLife = sectionQuest.getInt("HumanLife", 0); + if (humanLife != 0) { + qc.setHumanLife(humanLife); + } + qc.setUseBazaar(sectionQuest.getBoolean("UseBazaar", true)); + qc.setForceAnte(sectionQuest.contains("ForceAnte") ? sectionQuest.getBoolean("ForceAnte") : null); + + String humanDeck = sectionQuest.get("HumanDeck", null); + if (humanDeck != null) { + File humanFile = new File(Constants.DEFAULT_CHALLENGES_DIR, humanDeck); // Won't work in other worlds! + qc.setHumanDeck(DeckSerializer.fromFile(humanFile)); + } + + // Common properties + FileSection sectionMeta = FileSection.parse(contents.get("metadata"), "="); + qc.setTitle(sectionMeta.get("Title")); + qc.setName(qc.getTitle()); // Challenges have unique titles + qc.setDifficulty(QuestEventDifficulty.fromString(sectionMeta.get("Difficulty"))); + qc.setDescription(sectionMeta.get("Description")); + qc.setIconImageKey(ImageKeys.ICON_PREFIX + sectionMeta.get("Icon")); + + // Deck + qc.setEventDeck(DeckSerializer.fromSections(contents)); + return qc; + } + + @Override + protected FilenameFilter getFileFilter() { + return DeckStorage.DCK_FILE_FILTER; + } +} \ No newline at end of file diff --git a/forge-m-base/src/forge/quest/io/QuestDataIO.java b/forge-m-base/src/forge/quest/io/QuestDataIO.java new file mode 100644 index 00000000000..80cd821c5f5 --- /dev/null +++ b/forge-m-base/src/forge/quest/io/QuestDataIO.java @@ -0,0 +1,673 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.io; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +import forge.card.CardEdition; +import forge.deck.CardPool; +import forge.deck.Deck; +import forge.deck.DeckSection; +import forge.error.BugReporter; +import forge.item.*; +import forge.model.FModel; +import forge.quest.QuestController; +import forge.quest.QuestMode; +import forge.quest.bazaar.QuestItemType; +import forge.quest.data.*; +import forge.util.ItemPool; +import forge.utils.Constants; +import forge.utils.IgnoringXStream; +import forge.utils.XmlUtil; + +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.*; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import java.io.*; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + *

+ * QuestDataIO class. + *

+ * + * @author Forge + * @version $Id: QuestDataIO.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public class QuestDataIO { + + /** + * Gets the serializer. + * + * @param isIgnoring the is ignoring + * @return the serializer + */ + protected static XStream getSerializer(final boolean isIgnoring) { + final XStream xStream = isIgnoring ? new IgnoringXStream() : new XStream(); + xStream.registerConverter(new ItemPoolToXml()); + xStream.registerConverter(new DeckToXml()); + xStream.registerConverter(new GameFormatQuestToXml()); + xStream.registerConverter(new QuestModeToXml()); + xStream.autodetectAnnotations(true); + xStream.alias("CardPool", ItemPool.class); + xStream.alias("DeckSection", CardPool.class); + return xStream; + } + + /** + *

+ * loadData. + *

+ * + * @param xmlSaveFile + *   {@link java.io.File} + * @return {@link forge.quest.data.QuestData} + */ + public static QuestData loadData(final File xmlSaveFile) { + try { + QuestData data = null; + + final GZIPInputStream zin = new GZIPInputStream(new FileInputStream(xmlSaveFile)); + final StringBuilder xml = new StringBuilder(); + final char[] buf = new char[1024]; + final InputStreamReader reader = new InputStreamReader(zin); + while (reader.ready()) { + final int len = reader.read(buf); + if (len == -1) { + break; + } // when end of stream was reached + xml.append(buf, 0, len); + } + + zin.close(); + + String bigXML = xml.toString(); + data = (QuestData) QuestDataIO.getSerializer(true).fromXML(bigXML); + + if (data.getVersionNumber() != QuestData.CURRENT_VERSION_NUMBER) { + try { + QuestDataIO.updateSaveFile(data, bigXML, xmlSaveFile.getName().replace(".dat", "")); + } catch (final Exception e) { + forge.error.BugReporter.reportException(e); + } + } + + return data; + } + catch (final Exception ex) { + BugReporter.reportException(ex, "Error loading Quest Data"); + throw new RuntimeException(ex); + } + } + + private static void setFinalField(final Class clasz, final String fieldName, final T instance, + final Object newValue) throws IllegalAccessException, NoSuchFieldException { + final Field field = clasz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(instance, newValue); // no difference here (used only to set + // initial lives) + } + + /** + *

+ * updateSaveFile. + *

+ * + * @param newData + * a {@link forge.quest.data.QuestData} object. + * @param input + * a {@link java.lang.String} object. + * @throws ParserConfigurationException + * @throws IOException + * @throws SAXException + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + private static void updateSaveFile(final QuestData newData, final String input, String filename) throws ParserConfigurationException, SAXException, IOException, IllegalAccessException, NoSuchFieldException { + final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + final InputSource is = new InputSource(); + is.setCharacterStream(new StringReader(input)); + final Document document = builder.parse(is); + + final int saveVersion = newData.getVersionNumber(); + + if (saveVersion < 3) { + QuestDataIO.setFinalField(QuestData.class, "assets", newData, new QuestAssets(null)); + + final int diffIdx = Integer.parseInt(document.getElementsByTagName("diffIndex").item(0).getTextContent()); + QuestDataIO.setFinalField(QuestData.class, "achievements", newData, new QuestAchievements(diffIdx)); + } + + if (saveVersion < 4) { + QuestDataIO.setFinalField(QuestAssets.class, "inventoryItems", newData.getAssets(), new EnumMap(QuestItemType.class)); + } + + if (saveVersion < 5) { + QuestDataIO.setFinalField(QuestAssets.class, "combatPets", newData.getAssets(), new HashMap()); + } + + if (saveVersion < 6) { + QuestDataIO.setFinalField(QuestData.class, "petSlots", newData, new HashMap()); + } + + if(saveVersion < 8) { + QuestDataIO.setFinalField(QuestData.class, "isCharmActive", newData, false); + } + + final QuestAssets qS = newData.getAssets(); + final QuestAchievements qA = newData.getAchievements(); + + switch (saveVersion) { + // There should be a fall-through between the cases so that each + // version's changes get applied progressively + case 0: + // First beta release with new file format, + // inventory needs to be migrated + QuestDataIO.setFinalField(QuestAssets.class, "inventoryItems", newData.getAssets(), new EnumMap(QuestItemType.class)); + qS.setItemLevel(QuestItemType.ESTATES, Integer.parseInt(document.getElementsByTagName("estatesLevel").item(0).getTextContent())); + qS.setItemLevel(QuestItemType.LUCKY_COIN, Integer.parseInt(document.getElementsByTagName("luckyCoinLevel").item(0).getTextContent())); + qS.setItemLevel(QuestItemType.SLEIGHT, Integer.parseInt(document.getElementsByTagName("sleightOfHandLevel").item(0).getTextContent())); + + final int gearLevel = Integer.parseInt(document.getElementsByTagName("gearLevel").item(0).getTextContent()); + if (gearLevel >= 1) { + newData.getAssets().setItemLevel(QuestItemType.MAP, 1); + } + if (gearLevel == 2) { + newData.getAssets().setItemLevel(QuestItemType.ZEPPELIN, 1); + } + // fall-through + case 1: + // nothing to do here, everything is managed by CardPoolToXml + // deserializer + + case 2: + // questdata was divided into assets and achievements + if (StringUtils.isBlank(newData.getName())) { + QuestDataIO.setFinalField(QuestData.class, "name", newData, filename); + } + + QuestDataIO.setFinalField(QuestAchievements.class, "win", qA, Integer.parseInt(document.getElementsByTagName("win").item(0).getTextContent())); + QuestDataIO.setFinalField(QuestAchievements.class, "lost", qA, Integer.parseInt(document.getElementsByTagName("lost").item(0).getTextContent())); + + Node nw; + if ((nw = document.getElementsByTagName("winstreakBest").item(0)) != null) { + QuestDataIO.setFinalField(QuestAchievements.class, "winstreakBest", qA, Integer.parseInt(nw.getTextContent())); + } + if ((nw = document.getElementsByTagName("winstreakCurrent").item(0)) != null) { + QuestDataIO.setFinalField(QuestAchievements.class, "winstreakCurrent", qA, Integer.parseInt(nw.getTextContent())); + } + + QuestDataIO.setFinalField(QuestAchievements.class, "challengesPlayed", qA, Integer.parseInt(document.getElementsByTagName("challengesPlayed").item(0).getTextContent())); + + final ArrayList completedChallenges = new ArrayList(); + QuestDataIO.setFinalField(QuestAchievements.class, "completedChallenges", qA, completedChallenges); + + if ((nw = document.getElementsByTagName("completedChallenges").item(0)) != null) { + final NodeList ccs = nw.getChildNodes(); + for (int iN = 0; iN < ccs.getLength(); iN++) { + final Node n0 = ccs.item(iN); + if (n0.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + completedChallenges.add(Integer.parseInt(n0.getTextContent())); + } + } + + final XStream xs = QuestDataIO.getSerializer(true); + + QuestDataIO.setFinalField(QuestAssets.class, "credits", qS, Integer.parseInt(document.getElementsByTagName("credits").item(0).getTextContent())); + QuestDataIO.setFinalField(QuestAssets.class, "cardPool", qS, QuestDataIO.readAsset(xs, document, "cardPool", ItemPool.class)); + QuestDataIO.setFinalField(QuestAssets.class, "myDecks", qS, QuestDataIO.readAsset(xs, document, "myDecks", HashMap.class)); + QuestDataIO.setFinalField(QuestAssets.class, "shopList", qS, QuestDataIO.readAsset(xs, document, "shopList", ItemPool.class)); + QuestDataIO.setFinalField(QuestAssets.class, "newCardList", qS, QuestDataIO.readAsset(xs, document, "newCardList", ItemPool.class)); + + case 3: + // QuestInventory class no longer exists - KV pairs of + // QuestItemPair => level moved to assets + final Node oldInventory = saveVersion > 0 ? document.getElementsByTagName("inventory").item(1) : null; + if (null != oldInventory) { + for (int iN = 0; iN < oldInventory.getChildNodes().getLength(); iN++) { + final Node _n = oldInventory.getChildNodes().item(iN); + if (_n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + final Element n = (Element) _n; + final String name = n.getElementsByTagName("string").item(0).getTextContent(); + final QuestItemType qType = QuestItemType.valueFromSaveKey(name); + int level = 0; + for (int iX = 0; iX < n.getChildNodes().getLength(); iX++) { + final Node _x = n.getChildNodes().item(iX); + if (_x.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + final Element x = (Element) _x; + if (!x.getTagName().startsWith("forge.quest.data.")) { + continue; + } + final String sLevel = x.getElementsByTagName("level").item(0).getTextContent(); + if (StringUtils.isNotBlank(sLevel)) { + level = Integer.parseInt(sLevel); + } + } + qS.setItemLevel(qType, level); + } + } + + case 4: + if (saveVersion > 0) { + NodeList pets = document.getElementsByTagName("pets").item(0).getChildNodes(); + for (int i = 0; i < pets.getLength(); i++) { + if (pets.item(i).getNodeType() != Node.ELEMENT_NODE) { + continue; + } + final Element entry = (Element) pets.item(i); + String name = entry.getElementsByTagName("string").item(0).getTextContent(); + String sLevel = entry.getElementsByTagName("level").item(0).getTextContent(); + qS.setPetLevel(name, Integer.parseInt(sLevel)); + } + + } + + // pet manager will be re-engineered here + + case 6: + // have to convert completed challenges list members to strings. + for(int i = qA.getLockedChallenges().size()-1; i >= 0; i-- ) { + Object lc = qA.getLockedChallenges().get(i); + if (!(lc instanceof String)) + qA.getLockedChallenges().set(i, lc.toString()); + } + for(int i = qA.getCurrentChallenges().size()-1; i >= 0; i-- ) { + Object lc = qA.getCurrentChallenges().get(i); + if (!(lc instanceof String)) + qA.getCurrentChallenges().set(i, lc.toString()); + } + + default: + break; + } + + // mark the QD as the latest version + newData.setVersionNumber(QuestData.CURRENT_VERSION_NUMBER); + + + } + + @SuppressWarnings("unchecked") + private static T readAsset(final XStream xs, final Document doc, final String tagName, final Class clasz) + throws IllegalAccessException, NoSuchFieldException { + final NodeList nn = doc.getElementsByTagName(tagName); + final Node n = nn.item(0); + + final Attr att = doc.createAttribute("resolves-to"); + att.setValue(clasz.getCanonicalName()); + n.getAttributes().setNamedItem(att); + + final String xmlData = XmlUtil.nodeToString(n); + return (T) xs.fromXML(xmlData); + } + + /** + *

+ * saveData. + *

+ * + * @param qd + * a {@link forge.quest.data.QuestData} object. + */ + public static synchronized void saveData(final QuestData qd) { + try { + final XStream xStream = QuestDataIO.getSerializer(false); + + final File f = new File(Constants.QUEST_SAVE_DIR, qd.getName()); + QuestDataIO.savePacked(f + ".dat", xStream, qd); + // QuestDataIO.saveUnpacked(f + ".xml", xStream, qd); + + } + catch (final Exception ex) { + BugReporter.reportException(ex, "Error saving Quest Data."); + throw new RuntimeException(ex); + } + } + + private static void savePacked(final String f, final XStream xStream, final QuestData qd) throws IOException { + final BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(f)); + final GZIPOutputStream zout = new GZIPOutputStream(bout); + xStream.toXML(qd, zout); + zout.flush(); + zout.close(); + } + + @SuppressWarnings("unused") // used only for debug purposes + private static void saveUnpacked(final String f, final XStream xStream, final QuestData qd) throws IOException { + final BufferedOutputStream boutUnp = new BufferedOutputStream(new FileOutputStream(f)); + xStream.toXML(qd, boutUnp); + boutUnp.flush(); + boutUnp.close(); + } + + private static class GameFormatQuestToXml implements Converter { + @SuppressWarnings("rawtypes") + @Override + public boolean canConvert(final Class clasz) { + return clasz.equals(GameFormatQuest.class); + } + + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { + + writer.startNode("format"); + GameFormatQuest format = (GameFormatQuest) source; + writer.addAttribute("name", format.getName()); + writer.addAttribute("unlocksUsed", Integer.toString(format.getUnlocksUsed())); + writer.addAttribute("canUnlock", format.canUnlockSets() ? "1" : "0"); + writer.endNode(); + + for (String set : format.getAllowedSetCodes()) { + writer.startNode("set"); + writer.addAttribute("s", set); + writer.endNode(); + } + for (String ban : format.getBannedCardNames()) { + writer.startNode("ban"); + writer.addAttribute("s", ban); + writer.endNode(); + } + } + + @Override + public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + reader.moveDown(); + String name = reader.getAttribute("name"); + String unlocksUsed = reader.getAttribute("unlocksUsed"); + boolean canUnlock = !("0".equals(reader.getAttribute("canUnlock"))); + List allowedSets = new ArrayList(); + List bannedCards = new ArrayList(); + reader.moveUp(); + while (reader.hasMoreChildren()) { + reader.moveDown(); + final String nodename = reader.getNodeName(); + if (nodename.equals("ban")) { + bannedCards.add(reader.getAttribute("s")); + // System.out.println("Added + " + toBan + " to banned cards"); + } + else if (nodename.equals("set")) { + allowedSets.add(reader.getAttribute("s")); + // System.out.println("Added + " + toSets + " to legal sets"); + } + reader.moveUp(); + } + GameFormatQuest res = new GameFormatQuest(name, allowedSets, bannedCards); + try { + if (StringUtils.isNotEmpty(unlocksUsed)) { + setFinalField(GameFormatQuest.class, "unlocksUsed", res, Integer.parseInt(unlocksUsed)); + } + setFinalField(GameFormatQuest.class, "allowUnlocks", res, canUnlock); + } catch (NumberFormatException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + + return res; + } + } + + private static class QuestModeToXml implements Converter { + @SuppressWarnings("rawtypes") + @Override + public boolean canConvert(final Class clasz) { + return clasz.equals(QuestMode.class); + } + + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { + QuestMode mode = (QuestMode) source; + String sMode = mode.toString(); + writer.setValue(sMode); + } + + @Override + public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + final String value = reader.getValue(); + return QuestMode.smartValueOf(value, QuestMode.Classic); + } + } + + private static class DeckToXml extends ItemPoolToXml { + + /* (non-Javadoc) + * @see com.thoughtworks.xstream.converters.ConverterMatcher#canConvert(java.lang.Class) + */ + @SuppressWarnings("rawtypes") + @Override + public boolean canConvert(Class type) { + return type.equals(Deck.class); + } + + /* (non-Javadoc) + * @see com.thoughtworks.xstream.converters.Converter#marshal(java.lang.Object, com.thoughtworks.xstream.io.HierarchicalStreamWriter, com.thoughtworks.xstream.converters.MarshallingContext) + */ + @Override + public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { + Deck d = (Deck)source; + writer.startNode("name"); + writer.setValue(d.getName()); + writer.endNode(); + + for( Entry ds : d ) { + writer.startNode(ds.getKey().toString()); + for (final Entry e : ds.getValue()) { + this.write(e.getKey(), e.getValue(), writer); + } + writer.endNode(); + } + } + + /* (non-Javadoc) + * @see com.thoughtworks.xstream.converters.Converter#unmarshal(com.thoughtworks.xstream.io.HierarchicalStreamReader, com.thoughtworks.xstream.converters.UnmarshallingContext) + */ + @Override + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + + reader.moveDown(); // tag MUST be at first position at all times + String deckName = reader.getValue(); + reader.moveUp(); + + final Deck result = new Deck(deckName); + + while (reader.hasMoreChildren()) { + reader.moveDown(); + DeckSection section = DeckSection.smartValueOf(reader.getNodeName()); + if ( null == section ) + throw new RuntimeException("Quest deck has unknown section: " + reader.getNodeName()); + + CardPool pool = result.getOrCreate(section); + while (reader.hasMoreChildren()) { + reader.moveDown(); + final String sCnt = reader.getAttribute("n"); + final int cnt = StringUtils.isNumeric(sCnt) ? Integer.parseInt(sCnt) : 1; + final String nodename = reader.getNodeName(); + if ("string".equals(nodename)) { + pool.add(FModel.getMagicDb().getCommonCards().getCard(reader.getValue())); + } else if ("card".equals(nodename)) { // new format + pool.add(this.readCardPrinted(reader), cnt); + } + reader.moveUp(); + } + reader.moveUp(); + } + + return result; + } + } + + private static class ItemPoolToXml implements Converter { + @SuppressWarnings("rawtypes") + @Override + public boolean canConvert(final Class clasz) { + return clasz.equals(ItemPool.class); + } + + protected void write(final PaperCard cref, final Integer count, final HierarchicalStreamWriter writer) { + writer.startNode("card"); + writer.addAttribute("c", cref.getName()); + writer.addAttribute("s", cref.getEdition()); + if (cref.isFoil()) { + writer.addAttribute("foil", "1"); + } + writer.addAttribute("i", Integer.toString(cref.getArtIndex())); + writer.addAttribute("n", count.toString()); + writer.endNode(); + } + + protected void write(final BoosterPack booster, final Integer count, final HierarchicalStreamWriter writer) { + writer.startNode("booster"); + writer.addAttribute("s", booster.getEdition()); + writer.addAttribute("n", count.toString()); + writer.endNode(); + } + + protected void write(final FatPack fatpack, final Integer count, final HierarchicalStreamWriter writer) { + writer.startNode("fpack"); + writer.addAttribute("s", fatpack.getEdition()); + writer.addAttribute("n", count.toString()); + writer.endNode(); + } + + protected void write(final TournamentPack booster, final Integer count, final HierarchicalStreamWriter writer) { + writer.startNode("tpack"); + writer.addAttribute("s", booster.getEdition()); + writer.addAttribute("n", count.toString()); + writer.endNode(); + } + + protected void write(final PreconDeck deck, final Integer count, final HierarchicalStreamWriter writer) { + writer.startNode("precon"); + writer.addAttribute("name", deck.getName()); + writer.addAttribute("n", count.toString()); + writer.endNode(); + } + + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { + @SuppressWarnings("unchecked") + final ItemPool pool = (ItemPool) source; + for (final Entry e : pool) { + final InventoryItem item = e.getKey(); + final Integer count = e.getValue(); + if (item instanceof PaperCard) { + this.write((PaperCard) item, count, writer); + } else if (item instanceof BoosterPack) { + this.write((BoosterPack) item, count, writer); + } else if (item instanceof TournamentPack) { + this.write((TournamentPack) item, count, writer); + } else if (item instanceof FatPack) { + this.write((FatPack) item, count, writer); + } else if (item instanceof PreconDeck) { + this.write((PreconDeck) item, count, writer); + } + } + + } + + @Override + public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + final ItemPool result = new ItemPool(InventoryItem.class); + while (reader.hasMoreChildren()) { + reader.moveDown(); + final String sCnt = reader.getAttribute("n"); + final int cnt = StringUtils.isNumeric(sCnt) ? Integer.parseInt(sCnt) : 1; + final String nodename = reader.getNodeName(); + + if ("string".equals(nodename)) { + result.add(FModel.getMagicDb().getCommonCards().getCard(reader.getValue())); + } else if ("card".equals(nodename)) { // new format + result.add(this.readCardPrinted(reader), cnt); + } else if ("booster".equals(nodename)) { + result.add(this.readBooster(reader), cnt); + } else if ("tpack".equals(nodename)) { + result.add(this.readTournamentPack(reader), cnt); + } else if ("fpack".equals(nodename)) { + result.add(this.readFatPack(reader), cnt); + } else if ("precon".equals(nodename)) { + final PreconDeck toAdd = this.readPreconDeck(reader); + if (null != toAdd) { + result.add(toAdd, cnt); + } + } + reader.moveUp(); + } + return result; + } + + protected PreconDeck readPreconDeck(final HierarchicalStreamReader reader) { + String name = reader.getAttribute("name"); + if (name == null) { + name = reader.getAttribute("s"); + } + return QuestController.getPrecons().get(name); + } + + protected BoosterPack readBooster(final HierarchicalStreamReader reader) { + final CardEdition ed = FModel.getMagicDb().getEditions().get(reader.getAttribute("s")); + return BoosterPack.FN_FROM_SET.apply(ed); + } + + protected TournamentPack readTournamentPack(final HierarchicalStreamReader reader) { + final CardEdition ed = FModel.getMagicDb().getEditions().get(reader.getAttribute("s")); + return TournamentPack.FN_FROM_SET.apply(ed); + } + + protected FatPack readFatPack(final HierarchicalStreamReader reader) { + final CardEdition ed = FModel.getMagicDb().getEditions().get(reader.getAttribute("s")); + return FatPack.FN_FROM_SET.apply(ed); + } + + protected PaperCard readCardPrinted(final HierarchicalStreamReader reader) { + final String name = reader.getAttribute("c"); + final String set = reader.getAttribute("s"); + final String sIndex = reader.getAttribute("i"); + final short index = StringUtils.isNumeric(sIndex) ? Short.parseShort(sIndex) : 0; + final boolean foil = "1".equals(reader.getAttribute("foil")); + PaperCard c = FModel.getMagicDb().getCommonCards().getCard(name, set, index); + if ( null == c ) c = FModel.getMagicDb().getCommonCards().getCard(name); + return foil ? FModel.getMagicDb().getCommonCards().getFoiled(c) : c; + } + } +} diff --git a/forge-m-base/src/forge/quest/io/QuestDuelReader.java b/forge-m-base/src/forge/quest/io/QuestDuelReader.java new file mode 100644 index 00000000000..e3f4a868486 --- /dev/null +++ b/forge-m-base/src/forge/quest/io/QuestDuelReader.java @@ -0,0 +1,47 @@ +package forge.quest.io; + +import forge.ImageKeys; +import forge.deck.io.DeckSerializer; +import forge.deck.io.DeckStorage; +import forge.quest.QuestEvent; +import forge.quest.QuestEventDifficulty; +import forge.quest.QuestEventDuel; +import forge.util.FileSection; +import forge.util.FileUtil; +import forge.util.storage.StorageReaderFolder; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.List; +import java.util.Map; + +public class QuestDuelReader extends StorageReaderFolder { + public QuestDuelReader(File deckDir0) { + super(deckDir0, QuestEvent.FN_GET_NAME); + // TODO Auto-generated constructor stub + } + + @Override + protected QuestEventDuel read(File file) { + final Map> contents = FileSection.parseSections(FileUtil.readFile(file)); + final QuestEventDuel qc = new QuestEventDuel(); + + // Common properties + FileSection sectionMeta = FileSection.parse(contents.get("metadata"), "="); + qc.setTitle(sectionMeta.get("Title")); + qc.setName(sectionMeta.get("Name")); // Challenges have unique titles + qc.setDifficulty(QuestEventDifficulty.fromString(sectionMeta.get("Difficulty"))); + qc.setDescription(sectionMeta.get("Description")); + qc.setCardReward(sectionMeta.get("Card Reward")); + qc.setIconImageKey(ImageKeys.ICON_PREFIX + sectionMeta.get("Icon")); + + // Deck + qc.setEventDeck(DeckSerializer.fromSections(contents)); + return qc; + } + + @Override + protected FilenameFilter getFileFilter() { + return DeckStorage.DCK_FILE_FILTER; + } +} \ No newline at end of file diff --git a/forge-m-base/src/forge/quest/io/ReadPriceList.java b/forge-m-base/src/forge/quest/io/ReadPriceList.java new file mode 100644 index 00000000000..198bdec0b20 --- /dev/null +++ b/forge-m-base/src/forge/quest/io/ReadPriceList.java @@ -0,0 +1,132 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.quest.io; + +import forge.card.MagicColor; +import forge.error.BugReporter; +import forge.util.FileUtil; +import forge.util.MyRandom; +import forge.utils.Constants; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + *

+ * ReadPriceList class. + *

+ * + * @author Forge + * @version $Id: ReadPriceList.java 24769 2014-02-09 13:56:04Z Hellfish $ + */ +public class ReadPriceList { + + /** Constant comment="//". */ + private static final String COMMENT = "//"; + + private HashMap priceMap; + + /** + *

+ * Constructor for ReadPriceList. + *

+ */ + public ReadPriceList() { + this.setup(); + } + + /** + *

+ * setup. + *

+ */ + private void setup() { + this.priceMap = this.readFile(Constants.QUEST_CARD_PRICE_FILE); + this.priceMap.putAll(this.readFile(Constants.PRICES_BOOSTER_FILE)); + + } // setup() + + /** + *

+ * readFile. + *

+ * + * @param file + * a {@link java.io.File} object. + * @return a {@link java.util.HashMap} object. + */ + private HashMap readFile(String file) { + final HashMap map = new HashMap(); + final Random r = MyRandom.getRandom(); + + List lines = FileUtil.readFile(file); + for (String line : lines) { + if (line.trim().length() == 0) { + break; + } + + if (line.startsWith(ReadPriceList.COMMENT)) { + continue; + } + + final String[] s = line.split("="); + final String name = s[0].trim(); + final String price = s[1].trim(); + + try { + int val = Integer.parseInt(price.trim()); + + if (!(MagicColor.Constant.BASIC_LANDS.contains(name) || MagicColor.Constant.SNOW_LANDS.contains(name))) { + float ff = 0; + if (r.nextInt(100) < 90) { + ff = r.nextInt(10) * (float) .01; + } else { + // +/- 50% + ff = r.nextInt(50) * (float) .01; + } + + if (r.nextInt(100) < 50) { + val = (int) (val * (1 - ff)); + } else { + // +ff% + val = (int) (val * (1 + ff)); + } + } + + map.put(name, val); + } + catch (final NumberFormatException nfe) { + BugReporter.reportBug("NumberFormatException: " + nfe.getMessage()); + } + } + return map; + } // readFile() + + /** + *

+ * getPriceList. + *

+ * + * @return a {@link java.util.Map} object. + */ + public final Map getPriceList() { + return this.priceMap; + } +} diff --git a/forge-m-base/src/forge/quest/io/package-info.java b/forge-m-base/src/forge/quest/io/package-info.java new file mode 100644 index 00000000000..f82bb9b68d6 --- /dev/null +++ b/forge-m-base/src/forge/quest/io/package-info.java @@ -0,0 +1,3 @@ +/** Forge Card Game. */ +package forge.quest.io; + diff --git a/forge-m-base/src/forge/quest/package-info.java b/forge-m-base/src/forge/quest/package-info.java new file mode 100644 index 00000000000..ab7a0dc46a7 --- /dev/null +++ b/forge-m-base/src/forge/quest/package-info.java @@ -0,0 +1,3 @@ +/** Forge Card Game. */ +package forge.quest; + diff --git a/forge-m-base/src/forge/screens/constructed/ConstructedScreen.java b/forge-m-base/src/forge/screens/constructed/ConstructedScreen.java index f97c6c0871b..41834e9d744 100644 --- a/forge-m-base/src/forge/screens/constructed/ConstructedScreen.java +++ b/forge-m-base/src/forge/screens/constructed/ConstructedScreen.java @@ -1,21 +1,13 @@ package forge.screens.constructed; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.Vector; +import java.util.*; import org.apache.commons.lang3.StringUtils; import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment; import com.google.common.base.Predicate; +import forge.Forge; import forge.Forge.Graphics; import forge.assets.FSkin; import forge.assets.FSkinColor; @@ -24,17 +16,20 @@ import forge.assets.FSkinImage; import forge.assets.FTextureRegionImage; import forge.deck.CardPool; import forge.deck.Deck; +import forge.deck.DeckProxy; import forge.deck.DeckSection; +import forge.deck.DeckType; import forge.deck.DeckgenUtil; import forge.deck.FDeckChooser; import forge.game.GameType; import forge.game.player.LobbyPlayer; import forge.game.player.RegisteredPlayer; -import forge.game.player.LobbyPlayer.PlayerType; import forge.item.PaperCard; +import forge.model.CardCollections; import forge.model.FModel; import forge.net.FServer; import forge.net.Lobby; +import forge.screens.FScreen; import forge.screens.LaunchScreen; import forge.toolbox.FCheckBox; import forge.toolbox.FComboBox; @@ -48,6 +43,7 @@ import forge.toolbox.FOptionPane; import forge.toolbox.FScrollPane; import forge.toolbox.FTextField; import forge.util.Aggregates; +import forge.util.Lang; import forge.util.MyRandom; import forge.util.NameGenerator; import forge.util.storage.IStorage; @@ -93,16 +89,11 @@ public class ConstructedScreen extends LaunchScreen { private final List closePlayerBtnList = new ArrayList(6); private final FLabel addPlayerBtn = new FLabel.ButtonBuilder().fontSize(14).text("Add a Player").build(); - - private final List deckChoosers = new ArrayList(8); + private final FCheckBox cbSingletons = new FCheckBox("Singleton Mode"); private final FCheckBox cbArtifacts = new FCheckBox("Remove Artifacts"); // Variants - private final List schemeDeckLists = new ArrayList(); - private final List commanderDeckLists = new ArrayList(); - private final List planarDeckLists = new ArrayList(); - private final List vgdAvatarLists = new ArrayList(); private final List vgdAllAvatars = new ArrayList(); private final List vgdAllAiAvatars = new ArrayList(); private final List nonRandomHumanAvatars = new ArrayList(); @@ -113,7 +104,7 @@ public class ConstructedScreen extends LaunchScreen { public ConstructedScreen() { super("Constructed"); - + /*lblTitle.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2)); //////////////////////////////////////////////////////// @@ -160,13 +151,36 @@ public class ConstructedScreen extends LaunchScreen { add(playersScroll); - addPlayerBtn.setCommand(new FEventHandler() { + getDeckChooser(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK); + getDeckChooser(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, DeckType.COLOR_DECK); + getDeckChooser(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, DeckType.COLOR_DECK); + getDeckChooser(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, DeckType.COLOR_DECK); + getDeckChooser(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK); + getDeckChooser(5).initialize(FPref.CONSTRUCTED_P6_DECK_STATE, DeckType.COLOR_DECK); + getDeckChooser(6).initialize(FPref.CONSTRUCTED_P7_DECK_STATE, DeckType.COLOR_DECK); + getDeckChooser(7).initialize(FPref.CONSTRUCTED_P8_DECK_STATE, DeckType.COLOR_DECK); + + // Checkbox event handling + cbSingletons.setCommand(new FEventHandler() { @Override public void handleEvent(FEvent e) { - addPlayer(); + prefs.setPref(FPref.DECKGEN_SINGLETONS, String.valueOf(cbSingletons.isSelected())); + prefs.save(); } }); - add(addPlayerBtn); + cbArtifacts.setCommand(new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + prefs.setPref(FPref.DECKGEN_ARTIFACTS, String.valueOf(cbArtifacts.isSelected())); + prefs.save(); + } + }); + + // Pre-select checkboxes + cbSingletons.setSelected(prefs.getPrefBoolean(FPref.DECKGEN_SINGLETONS)); + cbArtifacts.setSelected(prefs.getPrefBoolean(FPref.DECKGEN_ARTIFACTS)); + + updatePlayersFromPrefs(); } @Override @@ -203,35 +217,13 @@ public class ConstructedScreen extends LaunchScreen { } public final FDeckChooser getDeckChooser(int playernum) { - return deckChoosers.get(playernum); - } - - private class DeckList extends FList { - Object selectedValue; - - public Object getSelectedValue() { - return selectedValue; - } - } - - public boolean isPlayerAI(int playernum) { - return playerPanels.get(playernum).getPlayerType() == PlayerType.COMPUTER; + return playerPanels.get(playernum).deckChooser; } public int getNumPlayers() { return activePlayersNum; } - public final List getParticipants() { - final List participants = new ArrayList(activePlayersNum); - for (final PlayerPanel panel : playerPanels) { - if (panel.isVisible()) { - participants.add(playerPanels.indexOf(panel)); - } - } - return participants; - } - @Override protected boolean buildLaunchParams(LaunchParams launchParams) { launchParams.gameType = GameType.Constructed; @@ -241,7 +233,7 @@ public class ConstructedScreen extends LaunchScreen { return false; } - for (final int i : getParticipants()) { + for (int i = 0; i < activePlayersNum; i++) { if (getDeckChooser(i).getPlayer() == null) { FOptionPane.showMessageDialog("Please specify a deck for " + getPlayerName(i)); return false; @@ -253,7 +245,7 @@ public class ConstructedScreen extends LaunchScreen { boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY); if (checkLegality && !variantTypes.contains(GameType.Commander)) { //Commander deck replaces regular deck and is checked later - for (final int i : getParticipants()) { + for (int i = 0; i < activePlayersNum; i++) { String name = getPlayerName(i); String errMsg = GameType.Constructed.getDecksFormat().getDeckConformanceProblem(getDeckChooser(i).getPlayer().getDeck()); if (errMsg != null) { @@ -265,11 +257,12 @@ public class ConstructedScreen extends LaunchScreen { Lobby lobby = FServer.getLobby(); List players = new ArrayList(); - for (final int i : getParticipants()) { + for (int i = 0; i < activePlayersNum; i++) { + PlayerPanel playerPanel = playerPanels.get(i); String name = getPlayerName(i); - LobbyPlayer lobbyPlayer = isPlayerAI(i) ? lobby.getAiPlayer(name, + LobbyPlayer lobbyPlayer = playerPanel.isPlayerAI() ? lobby.getAiPlayer(name, getPlayerAvatar(i)) : lobby.getGuiPlayer(); - RegisteredPlayer rp = getDeckChooser(i).getPlayer(); + RegisteredPlayer rp = playerPanel.deckChooser.getPlayer(); if (variantTypes.isEmpty()) { rp.setTeamNumber(getTeam(i)); @@ -279,7 +272,7 @@ public class ConstructedScreen extends LaunchScreen { Deck deck = null; boolean isCommanderMatch = variantTypes.contains(GameType.Commander); if (isCommanderMatch) { - Object selected = commanderDeckLists.get(i).getSelectedValue(); + Object selected = playerPanel.commanderDeckList.getSelectedValue(); if (selected instanceof String) { String sel = (String) selected; IStorage comDecks = FModel.getDecks().getCommander(); @@ -312,7 +305,7 @@ public class ConstructedScreen extends LaunchScreen { //Archenemy if (variantTypes.contains(GameType.ArchenemyRumble) || (variantTypes.contains(GameType.Archenemy) && playerIsArchenemy)) { - Object selected = schemeDeckLists.get(i).getSelectedValue(); + Object selected = playerPanel.schemeDeckList.getSelectedValue(); CardPool schemePool = null; if (selected instanceof String) { String sel = (String) selected; @@ -347,7 +340,7 @@ public class ConstructedScreen extends LaunchScreen { //Planechase if (variantTypes.contains(GameType.Planechase)) { - Object selected = planarDeckLists.get(i).getSelectedValue(); + Object selected = playerPanel.planarDeckList.getSelectedValue(); CardPool planePool = null; if (selected instanceof String) { String sel = (String) selected; @@ -381,7 +374,7 @@ public class ConstructedScreen extends LaunchScreen { //Vanguard if (variantTypes.contains(GameType.Vanguard)) { - Object selected = vgdAvatarLists.get(i).getSelectedValue(); + Object selected = playerPanel.vgdAvatarList.getSelectedValue(); if (selected instanceof String) { String sel = (String) selected; if (sel.contains("Use deck's default avatar") && deck.has(DeckSection.Avatar)) { @@ -448,10 +441,26 @@ public class ConstructedScreen extends LaunchScreen { private final FLabel vgdSelectorBtn = new FLabel.ButtonBuilder().text("Select a Vanguard avatar").build(); private final FLabel vgdLabel = newLabel("Vanguard:"); + private final FDeckChooser deckChooser; + private final DeckList schemeDeckList, commanderDeckList, planarDeckList, vgdAvatarList; + public PlayerPanel(final int index0) { super(); index = index0; playerIsArchenemy = index == 0; + deckChooser = new FDeckChooser(isPlayerAI()); + deckChooser.initialize(); + deckChooser.getLstDecks().setSelectCommand(new FEventHandler() { + @Override + public void handleEvent(FEvent e) { + String text = deckChooser.getSelectedDeckType().toString() + ": " + Lang.joinHomogenous(deckChooser.getLstDecks().getSelectedItems(), DeckProxy.FN_GET_NAME); + setDeckSelectorButtonText(text); + } + }); + schemeDeckList = new DeckList(); + commanderDeckList = new DeckList(); + planarDeckList = new DeckList(); + vgdAvatarList = new DeckList(); // Add a button to players 3+ to remove them from the setup if (index >= 2) { @@ -502,6 +511,39 @@ public class ConstructedScreen extends LaunchScreen { addHandlersToVariantsControls(); updateVariantControlsVisibility(); + + final CardCollections decks = FModel.getDecks(); + + commanderDeckList.list.addItem("Generate"); + if (decks.getCommander().size() > 0) { + commanderDeckList.list.addItem("Random"); + for (Deck comDeck : decks.getCommander()) { + commanderDeckList.list.addItem(comDeck); + } + } + commanderDeckList.setSelectedIndex(0); + + schemeDeckList.list.addItem("Use deck's scheme section (random if unavailable)"); + schemeDeckList.list.addItem("Generate"); + if (decks.getScheme().size() > 0) { + schemeDeckList.list.addItem("Random"); + for (Deck schemeDeck : decks.getScheme()) { + schemeDeckList.list.addItem(schemeDeck); + } + } + schemeDeckList.setSelectedIndex(0); + + planarDeckList.list.addItem("Use deck's planes section (random if unavailable)"); + planarDeckList.list.addItem("Generate"); + if (decks.getPlane().size() > 0) { + planarDeckList.list.addItem("Random"); + for (Deck planarDeck : decks.getPlane()) { + planarDeckList.list.addItem(planarDeck); + } + } + planarDeckList.setSelectedIndex(0); + + updateVanguardList(); } @Override @@ -557,7 +599,7 @@ public class ConstructedScreen extends LaunchScreen { private final FEventHandler humanAiSwitched = new FEventHandler() { @Override public void handleEvent(FEvent e) { - updateVanguardList(index); + updateVanguardList(); } }; @@ -634,8 +676,8 @@ public class ConstructedScreen extends LaunchScreen { vgdLabel.setVisible(appliedVariants.contains(GameType.Vanguard)); } - public PlayerType getPlayerType() { - return humanAiSwitch.isToggled() ? PlayerType.COMPUTER : PlayerType.HUMAN; + public boolean isPlayerAI() { + return humanAiSwitch.isToggled(); } public void setVanguardButtonText(String text) { @@ -871,6 +913,79 @@ public class ConstructedScreen extends LaunchScreen { public String getPlayerName() { return txtPlayerName.getText(); } + + private void changeDeck(final GameType forGameType) { + switch (forGameType) { + case Constructed: + Forge.openScreen(deckChooser); + break; + case Archenemy: + case ArchenemyRumble: + if (playerIsArchenemy) { + Forge.openScreen(schemeDeckList); + } + else { + Forge.openScreen(deckChooser); + } + break; + case Commander: + Forge.openScreen(commanderDeckList); + break; + case Planechase: + Forge.openScreen(planarDeckList); + break; + case Vanguard: + updateVanguardList(); + Forge.openScreen(vgdAvatarList); + break; + default: + break; + } + } + + /** update vanguard list. */ + public void updateVanguardList() { + Object lastSelection = vgdAvatarList.getSelectedValue(); + vgdAvatarList.setSelectedIndex(-1); + vgdAvatarList.list.setListData(isPlayerAI() ? aiListData : humanListData); + if (lastSelection != null) { + vgdAvatarList.setSelectedValue(lastSelection); + } + if (vgdAvatarList.getSelectedIndex() == -1) { + vgdAvatarList.setSelectedIndex(0); + } + } + + private class DeckList extends FScreen { + private final FList list; + private int selectedIndex; + + private DeckList() { + super(true, "", false); + list = new FList(); + } + + public int getSelectedIndex() { + return selectedIndex; + } + + public void setSelectedIndex(int index) { + selectedIndex = index; + } + + public Object getSelectedValue() { + return list.getItemAt(selectedIndex); + } + + public void setSelectedValue(Object value) { + selectedIndex = list.getIndexOf(value); + } + + @Override + protected void doLayout(float startY, float width, float height) { + list.setBounds(0, startY, width, height - startY); + } + } } /** Saves avatar prefs for players one and two. */ @@ -961,7 +1076,7 @@ public class ConstructedScreen extends LaunchScreen { int lastTeam = -1; final List teamList = appliedVariants.contains(GameType.Archenemy) ? archenemyTeams : teams; - for (final int i : getParticipants()) { + for (int i = 0; i < activePlayersNum; i++) { if (lastTeam == -1) { lastTeam = teamList.get(i); } @@ -977,7 +1092,7 @@ public class ConstructedScreen extends LaunchScreen { /** This listener unlocks the relevant buttons for players * and enables/disables archenemy combobox as appropriate. */ - private ItemListener iListenerVariants = new ItemListener() { + /*private ItemListener iListenerVariants = new ItemListener() { @Override public void itemStateChanged(ItemEvent arg0) { FCheckBox cb = (FCheckBox) arg0.getSource(); @@ -1043,7 +1158,7 @@ public class ConstructedScreen extends LaunchScreen { //This listener will look for a vanguard avatar being selected in the lists //and update the corresponding detail panel. - /*private ListSelectionListener vgdLSListener = new ListSelectionListener() { + private ListSelectionListener vgdLSListener = new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { int index = vgdAvatarLists.indexOf(e.getSource()); @@ -1074,23 +1189,11 @@ public class ConstructedScreen extends LaunchScreen { public int getTeam(final int playerIndex) { return appliedVariants.contains(GameType.Archenemy) ? archenemyTeams.get(playerIndex) : teams.get(playerIndex); } - - /*public List> getPlanarDeckLists() { - return planarDeckLists; - } - public List> getCommanderDeckLists() { - return commanderDeckLists; + public boolean isPlayerAI(final int playernum) { + return playerPanels.get(playernum).isPlayerAI(); } - public List> getSchemeDeckLists() { - return schemeDeckLists; - } - - public List> getVanguardLists() { - return vgdAvatarLists; - }*/ - public boolean isPlayerArchenemy(final int playernum) { return playerPanels.get(playernum).playerIsArchenemy; } @@ -1142,18 +1245,4 @@ public class ConstructedScreen extends LaunchScreen { } } } - - /** update vanguard list. */ - public void updateVanguardList(int playerIndex) { - /*FList vgdList = getVanguardLists().get(playerIndex); - Object lastSelection = vgdList.getSelectedValue(); - vgdList.setListData(isPlayerAI(playerIndex) ? aiListData : humanListData); - if (null != lastSelection) { - vgdList.setSelectedValue(lastSelection, true); - } - - if (-1 == vgdList.getSelectedIndex()) { - vgdList.setSelectedIndex(0); - }*/ - } } diff --git a/forge-m-base/src/forge/toolbox/FComboBox.java b/forge-m-base/src/forge/toolbox/FComboBox.java index fa9a221be14..610c54ccbe2 100644 --- a/forge-m-base/src/forge/toolbox/FComboBox.java +++ b/forge-m-base/src/forge/toolbox/FComboBox.java @@ -31,8 +31,17 @@ public class FComboBox extends FTextField { initialize(); } + private void initialize() { + if (!items.isEmpty()) { + setSelectedItem(items.get(0)); //select first item by default + } + } + public void addItem(E item) { items.add(item); + if (items.size() == 1) { + setSelectedItem(item); //select first item by default + } } public boolean removeItem(E item) { @@ -81,6 +90,7 @@ public class FComboBox extends FTextField { selectedItem = item; super.setText(item.toString()); } + else { return; } } else { selectedItem = null; @@ -104,12 +114,6 @@ public class FComboBox extends FTextField { setText(text0); } - private void initialize() { - if (!items.isEmpty()) { - setSelectedItem(items.get(0)); //select first item by default - } - } - @Override public boolean tap(float x, float y, int count) { dropDown.setVisible(!dropDown.isVisible()); diff --git a/forge-m-base/src/forge/toolbox/FList.java b/forge-m-base/src/forge/toolbox/FList.java index 29abbed2554..900ba96eaeb 100644 --- a/forge-m-base/src/forge/toolbox/FList.java +++ b/forge-m-base/src/forge/toolbox/FList.java @@ -83,6 +83,13 @@ public class FList extends FScrollPane { groups.clear(); } + public void setListData(Iterable items0) { + clear(); + for (E item : items0) { + addItem(item); + } + } + public boolean isEmpty() { return groups.isEmpty(); } @@ -95,7 +102,7 @@ public class FList extends FScrollPane { return count; } - public Object getItemAt(int index) { + public E getItemAt(int index) { int count = 0; for (ListGroup group : groups) { for (ListItem item : group.items) { @@ -108,6 +115,19 @@ public class FList extends FScrollPane { return null; } + public int getIndexOf(E value) { + int count = 0; + for (ListGroup group : groups) { + for (ListItem item : group.items) { + if (item.value == value) { + return count; + } + count++; + } + } + return -1; + } + public void setListItemRenderer(ListItemRenderer renderer0) { renderer = renderer0; } diff --git a/forge-m-base/src/forge/utils/XmlUtil.java b/forge-m-base/src/forge/utils/XmlUtil.java new file mode 100644 index 00000000000..ede3269b8c3 --- /dev/null +++ b/forge-m-base/src/forge/utils/XmlUtil.java @@ -0,0 +1,54 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package forge.utils; + +import org.w3c.dom.Node; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.StringWriter; + +/** + * TODO: Write javadoc for this type. + * + */ +public class XmlUtil { + + /** + * Node to string. + * + * @param node the node + * @return the string + */ + public static String nodeToString(final Node node) { + final StringWriter sw = new StringWriter(); + try { + final Transformer t = TransformerFactory.newInstance().newTransformer(); + t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + t.setOutputProperty(OutputKeys.INDENT, "yes"); + t.transform(new DOMSource(node), new StreamResult(sw)); + } catch (final TransformerException te) { + System.out.println("nodeToString Transformer Exception"); + } + return sw.toString(); + } +}