Skip to content

Commit

Permalink
Adds Loot Tables (#7242)
Browse files Browse the repository at this point in the history
* Add Loot Tables

* Optimize Imports

* Fix docs and add a registry class info

* Changes

* remove license thing

* Add converter for LootTables - LootTable,
Change toString for LootTable class info,
Change syntax

* add tests ? unsure if theyre correct and add expressions to get luck/killer/entity of context

* Replace current implementation to module, add NameSpacedUtils class from the Recipe pull request, and changes

* Add docs, optimize imports and changes

* Change syntax and add some tests

* Fix docs, fix tests, replace paper methods to bukkit ones and changes

* one silly thing hehe

* Add JUnit tests, fix tests and changes

* Fix JUnit test

* ok junit test

* fix tests

* Optimize imports and fix description of EffGenerateLoot and ExprLootItems

* Clean up LootTableUtils and update docs

* update tests

* add loot table type in the default.lang file

* Fix the seed expression not actually updating the state

* optimize imports

* change exampled for loot table seed

* Add default event value for LootTable in LootGenerate event

* Add escape character for colons

* Add LootContext event value in LootGenerateEvent

* Make cached loot context transient

* changes

* tests and fix docs

* alot of things

* fix toString

* remove consumer

* fix tests

* optimize imports

* Fix exception in Java 17

* hopefully one last thing

* move LootGenerateEvent and event values to module class because idk why i didn't have it there

* optimize imports and another one

* pickel era changes

* update tests

* more pickel era changes

* uh oh

* more uh ohs

* even more uh ohs

* we dont talk about it

* small changes

* optimize le importos

* even more pickel era changes

* wow pickel has alot of changes

* Move ExprLoot to module and clean it up

* changes

---------

Co-authored-by: Moderocky <admin@moderocky.com>
Co-authored-by: Efnilite <35348263+Efnilite@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 24, 2024
1 parent b0be357 commit b123b88
Show file tree
Hide file tree
Showing 30 changed files with 1,492 additions and 67 deletions.
2 changes: 2 additions & 0 deletions src/main/java/ch/njol/skript/Skript.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
import org.skriptlang.skript.bukkit.displays.DisplayModule;
import org.skriptlang.skript.bukkit.fishing.FishingModule;
import org.skriptlang.skript.bukkit.input.InputModule;
import org.skriptlang.skript.bukkit.loottables.LootTableModule;
import org.skriptlang.skript.lang.comparator.Comparator;
import org.skriptlang.skript.lang.comparator.Comparators;
import org.skriptlang.skript.lang.converter.Converter;
Expand Down Expand Up @@ -549,6 +550,7 @@ public void onEnable() {
BreedingModule.load();
DisplayModule.load();
InputModule.load();
LootTableModule.load();
} catch (final Exception e) {
exception(e, "Could not load required .class files: " + e.getLocalizedMessage());
setEnabled(false);
Expand Down
18 changes: 0 additions & 18 deletions src/main/java/ch/njol/skript/classes/data/BukkitEventValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -1629,24 +1629,6 @@ public ItemType get(PlayerStopUsingItemEvent event) {
}, EventValues.TIME_NOW);
}

// LootGenerateEvent
if (Skript.classExists("org.bukkit.event.world.LootGenerateEvent")) {
EventValues.registerEventValue(LootGenerateEvent.class, Entity.class, new Getter<Entity, LootGenerateEvent>() {
@Override
@Nullable
public Entity get(LootGenerateEvent event) {
return event.getEntity();
}
}, EventValues.TIME_NOW);
EventValues.registerEventValue(LootGenerateEvent.class, Location.class, new Getter<Location, LootGenerateEvent>() {
@Override
@Nullable
public Location get(LootGenerateEvent event) {
return event.getLootContext().getLocation();
}
}, EventValues.TIME_NOW);
}

// EntityResurrectEvent
EventValues.registerEventValue(EntityResurrectEvent.class, Slot.class, new Getter<Slot, EntityResurrectEvent>() {
@Override
Expand Down
16 changes: 1 addition & 15 deletions src/main/java/ch/njol/skript/events/SimpleEvents.java
Original file line number Diff line number Diff line change
Expand Up @@ -602,21 +602,7 @@ public class SimpleEvents {
"\t\tset chat format to \"&lt;orange&gt;[player]&lt;light gray&gt;: &lt;white&gt;[message]\""
)
.since("1.4.1");
if (Skript.classExists("org.bukkit.event.world.LootGenerateEvent")) {
Skript.registerEvent("Loot Generate", SimpleEvent.class, LootGenerateEvent.class, "loot generat(e|ing)")
.description(
"Called when a loot table of an inventory is generated in the world.",
"For example, when opening a shipwreck chest."
)
.examples(
"on loot generate:",
"\tchance of 10%",
"\tadd 64 diamonds to the loot",
"\tsend \"You hit the jackpot at %event-location%!\""
)
.since("2.7")
.requiredPlugins("MC 1.16+");
}

if (Skript.classExists("io.papermc.paper.event.player.PlayerDeepSleepEvent")) {
Skript.registerEvent("Player Deep Sleep", SimpleEvent.class, PlayerDeepSleepEvent.class, "[player] deep sleep[ing]")
.description(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.skriptlang.skript.bukkit.loottables;

import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.skriptlang.skript.bukkit.loottables.elements.expressions.ExprSecCreateLootContext;

/**
* The event used in the {@link ExprSecCreateLootContext} section.
*/
public class LootContextCreateEvent extends Event {

private final LootContextWrapper contextWrapper;

public LootContextCreateEvent(LootContextWrapper context) {
this.contextWrapper = context;
}

public LootContextWrapper getContextWrapper() {
return contextWrapper;
}

@Override
public HandlerList getHandlers() {
throw new UnsupportedOperationException();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package org.skriptlang.skript.bukkit.loottables;

import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.loot.LootContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Wrapper for a LootContext.Builder to allow easier creation of LootContexts.
*/
public class LootContextWrapper {

private @NotNull Location location;
private transient @Nullable LootContext cachedLootContext;
private @Nullable Player killer;
private @Nullable Entity entity;
private float luck;

/**
* Creates a new LootContextWrapper at the given location.
* @param location the location of the LootContext.
*/
public LootContextWrapper(@NotNull Location location) {
this.location = location;
}

/**
* Gets the LootContext from the wrapper.
* @return the LootContext.
*/
public LootContext getContext() {
if (cachedLootContext == null)
cachedLootContext = new LootContext.Builder(location)
.killer(killer)
.lootedEntity(entity)
.luck(luck)
.build();

return cachedLootContext;
}

/**
* Sets the location of the LootContext.
* @param location the location.
*/
public void setLocation(@NotNull Location location) {
this.location = location;
cachedLootContext = null;
}

/**
* Sets the killer of the LootContext.
* @param killer the killer.
*/
public void setKiller(@Nullable Player killer) {
this.killer = killer;
cachedLootContext = null;
}

/**
* Sets the entity of the LootContext.
* @param entity the entity.
*/
public void setEntity(@Nullable Entity entity) {
this.entity = entity;
cachedLootContext = null;
}

/**
* Sets the luck of the LootContext.
* @param luck the luck value.
*/
public void setLuck(float luck) {
this.luck = luck;
cachedLootContext = null;
}

/**
* Gets the location of the LootContext.
* @return the location.
*/
public Location getLocation() {
return location;
}

/**
* Gets the killer of the LootContext.
* @return the killer.
*/
public @Nullable Player getKiller() {
return killer;
}

/**
* Gets the entity of the LootContext.
* @return the entity.
*/
public @Nullable Entity getEntity() {
return entity;
}

/**
* Gets the luck of the LootContext.
* @return the luck value.
*/
public float getLuck() {
return luck;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package org.skriptlang.skript.bukkit.loottables;

import ch.njol.skript.Skript;
import ch.njol.skript.classes.ClassInfo;
import ch.njol.skript.classes.Parser;
import ch.njol.skript.expressions.base.EventValueExpression;
import ch.njol.skript.lang.ParseContext;
import ch.njol.skript.lang.util.SimpleEvent;
import ch.njol.skript.registrations.Classes;
import ch.njol.skript.registrations.EventValues;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.event.world.LootGenerateEvent;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;

public class LootTableModule {

public static void load() throws IOException {

// --- CLASSES --- //

Classes.registerClass(new ClassInfo<>(LootTable.class, "loottable")
.user("loot ?tables?")
.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.",
"You can find more information about this in https://minecraft.wiki/w/Loot_table"
)
.since("INSERT VERSION")
.parser(new Parser<>() {
@Override
public @Nullable LootTable parse(String key, ParseContext context) {
NamespacedKey namespacedKey = NamespacedKey.fromString(key);
if (namespacedKey == null)
return null;
return Bukkit.getLootTable(namespacedKey);
}

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

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

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.",
"",
"Some loot tables will require some values (i.e. looter, location, looted entity) "
+ "in a loot context when generating loot whereas others may not.",
"For example, the loot table of a simple dungeon chest will only require a location, "
+ "whereas the loot table of a cow will require a looting player, looted entity, and location.",
"You can find more information about this in https://minecraft.wiki/w/Loot_context"
)
.since("INSERT VERSION")
.defaultExpression(new EventValueExpression<>(LootContext.class))
.parser(new Parser<>() {
@Override
public boolean canParse(ParseContext context) {
return false;
}

@Override
public String toString(LootContext context, int flags) {
StringBuilder builder = new StringBuilder("loot context at ")
.append(Classes.toString(context.getLocation()));

if (context.getLootedEntity() != null)
builder.append(" with entity ").append(Classes.toString(context.getLootedEntity()));
if (context.getKiller() != null)
builder.append(" with killer ").append(Classes.toString(context.getKiller()));
if (context.getLuck() != 0)
builder.append(" with luck ").append(context.getLuck());

return builder.toString();
}

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

Skript.getAddonInstance().loadClasses("org.skriptlang.skript.bukkit.loottables", "elements");

// --- SIMPLE EVENTS --- //

Skript.registerEvent("Loot Generate", SimpleEvent.class, LootGenerateEvent.class, "loot generat(e|ing)")
.description(
"Called when a loot table of an inventory is generated in the world.",
"For example, when opening a shipwreck chest."
)
.examples(
"on loot generate:",
"\tchance of 10%",
"\tadd 64 diamonds to the loot",
"\tsend \"You hit the jackpot at %event-location%!\""
)
.since("2.7")
.requiredPlugins("MC 1.16+");

// --- EVENT VALUES --- //

// LootGenerateEvent
EventValues.registerEventValue(LootGenerateEvent.class, Entity.class, LootGenerateEvent::getEntity);
EventValues.registerEventValue(LootGenerateEvent.class, Location.class, event -> event.getLootContext().getLocation());
EventValues.registerEventValue(LootGenerateEvent.class, LootTable.class, LootGenerateEvent::getLootTable);
EventValues.registerEventValue(LootGenerateEvent.class, LootContext.class, LootGenerateEvent::getLootContext);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.skriptlang.skript.bukkit.loottables;

import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.Lootable;

/**
* Utility class for loot tables.
*/
public class LootTableUtils {

/**
* * Checks whether a block or entity is an instance of {@link Lootable}. This is done because a block is not an instance of Lootable, but a block state is.
* @param object the object to check.
* @return whether the object is lootable.
*/
public static boolean isLootable(Object object) {
if (object instanceof Block block)
object = block.getState();
return object instanceof Lootable;
}

/**
* Gets the Lootable instance of an object. You should call {@link #isLootable(Object)} before calling this method.
* @param object the object to get the Lootable instance of.
* @return the Lootable instance of the object.
*/
public static Lootable getAsLootable(Object object) {
if (object instanceof Block block)
object = block.getState();
if (object instanceof Lootable lootable)
return lootable;
return null;
}

/**
* Gets the loot table of an object. You should call {@link #isLootable(Object)} before calling this method.
* @param object the object to get the loot table of.
* @return returns the LootTable of the object.
*/
public static LootTable getLootTable(Object object) {
return getAsLootable(object).getLootTable();
}

/**
* Updates the state of a Lootable. This is done because setting the LootTable or seed of a BlockState changes the NBT value, but is never updated.
* @param lootable the Lootable to update the state of.
*/
public static void updateState(Lootable lootable) {
if (lootable instanceof BlockState blockState)
blockState.update(true, false);
}

}
Loading

0 comments on commit b123b88

Please sign in to comment.