Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Loot Tables #7242

Open
wants to merge 33 commits into
base: dev/feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e9f97f8
Add Loot Tables
Burbulinis Dec 3, 2024
7a144b7
Optimize Imports
Burbulinis Dec 4, 2024
11b69c1
Fix docs and add a registry class info
Burbulinis Dec 4, 2024
a6cdeea
Changes
Burbulinis Dec 4, 2024
ea1a3da
remove license thing
Burbulinis Dec 4, 2024
097e56a
Add converter for LootTables - LootTable,
Burbulinis Dec 4, 2024
6294fee
add tests ? unsure if theyre correct and add expressions to get luck/…
Burbulinis Dec 4, 2024
a4bdced
Replace current implementation to module, add NameSpacedUtils class f…
Burbulinis Dec 5, 2024
e261ee8
Add docs, optimize imports and changes
Burbulinis Dec 6, 2024
458bf88
Change syntax and add some tests
Burbulinis Dec 6, 2024
b295696
Fix docs, fix tests, replace paper methods to bukkit ones and changes
Burbulinis Dec 6, 2024
ce3ec4f
one silly thing hehe
Burbulinis Dec 6, 2024
c7777df
Add JUnit tests, fix tests and changes
Burbulinis Dec 7, 2024
554a705
Fix JUnit test
Burbulinis Dec 7, 2024
4d576cb
ok junit test
Burbulinis Dec 7, 2024
4f90a8e
fix tests
Burbulinis Dec 7, 2024
3bc7b69
Optimize imports and fix description of EffGenerateLoot and ExprLootI…
Burbulinis Dec 7, 2024
9acbcba
Clean up LootTableUtils and update docs
Burbulinis Dec 9, 2024
f261551
update tests
Burbulinis Dec 9, 2024
12b47dc
add loot table type in the default.lang file
Burbulinis Dec 9, 2024
01aa136
Fix the seed expression not actually updating the state
Burbulinis Dec 9, 2024
2da8311
optimize imports
Burbulinis Dec 9, 2024
e14ff8d
change exampled for loot table seed
Burbulinis Dec 9, 2024
2038063
Add default event value for LootTable in LootGenerate event
Burbulinis Dec 9, 2024
4dfb8d9
Add escape character for colons
Burbulinis Dec 15, 2024
70977bc
Merge branch 'dev/feature' into feature/loottable
Burbulinis Dec 15, 2024
cc92b1b
Add LootContext event value in LootGenerateEvent
Burbulinis Dec 15, 2024
01e38a1
Make cached loot context transient
Burbulinis Dec 15, 2024
9fc767c
changes
Burbulinis Dec 15, 2024
d6658aa
tests and fix docs
Burbulinis Dec 15, 2024
8620a07
Merge remote-tracking branch 'fork/feature/loottable' into feature/lo…
Burbulinis Dec 15, 2024
748b1b3
alot of things
Burbulinis Dec 15, 2024
00454e4
Merge branch 'dev/feature' into feature/loottable
Burbulinis Dec 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/main/java/ch/njol/skript/classes/data/BukkitClasses.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.LootTables;
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
import org.bukkit.metadata.Metadatable;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
Expand Down Expand Up @@ -1559,6 +1562,69 @@ public String toVariableNameString(EnchantmentOffer eo) {
.name("Experience Cooldown Change Reason")
.description("Represents a change reason of an <a href='events.html#experience cooldown change event'>experience cooldown change event</a>.")
.since("INSERT VERSION"));

Classes.registerClass(new RegistryClassInfo<>(LootTables.class, Registry.LOOT_TABLES, "loottabletype", "loot table types")
.user("(loot ?)?table ?types?")
.name("Loot Table Types")
.description("Represents the type of loot table.")
.since("INSERT VERSION"));

Classes.registerClass(new ClassInfo<>(LootTable.class, "loottable")
.user("loot ?tables?")
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
.name("Loot Table")
.description("Loot tables represent what items should be in naturally generated containers, what items should be dropped when killing a mob, or what items can be fished. ")
.since("INSERT VERSION")
.parser(new Parser<>() {
@Override
public boolean canParse(ParseContext context) {
return false;
}

@Override
public @Nullable LootTable parse(String s, ParseContext context) {
return null;
}

@Override
public String toString(LootTable o, int flags) {
return "loot table '" + o.getKey().value() + '\'';
}

@Override
public String toVariableNameString(LootTable o) {
return "loot table '" + o.getKey().value() + '\'';
}
}));

