diff --git a/src/main/java/spireQuests/questStats/QuestStatsScreen.java b/src/main/java/spireQuests/questStats/QuestStatsScreen.java index c3036164..ec515477 100644 --- a/src/main/java/spireQuests/questStats/QuestStatsScreen.java +++ b/src/main/java/spireQuests/questStats/QuestStatsScreen.java @@ -233,6 +233,9 @@ private void updateButtons() { public void render(SpriteBatch sb) { sb.setColor(Color.WHITE); renderBG(sb); + if (!questDropdown.isOpen) { + questDropdown.render(sb, LEFT_ALIGN, DROPDOWN_Y); + } if (selectedQuest == null){ renderTrophyHelp(sb); renderSummary(sb); @@ -243,7 +246,9 @@ public void render(SpriteBatch sb) { renderTrophyTooltip(sb); renderCheckbox(sb); } - questDropdown.render(sb, LEFT_ALIGN, DROPDOWN_Y); + if (questDropdown.isOpen) { + questDropdown.render(sb, LEFT_ALIGN, DROPDOWN_Y); + } cancelButton.render(sb); } diff --git a/src/main/java/spireQuests/questStats/StatRewardBox.java b/src/main/java/spireQuests/questStats/StatRewardBox.java index 43cda83c..194e66a0 100644 --- a/src/main/java/spireQuests/questStats/StatRewardBox.java +++ b/src/main/java/spireQuests/questStats/StatRewardBox.java @@ -4,6 +4,7 @@ import static spireQuests.Anniv8Mod.makeUIPath; import java.lang.reflect.Field; +import java.util.ArrayList; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; @@ -23,10 +24,12 @@ import basemod.IUIElement; import spireQuests.quests.AbstractQuest; import spireQuests.quests.QuestReward; +import spireQuests.quests.QuestReward.CardChoiceReward; import spireQuests.quests.QuestReward.CardReward; import spireQuests.quests.QuestReward.GoldReward; import spireQuests.quests.QuestReward.MaxHPReward; import spireQuests.quests.QuestReward.PotionReward; +import spireQuests.quests.QuestReward.QuestRewardType; import spireQuests.quests.QuestReward.RandomRelicReward; import spireQuests.quests.QuestReward.RelicReward; import spireQuests.util.ImageHelper; @@ -43,6 +46,7 @@ public class StatRewardBox implements IUIElement { private static final Texture GOLD_TEX = TexLoader.getTexture(makeUIPath("stats/gold.png")); private static final Texture HEALTH_TEX = TexLoader.getTexture(makeUIPath("stats/heart.png")); private static final Texture RANDOM_REWARD_TEX = TexLoader.getTexture(makeUIPath("stats/random_item.png")); + private static final Texture CARD_CHOICE_TEX = TexLoader.getTexture(makeUIPath("stats/card_choice.png")); private static final String ID = makeID(StatRewardBox.class.getSimpleName()); private UIStrings uiStrings = CardCrawlGame.languagePack.getUIString(ID); @@ -58,8 +62,10 @@ public class StatRewardBox implements IUIElement { private float xPos; private float yPos; private AbstractCard card = null; + private ArrayList cards = null; private AbstractPotion potion = null; private AbstractRelic relic = null; + private QuestRewardType type = QuestRewardType.OTHER; public StatRewardBox(QuestReward reward, float xPos, float yPos) { this(xPos, yPos); @@ -71,26 +77,42 @@ public StatRewardBox(QuestReward reward, float xPos, float yPos) { this.relic = ((RelicReward)reward).getRelic().makeCopy(); this.header = relic.name; this.body = relic.description; + this.type = QuestRewardType.RELIC; } if (reward instanceof CardReward) { this.card = ((CardReward)reward).getCard().makeStatEquivalentCopy(); + this.type = QuestRewardType.CARD; + } + if (reward instanceof CardChoiceReward) { + this.cards = ((CardChoiceReward)reward).getCards(); + if (((CardChoiceReward)reward).isColorless) { + this.header = uiStrings.TEXT[7]; + } else { + this.header = uiStrings.TEXT[6]; + } + this.img = new TextureRegion(CARD_CHOICE_TEX); + this.type = QuestRewardType.CARD_CHOICE; } if (reward instanceof PotionReward) { this.potion = ((PotionReward)reward).getPotion().makeCopy(); this.header = potion.name; this.body = potion.description; + this.type = QuestRewardType.POTION; } if (reward instanceof RandomRelicReward) { this.header = uiStrings.TEXT[0]; this.img = new TextureRegion(RANDOM_RELIC_TEX); + this.type = QuestRewardType.RANDOM_RELIC; } if (reward instanceof GoldReward) { this.header = uiStrings.TEXT[1]; this.img = new TextureRegion(GOLD_TEX); + this.type = QuestRewardType.GOLD; } if (reward instanceof MaxHPReward) { this.header = uiStrings.TEXT[3]; this.img = new TextureRegion(HEALTH_TEX); + this.type = QuestRewardType.MAX_HP; } } @@ -135,7 +157,7 @@ public void render(SpriteBatch sb) { sb.draw(FRAME, xPos, yPos, FRAME_X, FRAME_Y); } - if (this.card != null) { + if (this.type.equals(QuestRewardType.CARD)) { Color cardBGColor; switch (this.card.color) { case RED: @@ -160,7 +182,7 @@ public void render(SpriteBatch sb) { sb.setColor(Color.WHITE); this.img = new TextureRegion(CARD_FG_TEX); sb.draw(this.img, xPos + (FRAME_X - WIDTH) / 2, yPos + (FRAME_Y - HEIGHT) / 2, WIDTH, HEIGHT); - } else if (this.potion != null) { + } else if (this.type.equals(QuestRewardType.POTION)) { try { renderPotion(sb); } catch (Exception e) { @@ -172,13 +194,30 @@ public void render(SpriteBatch sb) { } if (this.hb.hovered) { - if (card != null) { + if (this.type.equals(QuestRewardType.CARD)) { card.current_x = InputHelper.mX + (AbstractCard.RAW_W * card.drawScale) * Settings.scale; card.current_y = InputHelper.mY; card.renderHoverShadow(sb); card.render(sb); + } else if (this.type.equals(QuestRewardType.CARD_CHOICE)){ + if (this.cards != null && !this.cards.isEmpty()) { + float gap = 25.0F * Settings.scale; + float cardWidth = AbstractCard.RAW_W * this.cards.get(0).drawScale * Settings.scale; + float totalWidth = (this.cards.size() * cardWidth) + ((this.cards.size() - 1) * gap); + float startX = InputHelper.mX - totalWidth / 2.0F; + float aboveY = (this.yPos + HEIGHT) + ((AbstractCard.RAW_W * this.cards.get(0).drawScale) * Settings.scale); + for (int i = 0; i < this.cards.size(); i++) { + AbstractCard c = this.cards.get(i); + c.current_x = startX + cardWidth * 0.5F + i * (cardWidth + gap); + c.current_y = aboveY; + c.renderHoverShadow(sb); + c.render(sb); + } + } else { + ImageHelper.tipBoxAtMousePos(this.header, this.body); + } } else { - ImageHelper.tipBoxAtMousePos(this.header, this.body); + ImageHelper.tipBoxAtMousePos(this.header, this.body); } } this.hb.render(sb); @@ -230,9 +269,9 @@ public void update() { CardCrawlGame.sound.play("UI_HOVER"); } if (this.hb.hovered && InputHelper.justClickedRight) { - if (card != null) { + if (type.equals(QuestRewardType.CARD)) { CardCrawlGame.cardPopup.open(card); - } else if (relic != null) { + } else if (type.equals(QuestRewardType.RELIC)) { CardCrawlGame.relicPopup.open(relic); } } diff --git a/src/main/java/spireQuests/quests/QuestReward.java b/src/main/java/spireQuests/quests/QuestReward.java index 84d2003a..433c9c03 100644 --- a/src/main/java/spireQuests/quests/QuestReward.java +++ b/src/main/java/spireQuests/quests/QuestReward.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.megacrit.cardcrawl.cards.AbstractCard; +import com.megacrit.cardcrawl.cards.AbstractCard.CardColor; import com.megacrit.cardcrawl.cards.CardSave; import com.megacrit.cardcrawl.core.CardCrawlGame; import com.megacrit.cardcrawl.core.Settings; @@ -29,12 +30,14 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.stream.Collectors; import static spireQuests.Anniv8Mod.makeID; import static spireQuests.Anniv8Mod.makeUIPath; import static spireQuests.util.LanguageUtils.formatLanguage; public abstract class QuestReward { + private static final String[] TEXT = CardCrawlGame.languagePack.getUIString(makeID("QuestReward")).TEXT; private static final Map rewardLoaders = new HashMap<>(); protected final Hitbox hb; @@ -45,9 +48,14 @@ public abstract class QuestReward { addRewardSaver(new RewardLoader(RandomRelicReward.class, (save) -> new RandomRelicReward(RelicLibrary.getRelic(save.param).makeCopy()))); addRewardSaver(new RewardLoader(PotionReward.class, (save) -> new PotionReward(PotionHelper.getPotion(save.param)))); addRewardSaver(new RewardLoader(CardReward.class, CardReward::fromSave)); + addRewardSaver(new RewardLoader(CardChoiceReward.class, CardChoiceReward::fromSave)); addRewardSaver(new RewardLoader(MaxHPReward.class, (save) -> new MaxHPReward(Integer.parseInt(save.param)))); } + public enum QuestRewardType { + OTHER, GOLD, RELIC, RANDOM_RELIC, POTION, CARD, CARD_CHOICE, MAX_HP + } + private static void addRewardSaver(RewardLoader loader) { rewardLoaders.put(loader.key, loader); } @@ -342,6 +350,95 @@ public AbstractCard getCard() { } } + public static class CardChoiceReward extends QuestReward { + + private static final TextureRegion IMG = TexLoader.getTextureAsAtlasRegion(makeUIPath("card_reward.png")); + private ArrayList rewardCards; + public boolean isColorless = false; + + public CardChoiceReward() { + super(TEXT[7]); + this.rewardCards = new ArrayList<>(); + } + + public CardChoiceReward(boolean isColorless) { + super(TEXT[9]); + this.isColorless = true; + this.rewardCards = new ArrayList<>(); + } + + public CardChoiceReward(ArrayList rewardCards) { + super(String.format(TEXT[8], String.join(", ", rewardCards.stream().map(c -> c.name).collect(Collectors.toList())))); + this.rewardCards = rewardCards; + } + + @Override + public TextureRegion icon() { + return IMG; + } + + @Override + protected String saveParam() { + return String.join(";", rewardCards.stream().map(c-> c.cardID).collect(Collectors.toList())); + } + + @Override + public QuestRewardSave getSave() { + ArrayList cardSaves = new ArrayList<>(); + for (AbstractCard c : rewardCards) { + cardSaves.add(new CardSave(c.cardID, c.timesUpgraded, c.misc)); + } + return new QuestRewardSave(getClass().getSimpleName(), saveParam(), cardSaves); + } + + public static CardChoiceReward fromSave(QuestRewardSave save) { + ArrayList cardSaves = save.cards; + ArrayList loadedCards = new ArrayList<>(); + for (CardSave s: cardSaves) { + AbstractCard loaded = CardLibrary.getCopy(s.id, s.upgrades, s.misc); + loadedCards.add(loaded); + } + return new CardChoiceReward(loadedCards); + } + + + @Override + public void obtainRewardItem() { + RewardItem reward = getRewardItem(); + AbstractDungeon.combatRewardScreen.rewards.add(0, reward); + AbstractDungeon.combatRewardScreen.positionRewards(); + } + + private RewardItem getRewardItem() { + RewardItem ret; + if (this.isColorless) { + ret = new RewardItem(CardColor.COLORLESS); + } else { + ret = new RewardItem(); + } + if (rewardCards != null && !rewardCards.isEmpty()) { + ret.cards = rewardCards; + for (AbstractCard c : ret.cards) { + for (AbstractRelic r : Wiz.p().relics) + r.onPreviewObtainCard(c); + } + } + return ret; + } + + @Override + public void obtainInstant() { + RewardItem reward = getRewardItem(); + AbstractDungeon.previousScreen = AbstractDungeon.screen; + AbstractDungeon.cardRewardScreen.open(reward.cards, reward, "TEXT[TODO]"); + } + + public ArrayList getCards() { + return rewardCards; + } + + } + public static class MaxHPReward extends QuestReward { private static final TextureRegion img = new TextureRegion(ImageMaster.TP_HP, 8, 0, 48, 48); private final int amount; @@ -428,19 +525,29 @@ public static class QuestRewardSave { public String type; public String param; public CardSave card; + public ArrayList cards; public QuestRewardSave() { } public QuestRewardSave(String type, String param) { - this(type, param, null); + this(type, param, null, null); } public QuestRewardSave(String type, String param, CardSave card) { + this(type, param, card, null); + } + + public QuestRewardSave(String type, String param, ArrayList cards) { + this(type, param, null, cards); + } + + public QuestRewardSave(String type, String param, CardSave card, ArrayList cards) { this.type = type; this.param = param; this.card = card; + this.cards = cards; } } diff --git a/src/main/java/spireQuests/quests/coda/MonsterHunterQuest.java b/src/main/java/spireQuests/quests/coda/MonsterHunterQuest.java new file mode 100644 index 00000000..67d12f16 --- /dev/null +++ b/src/main/java/spireQuests/quests/coda/MonsterHunterQuest.java @@ -0,0 +1,113 @@ +package spireQuests.quests.coda; + +import java.util.ArrayList; + +import com.megacrit.cardcrawl.cards.AbstractCard; +import com.megacrit.cardcrawl.cards.colorless.HandOfGreed; +import com.megacrit.cardcrawl.cards.colorless.Mayhem; +import com.megacrit.cardcrawl.cards.colorless.Metamorphosis; +import com.megacrit.cardcrawl.core.CardCrawlGame; +import com.megacrit.cardcrawl.dungeons.AbstractDungeon; +import com.megacrit.cardcrawl.helpers.CardLibrary; +import com.megacrit.cardcrawl.rooms.MonsterRoomElite; + +import spireQuests.patches.QuestTriggers; +import spireQuests.questStats.StatRewardBox; +import spireQuests.quests.AbstractQuest; +import spireQuests.quests.QuestReward; +import spireQuests.util.CompatUtil; +import spireQuests.util.NodeUtil; + +public class MonsterHunterQuest extends AbstractQuest{ + + private ArrayList cardRewards = new ArrayList<>(); + + + public MonsterHunterQuest() { + super(QuestType.SHORT, QuestDifficulty.HARD); + + new TriggerTracker<>(QuestTriggers.COMBAT_END, 3) + .triggerCondition((x) -> AbstractDungeon.getCurrRoom().eliteTrigger) + .setFailureTrigger(QuestTriggers.ACT_CHANGE) + .add(this); + + if (CardCrawlGame.isInARun()) { + if (CompatUtil.pmLoaded()) { + cardRewards = getPMCardRewards(); + } else { + cardRewards = getCardRewards(); + } + } + + addReward(new QuestReward.CardChoiceReward(cardRewards)); + } + + @Override + public void onComplete() { + super.onComplete(); + this.name = questStrings.EXTRA_TEXT[0]; + } + + private ArrayList getPMCardRewards() { + switch (AbstractDungeon.id) { + case "Exordium": + return getPMAct1Cards(); + case "TheCity": + return getPMAct2Cards(); + case "TheBeyond": + return getPMAct3Cards(); + default: + return getCardRewards(); + } + } + + + private ArrayList getPMAct1Cards() { + ArrayList ret = new ArrayList<>(); + ret.add(CardLibrary.getCopy("anniv5:SkullClub")); + ret.add(CardLibrary.getCopy("anniv5:CoreBlaster")); + ret.add(CardLibrary.getCopy("anniv5:ShellPauldrons")); + return ret; + } + private ArrayList getPMAct2Cards() { + ArrayList ret = new ArrayList<>(); + ret.add(CardLibrary.getCopy("anniv5:StabManual")); + ret.add(CardLibrary.getCopy("anniv5:SlaverWhip")); + ret.add(CardLibrary.getCopy("anniv5:GremlinLance")); + return ret; + } + private ArrayList getPMAct3Cards() { + ArrayList ret = new ArrayList<>(); + ret.add(CardLibrary.getCopy("anniv5:EphemeralShroud")); + ret.add(CardLibrary.getCopy("anniv5:StoneHelm")); + ret.add(CardLibrary.getCopy("anniv5:SerpentineDagger")); + return ret; + } + + private ArrayList getCardRewards() { + ArrayList ret = new ArrayList<>(); + ret.add(CardLibrary.getCopy(HandOfGreed.ID)); + ret.add(CardLibrary.getCopy(Metamorphosis.ID)); + ret.add(CardLibrary.getCopy(Mayhem.ID)); + return ret; + } + + @Override + public ArrayList getStatRewardBoxes() { + ArrayList ret = new ArrayList<>(); + if (CompatUtil.pmLoaded()) { + ret.add(new StatRewardBox(new QuestReward.CardChoiceReward(getPMAct1Cards()))); + ret.add(new StatRewardBox(new QuestReward.CardChoiceReward(getPMAct2Cards()))); + ret.add(new StatRewardBox(new QuestReward.CardChoiceReward(getPMAct3Cards()))); + } else { + ret.add(new StatRewardBox(new QuestReward.CardChoiceReward(getCardRewards()))); + } + return ret; + } + + @Override + public boolean canSpawn() { + return NodeUtil.canPathToNodes(node -> node.room instanceof MonsterRoomElite, 3); + } + +} diff --git a/src/main/java/spireQuests/util/NodeUtil.java b/src/main/java/spireQuests/util/NodeUtil.java index 8abeda7a..8a23932e 100644 --- a/src/main/java/spireQuests/util/NodeUtil.java +++ b/src/main/java/spireQuests/util/NodeUtil.java @@ -11,20 +11,26 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; - public class NodeUtil { + public static boolean canPathToElite() { return canPathToNode(node -> node.room instanceof MonsterRoomElite); } public static boolean canPathToNode(Function condition) { + return canPathToNodes(condition, 1); + } + + public static boolean canPathToNodes(Function condition, int count) { MapRoomNode start = AbstractDungeon.getCurrMapNode(); List processing = new ArrayList<>(); Set seen = new HashSet<>(); + + int found = 0; + if (start.y == -1) { processing.addAll(AbstractDungeon.map.get(0)); - } - else { + } else { processing.add(start); } @@ -34,7 +40,10 @@ public static boolean canPathToNode(Function condition) { continue; } if (condition.apply(current)) { - return true; + found++; + if (found >= count) { + return true; + } } processing.addAll(current.getEdges().stream().map(e -> getNode(e.dstX, e.dstY)).filter(n -> n != null && !seen.contains(n)).collect(Collectors.toList())); seen.add(current); diff --git a/src/main/resources/anniv8Resources/images/ui/stats/card_choice.png b/src/main/resources/anniv8Resources/images/ui/stats/card_choice.png new file mode 100644 index 00000000..df028000 Binary files /dev/null and b/src/main/resources/anniv8Resources/images/ui/stats/card_choice.png differ diff --git a/src/main/resources/anniv8Resources/localization/eng/UIstrings.json b/src/main/resources/anniv8Resources/localization/eng/UIstrings.json index 1efb94de..03f4cb15 100644 --- a/src/main/resources/anniv8Resources/localization/eng/UIstrings.json +++ b/src/main/resources/anniv8Resources/localization/eng/UIstrings.json @@ -44,7 +44,10 @@ "a random Uncommon relic", "a random Rare relic", "a random relic", - "Gain #b%d Max HP." + "Gain #b%d Max HP.", + "Choose a card to obtain.", + "Choose one of the following cards: %s", + "Choose a Colorless Card to obtain" ] }, "${ModID}:SingleCardReward": { @@ -108,7 +111,9 @@ "Other Reward", "Max HP Reward", "Random Quest Reward", - "This quest has a random reward each run!" + "This quest has a random reward each run!", + "Card Reward", + "Colorless Card Reward" ] }, "${ModID}:StatisticsFTUE": { diff --git a/src/main/resources/anniv8Resources/localization/eng/coda/Queststrings.json b/src/main/resources/anniv8Resources/localization/eng/coda/Queststrings.json index 3319d97e..2ab9fc42 100644 --- a/src/main/resources/anniv8Resources/localization/eng/coda/Queststrings.json +++ b/src/main/resources/anniv8Resources/localization/eng/coda/Queststrings.json @@ -83,5 +83,16 @@ "Skills added", "Powers added" ] + }, + "${ModID}:MonsterHunterQuest": { + "TITLE": "Monster Hunter", + "DESCRIPTION": "Defeat 3 elites this act.", + "AUTHOR": "coda", + "TRACKER_TEXT": [ + "Elites remaining this act" + ], + "EXTRA_TEXT": [ + "Monsters Hunted" + ] } } \ No newline at end of file