/*
* Copyright (C) 2022 NotEnoughUpdates contributors
*
* This file is part of NotEnoughUpdates.
*
* NotEnoughUpdates is free software: you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* NotEnoughUpdates is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with NotEnoughUpdates. If not, see .
*/
package io.github.moulberry.notenoughupdates.recipes;
import com.google.common.collect.Sets;
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.core.util.StringUtils;
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.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.input.Keyboard;
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 alternatives;
public final List extra;
private ItemStack itemStack;
private int lastHoveredIndex = 0;
public MobDrop(Ingredient drop, String chance, List extra, List alternatives) {
this.drop = drop;
this.chance = chance;
this.extra = extra;
this.alternatives = alternatives;
}
public ItemStack getItemStack() {
if (!Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) {
lastHoveredIndex = (int) ((System.currentTimeMillis() / 2000) % (alternatives.size() + 1));
}
if (lastHoveredIndex == alternatives.size()) {
if (itemStack == null) {
itemStack = drop.getItemStack().copy();
List arrayList = new ArrayList<>(extra);
arrayList.add("§r§e§lDrop Chance: §6" + formatDropChance());
ItemUtils.appendLore(itemStack, arrayList);
}
return itemStack;
}
return alternatives.get(lastHoveredIndex).getItemStack();
}
private String formatDropChance() {
if (chance == null) {
return "";
}
if (!chance.endsWith("%")) {
return chance;
}
String chanceText = chance.substring(0, chance.length() - 1);
double chanceIn;
try {
chanceIn = (100.0 / Double.parseDouble(chanceText));
} catch (NumberFormatException e) {
return chance;
}
String format = StringUtils.formatNumber(chanceIn);
return "1/" + format + " (" + chance + ")";
}
}
public static ResourceLocation BACKGROUND = new ResourceLocation(
"notenoughupdates",
"textures/gui/mob_loot_tall.png"
);
private final Ingredient mobIngredient;
private final List 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 extra;
private EntityLivingBase entityLivingBase;
private final String panoName;
private ResourceLocation[] panos = null;
public MobLootRecipe(
Ingredient mobIngredient,
List drops,
int level,
int coins,
int xp,
int combatXp,
String name,
String render,
List 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 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 getIngredients() {
return Sets.newHashSet(mobIngredient);
}
@Override
public Set getCatalystItems() {
return Sets.newHashSet(mobIngredient);
}
@Override
public Set getOutputs() {
return drops
.stream()
.flatMap(it -> Stream.concat(Stream.of(it.drop), it.alternatives.stream().map(that -> that.drop)))
.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 getSlots() {
List slots = new ArrayList<>();
BiConsumer 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 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: " + combatXp);
stuff.addAll(extra);
Utils.drawHoveringText(stuff, mouseX, mouseY, gui.width, gui.height, -1);
}
}
@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;
}
private static MobDrop parseMobDrop(NEUManager manager, JsonElement jsonElement) {
if (jsonElement.isJsonPrimitive()) {
return (new MobDrop(
new Ingredient(manager, jsonElement.getAsString()),
null,
Collections.emptyList(),
Collections.emptyList()
));
} else {
JsonObject jsonObject = jsonElement.getAsJsonObject();
return (
new MobDrop(
new Ingredient(manager, jsonObject.get("id").getAsString()),
jsonObject.has("chance") ? jsonObject.get("chance").getAsString() : null,
JsonUtils.getJsonArrayOrEmpty(jsonObject, "extra", JsonElement::getAsString),
JsonUtils.getJsonArrayOrEmpty(jsonObject, "alternatives", element -> parseMobDrop(manager, element))
));
}
}
public static MobLootRecipe parseRecipe(NEUManager manager, JsonObject recipe, JsonObject outputItemJson) {
return new MobLootRecipe(
new Ingredient(manager, outputItemJson.get("internalname").getAsString(), 1),
JsonUtils.getJsonArrayOrEmpty(recipe, "drops", element -> parseMobDrop(manager, element)),
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"
);
}
}