diff options
author | Roman / Nea <roman.graef@gmail.com> | 2022-04-18 17:33:32 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-18 17:33:32 +0200 |
commit | 2692193e54e4dd6c0117dcdb85368dc83bb04f1a (patch) | |
tree | 96f01454c404ac04a46ea0d9bf684c7dc5619a57 /src/main/java | |
parent | 9fe86ccb4d30b78826e513a6576027ca6e4c1600 (diff) | |
download | NotEnoughUpdates-2692193e54e4dd6c0117dcdb85368dc83bb04f1a.tar.gz NotEnoughUpdates-2692193e54e4dd6c0117dcdb85368dc83bb04f1a.tar.bz2 NotEnoughUpdates-2692193e54e4dd6c0117dcdb85368dc83bb04f1a.zip |
Mob loot recipe PR (#81)
* entity renderer (somewhat functionaL)
* more modifiers and entities
* Fix cookie fuckup
* add neu repo as resource pack, cause why not at this point
* add tabs, because i can
* add extra skin parts and make less tabs
* hot tall men
* fix texture offsets and also parts:true
* some untested changes
* still broken, but better (just like me (stop being edgy nea ( no u ))))
* stuff (with er skeletons
* niceities
* skytils interop
* horseys
* horseys ouch
* panos
* stupid tests :angery:
* NPE
* add drop chance
* colored leather armo
* finish off
* move shit into hover cause items look pretty terrible
* Update 2.1.md
* better recipe display name
* always show mobs toggle
* moving parts
Diffstat (limited to 'src/main/java')
41 files changed, 2143 insertions, 612 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java index abe0bdcf..6572431b 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java @@ -895,12 +895,12 @@ public class NEUManager { String clickcommand = item.get("clickcommand").getAsString(); switch (clickcommand.intern()) { case "viewrecipe": - displayGuiItemRecipe(internalName, null); + displayGuiItemRecipe(internalName); break; case "viewoption": neu.sendChatMessage("/viewpotion " + internalName.split(";")[0].toLowerCase(Locale.ROOT)); } - displayGuiItemRecipe(internalName, ""); + displayGuiItemRecipe(internalName); } public void showRecipe(String internalName) { @@ -990,16 +990,16 @@ public class NEUManager { List<NeuRecipe> usages = getAvailableUsagesFor(internalName); if (usages.isEmpty()) return false; Minecraft.getMinecraft().displayGuiScreen( - new GuiItemRecipe("Item Usages", usages, this)); + new GuiItemRecipe(usages, this)); return true; } - public boolean displayGuiItemRecipe(String internalName, String text) { + public boolean displayGuiItemRecipe(String internalName) { if (!recipesMap.containsKey(internalName)) return false; List<NeuRecipe> recipes = getAvailableRecipesFor(internalName); if (recipes.isEmpty()) return false; Minecraft.getMinecraft().displayGuiScreen( - new GuiItemRecipe(text != null ? text : "Item Recipe", recipes, this)); + new GuiItemRecipe(recipes, this)); return true; } @@ -1010,7 +1010,7 @@ public class NEUManager { public boolean failViewItem(String text) { if (viewItemAttemptID != null && !viewItemAttemptID.isEmpty()) { if (System.currentTimeMillis() - viewItemAttemptTime < 500) { - return displayGuiItemRecipe(viewItemAttemptID, text); + return displayGuiItemRecipe(viewItemAttemptID); } } return false; @@ -1570,4 +1570,10 @@ public class NEUManager { } } } + + public ItemStack createItem(String internalname) { + JsonObject jsonObject = itemMap.get(internalname); + if (jsonObject == null) return null; + return jsonToStack(jsonObject); + } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java index 7965abae..8d0955bb 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java @@ -1358,7 +1358,7 @@ public class NEUOverlay extends Gui { } if (getSortMode() == SORT_MODE_ALL) { - return !internalname.matches(mobRegex); + return NotEnoughUpdates.INSTANCE.config.itemlist.alwaysShowMonsters || !internalname.matches(mobRegex); } else if (getSortMode() == SORT_MODE_MOB) { return internalname.matches(mobRegex); } else if (getSortMode() == SORT_MODE_PET) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEURepoResourcePack.java b/src/main/java/io/github/moulberry/notenoughupdates/NEURepoResourcePack.java new file mode 100644 index 00000000..2a5cda92 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEURepoResourcePack.java @@ -0,0 +1,75 @@ +package io.github.moulberry.notenoughupdates; + +import com.google.gson.JsonObject; +import net.minecraft.client.resources.IResourcePack; +import net.minecraft.client.resources.data.IMetadataSection; +import net.minecraft.client.resources.data.IMetadataSerializer; +import net.minecraft.util.ResourceLocation; + +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.HashSet; +import java.util.Set; + +public class NEURepoResourcePack implements IResourcePack { + + File repoLocation; + Set<String> resourceDomains = new HashSet<>(); + + public NEURepoResourcePack(File repoLocation, String domain) { + this.repoLocation = repoLocation; + resourceDomains.add(domain); + } + + public boolean loadRepoLocation() { + if (repoLocation != null) return true; + NotEnoughUpdates instance = NotEnoughUpdates.INSTANCE; + if (instance == null) return false; + NEUManager manager = instance.manager; + if (manager == null) return false; + repoLocation = manager.repoLocation; + return repoLocation != null; + } + + public File getFileForResource(ResourceLocation loc) { + if (repoLocation == null) { + if (!loadRepoLocation()) + return null; + } + if (!"neurepo".equals(loc.getResourceDomain())) { + return null; + } + return new File(repoLocation, loc.getResourcePath()); + } + + @Override + public InputStream getInputStream(ResourceLocation resourceLocation) throws IOException { + return new BufferedInputStream(new FileInputStream(getFileForResource(resourceLocation))); + } + + @Override + public boolean resourceExists(ResourceLocation resourceLocation) { + File file = getFileForResource(resourceLocation); + return file != null && file.exists(); + } + + @Override + public Set<String> getResourceDomains() { + return resourceDomains; + } + + @Override + public <T extends IMetadataSection> T getPackMetadata(IMetadataSerializer iMetadataSerializer, String s) throws IOException { + return iMetadataSerializer.parseMetadataSection(s, new JsonObject()); + } + + @Override + public BufferedImage getPackImage() throws IOException { + return null; + } + + @Override + public String getPackName() { + return "NEU Repo Resources"; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 0a77c677..6b2fd09e 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -35,6 +35,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.Custom import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.DwarvenMinesTextures; import io.github.moulberry.notenoughupdates.miscgui.CalendarOverlay; import io.github.moulberry.notenoughupdates.miscgui.InventoryStorageSelector; +import io.github.moulberry.notenoughupdates.mixins.AccessorMinecraft; import io.github.moulberry.notenoughupdates.options.NEUConfig; import io.github.moulberry.notenoughupdates.overlays.FuelBar; import io.github.moulberry.notenoughupdates.overlays.OverlayManager; @@ -58,6 +59,7 @@ import net.minecraft.world.biome.BiomeGenJungle; import net.minecraft.world.biome.BiomeGenMesa; import net.minecraft.world.biome.BiomeGenSnow; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.client.FMLClientHandler; import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; @@ -160,6 +162,13 @@ public class NotEnoughUpdates { return this.neuDir; } + public NotEnoughUpdates() { + // Budget Construction Event + ((AccessorMinecraft) FMLClientHandler.instance().getClient()) + .onGetDefaultResourcePacks() + .add(new NEURepoResourcePack(null, "neurepo")); + } + /** * Instantiates NEUIo, NEUManager and NEUOverlay instances. Registers keybinds and adds a shutdown hook to clear tmp folder. */ diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/EntityViewerCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/EntityViewerCommand.java new file mode 100644 index 00000000..c7b1862e --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/EntityViewerCommand.java @@ -0,0 +1,81 @@ +package io.github.moulberry.notenoughupdates.commands; + +import com.google.common.collect.Lists; +import io.github.moulberry.notenoughupdates.miscfeatures.entityviewer.EntityViewer; +import net.minecraft.client.Minecraft; +import net.minecraft.command.CommandException; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.event.ClickEvent; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +import java.util.Arrays; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedDeque; + +public class EntityViewerCommand extends ClientCommandBase { + public EntityViewerCommand() { + super("neushowentity"); + MinecraftForge.EVENT_BUS.register(this); + } + + @Override + public List<String> getCommandAliases() { + return Lists.newArrayList("neuentityviewer"); + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return EnumChatFormatting.RED + "Use /neushowentity list"; + } + + public void showUsage(ICommandSender sender) { + sender.addChatMessage(new ChatComponentText(getCommandUsage(sender))); + } + + private final Queue<EntityViewer> queuedGUIS = new ConcurrentLinkedDeque<>(); + + @SubscribeEvent + public void onTick(TickEvent event) { + if (Minecraft.getMinecraft().currentScreen == null) { + EntityViewer poll = queuedGUIS.poll(); + if (poll == null) return; + Minecraft.getMinecraft().displayGuiScreen(poll); + } + } + + @Override + public void processCommand(ICommandSender sender, String[] strings) throws CommandException { + if (strings.length == 0) { + showUsage(sender); + return; + } + if (strings[0].equals("list")) { + for (String label : EntityViewer.validEntities.keySet()) { + sender.addChatMessage(new ChatComponentText(EnumChatFormatting.BLUE + " " + label) + .setChatStyle(new ChatStyle().setChatClickEvent( + new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/neuentityviewer " + label)))); + } + return; + } + EntityLivingBase entityLivingBase; + if (strings[0].startsWith("@")) { + ResourceLocation resourceLocation = new ResourceLocation(strings[0].substring(1)); + entityLivingBase = EntityViewer.constructEntity(resourceLocation); + } else { + entityLivingBase = EntityViewer.constructEntity(strings[0], Arrays.copyOfRange(strings, 1, strings.length)); + } + if (entityLivingBase == null) { + sender.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "Could not create that entity")); + return; + } + queuedGUIS.add(new EntityViewer(strings[0], entityLivingBase)); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java index 53a7894b..27944c92 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java @@ -1,4 +1,4 @@ -package io.github.moulberry.notenoughupdates.commands.dev; + package io.github.moulberry.notenoughupdates.commands.dev; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.commands.ClientCommandBase; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/AgeModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/AgeModifier.java new file mode 100644 index 00000000..5884512f --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/AgeModifier.java @@ -0,0 +1,30 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonObject; +import io.github.moulberry.notenoughupdates.mixins.AccessorEntityAgeable; +import io.github.moulberry.notenoughupdates.mixins.AccessorEntityArmorStand; +import net.minecraft.entity.EntityAgeable; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.item.EntityArmorStand; +import net.minecraft.entity.monster.EntityZombie; + +public class AgeModifier extends EntityViewerModifier { + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + boolean baby = info.has("baby") && info.get("baby").getAsBoolean(); + if (base instanceof EntityAgeable) { + ((AccessorEntityAgeable) base).setGrowingAgeDirect(baby ? -1 : 1); + return base; + } + if (base instanceof EntityZombie) { + ((EntityZombie) base).setChild(baby); + return base; + } + if (base instanceof EntityArmorStand) { + ((AccessorEntityArmorStand) base).setSmallDirect(baby); + return base; + } + System.out.println("Cannot apply age to a non ageable entity: " + base); + return null; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/ChargedModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/ChargedModifier.java new file mode 100644 index 00000000..17dce66d --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/ChargedModifier.java @@ -0,0 +1,17 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonObject; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; + +public class ChargedModifier extends EntityViewerModifier { + + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + if (base instanceof EntityCreeper) { + base.getDataWatcher().updateObject(17, (byte) 1); + return base; + } + return null; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EntityViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EntityViewer.java new file mode 100644 index 00000000..e9075e47 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EntityViewer.java @@ -0,0 +1,177 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.util.Utils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiInventory; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.boss.EntityDragon; +import net.minecraft.entity.boss.EntityWither; +import net.minecraft.entity.item.EntityArmorStand; +import net.minecraft.entity.monster.*; +import net.minecraft.entity.passive.*; +import net.minecraft.util.ResourceLocation; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class EntityViewer extends GuiScreen { + + public static Map<String, Supplier<? extends EntityLivingBase>> validEntities = new HashMap<String, Supplier<? extends EntityLivingBase>>() {{ + put("Zombie", () -> new EntityZombie(null)); + put("Chicken", () -> new EntityChicken(null)); + put("Slime", () -> new EntitySlime(null)); + put("Wolf", () -> new EntityWolf(null)); + put("Skeleton", () -> new EntitySkeleton(null)); + put("Creeper", () -> new EntityCreeper(null)); + put("Ocelot", () -> new EntityOcelot(null)); + put("Blaze", () -> new EntityBlaze(null)); + put("Rabbit", () -> new EntityRabbit(null)); + put("Sheep", () -> new EntitySheep(null)); + put("Horse", () -> new EntityHorse(null)); + put("Eisengolem", () -> new EntityIronGolem(null)); + put("Silverfish", () -> new EntitySilverfish(null)); + put("Witch", () -> new EntityWitch(null)); + put("Endermite", () -> new EntityEndermite(null)); + put("Snowman", () -> new EntitySnowman(null)); + put("Villager", () -> new EntityVillager(null)); + put("Guardian", () -> new EntityGuardian(null)); + put("ArmorStand", () -> new EntityArmorStand(null)); + put("Squid", () -> new EntitySquid(null)); + put("Bat", () -> new EntityBat(null)); + put("Spider", () -> new EntitySpider(null)); + put("CaveSpider", () -> new EntityCaveSpider(null)); + put("Pigman", () -> new EntityPigZombie(null)); + put("Ghast", () -> new EntityGhast(null)); + put("MagmaCube", () -> new EntityMagmaCube(null)); + put("Wither", () -> new EntityWither(null)); + put("Enderman", () -> new EntityEnderman(null)); + put("Mooshroom", ()-> new EntityMooshroom(null)); + put("WitherSkeleton", () -> { + EntitySkeleton skeleton = new EntitySkeleton(null); + skeleton.setSkeletonType(1); + return skeleton; + }); + put("Cow", () -> new EntityCow(null)); + put("Dragon", ()-> new EntityDragon(null)); + put("Player", () -> new GUIClientPlayer()); + }}; + + public static Map<String, EntityViewerModifier> validModifiers = new HashMap<String, EntityViewerModifier>() {{ + put("playerdata", new SkinModifier()); + put("equipment", new EquipmentModifier()); + put("riding", new RidingModifier()); + put("charged", new ChargedModifier()); + put("witherdata", new WitherModifier()); + put("invisible", new InvisibleModifier()); + put("age", new AgeModifier()); + put("horse", new HorseModifier()); + }}; + + public int guiLeft = 0; + public int guiTop = 0; + public int xSize = 176; + public int ySize = 166; + + private final String label; + private final EntityLivingBase entity; + private static final ResourceLocation BACKGROUND = new ResourceLocation("notenoughupdates", "textures/gui/entity_viewer.png"); + + public EntityViewer(String label, EntityLivingBase entity) { + this.label = label; + this.entity = entity; + } + + public static EntityLivingBase constructEntity(ResourceLocation resourceLocation) { + Gson gson = NotEnoughUpdates.INSTANCE.manager.gson; + try (Reader is = new InputStreamReader( + Minecraft.getMinecraft().getResourceManager().getResource(resourceLocation).getInputStream(), StandardCharsets.UTF_8)) { + return constructEntity(gson.fromJson(is, JsonObject.class)); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static EntityLivingBase constructEntity(JsonObject info) { + List<JsonObject> modifiers = info.has("modifiers") ? + StreamSupport.stream(info.get("modifiers").getAsJsonArray().spliterator(), false) + .map(JsonElement::getAsJsonObject).collect(Collectors.toList()) + : Collections.emptyList(); + return EntityViewer.constructEntity(info.get("entity").getAsString(), modifiers); + } + + public static EntityLivingBase constructEntity(String string, String[] modifiers) { + Gson gson = NotEnoughUpdates.INSTANCE.manager.gson; + return constructEntity(string, Arrays.stream(modifiers).map(it -> gson.fromJson(it, JsonObject.class)).collect(Collectors.toList())); + } + + public static EntityLivingBase constructEntity(String string, List<JsonObject> modifiers) { + Supplier<? extends EntityLivingBase> aClass = validEntities.get(string); + if (aClass == null) { + System.err.println("Could not find entity of type: " + string); + return null; + } + try { + EntityLivingBase entity = aClass.get(); + for (JsonObject modifier : modifiers) { + String type = modifier.get("type").getAsString(); + EntityViewerModifier entityViewerModifier = validModifiers.get(type); + entity = entityViewerModifier.applyModifier(entity, modifier); + if (entity == null) break; + } + return entity; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + drawDefaultBackground(); + FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj; + + this.guiLeft = (width - this.xSize) / 2; + this.guiTop = (height - this.ySize) / 2; + + + Minecraft.getMinecraft().getTextureManager().bindTexture(BACKGROUND); + drawTexturedModalRect(guiLeft, guiTop, 0, 0, this.xSize, this.ySize); + + + Utils.drawStringScaledMaxWidth(label, fontRenderer, guiLeft + 10, guiTop + 10, false, 100, 0xFF00FF); + renderEntity(entity, guiLeft + 90, guiTop + 75, mouseX, mouseY); + } + + public static void renderEntity(EntityLivingBase entity, int posX, int posY, int mouseX, int mouseY) { + GlStateManager.color(1F, 1F, 1F, 1F); + + int scale = 30; + float bottomOffset = 0F; + EntityLivingBase stack = entity; + while (true) { + + stack.ticksExisted = Minecraft.getMinecraft().thePlayer.ticksExisted; + GuiInventory.drawEntityOnScreen(posX, (int) (posY - bottomOffset * scale), scale, posX - mouseX, (int) (posY - stack.getEyeHeight() * scale - mouseY), stack); + bottomOffset += stack.getMountedYOffset(); + if (!(stack.riddenByEntity instanceof EntityLivingBase)) { + break; + } + stack = (EntityLivingBase) stack.riddenByEntity; + } + + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EntityViewerModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EntityViewerModifier.java new file mode 100644 index 00000000..bab6c354 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EntityViewerModifier.java @@ -0,0 +1,8 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonObject; +import net.minecraft.entity.EntityLivingBase; + +public abstract class EntityViewerModifier { + public abstract EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info); +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EquipmentModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EquipmentModifier.java new file mode 100644 index 00000000..3011e479 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/EquipmentModifier.java @@ -0,0 +1,72 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonObject; +import io.github.moulberry.notenoughupdates.NEUManager; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; + +public class EquipmentModifier extends EntityViewerModifier { + + private ItemStack createItem(String item) { + NEUManager manager = NotEnoughUpdates.INSTANCE.manager; + String[] split = item.split("#"); + if (split.length == 2) { + switch (split[0].intern()) { + case "LEATHER_LEGGINGS": + return coloredLeatherArmor(Items.leather_leggings, split[1]); + case "LEATHER_HELMET": + return coloredLeatherArmor(Items.leather_helmet, split[1]); + case "LEATHER_CHESTPLATE": + return coloredLeatherArmor(Items.leather_chestplate, split[1]); + case "LEATHER_BOOTS": + return coloredLeatherArmor(Items.leather_boots, split[1]); + default: + throw new RuntimeException("Unknown leather piece: " + item); + } + } + return manager.createItem(item); + } + + private ItemStack coloredLeatherArmor(ItemArmor item, String colorHex) { + ItemStack is = new ItemStack(item); + item.setColor(is, Integer.parseInt(colorHex, 16)); + return is; + } + + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + if (info.has("hand")) + setCurrentItemOrArmor(base, 0, createItem(info.get("hand").getAsString())); + if (info.has("helmet")) + setCurrentItemOrArmor(base, 4, createItem(info.get("helmet").getAsString())); + if (info.has("chestplate")) + setCurrentItemOrArmor(base, 3, createItem(info.get("chestplate").getAsString())); + if (info.has("leggings")) + setCurrentItemOrArmor(base, 2, createItem(info.get("leggings").getAsString())); + if (info.has("feet")) + setCurrentItemOrArmor(base, 1, createItem(info.get("feet").getAsString())); + return base; + } + + public void setCurrentItemOrArmor(EntityLivingBase entity, int slot, ItemStack itemStack) { + if (entity instanceof EntityPlayer) { + setPlayerCurrentItemOrArmor((EntityPlayer) entity, slot, itemStack); + } else { + entity.setCurrentItemOrArmor(slot, itemStack); + } + } + + // Biscuit person needs to learn how to code and not fuck up valid vanilla behaviour + public static void setPlayerCurrentItemOrArmor(EntityPlayer player, int slot, ItemStack itemStack) { + if (slot == 0) { + player.inventory.mainInventory[player.inventory.currentItem] = itemStack; + } else { + player.inventory.armorInventory[slot - 1] = itemStack; + } + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/GUIClientPlayer.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/GUIClientPlayer.java new file mode 100644 index 00000000..bbff2db1 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/GUIClientPlayer.java @@ -0,0 +1,40 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.mojang.authlib.GameProfile; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.network.NetworkPlayerInfo; +import net.minecraft.client.resources.DefaultPlayerSkin; +import net.minecraft.util.ResourceLocation; + +import java.util.UUID; + +public class GUIClientPlayer extends AbstractClientPlayer { + public GUIClientPlayer() { + super(null, new GameProfile(UUID.randomUUID(), "GuiPlayer")); + } + + ResourceLocation overrideSkin = DefaultPlayerSkin.getDefaultSkinLegacy(); + ResourceLocation overrideCape = null; + boolean overrideIsSlim = false; + NetworkPlayerInfo playerInfo = new NetworkPlayerInfo(this.getGameProfile()) { + @Override + public String getSkinType() { + return overrideIsSlim ? "slim" : "default"; + } + + @Override + public ResourceLocation getLocationSkin() { + return overrideSkin; + } + + @Override + public ResourceLocation getLocationCape() { + return overrideCape; + } + }; + + @Override + protected NetworkPlayerInfo getPlayerInfo() { + return playerInfo; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/HorseModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/HorseModifier.java new file mode 100644 index 00000000..7fd2dadd --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/HorseModifier.java @@ -0,0 +1,66 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.passive.EntityHorse; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +public class HorseModifier extends EntityViewerModifier { + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + if (!(base instanceof EntityHorse)) + return null; + EntityHorse horse = (EntityHorse) base; + if (info.has("kind")) { + String type = info.get("kind").getAsString().intern(); + switch (type) { + case "skeleton": + horse.setHorseType(4); + break; + case "zombie": + horse.setHorseType(3); + break; + case "mule": + horse.setHorseType(2); + break; + case "donkey": + horse.setHorseType(1); + break; + case "horse": + horse.setHorseType(0); + break; + default: + throw new IllegalArgumentException("Unknown horse type: " + type); + } + } + if (info.has("armor")) { + JsonElement el = info.get("armor"); + if (el.isJsonNull()) { + horse.setHorseArmorStack(null); + } else { + Item item; + switch (el.getAsString().intern()) { + case "iron": + item = Items.iron_horse_armor; + break; + case "golden": + item = Items.golden_horse_armor; + break; + case "diamond": + item = Items.diamond_horse_armor; + break; + default: + throw new IllegalArgumentException("Unknown horse armor: " + el.getAsString()); + } + horse.setHorseArmorStack(new ItemStack(item)); + } + } + if (info.has("saddled")) { + horse.setHorseSaddled(info.get("saddled").getAsBoolean()); + } + return horse; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/InvisibleModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/InvisibleModifier.java new file mode 100644 index 00000000..c2138b3f --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/InvisibleModifier.java @@ -0,0 +1,12 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonObject; +import net.minecraft.entity.EntityLivingBase; + +public class InvisibleModifier extends EntityViewerModifier { + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + base.setInvisible(!info.has("invisible") || info.get("invisible").getAsBoolean()); + return base; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/RidingModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/RidingModifier.java new file mode 100644 index 00000000..9879542a --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/RidingModifier.java @@ -0,0 +1,14 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonObject; +import net.minecraft.entity.EntityLivingBase; + +public class RidingModifier extends EntityViewerModifier { + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + EntityLivingBase newEntity = EntityViewer.constructEntity(info); + if (newEntity == null) return null; + newEntity.mountEntity(base); + return base; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/SkinModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/SkinModifier.java new file mode 100644 index 00000000..55e8a432 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/SkinModifier.java @@ -0,0 +1,46 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EnumPlayerModelParts; +import net.minecraft.util.ResourceLocation; + +import java.util.Map; + +public class SkinModifier extends EntityViewerModifier { + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + if (base instanceof GUIClientPlayer) { + GUIClientPlayer player = (GUIClientPlayer) base; + if (info.has("cape")) { + player.overrideCape = new ResourceLocation(info.get("cape").getAsString()); + } + if (info.has("skin")) { + player.overrideSkin = new ResourceLocation(info.get("skin").getAsString()); + } + if (info.has("slim")) { + player.overrideIsSlim = info.get("slim").getAsBoolean(); + } + if (info.has("parts")) { + JsonElement parts = info.get("parts"); + byte partBitField = player.getDataWatcher().getWatchableObjectByte(10); + if (parts.isJsonPrimitive() && parts.getAsJsonPrimitive().isBoolean()) { + partBitField = parts.getAsBoolean() ? (byte) -1 : 0; + } else { + JsonObject obj = parts.getAsJsonObject(); + for (Map.Entry<String, JsonElement> part : obj.entrySet()) { + EnumPlayerModelParts modelPart = EnumPlayerModelParts.valueOf(part.getKey()); + if (part.getValue().getAsBoolean()) { + partBitField |= modelPart.getPartMask(); + } else { + partBitField &= ~modelPart.getPartMask(); + } + } + } + player.getDataWatcher().updateObject(10, partBitField); + } + } + return base; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/WitherModifier.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/WitherModifier.java new file mode 100644 index 00000000..c5580f17 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/entityviewer/WitherModifier.java @@ -0,0 +1,29 @@ +package io.github.moulberry.notenoughupdates.miscfeatures.entityviewer; + +import com.google.gson.JsonObject; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.boss.EntityWither; + +public class WitherModifier extends EntityViewerModifier { + @Override + public EntityLivingBase applyModifier(EntityLivingBase base, JsonObject info) { + if (!(base instanceof EntityWither)) + return null; + EntityWither wither = (EntityWither) base; + if (info.has("tiny")) { + if (info.get("tiny").getAsBoolean()) { + wither.setInvulTime(800); + } else { + wither.setInvulTime(0); + } + } + if (info.has("armored")) { + if (info.get("armored").getAsBoolean()) { + wither.setHealth(1); + } else { + wither.setHealth(wither.getMaxHealth()); + } + } + return base; + } +} 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 63a4d6d8..be9ce6c7 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiItemRecipe.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; import io.github.moulberry.notenoughupdates.NEUManager; import io.github.moulberry.notenoughupdates.recipes.NeuRecipe; import io.github.moulberry.notenoughupdates.recipes.RecipeSlot; +import io.github.moulberry.notenoughupdates.recipes.RecipeType; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; @@ -12,6 +13,7 @@ import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; import org.lwjgl.input.Keyboard; @@ -20,48 +22,65 @@ import org.lwjgl.opengl.GL11; import java.awt.*; import java.io.IOException; -import java.util.ArrayList; import java.util.List; +import java.util.*; public class GuiItemRecipe extends GuiScreen { public static final ResourceLocation resourcePacksTexture = new ResourceLocation("textures/gui/resource_packs.png"); + public static final ResourceLocation tabsTexture = new ResourceLocation("notenoughupdates", "textures/gui/tab.png"); public static final int SLOT_SIZE = 16; public static final int SLOT_SPACING = SLOT_SIZE + 2; public static final int BUTTON_WIDTH = 7; public static final int BUTTON_HEIGHT = 11; - public static final int BUTTON_POSITION_Y = 63; - public static final int BUTTON_POSITION_LEFT_X = 110; - public static final int BUTTON_POSITION_RIGHT_X = 147; - public static final int PAGE_STRING_X = 132; - public static final int PAGE_STRING_Y = 69; public static final int TITLE_X = 28; public static final int TITLE_Y = 6; public static final int HOTBAR_SLOT_X = 8; - public static final int HOTBAR_SLOT_Y = 142; + public static final int HOTBAR_SLOT_Y = 197; public static final int PLAYER_INVENTORY_X = 8; - public static final int PLAYER_INVENTORY_Y = 84; + public static final int PLAYER_INVENTORY_Y = 140; + public static final int TAB_POS_X = -26; + public static final int TAB_POS_Y = 8; + public static final int TAB_OFFSET_Y = 30; + public static final int TAB_SIZE_X = 26; + public static final int TAB_SIZE_Y = 30; + public static final int TAB_TEXTURE_SIZE_X = 29; private int currentIndex = 0; + private int currentTab = 0; - private final String title; - private final List<NeuRecipe> craftingRecipes; + private final Map<RecipeType, List<NeuRecipe>> craftingRecipes = new HashMap<>(); + private final List<RecipeType> tabs = new ArrayList<>(); private final NEUManager manager; public int guiLeft = 0; public int guiTop = 0; public int xSize = 176; - public int ySize = 166; + public int ySize = 222; - public GuiItemRecipe(String title, List<NeuRecipe> craftingRecipes, NEUManager manager) { - this.craftingRecipes = craftingRecipes; + public GuiItemRecipe(List<NeuRecipe> unsortedRecipes, NEUManager manager) { this.manager = manager; - this.title = title; + + for (NeuRecipe recipe : unsortedRecipes) { + craftingRecipes.computeIfAbsent(recipe.getType(), ignored -> new ArrayList<>()).add(recipe); + if (!tabs.contains(recipe.getType())) + tabs.add(recipe.getType()); + } } public NeuRecipe getCurrentRecipe() { - currentIndex = MathHelper.clamp_int(currentIndex, 0, craftingRecipes.size()); - return craftingRecipes.get(currentIndex); + List<NeuRecipe> currentRecipes = getCurrentRecipeList(); + currentIndex = MathHelper.clamp_int(currentIndex, 0, currentRecipes.size() - 1); + return currentRecipes.get(currentIndex); + } + + public List<NeuRecipe> getCurrentRecipeList() { + return craftingRecipes.get(getCurrentTab()); + } + + public RecipeType getCurrentTab() { + currentTab = MathHelper.clamp_int(currentTab, 0, tabs.size() - 1); + return tabs.get(currentTab); } public boolean isWithinRect(int x, int y, int topLeftX, int topLeftY, int width, int height) { @@ -90,17 +109,19 @@ public class GuiItemRecipe extends GuiScreen { Minecraft.getMinecraft().getTextureManager().bindTexture(currentRecipe.getBackground()); this.drawTexturedModalRect(guiLeft, guiTop, 0, 0, this.xSize, this.ySize); + drawTabs(); + currentRecipe.drawExtraBackground(this, mouseX, mouseY); List<RecipeSlot> slots = getAllRenderedSlots(); for (RecipeSlot slot : slots) { - Utils.drawItemStack(slot.getItemStack(), slot.getX(this), slot.getY(this)); + Utils.drawItemStack(slot.getItemStack(), slot.getX(this), slot.getY(this), true); } - if (craftingRecipes.size() > 1) drawArrows(mouseX, mouseY); + drawArrows(currentRecipe, mouseX, mouseY); Utils.drawStringScaledMaxWidth( - title, + currentRecipe.getTitle(), fontRendererObj, guiLeft + TITLE_X, guiTop + TITLE_Y, @@ -126,42 +147,104 @@ public class GuiItemRecipe extends GuiScreen { } } currentRecipe.drawHoverInformation(this, mouseX, mouseY); + drawTabHoverInformation(mouseX, mouseY); + } + + private void drawTabHoverInformation(int mouseX, int mouseY) { + if (tabs.size() < 2) return; + for (int i = 0; i < tabs.size(); i++) { + if (isWithinRect( + mouseX - guiLeft, + mouseY - guiTop, + TAB_POS_X, + TAB_POS_Y + TAB_OFFSET_Y * i, + TAB_SIZE_X, + TAB_SIZE_Y + )) { + RecipeType type = tabs.get(i); + Utils.drawHoveringText( + Arrays.asList( + "" + EnumChatFormatting.RESET + EnumChatFormatting.GREEN + type.getLabel(), + "" + EnumChatFormatting.RESET + EnumChatFormatting.GRAY + craftingRecipes.get(type).size() + " Recipes" + ), + mouseX, mouseY, width, height, -1, Minecraft.getMinecraft().fontRendererObj + ); + return; + } + } } - private void drawArrows(int mouseX, int mouseY) { + private void drawTabs() { + if (tabs.size() < 2) return; + for (int i = 0; i < tabs.size(); i++) { + RecipeType recipeType = tabs.get(i); + int tabPosX = guiLeft + TAB_POS_X, tabPosY = guiTop + TAB_OFFSET_Y * i + TAB_POS_Y; + int textureOffset = 0; + if (currentTab == i) { + textureOffset = 30; + } + Minecraft.getMinecraft().getTextureManager().bindTexture(tabsTexture); + drawTexturedModalRect( + tabPosX, tabPosY, + 0, textureOffset, + TAB_TEXTURE_SIZE_X, TAB_SIZE_Y + ); + Utils.drawItemStack(recipeType.getIcon(), tabPosX + 7, tabPosY + 7); + } + } + + public static final int BUTTON_POSITION_RIGHT_OFFSET_X = 37; + public static final int PAGE_STRING_OFFSET_X = 22; + public static final int PAGE_STRING_OFFSET_Y = 6; + + private void drawArrows( + NeuRecipe currentRecipe, + int mouseX, + int mouseY + ) { + int recipeCount = getCurrentRecipeList().size(); + if (recipeCount < 2) return; + int[] topLeft = currentRecipe.getPageFlipPositionLeftTopCorner(); + int buttonPositionLeftX = topLeft[0]; + int buttonPositionRightX = buttonPositionLeftX + BUTTON_POSITION_RIGHT_OFFSET_X; + int pageStringX = buttonPositionLeftX + PAGE_STRING_OFFSET_X; + int buttonPositionY = topLeft[1]; + int pageStringY = buttonPositionY + PAGE_STRING_OFFSET_Y; + boolean leftSelected = isWithinRect( mouseX - guiLeft, mouseY - guiTop, - BUTTON_POSITION_LEFT_X, - BUTTON_POSITION_Y, + buttonPositionLeftX, + buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT ); boolean rightSelected = isWithinRect( mouseX - guiLeft, mouseY - guiTop, - BUTTON_POSITION_RIGHT_X, - BUTTON_POSITION_Y, + buttonPositionRightX, + buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT ); - Minecraft.getMinecraft().getTextureManager().bindTexture(resourcePacksTexture); - Utils.drawTexturedRect(guiLeft + BUTTON_POSITION_LEFT_X, guiTop + BUTTON_POSITION_Y, BUTTON_WIDTH, BUTTON_HEIGHT, - 34 / 256f, 48 / 256f, - leftSelected ? 37 / 256f : 5 / 256f, leftSelected ? 59 / 256f : 27 / 256f - ); - Utils.drawTexturedRect(guiLeft + BUTTON_POSITION_RIGHT_X, guiTop + BUTTON_POSITION_Y, BUTTON_WIDTH, BUTTON_HEIGHT, - 10 / 256f, 24 / 256f, - rightSelected ? 37 / 256f : 5 / 256f, rightSelected ? 59 / 256f : 27 / 256f - ); + if (currentIndex != 0) + Utils.drawTexturedRect(guiLeft + buttonPositionLeftX, guiTop + buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT, + 34 / 256f, 48 / 256f, + leftSelected ? 37 / 256f : 5 / 256f, leftSelected ? 59 / 256f : 27 / 256f + ); + if (currentIndex != recipeCount - 1) + Utils.drawTexturedRect(guiLeft + buttonPositionRightX, guiTop + buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT, + 10 / 256f, 24 / 256f, + rightSelected ? 37 / 256f : 5 / 256f, rightSelected ? 59 / 256f : 27 / 256f + ); GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0); - String selectedPage = (currentIndex + 1) + "/" + craftingRecipes.size(); + String selectedPage = (currentIndex + 1) + "/" + recipeCount; Utils.drawStringCenteredScaledMaxWidth(selectedPage, fontRendererObj, - guiLeft + PAGE_STRING_X, guiTop + PAGE_STRING_Y, false, 24, Color.BLACK.getRGB() + guiLeft + pageStringX, guiTop + pageStringY, false, 24, Color.BLACK.getRGB() ); } @@ -196,12 +279,12 @@ public class GuiItemRecipe extends GuiScreen { int mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth; int mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1; int keyPressed = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey(); - + if (Keyboard.getEventKeyState()) return; for (RecipeSlot slot : getAllRenderedSlots()) { if (isWithinRect(mouseX, mouseY, slot.getX(this), slot.getY(this), SLOT_SIZE, SLOT_SIZE)) { ItemStack itemStack = slot.getItemStack(); - if (keyPressed == manager.keybindViewRecipe.getKeyCode()) { // TODO: rework this so it doesnt skip recipe chains - manager.displayGuiItemRecipe(manager.getInternalNameForItem(itemStack), ""); + if (keyPressed == manager.keybindViewRecipe.getKeyCode()) { + manager.displayGuiItemRecipe(manager.getInternalNameForItem(itemStack)); } else if (keyPressed == manager.keybindViewUsages.getKeyCode()) { manager.displayGuiItemUsages(manager.getInternalNameForItem(itemStack)); } @@ -212,16 +295,22 @@ public class GuiItemRecipe extends GuiScreen { @Override protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException { super.mouseClicked(mouseX, mouseY, mouseButton); + NeuRecipe currentRecipe = getCurrentRecipe(); + int[] topLeft = currentRecipe.getPageFlipPositionLeftTopCorner(); + int buttonPositionLeftX = topLeft[0]; + int buttonPositionRightX = buttonPositionLeftX + BUTTON_POSITION_RIGHT_OFFSET_X; + int buttonPositionY = topLeft[1]; if (isWithinRect( mouseX - guiLeft, mouseY - guiTop, - BUTTON_POSITION_LEFT_X, - BUTTON_POSITION_Y, + buttonPositionLeftX, + buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT - )) { - currentIndex = currentIndex == 0 ? 0 : currentIndex - 1; + ) && + currentIndex > 0) { + currentIndex = currentIndex - 1; Utils.playPressSound(); return; } @@ -229,21 +318,37 @@ public class GuiItemRecipe extends GuiScreen { if (isWithinRect( mouseX - guiLeft, mouseY - guiTop, - BUTTON_POSITION_RIGHT_X, - BUTTON_POSITION_Y, + buttonPositionRightX, + buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT - )) { - currentIndex = currentIndex == craftingRecipes.size() - 1 ? currentIndex : currentIndex + 1; + ) && + currentIndex < getCurrentRecipeList().size()) { + currentIndex = currentIndex + 1; Utils.playPressSound(); return; } + for (int i = 0; i < tabs.size(); i++) { + if (isWithinRect( + mouseX - guiLeft, + mouseY - guiTop, + TAB_POS_X, + TAB_POS_Y + TAB_OFFSET_Y * i, + TAB_SIZE_X, + TAB_SIZE_Y + )) { + currentTab = i; + Utils.playPressSound(); + return; + } + } + for (RecipeSlot slot : getAllRenderedSlots()) { if (isWithinRect(mouseX, mouseY, slot.getX(this), slot.getY(this), SLOT_SIZE, SLOT_SIZE)) { ItemStack itemStack = slot.getItemStack(); if (mouseButton == 0) { - manager.displayGuiItemRecipe(manager.getInternalNameForItem(itemStack), ""); + manager.displayGuiItemRecipe(manager.getInternalNameForItem(itemStack)); } else if (mouseButton == 1) { manager.displayGuiItemUsages(manager.getInternalNameForItem(itemStack)); } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorEntityAgeable.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorEntityAgeable.java new file mode 100644 index 00000000..9228f93d --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorEntityAgeable.java @@ -0,0 +1,12 @@ +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.entity.EntityAgeable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntityAgeable.class) +public interface AccessorEntityAgeable { + @Accessor(value = "growingAge") + void setGrowingAgeDirect(int newValue); + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorEntityArmorStand.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorEntityArmorStand.java new file mode 100644 index 00000000..b6a3ca6e --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorEntityArmorStand.java @@ -0,0 +1,12 @@ +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.entity.item.EntityArmorStand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(EntityArmorStand.class) +public interface AccessorEntityArmorStand { + @Invoker(value = "setSmall") + void setSmallDirect(boolean isSmall); + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorMinecraft.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorMinecraft.java new file mode 100644 index 00000000..0277a18b --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/AccessorMinecraft.java @@ -0,0 +1,14 @@ +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.IResourcePack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(Minecraft.class) +public interface AccessorMinecraft { + @Accessor(value = "defaultResourcePacks") + List<IResourcePack> onGetDefaultResourcePacks(); +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java index b8f07b53..801b9041 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityAgeable.java @@ -3,6 +3,7 @@ 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.gen.Accessor; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityHorse.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityHorse.java new file mode 100644 index 00000000..fe88922e --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntityHorse.java @@ -0,0 +1,17 @@ +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.entity.passive.EntityHorse; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(EntityHorse.class) +public class MixinEntityHorse { + @Redirect(method = "updateHorseSlots", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isRemote:Z")) + public boolean onUpdateHorseSlots(World instance) { + if (instance == null) + return true; + return instance.isRemote; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntitySkeleton.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntitySkeleton.java new file mode 100644 index 00000000..54fe53f3 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinEntitySkeleton.java @@ -0,0 +1,17 @@ +package io.github.moulberry.notenoughupdates.mixins; + +import net.minecraft.entity.monster.EntitySkeleton; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(EntitySkeleton.class) +public class MixinEntitySkeleton { + @Redirect(method = "setCurrentItemOrArmor", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isRemote:Z")) + public boolean onSetCurrentItemOrArmor(World instance) { + if (instance == null) + return true; + return instance.isRemote; + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java index 2fba6e80..68449ba8 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java @@ -150,6 +150,10 @@ public class NEUConfig extends Config { return; case 20: FairySouls.getInstance().setTrackFairySouls(NotEnoughUpdates.INSTANCE.config.misc.trackFairySouls); + return; + case 21: + NotEnoughUpdates.INSTANCE.overlay.updateSearch(); + return; } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Itemlist.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Itemlist.java index 3e5a4cdf..db154c24 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Itemlist.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Itemlist.java @@ -107,4 +107,14 @@ public class Itemlist { ) @ConfigEditorColour public String backgroundColour = "15:6:0:0:255"; + + @Expose + @ConfigOption( + name = "Always show Monsters", + desc = "Always show Monster Items in the item list" + ) + @ConfigEditorBoolean( + runnableId = 21 + ) + public boolean alwaysShowMonsters = false; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java index 11c10ea0..691ccaae 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/GuiProfileViewer.java @@ -2222,7 +2222,7 @@ public class GuiProfileViewer extends GuiScreen { } Panorama.drawPanorama(-backgroundRotation, guiLeft + 212, guiTop + 44, 81, 108, -0.37f, 0.6f, - getPanoramasForLocation(location == null ? "dynamic" : location, panoramaIdentifier) + Panorama.getPanoramasForLocation(location == null ? "dynamic" : location, panoramaIdentifier) ); Minecraft.getMinecraft().getTextureManager().bindTexture(pv_pets); @@ -3114,38 +3114,6 @@ public class GuiProfileViewer extends GuiScreen { return entityPlayer; } - public ResourceLocation[] getPanoramasForLocation(String location, String identifier) { - if (panoramasMap.containsKey(location + identifier)) return panoramasMap.get(location + identifier); - try { - ResourceLocation[] panoramasArray = new ResourceLocation[6]; - for (int i = 0; i < 6; i++) { - panoramasArray[i] = - new ResourceLocation("notenoughupdates:panoramas/" + location + "_" + identifier + "/panorama_" + i + ".jpg"); - Minecraft.getMinecraft().getResourceManager().getResource(panoramasArray[i]); - } - panoramasMap.put(location + identifier, panoramasArray); - return panoramasArray; - } catch (IOException e) { - try { - ResourceLocation[] panoramasArray = new ResourceLocation[6]; - for (int i = 0; i < 6; i++) { - panoramasArray[i] = - new ResourceLocation("notenoughupdates:panoramas/" + location + "/panorama_" + i + ".jpg"); - Minecraft.getMinecraft().getResourceManager().getResource(panoramasArray[i]); - } - panoramasMap.put(location + identifier, panoramasArray); - return panoramasArray; - } catch (IOException e2) { - ResourceLocation[] panoramasArray = new ResourceLocation[6]; - for (int i = 0; i < 6; i++) { - panoramasArray[i] = new ResourceLocation("notenoughupdates:panoramas/unknown/panorama_" + i + ".jpg"); - } - panoramasMap.put(location + identifier, panoramasArray); - return panoramasArray; - } - } - } - private void drawExtraPage(int mouseX, int mouseY, float partialTicks) { FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; @@ -4717,7 +4685,7 @@ public class GuiProfileViewer extends GuiScreen { } Panorama.drawPanorama(-backgroundRotation - extraRotation, guiLeft + 23, guiTop + 44, 81, 108, 0.37f, 0.8f, - getPanoramasForLocation(location == null ? "unknown" : location, panoramaIdentifier) + Panorama.getPanoramasForLocation(location == null ? "unknown" : location, panoramaIdentifier) ); Minecraft.getMinecraft().getTextureManager().bindTexture(pv_basic); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/Panorama.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/Panorama.java index ac117bc2..a2d297f5 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/Panorama.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/Panorama.java @@ -15,6 +15,9 @@ import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.Project; +import java.io.IOException; +import java.util.HashMap; + public class Panorama { private static final TexLoc tl = new TexLoc(97, 19, Keyboard.KEY_P); private static final TexLoc tl2 = new TexLoc(37, 80, Keyboard.KEY_L); @@ -24,6 +27,41 @@ public class Panorama { private static int lastWidth = 0; private static int lastHeight = 0; + + private static final HashMap<String, ResourceLocation[]> panoramasMap = new HashMap<>(); + + public static synchronized ResourceLocation[] getPanoramasForLocation(String location, String identifier) { + if (panoramasMap.containsKey(location + identifier)) return panoramasMap.get(location + identifier); + try { + ResourceLocation[] panoramasArray = new ResourceLocation[6]; + for (int i = 0; i < 6; i++) { + panoramasArray[i] = + new ResourceLocation("notenoughupdates:panoramas/" + location + "_" + identifier + "/panorama_" + i + ".jpg"); + Minecraft.getMinecraft().getResourceManager().getResource(panoramasArray[i]); + } + panoramasMap.put(location + identifier, panoramasArray); + return panoramasArray; + } catch (IOException e) { + try { + ResourceLocation[] panoramasArray = new ResourceLocation[6]; + for (int i = 0; i < 6; i++) { + panoramasArray[i] = + new ResourceLocation("notenoughupdates:panoramas/" + location + "/panorama_" + i + ".jpg"); + Minecraft.getMinecraft().getResourceManager().getResource(panoramasArray[i]); + } + panoramasMap.put(location + identifier, panoramasArray); + return panoramasArray; + } catch (IOException e2) { + ResourceLocation[] panoramasArray = new ResourceLocation[6]; + for (int i = 0; i < 6; i++) { + panoramasArray[i] = new ResourceLocation("notenoughupdates:panoramas/unknown/panorama_" + i + ".jpg"); + } + panoramasMap.put(location + identifier, panoramasArray); + return panoramasArray; + } + } + } + public static void drawPanorama( float angle, int x, diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingOverlay.java index d0b464f6..9d305e1d 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingOverlay.java @@ -103,7 +103,7 @@ public class CraftingOverlay { if (Keyboard.getEventKey() == Keyboard.KEY_R) manager.showRecipe(recipeIngredient.getInternalItemId()); if (Keyboard.getEventKey() == Keyboard.KEY_U) - manager.displayGuiItemRecipe(recipeIngredient.getInternalItemId(), null); + manager.displayGuiItemRecipe(recipeIngredient.getInternalItemId()); } }); }); 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 576cbbd4..79f349fa 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/CraftingRecipe.java @@ -17,130 +17,135 @@ import java.util.Set; public class CraftingRecipe implements NeuRecipe { - public static final ResourceLocation BACKGROUND = new ResourceLocation("textures/gui/container/crafting_table.png"); - - private static final int EXTRA_STRING_X = 132; - private static final int EXTRA_STRING_Y = 25; - - private final NEUManager manager; - private final Ingredient[] inputs; - private final String extraText; - private final Ingredient outputIngredient; - private List<RecipeSlot> slots; - - public CraftingRecipe(NEUManager manager, Ingredient[] inputs, Ingredient output, String extra) { - this.manager = manager; - this.inputs = inputs; - this.outputIngredient = output; - this.extraText = extra; - if (inputs.length != 9) - throw new IllegalArgumentException("Cannot construct crafting recipe with non standard crafting grid size"); - } - - @Override - public Set<Ingredient> getIngredients() { - Set<Ingredient> ingredients = Sets.newHashSet(inputs); - ingredients.remove(null); - return ingredients; - } - - @Override - public boolean hasVariableCost() { - return false; - } - - @Override - public Set<Ingredient> getOutputs() { - return Collections.singleton(getOutput()); - } - - public Ingredient getOutput() { - return outputIngredient; - } - - public Ingredient[] getInputs() { - return inputs; - } - - @Override - public List<RecipeSlot> getSlots() { - if (slots != null) return slots; - slots = new ArrayList<>(); - for (int x = 0; x < 3; x++) { - for (int y = 0; y < 3; y++) { - Ingredient input = inputs[x + y * 3]; - if (input == null) continue; - ItemStack item = input.getItemStack(); - if (item == null) continue; - slots.add(new RecipeSlot(30 + x * GuiItemRecipe.SLOT_SPACING, 17 + y * GuiItemRecipe.SLOT_SPACING, item)); - } - } - slots.add(new RecipeSlot(124, 35, outputIngredient.getItemStack())); - return slots; - } - - public String getCraftText() { - return extraText; - } - - @Override - public ResourceLocation getBackground() { - return BACKGROUND; - } - - @Override - public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { - FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj; - - String craftingText = getCraftText(); - if (craftingText != null) - Utils.drawStringCenteredScaledMaxWidth(craftingText, fontRenderer, - gui.guiLeft + EXTRA_STRING_X, gui.guiTop + EXTRA_STRING_Y, false, 75, 0x404040 - ); - } - - @Override - public JsonObject serialize() { - JsonObject object = new JsonObject(); - object.addProperty("type", "crafting"); - object.addProperty("count", outputIngredient.getCount()); - object.addProperty("overrideOutputId", outputIngredient.getInternalItemId()); - for (int i = 0; i < 9; i++) { - Ingredient ingredient = inputs[i]; - if (ingredient == null) continue; - String[] x = {"1", "2", "3"}; - String[] y = {"A", "B", "C"}; - String name = x[i / 3] + y[i % 3]; - object.addProperty(name, ingredient.serialize()); - } - if (extraText != null) - object.addProperty("crafttext", extraText); - return object; - } - - public static CraftingRecipe parseCraftingRecipe(NEUManager manager, JsonObject recipe, JsonObject outputItem) { - Ingredient[] craftMatrix = new Ingredient[9]; - - String[] x = {"1", "2", "3"}; - String[] y = {"A", "B", "C"}; - for (int i = 0; i < 9; i++) { - String name = y[i / 3] + x[i % 3]; - if (!recipe.has(name)) continue; - String item = recipe.get(name).getAsString(); - if (item == null || item.isEmpty()) continue; - craftMatrix[i] = new Ingredient(manager, item); - } - int resultCount = 1; - if (recipe.has("count")) - resultCount = recipe.get("count").getAsInt(); - String extra = null; - if (outputItem.has("crafttext")) - extra = outputItem.get("crafttext").getAsString(); - if (recipe.has("crafttext")) - extra = recipe.get("crafttext").getAsString(); - String outputItemId = outputItem.get("internalname").getAsString(); - if (recipe.has("overrideOutputId")) - outputItemId = recipe.get("overrideOutputId").getAsString(); - return new CraftingRecipe(manager, craftMatrix, new Ingredient(manager, outputItemId, resultCount), extra); - } + public static final ResourceLocation BACKGROUND = new ResourceLocation("notenoughupdates","textures/gui/crafting_table_tall.png"); + + private static final int EXTRA_STRING_X = 132; + private static final int EXTRA_STRING_Y = 50; + + private final NEUManager manager; + private final Ingredient[] inputs; + private final String extraText; + private final Ingredient outputIngredient; + private List<RecipeSlot> slots; + + public CraftingRecipe(NEUManager manager, Ingredient[] inputs, Ingredient output, String extra) { + this.manager = manager; + this.inputs = inputs; + this.outputIngredient = output; + this.extraText = extra; + if (inputs.length != 9) + throw new IllegalArgumentException("Cannot construct crafting recipe with non standard crafting grid size"); + } + + @Override + public Set<Ingredient> getIngredients() { + Set<Ingredient> ingredients = Sets.newHashSet(inputs); + ingredients.remove(null); + return ingredients; + } + + @Override + public RecipeType getType() { + return RecipeType.CRAFTING; + } + + @Override + public boolean hasVariableCost() { + return false; + } + + @Override + public Set<Ingredient> getOutputs() { + return Collections.singleton(getOutput()); + } + + public Ingredient getOutput() { + return outputIngredient; + } + + public Ingredient[] getInputs() { + return inputs; + } + + + @Override + public List<RecipeSlot> getSlots() { + if (slots != null) return slots; + slots = new ArrayList<>(); + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + Ingredient input = inputs[x + y * 3]; + if (input == null) continue; + ItemStack item = input.getItemStack(); + if (item == null) continue; + slots.add(new RecipeSlot(30 + x * GuiItemRecipe.SLOT_SPACING, 48 + y * GuiItemRecipe.SLOT_SPACING, item)); + } + } + slots.add(new RecipeSlot(124, 66, outputIngredient.getItemStack())); + return slots; + } + + public String getCraftText() { + return extraText; + } + + @Override + public ResourceLocation getBackground() { + return BACKGROUND; + } + + @Override + public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { + FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj; + + String craftingText = getCraftText(); + if (craftingText != null) + Utils.drawStringCenteredScaledMaxWidth(craftingText, fontRenderer, + gui.guiLeft + EXTRA_STRING_X, gui.guiTop + EXTRA_STRING_Y, false, 75, 0x404040); + } + + @Override + public JsonObject serialize() { + JsonObject object = new JsonObject(); + object.addProperty("type", "crafting"); + object.addProperty("count", outputIngredient.getCount()); + object.addProperty("overrideOutputId", outputIngredient.getInternalItemId()); + for (int i = 0; i < 9; i++) { + Ingredient ingredient = inputs[i]; + if (ingredient == null) continue; + String[] x = {"1", "2", "3"}; + String[] y = {"A", "B", "C"}; + String name = x[i / 3] + y[i % 3]; + object.addProperty(name, ingredient.serialize()); + } + if(extraText != null) + object.addProperty("crafttext", extraText); + return object; + } + + public static CraftingRecipe parseCraftingRecipe(NEUManager manager, JsonObject recipe, JsonObject outputItem) { + Ingredient[] craftMatrix = new Ingredient[9]; + + String[] x = {"1", "2", "3"}; + String[] y = {"A", "B", "C"}; + for (int i = 0; i < 9; i++) { + String name = y[i / 3] + x[i % 3]; + if (!recipe.has(name)) continue; + String item = recipe.get(name).getAsString(); + if (item == null || item.isEmpty()) continue; + craftMatrix[i] = new Ingredient(manager, item); + } + int resultCount = 1; + if (recipe.has("count")) + resultCount = recipe.get("count").getAsInt(); + String extra = null; + if (outputItem.has("crafttext")) + extra = outputItem.get("crafttext").getAsString(); + if (recipe.has("crafttext")) + extra = recipe.get("crafttext").getAsString(); + String outputItemId = outputItem.get("internalname").getAsString(); + if (recipe.has("overrideOutputId")) + outputItemId = recipe.get("overrideOutputId").getAsString(); + return new CraftingRecipe(manager, craftMatrix, new Ingredient(manager, outputItemId, resultCount), extra); + } } 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 c971d82a..7847b0b4 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/ForgeRecipe.java @@ -19,238 +19,212 @@ import java.util.*; public class ForgeRecipe implements NeuRecipe { - private static final ResourceLocation BACKGROUND = - new ResourceLocation("notenoughupdates", "textures/gui/forge_recipe.png"); - - private static final int SLOT_IMAGE_U = 176; - private static final int SLOT_IMAGE_V = 0; - private static final int SLOT_IMAGE_SIZE = 18; - private static final int SLOT_PADDING = 1; - private static final int EXTRA_INFO_MAX_WIDTH = 75; - public static final int EXTRA_INFO_X = 132; - public static final int EXTRA_INFO_Y = 25; - - public enum ForgeType { - REFINING, ITEM_FORGING - } - - private final NEUManager manager; - private final List<Ingredient> inputs; - private final Ingredient output; - private final int hotmLevel; - private final int timeInSeconds; // TODO: quick forge - private List<RecipeSlot> slots; - - public ForgeRecipe( - NEUManager manager, - List<Ingredient> inputs, - Ingredient output, - int durationInSeconds, - int hotmLevel - ) { - this.manager = manager; - this.inputs = inputs; - this.output = output; - this.hotmLevel = hotmLevel; - this.timeInSeconds = durationInSeconds; - } - - public List<Ingredient> getInputs() { - return inputs; - } - - public Ingredient getOutput() { - return output; - } - - public int getHotmLevel() { - return hotmLevel; - } - - public int getTimeInSeconds() { - return timeInSeconds; - } - - @Override - public ResourceLocation getBackground() { - return BACKGROUND; - } - - @Override - public Set<Ingredient> getIngredients() { - return Sets.newHashSet(inputs); - } - - @Override - public boolean hasVariableCost() { - return false; - } - - @Override - public Set<Ingredient> getOutputs() { - return Collections.singleton(output); - } - - @Override - public List<RecipeSlot> getSlots() { - if (slots != null) return slots; - slots = new ArrayList<>(); - for (int i = 0; i < inputs.size(); i++) { - Ingredient input = inputs.get(i); - ItemStack itemStack = input.getItemStack(); - if (itemStack == null) continue; - int[] slotCoordinates = getSlotCoordinates(i, inputs.size()); - slots.add(new RecipeSlot(slotCoordinates[0], slotCoordinates[1], itemStack)); - } - slots.add(new RecipeSlot(124, 35, output.getItemStack())); - return slots; - } - - @Override - 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()); - gui.drawTexturedModalRect( - gui.guiLeft + slotCoordinates[0] - SLOT_PADDING, gui.guiTop + slotCoordinates[1] - SLOT_PADDING, - SLOT_IMAGE_U, SLOT_IMAGE_V, - SLOT_IMAGE_SIZE, SLOT_IMAGE_SIZE - ); - } - } - - @Override - 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 - ); - } - - @Override - public void drawHoverInformation(GuiItemRecipe gui, int mouseX, int mouseY) { - manager.hotm.getInformationOnCurrentProfile().ifPresent(hotmTree -> { - if (timeInSeconds > 0 && gui.isWithinRect( - mouseX, mouseY, - gui.guiLeft + EXTRA_INFO_X - EXTRA_INFO_MAX_WIDTH / 2, - gui.guiTop + EXTRA_INFO_Y - 8, - EXTRA_INFO_MAX_WIDTH, 16 - )) { - int qf = hotmTree.getLevel("forge_time"); - int reducedTime = getReducedTime(qf); - if (qf > 0) { - - Utils.drawHoveringText( - Arrays.asList( - EnumChatFormatting.YELLOW + formatDuration(reducedTime) + " with Quick Forge (Level " + qf + ")"), - mouseX, - mouseY, - gui.width, - gui.height, - 500, - Minecraft.getMinecraft().fontRendererObj - ); - } - } - }); - } - - public int getReducedTime(int quickForgeUpgradeLevel) { - return HotmInformation.getQuickForgeMultiplier(quickForgeUpgradeLevel) * timeInSeconds / 1000; - } - - @Override - public JsonObject serialize() { - JsonObject object = new JsonObject(); - JsonArray ingredients = new JsonArray(); - for (Ingredient input : inputs) { - ingredients.add(new JsonPrimitive(input.serialize())); - } - object.addProperty("type", "forge"); - object.add("inputs", ingredients); - object.addProperty("count", output.getCount()); - object.addProperty("overrideOutputId", output.getInternalItemId()); - if (hotmLevel >= 0) - object.addProperty("hotmLevel", hotmLevel); - if (timeInSeconds >= 0) - object.addProperty("duration", timeInSeconds); - return object; - } - - static ForgeRecipe parseForgeRecipe(NEUManager manager, JsonObject recipe, JsonObject output) { - List<Ingredient> ingredients = new ArrayList<>(); - for (JsonElement element : recipe.getAsJsonArray("inputs")) { - String ingredientString = element.getAsString(); - ingredients.add(new Ingredient(manager, ingredientString)); - } - String internalItemId = output.get("internalname").getAsString(); - if (recipe.has("overrideOutputId")) - internalItemId = recipe.get("overrideOutputId").getAsString(); - int resultCount = 1; - if (recipe.has("count")) { - resultCount = recipe.get("count").getAsInt(); - } - int duration = -1; - if (recipe.has("duration")) { - duration = recipe.get("duration").getAsInt(); - } - int hotmLevel = -1; - if (recipe.has("hotmLevel")) { - hotmLevel = recipe.get("hotmLevel").getAsInt(); - } - return new ForgeRecipe( - manager, - ingredients, - new Ingredient(manager, internalItemId, resultCount), - duration, - hotmLevel - ); - } - - private static final int RECIPE_CENTER_X = 40; - private static final int RECIPE_CENTER_Y = 34; - private static final int SLOT_DISTANCE_FROM_CENTER = 22; - private static final int RECIPE_FALLBACK_X = 20; - private static final int RECIPE_FALLBACK_Y = 15; - - static int[] getSlotCoordinates(int slotNumber, int totalSlotCount) { - if (totalSlotCount > 6) { - return new int[]{ - RECIPE_FALLBACK_X + (slotNumber % 4) * GuiItemRecipe.SLOT_SPACING, - RECIPE_FALLBACK_Y + (slotNumber / 4) * GuiItemRecipe.SLOT_SPACING, - }; - } - if (totalSlotCount == 1) { - return new int[]{ - RECIPE_CENTER_X - GuiItemRecipe.SLOT_SIZE / 2, - RECIPE_CENTER_Y - GuiItemRecipe.SLOT_SIZE / 2 - }; - } - double rad = Math.PI * 2 * slotNumber / totalSlotCount; - int x = (int) (Math.cos(rad) * SLOT_DISTANCE_FROM_CENTER); - int y = (int) (Math.sin(rad) * SLOT_DISTANCE_FROM_CENTER); - return new int[]{RECIPE_CENTER_X + x, RECIPE_CENTER_Y + y}; - } - - static String formatDuration(int seconds) { - int minutes = seconds / 60; - seconds %= 60; - int hours = minutes / 60; - minutes %= 60; - int days = hours / 24; - hours %= 24; - StringBuilder sB = new StringBuilder(); - if (days != 0) sB.append(days).append("d "); - if (hours != 0) sB.append(hours).append("h "); - if (minutes != 0) sB.append(minutes).append("m "); - if (seconds != 0) sB.append(seconds).append("s "); - return sB.substring(0, sB.length() - 1); - } + private static final ResourceLocation BACKGROUND = new ResourceLocation("notenoughupdates", "textures/gui/forge_recipe_tall.png"); + + private static final int SLOT_IMAGE_U = 176; + private static final int SLOT_IMAGE_V = 0; + private static final int SLOT_IMAGE_SIZE = 18; + private static final int SLOT_PADDING = 1; + private static final int EXTRA_INFO_MAX_WIDTH = 75; + public static final int EXTRA_INFO_X = 132; + public static final int EXTRA_INFO_Y = 55; + + public enum ForgeType { + REFINING, ITEM_FORGING + } + + private final NEUManager manager; + private final List<Ingredient> inputs; + private final Ingredient output; + private final int hotmLevel; + private final int timeInSeconds; // TODO: quick forge + private List<RecipeSlot> slots; + + public ForgeRecipe(NEUManager manager, List<Ingredient> inputs, Ingredient output, int durationInSeconds, int hotmLevel) { + this.manager = manager; + this.inputs = inputs; + this.output = output; + this.hotmLevel = hotmLevel; + this.timeInSeconds = durationInSeconds; + } + + public List<Ingredient> getInputs() { + return inputs; + } + + public Ingredient getOutput() { + return output; + } + + public int getHotmLevel() { + return hotmLevel; + } + + public int getTimeInSeconds() { + return timeInSeconds; + } + + @Override + public ResourceLocation getBackground() { + return BACKGROUND; + } + + @Override + public RecipeType getType() { + return RecipeType.FORGE; + } + + @Override + public Set<Ingredient> getIngredients() { + return Sets.newHashSet(inputs); + } + + @Override + public boolean hasVariableCost() { + return false; + } + + @Override + public Set<Ingredient> getOutputs() { + return Collections.singleton(output); + } + + @Override + public List<RecipeSlot> getSlots() { + if (slots != null) return slots; + slots = new ArrayList<>(); + for (int i = 0; i < inputs.size(); i++) { + Ingredient input = inputs.get(i); + ItemStack itemStack = input.getItemStack(); + if (itemStack == null) continue; + int[] slotCoordinates = getSlotCoordinates(i, inputs.size()); + slots.add(new RecipeSlot(slotCoordinates[0], slotCoordinates[1], itemStack)); + } + slots.add(new RecipeSlot(124, 66, output.getItemStack())); + return slots; + } + + @Override + 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()); + gui.drawTexturedModalRect( + gui.guiLeft + slotCoordinates[0] - SLOT_PADDING, gui.guiTop + slotCoordinates[1] - SLOT_PADDING, + SLOT_IMAGE_U, SLOT_IMAGE_V, + SLOT_IMAGE_SIZE, SLOT_IMAGE_SIZE); + } + } + + @Override + 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); + } + + @Override + public void drawHoverInformation(GuiItemRecipe gui, int mouseX, int mouseY) { + manager.hotm.getInformationOnCurrentProfile().ifPresent(hotmTree -> { + if (timeInSeconds > 0 && gui.isWithinRect( + mouseX, mouseY, + gui.guiLeft + EXTRA_INFO_X - EXTRA_INFO_MAX_WIDTH / 2, + gui.guiTop + EXTRA_INFO_Y - 8, + EXTRA_INFO_MAX_WIDTH, 16 + )) { + int qf = hotmTree.getLevel("forge_time"); + int reducedTime = getReducedTime(qf); + if (qf > 0) { + + Utils.drawHoveringText(Arrays.asList(EnumChatFormatting.YELLOW + formatDuration(reducedTime) + " with Quick Forge (Level " + qf + ")"), mouseX, mouseY, gui.width, gui.height, 500, Minecraft.getMinecraft().fontRendererObj); + } + } + }); + } + + public int getReducedTime(int quickForgeUpgradeLevel) { + return HotmInformation.getQuickForgeMultiplier(quickForgeUpgradeLevel) * timeInSeconds / 1000; + } + + @Override + public JsonObject serialize() { + JsonObject object = new JsonObject(); + JsonArray ingredients = new JsonArray(); + for (Ingredient input : inputs) { + ingredients.add(new JsonPrimitive(input.serialize())); + } + object.addProperty("type", "forge"); + object.add("inputs", ingredients); + object.addProperty("count", output.getCount()); + object.addProperty("overrideOutputId", output.getInternalItemId()); + if (hotmLevel >= 0) + object.addProperty("hotmLevel", hotmLevel); + if (timeInSeconds >= 0) + object.addProperty("duration", timeInSeconds); + return object; + } + + static ForgeRecipe parseForgeRecipe(NEUManager manager, JsonObject recipe, JsonObject output) { + List<Ingredient> ingredients = new ArrayList<>(); + for (JsonElement element : recipe.getAsJsonArray("inputs")) { + String ingredientString = element.getAsString(); + ingredients.add(new Ingredient(manager, ingredientString)); + } + String internalItemId = output.get("internalname").getAsString(); + if (recipe.has("overrideOutputId")) + internalItemId = recipe.get("overrideOutputId").getAsString(); + int resultCount = 1; + if (recipe.has("count")) { + resultCount = recipe.get("count").getAsInt(); + } + int duration = -1; + if (recipe.has("duration")) { + duration = recipe.get("duration").getAsInt(); + } + int hotmLevel = -1; + if (recipe.has("hotmLevel")) { + hotmLevel = recipe.get("hotmLevel").getAsInt(); + } + return new ForgeRecipe(manager, ingredients, new Ingredient(manager, internalItemId, resultCount), duration, hotmLevel); + } + + private static final int RECIPE_CENTER_X = 49; + private static final int RECIPE_CENTER_Y = 74; + private static final int SLOT_DISTANCE_FROM_CENTER = 30; + private static final int RECIPE_FALLBACK_X = 20; + private static final int RECIPE_FALLBACK_Y = 15; + + static int[] getSlotCoordinates(int slotNumber, int totalSlotCount) { + if (totalSlotCount > 8) { + return new int[]{ + RECIPE_FALLBACK_X + (slotNumber % 4) * GuiItemRecipe.SLOT_SPACING, + RECIPE_FALLBACK_Y + (slotNumber / 4) * GuiItemRecipe.SLOT_SPACING, + }; + } + if (totalSlotCount == 1) { + return new int[]{ + RECIPE_CENTER_X - GuiItemRecipe.SLOT_SIZE / 2, + RECIPE_CENTER_Y - GuiItemRecipe.SLOT_SIZE / 2 + }; + } + double rad = Math.PI * 2 * slotNumber / totalSlotCount; + int x = (int) (Math.cos(rad) * SLOT_DISTANCE_FROM_CENTER); + int y = (int) (Math.sin(rad) * SLOT_DISTANCE_FROM_CENTER); + return new int[]{RECIPE_CENTER_X + x - GuiItemRecipe.SLOT_SIZE / 2, RECIPE_CENTER_Y + y - GuiItemRecipe.SLOT_SIZE / 2}; + } + + static String formatDuration(int seconds) { + int minutes = seconds / 60; + seconds %= 60; + int hours = minutes / 60; + minutes %= 60; + int days = hours / 24; + hours %= 24; + StringBuilder sB = new StringBuilder(); + if (days != 0) sB.append(days).append("d "); + if (hours != 0) sB.append(hours).append("h "); + if (minutes != 0) sB.append(minutes).append("m "); + if (seconds != 0) sB.append(seconds).append("s "); + return sB.substring(0, sB.length() - 1); + } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/Ingredient.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/Ingredient.java index 79b548da..c4928605 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/Ingredient.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/Ingredient.java @@ -2,6 +2,7 @@ package io.github.moulberry.notenoughupdates.recipes; import com.google.gson.JsonObject; import io.github.moulberry.notenoughupdates.NEUManager; +import io.github.moulberry.notenoughupdates.util.ItemUtils; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; @@ -75,9 +76,7 @@ public class Ingredient { public ItemStack getItemStack() { if (itemStack != null) return itemStack; if (isCoins()) { - itemStack = new ItemStack(Items.gold_nugget); - itemStack.setStackDisplayName("\u00A7r\u00A76" + Utils.formatNumberWithDots(getCount()) + " Coins"); - return itemStack; + return ItemUtils.getCoinItemStack(count); } JsonObject itemInfo = manager.getItemInformation().get(internalItemId); itemStack = manager.jsonToStack(itemInfo); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/MobLootRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/MobLootRecipe.java new file mode 100644 index 00000000..dcbf71da --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/MobLootRecipe.java @@ -0,0 +1,309 @@ +package io.github.moulberry.notenoughupdates.recipes; + +import com.google.common.collect.Sets; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import io.github.moulberry.notenoughupdates.NEUManager; +import io.github.moulberry.notenoughupdates.miscfeatures.entityviewer.EntityViewer; +import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe; +import io.github.moulberry.notenoughupdates.profileviewer.Panorama; +import io.github.moulberry.notenoughupdates.util.ItemUtils; +import io.github.moulberry.notenoughupdates.util.JsonUtils; +import io.github.moulberry.notenoughupdates.util.Utils; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MobLootRecipe implements NeuRecipe { + + private static final int MOB_POS_X = 38, MOB_POS_Y = 100; + private static final int SLOT_POS_X = 82, SLOT_POS_Y = 24; + + public static class MobDrop { + public final Ingredient drop; + public final String chance; + public final List<String> extra; + + private ItemStack itemStack; + + public MobDrop(Ingredient drop, String chance, List<String> extra) { + this.drop = drop; + this.chance = chance; + this.extra = extra; + } + + public ItemStack getItemStack() { + if (itemStack == null) { + itemStack = drop.getItemStack().copy(); + List<String> arrayList = new ArrayList<>(extra); + arrayList.add("§r§e§lDrop Chance: §6" + chance); + ItemUtils.appendLore(itemStack, arrayList); + } + return itemStack; + } + } + + public static ResourceLocation BACKGROUND = new ResourceLocation( + "notenoughupdates", + "textures/gui/mob_loot_tall.png" + ); + private final Ingredient mobIngredient; + private final List<MobDrop> drops; + private final int coins; + private final int combatXp; + private final int xp; + private final String name; + private final String render; + private final int level; + private final List<String> extra; + private EntityLivingBase entityLivingBase; + + private final String panoName; + + private ResourceLocation[] panos = null; + + public MobLootRecipe( + Ingredient mobIngredient, + List<MobDrop> drops, + int level, + int coins, + int xp, + int combatXp, + String name, + String render, + List<String> extra, + String panoName + ) { + this.mobIngredient = mobIngredient; + this.drops = drops; + this.level = level; + this.coins = coins; + this.xp = xp; + this.extra = extra; + this.combatXp = combatXp; + this.name = name; + this.render = render; + this.panoName = panoName; + } + + public String getName() { + return name; + } + + public List<MobDrop> getDrops() { + return drops; + } + + public int getCoins() { + return coins; + } + + public int getCombatXp() { + return combatXp; + } + + public Ingredient getMob() { + return mobIngredient; + } + + public int getXp() { + return xp; + } + + public String getRender() { + return render; + } + + public synchronized EntityLivingBase getRenderEntity() { + if (entityLivingBase == null) { + if (render == null) return null; + if (render.startsWith("@")) { + entityLivingBase = EntityViewer.constructEntity(new ResourceLocation(render.substring(1))); + } else { + entityLivingBase = EntityViewer.constructEntity(render, Collections.emptyList()); + } + } + return entityLivingBase; + } + + @Override + public Set<Ingredient> getIngredients() { + return Sets.newHashSet(mobIngredient); + } + + @Override + public Set<Ingredient> getOutputs() { + return Stream.concat(drops.stream().map(it -> it.drop), Stream.of(mobIngredient)).collect(Collectors.toSet()); + } + + @Override + public String getTitle() { + return getFullMobName(); + } + + public String getFullMobName() { + return (level > 0 ? "§8[§7Lv " + level + "§8] §c" : "§c") + name; + } + + @Override + public List<RecipeSlot> getSlots() { + List<RecipeSlot> slots = new ArrayList<>(); + BiConsumer<Integer, ItemStack> addSlot = (sl, is) -> slots.add( + new RecipeSlot( + SLOT_POS_X + (sl % 5) * 16, + SLOT_POS_Y + (sl / 5) * 16, + is + )); + int i = 0; + for (; i < drops.size(); i++) { + MobDrop mobDrop = drops.get(i); + addSlot.accept(i, mobDrop.getItemStack()); + } + return slots; + } + + @Override + public RecipeType getType() { + return RecipeType.MOB_LOOT; + } + + @Override + public boolean shouldUseForCraftCost() { + return false; + } + + @Override + public boolean hasVariableCost() { + return true; + } + + public static final int PANORAMA_POS_X = 13; + public static final int PANORAMA_POS_Y = 23; + public static final int PANORAMA_WIDTH = 50; + public static final int PANORAMA_HEIGHT = 80; + + @Override + public void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) { + if (panos == null) { + panos = Panorama.getPanoramasForLocation(panoName, "day"); + } + Panorama.drawPanorama( + ((System.nanoTime() / 20000000000F) % 1) * 360, + gui.guiLeft + PANORAMA_POS_X, + gui.guiTop + PANORAMA_POS_Y, + PANORAMA_WIDTH, + PANORAMA_HEIGHT, + 0F, + 0F, + panos + ); + if (getRenderEntity() != null) + EntityViewer.renderEntity(entityLivingBase, gui.guiLeft + MOB_POS_X, gui.guiTop + MOB_POS_Y, mouseX, mouseY); + } + + @Override + public void drawHoverInformation(GuiItemRecipe gui, int mouseX, int mouseY) { + if (gui.isWithinRect( + mouseX, + mouseY, + gui.guiLeft + PANORAMA_POS_X, + gui.guiTop + PANORAMA_POS_Y, + PANORAMA_WIDTH, + PANORAMA_HEIGHT + )) { + List<String> stuff = new ArrayList<>(); + stuff.add(getFullMobName()); + stuff.add(""); + if (coins > 0) + stuff.add("§r§6Coins: " + coins); + if (xp > 0) + stuff.add("§r§aExperience: " + xp); + if (combatXp > 0) + stuff.add("§r§bCombat Experience: " + xp); + stuff.addAll(extra); + Utils.drawHoveringText( + stuff, + mouseX, + mouseY, + gui.width, + gui.height, + -1, + Minecraft.getMinecraft().fontRendererObj + ); + } + } + + @Override + public int[] getPageFlipPositionLeftTopCorner() { + return new int[]{14, 118}; + } + + @Override + public JsonObject serialize() { + JsonObject recipe = new JsonObject(); + recipe.addProperty("level", level); + recipe.addProperty("coins", coins); + recipe.addProperty("xp", xp); + recipe.addProperty("combat_xp", combatXp); + recipe.addProperty("name", name); + recipe.addProperty("render", render); + recipe.addProperty("type", getType().getId()); + recipe.addProperty("panorama", "unknown"); + recipe.add("extra", JsonUtils.transformListToJsonArray(extra, JsonPrimitive::new)); + recipe.add("drops", JsonUtils.transformListToJsonArray(drops, drop -> { + JsonObject dropObject = new JsonObject(); + dropObject.addProperty("id", drop.drop.serialize()); + dropObject.add("extra", JsonUtils.transformListToJsonArray(drop.extra, JsonPrimitive::new)); + dropObject.addProperty("chance", drop.chance); + return dropObject; + })); + return recipe; + } + + @Override + public ResourceLocation getBackground() { + return BACKGROUND; + } + + public static MobLootRecipe parseRecipe(NEUManager manager, JsonObject recipe, JsonObject outputItemJson) { + List<MobDrop> drops = new ArrayList<>(); + for (JsonElement jsonElement : recipe.getAsJsonArray("drops")) { + if (jsonElement.isJsonPrimitive()) { + drops.add(new MobDrop(new Ingredient(manager, jsonElement.getAsString()), null, Collections.emptyList())); + } else { + JsonObject jsonObject = jsonElement.getAsJsonObject(); + drops.add( + new MobDrop( + new Ingredient(manager, jsonObject.get("id").getAsString()), + jsonObject.has("chance") ? jsonObject.get("chance").getAsString() : null, + JsonUtils.getJsonArrayOrEmpty(jsonObject, "extra", JsonElement::getAsString) + )); + } + } + + return new MobLootRecipe( + new Ingredient(manager, outputItemJson.get("internalname").getAsString(), 1), + drops, + recipe.has("level") ? recipe.get("level").getAsInt() : 0, + recipe.has("coins") ? recipe.get("coins").getAsInt() : 0, + recipe.has("xp") ? recipe.get("xp").getAsInt() : 0, + recipe.has("combat_xp") ? recipe.get("combat_xp").getAsInt() : 0, + recipe.get("name").getAsString(), + recipe.has("render") && !recipe.get("render").isJsonNull() ? recipe.get("render").getAsString() : null, + JsonUtils.getJsonArrayOrEmpty(recipe, "extra", JsonElement::getAsString), + recipe.has("panorama") ? recipe.get("panorama").getAsString() : "unknown" + ); + } +} 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 3516f707..8202bb48 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/NeuRecipe.java @@ -15,6 +15,12 @@ public interface NeuRecipe { List<RecipeSlot> getSlots(); + RecipeType getType(); + + default String getTitle() { + return getType().getLabel(); + } + default void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { } @@ -31,15 +37,12 @@ public interface NeuRecipe { ResourceLocation getBackground(); static NeuRecipe parseRecipe(NEUManager manager, JsonObject recipe, JsonObject output) { + RecipeType recipeType = RecipeType.CRAFTING; if (recipe.has("type")) { - switch (recipe.get("type").getAsString().intern()) { - case "forge": - return ForgeRecipe.parseForgeRecipe(manager, recipe, output); - case "trade": - return VillagerTradeRecipe.parseStaticRecipe(manager, recipe); - } + recipeType = RecipeType.getRecipeTypeForId(recipe.get("type").getAsString()); } - return CraftingRecipe.parseCraftingRecipe(manager, recipe, output); + if (recipeType == null) return null; + return recipeType.createRecipe(manager, recipe, output); } default boolean shouldUseForCraftCost() { @@ -49,4 +52,11 @@ public interface NeuRecipe { default boolean isAvailable() { return true; } + + /** + * @return an array of length two in the format [leftmost x, topmost y] of the page buttons + */ + default int[] getPageFlipPositionLeftTopCorner() { + return new int[]{110, 90}; + } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeGenerator.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeGenerator.java index 46aff6c4..e5028146 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeGenerator.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeGenerator.java @@ -9,9 +9,11 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.inventory.GuiChest; +import net.minecraft.init.Items; import net.minecraft.inventory.ContainerChest; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; @@ -19,10 +21,10 @@ import net.minecraftforge.fml.common.gameevent.TickEvent; import org.lwjgl.input.Keyboard; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; public class RecipeGenerator { public static final String DURATION = "Duration: "; @@ -81,7 +83,7 @@ public class RecipeGenerator { " seconds (no QF) .")); boolean saved = false; try { - saved = saveRecipe(recipe); + saved = saveRecipes(recipe.getOutput().getInternalItemId(), Collections.singletonList(recipe)); } catch (IOException e) { } if (!saved) @@ -93,21 +95,152 @@ public class RecipeGenerator { " Failed to save recipe. Does the item already exist?")); } } + if (saveRecipe) attemptToSaveBestiary(menu); } - public boolean saveRecipe(NeuRecipe recipe) throws IOException { - JsonObject recipeJson = recipe.serialize(); - for (Ingredient i : recipe.getOutputs()) { - if (i.isCoins()) continue; - JsonObject outputJson = neu.manager.readJsonDefaultDir(i.getInternalItemId() + ".json"); - if (outputJson == null) return false; - outputJson.addProperty("clickcommand", "viewrecipe"); - JsonArray array = new JsonArray(); - array.add(recipeJson); - outputJson.add("recipes", array); - neu.manager.writeJsonDefaultDir(outputJson, i.getInternalItemId() + ".json"); - neu.manager.loadItem(i.getInternalItemId()); + private List<String> getLore(ItemStack item) { + NBTTagList loreTag = item.getTagCompound().getCompoundTag("display").getTagList("Lore", 8); + List<String> loreList = new ArrayList<>(); + for (int i = 0; i < loreTag.tagCount(); i++) { + loreList.add(loreTag.getStringTagAt(i)); } + return loreList; + } + + // §8[§7Lv1§8] §fZombie Villager + private static final Pattern MOB_DISPLAY_NAME_PATTERN = Pattern.compile("^§8\\[§7Lv(?<level>\\d+)§8] (?<name>.*)$"); + // §7Coins per Kill: §61 + // §7Combat Exp: §3120 + // §8 ■ §7§5Skeleton Grunt Helmet §8(§a5%§8) + // §8 ■ §7§fRotten Flesh + // §8 ■ Dragon Essence §8x3-5 + private static final Pattern LORE_PATTERN = Pattern.compile("^(?:" + + "§7Coins per Kill: §6(?<coins>[,\\d]+)|" + + "§7Combat Exp: §3(?<combatxp>[,\\d]+)|" + + "§7XP Orbs: §3(?<xp>[,\\d]+)|" + + "§8 ■ (?:§7)?(?<dropName>(?:§.)?.+?)(?: §8\\(§a(?<dropChances>[\\d.<]+%)§8\\)| §8(?<dropCount>x.*))?|" + + "§7Kills: §a[,\\d]+|" + + "§.[a-zA-Z]+ Loot|" + + "§7Deaths: §a[,\\d]+|" + + " §8■ (?<missing>§c\\?\\?\\?)|" + + "" + + ")$"); + + private void attemptToSaveBestiary(IInventory menu) { + if (!menu.getDisplayName().getUnformattedText().contains("➜")) return; + ItemStack backArrow = menu.getStackInSlot(48); + if (backArrow == null || backArrow.getItem() != Items.arrow) return; + if (!getLore(backArrow).stream().anyMatch(it -> it.startsWith("§7To Bestiary ➜"))) return; + List<NeuRecipe> recipes = new ArrayList<>(); + String internalMobName = + menu.getDisplayName().getUnformattedText().split("➜")[1].toUpperCase(Locale.ROOT).trim() + "_MONSTER"; + for (int i = 9; i < 44; i++) { + ItemStack mobStack = menu.getStackInSlot(i); + if (mobStack == null || mobStack.getItem() != Items.skull) continue; + Matcher matcher = MOB_DISPLAY_NAME_PATTERN.matcher(mobStack.getDisplayName()); + if (!matcher.matches()) continue; + String name = matcher.group("name"); + int level = parseIntIgnoringCommas(matcher.group("level")); + List<String> mobLore = getLore(mobStack); + int coins = 0, xp = 0, combatXp = 0; + List<MobLootRecipe.MobDrop> drops = new ArrayList<>(); + for (String loreLine : mobLore) { + Matcher loreMatcher = LORE_PATTERN.matcher(loreLine); + if (!loreMatcher.matches()) { + Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText( + "[WARNING] Unknown lore line: " + loreLine)); + continue; + } + if (loreMatcher.group("coins") != null) + coins = parseIntIgnoringCommas(loreMatcher.group("coins")); + if (loreMatcher.group("combatxp") != null) + combatXp = parseIntIgnoringCommas(loreMatcher.group("combatxp")); + if (loreMatcher.group("xp") != null) + xp = parseIntIgnoringCommas(loreMatcher.group("xp")); + if (loreMatcher.group("dropName") != null) { + String dropName = loreMatcher.group("dropName"); + List<JsonObject> possibleItems = neu.manager.getItemInformation().values().stream().filter(it -> it.get( + "displayname").getAsString().equals(dropName)).collect(Collectors.toList()); + if (possibleItems.size() != 1) { + Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText( + "[WARNING] Could not parse drop, ambiguous or missing item information: " + loreLine)); + continue; + } + Ingredient item = new Ingredient(neu.manager, possibleItems.get(0).get("internalname").getAsString()); + String chance = loreMatcher.group("dropChances") != null + ? loreMatcher.group("dropChances") + : loreMatcher.group("dropCount"); + drops.add(new MobLootRecipe.MobDrop(item, chance, new ArrayList<>())); + } + if (loreMatcher.group("missing") != null) { + Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText( + "[WARNING] You are missing Bestiary levels for drop: " + loreLine)); + + } + } + recipes.add(new MobLootRecipe( + new Ingredient(neu.manager, internalMobName, 1), + drops, + level, + coins, + xp, + combatXp, + name, + null, + new ArrayList<>(), + "unknown" + )); + } + boolean saved = false; + try { + saved = saveRecipes(internalMobName, recipes); + } catch (IOException e) { + } + if (!saved) + Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("" + + EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + EnumChatFormatting.OBFUSCATED + "#" + + EnumChatFormatting.RESET + EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + " ERROR " + + EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + EnumChatFormatting.OBFUSCATED + "#" + + EnumChatFormatting.RESET + EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + + " Failed to save recipe. Does the item already exist?")); // TODO: MERGE CODE OVER + } + + private int parseIntIgnoringCommas(String text) { + return Integer.parseInt(text.replace(",", "")); + } + + /*{ + id: "minecraft:skull", + Count: 1b, + tag: { + overrideMeta: 1b, + SkullOwner: { + Id: "2005daad-730b-363c-abae-e6f3830816fb", + Properties: { + textures: [{ + Value: "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTZjMGIzNmQ1M2ZmZjY5YTQ5YzdkNmYzOTMyZjJiMGZlOTQ4ZTAzMjIyNmQ1ZTgwNDVlYzU4NDA4YTM2ZTk1MSJ9fX0=" + }] + } + }, + display: { + Lore: ["§7Coins per Kill: §610", "§7Combat Exp: §340", "§7XP Orbs: §312", "", "§7Kills: §a990", "§7Deaths: §a2", "", "§fCommon Loot", "§8 ■ §7§fEnder Pearl §8x1-3", "", "§9Rare Loot", "§8 ■ §7§aEnchanted Ender Pearl §8(§a1%§8)", "", "§6Legendary Loot", "§8 ■ §7§7[Lvl 1] §aEnderman §8(§a0.02%§8)", "§8 ■ §7§7[Lvl 1] §fEnderman §8(§a0.05%§8)", "§8 ■ §7§5Ender Helmet §8(§a0.1%§8)", "§8 ■ §7§5Ender Boots §8(§a0.1%§8)", "§8 ■ §7§5Ender Leggings §8(§a0.1%§8)", "§8 ■ §7§5Ender Chestplate §8(§a0.1%§8)", "", "§dRNGesus Loot", " §8■ §c???"], + Name: "§8[§7Lv42§8] §fEnderman" + }, + AttributeModifiers: [] + }, + Damage: 3s +}*/ + public boolean saveRecipes(String relevantItem, List<NeuRecipe> recipes) throws IOException { + JsonObject outputJson = neu.manager.readJsonDefaultDir(relevantItem + ".json"); + if (outputJson == null) return false; + outputJson.addProperty("clickcommand", "viewrecipe"); + JsonArray array = new JsonArray(); + for (NeuRecipe recipe : recipes) { + array.add(recipe.serialize()); + } + outputJson.add("recipes", array); + neu.manager.writeJsonDefaultDir(outputJson, relevantItem + ".json"); + neu.manager.loadItem(relevantItem); return true; } @@ -149,7 +282,7 @@ public class RecipeGenerator { ); } - private static final Map<Character, Integer> durationSuffixLengthMap = new HashMap<Character, Integer>() {{ + private static Map<Character, Integer> durationSuffixLengthMap = new HashMap<Character, Integer>() {{ put('d', 60 * 60 * 24); put('h', 60 * 60); put('m', 60); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java new file mode 100644 index 00000000..cc8b50be --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/RecipeType.java @@ -0,0 +1,59 @@ +package io.github.moulberry.notenoughupdates.recipes; + +import com.google.gson.JsonObject; +import io.github.moulberry.notenoughupdates.NEUManager; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; + +public enum RecipeType { + CRAFTING("crafting", "Crafting", CraftingRecipe::parseCraftingRecipe, new ItemStack(Blocks.crafting_table)), + FORGE("forge", "Forging", ForgeRecipe::parseForgeRecipe, new ItemStack(Blocks.anvil)), + TRADE("trade", "Trading", VillagerTradeRecipe::parseStaticRecipe, new ItemStack(Items.emerald)), + MOB_LOOT("drops", "Mob Loot", MobLootRecipe::parseRecipe, new ItemStack(Items.diamond_sword)); + + private final String id; + private final String label; + private final RecipeFactory recipeFactory; + private final ItemStack icon; + + RecipeType(String id, String label, RecipeFactory recipeFactory, ItemStack icon) { + this.id = id; + this.label = label; + this.recipeFactory = recipeFactory; + this.icon = icon; + icon.setStackDisplayName("neurecipe-" + id); + } + + public String getId() { + return id; + } + + public String getLabel() { + return label; + } + + public RecipeFactory getRecipeFactory() { + return recipeFactory; + } + + public ItemStack getIcon() { + return icon; + } + + public NeuRecipe createRecipe(NEUManager manager, JsonObject recipe, JsonObject outputItemJson) { + return recipeFactory.createRecipe(manager, recipe, outputItemJson); + } + + public static RecipeType getRecipeTypeForId(String id) { + for (RecipeType value : values()) { + if (value.id.equals(id)) return value; + } + return null; + } + + @FunctionalInterface + interface RecipeFactory { + NeuRecipe createRecipe(NEUManager manager, JsonObject recipe, JsonObject outputItemJson); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java b/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java index 371d4f3c..2e53e153 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/recipes/VillagerTradeRecipe.java @@ -21,136 +21,130 @@ 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<Ingredient> getIngredients() { - return Sets.newHashSet(cost); - } - - @Override - public Set<Ingredient> getOutputs() { - return Sets.newHashSet(result); - } - - @Override - public List<RecipeSlot> 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 - ); - } + public static final int COST_SLOT_X = 52; + public static final int COST_SLOT_Y = 66; + public static final int RESULT_SLOT_Y = 66; + 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_tall.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 RecipeType getType() { + return RecipeType.TRADE; + } + + @Override + public Set<Ingredient> getIngredients() { + return Sets.newHashSet(cost); + } + + @Override + public Set<Ingredient> getOutputs() { + return Sets.newHashSet(result); + } + + @Override + public List<RecipeSlot> 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 + 90, false, 75, 0xff00ff); + + } + } + + @Override + public void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) { + GuiInventory.drawEntityOnScreen(gui.guiLeft + 90, gui.guiTop + 100, 30, gui.guiLeft - mouseX + 110, 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, JsonObject result) { + 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/ItemUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java new file mode 100644 index 00000000..e3d90aaa --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java @@ -0,0 +1,33 @@ +package io.github.moulberry.notenoughupdates.util; + +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; + +import java.util.List; + +public class ItemUtils { + + public static ItemStack getCoinItemStack(int coinAmount) { + ItemStack itemStack = new ItemStack(Items.gold_nugget); + itemStack.setStackDisplayName("\u00A7r\u00A76" + Utils.formatNumberWithDots(coinAmount) + " Coins"); + return itemStack; + } + + public static void appendLore(ItemStack is, List<String> moreLore) { + NBTTagCompound tagCompound = is.getTagCompound(); + if (tagCompound == null) { + tagCompound = new NBTTagCompound(); + } + NBTTagCompound display = tagCompound.getCompoundTag("display"); + NBTTagList lore = display.getTagList("Lore", 8); + for (String s : moreLore) { + lore.appendTag(new NBTTagString(s)); + } + display.setTag("Lore", lore); + tagCompound.setTag("display", display); + is.setTagCompound(tagCompound); + } +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/JsonUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/JsonUtils.java new file mode 100644 index 00000000..916631b7 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/JsonUtils.java @@ -0,0 +1,42 @@ +package io.github.moulberry.notenoughupdates.util; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class JsonUtils { + public static Stream<JsonElement> getJsonArrayAsStream(JsonArray array) { + return StreamSupport.stream(array.spliterator(), false); + } + + public static <T> List<T> transformJsonArrayToList(JsonArray array, Function<? super JsonElement, ? extends T> mapper) { + return getJsonArrayAsStream(array).map(mapper).collect(Collectors.toList()); + } + + public static <T> List<T> getJsonArrayOrEmpty(JsonObject rootObject, String name, Function<? super JsonElement, ? extends T> mapper) { + if (!rootObject.has(name)) { + return Collections.emptyList(); + } + JsonElement jsonElement = rootObject.get(name); + if (jsonElement.isJsonArray()) { + return transformJsonArrayToList(jsonElement.getAsJsonArray(), mapper); + } + return Collections.emptyList(); + } + + public static <T> JsonArray transformListToJsonArray(List<T> things, Function<? super T, ? extends JsonElement> mapper) { + JsonArray array = new JsonArray(); + for (T t : things) { + array.add(mapper.apply(t)); + } + return array; + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/SkytilsCompat.java b/src/main/java/io/github/moulberry/notenoughupdates/util/SkytilsCompat.java new file mode 100644 index 00000000..193f6133 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/SkytilsCompat.java @@ -0,0 +1,82 @@ +package io.github.moulberry.notenoughupdates.util; + +import net.minecraft.item.ItemStack; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +public class SkytilsCompat { + // Defer static initialization + private static class Holder { + // Skytils is present in some capacity + static boolean isSkytilsPresent = false; + // All classes successfully loaded + static boolean isSkytilsFullyPresent = false; + + static Class<?> skytilsClass = null; + static Method renderRarityMethod = null; + static Class<?> renderUtilClass = null; + + static Object skytilsCompanionObject = null; + + static Class<?> skytilsConfigClass = null; + + static Object skytilsConfigObject = null; + static Method skytilsGetShowItemRarity = null; + + static { + try { + skytilsClass = Class.forName("skytils.skytilsmod.Skytils"); + isSkytilsPresent = true; + } catch (ClassNotFoundException ignored) { + } + try { + Class<?> skytilsCompanionClass = Class.forName("skytils.skytilsmod.Skytils$Companion"); + skytilsConfigClass = Class.forName("skytils.skytilsmod.core.Config"); + Field skytilsCompanionField = skytilsClass.getField("Companion"); + skytilsCompanionObject = skytilsCompanionField.get(null); + Method skytilsGetConfigMethod = skytilsCompanionClass.getMethod("getConfig"); + skytilsConfigObject = skytilsGetConfigMethod.invoke(skytilsCompanionObject); + skytilsGetShowItemRarity = skytilsConfigClass.getMethod("getShowItemRarity"); + renderUtilClass = Class.forName("skytils.skytilsmod.utils.RenderUtil"); + renderRarityMethod = renderUtilClass.getDeclaredMethod( + "renderRarity", + ItemStack.class, + Integer.TYPE, + Integer.TYPE + ); + isSkytilsFullyPresent = true; + } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException | IllegalAccessException | + InvocationTargetException e) { + System.err.println("Failed to get Skytils class even tho Skytils mod is present. This is (probably) a NEU bug"); + e.printStackTrace(); + } + } + + } + + public static boolean isSkytilsFullyLoaded() { + return Holder.isSkytilsFullyPresent; + } + + public static boolean isSkytilsPresent() { + return Holder.isSkytilsPresent; + } + public static void renderSkytilsRarity(ItemStack stack, int x, int y) { + renderSkytilsRarity(stack, x, y, false); + } + + public static void renderSkytilsRarity(ItemStack stack, int x, int y, boolean force) { + if (Holder.isSkytilsFullyPresent) { + try { + if (force || (boolean) Holder.skytilsGetShowItemRarity.invoke(Holder.skytilsConfigObject)) + Holder.renderRarityMethod.invoke(null, stack, x, y); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java index 31499f7f..5fac9208 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java @@ -9,6 +9,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.miscfeatures.SlotLocking; +import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.PositionedSoundRecord; import net.minecraft.client.entity.EntityPlayerSP; @@ -175,8 +176,13 @@ public class Utils { } public static void drawItemStackWithText(ItemStack stack, int x, int y, String text) { - if (stack == null) return; + drawItemStackWithText(stack, x, y, text, false); + } + public static void drawItemStackWithText(ItemStack stack, int x, int y, String text, boolean skytilsRarity) { + if (stack == null) return; + if (skytilsRarity) + SkytilsCompat.renderSkytilsRarity(stack, x, y); RenderItem itemRender = Minecraft.getMinecraft().getRenderItem(); disableCustomDungColours = true; @@ -190,11 +196,13 @@ public class Utils { } public static void drawItemStack(ItemStack stack, int x, int y) { - if (stack == null) return; - drawItemStackWithText(stack, x, y, null); } + public static void drawItemStack(ItemStack stack, int x, int y, boolean skytilsRarity) { + drawItemStackWithText(stack, x, y, null, skytilsRarity); + } + private static final EnumChatFormatting[] rainbow = new EnumChatFormatting[]{ EnumChatFormatting.RED, EnumChatFormatting.GOLD, @@ -767,6 +775,9 @@ public class Utils { public static ItemStack createItemStack(Item item, String displayname, String... lore) { return createItemStack(item, displayname, 0, lore); } + public static ItemStack createItemStack(Block item, String displayname, String... lore) { + return createItemStack(Item.getItemFromBlock(item), displayname, lore); + } public static ItemStack createItemStack(Item item, String displayname, int damage, String... lore) { ItemStack stack = new ItemStack(item, 1, damage); |