From b0b0b7567ec0656c60f2f3e4730c0edace353fb7 Mon Sep 17 00:00:00 2001 From: Roman / Nea Date: Thu, 30 Dec 2021 14:37:02 +0100 Subject: Adding support for more recipe types and forge recipes (#40) * Foundations for support of different crafting recipe types. NeuRecipe is now a base class for a recipe which provides common concepts, such as inputs, outputs and a rendering task. GuiItemRecipe has been reworked to work with this new NeuRecipe. NeuManager now parses said recipes. This should be reworked to be a two step process (first register items, then register recipes). To keep compatibility with older repo versions, NeuRecipes are parse lenient and default to a crafting recipe. New recipes should be added in the `recipes` json field which is an array of json dictionaries, which have a `type` and other fields depending on the `type` of that recipe. This also adds support for having multiple recipes for a single item (e.g. uncrafting storage blocks). * Remove references in existing code * Recipe Generation * ring recipes * recipe generator v2 * quick forge * bugfixes and performance improvements * fix raw craft cost * reload hotm if you open the hotm tree inv * add myself to the changelog * replace quickforge formular with lookup table * do not crash anymore when opening recipes outside of skyblock * format coins differently * remove debug logs * change recipe generator so that it doesnt crash old versions --- .../assets/notenoughupdates/textures/gui/forge_recipe.png | Bin 0 -> 889 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/assets/notenoughupdates/textures/gui/forge_recipe.png (limited to 'src/main/resources/assets/notenoughupdates/textures/gui') diff --git a/src/main/resources/assets/notenoughupdates/textures/gui/forge_recipe.png b/src/main/resources/assets/notenoughupdates/textures/gui/forge_recipe.png new file mode 100644 index 00000000..2c3d2eb1 Binary files /dev/null and b/src/main/resources/assets/notenoughupdates/textures/gui/forge_recipe.png differ -- cgit From f11f6953a207606ae920ede9e713467a47cfc018 Mon Sep 17 00:00:00 2001 From: Roman / Nea Date: Sat, 12 Feb 2022 13:53:01 +0100 Subject: Dream skin (#80) --- Update Notes/2.1.md | 1 + .../moulberry/notenoughupdates/NEUManager.java | 18 ++- .../moulberry/notenoughupdates/NEUOverlay.java | 17 +-- .../notenoughupdates/auction/APIManager.java | 1 + .../notenoughupdates/commands/Commands.java | 11 +- .../notenoughupdates/miscgui/GuiItemRecipe.java | 4 +- .../notenoughupdates/mixins/MixinEntity.java | 24 ++++ .../mixins/MixinEntityAgeable.java | 21 +++ .../notenoughupdates/mixins/MixinEntityPlayer.java | 40 ++++++ .../notenoughupdates/recipes/CraftingRecipe.java | 7 +- .../notenoughupdates/recipes/ForgeRecipe.java | 9 +- .../notenoughupdates/recipes/NeuRecipe.java | 17 ++- .../recipes/VillagerTradeRecipe.java | 145 +++++++++++++++++++++ .../moulberry/notenoughupdates/util/SBInfo.java | 110 +++++++++++++++- .../assets/notenoughupdates/dreamskin.png | Bin 0 -> 220 bytes .../textures/gui/villager_recipe.png | Bin 0 -> 8633 bytes src/main/resources/mixins.notenoughupdates.json | 73 ++++++----- 17 files changed, 432 insertions(+), 66 deletions(-) create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java create mode 100644 src/main/resources/assets/notenoughupdates/dreamskin.png create mode 100644 src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.png (limited to 'src/main/resources/assets/notenoughupdates/textures/gui') diff --git a/Update Notes/2.1.md b/Update Notes/2.1.md index ee124398..d6618bd3 100644 --- a/Update Notes/2.1.md +++ b/Update Notes/2.1.md @@ -15,6 +15,7 @@ - [Price graph for items on /ah and /bz](https://cdn.discordapp.com/attachments/896407218151366687/926968296929107999/unknown.png) - DeDiamondPro ### **Minor Changes:** - Add built-in recipes for forge crafts - nea89 +- Add Stranded Villager Trades to the item list - nea89 - Make cata xp in /pv be calculated on how many runs you have and shows master mode xp rates - Hide mine waypoints when at location setting - Lulonaut - Added some info panels to some settings in /neu diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java index 614884d9..bf3a0714 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java @@ -3,7 +3,6 @@ package io.github.moulberry.notenoughupdates; import com.google.gson.*; import io.github.moulberry.notenoughupdates.auction.APIManager; import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe; -import io.github.moulberry.notenoughupdates.options.NEUConfig; import io.github.moulberry.notenoughupdates.recipes.CraftingOverlay; import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe; import io.github.moulberry.notenoughupdates.recipes.Ingredient; @@ -33,6 +32,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -418,10 +418,18 @@ public class NEUManager { return recipesMap.getOrDefault(internalName, Collections.emptySet()); } + public List getAvailableRecipesFor(String internalname) { + return getRecipesFor(internalname).stream().filter(NeuRecipe::isAvailable).collect(Collectors.toList()); + } + public Set getUsagesFor(String internalName) { return usagesMap.getOrDefault(internalName, Collections.emptySet()); } + public List getAvailableUsagesFor(String internalname) { + return getUsagesFor(internalname).stream().filter(NeuRecipe::isAvailable).collect(Collectors.toList()); + } + /** * Searches a string for a query. This method is used to mimic the behaviour of the more complex map-based search * function. This method is used for the chest-item-search feature. @@ -952,19 +960,19 @@ public class NEUManager { public boolean displayGuiItemUsages(String internalName) { if (!usagesMap.containsKey(internalName)) return false; - Set usages = usagesMap.get(internalName); + List usages = getAvailableUsagesFor(internalName); if (usages.isEmpty()) return false; Minecraft.getMinecraft().displayGuiScreen( - new GuiItemRecipe("Item Usages", new ArrayList<>(usages), this)); + new GuiItemRecipe("Item Usages", usages, this)); return true; } public boolean displayGuiItemRecipe(String internalName, String text) { if (!recipesMap.containsKey(internalName)) return false; - Set recipes = recipesMap.get(internalName); + List recipes = getAvailableRecipesFor(internalName); if (recipes.isEmpty()) return false; Minecraft.getMinecraft().displayGuiScreen( - new GuiItemRecipe(text != null ? text : "Item Recipe", new ArrayList<>(recipes), this)); + new GuiItemRecipe(text != null ? text : "Item Recipe", recipes, this)); return true; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java index 1fbffd5f..04ed9964 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java @@ -62,9 +62,8 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL14; import org.lwjgl.util.vector.Vector2f; -import java.awt.*; +import java.awt.Color; import java.lang.reflect.InvocationTargetException; -import java.util.List; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; @@ -2345,14 +2344,10 @@ public class NEUOverlay extends Gui { ItemPriceInformation.addToTooltip(text, internalname, stack); } - boolean hasClick = false; - boolean hasInfo = false; - if (json.has("clickcommand") && !json.get("clickcommand").getAsString().isEmpty()) { - hasClick = true; - } - if (json.has("info") && json.get("info").getAsJsonArray().size() > 0) { - hasInfo = true; - } + boolean hasClick = + (json.has("clickcommand") && !json.get("clickcommand").getAsString().isEmpty()) + || !manager.getAvailableRecipesFor(internalname).isEmpty(); + boolean hasInfo = json.has("info") && json.get("info").getAsJsonArray().size() > 0; if (hasClick || hasInfo) text.add(""); if (hasClick) @@ -2685,4 +2680,4 @@ public class NEUOverlay extends Gui { public float getInfoPaneOffsetFactor() { return infoPaneOffsetFactor.getValue() * getWidthMult(); } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java index d0b4a7f5..9455eb64 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java @@ -799,6 +799,7 @@ public class APIManager { if (recipes != null) RECIPE_ITER: for (NeuRecipe recipe : recipes) { + if (recipe.hasVariableCost() || !recipe.shouldUseForCraftCost()) continue; float craftPrice = 0; for (Ingredient i : recipe.getIngredients()) { if (i.isCoins()) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java index d3fd7e0c..50662a80 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java @@ -14,13 +14,13 @@ import io.github.moulberry.notenoughupdates.cosmetics.GuiCosmetics; import io.github.moulberry.notenoughupdates.dungeons.DungeonWin; import io.github.moulberry.notenoughupdates.dungeons.GuiDungeonMapEditor; import io.github.moulberry.notenoughupdates.gamemodes.GuiGamemodes; -import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes; -import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent; -import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.SpecialBlockZone; import io.github.moulberry.notenoughupdates.miscfeatures.FairySouls; import io.github.moulberry.notenoughupdates.miscfeatures.FancyPortals; import io.github.moulberry.notenoughupdates.miscfeatures.FishingHelper; import io.github.moulberry.notenoughupdates.miscfeatures.NullzeeSphere; +import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes; +import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent; +import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.SpecialBlockZone; import io.github.moulberry.notenoughupdates.miscgui.*; import io.github.moulberry.notenoughupdates.miscgui.tutorials.NeuTutorial; import io.github.moulberry.notenoughupdates.options.NEUConfig; @@ -745,6 +745,11 @@ public class Commands { DupePOC.doDupe(args[0]); return; }*/ + if (args.length >= 1 && args[0].equalsIgnoreCase("profileinfo")) { + String currentProfile = SBInfo.getInstance().currentProfile; + SBInfo.Gamemode gamemode = SBInfo.getInstance().getGamemodeForProfile(currentProfile); + sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GOLD + "You are on Profile " + currentProfile + " with the mode " + gamemode)); + } if (args.length >= 1 && args[0].equalsIgnoreCase("pricetest")) { if (args.length == 1) { NotEnoughUpdates.INSTANCE.manager.auctionManager.updateBazaar(); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java index e820378b..6e4d13f6 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java @@ -90,7 +90,7 @@ public class GuiItemRecipe extends GuiScreen { Minecraft.getMinecraft().getTextureManager().bindTexture(currentRecipe.getBackground()); this.drawTexturedModalRect(guiLeft, guiTop, 0, 0, this.xSize, this.ySize); - currentRecipe.drawExtraBackground(this); + currentRecipe.drawExtraBackground(this, mouseX, mouseY); List slots = getAllRenderedSlots(); for (RecipeSlot slot : slots) { @@ -101,7 +101,7 @@ public class GuiItemRecipe extends GuiScreen { Utils.drawStringScaledMaxWidth(title, fontRendererObj, guiLeft + TITLE_X, guiTop + TITLE_Y, false, xSize - 38, 0x404040); - currentRecipe.drawExtraInfo(this); + currentRecipe.drawExtraInfo(this, mouseX, mouseY); for (RecipeSlot slot : slots) { if (isWithinRect(mouseX, mouseY, slot.getX(this), slot.getY(this), SLOT_SIZE, SLOT_SIZE)) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java new file mode 100644 index 00000000..6e8f5cd2 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntity.java @@ -0,0 +1,24 @@ +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Entity.class) +public class MixinEntity { + // Fix NPE in vanilla code, that we need to work for VillagerTradeRecipe + @Inject(method = "getBrightnessForRender", at = @At("HEAD"), cancellable = true) + public void onGetBrightnessForRender(float p_getBrightnessForRender_1_, CallbackInfoReturnable cir) { + if (((Entity) (Object) this).worldObj == null) + cir.setReturnValue(-1); + } + + // Fix NPE in vanilla code, that we need to work for VillagerTradeRecipe + @Inject(method = "getBrightness", at = @At("HEAD"), cancellable = true) + public void onGetBrightness(float p_getBrightness_1_, CallbackInfoReturnable cir) { + if (((Entity) (Object) this).worldObj == null) + cir.setReturnValue(1.0F); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java new file mode 100644 index 00000000..a6f6c0c6 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java @@ -0,0 +1,21 @@ +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.entity.EntityAgeable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(EntityAgeable.class) +public class MixinEntityAgeable { + @Shadow + protected int growingAge; + + // Fix NPE in vanilla code, that we need to work for VillagerTradeRecipe + @Inject(method = "getGrowingAge", cancellable = true, at = @At("HEAD")) + public void onGetGrowingAge(CallbackInfoReturnable cir) { + if (((EntityAgeable) (Object) this).worldObj == null) + cir.setReturnValue(growingAge); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java index 218e03eb..21063ea6 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityPlayer.java @@ -1,15 +1,26 @@ package io.github.moulberry.notenoughupdates.mixins; import io.github.moulberry.notenoughupdates.cosmetics.CapeManager; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EnumPlayerModelParts; +import net.minecraft.scoreboard.ScorePlayerTeam; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.util.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.lib.Opcodes; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin({EntityPlayer.class}) public abstract class MixinEntityPlayer { + @Shadow + public abstract boolean interactWith(Entity par1); + @Inject(method = "isWearing", at = @At("HEAD"), cancellable = true) public void isWearing(EnumPlayerModelParts part, CallbackInfoReturnable cir) { if (part == EnumPlayerModelParts.CAPE) { @@ -21,4 +32,33 @@ public abstract class MixinEntityPlayer { } } } + + @Redirect(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isRemote:Z", opcode = Opcodes.GETFIELD)) + public boolean onIsRemote(World instance) { + if (instance == null) return true; + return instance.isRemote; + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;getSpawnPoint()Lnet/minecraft/util/BlockPos;")) + public BlockPos onGetSpawnPoint(World instance) { + if (instance == null) + return new BlockPos(0, 0, 0); + return instance.getSpawnPoint(); + } + + @Inject(method = "getWorldScoreboard", at = @At("HEAD"), cancellable = true) + public void onGetWorldScoreboard(CallbackInfoReturnable cir) { + if (((EntityPlayer) (Object) this).worldObj == null) { + cir.setReturnValue(null); + } + } + + @Redirect(method = "getTeam", at = @At(value = "INVOKE", target = "Lnet/minecraft/scoreboard/Scoreboard;getPlayersTeam(Ljava/lang/String;)Lnet/minecraft/scoreboard/ScorePlayerTeam;")) + public ScorePlayerTeam onGetTeam(Scoreboard instance, String p_getPlayersTeam_1_) { + if (instance == null) { + return null; + } + return instance.getPlayersTeam(p_getPlayersTeam_1_); + } + } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java index 00e70462..082c1ad8 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java @@ -44,6 +44,11 @@ public class CraftingRecipe implements NeuRecipe { return ingredients; } + @Override + public boolean hasVariableCost() { + return false; + } + @Override public Set getOutputs() { return Collections.singleton(getOutput()); @@ -85,7 +90,7 @@ public class CraftingRecipe implements NeuRecipe { } @Override - public void drawExtraInfo(GuiItemRecipe gui) { + public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj; String craftingText = getCraftText(); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java index 5cbb4afe..2870a54e 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java @@ -74,6 +74,11 @@ public class ForgeRecipe implements NeuRecipe { return Sets.newHashSet(inputs); } + @Override + public boolean hasVariableCost() { + return false; + } + @Override public Set getOutputs() { return Collections.singleton(output); @@ -95,7 +100,7 @@ public class ForgeRecipe implements NeuRecipe { } @Override - public void drawExtraBackground(GuiItemRecipe gui) { + public void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) { Minecraft.getMinecraft().getTextureManager().bindTexture(BACKGROUND); for (int i = 0; i < inputs.size(); i++) { int[] slotCoordinates = getSlotCoordinates(i, inputs.size()); @@ -107,7 +112,7 @@ public class ForgeRecipe implements NeuRecipe { } @Override - public void drawExtraInfo(GuiItemRecipe gui) { + public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj; if (timeInSeconds > 0) Utils.drawStringCenteredScaledMaxWidth(formatDuration(timeInSeconds), fontRenderer, gui.guiLeft + EXTRA_INFO_X, gui.guiTop + EXTRA_INFO_Y, false, EXTRA_INFO_MAX_WIDTH, 0xff00ff); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java index cfa091d5..99b05d28 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java @@ -15,14 +15,17 @@ public interface NeuRecipe { List getSlots(); - void drawExtraInfo(GuiItemRecipe gui); + default void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { + } - default void drawExtraBackground(GuiItemRecipe gui) { + default void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) { } default void drawHoverInformation(GuiItemRecipe gui, int mouseX, int mouseY) { } + boolean hasVariableCost(); + JsonObject serialize(); ResourceLocation getBackground(); @@ -32,8 +35,18 @@ public interface NeuRecipe { switch (recipe.get("type").getAsString().intern()) { case "forge": return ForgeRecipe.parseForgeRecipe(manager, recipe, output); + case "trade": + return VillagerTradeRecipe.parseStaticRecipe(manager, recipe); } } return CraftingRecipe.parseCraftingRecipe(manager, recipe, output); } + + default boolean shouldUseForCraftCost() { + return true; + } + + default boolean isAvailable() { + return true; + } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java new file mode 100644 index 00000000..530e8e32 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java @@ -0,0 +1,145 @@ +package io.github.moulberry.notenoughupdates.recipes; + +import com.google.common.collect.Sets; +import com.google.gson.JsonObject; +import com.mojang.authlib.GameProfile; +import io.github.moulberry.notenoughupdates.NEUManager; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe; +import io.github.moulberry.notenoughupdates.util.SBInfo; +import io.github.moulberry.notenoughupdates.util.Utils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiInventory; +import net.minecraft.client.network.NetworkPlayerInfo; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.passive.EntityVillager; +import net.minecraft.util.ResourceLocation; + +import java.util.*; + +public class VillagerTradeRecipe implements NeuRecipe { + + public static final int COST_SLOT_X = 51; + public static final int COST_SLOT_Y = 34; + public static final int RESULT_SLOT_Y = 35; + public static final int RESULT_SLOT_X = 124; + + private static class Holder { // This holder object exists to defer initialization to first access + private static final GameProfile DREAM_PROFILE = new GameProfile(UUID.fromString("ec70bcaf-702f-4bb8-b48d-276fa52a780c"), "Dream"); + private static final EntityLivingBase DEMO_DREAM = new AbstractClientPlayer(null, DREAM_PROFILE) { + @Override + protected NetworkPlayerInfo getPlayerInfo() { + return new NetworkPlayerInfo(DREAM_PROFILE) { + @Override + public ResourceLocation getLocationSkin() { + return new ResourceLocation("notenoughupdates", "dreamskin.png"); + } + }; + } + }; + private static final EntityLivingBase DEMO_VILLAGER = new EntityVillager(null); + + private static boolean isAprilFirst() { + Calendar cal = Calendar.getInstance(); + return cal.get(Calendar.DAY_OF_MONTH) == 1 && cal.get(Calendar.MONTH) == Calendar.APRIL; + } + + private static final EntityLivingBase DEMO_ENTITY = isAprilFirst() ? DEMO_DREAM : DEMO_VILLAGER; + + } + + private final static ResourceLocation BACKGROUND = new ResourceLocation("notenoughupdates", "textures/gui/villager_recipe.png"); + + private final Ingredient result; + private final Ingredient cost; + private final int minCost, maxCost; + + public VillagerTradeRecipe(Ingredient result, Ingredient cost, int minCost, int maxCost) { + this.result = result; + this.cost = cost; + this.minCost = minCost; + this.maxCost = maxCost; + } + + public VillagerTradeRecipe(Ingredient result, Ingredient cost) { + this(result, cost, -1, -1); + } + + public boolean hasVariableCost() { + return minCost != -1 && maxCost != -1; + } + + @Override + public Set getIngredients() { + return Sets.newHashSet(cost); + } + + @Override + public Set getOutputs() { + return Sets.newHashSet(result); + } + + @Override + public List getSlots() { + return Arrays.asList( + new RecipeSlot(COST_SLOT_X, COST_SLOT_Y, cost.getItemStack()), + new RecipeSlot(RESULT_SLOT_X, RESULT_SLOT_Y, result.getItemStack()) + ); + } + + @Override + public boolean shouldUseForCraftCost() { + return false; + } + + @Override + public boolean isAvailable() { + return SBInfo.getInstance().getCurrentMode() == SBInfo.Gamemode.STRANDED || NotEnoughUpdates.INSTANCE.config.hidden.dev; + } + + @Override + public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { + if (hasVariableCost()) { + FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj; + Utils.drawStringCenteredScaledMaxWidth( + minCost + " - " + maxCost, fontRenderer, + gui.guiLeft + 50, gui.guiTop + 60, false, 75, 0xff00ff); + + } + } + + @Override + public void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) { + GuiInventory.drawEntityOnScreen(gui.guiLeft + 90, gui.guiTop + 75, 30, gui.guiLeft - mouseX + 80, gui.guiTop + 60 - mouseY, Holder.DEMO_ENTITY); + } + + @Override + public JsonObject serialize() { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("type", "trade"); + jsonObject.addProperty("result", result.serialize()); + jsonObject.addProperty("cost", cost.getInternalItemId()); + if (minCost > 0) + jsonObject.addProperty("min", minCost); + if (maxCost > 0) + jsonObject.addProperty("max", maxCost); + return jsonObject; + } + + @Override + public ResourceLocation getBackground() { + return BACKGROUND; + } + + public static VillagerTradeRecipe parseStaticRecipe(NEUManager manager, JsonObject recipe) { + return new VillagerTradeRecipe( + new Ingredient(manager, recipe.get("result").getAsString()), + new Ingredient(manager, recipe.get("cost").getAsString()), + recipe.has("min") ? recipe.get("min").getAsInt() : -1, + recipe.has("max") ? recipe.get("max").getAsInt() : -1 + ); + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java index 6dcd284e..7b5a29a9 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/SBInfo.java @@ -1,13 +1,19 @@ package io.github.moulberry.notenoughupdates.util; +import com.google.common.reflect.TypeToken; import com.google.gson.JsonObject; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.LocationChangeEvent; import io.github.moulberry.notenoughupdates.overlays.SlayerOverlay; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.network.NetworkPlayerInfo; +import net.minecraft.init.Blocks; import net.minecraft.inventory.ContainerChest; +import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.scoreboard.Score; import net.minecraft.scoreboard.ScoreObjective; import net.minecraft.scoreboard.ScorePlayerTeam; @@ -19,13 +25,16 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -61,6 +70,31 @@ public class SBInfo { public boolean isInDungeon = false; public boolean hasNewTab = false; + + public enum Gamemode { + NORMAL("", ""), IRONMAN("Ironman", "♲"), STRANDED("Stranded", "☀"); + + private final String name; + private final String emoji; + + Gamemode(String name, String emoji) { + this.name = name; + this.emoji = emoji; + } + + public static Gamemode find(String type) { + for (Gamemode gamemode : values()) { + if (type.contains(gamemode.name)) + return gamemode; + } + return null; + } + } + + + private Map gamemodes = new HashMap<>(); + private boolean areGamemodesLoaded = false; + private int tickCount = 0; public String currentProfile = null; @SubscribeEvent @@ -76,6 +110,74 @@ public class SBInfo { } } + @SubscribeEvent + public void onGuiTick(TickEvent event) { + if (tickCount++ % 10 != 0) return; + GuiScreen currentScreen = Minecraft.getMinecraft().currentScreen; + if (currentScreen instanceof GuiChest) { + ContainerChest container = (ContainerChest) ((GuiChest) currentScreen).inventorySlots; + if ("Profile Management".equals(container.getLowerChestInventory().getDisplayName().getUnformattedText())) { + updateProfileInformation(container); + } + } + } + + private static final Pattern PROFILE_PATTERN = Pattern.compile("(?(♲ Ironman)|(☀ Stranded)|()) *Profile: (?[^ ]+)"); + + private void updateProfileInformation(ContainerChest container) { + for (int i = 11; i < 16; i = -~i) { + Slot slot = container.getSlot(i); + if (slot == null || !slot.getHasStack()) continue; + ItemStack item = slot.getStack(); + if (item == null || item.getItem() == Item.getItemFromBlock(Blocks.bedrock)) continue; + String displayName = Utils.cleanColour(item.getDisplayName()); + Matcher matcher = PROFILE_PATTERN.matcher(displayName); + if (!matcher.matches()) continue; + String type = matcher.group("type"); + String name = matcher.group("name"); + Gamemode gamemode = Gamemode.find(type); + gamemodes.put(name, gamemode); + } + areGamemodesLoaded = true; + saveGameModes(); + } + + private Path getProfilesFile() { + return new File(NotEnoughUpdates.INSTANCE.manager.configLocation, "profiles.json").toPath(); + } + + public Map getAllGamemodes() { + if (!areGamemodesLoaded) + loadGameModes(); + return gamemodes; + } + + public void saveGameModes() { + try { + Files.write(getProfilesFile(), NotEnoughUpdates.INSTANCE.manager.gson.toJson(gamemodes).getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public Gamemode getGamemodeForProfile(String profiles) { + return getAllGamemodes().get(profiles); + } + + public Gamemode getCurrentMode() { + return getGamemodeForProfile(currentProfile); + } + + public void loadGameModes() { + try { + gamemodes = NotEnoughUpdates.INSTANCE.manager.gson.fromJson(Files.newBufferedReader(getProfilesFile()), new TypeToken>() { + }.getType()); + areGamemodesLoaded = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + @SubscribeEvent public void onWorldLoad(WorldEvent.Load event) { lastLocRaw = -1; diff --git a/src/main/resources/assets/notenoughupdates/dreamskin.png b/src/main/resources/assets/notenoughupdates/dreamskin.png new file mode 100644 index 00000000..067d396e Binary files /dev/null and b/src/main/resources/assets/notenoughupdates/dreamskin.png differ diff --git a/src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.png b/src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.png new file mode 100644 index 00000000..42b6241c Binary files /dev/null and b/src/main/resources/assets/notenoughupdates/textures/gui/villager_recipe.png differ diff --git a/src/main/resources/mixins.notenoughupdates.json b/src/main/resources/mixins.notenoughupdates.json index bec344ec..a566ee2e 100644 --- a/src/main/resources/mixins.notenoughupdates.json +++ b/src/main/resources/mixins.notenoughupdates.json @@ -3,40 +3,41 @@ "refmap": "mixins.notenoughupdates.refmap.json", "compatibilityLevel": "JAVA_8", "mixins": [ - "MixinMinecraft", - "MixinAbstractClientPlayer", - "MixinContainer", - "MixinEffectRenderer", - "MixinEntityPlayer", - "MixinEntityPlayerSP", - "MixinEntityRenderer", - "MixinGuiChest", - "MixinGuiContainer", - "MixinGuiIngame", - "MixinGuiInventory", - "MixinGuiScreen", - "MixinInventoryEffectRenderer", - "MixinInventoryPlayer", - "MixinItemCameraTransforms", - "MixinItemRenderer", - "MixinItemStack", - "MixinLayerArmorBase", - "MixinLayerCustomHead", - - "MixinMouseHelper", - "MixinNetHandlerPlayClient", - "MixinPlayerControllerMP", - "MixinRender", - "MixinRendererLivingEntity", - "MixinRenderFish", - "MixinRenderGlobal", - "MixinRenderItem", - "MixinRenderList", - "MixinTextureManager", - "MixinTileEntitySkullRenderer", - "MixinTileEntitySpecialRenderer", - "MixinVboRenderList", - "MixinWorld", - "MixinWorldClient" + "MixinAbstractClientPlayer", + "MixinContainer", + "MixinEffectRenderer", + "MixinEntity", + "MixinEntityAgeable", + "MixinEntityPlayer", + "MixinEntityPlayerSP", + "MixinEntityRenderer", + "MixinGuiChest", + "MixinGuiContainer", + "MixinGuiIngame", + "MixinGuiInventory", + "MixinGuiScreen", + "MixinInventoryEffectRenderer", + "MixinInventoryPlayer", + "MixinItemCameraTransforms", + "MixinItemRenderer", + "MixinItemStack", + "MixinLayerArmorBase", + "MixinLayerCustomHead", + "MixinMinecraft", + "MixinMouseHelper", + "MixinNetHandlerPlayClient", + "MixinPlayerControllerMP", + "MixinRender", + "MixinRendererLivingEntity", + "MixinRenderFish", + "MixinRenderGlobal", + "MixinRenderItem", + "MixinRenderList", + "MixinTextureManager", + "MixinTileEntitySkullRenderer", + "MixinTileEntitySpecialRenderer", + "MixinVboRenderList", + "MixinWorld", + "MixinWorldClient" ] -} \ No newline at end of file +} -- cgit