Skip to content

Commit

Permalink
added method for adding loot to chests
Browse files Browse the repository at this point in the history
  • Loading branch information
Trinsdar committed Feb 2, 2025
1 parent 005ba15 commit da246af
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 3 deletions.
3 changes: 2 additions & 1 deletion common/src/main/java/muramasa/antimatter/Antimatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import muramasa.antimatter.data.AntimatterMaterials;
import muramasa.antimatter.data.AntimatterStoneTypes;
import muramasa.antimatter.datagen.AntimatterDynamics;
import muramasa.antimatter.datagen.AntimatterLoot;
import muramasa.antimatter.datagen.loaders.MaterialRecipes;
import muramasa.antimatter.datagen.loaders.StoneRecipes;
import muramasa.antimatter.datagen.providers.AntimatterBlockLootProvider;
Expand Down Expand Up @@ -149,7 +150,7 @@ protected void processTags(String domain) {
public void onRegistrationEvent(RegistrationEvent event, Side side) {
if (event == RegistrationEvent.DATA_INIT) {
Recipe.init();

AntimatterLoot.RandomWeightLootFunction.init();
SlotType.init();
RecipeBuilders.init();
MachineState.init();
Expand Down
197 changes: 197 additions & 0 deletions common/src/main/java/muramasa/antimatter/datagen/AntimatterLoot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package muramasa.antimatter.datagen;

import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import muramasa.antimatter.Antimatter;
import muramasa.antimatter.Ref;
import muramasa.antimatter.mixin.LootPoolAccessor;
import muramasa.antimatter.recipe.RecipeUtil;
import muramasa.antimatter.util.AntimatterPlatformUtils;
import muramasa.antimatter.util.ItemStackHashStrategy;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
import net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction;
import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
import net.minecraft.world.level.storage.loot.functions.LootItemFunctionType;
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

public class AntimatterLoot {

private static final Map<ResourceLocation, List<LootEntryItem>> lootEntryItems = new Object2ObjectOpenHashMap<>();
private static final Map<ResourceLocation, NumberProvider> rollValues = new Object2ObjectOpenHashMap<>();

private static final LootItemCondition[] NO_CONDITIONS = new LootItemCondition[0];

public static void onLootTableLoad(LootPool mainPool, ResourceLocation name) {
if (mainPool == null) return;

if (lootEntryItems.containsKey(name)) {
List<LootEntryItem> entryItems = lootEntryItems.get(name);
for (LootEntryItem entry : entryItems) {
/*if (ConfigHolder.INSTANCE.dev.debug) {
Antimatter.LOGGER.info("adding {} to lootTable {}", entry, name);
}*/

try {
LootPoolEntryContainer[] entries = ((LootPoolAccessor) mainPool).getEntries();
entries = ArrayUtils.add(entries, entry);
((LootPoolAccessor) mainPool).setEntries(entries);
} catch (RuntimeException e) {
Antimatter.LOGGER.error("Couldn't add {} to lootTable {}: {}", entry, name, e.getMessage());
}
}
}

if (rollValues.containsKey(name)) {
NumberProvider rangeAdd = rollValues.get(name);
NumberProvider range = ((LootPoolAccessor)mainPool).getRolls();
// mainPool.setRolls(UniformGenerator.between(range.getMin() + rangeAdd.getMin(), range.getMax() +
// rangeAdd.getMax())); TODO additional rolls
}
}

public static void addItem(@NotNull ResourceLocation lootTable, @NotNull ItemStack stack, int minAmount,
int maxAmount, int weight) {
RandomWeightLootFunction lootFunction = new RandomWeightLootFunction(stack, minAmount, maxAmount);
String modid = Objects.requireNonNull(AntimatterPlatformUtils.INSTANCE.getIdFromItem(stack.getItem())).getNamespace();
String entryName = createEntryName(stack, modid, weight, lootFunction);
LootEntryItem itemEntry = new LootEntryItem(stack, weight, lootFunction, entryName);
lootEntryItems.computeIfAbsent(lootTable, $ -> new ArrayList<>()).add(itemEntry);
}

public static void addRolls(ResourceLocation tableLocation, int minAdd, int maxAdd) {
rollValues.put(tableLocation, UniformGenerator.between(minAdd, maxAdd));
}

private static final ItemStackHashStrategy HASH_STRATEGY = ItemStackHashStrategy.comparingAllButCount();

private static @NotNull String createEntryName(@NotNull ItemStack stack, @NotNull String modid, int weight,
@NotNull RandomWeightLootFunction function) {
int hashCode = Objects.hash(HASH_STRATEGY.hashCode(stack), modid, weight, function.getMinAmount(),
function.getMaxAmount());
return String.format("#%s:loot_%s", modid, hashCode);
}

private static class LootEntryItem extends LootItem {

private final ItemStack stack;
private final String entryName;

public LootEntryItem(@NotNull ItemStack stack, int weight, LootItemFunction lootFunction,
@NotNull String entryName) {
super(stack.getItem(), weight, 1, NO_CONDITIONS, new LootItemFunction[] { lootFunction });
this.stack = stack;
this.entryName = entryName;
}

public void createItemStack(Consumer<ItemStack> stackConsumer, LootContext lootContext) {
stackConsumer.accept(this.stack.copy());
}

@Override
public @NotNull String toString() {
return "LootEntryItem{name=" + entryName + ", stack=" + stack.toString() + '}';
}
}

public static class RandomWeightLootFunction extends LootItemConditionalFunction implements LootItemFunction {

public static final LootItemFunctionType TYPE = Registry.register(Registry.LOOT_FUNCTION_TYPE,
new ResourceLocation(Ref.ID,"random_weight"), new LootItemFunctionType(new Serializer()));

private final ItemStack stack;
@Getter
private final int minAmount;
@Getter
private final int maxAmount;

public RandomWeightLootFunction(@NotNull ItemStack stack, int minAmount, int maxAmount) {
super(NO_CONDITIONS);
Preconditions.checkArgument(minAmount <= maxAmount, "minAmount must be <= maxAmount");
this.stack = stack;
this.minAmount = minAmount;
this.maxAmount = maxAmount;
}

public static void init() {
// Do nothing here. This just ensures that TYPE is being set immediately when called.
}

@Override
public LootItemFunctionType getType() {
return TYPE;
}

@Override
protected ItemStack run(ItemStack itemStack, LootContext context) {
if (stack.getDamageValue() != 0) {
itemStack.setDamageValue(stack.getDamageValue());
}
CompoundTag tagCompound = stack.getTag();
if (tagCompound != null) {
itemStack.setTag(tagCompound.copy());
}

if (minAmount == maxAmount) {
itemStack.setCount(minAmount);
return itemStack;
}

int count = Math.min(minAmount + context.getRandom().nextInt(maxAmount - minAmount + 1),
stack.getMaxStackSize());
itemStack.setCount(count);
return itemStack;
}

public static class Serializer extends LootItemConditionalFunction.Serializer<RandomWeightLootFunction> {

/**
* Serialize the {@link SetItemCountFunction} by putting its data into the JsonObject.
*/
public void serialize(JsonObject json, RandomWeightLootFunction setItemCountFunction,
JsonSerializationContext serializationContext) {
super.serialize(json, setItemCountFunction, serializationContext);
json.add("min", serializationContext.serialize(setItemCountFunction.minAmount));
json.add("max", serializationContext.serialize(setItemCountFunction.maxAmount));
JsonObject stack = new JsonObject();
stack.addProperty("item",
AntimatterPlatformUtils.INSTANCE.getIdFromItem(setItemCountFunction.stack.getItem()).toString());
stack.addProperty("count", setItemCountFunction.stack.getCount());
if (setItemCountFunction.stack.hasTag())
stack.addProperty("nbt", setItemCountFunction.stack.getTag().toString());
json.add("stack", stack);
}

public RandomWeightLootFunction deserialize(JsonObject object,
JsonDeserializationContext deserializationContext,
LootItemCondition[] conditions) {
ItemStack stack = RecipeUtil.INSTANCE.getItemStack(object.getAsJsonObject("stack"), true);
int min = GsonHelper.getAsInt(object, "min");
int max = GsonHelper.getAsInt(object, "max");
return new RandomWeightLootFunction(stack, min, max);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package muramasa.antimatter.mixin;

import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(LootPool.class)
public interface LootPoolAccessor {
@Accessor
LootPoolEntryContainer[] getEntries();

@Accessor
@Mutable
void setEntries(LootPoolEntryContainer[] entries);

@Accessor
NumberProvider getRolls();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package muramasa.antimatter.util;

import net.minecraft.world.item.ItemStack;

import it.unimi.dsi.fastutil.Hash;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

/**
* A configurable generator of hashing strategies, allowing for consideration of select properties of ItemStacks when
* considering equality.
*/
public interface ItemStackHashStrategy extends Hash.Strategy<ItemStack> {

/**
* @return a builder object for producing a custom ItemStackHashStrategy.
*/
static ItemStackHashStrategyBuilder builder() {
return new ItemStackHashStrategyBuilder();
}

/**
* Generates an ItemStackHash configured to compare every aspect of ItemStacks.
*
* @return the ItemStackHashStrategy as described above.
*/
static ItemStackHashStrategy comparingAll() {
return builder().compareItem(true)
.compareCount(true)
.compareTag(true)
.build();
}

/**
* Generates an ItemStackHash configured to compare every aspect of ItemStacks except the number
* of items in the stack.
*
* @return the ItemStackHashStrategy as described above.
*/
static ItemStackHashStrategy comparingAllButCount() {
return builder().compareItem(true)
.compareTag(true)
.build();
}

static ItemStackHashStrategy comparingItem() {
return builder().compareItem(true)
.build();
}

/**
* Builder pattern class for generating customized ItemStackHashStrategy
*/
class ItemStackHashStrategyBuilder {

private boolean item, count, tag;

/**
* Defines whether the Item type should be considered for equality.
*
* @param choice {@code true} to consider this property, {@code false} to ignore it.
* @return {@code this}
*/
public ItemStackHashStrategyBuilder compareItem(boolean choice) {
item = choice;
return this;
}

/**
* Defines whether stack size should be considered for equality.
*
* @param choice {@code true} to consider this property, {@code false} to ignore it.
* @return {@code this}
*/
public ItemStackHashStrategyBuilder compareCount(boolean choice) {
count = choice;
return this;
}

/**
* Defines whether NBT Tags should be considered for equality.
*
* @param choice {@code true} to consider this property, {@code false} to ignore it.
* @return {@code this}
*/
public ItemStackHashStrategyBuilder compareTag(boolean choice) {
tag = choice;
return this;
}

/**
* @return the ItemStackHashStrategy as configured by "compare" methods.
*/
public ItemStackHashStrategy build() {
return new ItemStackHashStrategy() {

@Override
public int hashCode(@Nullable ItemStack o) {
return o == null || o.isEmpty() ? 0 : Objects.hash(
item ? o.getItem() : null,
count ? o.getCount() : null,
tag ? o.getTag() : null);
}

@Override
public boolean equals(@Nullable ItemStack a, @Nullable ItemStack b) {
if (a == null || a.isEmpty()) return b == null || b.isEmpty();
if (b == null || b.isEmpty()) return false;

return (!item || a.getItem() == b.getItem()) &&
(!count || a.getCount() == b.getCount()) &&
(!tag || Objects.equals(a.getTag(), b.getTag()));
}
};
}
}
}
3 changes: 2 additions & 1 deletion common/src/main/resources/antimatter.accesswidener
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ accessible method net/minecraft/world/item/crafting/ShapedRecipe matches (Lnet/m
accessible method net/minecraft/world/item/context/UseOnContext <init> (Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/InteractionHand;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/phys/BlockHitResult;)V
accessible method net/minecraft/world/item/context/UseOnContext getHitResult ()Lnet/minecraft/world/phys/BlockHitResult;
accessible class net/minecraft/client/gui/screens/MenuScreens$ScreenConstructor
accessible class net/minecraft/world/level/block/entity/BlockEntityType$BlockEntitySupplier
accessible class net/minecraft/world/level/block/entity/BlockEntityType$BlockEntitySupplier
accessible method net/minecraft/world/level/storage/loot/entries/LootItem <init> (Lnet/minecraft/world/item/Item;II[Lnet/minecraft/world/level/storage/loot/predicates/LootItemCondition;[Lnet/minecraft/world/level/storage/loot/functions/LootItemFunction;)V
3 changes: 2 additions & 1 deletion common/src/main/resources/antimatter.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"LeavesBlockMixin",
"LivingEntityAccessor",
"LivingEntityMixin",
"LootPoolAccessor",
"PickaxeItemMixin",
"PlayerMixin",
"RecipeManagerMixin",
Expand All @@ -43,5 +44,5 @@
"TagLoaderMixin"
],
"minVersion": "0.8",
"refmap" : "antimatter.refmap.json"
"refmap": "antimatter.refmap.json"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import muramasa.antimatter.capability.Holder;
import muramasa.antimatter.common.event.CommonEvents;
import muramasa.antimatter.data.AntimatterMaterialTypes;
import muramasa.antimatter.datagen.AntimatterLoot;
import muramasa.antimatter.material.Material;
import muramasa.antimatter.ore.BlockOre;
import muramasa.antimatter.pipe.TileTicker;
Expand Down Expand Up @@ -74,6 +75,7 @@ public static void onAnvilUpdated(AnvilUpdateEvent event) {
@SubscribeEvent(priority = EventPriority.LOWEST)
public static void onLootTableLoad(LootTableLoadEvent event) {
CommonEvents.lootTableLoad(event.getTable(), event.getName());
AntimatterLoot.onLootTableLoad(event.getTable().getPool("main"), event.getName());
}

@SubscribeEvent
Expand Down
Loading

0 comments on commit da246af

Please sign in to comment.