mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-01 05:58:00 +01:00
Loot table entry types + Defaults for loot table functions and conditions
This commit is contained in:
parent
5cb31171e6
commit
fe3025fce5
@ -14,4 +14,7 @@ public interface Condition {
|
||||
* @return 'true' if the condition passed, 'false' otherwise
|
||||
*/
|
||||
boolean test(Data data);
|
||||
|
||||
Condition ALWAYS_YES = (_d) -> true;
|
||||
Condition ALWAYS_NO = (_d) -> false;
|
||||
}
|
||||
|
@ -83,12 +83,19 @@ public class LootTable {
|
||||
int rollCount = rng.nextInt(maxRollCount - minRollCount +1 /*inclusive*/) + minRollCount;
|
||||
int bonusRollCount = rng.nextInt(bonusMaxRollCount - bonusMinRollCount +1 /*inclusive*/) + bonusMinRollCount;
|
||||
bonusRollCount *= luck;
|
||||
// TODO: implement luck (quality/weight) weight=floor( weight + (quality * generic.luck))
|
||||
WeightedRandom<Entry> weightedRandom = new WeightedRandom<>(entries);
|
||||
for (int i = 0; i < rollCount+bonusRollCount; i++) {
|
||||
Entry entry = weightedRandom.get(rng);
|
||||
ItemStack stack = entry.generateStack(arguments);
|
||||
if(!stack.isAir()) {
|
||||
output.add(stack);
|
||||
boolean shouldGenerate = true;
|
||||
for(Condition c : entry.getConditions()) {
|
||||
if(!c.test(arguments)) {
|
||||
shouldGenerate = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(shouldGenerate) {
|
||||
entry.generateStacks(output, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,11 +105,17 @@ public class LootTable {
|
||||
private final LootTableEntryType type;
|
||||
private final int weight;
|
||||
private final int quality;
|
||||
private final List<Condition> conditions;
|
||||
|
||||
public Entry(LootTableEntryType type, int weight, int quality) {
|
||||
public Entry(LootTableEntryType type, int weight, int quality, List<Condition> conditions) {
|
||||
this.type = type;
|
||||
this.weight = weight;
|
||||
this.quality = quality;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
public List<Condition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
public int getQuality() {
|
||||
@ -117,6 +130,6 @@ public class LootTable {
|
||||
return type;
|
||||
}
|
||||
|
||||
public abstract ItemStack generateStack(Data arguments);
|
||||
public abstract void generateStacks(List<ItemStack> output, Data arguments);
|
||||
}
|
||||
}
|
||||
|
@ -16,5 +16,7 @@ public interface LootTableFunction {
|
||||
* @return
|
||||
*/
|
||||
ItemStack apply(ItemStack stack, Data data);
|
||||
|
||||
LootTableFunction IDENTITY = (stack, _d) -> stack;
|
||||
}
|
||||
|
||||
|
@ -88,19 +88,43 @@ public class LootTableManager {
|
||||
return container.createTable(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered condition corresponding to the given namespace ID. If none is registered, returns {@link Condition#ALWAYS_NO}.
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public Condition getCondition(NamespaceID id) {
|
||||
return conditions.get(id);
|
||||
return conditions.getOrDefault(id, Condition.ALWAYS_NO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered table type corresponding to the given namespace ID. If none is registered, throws {@link IllegalArgumentException}
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public LootTableType getTableType(NamespaceID id) {
|
||||
if(!tableTypes.containsKey(id))
|
||||
throw new IllegalArgumentException("Unknown table type: "+id);
|
||||
return tableTypes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered entry type corresponding to the given namespace ID. If none is registered, throws {@link IllegalArgumentException}
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public LootTableEntryType getEntryType(NamespaceID id) {
|
||||
if(!entryTypes.containsKey(id))
|
||||
throw new IllegalArgumentException("Unknown entry type: "+id);
|
||||
return entryTypes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered table type corresponding to the given namespace ID. If none is registered, returns {@link LootTableFunction#IDENTITY}
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public LootTableFunction getFunction(NamespaceID id) {
|
||||
return functions.get(id);
|
||||
return functions.getOrDefault(id, LootTableFunction.IDENTITY);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AlternativesEntry extends LootTable.Entry {
|
||||
private final List<LootTable.Entry> children;
|
||||
|
||||
public AlternativesEntry(AlternativesType type, List<LootTable.Entry> children, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
for(LootTable.Entry c : children) {
|
||||
int previousSize = output.size();
|
||||
c.generateStacks(output, arguments);
|
||||
int newSize = previousSize;
|
||||
if(newSize != previousSize) { // an entry managed to generate, stop here
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:alternatives
|
||||
*/
|
||||
public class AlternativesType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new AlternativesEntry(this, children, weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AnotherLootTableEntry extends LootTable.Entry {
|
||||
private final LootTable table;
|
||||
|
||||
public AnotherLootTableEntry(AnotherLootTableType type, LootTable table, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
output.addAll(table.generate(arguments));
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Allows to sample from a different loot table
|
||||
*
|
||||
* minecraft:loot_table
|
||||
*/
|
||||
public class AnotherLootTableType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
try {
|
||||
return new AnotherLootTableEntry(this, lootTableManager.load(NamespaceID.from(name)), weight, quality, conditions);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IllegalArgumentException(name+" is not a valid loot table name", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class DynamicEntry extends LootTable.Entry {
|
||||
|
||||
public static final String DROP_LIST_KEY = "minestom:loot_table_drop_list";
|
||||
|
||||
private final DynamicEntry.Type entryType;
|
||||
|
||||
public DynamicEntry(DynamicType type, DynamicEntry.Type entryType, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.entryType = entryType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
List<ItemStack> toDrop = arguments.getOrDefault(DROP_LIST_KEY, Collections.emptyList());
|
||||
output.addAll(toDrop);
|
||||
}
|
||||
|
||||
public DynamicEntry.Type getEntryType() {
|
||||
return entryType;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
SELF,
|
||||
CONTENTS;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:dynamic
|
||||
*/
|
||||
public class DynamicType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new DynamicEntry(this, DynamicEntry.Type.valueOf(name.toUpperCase()), weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GroupEntry extends LootTable.Entry {
|
||||
private final List<LootTable.Entry> children;
|
||||
|
||||
public GroupEntry(GroupType type, List<LootTable.Entry> children, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
for (LootTable.Entry child : children) {
|
||||
child.generateStacks(output, arguments);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:group
|
||||
*/
|
||||
public class GroupType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new GroupEntry(this, children, weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -13,37 +13,29 @@ import java.util.List;
|
||||
public class ItemEntry extends LootTable.Entry {
|
||||
|
||||
private final List<LootTableFunction> functions;
|
||||
private final List<Condition> conditions;
|
||||
private final Material item;
|
||||
|
||||
ItemEntry(ItemType type, Material baseItem, int weight, int quality, List<LootTableFunction> functions, List<Condition> conditions) {
|
||||
super(type, weight, quality);
|
||||
super(type, weight, quality, conditions);
|
||||
this.item = baseItem;
|
||||
this.functions = new LinkedList<>(functions);
|
||||
this.conditions = new LinkedList<>(conditions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack generateStack(Data arguments) {
|
||||
for(Condition c : conditions) {
|
||||
if(!c.test(arguments))
|
||||
return ItemStack.getAirItem();
|
||||
}
|
||||
public void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
ItemStack stack = new ItemStack(item, (byte)1);
|
||||
for (LootTableFunction function : functions) {
|
||||
stack = function.apply(stack, arguments);
|
||||
}
|
||||
return stack;
|
||||
if(!stack.isAir()) {
|
||||
output.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public List<LootTableFunction> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public List<Condition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
public Material getItem() {
|
||||
return item;
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:item
|
||||
*/
|
||||
public class ItemType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
|
@ -0,0 +1,29 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SequenceEntry extends LootTable.Entry {
|
||||
private final List<LootTable.Entry> children;
|
||||
|
||||
public SequenceEntry(SequenceType type, List<LootTable.Entry> children, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
for(LootTable.Entry c : children) {
|
||||
int previousSize = output.size();
|
||||
c.generateStacks(output, arguments);
|
||||
int newSize = previousSize;
|
||||
if(newSize == previousSize) { // an entry failed to generate, stop here
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:sequence
|
||||
*/
|
||||
public class SequenceType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new SequenceEntry(this, children, weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TagEntry extends LootTable.Entry {
|
||||
// TODO: replace with Tag reference
|
||||
private final String name;
|
||||
private final boolean expand;
|
||||
|
||||
TagEntry(TagType type, String name, boolean expand, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.name = name;
|
||||
this.expand = expand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
// TODO: load tags
|
||||
if(expand) {
|
||||
// TODO: choose a single random item from the tag
|
||||
} else {
|
||||
// TODO: add all items from the tag
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:tag
|
||||
*/
|
||||
public class TagType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new TagEntry(this, name, expand, weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -72,26 +72,6 @@ public class TestLootTables {
|
||||
|
||||
@Test
|
||||
public void loadFromFile() throws FileNotFoundException {
|
||||
// from acacia_button.json
|
||||
final String lootTableJson = "{\n" +
|
||||
" \"type\": \"minecraft:block\",\n" +
|
||||
" \"pools\": [\n" +
|
||||
" {\n" +
|
||||
" \"rolls\": 1,\n" +
|
||||
" \"entries\": [\n" +
|
||||
" {\n" +
|
||||
" \"type\": \"minecraft:item\",\n" +
|
||||
" \"name\": \"minecraft:acacia_button\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"conditions\": [\n" +
|
||||
" {\n" +
|
||||
" \"condition\": \"minecraft:survives_explosion\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
LootTable lootTable = tableManager.load(NamespaceID.from("blocks/acacia_button"));
|
||||
Assert.assertTrue(lootTable.getType() instanceof BlockType);
|
||||
Assert.assertEquals(1, lootTable.getPools().size());
|
||||
@ -132,4 +112,31 @@ public class TestLootTables {
|
||||
List<ItemStack> stacks = lootTable.generate(arguments);
|
||||
Assert.assertEquals(0, stacks.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownCondition() {
|
||||
// from acacia_button.json
|
||||
final String lootTableJson = "{\n" +
|
||||
" \"type\": \"minecraft:block\",\n" +
|
||||
" \"pools\": [\n" +
|
||||
" {\n" +
|
||||
" \"rolls\": 1,\n" +
|
||||
" \"entries\": [\n" +
|
||||
" {\n" +
|
||||
" \"type\": \"minecraft:item\",\n" +
|
||||
" \"name\": \"minecraft:acacia_button\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"conditions\": [\n" +
|
||||
" {\n" +
|
||||
" \"condition\": \"minestom:unknown\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
LootTable lootTable = tableManager.load(NamespaceID.from("blocks/none"), new StringReader(lootTableJson));
|
||||
List<ItemStack> stacks = lootTable.generate(Data.EMPTY);
|
||||
Assert.assertEquals(0, stacks.size());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user