diff --git a/mod/src/main/java/basemod/BaseMod.java b/mod/src/main/java/basemod/BaseMod.java index b418263c1..aab581c96 100644 --- a/mod/src/main/java/basemod/BaseMod.java +++ b/mod/src/main/java/basemod/BaseMod.java @@ -120,6 +120,8 @@ public class BaseMod { private static ArrayList postDungeonInitializeSubscribers; private static ArrayList postEnergyRechargeSubscribers; private static ArrayList postInitializeSubscribers; + private static ArrayList postGridInitializeSubscribers; + private static ArrayList postShopInitializeSubscribers; private static ArrayList preMonsterTurnSubscribers; private static ArrayList renderSubscribers; private static ArrayList preRenderSubscribers; @@ -461,6 +463,8 @@ private static void initializeSubscriptions() { postDungeonInitializeSubscribers = new ArrayList<>(); postEnergyRechargeSubscribers = new ArrayList<>(); postInitializeSubscribers = new ArrayList<>(); + postGridInitializeSubscribers = new ArrayList<>(); + postShopInitializeSubscribers = new ArrayList<>(); preMonsterTurnSubscribers = new ArrayList<>(); renderSubscribers = new ArrayList<>(); preRenderSubscribers = new ArrayList<>(); @@ -2308,6 +2312,26 @@ public static void publishPostInitialize() { unsubscribeLaterHelper(PostInitializeSubscriber.class); } + // publishPostGridInitialize - + public static void publishPostGridInitialize() { + logger.info("publishPostGridInitialize"); + + for (PostGridInitializeSubscriber sub : postGridInitializeSubscribers) { + sub.receivePostGridInitialize(); + } + unsubscribeLaterHelper(PostGridInitializeSubscriber.class); + } + + // publishPostShopInitialize - + public static void publishPostShopInitialize() { + logger.info("publishPostShopInitialize"); + + for (PostShopInitializeSubscriber sub : postShopInitializeSubscribers) { + sub.receivePostShopInitialize(); + } + unsubscribeLaterHelper(PostShopInitializeSubscriber.class); + } + // publishPreMonsterTurn - false skips monster turn public static boolean publishPreMonsterTurn(AbstractMonster m) { logger.info("publishPreMonsterTurn"); @@ -2874,6 +2898,7 @@ public static void subscribe(ISubscriber sub) { subscribeIfInstance(postDungeonInitializeSubscribers, sub, PostDungeonInitializeSubscriber.class); subscribeIfInstance(postEnergyRechargeSubscribers, sub, PostEnergyRechargeSubscriber.class); subscribeIfInstance(postInitializeSubscribers, sub, PostInitializeSubscriber.class); + subscribeIfInstance(postShopInitializeSubscribers, sub, PostShopInitializeSubscriber.class); subscribeIfInstance(preMonsterTurnSubscribers, sub, PreMonsterTurnSubscriber.class); subscribeIfInstance(renderSubscribers, sub, RenderSubscriber.class); subscribeIfInstance(preRenderSubscribers, sub, PreRenderSubscriber.class); @@ -2937,6 +2962,8 @@ public static void subscribe(ISubscriber sub, Class addit postEnergyRechargeSubscribers.add((PostEnergyRechargeSubscriber) sub); } else if (additionClass.equals(PostInitializeSubscriber.class)) { postInitializeSubscribers.add((PostInitializeSubscriber) sub); + } else if (additionClass.equals(PostShopInitializeSubscriber.class)) { + postShopInitializeSubscribers.add((PostShopInitializeSubscriber) sub); } else if (additionClass.equals(PreMonsterTurnSubscriber.class)) { preMonsterTurnSubscribers.add((PreMonsterTurnSubscriber) sub); } else if (additionClass.equals(RenderSubscriber.class)) { @@ -3033,6 +3060,7 @@ public static void unsubscribe(ISubscriber sub) { unsubscribeIfInstance(postDungeonInitializeSubscribers, sub, PostDungeonInitializeSubscriber.class); unsubscribeIfInstance(postEnergyRechargeSubscribers, sub, PostEnergyRechargeSubscriber.class); unsubscribeIfInstance(postInitializeSubscribers, sub, PostInitializeSubscriber.class); + unsubscribeIfInstance(postShopInitializeSubscribers, sub, PostShopInitializeSubscriber.class); unsubscribeIfInstance(preMonsterTurnSubscribers, sub, PreMonsterTurnSubscriber.class); unsubscribeIfInstance(renderSubscribers, sub, RenderSubscriber.class); unsubscribeIfInstance(preRenderSubscribers, sub, PreRenderSubscriber.class); @@ -3095,6 +3123,8 @@ public static void unsubscribe(ISubscriber sub, Class rem postEnergyRechargeSubscribers.remove(sub); } else if (removalClass.equals(PostInitializeSubscriber.class)) { postInitializeSubscribers.remove(sub); + } else if (removalClass.equals(PostInitializeSubscriber.class)) { + postShopInitializeSubscribers.remove(sub); } else if (removalClass.equals(PreMonsterTurnSubscriber.class)) { preMonsterTurnSubscribers.remove(sub); } else if (removalClass.equals(RenderSubscriber.class)) { diff --git a/mod/src/main/java/basemod/ShopGrid.java b/mod/src/main/java/basemod/ShopGrid.java new file mode 100644 index 000000000..603cba09d --- /dev/null +++ b/mod/src/main/java/basemod/ShopGrid.java @@ -0,0 +1,545 @@ +package basemod; + +import java.util.ArrayList; +import java.util.LinkedList; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.megacrit.cardcrawl.core.Settings; +import com.megacrit.cardcrawl.dungeons.AbstractDungeon; +import com.megacrit.cardcrawl.helpers.FontHelper; +import com.megacrit.cardcrawl.helpers.Hitbox; +import com.megacrit.cardcrawl.helpers.ImageMaster; +import com.megacrit.cardcrawl.helpers.controller.CInputActionSet; +import com.megacrit.cardcrawl.helpers.input.InputHelper; +import com.megacrit.cardcrawl.shop.ShopScreen; +import com.megacrit.cardcrawl.shop.StorePotion; +import com.megacrit.cardcrawl.shop.StoreRelic; + +import basemod.abstracts.CustomShopItem; + +public class ShopGrid { + + public static final String DEFAULT_PAGE_ID = "basemod:"; + + private static int pageIdCounter = 0; + + // temporary dimensions to help support SpicyShops + public static int[] initialPageDimensions = { 3, 3, 4 }; + + public static int defaultPageRows = 2; + + public static int defaultPageCols = 3; + + public static float rightEdge = Settings.WIDTH * 0.74F; + + public static float leftEdge = Settings.WIDTH * 0.44F; + + public static float topEdge = Settings.HEIGHT * 0.48F; + + public static float bottomEdge = Settings.HEIGHT * 0.035F; + + // this list holds pages specifically added by modders + public static LinkedList customPages = new LinkedList<>(); + + // this list has the pages for the remaining items that were added and the initial shop items + public static LinkedList pages = new LinkedList<>(); + + // used for when the shop grid is empty, will always have id of basemod:0 and cannot be removed + public static final Page EMPTY_SHOP_PAGE = new Page(); + + private static Page currentPage; + + public static NavButton leftArrow; + + public static NavButton rightArrow; + + private static float pageY; + + public static void initialize() { + pages.clear(); + customPages.clear(); + pageIdCounter = 1; + // currentPage = addDefaultPage(); + Page initialPage = new Page(initialPageDimensions); + pages.addLast(initialPage); + currentPage = initialPage; + rightArrow = new NavButton(true); + leftArrow = new NavButton(false); + } + + public static Page getCurrentPage() { + if (currentPage == null || (pages.isEmpty() && customPages.isEmpty())) + return EMPTY_SHOP_PAGE; + return currentPage; + } + + public static void setCurrentPage(Page page) { + if (page == null) + currentPage = EMPTY_SHOP_PAGE; + else + currentPage = page; + } + + public static Page addEmptyPage() { + Page page = new Page(); + pages.addLast(page); + return page; + } + + public static Page addDefaultPage() { + int[] rowSizes = new int[defaultPageRows]; + for (int i = 0; i < rowSizes.length; i++) + rowSizes[i] = defaultPageCols; + Page page = new Page(rowSizes); + pages.addLast(page); + return page; + } + + public static Page addPage(int ... rowSizes) { + Page page = new Page(rowSizes); + pages.addLast(page); + return page; + } + + public static Page addCustomPage(String modId, int ... rowSizes) { + Page page = new Page(modId, rowSizes); + customPages.addLast(page); + return page; + } + + public static boolean removePage(Page page) { + if (page == EMPTY_SHOP_PAGE) + return false; + if (page == currentPage) { + currentPage = page.getNextPage(); + if (currentPage == page) + currentPage = EMPTY_SHOP_PAGE; + } + if (pages.contains(page)) + return pages.remove(page); + else if (customPages.contains(page)) + return customPages.remove(page); + return false; + } + + public static boolean removePage(String pageId) { + for (Page page : pages) + if (page.id.equals(pageId)) + return removePage(page); + for (Page page : customPages) + if (page.id.equals(pageId)) + return removePage(page); + return false; + } + + public static boolean tryAddItem(CustomShopItem item) { + for (Page page : pages) + if (page.tryAddItem(item)) + return true; + + Page page = addDefaultPage(); + return page.tryAddItem(item); + } + + public static boolean tryAddItemToCustomPage(String id, CustomShopItem item) { + for (Page customPage : customPages) + if (customPage.id.equals(id)) + return customPage.tryAddItem(item); + return false; + } + + public static float gridWidth() { + return rightEdge - leftEdge; + } + + public static float gridHeight() { + return topEdge - bottomEdge; + } + + public static void removeEmptyPages() { + Page previousPage = null; + if (currentPage != null) + previousPage = currentPage.getPreviousPage(); + pages.removeIf((page) -> page.isEmpty()); + customPages.removeIf((page) -> page.isEmpty()); + if (!pages.contains(currentPage) && !customPages.contains(currentPage)) + currentPage = previousPage; + else if (currentPage == previousPage && pages.size() == 0 && customPages.size() == 0) + currentPage = EMPTY_SHOP_PAGE; + } + + public static void removeEmptyRows() { + currentPage.rows.removeIf((row) -> row.isEmpty()); + } + + public static void hide() { + if (currentPage != null && currentPage != EMPTY_SHOP_PAGE) + currentPage.hide(); + leftArrow.hide(); + rightArrow.hide(); + } + + public static void update(float rugY) { + if (currentPage != null) + currentPage.update(rugY); + } + + public static void render(SpriteBatch sb, float rugY) { + if (Settings.isDebug || Settings.isInfo) { + sb.setColor(Color.RED); + sb.draw(ImageMaster.DEBUG_HITBOX_IMG, leftEdge, rugY + bottomEdge, gridWidth(), gridHeight()); + } + if (currentPage != null) + currentPage.render(sb); + } + + public static boolean isEmpty() { + if (getCurrentPage() == EMPTY_SHOP_PAGE) + return true; + for (Page page : pages) + if (!page.isEmpty()) + return false; + for (Page page : customPages) + if (!page.isEmpty()) + return false; + return true; + } + + public static class Page { + + public String id; + + public ArrayList rows = new ArrayList(); + + public Page(int ... rowSizes) { + for (int i = 0; i < rowSizes.length; i++) { + rows.add(new Row(this, i, rowSizes[i])); + } + this.id = DEFAULT_PAGE_ID + pageIdCounter++; + } + + public Page(String id, int ... rowSizes) { + for (int i = 0; i < rowSizes.length; i++) { + rows.add(new Row(this, i, rowSizes[i])); + } + this.id = id; + } + + public void hide() { + for (Row row : rows) + row.hide(); + } + + public void update(float rugY) { + for (Row row : rows) + row.update(rugY); + leftArrow.update(rugY); + rightArrow.update(rugY); + pageY = rugY + 510.0F * Settings.yScale; + } + + public void render(SpriteBatch sb) { + for (Row row : rows) + if (!row.isEmpty()) + row.render(sb); + leftArrow.render(sb); + rightArrow.render(sb); + if (pages.size() + customPages.size() > 1) { + int currPageIdx = 1; + if (pages.contains(currentPage)) + currPageIdx += pages.indexOf(currentPage); + else if (customPages.contains(currentPage)) + currPageIdx += pages.size() + customPages.indexOf(currentPage); + + FontHelper.renderFontCentered( + sb, + FontHelper.buttonLabelFont, + currPageIdx + "/" + (pages.size() + customPages.size()), + 1130F * Settings.xScale, + pageY, + Color.WHITE + ); + } + } + + public boolean tryAddItem(CustomShopItem item) { + for (Row row : rows) { + if (!row.isFull()) { + row.tryAddItem(item); + return true; + } + } + return false; + } + + public Row addDefaultRow() { + return addRow(defaultPageCols); + } + + public Row addRow(int size) { + Row row = new Row(this, rows.size(), size); + rows.add(row); + return row; + } + + public Row insertRow(int idx, int size) { + Row row = new Row(this, idx, size); + rows.add(idx, row); + for (int i = idx + 1; i < rows.size(); i++) + rows.get(i).rowNumber++; + return row; + } + + public boolean isFull() { + for (Row row : rows) + if (!row.isFull()) + return false; + return true; + } + + public boolean isEmpty() { + for (Row row : rows) + if (!row.isEmpty()) + return false; + return true; + } + + public Page getPreviousPage() { + if (pages.contains(this)) { + if (pages.getFirst() == this) { + if (customPages.isEmpty()) { + return pages.getLast(); + } + return customPages.getLast(); + } + return pages.get(pages.indexOf(this) - 1); + } else if (customPages.contains(this)) { + if (customPages.getFirst() == this) { + if (pages.isEmpty()) { + return customPages.getLast(); + } + return pages.getLast(); + } + return customPages.get(pages.indexOf(this) - 1); + } + return currentPage; + } + + public Page getNextPage() { + if (pages.contains(this)) { + if (pages.getLast() == this) { + if (customPages.isEmpty()) { + return pages.getFirst(); + } + return customPages.getFirst(); + } + return pages.get(pages.indexOf(this) + 1); + } else if (customPages.contains(this)) { + if (customPages.getLast() == this) { + if (pages.isEmpty()) { + return customPages.getFirst(); + } + return pages.getFirst(); + } + return customPages.get(pages.indexOf(this) + 1); + } + return currentPage; + } + + public boolean contains(CustomShopItem item) { + for (Row row : rows) + if (row.items.contains(item)) + return true; + return false; + } + + public boolean contains (StoreRelic relic) { + for (Row row : rows) + for (CustomShopItem item : row.items) + if (relic == item.storeRelic) + return true; + return false; + } + + public boolean contains(StorePotion potion) { + for (Row row : rows) + for (CustomShopItem item : row.items) + if (potion == item.storePotion) + return true; + return false; + } + + public CustomShopItem getItem(StorePotion potion) { + for (Row row : rows) + for (CustomShopItem item : row.items) + if (potion == item.storePotion) + return item; + return null; + } + + public CustomShopItem getItem(StoreRelic relic) { + for (Row row : rows) + for (CustomShopItem item : row.items) + if (relic == item.storeRelic) + return item; + return null; + } + } + + public static class Row { + + public Page owner; + + public ArrayList items; + + public int rowNumber; + + public int maxColumns; + + public Row(Page owner, int rowNumber, int maxColumns) { + this.owner = owner; + this.items = new ArrayList<>(); + this.maxColumns = maxColumns; + this.rowNumber = rowNumber; + } + + public float getX(int col) { + return leftEdge + (col + 1F) / (maxColumns + 1F) * gridWidth(); + } + + public float getY(int row, float rugY) { + float y = rugY; + if (this.owner.rows.size() > 2) { + y += (topEdge - ((row + 1F) / (owner.rows.size() + 1F) * gridHeight())); + } else if (row == 0) { + y += 400F * Settings.xScale; + } else { + y += 200F * Settings.xScale; + } + return y; + } + + @SuppressWarnings("unchecked") + public boolean tryAddItem(CustomShopItem item) { + if (items.size() < maxColumns) { + item.row = rowNumber; + item.col = items.size(); + item.gridRow = this; + items.add(item); + if (item.storeRelic != null) { + if (item.applyDiscounts) { + item.applyDiscounts(item.storeRelic.price); + item.storeRelic.price = item.price; + } + ArrayList relics = (ArrayList)ReflectionHacks.getPrivate(AbstractDungeon.shopScreen, ShopScreen.class, "relics"); + if (!relics.contains(item.storeRelic)) + relics.add(item.storeRelic); + } else if (item.storePotion != null) { + if (item.applyDiscounts) { + item.applyDiscounts(item.storePotion.price); + item.storePotion.price = item.price; + } + ArrayList potions = (ArrayList)ReflectionHacks.getPrivate(AbstractDungeon.shopScreen, ShopScreen.class, "potions"); + if (!potions.contains(item.storePotion)) + potions.add(item.storePotion); + } + return true; + } + return false; + } + + public void update(float rugY) { + for (CustomShopItem item : items) { + if (item.storePotion == null && item.storeRelic == null) { + item.update(rugY); + } + } + } + + public void render(SpriteBatch sb) { + if (AbstractDungeon.shopScreen != null) + for (CustomShopItem item : items) + if (item.storePotion == null && item.storeRelic == null) + item.render(sb); + } + + public void hide() { + for (CustomShopItem item : items) { + item.hide(); + } + } + + public boolean isEmpty() { + return items.isEmpty() || (items.stream().filter((item) -> !item.isPurchased && item.isValid()).count() == 0); + } + + public boolean isFull() { + return items.size() == maxColumns; + } + } + + public static class NavButton { + public static Texture texture = ImageMaster.POPUP_ARROW; + public Hitbox hb; + + private float x, y; + private float scale; + + public boolean forward = true; + + public NavButton(boolean forward) { + this.forward = forward; + this.hb = new Hitbox(64.0F * Settings.scale, 64.0F * Settings.scale); + } + + public void update(float rugY) { + this.x = (forward ? 1205.0F : 1055.0F) * Settings.xScale; + this.y = rugY + 500.0F * Settings.yScale; + this.hb.move(x, y); + this.hb.update(); + if (this.hb.hovered) { + this.scale = Settings.scale / 2F * 1.1F; + if (InputHelper.justClickedLeft) { + hb.clickStarted = true; + } + } else { + this.scale = Settings.scale / 2F; + } + if (this.hb.clicked || (this.hb.hovered && CInputActionSet.select.isJustPressed())) { + int curIdx = -1; + if (pages.contains(currentPage)) + curIdx = pages.indexOf(currentPage); + else if (customPages.contains(currentPage)) + curIdx = pages.size() + customPages.indexOf(currentPage); + if (forward && curIdx < (pages.size() + customPages.size() - 1)) + currentPage = currentPage.getNextPage(); + else if (!forward && curIdx > 0) + currentPage = currentPage.getPreviousPage(); + this.hb.clicked = false; + } + } + + public void render(SpriteBatch sb) { + int curIdx = -1; + if (pages.contains(currentPage)) + curIdx = pages.indexOf(currentPage); + else if (customPages.contains(currentPage)) + curIdx = pages.size() + customPages.indexOf(currentPage); + if (((!forward && curIdx > 0) || (forward && (curIdx < pages.size() + customPages.size() - 1)))) { + sb.setColor(Color.WHITE); + TextureRegion region = new TextureRegion(texture); + if (forward) + region.flip(true, false); + sb.draw(region, x - 96.0F * Settings.scale, y - 86.0F * Settings.scale, 128.0F, 128.0F, 128.0F, 128.0F, scale, scale, 0.0F); + hb.render(sb); + } + } + + public void hide() { + this.hb.move(this.hb.x, Settings.HEIGHT + 200.0F * Settings.scale); + } + } +} diff --git a/mod/src/main/java/basemod/abstracts/CustomShopItem.java b/mod/src/main/java/basemod/abstracts/CustomShopItem.java new file mode 100644 index 000000000..b701d7bc9 --- /dev/null +++ b/mod/src/main/java/basemod/abstracts/CustomShopItem.java @@ -0,0 +1,211 @@ +package basemod.abstracts; + +import basemod.BaseMod; +import basemod.ReflectionHacks; +import basemod.ShopGrid; +import basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.megacrit.cardcrawl.core.CardCrawlGame; +import com.megacrit.cardcrawl.core.Settings; +import com.megacrit.cardcrawl.dungeons.AbstractDungeon; +import com.megacrit.cardcrawl.helpers.FontHelper; +import com.megacrit.cardcrawl.helpers.Hitbox; +import com.megacrit.cardcrawl.helpers.ImageMaster; +import com.megacrit.cardcrawl.helpers.MathHelper; +import com.megacrit.cardcrawl.helpers.TipHelper; +import com.megacrit.cardcrawl.helpers.controller.CInputActionSet; +import com.megacrit.cardcrawl.helpers.input.InputHelper; +import com.megacrit.cardcrawl.potions.AbstractPotion; +import com.megacrit.cardcrawl.relics.AbstractRelic; +import com.megacrit.cardcrawl.relics.Courier; +import com.megacrit.cardcrawl.relics.MembershipCard; +import com.megacrit.cardcrawl.shop.ShopScreen; +import com.megacrit.cardcrawl.shop.StorePotion; +import com.megacrit.cardcrawl.shop.StoreRelic; + +public class CustomShopItem { + + public String id; + + public ShopScreen screenRef = null; + public ShopGrid.Row gridRow; + public StoreRelic storeRelic = null; + public StorePotion storePotion = null; + + public String tipTitle = null; + public String tipBody = null; + + public Texture img = null; + public Hitbox hb; + public float x, y; + public float scale; + + public int price = 0; + public int row = 0; + public int col = 0; + + public boolean isPurchased = false; + + public boolean applyDiscounts = true; + + private static final float GOLD_IMG_WIDTH = ReflectionHacks.getPrivateStatic(StoreRelic.class, "GOLD_IMG_WIDTH"); + private static final float GOLD_OFFSET_X = ReflectionHacks.getPrivateStatic(StoreRelic.class, "RELIC_GOLD_OFFSET_X"); + private static final float GOLD_OFFSET_Y = ReflectionHacks.getPrivateStatic(StoreRelic.class, "RELIC_GOLD_OFFSET_Y"); + private static final float PRICE_OFFSET_X = ReflectionHacks.getPrivateStatic(StoreRelic.class, "RELIC_PRICE_OFFSET_X"); + private static final float PRICE_OFFSET_Y = ReflectionHacks.getPrivateStatic(StoreRelic.class, "RELIC_PRICE_OFFSET_Y"); + + public CustomShopItem(AbstractRelic relic) { + this(new StoreRelic(relic, 0, AbstractDungeon.shopScreen)); + } + + public CustomShopItem(AbstractPotion potion) { + this(new StorePotion(potion, 0, AbstractDungeon.shopScreen)); + } + + public CustomShopItem(StoreRelic storeRelic) { + if (storeRelic.relic != null) { + ShopGridPatch.StoreRelicPatches.compatibleWithGrid.set(storeRelic, true); + this.storeRelic = storeRelic; + this.price = storeRelic.price; + } else { + BaseMod.logger.error("StoreRelic cannot have a null relic"); + } + } + + public CustomShopItem(StorePotion storePotion) { + if (storePotion.potion != null) { + ShopGridPatch.StorePotionPatches.compatibleWithGrid.set(storePotion, true); + this.storePotion = storePotion; + this.price = storePotion.price; + } else { + BaseMod.logger.error("StorePotion cannot have a null potion"); + } + } + + public CustomShopItem(String modId, Texture img, int price, String tipTitle, String tipBody) { + this(modId, AbstractDungeon.shopScreen, img, price, tipTitle, tipBody); + } + + public CustomShopItem(String id, ShopScreen screenRef, Texture img, int price, String tipTitle, String tipBody) { + this.id = id; + this.screenRef = screenRef; + this.tipTitle = tipTitle; + this.tipBody = tipBody; + this.img = img; + this.hb = new Hitbox(72F * Settings.scale, 72F * Settings.scale); + applyDiscounts(price); + } + + public void applyDiscounts(int price) { + float mult = 1F; + for (AbstractRelic relic : AbstractDungeon.player.relics) { + if (relic.relicId.equals(Courier.ID)) + mult *= 0.8F; + else if (relic.relicId.equals(MembershipCard.ID)) + mult *= 0.5F; + } + this.price = (int)(mult * price); + } + + public void update(float rugY) { + if (!this.isPurchased) { + this.x = gridRow.getX(col); + this.y = gridRow.getY(row, rugY); + this.hb.move(this.x, this.y); + this.hb.update(); + if (this.hb.hovered) { + this.screenRef.moveHand(this.x - 190.0F * Settings.scale, this.y - 70.0F * Settings.scale); + if (InputHelper.justClickedLeft) + this.hb.clickStarted = true; + this.scale = Settings.scale * 1.25F; + } else { + this.scale = MathHelper.scaleLerpSnap(this.scale, Settings.scale); + } + if (this.hb.clicked || (this.hb.hovered && CInputActionSet.select.isJustPressed())) { + attemptPurchase(); + this.hb.clicked = false; + } + ShopGrid.removeEmptyPages(); + } + } + + public void render(SpriteBatch sb) { + if (!this.isPurchased) { + if (storeRelic == null && storePotion == null) { + sb.setColor(Color.WHITE); + hb.render(sb); + // assumes the size of a relic image + sb.draw(img, x - 64.0F, y - 64.0F, 64.0F, 64.0F, 128.0F, 128.0F, scale, scale, 0.0F, 0, 0, 128, 128, false, false); + if (gridRow != null && gridRow.maxColumns <= 5 && gridRow.owner.rows.size() <= 4) { + float goldY = y; + if (gridRow.owner.rows.size() > 3) + goldY += 26F * Settings.scale; + float textX = x; + if (gridRow.maxColumns > 4) + textX -= 10 * Settings.scale; + float textY = y; + if (gridRow.owner.rows.size() > 3) + textY += 22F * Settings.scale; + + sb.draw(ImageMaster.UI_GOLD, x + GOLD_OFFSET_X, goldY + GOLD_OFFSET_Y, GOLD_IMG_WIDTH, GOLD_IMG_WIDTH); + Color color = Color.WHITE; + if (this.price > AbstractDungeon.player.gold) + color = Color.SALMON; + FontHelper.renderFontLeftTopAligned(sb, FontHelper.tipHeaderFont, Integer.toString(this.price), textX + PRICE_OFFSET_X, textY + PRICE_OFFSET_Y, color); + if (this.hb.hovered && tipTitle != null && tipBody != null) + TipHelper.renderGenericTip( + InputHelper.mX + 50.0F * Settings.xScale, + InputHelper.mY + 50.0F * Settings.xScale, + tipTitle, + tipBody + ); + } else if (hb.hovered) { + ShopGridPatch.x = x; + ShopGridPatch.y = y; + ShopGridPatch.price = price; + ShopGridPatch.hoveringItem = true; + } + } + } + } + + public void hide() { + if (!this.isPurchased) { + if (storeRelic != null && storeRelic.relic != null) { + this.storeRelic.hide(); + this.y = storeRelic.relic.currentY; + } else if (storePotion != null && storePotion.potion != null) { + this.storePotion.hide(); + this.y = storePotion.potion.posY; + } else { + this.y = Settings.HEIGHT + 200.0F * Settings.scale; + } + } + } + + protected void attemptPurchase() { + if (!this.isPurchased && this.storePotion == null && this.storeRelic == null) { + if (AbstractDungeon.player.gold >= this.price) { + purchase(); + } else { + this.screenRef.playCantBuySfx(); + this.screenRef.createSpeech(ShopScreen.getCantBuyMsg()); + } + } + } + + public void purchase() { + this.isPurchased = true; + AbstractDungeon.player.loseGold(this.price); + CardCrawlGame.sound.play("SHOP_PURCHASE", 0.1F); + } + + public boolean isValid() { + return (storePotion != null && storePotion.potion != null) + || (storeRelic != null && storeRelic.relic != null) + || (screenRef != null && tipTitle != null && tipBody != null && img != null); + } +} diff --git a/mod/src/main/java/basemod/devcommands/ConsoleCommand.java b/mod/src/main/java/basemod/devcommands/ConsoleCommand.java index c43ecec95..26aa79782 100644 --- a/mod/src/main/java/basemod/devcommands/ConsoleCommand.java +++ b/mod/src/main/java/basemod/devcommands/ConsoleCommand.java @@ -22,6 +22,7 @@ import basemod.devcommands.potions.Potions; import basemod.devcommands.power.Power; import basemod.devcommands.relic.Relic; +import basemod.devcommands.shop.Shop; import basemod.devcommands.statics.SetStatic; import basemod.devcommands.unlock.Unlock; import basemod.DevConsole; @@ -151,6 +152,7 @@ public static void initialize() { addCommand("setstatic", SetStatic.class); addCommand("evalstatic", EvalStatic.class); addCommand("evalcode", EvalCode.class); + addCommand("shop", Shop.class); ActCommand.initialize(); } diff --git a/mod/src/main/java/basemod/devcommands/shop/Shop.java b/mod/src/main/java/basemod/devcommands/shop/Shop.java new file mode 100644 index 000000000..0dcd39a19 --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/Shop.java @@ -0,0 +1,32 @@ +package basemod.devcommands.shop; + +import basemod.DevConsole; +import basemod.devcommands.ConsoleCommand; + +public class Shop extends ConsoleCommand { + public Shop() { + followup.put("add", ShopAdd.class); + followup.put("remove", ShopRemove.class); + } + + @Override + protected void execute(String[] tokens, int depth) { + cmdShopHelp(); + } + + @Override + public void errorMsg() { + cmdShopHelp(); + } + + public static void cmdShopHelp() { + DevConsole.couldNotParse(); + DevConsole.log("options are:"); + DevConsole.log("* add page [row size] [row size] ..."); + DevConsole.log("* add potion [id]"); + DevConsole.log("* add relic [id]"); + DevConsole.log("* add row [size]"); + DevConsole.log("* remove item [row] [col]"); + DevConsole.log("* remove page [id]"); + } +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopAdd.java b/mod/src/main/java/basemod/devcommands/shop/ShopAdd.java new file mode 100644 index 000000000..1dc54405d --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopAdd.java @@ -0,0 +1,60 @@ +package basemod.devcommands.shop; + +import java.util.ArrayList; +import java.util.List; + +import com.megacrit.cardcrawl.characters.AbstractPlayer; +import com.megacrit.cardcrawl.dungeons.AbstractDungeon; +import com.megacrit.cardcrawl.helpers.PotionHelper; +import com.megacrit.cardcrawl.helpers.RelicLibrary; +import com.megacrit.cardcrawl.potions.AbstractPotion; +import com.megacrit.cardcrawl.relics.AbstractRelic; + +import basemod.abstracts.CustomShopItem; +import basemod.devcommands.ConsoleCommand; + +public class ShopAdd extends ConsoleCommand { + + public ShopAdd() { + followup.put("page", ShopAddPage.class); + followup.put("potion", ShopAddPotion.class); + followup.put("relic", ShopAddRelic.class); + followup.put("row", ShopAddRow.class); + } + + public static ArrayList allPotions() { + ArrayList results = new ArrayList<>(); + List allPotions = PotionHelper.getPotions(AbstractPlayer.PlayerClass.IRONCLAD, true); + for (String key : allPotions) { + results.add(key.replace(' ', '_')); + } + return results; + } + + public static CustomShopItem randomItem() { + if (AbstractDungeon.miscRng.randomBoolean()) + return new CustomShopItem(AbstractDungeon.returnRandomRelic(AbstractDungeon.returnRandomRelicTier()).makeCopy()); + else + return new CustomShopItem(AbstractDungeon.returnRandomPotion()); + } + + public static CustomShopItem itemFromId(String id) { + Object obj = RelicLibrary.getRelic(id); + CustomShopItem item = null; + if (obj != null) + item = new CustomShopItem((AbstractRelic)obj); + else if ((obj = PotionHelper.getPotion(id)) != null) + item = new CustomShopItem((AbstractPotion)obj); + return item; + } + + @Override + public void errorMsg() { + Shop.cmdShopHelp(); + } + + @Override + protected void execute(String[] tokens, int depth) { + Shop.cmdShopHelp(); + } +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopAddPage.java b/mod/src/main/java/basemod/devcommands/shop/ShopAddPage.java new file mode 100644 index 000000000..2186c46c4 --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopAddPage.java @@ -0,0 +1,45 @@ +package basemod.devcommands.shop; + +import java.util.ArrayList; + +import basemod.ShopGrid; +import basemod.devcommands.ConsoleCommand; + +public class ShopAddPage extends ConsoleCommand { + + public ShopAddPage() { + requiresPlayer = true; + minExtraTokens = 0; + maxExtraTokens = 9; + simpleCheck = true; + } + + @Override + public ArrayList extraOptions(String[] tokens, int depth) { + ArrayList opts = new ArrayList(); + for (int i = 1; i < 10; i++) + opts.add(Integer.toString(i)); + return opts; + } + + @Override + protected void execute(String[] tokens, int depth) { + if (tokens.length == 3) { + ShopGrid.Page page = ShopGrid.addDefaultPage(); + while (page.tryAddItem(ShopAdd.randomItem())); + ShopGrid.setCurrentPage(page); + } else { + ShopGrid.Page page = ShopGrid.addEmptyPage(); + try { + for (int i = 3; i < tokens.length; i++) + page.addRow(Integer.parseInt(tokens[i])); + while (page.tryAddItem(ShopAdd.randomItem())); + ShopGrid.setCurrentPage(page); + } catch (NumberFormatException nfe) { + nfe.printStackTrace(); + Shop.cmdShopHelp(); + } + } + } + +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopAddPotion.java b/mod/src/main/java/basemod/devcommands/shop/ShopAddPotion.java new file mode 100644 index 000000000..7f9bcbc3c --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopAddPotion.java @@ -0,0 +1,44 @@ +package basemod.devcommands.shop; + +import java.util.ArrayList; + +import com.megacrit.cardcrawl.dungeons.AbstractDungeon; +import com.megacrit.cardcrawl.helpers.PotionHelper; +import com.megacrit.cardcrawl.potions.AbstractPotion; + +import basemod.DevConsole; +import basemod.ShopGrid; +import basemod.abstracts.CustomShopItem; +import basemod.devcommands.ConsoleCommand; + +public class ShopAddPotion extends ConsoleCommand { + + public ShopAddPotion() { + requiresPlayer = true; + minExtraTokens = 0; + maxExtraTokens = 1; + simpleCheck = true; + } + + @Override + public ArrayList extraOptions(String[] tokens, int depth) { + return ShopAdd.allPotions(); + } + + @Override + public void errorMsg() { + Shop.cmdShopHelp(); + } + + @Override + protected void execute(String[] tokens, int depth) { + AbstractPotion potion; + if (tokens.length < 4) + potion = AbstractDungeon.returnRandomPotion(); + else + potion = PotionHelper.getPotion(tokens[3]); + if (!ShopGrid.tryAddItem(new CustomShopItem(potion))) + DevConsole.log("could not add " + potion.ID + " to shop grid"); + } + +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopAddRelic.java b/mod/src/main/java/basemod/devcommands/shop/ShopAddRelic.java new file mode 100644 index 000000000..df2f9da5c --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopAddRelic.java @@ -0,0 +1,44 @@ +package basemod.devcommands.shop; + +import java.util.ArrayList; + +import com.megacrit.cardcrawl.dungeons.AbstractDungeon; +import com.megacrit.cardcrawl.helpers.RelicLibrary; +import com.megacrit.cardcrawl.relics.AbstractRelic; + +import basemod.DevConsole; +import basemod.ShopGrid; +import basemod.abstracts.CustomShopItem; +import basemod.devcommands.ConsoleCommand; + +public class ShopAddRelic extends ConsoleCommand { + + public ShopAddRelic() { + requiresPlayer = true; + minExtraTokens = 0; + maxExtraTokens = 1; + simpleCheck = true; + } + + @Override + public ArrayList extraOptions(String[] tokens, int depth) { + return ConsoleCommand.getRelicOptions(); + } + + @Override + public void errorMsg() { + Shop.cmdShopHelp(); + } + + @Override + protected void execute(String[] tokens, int depth) { + AbstractRelic relic; + if (tokens.length == 3) + relic = AbstractDungeon.returnRandomRelic(AbstractDungeon.returnRandomRelicTier()).makeCopy(); + else + relic = RelicLibrary.getRelic(tokens[3]).makeCopy(); + if (!ShopGrid.tryAddItem(new CustomShopItem(relic))) + DevConsole.log("could not add " + relic.relicId + " to shop grid"); + } + +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopAddRow.java b/mod/src/main/java/basemod/devcommands/shop/ShopAddRow.java new file mode 100644 index 000000000..6bed382e3 --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopAddRow.java @@ -0,0 +1,45 @@ +package basemod.devcommands.shop; + +import java.util.ArrayList; +import basemod.DevConsole; +import basemod.ShopGrid; +import basemod.devcommands.ConsoleCommand; + +public class ShopAddRow extends ConsoleCommand { + + public ShopAddRow() { + requiresPlayer = true; + minExtraTokens = 1; + maxExtraTokens = 1; + simpleCheck = true; + } + + @Override + public ArrayList extraOptions(String[] tokens, int depth) { + ArrayList opts = new ArrayList(); + for (int i = 0; i < 10; i++) + opts.add(Integer.toString(i)); + return opts; + } + + @Override + public void errorMsg() { + Shop.cmdShopHelp(); + } + + @Override + protected void execute(String[] tokens, int depth) { + if (ShopGrid.getCurrentPage() != ShopGrid.EMPTY_SHOP_PAGE) { + try { + int size = Integer.parseInt(tokens[3]); + ShopGrid.Row row = ShopGrid.getCurrentPage().addRow(size); + while (row.tryAddItem(ShopAdd.randomItem())); + } catch (NumberFormatException nfe) { + nfe.printStackTrace(); + Shop.cmdShopHelp(); + } + } else { + DevConsole.log("no page to add a row to"); + } + } +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopRemove.java b/mod/src/main/java/basemod/devcommands/shop/ShopRemove.java new file mode 100644 index 000000000..9753780e6 --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopRemove.java @@ -0,0 +1,21 @@ +package basemod.devcommands.shop; + +import basemod.devcommands.ConsoleCommand; + +public class ShopRemove extends ConsoleCommand { + + public ShopRemove() { + followup.put("item", ShopRemoveItem.class); + followup.put("page", ShopRemovePage.class); + } + + @Override + public void errorMsg() { + Shop.cmdShopHelp(); + } + + @Override + public void execute(String[] tokens, int depth) { + Shop.cmdShopHelp(); + } +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopRemoveItem.java b/mod/src/main/java/basemod/devcommands/shop/ShopRemoveItem.java new file mode 100644 index 000000000..6d8695590 --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopRemoveItem.java @@ -0,0 +1,70 @@ +package basemod.devcommands.shop; + +import java.util.ArrayList; +import java.util.Iterator; + +import basemod.DevConsole; +import basemod.ShopGrid; +import basemod.abstracts.CustomShopItem; +import basemod.devcommands.ConsoleCommand; + +public class ShopRemoveItem extends ConsoleCommand { + + public ShopRemoveItem() { + requiresPlayer = true; + minExtraTokens = 1; + maxExtraTokens = 2; + simpleCheck = true; + } + + @Override + public ArrayList extraOptions(String[] tokens, int depth) { + ArrayList opts = new ArrayList<>(); + if (tokens.length <= 4) { + if (ShopGrid.getCurrentPage() != ShopGrid.EMPTY_SHOP_PAGE) { + for (int i = 0; i < ShopGrid.getCurrentPage().rows.size(); i++) + opts.add(String.valueOf(i)); + } + } + else { + try { + int row = Integer.parseInt(tokens[3]); + for (int i = 0; i < ShopGrid.getCurrentPage().rows.get(row).maxColumns; i++) + opts.add(String.valueOf(i)); + } catch (NumberFormatException nfe) { + nfe.printStackTrace(); + } + } + return opts; + } + + @Override + public void errorMsg() { + Shop.cmdShopHelp(); + } + + @Override + protected void execute(String[] tokens, int depth) { + try { + int row = Integer.parseInt(tokens[3]); + if (tokens.length == 4) { + Iterator it = ShopGrid.getCurrentPage().rows.get(row).items.iterator(); + while (it.hasNext()) { + it.next(); + it.remove(); + } + } else { + int col = Integer.parseInt(tokens[4]); + ShopGrid.getCurrentPage().rows.get(row).items.remove(col); + } + ShopGrid.removeEmptyPages(); + } catch(NumberFormatException nfe) { + nfe.printStackTrace(); + Shop.cmdShopHelp(); + } catch (IndexOutOfBoundsException iobe) { + iobe.printStackTrace(); + DevConsole.log("row could not be found"); + } + } + +} diff --git a/mod/src/main/java/basemod/devcommands/shop/ShopRemovePage.java b/mod/src/main/java/basemod/devcommands/shop/ShopRemovePage.java new file mode 100644 index 000000000..ccb08454c --- /dev/null +++ b/mod/src/main/java/basemod/devcommands/shop/ShopRemovePage.java @@ -0,0 +1,46 @@ +package basemod.devcommands.shop; + +import java.util.ArrayList; +import java.util.Comparator; + +import basemod.DevConsole; +import basemod.ShopGrid; +import basemod.devcommands.ConsoleCommand; + +public class ShopRemovePage extends ConsoleCommand { + + public ShopRemovePage() { + requiresPlayer = true; + minExtraTokens = 0; + maxExtraTokens = 1; + simpleCheck = true; + } + + @Override + public ArrayList extraOptions(String[] tokens, int depth) { + ArrayList opts = new ArrayList<>(); + for (ShopGrid.Page page : ShopGrid.pages) + opts.add(page.id); + for (ShopGrid.Page page : ShopGrid.customPages) + opts.add(page.id); + opts.sort(Comparator.comparing(String::toString)); + return opts; + } + + @Override + public void errorMsg() { + Shop.cmdShopHelp(); + } + + @Override + protected void execute(String[] tokens, int depth) { + if (tokens.length == 3) { + if (!ShopGrid.removePage(ShopGrid.getCurrentPage())) + DevConsole.log("could not remove current page"); + } else { + if (!ShopGrid.removePage(tokens[3])) + DevConsole.log("could not remove page with id " + tokens[3]); + } + ShopGrid.removeEmptyPages(); + } +} diff --git a/mod/src/main/java/basemod/interfaces/PostGridInitializeSubscriber.java b/mod/src/main/java/basemod/interfaces/PostGridInitializeSubscriber.java new file mode 100644 index 000000000..74d8a75e5 --- /dev/null +++ b/mod/src/main/java/basemod/interfaces/PostGridInitializeSubscriber.java @@ -0,0 +1,5 @@ +package basemod.interfaces; + +public interface PostGridInitializeSubscriber extends ISubscriber { + void receivePostGridInitialize(); +} diff --git a/mod/src/main/java/basemod/interfaces/PostShopInitializeSubscriber.java b/mod/src/main/java/basemod/interfaces/PostShopInitializeSubscriber.java new file mode 100644 index 000000000..1004d39ff --- /dev/null +++ b/mod/src/main/java/basemod/interfaces/PostShopInitializeSubscriber.java @@ -0,0 +1,5 @@ +package basemod.interfaces; + +public interface PostShopInitializeSubscriber extends ISubscriber { + void receivePostShopInitialize(); +} diff --git a/mod/src/main/java/basemod/patches/com/megacrit/cardcrawl/shop/ShopScreen/ShopGridPatch.java b/mod/src/main/java/basemod/patches/com/megacrit/cardcrawl/shop/ShopScreen/ShopGridPatch.java new file mode 100644 index 000000000..f764bd7d1 --- /dev/null +++ b/mod/src/main/java/basemod/patches/com/megacrit/cardcrawl/shop/ShopScreen/ShopGridPatch.java @@ -0,0 +1,479 @@ +package basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen; + +import java.util.ArrayList; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.evacipated.cardcrawl.modthespire.lib.LineFinder; +import com.evacipated.cardcrawl.modthespire.lib.Matcher; +import com.evacipated.cardcrawl.modthespire.lib.SpireField; +import com.evacipated.cardcrawl.modthespire.lib.SpireInsertLocator; +import com.evacipated.cardcrawl.modthespire.lib.SpireInsertPatch; +import com.evacipated.cardcrawl.modthespire.lib.SpireInstrumentPatch; +import com.evacipated.cardcrawl.modthespire.lib.SpirePatch; +import com.evacipated.cardcrawl.modthespire.lib.SpirePatch2; +import com.evacipated.cardcrawl.modthespire.lib.SpirePostfixPatch; +import com.evacipated.cardcrawl.modthespire.lib.SpirePrefixPatch; +import com.evacipated.cardcrawl.modthespire.lib.SpireReturn; +import com.megacrit.cardcrawl.core.Settings; +import com.megacrit.cardcrawl.dungeons.AbstractDungeon; +import com.megacrit.cardcrawl.helpers.FontHelper; +import com.megacrit.cardcrawl.helpers.Hitbox; +import com.megacrit.cardcrawl.helpers.ImageMaster; +import com.megacrit.cardcrawl.helpers.TipHelper; +import com.megacrit.cardcrawl.shop.ShopScreen; +import com.megacrit.cardcrawl.shop.StorePotion; +import com.megacrit.cardcrawl.shop.StoreRelic; + +import basemod.BaseMod; +import basemod.ReflectionHacks; +import basemod.ShopGrid; +import basemod.abstracts.CustomShopItem; +import javassist.CannotCompileException; +import javassist.CtBehavior; +import javassist.expr.ExprEditor; +import javassist.expr.MethodCall; + +public class ShopGridPatch { + + private static final float GOLD_IMG_WIDTH = ImageMaster.UI_GOLD.getWidth() * Settings.scale; + + private static final float SHADOW_DIST_X = 4F * Settings.scale; + + private static final float SHADOW_DIST_Y = 4F * Settings.scale; + + private static final float BOX_W = 100F * Settings.scale; + + private static final float OFFSET_X = -50F * Settings.scale; + + private static final float OFFSET_Y = -78F * Settings.scale; + + private static final float TEXT_OFFSET_X = -16F * Settings.scale; + + private static final float TEXT_OFFSET_Y = 16F * Settings.scale; + + private static final float RELIC_GOLD_OFFSET_X = -57F * Settings.scale; + + private static final float RELIC_GOLD_OFFSET_Y = -102F * Settings.scale; + + public static boolean hoveringItem = false; + + public static float x; + + public static float y; + + public static int price; + + public static void renderPrice(SpriteBatch sb) { + final float BOX_EDGE_H = ReflectionHacks.getPrivateStatic(TipHelper.class, "BOX_EDGE_H"); + final float TIP_DESC_LINE_SPACING = ReflectionHacks.getPrivateStatic(TipHelper.class, "TIP_DESC_LINE_SPACING"); + Color color = ReflectionHacks.getPrivateStatic(TipHelper.class, "BASE_COLOR"); + float h = FontHelper.getHeight(FontHelper.tipBodyFont, Integer.toString(price), 1F); + sb.setColor(Settings.TOP_PANEL_SHADOW_COLOR); + sb.draw(ImageMaster.KEYWORD_TOP, x + OFFSET_X + SHADOW_DIST_X, y + OFFSET_Y - SHADOW_DIST_Y, BOX_W, BOX_EDGE_H); + sb.draw(ImageMaster.KEYWORD_BOT, x + OFFSET_X + SHADOW_DIST_X, y + OFFSET_Y - h - SHADOW_DIST_Y, BOX_W, BOX_EDGE_H + Math.min(h, 0F)); + sb.setColor(Color.WHITE); + sb.draw(ImageMaster.KEYWORD_TOP, x + OFFSET_X, y + OFFSET_Y, BOX_W, BOX_EDGE_H); + sb.draw(ImageMaster.KEYWORD_BOT, x + OFFSET_X, y + OFFSET_Y - h, BOX_W, BOX_EDGE_H); + sb.draw(ImageMaster.UI_GOLD, x + RELIC_GOLD_OFFSET_X, y + RELIC_GOLD_OFFSET_Y, GOLD_IMG_WIDTH, GOLD_IMG_WIDTH); + if (price > AbstractDungeon.player.gold) + color = Color.SALMON; + FontHelper.renderSmartText(sb, FontHelper.tipBodyFont, Integer.toString(price), x + TEXT_OFFSET_X + OFFSET_X + GOLD_IMG_WIDTH, y + TEXT_OFFSET_Y + OFFSET_Y, BOX_W, TIP_DESC_LINE_SPACING, color); + } + + @SpirePatch2(clz = ShopScreen.class, method = SpirePatch.CLASS) + public static class ShopScreenPatches { + + @SpirePatch2(clz = ShopScreen.class, method = "init") + public static class ShopScreenInit { + + @SpirePostfixPatch + public static void PostShopInitializeHook() { + BaseMod.publishPostShopInitialize(); + } + } + + @SpirePatch2(clz = ShopScreen.class, method = "open") + public static class ShopScreenOpen { + + @SpirePostfixPatch + public static void HideGridOnOpen() { + if (!ShopGrid.getCurrentPage().isEmpty()) { + ShopGrid.hide(); + } + } + } + + @SpirePatch2(clz = ShopScreen.class, method = "initRelics") + public static class InitRelics { + + @SpirePrefixPatch + public static void InitializeGrid() { + basemod.ShopGrid.initialize(); + BaseMod.publishPostGridInitialize(); + } + + @SpireInsertPatch(locator = ArrayAddLocator.class, localvars = { "relic" }) + public static void AddGridRelic(StoreRelic relic) { + CustomShopItem item = new CustomShopItem(relic); + item.applyDiscounts = false; + if (!ShopGrid.tryAddItem(item)) + BaseMod.logger.warn("not adding default shop relic because grid is full, is this intentional?"); + } + + private static class ArrayAddLocator extends SpireInsertLocator { + + @Override + public int[] Locate(CtBehavior ct) throws Exception { + return LineFinder.findInOrder(ct, new Matcher.MethodCallMatcher(ArrayList.class, "add")); + } + } + } + + @SpirePatch2(clz = ShopScreen.class, method = "initPotions") + public static class PostInitPotions { + + @SpirePostfixPatch + public static void ClearEmptyPagesAfterInit() { + ShopGrid.removeEmptyPages(); + ShopGrid.removeEmptyRows(); + } + + @SpireInsertPatch(locator = ArrayAddLocator.class, localvars = { "potion" }) + public static void AddGridPotion(StorePotion potion) { + CustomShopItem item = new CustomShopItem(potion); + item.applyDiscounts = false; + if (!ShopGrid.tryAddItem(item)) + BaseMod.logger.warn("not adding default potion because grid is full, is this intentional?"); + } + + private static class ArrayAddLocator extends SpireInsertLocator { + + @Override + public int[] Locate(CtBehavior ct) throws Exception { + return LineFinder.findInOrder(ct, new Matcher.MethodCallMatcher(ArrayList.class, "add")); + } + } + } + + @SpirePatch2(clz = ShopScreen.class, method = "updateRelics") + public static class Update { + + @SpirePrefixPatch + public static void UpdateGrid(float ___rugY) { + ShopGrid.update(___rugY); + } + } + + @SpirePatch2(clz = ShopScreen.class, method = "render") + public static class Render { + + @SpirePrefixPatch + public static void ResetHoveringItem() { + hoveringItem = false; + } + + @SpirePostfixPatch + public static void RenderItemPrice(SpriteBatch sb) { + if (hoveringItem) + renderPrice(sb); + } + + @SpireInsertPatch(locator = RenderRelicsLocator.class) + public static void RenderGrid(SpriteBatch sb, float ___rugY) { + ShopGrid.render(sb, ___rugY); + } + + private static class RenderRelicsLocator extends SpireInsertLocator { + @Override + public int[] Locate(CtBehavior ct) throws Exception { + return LineFinder.findInOrder(ct, new Matcher.MethodCallMatcher(ShopScreen.class, "renderRelics")); + } + } + } + } + + @SpirePatch2(clz = StoreRelic.class, method = SpirePatch.CLASS) + public static class StoreRelicPatches { + + public static SpireField compatibleWithGrid = new SpireField<>(() -> false); + + @SpirePatch2(clz = StoreRelic.class, method = "update") + public static class SetCoords { + + @SpireInsertPatch(locator = HBMoveLocator.class) + public static SpireReturn Insert(StoreRelic __instance, float rugY) { + if (__instance.relic != null && compatibleWithGrid.get(__instance)) { + for (ShopGrid.Row gridRow : ShopGrid.getCurrentPage().rows) + for (CustomShopItem item : gridRow.items) + if (item.storeRelic == __instance) { + __instance.relic.currentY = gridRow.getY(item.row, rugY); + __instance.relic.currentX = gridRow.getX(item.col); + return SpireReturn.Continue(); + } + return SpireReturn.Return(); + } + return SpireReturn.Continue(); + } + + private static class HBMoveLocator extends SpireInsertLocator { + @Override + public int[] Locate(CtBehavior ctb) throws Exception { + Matcher finalMatcher = new Matcher.MethodCallMatcher(Hitbox.class, "move"); + return LineFinder.findInOrder(ctb, new ArrayList(), finalMatcher); + } + } + } + + @SpirePatch2(clz = StoreRelic.class, method = "purchaseRelic") + public static class PurchaseRelic { + @SpirePostfixPatch + public static void RemoveRelic(StoreRelic __instance) { + if (__instance.isPurchased && compatibleWithGrid.get(__instance)) { + for (ShopGrid.Row gridRow : ShopGrid.getCurrentPage().rows) + for (CustomShopItem item : gridRow.items) { + if (item.storeRelic == __instance) { + item.isPurchased = true; + ShopGrid.removeEmptyPages(); + return; + } + } + ShopGrid.removeEmptyPages(); + } + } + } + + @SpirePatch2(clz = StoreRelic.class, method = "render") + public static class Render { + + @SpirePrefixPatch + public static SpireReturn CheckIfRelicInCurrentPage(StoreRelic __instance, SpriteBatch sb) { + if (__instance.relic != null && compatibleWithGrid.get(__instance) && !ShopGrid.getCurrentPage().contains(__instance)) { + return SpireReturn.Return(); + } + return SpireReturn.Continue(); + } + + public static boolean canRender(StoreRelic instance) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) + && ((ShopGrid.getCurrentPage().getItem(instance).gridRow.maxColumns > 5) + || ShopGrid.getCurrentPage().rows.size() > 4)) + return false; + return true; + } + + public static boolean canRenderGold(StoreRelic instance) { + if (!canRender(instance)) { + if (instance.relic.hb.hovered) { + hoveringItem = true; + x = instance.relic.currentX; + y = instance.relic.currentY; + price = instance.price; + } + return false; + } + return true; + } + + public static float goldY(StoreRelic instance, float yOffset) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) && ShopGrid.getCurrentPage().rows.size() > 3) + return instance.relic.currentY - 75F * Settings.yScale; + return instance.relic.currentY + yOffset; + } + + @SpireInstrumentPatch + public static ExprEditor ChangeGoldPosition() { + return new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getMethodName().equals("draw")) { + m.replace("" + + "{" + + "if (basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StoreRelicPatches.Render.canRenderGold(this))" + + "sb.draw($1, $2, basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StoreRelicPatches.Render.goldY(this, RELIC_GOLD_OFFSET_Y), $4, $5);" + + "}" + ); + } + } + }; + } + + public static boolean canRenderText(SpriteBatch sb, StoreRelic instance) { + return canRender(instance); + } + + public static float textX(StoreRelic instance, float xOffset) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) && ShopGrid.getCurrentPage().getItem(instance).gridRow.maxColumns > 4) + return instance.relic.currentX + 3F * Settings.scale; + return instance.relic.currentX + xOffset; + } + + public static float textY(StoreRelic instance, float yOffset) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) && ShopGrid.getCurrentPage().rows.size() > 3) + return instance.relic.currentY - 40F * Settings.scale; + return instance.relic.currentY + yOffset; + } + + @SpireInstrumentPatch + public static ExprEditor ChangeTextPosition() { + return new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getMethodName().equals("renderFontLeftTopAligned")) { + m.replace("" + + "{" + + "if (basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StoreRelicPatches.Render.canRenderText($1, this))" + + "com.megacrit.cardcrawl.helpers.FontHelper.renderFontLeftTopAligned($1, $2, $3, " + + "basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StoreRelicPatches.Render.textX(this, RELIC_PRICE_OFFSET_X), " + + "basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StoreRelicPatches.Render.textY(this, RELIC_PRICE_OFFSET_Y), $6);" + + "}" + ); + } + } + }; + } + } + } + + @SpirePatch2(clz = StorePotion.class, method = SpirePatch.CLASS) + public static class StorePotionPatches { + + public static SpireField compatibleWithGrid = new SpireField<>(() -> false); + + @SpirePatch2(clz = StorePotion.class, method = "update") + public static class Update { + + @SpireInsertPatch(locator = HBMoveLocator.class) + public static SpireReturn SetCoords(StorePotion __instance, float rugY) { + if (__instance.potion != null && compatibleWithGrid.get(__instance)) { + for (ShopGrid.Row gridRow : ShopGrid.getCurrentPage().rows) + for (CustomShopItem item : gridRow.items) { + if (item.storePotion == __instance) { + if (gridRow.owner.rows.size() > 2) { + __instance.potion.posY = gridRow.getY(item.row, rugY); + } else { + __instance.potion.posY = rugY + (item.row == 0 ? 400F : 200F) * Settings.xScale; + } + __instance.potion.posX = gridRow.getX(item.col); + return SpireReturn.Continue(); + } + } + return SpireReturn.Return(); + } + return SpireReturn.Continue(); + } + + private static class HBMoveLocator extends SpireInsertLocator { + @Override + public int[] Locate(CtBehavior ctb) throws Exception { + Matcher finalMatcher = new Matcher.MethodCallMatcher(Hitbox.class, "move"); + return LineFinder.findInOrder(ctb, new ArrayList(), finalMatcher); + } + } + } + + @SpirePatch2(clz = StorePotion.class, method = "purchasePotion") + public static class PurchasePotion { + public static void Postfix(StorePotion __instance) { + if (__instance.isPurchased && compatibleWithGrid.get(__instance)) { + for (ShopGrid.Row gridRow : ShopGrid.getCurrentPage().rows) + for (CustomShopItem item : gridRow.items) { + if (item.storePotion == __instance) { + item.isPurchased = true; + ShopGrid.removeEmptyPages(); + break; + } + } + ShopGrid.removeEmptyPages(); + } + } + } + + @SpirePatch2(clz = StorePotion.class, method = "render") + public static class Render { + + @SpirePrefixPatch + public static SpireReturn CheckIfPotionInCurrentPage(StorePotion __instance, SpriteBatch sb) { + if (__instance.potion != null && compatibleWithGrid.get(__instance) && !ShopGrid.getCurrentPage().contains(__instance)) { + return SpireReturn.Return(); + } + return SpireReturn.Continue(); + } + + public static boolean canRender(StorePotion instance) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) + && ((ShopGrid.getCurrentPage().getItem(instance).gridRow.maxColumns > 5) + || ShopGrid.getCurrentPage().rows.size() > 4)) + return false; + return true; + } + + public static boolean canRenderGold(StorePotion instance) { + if (!canRender(instance)) { + if (instance.potion.hb.hovered) { + hoveringItem = true; + x = instance.potion.posX; + y = instance.potion.posY; + price = instance.price; + } + return false; + } + return true; + } + + public static float goldY(StorePotion instance, float yOffset) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) && ShopGrid.getCurrentPage().rows.size() > 3) + return instance.potion.posY - 75F * Settings.yScale; + return instance.potion.posY + yOffset; + } + + @SpireInstrumentPatch + public static ExprEditor ChangeGoldPosition() { + return new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getMethodName().equals("draw")) { + m.replace("" + + "{" + + "if (basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StorePotionPatches.Render.canRenderGold(this))" + + "sb.draw($1, $2, basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StorePotionPatches.Render.goldY(this, RELIC_GOLD_OFFSET_Y), $4, $5);" + + "}" + ); + } + } + }; + } + + public static boolean canRenderText(SpriteBatch sb, StorePotion instance) { + return canRender(instance); + } + + public static float textX(StorePotion instance, float xOffset) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) && ShopGrid.getCurrentPage().getItem(instance).gridRow.maxColumns > 4) + return instance.potion.posX + 3F * Settings.scale; + return instance.potion.posX + xOffset; + } + + public static float textY(StorePotion instance, float yOffset) { + if (compatibleWithGrid.get(instance) && ShopGrid.getCurrentPage().contains(instance) && ShopGrid.getCurrentPage().rows.size() > 3) + return instance.potion.posY - 40F * Settings.scale; + return instance.potion.posY + yOffset; + } + + @SpireInstrumentPatch + public static ExprEditor ChangeTextPosition() { + return new ExprEditor() { + public void edit(MethodCall m) throws CannotCompileException { + if (m.getMethodName().equals("renderFontLeftTopAligned")) { + m.replace("" + + "{" + + "if (basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StorePotionPatches.Render.canRenderText($1, this))" + + "com.megacrit.cardcrawl.helpers.FontHelper.renderFontLeftTopAligned($1, $2, $3, " + + "basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StorePotionPatches.Render.textX(this, RELIC_PRICE_OFFSET_X), " + + "basemod.patches.com.megacrit.cardcrawl.shop.ShopScreen.ShopGridPatch.StorePotionPatches.Render.textY(this, RELIC_PRICE_OFFSET_Y), $6);" + + "}" + ); + } + } + }; + } + } + } +}