Classes.registerClass(new ClassInfo<>(LootContext.class, "lootcontext")
.user("loot ?contexts?")
.name("Loot Context")
.description("Represents additional information a loot table can use to modify its generated loot.")
.since("INSERT VERSION")
.parser(new Parser<LootContext>() {
@Override
public boolean canParse(ParseContext context) {
return false;
}

@Override
public @Nullable LootContext parse(String s, ParseContext context) {
return null;
}

@Override
public String toString(LootContext lootContext, int flags) {
return "loot context at " + Classes.toString(lootContext.getLocation()) +
((lootContext.getLootedEntity() != null) ? (" with looted entity " + Classes.toString(lootContext.getLootedEntity())) : "") +
((lootContext.getKiller() != null) ? " with killer " + Classes.toString(lootContext.getKiller()) : "") +
((lootContext.getLuck() != 0) ? " with luck " + lootContext.getLuck() : "");
}

@Override
public String toVariableNameString(LootContext lootContext) {
return "loot context:" + lootContext.hashCode();
}
}));
}

}
15 changes: 10 additions & 5 deletions src/main/java/ch/njol/skript/classes/data/DefaultConverters.java
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@
import ch.njol.skript.util.EnchantmentType;
import ch.njol.skript.util.Experience;
import ch.njol.skript.util.slot.Slot;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.*;
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.DoubleChest;
Expand All @@ -48,6 +44,9 @@
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.LootTables;
import org.bukkit.util.Vector;
import org.skriptlang.skript.lang.converter.Converter;
import org.skriptlang.skript.lang.converter.Converters;
Expand Down Expand Up @@ -198,6 +197,12 @@ public DefaultConverters() {}

Converters.registerConverter(String.class, World.class, Bukkit::getWorld);

// LootContext - Location
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
Converters.registerConverter(LootContext.class, Location.class, LootContext::getLocation, Commands.CONVERTER_NO_COMMAND_ARGUMENTS);

// LootTables - LootTable
Converters.registerConverter(LootTables.class, LootTable.class, LootTables::getLootTable);

// // Entity - String (UUID) // Very slow, thus disabled for now
// Converters.registerConverter(String.class, Entity.class, new Converter<String, Entity>() {
//
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/ch/njol/skript/conditions/CondHasLootTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ch.njol.skript.conditions;

import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Condition;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.util.Kleenean;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.bukkit.loot.Lootable;
import org.jetbrains.annotations.Nullable;

@Name("Has Loot Table")
@Description("Checks whether an entity or block has a loot table. The loot tables of chests will be deleted when the chest is opened or broken.")
@Examples("if block has a loot table:")
@Since("INSERT VERSION")
public class CondHasLootTable extends Condition {

static {
Skript.registerCondition(CondHasLootTable.class,
"%entities/blocks% (has|have) [a] loot[ ]table",
"%entities/blocks% does(n't| not) have [a] loot[ ]table",
"%entities/blocks% (has|have) no loot[ ]table"
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
);}

private Expression<?> lootables;

@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
lootables = exprs[0];
setNegated(matchedPattern > 0);
return true;
}

@Override
public boolean check(Event event) {
return lootables.check(event, (lootable) -> {
if (lootable instanceof Lootable it) {
return it.hasLootTable();
} else if (lootable instanceof Block block) {
return block.getState() instanceof Lootable it && it.hasLootTable();
} else {
return false;
}
}, isNegated());
}

@Override
public String toString(@Nullable Event event, boolean debug) {
return lootables.toString(event, debug) + " has" + (isNegated() ? " no" : "") + " loot table";
}
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
}
34 changes: 34 additions & 0 deletions src/main/java/ch/njol/skript/conditions/CondIsLootable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ch.njol.skript.conditions;

import ch.njol.skript.conditions.base.PropertyCondition;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import org.bukkit.block.Block;
import org.bukkit.loot.Lootable;

@Name("Is Lootable")
@Description("Checks whether an entity or block is lootable. Lootables are entities or blocks that can have a loot table.")
@Examples("if entity is lootable:")
@Since("INSERT VERSION")
public class CondIsLootable extends PropertyCondition<Object> {

static {
PropertyCondition.register(CondIsLootable.class, "lootable", "blocks/entities");
}

@Override
public boolean check(Object object) {
if (object instanceof Lootable)
return true;
if (object instanceof Block block)
return block.getState() instanceof Lootable;
return false;
}

@Override
protected String getPropertyName() {
return "lootable";
}
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
}
78 changes: 78 additions & 0 deletions src/main/java/ch/njol/skript/effects/EffGenerateLoot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package ch.njol.skript.effects;

