package com.zivilon.cinder_loe.droptables; import com.zivilon.cinder_loe.util.ILootableEntity; import com.zivilon.cinder_loe.util.Utilities; import com.zivilon.cinder_loe.CinderLoE; import net.minecraft.nbt.*; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry; import lotr.common.entity.npc.LOTREntityNPC; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; public class DropTable { public static Random random = new Random(); public List drop_list; public DropTable() { drop_list = new ArrayList(); } public static void drop_items(LOTREntityNPC entity, DropContext[] context_flags, int looting_level) { List drops = generate_drops(entity, context_flags, looting_level); if (drops == null || drops.size() < 1) return; for (ItemStack drop : drops) { entity.npcDropItem(drop, 0.0F, false, false); } } public static List generate_drops(LOTREntityNPC entity, DropContext[] context_flags, int looting_level) { DropTable table = ((ILootableEntity)entity).get_drop_table(); if (table == null) { return null; } if (context_flags == null) { context_flags = new DropContext[0]; } List results = new ArrayList<>(); for (DropInstance drop : table.drop_list) { ItemStack stack = get_drop(drop, looting_level, context_flags); if (stack != null) { results.add(stack); } } return results; } public static ItemStack get_drop(DropInstance drop, int looting_level, DropContext[] context) { if (!(Utilities.array_contains_array(context, drop.conditions))) return null; if (drop instanceof SingleItemDrop) { return get_single_drop((SingleItemDrop)drop, looting_level); } if (drop instanceof ItemGroup) { return get_group_drop((ItemGroup)drop, looting_level); } return null; } public static ItemStack get_single_drop(SingleItemDrop drop, int looting_level) { float chance = drop.drop_chance; if (drop.looting_affects_chance) chance = 1.0F - (float)Math.pow(1.0F - chance, 1.0F + 0.1F * looting_level); if (chance < random.nextFloat()) return null; int range = drop.max_amount - drop.min_amount + 1; if (drop.looting_affects_quantity && looting_level > 0) range = (int)Math.ceil(range * (1.25F * looting_level)); int count = random.nextInt(range) + drop.min_amount; ItemStack stack = new ItemStack(drop.item, count); if (drop.nbt != null) stack.setTagCompound((NBTTagCompound)drop.nbt.copy()); return stack; } public static ItemStack get_group_drop(ItemGroup drop, int looting_level) { float chance = drop.drop_chance; if (drop.looting_affects_chance) chance = 1.0F - (float)Math.pow(1.0F - chance, 1.0F + 0.1F * looting_level); if (chance < random.nextFloat()) return null; int total_weight = drop.entries.stream().mapToInt(e -> e.weight).sum(); int pick = random.nextInt(total_weight); for (ItemGroupEntry entry : drop.entries) { if (pick < entry.weight) { int range = entry.max_amount - entry.min_amount + 1; if (entry.looting_affects_quantity && looting_level > 0) range = (int)Math.ceil(range * (1.25F * looting_level)); int count = random.nextInt(range) + entry.min_amount; ItemStack stack = new ItemStack(entry.item, count); if (entry.nbt != null) stack.setTagCompound((NBTTagCompound)entry.nbt.copy()); return stack; } pick -= entry.weight; } return null; } public static NBTTagCompound serialize_to_nbt(DropTable table) { NBTTagCompound nbt = new NBTTagCompound(); NBTTagList drop_list = new NBTTagList(); for (DropInstance drop : table.drop_list) { NBTTagCompound drop_tag = new NBTTagCompound(); drop_tag.setString("type", drop instanceof ItemGroup ? "group" : "single"); drop_tag.setFloat("drop_chance", drop.drop_chance); drop_tag.setInteger("min_amount", drop.min_amount); drop_tag.setInteger("max_amount", drop.max_amount); drop_tag.setBoolean("looting_affects_chance", drop.looting_affects_chance); NBTTagList condition_list = new NBTTagList(); if (drop instanceof SingleItemDrop) { SingleItemDrop single = (SingleItemDrop)drop; Item drop_item = single.item; drop_tag.setBoolean("looting_affects_quantity", single.looting_affects_quantity); drop_tag.setInteger("id", ((FMLControlledNamespacedRegistry)Item.itemRegistry).getId(drop_item)); if (single.nbt != null) drop_tag.setTag("nbt", single.nbt); } if (drop instanceof ItemGroup) { ItemGroup group = (ItemGroup) drop; NBTTagList entries = new NBTTagList(); for (ItemGroupEntry entry : group.entries) { NBTTagCompound entry_tag = new NBTTagCompound(); entry_tag.setInteger("id", Item.getIdFromItem(entry.item)); entry_tag.setInteger("weight", entry.weight); entry_tag.setInteger("min", entry.min_amount); entry_tag.setInteger("max", entry.max_amount); entry_tag.setBoolean("looting_quantity", entry.looting_affects_quantity); if (entry.nbt != null) { entry_tag.setTag("nbt", entry.nbt.copy()); } entries.appendTag(entry_tag); } drop_tag.setTag("entries", entries); } for (DropContext condition : drop.conditions) { condition_list.appendTag(new NBTTagString(condition.name())); } drop_tag.setTag("conditions", condition_list); drop_list.appendTag(drop_tag); nbt.setTag("Drops", drop_list); } return nbt; } public static DropTable deserialize_from_nbt(NBTTagCompound tag) { DropTable table = new DropTable(); if (!tag.hasKey("Drops")) return table; NBTTagList drop_list = tag.getTagList("Drops", 10); for (int i = 0; i < drop_list.tagCount(); i++) { NBTTagCompound drop_tag = drop_list.getCompoundTagAt(i); String type = drop_tag.getString("type"); float drop_chance = drop_tag.getFloat("drop_chance"); int min = drop_tag.getInteger("min_amount"); int max = drop_tag.getInteger("max_amount"); boolean looting_chance = drop_tag.getBoolean("looting_affects_chance"); // Read conditions DropContext[] conditions; if (drop_tag.hasKey("conditions")) { NBTTagList cond_list = drop_tag.getTagList("conditions", 8); conditions = new DropContext[cond_list.tagCount()]; for (int j = 0; j < cond_list.tagCount(); j++) { String ctx = cond_list.getStringTagAt(j); conditions[j] = DropContext.valueOf(ctx); } } else { conditions = new DropContext[0]; } // Construct drop if (type.equals("single")) { Item item = Item.getItemById(drop_tag.getInteger("id")); boolean looting_quantity = drop_tag.hasKey("looting_affects_quantity") && drop_tag.getBoolean("looting_affects_quantity"); NBTTagCompound nbt = drop_tag.hasKey("nbt") ? drop_tag.getCompoundTag("nbt") : null; SingleItemDrop single = new SingleItemDrop(item, nbt, drop_chance, min, max, looting_quantity, looting_chance, conditions); if (single != null) table.drop_list.add(single); if (single == null) System.out.println("[DropTable_deserializer] WARNING: Single drop was null!"); } else if (type.equals("group")) { List entries = new ArrayList<>(); if (drop_tag.hasKey("entries")) { NBTTagList entry_list = drop_tag.getTagList("entries", 10); // 10 = compound for (int j = 0; j < entry_list.tagCount(); j++) { NBTTagCompound entry_tag = entry_list.getCompoundTagAt(j); Item item = Item.getItemById(entry_tag.getInteger("id")); int weight = entry_tag.getInteger("weight"); int minAmt = entry_tag.getInteger("min"); int maxAmt = entry_tag.getInteger("max"); boolean lootQty = entry_tag.getBoolean("looting_quantity"); NBTTagCompound nbt = entry_tag.hasKey("nbt") ? entry_tag.getCompoundTag("nbt") : null; entries.add(new ItemGroupEntry(item, nbt, weight, minAmt, maxAmt, lootQty)); } } ItemGroup group = new ItemGroup(drop_chance, min, max, looting_chance, conditions, entries.toArray(new ItemGroupEntry[0])); if (group != null) table.drop_list.add(group); if (group == null) System.out.println("[DropTable_deserializer] WARNING: Group drop was null!"); } } return table; } public static class DropInstance { public float drop_chance; public int min_amount; public int max_amount; public boolean looting_affects_chance; public DropContext[] conditions; public DropInstance(float drop_chance, int min_amount, int max_amount, boolean looting_chance, DropContext[] conditions) { this.drop_chance = drop_chance; this.min_amount = min_amount; this.max_amount = max_amount; this.looting_affects_chance = looting_chance; this.conditions = conditions; } } public static class SingleItemDrop extends DropInstance { public Item item; public NBTTagCompound nbt; boolean looting_affects_quantity; public SingleItemDrop(Item item, NBTTagCompound nbt, float chance, int min, int max, boolean looting_quantity, boolean looting_chance, DropContext[] conditions) { super(chance, min, max, looting_chance, conditions); this.item = item; this.nbt = nbt; this.looting_affects_quantity = looting_quantity; } public SingleItemDrop(Item item, NBTTagCompound nbt, float chance, DropContext[] conditions) { super(chance, 1, 1, false, conditions); this.item = item; this.nbt = nbt; this.looting_affects_quantity = false; } } public static class ItemGroup extends DropInstance { public List entries; public ItemGroup(float chance, int min, int max, boolean looting_chance, DropContext[] conditions, ItemGroupEntry... drops) { super(chance, min, max, looting_chance, conditions); this.entries = Arrays.asList(drops); } } public static class ItemGroupEntry { public Item item; public int min_amount; public int max_amount; public NBTTagCompound nbt; public int weight; public boolean looting_affects_quantity; public ItemGroupEntry(Item item, NBTTagCompound nbt, int weight, int min_amount, int max_amount, boolean looting_quantity) { this.item = item; this.nbt = nbt; this.weight = weight; this.min_amount = min_amount; this.max_amount = max_amount; this.looting_affects_quantity = looting_quantity; } public ItemGroupEntry(Item item, NBTTagCompound nbt, int weight) { this.item = item; this.nbt = nbt; this.weight = weight; this.min_amount = 1; this.max_amount = 1; this.looting_affects_quantity = false; } } }