From cff588c98bfdd18a6703e1ee711c8d10367c9fb8 Mon Sep 17 00:00:00 2001 From: feldenr <135638674+feldenr@users.noreply.github.com> Date: Thu, 30 Jul 2026 10:47:47 +0200 Subject: [PATCH] refactor: deduplicate ore type mapping, zone report, drop math, tool tiers - Extract OreTypes (block -> ore type), previously copy-pasted in OreBreakEventHandler and CustomOreLootModifier - Extract OreZoneCatalog.buildZoneReport, previously ~85 lines duplicated between OresCommand and OreBiomeFinderItem (also removes the pointless LinkedHashSet allocation used only for its size) - CustomOreLootModifier now delegates drop math to the unit-tested OreDropMath instead of re-implementing fortune inline; per-ore drop parameters live in a DropSpec lookup table instead of a 90-line switch - OreDropMath now draws through a minimal IntRandom functional interface: tests keep their seeded java.util.Random (::nextInt, pinned values unchanged) while the GLM passes the LootContext's world-seeded RandomSource - Extract ShardDiamondToolTier enum, replacing 4 anonymous Tier clones (pickaxe/axe/shovel/paxel) - Add OreZoneCatalogTest (4 tests) --- .../net/mcreator/customoregen/OreTypes.java | 44 ++++ .../mcreator/customoregen/OresCommand.java | 69 +------ .../event/OreBreakEventHandler.java | 33 +-- .../customoregen/item/OreBiomeFinderItem.java | 69 +------ .../item/ShardDiamondToolTier.java | 87 ++++++++ .../item/SharddiamondaxeItem.java | 34 +-- .../item/SharddiamondpaxelItem.java | 32 +-- .../item/SharddiamondpickaxeItem.java | 33 +-- .../item/SharddiamondshovelItem.java | 33 +-- .../loot/CustomOreLootModifier.java | 193 +++++++----------- .../ConfigurableOreDropsProcedure.java | 2 +- .../customoregen/procedures/OreDropMath.java | 22 +- .../customoregen/worldgen/OreZoneCatalog.java | 86 ++++++++ .../procedures/OreDropMathTest.java | 60 +++--- .../worldgen/OreZoneCatalogTest.java | 72 +++++++ 15 files changed, 424 insertions(+), 445 deletions(-) create mode 100644 src/main/java/net/mcreator/customoregen/OreTypes.java create mode 100644 src/main/java/net/mcreator/customoregen/item/ShardDiamondToolTier.java create mode 100644 src/main/java/net/mcreator/customoregen/worldgen/OreZoneCatalog.java create mode 100644 src/test/java/net/mcreator/customoregen/worldgen/OreZoneCatalogTest.java diff --git a/src/main/java/net/mcreator/customoregen/OreTypes.java b/src/main/java/net/mcreator/customoregen/OreTypes.java new file mode 100644 index 00000000..19ada175 --- /dev/null +++ b/src/main/java/net/mcreator/customoregen/OreTypes.java @@ -0,0 +1,44 @@ +package net.mcreator.customoregen; + +import net.minecraft.world.level.block.Block; + +import net.mcreator.customoregen.init.CustomOreGenModBlocks; + +/** + * Central mapping between the mod's ore blocks and their logical ore type. + * + *

The ore type drives both item drops ({@code CustomOreLootModifier}) and experience + * ({@code ConfigurableOreDropsProcedure} via {@code OreDropMath}). Single source of truth: + * previously this mapping was copy-pasted in two classes.