import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Effect;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.SyntaxStringBuilder;
import ch.njol.util.Kleenean;
import org.bukkit.event.Event;
import org.bukkit.inventory.Inventory;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import org.jetbrains.annotations.Nullable;

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

@Name("Generate Loot")
@Description({
"Generates the loot in the specified inventories from a loot table using a loot context.",
"Note that loot contexts require the killer and looted entity if the loot table is under the entities category. It only requires the location if the loot table is not in the entities category, eg. blocks, chests.",
"Also note that if the inventory is full, it will cause warnings in the console due to over-filling the inventory."
})
@Examples("the loot context at {_location}")
@Since("INSERT VERSION")
public class EffGenerateLoot extends Effect {

static {
Skript.registerEffect(EffGenerateLoot.class,
"generate loot (of|using) [loot[ ]table] %loottable% (with|using) [[loot] context] %lootcontext% in [inventor(y|ies)] %inventories%"
);
}

private Expression<LootTable> lootTable;
private Expression<LootContext> lootContext;
private Expression<Inventory> inventories;

@Override
@SuppressWarnings("unchecked")
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
lootTable = (Expression<LootTable>) exprs[0];
lootContext = (Expression<LootContext>) exprs[1];
inventories = (Expression<Inventory>) exprs[2];
return true;
}

@Override
protected void execute(Event event) {
Random random = ThreadLocalRandom.current();

LootContext context = lootContext.getSingle(event);
LootTable table = lootTable.getSingle(event);
if (context == null || table == null)
return;

for (Inventory inventory : inventories.getArray(event)) {
try {
table.fillInventory(inventory, random, context);
}
catch (IllegalArgumentException ignore) {}
}
}

@Override
public String toString(@Nullable Event event, boolean debug) {
SyntaxStringBuilder builder = new SyntaxStringBuilder(event, debug);

builder.append("generate loot using loot table", lootTable);
builder.append("with context", lootContext);
builder.append("in inventories", inventories);

return builder.toString();
}
}
66 changes: 66 additions & 0 deletions src/main/java/ch/njol/skript/expressions/ExprLootContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package ch.njol.skript.expressions;

import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.util.Direction;
import ch.njol.util.Kleenean;
import org.bukkit.Location;
import org.bukkit.event.Event;
import org.bukkit.loot.LootContext;
import org.jetbrains.annotations.Nullable;

@Name("Loot Context")
@Description("Creates a loot context at a location. Loot contexts help determine the loot that would be dropped from a loot table.")
@Examples("the loot context at {_location}")
@Since("INSERT VERSION")
public class ExprLootContext extends SimpleExpression<LootContext> {

static {
Skript.registerExpression(ExprLootContext.class, LootContext.class, ExpressionType.COMBINED,
"[the] loot[ ]context %direction% %location%"
);
}

private Expression<Location> location;

@Override
@SuppressWarnings("unchecked")
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
location = (Expression<Location>) exprs[1];
if (exprs[0] != null)
location = Direction.combine((Expression<? extends Direction>) exprs[0], location);

return true;
}

@Nullable
@Override
protected LootContext[] get(Event event) {
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
Location loc = location.getSingle(event);
if (loc == null)
return new LootContext[0];
return new LootContext[]{new LootContext.Builder(loc).build()};
}

@Override
public boolean isSingle() {
return true;
}

@Override
public Class<? extends LootContext> getReturnType() {
return LootContext.class;
}

@Override
public String toString(@Nullable Event event, boolean debug) {
return "loot context " + location.toString(event, debug);
}
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ch.njol.skript.expressions;

import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Examples;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.expressions.base.SimplePropertyExpression;
import org.bukkit.entity.Entity;
import org.bukkit.loot.LootContext;

@Name("Looted Entity of Loot Context")
@Description("Returns the looted entity of a loot context.")
@Examples("looted entity of {_context}")
@Since("INSERT VERSION")
public class ExprLootContextEntity extends SimplePropertyExpression<LootContext, Entity> {

static {
register(ExprLootContextEntity.class, Entity.class, "[looted] entity", "lootcontexts");
}

@Override
public Entity convert(LootContext context) {
return context.getLootedEntity();
}

@Override
public Class<? extends Entity> getReturnType() {
return Entity.class;
}

@Override
protected String getPropertyName() {
return "looted entity";
}
Burbulinis marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading