/* * Ex Deorum * Copyright (c) 2024 thedarkcolour * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package thedarkcolour.exdeorum.loot; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; import net.minecraft.tags.TagKey; import net.minecraft.util.RandomSource; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; import net.neoforged.neoforge.common.loot.IGlobalLootModifier; import net.neoforged.neoforge.common.loot.LootModifier; import org.jetbrains.annotations.Nullable; import thedarkcolour.exdeorum.recipe.RecipeUtil; import thedarkcolour.exdeorum.recipe.hammer.HammerRecipe; import thedarkcolour.exdeorum.tag.EItemTags; public class HammerLootModifier extends LootModifier { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> LootModifier.codecStart(inst).apply(inst, HammerLootModifier::new)); private final TagKey fortuneBlacklistTag; public HammerLootModifier(LootItemCondition[] conditionsIn) { super(conditionsIn); this.fortuneBlacklistTag = EItemTags.HAMMER_FORTUNE_BLACKLIST; } protected HammerLootModifier(LootItemCondition[] conditionsIn, TagKey fortuneBlacklistTag) { super(conditionsIn); this.fortuneBlacklistTag = fortuneBlacklistTag; } @Override protected ObjectArrayList doApply(ObjectArrayList generatedLoot, LootContext context) { var state = context.getParamOrNull(LootContextParams.BLOCK_STATE); if (state == null) { return generatedLoot; } var itemForm = state.getBlock().asItem(); if (itemForm == Items.AIR) { return generatedLoot; } var recipe = getRecipe(itemForm, context); if (recipe == null) { return generatedLoot; } ObjectArrayList newLoot = new ObjectArrayList<>(); var resultAmount = recipe.resultAmount.getInt(context); if (!itemForm.builtInRegistryHolder().is(this.fortuneBlacklistTag) && context.hasParam(LootContextParams.TOOL)) { var hammer = context.getParam(LootContextParams.TOOL); // fortune handling; more likely to boost drops if there are none to begin with resultAmount += calculateFortuneBonus(context.getLevel().registryAccess(), hammer, context.getRandom(), resultAmount == 0); } if (resultAmount > 0) { newLoot.add(recipe.result.copyWithCount(resultAmount)); } return newLoot; } @Nullable protected HammerRecipe getRecipe(Item itemForm, LootContext context) { return RecipeUtil.getCaches(context.getLevel()).getHammerRecipe(itemForm); } @Override public MapCodec codec() { return CODEC; } /** * Calculates the bonus number of drops for a hammer enchanted with fortune. * * @param registryAccess The registry access used for looking up enchantments * @param hammer The hammer in question * @param rand RNG * @param zeroBaseDrops Whether there were no drops to begin with * @return The additional number of drops, to be added to the number of base drops */ public static int calculateFortuneBonus(RegistryAccess registryAccess, ItemStack hammer, RandomSource rand, boolean zeroBaseDrops) { var fortune = hammer.getEnchantmentLevel(registryAccess.lookupOrThrow(Registries.ENCHANTMENT).getOrThrow(Enchantments.FORTUNE)); if (fortune != 0) { var chance = rand.nextFloat(); if (zeroBaseDrops) { if (chance < 0.06f * fortune) { return 1; } } else { if (chance < 0.03f * fortune) { return 1; } } } return 0; } }