+ */ +public final class OreTypes { + + public static final String SHARD_DIAMOND = "shard_diamond"; + public static final String CONCENTRATED_DIAMOND = "concentrated_diamond"; + public static final String CONCENTRATED_COAL = "concentrated_coal"; + public static final String PURE_GOLDEN = "pure_golden"; + public static final String IMPURE_IRON = "impure_iron"; + public static final String LAPIS = "lapis"; + public static final String REDSTONE = "redstone"; + public static final String EMERALD = "emerald"; + public static final String COPPER = "copper"; + + private OreTypes() {} + + /** + * Returns the logical ore type for a block, or {@code null} if the block is not one of + * the mod's custom ores. + */ + public static String of(Block block) { + if (block == CustomOreGenModBlocks.SHARDDIAMONDBLOCKORE.get() || block == CustomOreGenModBlocks.DEEPSLATESHARDDIAMONDORE.get()) return SHARD_DIAMOND; + if (block == CustomOreGenModBlocks.CONCENTRATEDCOALORE.get()) return CONCENTRATED_COAL; + if (block == CustomOreGenModBlocks.PUREGOLDENORE.get() || block == CustomOreGenModBlocks.DEEPSLATEPUREGOLDENORE.get()) return PURE_GOLDEN; + if (block == CustomOreGenModBlocks.IRONORE.get() || block == CustomOreGenModBlocks.DEEPSLATEIRONORE.get()) return IMPURE_IRON; + if (block == CustomOreGenModBlocks.DEEPSLATEDIAMONDORE.get()) return CONCENTRATED_DIAMOND; + if (block == CustomOreGenModBlocks.LAPISORE.get() || block == CustomOreGenModBlocks.DEEPSLATELAPISORE.get()) return LAPIS; + if (block == CustomOreGenModBlocks.REDSTONEORE.get() || block == CustomOreGenModBlocks.DEEPSLATEREDSTONEORE.get()) return REDSTONE; + if (block == CustomOreGenModBlocks.HIGHEMERALDORE.get() || block == CustomOreGenModBlocks.LOWEREMERALDORE.get()) return EMERALD; + if (block == CustomOreGenModBlocks.COPPERHIGHORE.get() || block == CustomOreGenModBlocks.COPPERLOWERORE.get()) return COPPER; + return null; + } +} diff --git a/src/main/java/net/mcreator/customoregen/OresCommand.java b/src/main/java/net/mcreator/customoregen/OresCommand.java index 8ff2ccc8..b0ea9b3f 100644 --- a/src/main/java/net/mcreator/customoregen/OresCommand.java +++ b/src/main/java/net/mcreator/customoregen/OresCommand.java @@ -2,7 +2,6 @@ package net.mcreator.customoregen; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.network.chat.Component; @@ -10,35 +9,11 @@ import net.neoforged.neoforge.event.RegisterCommandsEvent; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; -import java.util.*; - -import net.mcreator.customoregen.worldgen.LatitudeConfig; +import net.mcreator.customoregen.worldgen.OreZoneCatalog; @EventBusSubscriber(modid = CustomOreGenMod.MODID, bus = EventBusSubscriber.Bus.GAME) public class OresCommand { - private static final List COLD_ORES = Arrays.asList( - "Lapis (stone) [Y: 0 a 32]", - "Lapis (deepslate) [Y: -64 a 0]", - "Diamant (deepslate) [Y: -64 a 0]"); - - private static final List HOT_ORES = Arrays.asList( - "Or (stone) [Y: 0 a 320]", - "Or (deepslate) [Y: -64 a 0]", - "Cuivre (haut) [Y: 15 a 320]", - "Cuivre (bas) [Y: -64 a 0]", - "Redstone (stone) [Y: -10 a 20]", - "Redstone (deepslate) [Y: -80 a -30]"); - - private static final List TEMPERATE_ORES = Arrays.asList( - "Fer (stone) [Y: 0 a 100]", - "Fer (deepslate) [Y: -64 a 0]", - "Charbon concentre [Y: 0 a 70]"); - - private static final List EVERYWHERE_ORES = Arrays.asList( - "Diamant Shard (deepslate) [Y: -64 a -40]", - "Bloc Diamant Shard [Y: 0 a 15]"); - @SubscribeEvent public static void onRegisterCommands(RegisterCommandsEvent event) { event.getDispatcher().register(Commands.literal("ores") @@ -58,48 +33,10 @@ public class OresCommand { int x = player.blockPosition().getX(); int z = player.blockPosition().getZ(); - String zone = LatitudeConfig.zoneName(z); - int coldZ = LatitudeConfig.getColdZoneZ(); - int hotZ = LatitudeConfig.getHotZoneZ(); - ChatFormatting zoneColor = zoneColor(zone); - - context.getSource().sendSuccess(() -> Component.literal( - "=== Position: X=" + x + " Z=" + z + " ===").withStyle(ChatFormatting.GRAY), true); - context.getSource().sendSuccess(() -> Component.literal( - "Zone determinee par la coordonnee Z : " + zone).withStyle(zoneColor), true); - context.getSource().sendSuccess(() -> Component.literal( - "Z < " + coldZ + " = froid | " + coldZ + " a " + hotZ + " = tempere | Z > " + hotZ + " = chaud") - .withStyle(ChatFormatting.GRAY), true); - - List zoneOres = new ArrayList<>(); - switch (zone) { - case "COLD" -> zoneOres.addAll(COLD_ORES); - case "HOT" -> zoneOres.addAll(HOT_ORES); - case "TEMPERATE" -> zoneOres.addAll(TEMPERATE_ORES); - } - - Set allOres = new LinkedHashSet<>(zoneOres); - allOres.addAll(EVERYWHERE_ORES); - - context.getSource().sendSuccess(() -> Component.literal( - "Minerais trouvables (" + allOres.size() + ") :").withStyle(ChatFormatting.WHITE), true); - for (String ore : zoneOres) { - context.getSource().sendSuccess(() -> Component.literal(" * " + ore).withStyle(zoneColor), true); - } - for (String ore : EVERYWHERE_ORES) { - context.getSource().sendSuccess(() -> Component.literal(" * " + ore) - .withStyle(ChatFormatting.LIGHT_PURPLE), true); + for (Component line : OreZoneCatalog.buildZoneReport(x, z)) { + context.getSource().sendSuccess(() -> line, true); } return 1; } - - private static ChatFormatting zoneColor(String zone) { - return switch (zone) { - case "COLD" -> ChatFormatting.AQUA; - case "HOT" -> ChatFormatting.GOLD; - case "TEMPERATE" -> ChatFormatting.GREEN; - default -> ChatFormatting.WHITE; - }; - } } diff --git a/src/main/java/net/mcreator/customoregen/event/OreBreakEventHandler.java b/src/main/java/net/mcreator/customoregen/event/OreBreakEventHandler.java index e80c0ea5..4ad2e066 100644 --- a/src/main/java/net/mcreator/customoregen/event/OreBreakEventHandler.java +++ b/src/main/java/net/mcreator/customoregen/event/OreBreakEventHandler.java @@ -3,10 +3,8 @@ package net.mcreator.customoregen.event; import net.neoforged.neoforge.event.level.BlockDropsEvent; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.Block; -import net.mcreator.customoregen.init.CustomOreGenModBlocks; +import net.mcreator.customoregen.OreTypes; import net.mcreator.customoregen.procedures.ConfigurableOreDropsProcedure; @EventBusSubscriber @@ -14,30 +12,13 @@ public class OreBreakEventHandler { @SubscribeEvent public static void onBlockDrops(BlockDropsEvent event) { - BlockState state = event.getState(); - Block block = state.getBlock(); - String oreType = getOreType(block); + String oreType = OreTypes.of(event.getState().getBlock()); - if (oreType != null) { - // Item drops are now handled by CustomOreLootModifier (Global Loot Modifier) - // to ensure compatibility with machines and avoid duplication. - - if (event.getBreaker() != null) { - ConfigurableOreDropsProcedure.execute(event.getLevel(), event.getPos().getX(), event.getPos().getY(), event.getPos().getZ(), event.getBreaker(), oreType); - } + // Item drops are handled by CustomOreLootModifier (Global Loot Modifier) + // to ensure compatibility with machines and avoid duplication. + // This handler only spawns the experience orb for player-mined ores. + if (oreType != null && event.getBreaker() != null) { + ConfigurableOreDropsProcedure.execute(event.getLevel(), event.getPos().getX(), event.getPos().getY(), event.getPos().getZ(), event.getBreaker(), oreType); } } - - private static String getOreType(Block block) { - if (block == CustomOreGenModBlocks.SHARDDIAMONDBLOCKORE.get() || block == CustomOreGenModBlocks.DEEPSLATESHARDDIAMONDORE.get()) return "shard_diamond"; - if (block == CustomOreGenModBlocks.CONCENTRATEDCOALORE.get()) return "concentrated_coal"; - if (block == CustomOreGenModBlocks.PUREGOLDENORE.get() || block == CustomOreGenModBlocks.DEEPSLATEPUREGOLDENORE.get()) return "pure_golden"; - if (block == CustomOreGenModBlocks.IRONORE.get() || block == CustomOreGenModBlocks.DEEPSLATEIRONORE.get()) return "impure_iron"; - if (block == CustomOreGenModBlocks.DEEPSLATEDIAMONDORE.get()) return "concentrated_diamond"; - if (block == CustomOreGenModBlocks.LAPISORE.get() || block == CustomOreGenModBlocks.DEEPSLATELAPISORE.get()) return "lapis"; - if (block == CustomOreGenModBlocks.REDSTONEORE.get() || block == CustomOreGenModBlocks.DEEPSLATEREDSTONEORE.get()) return "redstone"; - if (block == CustomOreGenModBlocks.HIGHEMERALDORE.get() || block == CustomOreGenModBlocks.LOWEREMERALDORE.get()) return "emerald"; - if (block == CustomOreGenModBlocks.COPPERHIGHORE.get() || block == CustomOreGenModBlocks.COPPERLOWERORE.get()) return "copper"; - return null; - } } diff --git a/src/main/java/net/mcreator/customoregen/item/OreBiomeFinderItem.java b/src/main/java/net/mcreator/customoregen/item/OreBiomeFinderItem.java index 59f0caa7..1a1f1dfd 100644 --- a/src/main/java/net/mcreator/customoregen/item/OreBiomeFinderItem.java +++ b/src/main/java/net/mcreator/customoregen/item/OreBiomeFinderItem.java @@ -1,6 +1,5 @@ package net.mcreator.customoregen.item; -import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResultHolder; @@ -9,34 +8,10 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; -import java.util.*; - -import net.mcreator.customoregen.worldgen.LatitudeConfig; +import net.mcreator.customoregen.worldgen.OreZoneCatalog; public class OreBiomeFinderItem extends Item { - private static final List COLD_ORES = Arrays.asList( - "Lapis (stone) [Y: 0 a 32]", - "Lapis (deepslate) [Y: -64 a 0]", - "Diamant (deepslate) [Y: -64 a 0]"); - - private static final List HOT_ORES = Arrays.asList( - "Or (stone) [Y: 0 a 320]", - "Or (deepslate) [Y: -64 a 0]", - "Cuivre (haut) [Y: 15 a 320]", - "Cuivre (bas) [Y: -64 a 0]", - "Redstone (stone) [Y: -10 a 20]", - "Redstone (deepslate) [Y: -80 a -30]"); - - private static final List TEMPERATE_ORES = Arrays.asList( - "Fer (stone) [Y: 0 a 100]", - "Fer (deepslate) [Y: -64 a 0]", - "Charbon concentre [Y: 0 a 70]"); - - private static final List EVERYWHERE_ORES = Arrays.asList( - "Diamant Shard (deepslate) [Y: -64 a -40]", - "Bloc Diamant Shard [Y: 0 a 15]"); - public OreBiomeFinderItem() { super(new Item.Properties().stacksTo(1)); } @@ -49,49 +24,11 @@ public class OreBiomeFinderItem extends Item { int x = player.blockPosition().getX(); int z = player.blockPosition().getZ(); - String zone = LatitudeConfig.zoneName(z); - int coldZ = LatitudeConfig.getColdZoneZ(); - int hotZ = LatitudeConfig.getHotZoneZ(); - ChatFormatting zoneColor = zoneColor(zone); - - player.displayClientMessage(Component.literal( - "=== Position: X=" + x + " Z=" + z + " ===").withStyle(ChatFormatting.GRAY), false); - player.displayClientMessage(Component.literal( - "Zone determinee par la coordonnee Z : " + zone).withStyle(zoneColor), false); - player.displayClientMessage(Component.literal( - "Z < " + coldZ + " = froid | " + coldZ + " a " + hotZ + " = tempere | Z > " + hotZ + " = chaud") - .withStyle(ChatFormatting.GRAY), false); - - List zoneOres = new ArrayList<>(); - switch (zone) { - case "COLD" -> zoneOres.addAll(COLD_ORES); - case "HOT" -> zoneOres.addAll(HOT_ORES); - case "TEMPERATE" -> zoneOres.addAll(TEMPERATE_ORES); - } - - Set allOres = new LinkedHashSet<>(zoneOres); - allOres.addAll(EVERYWHERE_ORES); - - player.displayClientMessage(Component.literal( - "Minerais trouvables (" + allOres.size() + ") :").withStyle(ChatFormatting.WHITE), false); - for (String ore : zoneOres) { - player.displayClientMessage(Component.literal(" * " + ore).withStyle(zoneColor), false); - } - for (String ore : EVERYWHERE_ORES) { - player.displayClientMessage(Component.literal(" * " + ore) - .withStyle(ChatFormatting.LIGHT_PURPLE), false); + for (Component line : OreZoneCatalog.buildZoneReport(x, z)) { + player.displayClientMessage(line, false); } } return InteractionResultHolder.sidedSuccess(stack, level.isClientSide); } - - private static ChatFormatting zoneColor(String zone) { - return switch (zone) { - case "COLD" -> ChatFormatting.AQUA; - case "HOT" -> ChatFormatting.GOLD; - case "TEMPERATE" -> ChatFormatting.GREEN; - default -> ChatFormatting.WHITE; - }; - } } diff --git a/src/main/java/net/mcreator/customoregen/item/ShardDiamondToolTier.java b/src/main/java/net/mcreator/customoregen/item/ShardDiamondToolTier.java new file mode 100644 index 00000000..d2d89330 --- /dev/null +++ b/src/main/java/net/mcreator/customoregen/item/ShardDiamondToolTier.java @@ -0,0 +1,87 @@ +package net.mcreator.customoregen.item; + +import java.util.function.DoubleSupplier; +import java.util.function.IntSupplier; + +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Tier; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; + +import net.mcreator.customoregen.config.ModConfigs; +import net.mcreator.customoregen.init.CustomOreGenModItems; + +/** + * Shared {@link Tier} for all Shard Diamond tools (previously an anonymous Tier + * copy-pasted in each tool class). Stats are read live from the config, with + * fallbacks for the pre-config-load phase (item registration). + */ +public enum ShardDiamondToolTier implements Tier { + PICKAXE(200, 7.0f, 1.0f, 9, + () -> ModConfigs.TOOL_STATS.shardDiamondPickaxeDurability.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondPickaxeSpeed.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondPickaxeAttackDamage.get()), + AXE(200, 7.0f, 6.0f, 9, + () -> ModConfigs.TOOL_STATS.shardDiamondAxeDurability.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondAxeSpeed.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondAxeAttackDamage.get()), + SHOVEL(200, 4.0f, 2.0f, 9, + () -> ModConfigs.TOOL_STATS.shardDiamondShovelDurability.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondShovelSpeed.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondShovelAttackDamage.get()), + PAXEL(1000, 6.5f, 4.0f, 14, + () -> ModConfigs.TOOL_STATS.shardDiamondPaxelDurability.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondPaxelSpeed.get(), + () -> ModConfigs.TOOL_STATS.shardDiamondPaxelAttackDamage.get()); + + private final int fallbackUses; + private final float fallbackSpeed; + private final float fallbackAttackDamage; + private final int enchantmentValue; + private final IntSupplier uses; + private final DoubleSupplier speed; + private final IntSupplier attackDamage; + + ShardDiamondToolTier(int fallbackUses, float fallbackSpeed, float fallbackAttackDamage, int enchantmentValue, + IntSupplier uses, DoubleSupplier speed, IntSupplier attackDamage) { + this.fallbackUses = fallbackUses; + this.fallbackSpeed = fallbackSpeed; + this.fallbackAttackDamage = fallbackAttackDamage; + this.enchantmentValue = enchantmentValue; + this.uses = uses; + this.speed = speed; + this.attackDamage = attackDamage; + } + + @Override + public int getUses() { + return ModConfigs.isLoaded() ? uses.getAsInt() : fallbackUses; + } + + @Override + public float getSpeed() { + return ModConfigs.isLoaded() ? (float) speed.getAsDouble() : fallbackSpeed; + } + + @Override + public float getAttackDamageBonus() { + return ModConfigs.isLoaded() ? attackDamage.getAsInt() : fallbackAttackDamage; + } + + @Override + public int getEnchantmentValue() { + return enchantmentValue; + } + + @Override + public Ingredient getRepairIngredient() { + return Ingredient.of(new ItemStack(CustomOreGenModItems.DIAMONDSHARD.get())); + } + + @Override + public TagKey getIncorrectBlocksForDrops() { + return BlockTags.INCORRECT_FOR_DIAMOND_TOOL; + } +} diff --git a/src/main/java/net/mcreator/customoregen/item/SharddiamondaxeItem.java b/src/main/java/net/mcreator/customoregen/item/SharddiamondaxeItem.java index 4e0d79c8..7a18f494 100644 --- a/src/main/java/net/mcreator/customoregen/item/SharddiamondaxeItem.java +++ b/src/main/java/net/mcreator/customoregen/item/SharddiamondaxeItem.java @@ -1,46 +1,16 @@ package net.mcreator.customoregen.item; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.Tier; -import net.minecraft.world.item.Item; import net.minecraft.world.item.AxeItem; import net.minecraft.world.item.ItemStack; -import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; import net.minecraft.tags.BlockTags; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.Block; -import net.mcreator.customoregen.init.CustomOreGenModItems; import net.mcreator.customoregen.config.ModConfigs; public class SharddiamondaxeItem extends AxeItem { public SharddiamondaxeItem() { - super(new Tier() { - public int getUses() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondAxeDurability.get() : 200; - } - - public float getSpeed() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondAxeSpeed.get().floatValue() : 7.0f; - } - - public float getAttackDamageBonus() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondAxeAttackDamage.get().floatValue() : 6.0f; - } - - public int getEnchantmentValue() { - return 9; - } - - public Ingredient getRepairIngredient() { - return Ingredient.of(new ItemStack(CustomOreGenModItems.DIAMONDSHARD.get())); - } - - // 1.21 - new method required - returns diamond-tier incorrect blocks - public TagKey getIncorrectBlocksForDrops() { - return BlockTags.INCORRECT_FOR_DIAMOND_TOOL; - } - }, new Item.Properties()); + super(ShardDiamondToolTier.AXE, new Item.Properties()); } @Override diff --git a/src/main/java/net/mcreator/customoregen/item/SharddiamondpaxelItem.java b/src/main/java/net/mcreator/customoregen/item/SharddiamondpaxelItem.java index 6c1f5118..c59e3739 100644 --- a/src/main/java/net/mcreator/customoregen/item/SharddiamondpaxelItem.java +++ b/src/main/java/net/mcreator/customoregen/item/SharddiamondpaxelItem.java @@ -1,46 +1,16 @@ package net.mcreator.customoregen.item; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.Tier; import net.minecraft.world.item.PickaxeItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Item; -import net.minecraft.tags.TagKey; import net.minecraft.tags.BlockTags; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.Block; -import net.mcreator.customoregen.init.CustomOreGenModItems; import net.mcreator.customoregen.config.ModConfigs; public class SharddiamondpaxelItem extends PickaxeItem { public SharddiamondpaxelItem() { - super(new Tier() { - public int getUses() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondPaxelDurability.get() : 1000; - } - - public float getSpeed() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondPaxelSpeed.get().floatValue() : 6.5f; - } - - public float getAttackDamageBonus() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondPaxelAttackDamage.get().floatValue() : 4.0f; - } - - public int getEnchantmentValue() { - return 14; - } - - public Ingredient getRepairIngredient() { - return Ingredient.of(new ItemStack(CustomOreGenModItems.DIAMONDSHARD.get())); - } - - // 1.21 - new method required - returns diamond-tier incorrect blocks - public TagKey getIncorrectBlocksForDrops() { - return BlockTags.INCORRECT_FOR_DIAMOND_TOOL; - } - }, new Item.Properties()); + super(ShardDiamondToolTier.PAXEL, new Item.Properties()); } @Override diff --git a/src/main/java/net/mcreator/customoregen/item/SharddiamondpickaxeItem.java b/src/main/java/net/mcreator/customoregen/item/SharddiamondpickaxeItem.java index f782ab2e..ee964f25 100644 --- a/src/main/java/net/mcreator/customoregen/item/SharddiamondpickaxeItem.java +++ b/src/main/java/net/mcreator/customoregen/item/SharddiamondpickaxeItem.java @@ -1,47 +1,16 @@ - package net.mcreator.customoregen.item; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.Tier; import net.minecraft.world.item.PickaxeItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Item; -import net.minecraft.tags.TagKey; import net.minecraft.tags.BlockTags; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.Block; -import net.mcreator.customoregen.init.CustomOreGenModItems; import net.mcreator.customoregen.config.ModConfigs; public class SharddiamondpickaxeItem extends PickaxeItem { public SharddiamondpickaxeItem() { - super(new Tier() { - public int getUses() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondPickaxeDurability.get() : 200; - } - - public float getSpeed() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondPickaxeSpeed.get().floatValue() : 7.0f; - } - - public float getAttackDamageBonus() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondPickaxeAttackDamage.get().floatValue() : 1.0f; - } - - public int getEnchantmentValue() { - return 9; - } - - public Ingredient getRepairIngredient() { - return Ingredient.of(new ItemStack(CustomOreGenModItems.DIAMONDSHARD.get())); - } - - // 1.21 - new method required - returns diamond-tier incorrect blocks - public TagKey getIncorrectBlocksForDrops() { - return BlockTags.INCORRECT_FOR_DIAMOND_TOOL; - } - }, new Item.Properties()); + super(ShardDiamondToolTier.PICKAXE, new Item.Properties()); } @Override diff --git a/src/main/java/net/mcreator/customoregen/item/SharddiamondshovelItem.java b/src/main/java/net/mcreator/customoregen/item/SharddiamondshovelItem.java index 96bc153c..9e45c089 100644 --- a/src/main/java/net/mcreator/customoregen/item/SharddiamondshovelItem.java +++ b/src/main/java/net/mcreator/customoregen/item/SharddiamondshovelItem.java @@ -1,47 +1,16 @@ - package net.mcreator.customoregen.item; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.item.Tier; import net.minecraft.world.item.ShovelItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Item; -import net.minecraft.tags.TagKey; import net.minecraft.tags.BlockTags; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.Block; -import net.mcreator.customoregen.init.CustomOreGenModItems; import net.mcreator.customoregen.config.ModConfigs; public class SharddiamondshovelItem extends ShovelItem { public SharddiamondshovelItem() { - super(new Tier() { - public int getUses() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondShovelDurability.get() : 200; - } - - public float getSpeed() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondShovelSpeed.get().floatValue() : 4.0f; - } - - public float getAttackDamageBonus() { - return ModConfigs.isLoaded() ? ModConfigs.TOOL_STATS.shardDiamondShovelAttackDamage.get().floatValue() : 2.0f; - } - - public int getEnchantmentValue() { - return 9; - } - - public Ingredient getRepairIngredient() { - return Ingredient.of(new ItemStack(CustomOreGenModItems.DIAMONDSHARD.get())); - } - - // 1.21 - new method required - returns diamond-tier incorrect blocks - public TagKey getIncorrectBlocksForDrops() { - return BlockTags.INCORRECT_FOR_DIAMOND_TOOL; - } - }, new Item.Properties()); + super(ShardDiamondToolTier.SHOVEL, new Item.Properties()); } @Override diff --git a/src/main/java/net/mcreator/customoregen/loot/CustomOreLootModifier.java b/src/main/java/net/mcreator/customoregen/loot/CustomOreLootModifier.java index cd64e59e..dd4d2868 100644 --- a/src/main/java/net/mcreator/customoregen/loot/CustomOreLootModifier.java +++ b/src/main/java/net/mcreator/customoregen/loot/CustomOreLootModifier.java @@ -3,6 +3,8 @@ package net.mcreator.customoregen.loot; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +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.level.block.Block; @@ -16,18 +18,57 @@ import net.neoforged.neoforge.common.loot.LootModifier; import net.minecraft.core.registries.Registries; import net.minecraft.world.item.enchantment.Enchantments; +import net.mcreator.customoregen.OreTypes; import net.mcreator.customoregen.init.CustomOreGenModBlocks; import net.mcreator.customoregen.init.CustomOreGenModItems; import net.mcreator.customoregen.config.ModConfigs; +import net.mcreator.customoregen.procedures.OreDropMath; import net.mcreator.customoregen.CustomOreGenMod; +import java.util.Map; +import java.util.function.Supplier; + import javax.annotation.Nonnull; public class CustomOreLootModifier extends LootModifier { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> codecStart(inst).apply(inst, CustomOreLootModifier::new) ); + /** Drop parameters for one ore type, read live from the config. */ + private record DropSpec(Supplier minDrops, Supplier maxDrops, + Supplier enableFortune, Supplier item) {} + + private static final Map DROP_SPECS = Map.of( + OreTypes.SHARD_DIAMOND, new DropSpec(ModConfigs.DROPS.shardDiamondOreMinDrops::get, + ModConfigs.DROPS.shardDiamondOreMaxDrops::get, ModConfigs.DROPS.shardDiamondOreEnableFortune::get, + CustomOreGenModItems.DIAMONDSHARD::get), + OreTypes.CONCENTRATED_DIAMOND, new DropSpec(ModConfigs.DROPS.concentratedDiamondOreMinDrops::get, + ModConfigs.DROPS.concentratedDiamondOreMaxDrops::get, ModConfigs.DROPS.concentratedDiamondOreEnableFortune::get, + () -> Items.DIAMOND), + OreTypes.CONCENTRATED_COAL, new DropSpec(ModConfigs.DROPS.concentratedCoalOreMinDrops::get, + ModConfigs.DROPS.concentratedCoalOreMaxDrops::get, ModConfigs.DROPS.concentratedCoalOreEnableFortune::get, + () -> Items.COAL), + OreTypes.PURE_GOLDEN, new DropSpec(ModConfigs.DROPS.pureGoldenOreMinDrops::get, + ModConfigs.DROPS.pureGoldenOreMaxDrops::get, ModConfigs.DROPS.pureGoldenOreEnableFortune::get, + () -> Items.RAW_GOLD), + OreTypes.IMPURE_IRON, new DropSpec(ModConfigs.DROPS.impureIronOreMinDrops::get, + ModConfigs.DROPS.impureIronOreMaxDrops::get, ModConfigs.DROPS.impureIronOreEnableFortune::get, + () -> Items.RAW_IRON), + OreTypes.LAPIS, new DropSpec(ModConfigs.DROPS.lapisOreMinDrops::get, + ModConfigs.DROPS.lapisOreMaxDrops::get, ModConfigs.DROPS.lapisOreEnableFortune::get, + () -> Items.LAPIS_LAZULI), + OreTypes.REDSTONE, new DropSpec(ModConfigs.DROPS.redstoneOreMinDrops::get, + ModConfigs.DROPS.redstoneOreMaxDrops::get, ModConfigs.DROPS.redstoneOreEnableFortune::get, + () -> Items.REDSTONE), + OreTypes.EMERALD, new DropSpec(ModConfigs.DROPS.emeraldOreMinDrops::get, + ModConfigs.DROPS.emeraldOreMaxDrops::get, () -> true, + () -> Items.EMERALD), + OreTypes.COPPER, new DropSpec(ModConfigs.DROPS.copperOreMinDrops::get, + ModConfigs.DROPS.copperOreMaxDrops::get, ModConfigs.DROPS.copperOreEnableFortune::get, + () -> Items.RAW_COPPER) + ); + public CustomOreLootModifier(LootItemCondition[] conditionsIn) { super(conditionsIn); } @@ -45,44 +86,40 @@ public class CustomOreLootModifier extends LootModifier { if (state == null) return generatedLoot; Block block = state.getBlock(); - String oreType = getOreType(block); + String oreType = OreTypes.of(block); if (oreType != null) { ItemStack tool = context.getParamOrNull(LootContextParams.TOOL); - - // Check for Silk Touch - if (tool != null && !tool.isEmpty()) { - var registry = context.getLevel().registryAccess().lookupOrThrow(Registries.ENCHANTMENT); - int silkLevel = tool.getEnchantmentLevel(registry.getOrThrow(Enchantments.SILK_TOUCH)); - - if (silkLevel > 0) { - // For Diamond Shard, we want to keep the modded block itself - if (oreType.equals("shard_diamond")) { - ObjectArrayList shardLoot = new ObjectArrayList<>(); - shardLoot.add(new ItemStack(block)); - return shardLoot; - } - // For others, convert to vanilla block equivalent - ItemStack vanillaBlock = getVanillaBlockDrop(block); - if (!vanillaBlock.isEmpty()) { - ObjectArrayList silkLoot = new ObjectArrayList<>(); - silkLoot.add(vanillaBlock); - return silkLoot; - } - return generatedLoot; + var enchantmentRegistry = context.getLevel().registryAccess().lookupOrThrow(Registries.ENCHANTMENT); + + // Silk Touch: drop the ore block itself instead of the custom drops + if (tool != null && !tool.isEmpty() + && tool.getEnchantmentLevel(enchantmentRegistry.getOrThrow(Enchantments.SILK_TOUCH)) > 0) { + // For Diamond Shard, we want to keep the modded block itself + if (oreType.equals(OreTypes.SHARD_DIAMOND)) { + ObjectArrayList shardLoot = new ObjectArrayList<>(); + shardLoot.add(new ItemStack(block)); + return shardLoot; } + // For others, convert to vanilla block equivalent + ItemStack vanillaBlock = getVanillaBlockDrop(block); + if (!vanillaBlock.isEmpty()) { + ObjectArrayList silkLoot = new ObjectArrayList<>(); + silkLoot.add(vanillaBlock); + return silkLoot; + } + return generatedLoot; } - // If not silk touch, replace with custom drops - ObjectArrayList customDrops = new ObjectArrayList<>(); + // No Silk Touch: replace with custom drops (fortune-aware) int fortuneLevel = 0; if (tool != null && !tool.isEmpty()) { - var registry = context.getLevel().registryAccess().lookupOrThrow(Registries.ENCHANTMENT); - fortuneLevel = tool.getEnchantmentLevel(registry.getOrThrow(Enchantments.FORTUNE)); + fortuneLevel = tool.getEnchantmentLevel(enchantmentRegistry.getOrThrow(Enchantments.FORTUNE)); } + ObjectArrayList customDrops = new ObjectArrayList<>(); addCustomDrops(customDrops, oreType, fortuneLevel, context.getRandom()); - + // Only return custom drops if we generated some, otherwise fallback to original if (!customDrops.isEmpty()) { return customDrops; @@ -91,21 +128,8 @@ public class CustomOreLootModifier extends LootModifier { } catch (Exception e) { CustomOreGenMod.LOGGER.error("Error applying CustomOreLootModifier", e); } - - return generatedLoot; - } - private String getOreType(Block block) { - if (block == CustomOreGenModBlocks.SHARDDIAMONDBLOCKORE.get() || block == CustomOreGenModBlocks.DEEPSLATESHARDDIAMONDORE.get()) return "shard_diamond"; - if (block == CustomOreGenModBlocks.CONCENTRATEDCOALORE.get()) return "concentrated_coal"; - if (block == CustomOreGenModBlocks.PUREGOLDENORE.get() || block == CustomOreGenModBlocks.DEEPSLATEPUREGOLDENORE.get()) return "pure_golden"; - if (block == CustomOreGenModBlocks.IRONORE.get() || block == CustomOreGenModBlocks.DEEPSLATEIRONORE.get()) return "impure_iron"; - if (block == CustomOreGenModBlocks.DEEPSLATEDIAMONDORE.get()) return "concentrated_diamond"; - if (block == CustomOreGenModBlocks.LAPISORE.get() || block == CustomOreGenModBlocks.DEEPSLATELAPISORE.get()) return "lapis"; - if (block == CustomOreGenModBlocks.REDSTONEORE.get() || block == CustomOreGenModBlocks.DEEPSLATEREDSTONEORE.get()) return "redstone"; - if (block == CustomOreGenModBlocks.HIGHEMERALDORE.get() || block == CustomOreGenModBlocks.LOWEREMERALDORE.get()) return "emerald"; - if (block == CustomOreGenModBlocks.COPPERHIGHORE.get() || block == CustomOreGenModBlocks.COPPERLOWERORE.get()) return "copper"; - return null; + return generatedLoot; } private ItemStack getVanillaBlockDrop(Block block) { @@ -126,87 +150,14 @@ public class CustomOreLootModifier extends LootModifier { return ItemStack.EMPTY; } - private void addCustomDrops(ObjectArrayList drops, String oreType, int fortuneLevel, net.minecraft.util.RandomSource random) { - int minDrops = 1; - int maxDrops = 1; - boolean enableFortune = true; - ItemStack dropItem = ItemStack.EMPTY; - - switch (oreType) { - case "shard_diamond": - minDrops = ModConfigs.DROPS.shardDiamondOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.shardDiamondOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.shardDiamondOreEnableFortune.get(); - dropItem = new ItemStack(CustomOreGenModItems.DIAMONDSHARD.get()); - break; - case "concentrated_diamond": - minDrops = ModConfigs.DROPS.concentratedDiamondOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.concentratedDiamondOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.concentratedDiamondOreEnableFortune.get(); - dropItem = new ItemStack(Items.DIAMOND); - break; - case "concentrated_coal": - minDrops = ModConfigs.DROPS.concentratedCoalOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.concentratedCoalOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.concentratedCoalOreEnableFortune.get(); - dropItem = new ItemStack(Items.COAL); - break; - case "pure_golden": - minDrops = ModConfigs.DROPS.pureGoldenOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.pureGoldenOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.pureGoldenOreEnableFortune.get(); - dropItem = new ItemStack(Items.RAW_GOLD); - break; - case "impure_iron": - minDrops = ModConfigs.DROPS.impureIronOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.impureIronOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.impureIronOreEnableFortune.get(); - dropItem = new ItemStack(Items.RAW_IRON); - break; - case "lapis": - minDrops = ModConfigs.DROPS.lapisOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.lapisOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.lapisOreEnableFortune.get(); - dropItem = new ItemStack(Items.LAPIS_LAZULI); - break; - case "redstone": - minDrops = ModConfigs.DROPS.redstoneOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.redstoneOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.redstoneOreEnableFortune.get(); - dropItem = new ItemStack(Items.REDSTONE); - break; - case "emerald": - minDrops = ModConfigs.DROPS.emeraldOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.emeraldOreMaxDrops.get(); - dropItem = new ItemStack(Items.EMERALD); - break; - case "copper": - minDrops = ModConfigs.DROPS.copperOreMinDrops.get(); - maxDrops = ModConfigs.DROPS.copperOreMaxDrops.get(); - enableFortune = ModConfigs.DROPS.copperOreEnableFortune.get(); - dropItem = new ItemStack(Items.RAW_COPPER); - break; - } - - if (dropItem.isEmpty()) return; - - int dropCount = minDrops + (maxDrops > minDrops ? random.nextInt(maxDrops - minDrops + 1) : 0); - - if (enableFortune && fortuneLevel > 0) { - if (oreType.equals("lapis") || oreType.equals("copper") || oreType.equals("redstone")) { - int multiplier = random.nextInt(fortuneLevel + 2) - 1; - if (multiplier < 0) multiplier = 0; - dropCount *= (multiplier + 1); - } else { - int fortuneBonus = random.nextInt(fortuneLevel + 2) - 1; - if (fortuneBonus < 0) fortuneBonus = 0; - dropCount += fortuneBonus; - } - } + private void addCustomDrops(ObjectArrayList drops, String oreType, int fortuneLevel, RandomSource random) { + DropSpec spec = DROP_SPECS.get(oreType); + if (spec == null) return; + int dropCount = OreDropMath.dropCount(oreType, spec.minDrops().get(), spec.maxDrops().get(), + spec.enableFortune().get(), fortuneLevel, random::nextInt); if (dropCount > 0) { - dropItem.setCount(dropCount); - drops.add(dropItem); + drops.add(new ItemStack(spec.item().get(), dropCount)); } } } diff --git a/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java b/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java index bbc89aaa..3657c5f0 100644 --- a/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java +++ b/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java @@ -34,7 +34,7 @@ public class ConfigurableOreDropsProcedure { return; } - int expAmount = OreDropMath.experienceFor(oreType, new Random()); + int expAmount = OreDropMath.experienceFor(oreType, new Random()::nextInt); if (expAmount > 0 && world instanceof ServerLevel serverLevel) { serverLevel.addFreshEntity(new ExperienceOrb(serverLevel, x + 0.5, y + 0.5, z + 0.5, expAmount)); } diff --git a/src/main/java/net/mcreator/customoregen/procedures/OreDropMath.java b/src/main/java/net/mcreator/customoregen/procedures/OreDropMath.java index 71af422e..55fbfa0c 100644 --- a/src/main/java/net/mcreator/customoregen/procedures/OreDropMath.java +++ b/src/main/java/net/mcreator/customoregen/procedures/OreDropMath.java @@ -1,18 +1,24 @@ package net.mcreator.customoregen.procedures; -import java.util.Random; - /** * Pure (Minecraft-free) ore drop math, extracted from {@link ConfigurableOreDropsProcedure} * so it can be unit-tested without a game server. The procedure delegates the drop-count and * experience calculations here, which mirrors vanilla 1.21 behaviour for the multi-drop ores * (Lapis, Copper, Redstone) vs the discrete ores (Diamond, Coal, Emerald). * - *

All methods are deterministic given the {@link Random} argument, so tests can pin exact - * outputs with a seeded {@code Random}.

+ *

All methods draw randomness through {@link IntRandom}, so they accept both + * {@code new Random(seed)::nextInt} (deterministic unit tests) and + * {@code lootContext.getRandom()::nextInt} (world-seeded Minecraft {@code RandomSource}).

*/ public final class OreDropMath { + /** Minimal random source abstraction: compatible with both {@link java.util.Random} and + * Minecraft's {@code RandomSource} via a {@code ::nextInt} method reference. */ + @FunctionalInterface + public interface IntRandom { + int nextInt(int bound); + } + private OreDropMath() {} /** @@ -29,7 +35,7 @@ public final class OreDropMath { * {@code minDrops + random.nextInt(maxDrops - minDrops + 1)} when {@code maxDrops > minDrops}, * else {@code minDrops} (no-op random draw when the range is a single value). */ - public static int baseDropCount(int minDrops, int maxDrops, Random random) { + public static int baseDropCount(int minDrops, int maxDrops, IntRandom random) { if (maxDrops > minDrops) { return minDrops + random.nextInt(maxDrops - minDrops + 1); } @@ -49,7 +55,7 @@ public final class OreDropMath { * (and no random draw is consumed, matching the procedure guard).

*/ public static int applyFortune(String oreType, int baseCount, boolean enableFortune, - int fortuneLevel, Random random) { + int fortuneLevel, IntRandom random) { if (!enableFortune || fortuneLevel <= 0) { return baseCount; } @@ -69,7 +75,7 @@ public final class OreDropMath { * single fortune draw). */ public static int dropCount(String oreType, int minDrops, int maxDrops, - boolean enableFortune, int fortuneLevel, Random random) { + boolean enableFortune, int fortuneLevel, IntRandom random) { int base = baseDropCount(minDrops, maxDrops, random); return applyFortune(oreType, base, enableFortune, fortuneLevel, random); } @@ -80,7 +86,7 @@ public final class OreDropMath { * * @return XP amount in {@code [0, max]} for the ore type, or 0 if the type does not drop XP */ - public static int experienceFor(String oreType, Random random) { + public static int experienceFor(String oreType, IntRandom random) { switch (oreType) { case "concentrated_coal": return random.nextInt(3); // 0-2 diff --git a/src/main/java/net/mcreator/customoregen/worldgen/OreZoneCatalog.java b/src/main/java/net/mcreator/customoregen/worldgen/OreZoneCatalog.java new file mode 100644 index 00000000..cf0831e0 --- /dev/null +++ b/src/main/java/net/mcreator/customoregen/worldgen/OreZoneCatalog.java @@ -0,0 +1,86 @@ +package net.mcreator.customoregen.worldgen; + +import java.util.List; +import java.util.ArrayList; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; + +/** + * Shared "which ores generate at this Z" report, used by both the {@code /ores} command + * and the Ore Biome Finder item (previously copy-pasted in both classes). + * + *

Each caller decides how to deliver the lines (command feedback vs client chat).

+ */ +public final class OreZoneCatalog { + + private static final List COLD_ORES = List.of( + "Lapis (stone) [Y: 0 a 32]", + "Lapis (deepslate) [Y: -64 a 0]", + "Diamant (deepslate) [Y: -64 a 0]"); + + private static final List HOT_ORES = List.of( + "Or (stone) [Y: 0 a 320]", + "Or (deepslate) [Y: -64 a 0]", + "Cuivre (haut) [Y: 15 a 320]", + "Cuivre (bas) [Y: -64 a 0]", + "Redstone (stone) [Y: -10 a 20]", + "Redstone (deepslate) [Y: -80 a -30]"); + + private static final List TEMPERATE_ORES = List.of( + "Fer (stone) [Y: 0 a 100]", + "Fer (deepslate) [Y: -64 a 0]", + "Charbon concentre [Y: 0 a 70]"); + + private static final List EVERYWHERE_ORES = List.of( + "Diamant Shard (deepslate) [Y: -64 a -40]", + "Bloc Diamant Shard [Y: 0 a 15]"); + + private OreZoneCatalog() {} + + /** Ores specific to the given zone ("COLD", "HOT" or "TEMPERATE"). */ + public static List oresForZone(String zone) { + return switch (zone) { + case "COLD" -> COLD_ORES; + case "HOT" -> HOT_ORES; + default -> TEMPERATE_ORES; + }; + } + + /** Color associated with a zone for chat output. */ + public static ChatFormatting zoneColor(String zone) { + return switch (zone) { + case "COLD" -> ChatFormatting.AQUA; + case "HOT" -> ChatFormatting.GOLD; + case "TEMPERATE" -> ChatFormatting.GREEN; + default -> ChatFormatting.WHITE; + }; + } + + /** + * Builds the full zone report (position, zone, thresholds, ore list) as chat components, + * one per line. + */ + public static List buildZoneReport(int x, int z) { + String zone = LatitudeConfig.zoneName(z); + int coldZ = LatitudeConfig.getColdZoneZ(); + int hotZ = LatitudeConfig.getHotZoneZ(); + ChatFormatting zoneColor = zoneColor(zone); + List zoneOres = oresForZone(zone); + + List lines = new ArrayList<>(); + lines.add(Component.literal("=== Position: X=" + x + " Z=" + z + " ===").withStyle(ChatFormatting.GRAY)); + lines.add(Component.literal("Zone determinee par la coordonnee Z : " + zone).withStyle(zoneColor)); + lines.add(Component.literal("Z < " + coldZ + " = froid | " + coldZ + " a " + hotZ + " = tempere | Z > " + hotZ + " = chaud") + .withStyle(ChatFormatting.GRAY)); + lines.add(Component.literal("Minerais trouvables (" + (zoneOres.size() + EVERYWHERE_ORES.size()) + ") :") + .withStyle(ChatFormatting.WHITE)); + for (String ore : zoneOres) { + lines.add(Component.literal(" * " + ore).withStyle(zoneColor)); + } + for (String ore : EVERYWHERE_ORES) { + lines.add(Component.literal(" * " + ore).withStyle(ChatFormatting.LIGHT_PURPLE)); + } + return lines; + } +} diff --git a/src/test/java/net/mcreator/customoregen/procedures/OreDropMathTest.java b/src/test/java/net/mcreator/customoregen/procedures/OreDropMathTest.java index cc5115c2..491925d3 100644 --- a/src/test/java/net/mcreator/customoregen/procedures/OreDropMathTest.java +++ b/src/test/java/net/mcreator/customoregen/procedures/OreDropMathTest.java @@ -52,7 +52,7 @@ class OreDropMathTest { void baseDropCount_range() { Random rng = new Random(42L); for (int i = 0; i < 10_000; i++) { - int c = OreDropMath.baseDropCount(2, 5, rng); + int c = OreDropMath.baseDropCount(2, 5, rng::nextInt); assertTrue(c >= 2 && c <= 5, "out of [2,5]: " + c); } } @@ -64,7 +64,7 @@ class OreDropMathTest { Random rng = new Random(7L); boolean[] seen = new boolean[10]; for (int i = 0; i < 60_000; i++) { - seen[OreDropMath.baseDropCount(4, 9, rng)] = true; + seen[OreDropMath.baseDropCount(4, 9, rng::nextInt)] = true; } for (int v = 4; v <= 9; v++) { assertTrue(seen[v], "value " + v + " never appeared in [4,9] base draw"); @@ -79,7 +79,7 @@ class OreDropMathTest { // Vanilla Emerald default: 1..1 -> always 1, and the random must NOT be consumed // (so that the subsequent Fortune draw lands on the same sequence). Random rngA = new Random(123L); - int c = OreDropMath.baseDropCount(1, 1, rngA); + int c = OreDropMath.baseDropCount(1, 1, rngA::nextInt); assertEquals(1, c); // Verify no random draw happened: an identical RNG feeding dropCount()+fortune must @@ -102,7 +102,7 @@ class OreDropMathTest { // When Fortune is disabled, the RNG must not be consumed at all. Random rngA = new Random(999L); Random rngB = new Random(999L); - int with = OreDropMath.applyFortune("shard_diamond", 3, false, 3, rngA); + int with = OreDropMath.applyFortune("shard_diamond", 3, false, 3, rngA::nextInt); assertEquals(3, with); assertEquals(rngB.nextInt(), rngA.nextInt(), "disabled fortune must not consume a random draw"); @@ -113,7 +113,7 @@ class OreDropMathTest { void applyFortune_zeroLevelIsNoOp() { Random rngA = new Random(999L); Random rngB = new Random(999L); - int with = OreDropMath.applyFortune("shard_diamond", 3, true, 0, rngA); + int with = OreDropMath.applyFortune("shard_diamond", 3, true, 0, rngA::nextInt); assertEquals(3, with); assertEquals(rngB.nextInt(), rngA.nextInt(), "fortune level 0 must not consume a random draw"); @@ -131,7 +131,7 @@ class OreDropMathTest { Random rng = new Random(1L); for (int i = 0; i < 5_000; i++) { int base = 1 + rng.nextInt(8); - int with = OreDropMath.applyFortune(t, base, true, fortune, rng); + int with = OreDropMath.applyFortune(t, base, true, fortune, rng::nextInt); assertTrue(with >= base, t + " fortune=" + fortune + " base=" + base + " -> " + with + " < base"); assertTrue(with >= 0, t + " fortune result negative: " + with); @@ -154,7 +154,7 @@ class OreDropMathTest { Random rng = new Random(t.hashCode() + fortune); int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE; for (int i = 0; i < 20_000; i++) { - int with = OreDropMath.applyFortune(t, 1, true, fortune, rng); + int with = OreDropMath.applyFortune(t, 1, true, fortune, rng::nextInt); // base=1, so 'with' equals 1 + bonus in [1, 1+fortune] assertTrue(with >= 1 && with <= 1 + fortune, t + " fortune=" + fortune + " with=" + with + " outside [1," + (1 + fortune) + "]"); @@ -176,7 +176,7 @@ class OreDropMathTest { int[] counts = new int[5]; // index 0..4 int n = 200_000; for (int i = 0; i < n; i++) { - counts[OreDropMath.applyFortune("shard_diamond", 1, true, 3, rng)]++; + counts[OreDropMath.applyFortune("shard_diamond", 1, true, 3, rng::nextInt)]++; } // Allow generous tolerance ±5% for RNG noise. assertRatio("discrete fortune III base=1 -> outcome 1 (+0)", counts[1] / (double) n, 0.40, 0.05); @@ -201,7 +201,7 @@ class OreDropMathTest { Random rng = new Random(t.hashCode() + fortune * 7); for (int i = 0; i < 10_000; i++) { int base = 4; // vanilla lapis-ish - int with = OreDropMath.applyFortune(t, base, true, fortune, rng); + int with = OreDropMath.applyFortune(t, base, true, fortune, rng::nextInt); assertTrue(with % base == 0, t + " multiplier result " + with + " is not a multiple of base " + base); assertTrue(with >= base && with <= base * (fortune + 1), @@ -220,7 +220,7 @@ class OreDropMathTest { int[] counts = new int[8]; int n = 200_000; for (int i = 0; i < n; i++) { - counts[OreDropMath.applyFortune("lapis", 1, true, 3, rng)]++; + counts[OreDropMath.applyFortune("lapis", 1, true, 3, rng::nextInt)]++; } assertRatio("lapis multiplier fortune III -> result 1 (x1)", counts[1] / (double) n, 0.40, 0.05); assertRatio("lapis multiplier fortune III -> result 2 (x2)", counts[2] / (double) n, 0.20, 0.05); @@ -239,11 +239,11 @@ class OreDropMathTest { // SAME continuation of the RNG. This guards against subtle behaviour drift if a refactor // reorders the random draws. Random rngA = new Random(55L); - int base = OreDropMath.baseDropCount(4, 9, rngA); - int fortune = OreDropMath.applyFortune("lapis", base, true, 3, rngA); + int base = OreDropMath.baseDropCount(4, 9, rngA::nextInt); + int fortune = OreDropMath.applyFortune("lapis", base, true, 3, rngA::nextInt); Random rngB = new Random(55L); - int combined = OreDropMath.dropCount("lapis", 4, 9, true, 3, rngB); + int combined = OreDropMath.dropCount("lapis", 4, 9, true, 3, rngB::nextInt); assertEquals(fortune, combined, "dropCount must be base+fortune in order"); } @@ -256,12 +256,12 @@ class OreDropMathTest { @DisplayName("XP: iron/gold/copper/unknown drop 0 raw XP") void experienceFor_zeroXpTypes() { Random rng = new Random(1L); - assertEquals(0, OreDropMath.experienceFor("impure_iron", rng)); - assertEquals(0, OreDropMath.experienceFor("impure_gold", rng)); - assertEquals(0, OreDropMath.experienceFor("pure_golden", rng)); - assertEquals(0, OreDropMath.experienceFor("copper", rng)); - assertEquals(0, OreDropMath.experienceFor("unknown", rng)); - assertEquals(0, OreDropMath.experienceFor("", rng)); + assertEquals(0, OreDropMath.experienceFor("impure_iron", rng::nextInt)); + assertEquals(0, OreDropMath.experienceFor("impure_gold", rng::nextInt)); + assertEquals(0, OreDropMath.experienceFor("pure_golden", rng::nextInt)); + assertEquals(0, OreDropMath.experienceFor("copper", rng::nextInt)); + assertEquals(0, OreDropMath.experienceFor("unknown", rng::nextInt)); + assertEquals(0, OreDropMath.experienceFor("", rng::nextInt)); } @Test @@ -270,24 +270,24 @@ class OreDropMathTest { Random rng = new Random(2L); // coal: 0-2 for (int i = 0; i < 5_000; i++) { - int xp = OreDropMath.experienceFor("concentrated_coal", rng); + int xp = OreDropMath.experienceFor("concentrated_coal", rng::nextInt); assertTrue(xp >= 0 && xp <= 2, "coal XP " + xp + " out of [0,2]"); } // diamond/emerald/shard: 3-7 for (String t : new String[]{"shard_diamond", "concentrated_diamond", "emerald"}) { for (int i = 0; i < 5_000; i++) { - int xp = OreDropMath.experienceFor(t, rng); + int xp = OreDropMath.experienceFor(t, rng::nextInt); assertTrue(xp >= 3 && xp <= 7, t + " XP " + xp + " out of [3,7]"); } } // lapis: 2-5 for (int i = 0; i < 5_000; i++) { - int xp = OreDropMath.experienceFor("lapis", rng); + int xp = OreDropMath.experienceFor("lapis", rng::nextInt); assertTrue(xp >= 2 && xp <= 5, "lapis XP " + xp + " out of [2,5]"); } // redstone: 1-5 for (int i = 0; i < 5_000; i++) { - int xp = OreDropMath.experienceFor("redstone", rng); + int xp = OreDropMath.experienceFor("redstone", rng::nextInt); assertTrue(xp >= 1 && xp <= 5, "redstone XP " + xp + " out of [1,5]"); } } @@ -300,9 +300,9 @@ class OreDropMathTest { boolean[] redstoneSeen = new boolean[6]; // 1..5 boolean[] diamondSeen = new boolean[8]; // 3..7 for (int i = 0; i < 100_000; i++) { - coalSeen[OreDropMath.experienceFor("concentrated_coal", rng)] = true; - redstoneSeen[OreDropMath.experienceFor("redstone", rng)] = true; - diamondSeen[OreDropMath.experienceFor("emerald", rng)] = true; + coalSeen[OreDropMath.experienceFor("concentrated_coal", rng::nextInt)] = true; + redstoneSeen[OreDropMath.experienceFor("redstone", rng::nextInt)] = true; + diamondSeen[OreDropMath.experienceFor("emerald", rng::nextInt)] = true; } for (int v = 0; v <= 2; v++) assertTrue(coalSeen[v], "coal XP " + v + " never observed"); for (int v = 1; v <= 5; v++) assertTrue(redstoneSeen[v], "redstone XP " + v + " never observed"); @@ -321,11 +321,11 @@ class OreDropMathTest { Random rngA = new Random(0xC0FFEE); Random rngB = new Random(0xC0FFEE); for (String t : oreTypes) { - int da = OreDropMath.dropCount(t, 2, 5, true, 3, rngA); - int db = OreDropMath.dropCount(t, 2, 5, true, 3, rngB); + int da = OreDropMath.dropCount(t, 2, 5, true, 3, rngA::nextInt); + int db = OreDropMath.dropCount(t, 2, 5, true, 3, rngB::nextInt); assertEquals(da, db, t + " dropCount diverged with same seed"); - int xa = OreDropMath.experienceFor(t, rngA); - int xb = OreDropMath.experienceFor(t, rngB); + int xa = OreDropMath.experienceFor(t, rngA::nextInt); + int xb = OreDropMath.experienceFor(t, rngB::nextInt); assertEquals(xa, xb, t + " XP diverged with same seed"); } } diff --git a/src/test/java/net/mcreator/customoregen/worldgen/OreZoneCatalogTest.java b/src/test/java/net/mcreator/customoregen/worldgen/OreZoneCatalogTest.java new file mode 100644 index 00000000..d5f5d436 --- /dev/null +++ b/src/test/java/net/mcreator/customoregen/worldgen/OreZoneCatalogTest.java @@ -0,0 +1,72 @@ +package net.mcreator.customoregen.worldgen; + +import net.minecraft.network.chat.Component; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for the shared zone report used by the /ores command and the Ore Biome Finder item. + * + * Note: the default config thresholds (cold: Z < -8000, hot: Z > 8000) apply because + * the config is not loaded in unit tests (LatitudeConfig falls back to its defaults). + */ +@DisplayName("OreZoneCatalog Tests") +class OreZoneCatalogTest { + + private static String reportText(int x, int z) { + List report = OreZoneCatalog.buildZoneReport(x, z); + StringBuilder sb = new StringBuilder(); + for (Component line : report) { + sb.append(line.getString()).append('\n'); + } + return sb.toString(); + } + + @Test + @DisplayName("Cold zone report lists cold ores plus shard ores") + void testReport_ColdZone() { + String report = reportText(100, -10000); + assertTrue(report.contains("COLD"), "should name the COLD zone"); + assertTrue(report.contains("Lapis"), "cold zone should list lapis"); + assertTrue(report.contains("Diamant Shard"), "everywhere ores should always be listed"); + assertFalse(report.contains("Cuivre"), "cold zone should not list hot ores"); + // 3 cold ores + 2 everywhere ores + assertTrue(report.contains("Minerais trouvables (5)"), "should count 3 zone ores + 2 everywhere ores"); + } + + @Test + @DisplayName("Hot zone report lists hot ores plus shard ores") + void testReport_HotZone() { + String report = reportText(0, 20000); + assertTrue(report.contains("HOT"), "should name the HOT zone"); + assertTrue(report.contains("Cuivre"), "hot zone should list copper"); + assertTrue(report.contains("Redstone"), "hot zone should list redstone"); + // 6 hot ores + 2 everywhere ores + assertTrue(report.contains("Minerais trouvables (8)"), "should count 6 zone ores + 2 everywhere ores"); + } + + @Test + @DisplayName("Temperate zone report lists temperate ores plus shard ores") + void testReport_TemperateZone() { + String report = reportText(-50, 0); + assertTrue(report.contains("TEMPERATE"), "should name the TEMPERATE zone"); + assertTrue(report.contains("Fer"), "temperate zone should list iron"); + assertFalse(report.contains("Lapis"), "temperate zone should not list cold ores"); + // 3 temperate ores + 2 everywhere ores + assertTrue(report.contains("Minerais trouvables (5)"), "should count 3 zone ores + 2 everywhere ores"); + } + + @Test + @DisplayName("Report includes position and zone thresholds") + void testReport_ContainsPositionAndThresholds() { + String report = reportText(123, -456); + assertTrue(report.contains("X=123"), "should show the X coordinate"); + assertTrue(report.contains("Z=-456"), "should show the Z coordinate"); + assertTrue(report.contains("-8000"), "should show the cold threshold"); + assertTrue(report.contains("8000"), "should show the hot threshold"); + } +}