diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..a02c33429 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,172 @@ +# Custom Ore Gem - Developer Guide for Agents + +This repository is a Minecraft **NeoForge 1.21.1** mod created with **MCreator**. Agents must follow these strict guidelines to ensure build stability and code preservation. + +## 1. Build & Test Commands + +Use the Gradle wrapper for all operations. Ensure you are using Java 21 (NeoForge 1.21.1 requirement). + +- **Build Mod:** + ```bash + ./gradlew build + ``` + - Generates the mod JAR in `build/libs/`. + - Always run this after making changes to verify compilation. + +- **Run Client:** + ```bash + ./gradlew runClient + ``` + +- **Run Server:** + ```bash + ./gradlew runServer + ``` + +- **Run Data Generation:** + ```bash + ./gradlew runData + ``` + - This generates resources (blockstates, models, loot tables) into `src/generated/resources`. + +- **Run All Tests:** + ```bash + ./gradlew test + ``` + +- **Run Single Test Class:** + ```bash + ./gradlew test --tests "net.mcreator.customoregen.OresCommandTest" + ``` + +- **Run Single Test Method:** + ```bash + ./gradlew test --tests "net.mcreator.customoregen.OresCommandTest.testCommandRegistration_ShouldRegisterOresCommand" + ``` + +- **Clean Project:** + ```bash + ./gradlew clean + ``` + +## 2. MCreator & Code Preservation + +**CRITICAL:** This project is partially generated by MCreator. +- **Generated Files:** Many files in `src/main/java` are regenerated on every build or MCreator export. +- **User Code Blocks:** You **MUST** only edit code within designated user code blocks in these files. + ```java + // Start of user code block [block_name] + // ... YOUR CODE HERE ... + // End of user code block [block_name] + ``` + - If a file does not have these blocks, assume it is **UNSAFE** to edit unless you created it yourself. + +- **Safe Files:** + - Files strictly created by you (e.g., in `src/test/java`). + - New utility classes or event handlers not managed by MCreator. + +- **Do NOT** directly modify the following unless inside a user block: + - `CustomOreGenModBlocks.java` + - `CustomOreGenModItems.java` + - `CustomOreGenModTabs.java` + - Any file in `net.mcreator.customoregen.procedures` (unless explicitly safe). + +## 3. Code Style & Conventions + +### Formatting +- **Indentation:** + - **Source Code (`src/main`):** Use **TABS** (default MCreator style). + - **Tests (`src/test`):** Use **4 SPACES** (typical for JUnit tests). + - **Consistency:** Always check the current file's indentation before editing. +- **Encoding:** UTF-8. + +### Naming +- **Classes:** PascalCase (e.g., `CustomOreGenMod`). +- **Methods:** camelCase (e.g., `queueServerWork`). +- **Constants:** UPPER_SNAKE_CASE (e.g., `MODID`, `COLD_BIOMES_TAG`). +- **Fields:** camelCase (e.g., `workQueue`, `oreGenConfig`). +- **Packages:** `net.mcreator.customoregen` (lowercase). + +### Imports +- Group imports in this specific order: + 1. Java/Standard Libraries (`java.*`, `javax.*`) + 2. Third-party Libraries (e.g., `org.apache.logging.log4j.*`) + 3. Minecraft/NeoForge (`net.minecraft.*`, `net.neoforged.*`) + 4. Project Classes (`net.mcreator.customoregen.*`) +- Avoid `import *` unless there are many imports from the same package (e.g., `java.util.*` is acceptable if heavily used, but explicit imports are preferred for clarity). + +### Logging +- Use `LogManager.getLogger(Class.class)` for loggers. +- Field name: `LOGGER`. +- Log levels: Use `debug` for dev info, `info` for general status, `error` for exceptions. + +## 4. Architecture & Patterns + +- **Framework:** NeoForge 1.21.1 (Java 21). +- **Registries:** Use `DeferredRegister` for all registries (Blocks, Items, Tabs, SoundEvents). + - Example: `public static final DeferredRegister.Blocks REGISTRY = DeferredRegister.createBlocks(CustomOreGenMod.MODID);` + +- **Event Bus:** + - The `@Mod` class registers the `IEventBus`. + - Use `@SubscribeEvent` for event handling. + - Event handlers often reside in `net.mcreator.customoregen.event` or static inner classes annotated with `@EventBusSubscriber`. + +- **Configuration:** + - Located in `net.mcreator.customoregen.config.ModConfigs`. + - Uses `ModConfig.Type.COMMON` built with `ModConfigSpec`. + - Access configs via the public static fields (e.g., `ModConfigs.ORE_GEN.shardDiamondOreCount.get()`). + +- **Commands:** + - Registered via `RegisterCommandsEvent`. + - Use Brigadier (`CommandDispatcher`, `CommandContext`). + - See `OresCommand.java` for the reference implementation. + +## 5. Testing Guidelines + +- **Framework:** JUnit 5 (Jupiter) + Mockito. +- **Location:** `src/test/java`. +- **Mocking Strategy:** + - Since a full Minecraft environment is not available in unit tests, you **MUST** mock Minecraft classes. + - Use `@ExtendWith(MockitoExtension.class)`. + - Mock critical classes: `@Mock ServerPlayer player`, `@Mock Level level`, `@Mock BlockPos pos`. + - Stub methods: `when(level.getBiome(pos)).thenReturn(biomeHolder);`. +- **Assertions:** Use `org.junit.jupiter.api.Assertions` (e.g., `assertEquals`, `assertDoesNotThrow`). + +## 6. Common Tasks + +- **Adding a New Ore:** + 1. Create Block & Item (MCreator/Manual). + 2. Add JSONs: Loot Table, Configured Feature, Placed Feature, Biome Modifier. + 3. Register in `CustomOreGenModBlocks` and `CustomOreGenModItems`. + 4. Update `OresCommand.java` lists (e.g., `COLD_ORES`, `HOT_ORES`) to make it discoverable. + 5. Update `OreBreakEventHandler.java` if it has custom drops logic. + 6. Add to `ModConfigs.java` for generation parameters (vein size, count, etc.). + +- **Modifying Logic:** + - Check `procedures/` for game logic (often MCreator generated). + - Check `event/` for event-driven logic. + - Always verify if logic changes need a corresponding test update. + +## 7. Safety & Verification + +- **Backups:** If you are unsure about MCreator regeneration, backup the file before editing. +- **Verification:** + - Always run `./gradlew build` after changes to ensure no compilation errors. + - If you touch config files, ensure `ModConfigsTest` still passes. + - If you touch commands, ensure `OresCommandTest` still passes. + +## 8. Directory Structure +``` +src/ +├── main/ +│ ├── java/net/mcreator/customoregen/ +│ │ ├── block/ # Block definitions +│ │ ├── config/ # Configuration classes +│ │ ├── event/ # Event handlers +│ │ ├── init/ # Registration (Blocks, Items, Tabs) +│ │ ├── item/ # Item definitions +│ │ └── procedures/ # Game logic procedures +│ └── resources/ # Assets and data (textures, models, lang) +└── test/ + └── java/net/mcreator/customoregen/ # Unit tests +``` diff --git a/CLAUDE.md b/CLAUDE.md index fce064bae..1d2730005 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,6 +57,7 @@ net.mcreator.customoregen/ ├── block/ # Ore block classes (17 blocks) ├── item/ # Items (Diamond Shard, tools, armor, Paxel, OreBiomeFinder) ├── config/ # NeoForge configuration system (ModConfigs.java) +├── event/ # Event handlers (OreBreakEventHandler, EnchantabilityFix) ├── procedures/ # Game logic (ConfigurableOreDropsProcedure, OreexperienceProcedure) └── init/ ├── CustomOreGenModBlocks.java # Block registry (deferred register) @@ -76,13 +77,13 @@ The mod uses **NeoForge** biome modifiers to distribute ores based on biome temp - `tempered_biomes.json` - Temperate biomes (iron, concentrated coal) - BOP biomes are included with `"required": false` for optional compatibility -2. **Biome Modifiers** (`src/main/resources/data/custom_ore_gen/forge/biome_modifier/`): +2. **Biome Modifiers** (`src/main/resources/data/custom_ore_gen/neoforge/biome_modifier/`): - Each ore has a JSON file linking it to biome tags - Special case: `deepslatesharddiamondore_biome_modifier.json` uses `"type": "forge:any"` for all biomes - **JSON Structure**: ```json { - "type": "forge:add_features", + "type": "neoforge:add_features", "biomes": "custom_ore_gen:cold_biomes", // or {"type": "forge:any"} for all biomes "features": "custom_ore_gen:deepslatesharddiamondore", "step": "underground_ores" @@ -111,10 +112,11 @@ Located in `src/main/java/net/mcreator/customoregen/config/`: - Generated config file: `config/custom_ore_gen-common.toml` (created on first run) **Current Implementation Status**: +- **✅ Ore Drops**: Fully implemented via `OreBreakEventHandler.java` which listens to `BlockEvent.BreakEvent` and calls `ConfigurableOreDropsProcedure.execute()` for all custom ores - **⚠️ Tool Stats**: Config classes exist in `ModConfigs.java`, but tools currently have hardcoded values (e.g., `SharddiamondpickaxeItem.java:18` has `return 200` for durability). Config reading not yet implemented. -- **⚠️ Ore Drops**: `ConfigurableOreDropsProcedure.java` exists but requires MCreator integration to link to block loot tables - **⚠️ Feature Toggles**: Defined in `FeatureToggleConfig` but not yet wired to block/item registration conditional logic - **⚠️ Ore Generation**: Config values exist but worldgen JSON files still use hardcoded values +- **⚠️ Enchantability**: `EnchantabilityFix.java` exists but is commented out - enchantment values not yet applied to items ### Ore Biome Finder @@ -131,12 +133,13 @@ The `OreBiomeFinderItem` (`item/OreBiomeFinderItem.java`) and `/ores` command (` To add a new ore type (requires MCreator for full integration): 1. **Create the block** in MCreator with proper properties (sound type, harvest level, etc.) -2. **Add loot table** at `src/main/resources/data/custom_ore_gen/loot_tables/blocks/{orename}.json` +2. **Add loot table** at `src/main/resources/data/custom_ore_gen/loot_table/blocks/{orename}.json` (note: `loot_table` not `loot_tables`) 3. **Add configured_feature** JSON in `src/main/resources/data/custom_ore_gen/worldgen/configured_feature/` 4. **Add placed_feature** JSON in `src/main/resources/data/custom_ore_gen/worldgen/placed_feature/` -5. **Create biome_modifier** JSON in `src/main/resources/data/custom_ore_gen/forge/biome_modifier/` linking to a biome tag (or create a new tag in `tags/worldgen/biome/`) +5. **Create biome_modifier** JSON in `src/main/resources/data/custom_ore_gen/neoforge/biome_modifier/` linking to a biome tag (or create a new tag in `tags/worldgen/biome/`) 6. **Add BOP entries** (optional) to appropriate biome tag JSON files with `"required": false` wrapper 7. **Update `OreBiomeFinderItem.java`** to add the new ore to the appropriate category list +8. **Add ore type mapping** in `OreBreakEventHandler.java` if you want configurable drops via `ConfigurableOreDropsProcedure` ## User Code Sections @@ -156,6 +159,36 @@ public static final Supplier ORE_BIOME_FINDER = REGISTRY.register("ore_bio **Important**: Custom items like the Ore Biome Finder, Shard Diamond armor, and Paxel are registered in this protected section and will survive MCreator rebuilds. +## Event Handlers + +The mod uses NeoForge's event system for ore processing: + +### OreBreakEventHandler +- Listens to `BlockEvent.BreakEvent` with `@SubscribeEvent` +- Maps custom ore blocks to ore type strings (`shard_diamond`, `concentrated_coal`, `pure_golden`, etc.) +- Calls `ConfigurableOreDropsProcedure.execute()` with ore type when player breaks ore with correct tool +- Supports 10 ore types: shard_diamond, concentrated_coal, pure_golden, impure_iron, concentrated_diamond, lapis, redstone, emerald, copper + +### EnchantabilityFix +- Uses `ModifyDefaultComponentsEvent` to set enchantability values on tools and armor +- Currently **commented out** - enchantability not yet applied +- When active, should set `DataComponents.ENCHANTABLE` with value 9 (tools) or 14 (armor) + +## Loot Table Format + +NeoForge 1.21 uses `loot_table` (singular) instead of `loot_tables` (plural): +- Location: `src/main/resources/data/custom_ore_gen/loot_table/blocks/{orename}.json` +- Includes Silk Touch support via `match_tool` condition +- Uses `random_sequence` for loot table randomization +- Example structure in `deepslatesharddiamondore.json` shows Silk Touch → drop block, otherwise drops handled by `OreBreakEventHandler` + +## Vanilla Ore Removal + +The mod removes vanilla ores via NeoForge biome modifiers (NOT KubeJS anymore): +- `src/main/resources/data/custom_ore_gen/neoforge/biome_modifier/remove_vanilla_ores.json` +- Uses `neoforge:remove_features` type to remove vanilla ore generation +- This replaces the old KubeJS automatic script system from Forge 1.20.1 + ## Biomes O' Plenty Integration The mod includes BOP biome support through biome tag entries. BOP biomes are wrapped with: @@ -175,6 +208,16 @@ After making changes: 2. Run `./gradlew runClient` to test in-game 3. Check logs in `run/logs/` for errors +## Enchantment Tags + +The mod includes enchantment tags at `src/main/resources/data/minecraft/tags/item/enchantable/`: +- **armor.json** - Marks Shard Diamond armor pieces as enchantable +- **durability.json** - Marks tools and armor for durability enchantments +- **mining.json** - Marks pickaxes, shovels, and paxel as mining tools +- **weapon.json** - Marks axes as weapons + +These tags enable proper enchantment behavior for custom items in the enchanting table and anvil. + ## Important Notes ### README Disclaimer diff --git a/gradle.properties b/gradle.properties index 424e86d4a..9394fecb7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,6 @@ org.gradle.jvmargs=-Xmx4G org.gradle.daemon=true +org.gradle.java.home=C:\\Program Files\\Java\\jdk-24 # Mod Properties mod_version=3.0 diff --git a/src/main/java/net/mcreator/customoregen/config/ModConfigs.java b/src/main/java/net/mcreator/customoregen/config/ModConfigs.java index e49d7c96d..19e513aa8 100644 --- a/src/main/java/net/mcreator/customoregen/config/ModConfigs.java +++ b/src/main/java/net/mcreator/customoregen/config/ModConfigs.java @@ -189,6 +189,7 @@ public class ModConfigs { public final ModConfigSpec.ConfigValue concentratedCoalOreMinDrops; public final ModConfigSpec.ConfigValue concentratedCoalOreMaxDrops; + public final ModConfigSpec.BooleanValue concentratedCoalOreEnableFortune; public final ModConfigSpec.ConfigValue pureGoldenOreMinDrops; public final ModConfigSpec.ConfigValue pureGoldenOreMaxDrops; @@ -204,15 +205,18 @@ public class ModConfigs { public final ModConfigSpec.ConfigValue lapisOreMinDrops; public final ModConfigSpec.ConfigValue lapisOreMaxDrops; + public final ModConfigSpec.BooleanValue lapisOreEnableFortune; public final ModConfigSpec.ConfigValue redstoneOreMinDrops; public final ModConfigSpec.ConfigValue redstoneOreMaxDrops; + public final ModConfigSpec.BooleanValue redstoneOreEnableFortune; public final ModConfigSpec.ConfigValue emeraldOreMinDrops; public final ModConfigSpec.ConfigValue emeraldOreMaxDrops; public final ModConfigSpec.ConfigValue copperOreMinDrops; public final ModConfigSpec.ConfigValue copperOreMaxDrops; + public final ModConfigSpec.BooleanValue copperOreEnableFortune; public final ModConfigSpec.ConfigValue oreExperienceDrops; @@ -234,32 +238,35 @@ public class ModConfigs { builder.push("concentrated_diamond_ore"); concentratedDiamondOreMinDrops = builder - .comment("Minimum diamonds dropped by Concentrated Diamond Ore (default: 1)") + .comment("Minimum diamonds dropped by Deepslate Diamond Ore (default: 1) - vanilla equivalent") .defineInRange("minDrops", 1, 0, 64); concentratedDiamondOreMaxDrops = builder - .comment("Maximum diamonds dropped by Concentrated Diamond Ore (default: 2)") - .defineInRange("maxDrops", 2, 0, 64); + .comment("Maximum diamonds dropped by Deepslate Diamond Ore (default: 1) - vanilla equivalent") + .defineInRange("maxDrops", 1, 0, 64); concentratedDiamondOreEnableFortune = builder - .comment("Enable Fortune enchantment on Concentrated Diamond Ore (default: true)") + .comment("Enable Fortune enchantment on Deepslate Diamond Ore (default: true) - vanilla equivalent") .define("enableFortune", true); builder.pop(); builder.push("concentrated_coal_ore"); concentratedCoalOreMinDrops = builder - .comment("Minimum coal dropped by Concentrated Coal Ore (default: 2)") - .defineInRange("minDrops", 2, 0, 64); + .comment("Minimum coal dropped by Concentrated Coal Ore (default: 1) - vanilla equivalent") + .defineInRange("minDrops", 1, 0, 64); concentratedCoalOreMaxDrops = builder - .comment("Maximum coal dropped by Concentrated Coal Ore (default: 4)") - .defineInRange("maxDrops", 4, 0, 64); + .comment("Maximum coal dropped by Concentrated Coal Ore (default: 1) - vanilla equivalent") + .defineInRange("maxDrops", 1, 0, 64); + concentratedCoalOreEnableFortune = builder + .comment("Enable Fortune enchantment on Concentrated Coal Ore (default: true) - vanilla equivalent") + .define("enableFortune", true); builder.pop(); builder.push("pure_golden_ore"); pureGoldenOreMinDrops = builder - .comment("Minimum gold nuggets dropped by Pure Golden Ore (default: 2)") - .defineInRange("minDrops", 2, 0, 64); + .comment("Minimum raw gold dropped by Pure Golden Ore (default: 1) - vanilla equivalent") + .defineInRange("minDrops", 1, 0, 64); pureGoldenOreMaxDrops = builder - .comment("Maximum gold nuggets dropped by Pure Golden Ore (default: 4)") - .defineInRange("maxDrops", 4, 0, 64); + .comment("Maximum raw gold dropped by Pure Golden Ore (default: 1) - vanilla equivalent") + .defineInRange("maxDrops", 1, 0, 64); builder.pop(); builder.push("ash_coal_ore"); @@ -273,44 +280,53 @@ public class ModConfigs { builder.push("impure_ores"); impureIronOreMinDrops = builder - .comment("Minimum impure iron dropped by Impure Iron Ore (default: 1)") + .comment("Minimum raw iron dropped by Iron Ore (default: 1) - vanilla equivalent") .defineInRange("ironMinDrops", 1, 0, 64); impureIronOreMaxDrops = builder - .comment("Maximum impure iron dropped by Impure Iron Ore (default: 2)") - .defineInRange("ironMaxDrops", 2, 0, 64); + .comment("Maximum raw iron dropped by Iron Ore (default: 1) - vanilla equivalent") + .defineInRange("ironMaxDrops", 1, 0, 64); impureGoldOreMinDrops = builder - .comment("Minimum impure gold dropped by Impure Gold Ore (default: 1)") + .comment("Minimum raw gold dropped by Impure Gold Ore (default: 1)") .defineInRange("goldMinDrops", 1, 0, 64); impureGoldOreMaxDrops = builder - .comment("Maximum impure gold dropped by Impure Gold Ore (default: 2)") - .defineInRange("goldMaxDrops", 2, 0, 64); + .comment("Maximum raw gold dropped by Impure Gold Ore (default: 1)") + .defineInRange("goldMaxDrops", 1, 0, 64); builder.pop(); builder.push("vanilla_ore_variants"); lapisOreMinDrops = builder - .comment("Minimum lapis dropped (default: 4)") + .comment("Minimum lapis dropped (default: 4) - vanilla equivalent") .defineInRange("lapisMinDrops", 4, 0, 64); lapisOreMaxDrops = builder - .comment("Maximum lapis dropped (default: 9)") + .comment("Maximum lapis dropped (default: 9) - vanilla equivalent") .defineInRange("lapisMaxDrops", 9, 0, 64); + lapisOreEnableFortune = builder + .comment("Enable Fortune enchantment on Lapis Ore (default: true) - vanilla equivalent") + .define("enableFortune", true); redstoneOreMinDrops = builder - .comment("Minimum redstone dropped (default: 4)") + .comment("Minimum redstone dropped (default: 4) - vanilla equivalent") .defineInRange("redstoneMinDrops", 4, 0, 64); redstoneOreMaxDrops = builder - .comment("Maximum redstone dropped (default: 5)") + .comment("Maximum redstone dropped (default: 5) - vanilla equivalent") .defineInRange("redstoneMaxDrops", 5, 0, 64); + redstoneOreEnableFortune = builder + .comment("Enable Fortune enchantment on Redstone Ore (default: true) - vanilla equivalent") + .define("enableFortune", true); emeraldOreMinDrops = builder - .comment("Minimum emerald dropped (default: 1)") + .comment("Minimum emerald dropped (default: 1) - vanilla equivalent") .defineInRange("emeraldMinDrops", 1, 0, 64); emeraldOreMaxDrops = builder - .comment("Maximum emerald dropped (default: 1)") + .comment("Maximum emerald dropped (default: 1) - vanilla equivalent") .defineInRange("emeraldMaxDrops", 1, 0, 64); copperOreMinDrops = builder - .comment("Minimum copper dropped (default: 2)") + .comment("Minimum raw copper dropped (default: 2) - vanilla equivalent") .defineInRange("copperMinDrops", 2, 0, 64); copperOreMaxDrops = builder - .comment("Maximum copper dropped (default: 5)") + .comment("Maximum raw copper dropped (default: 5) - vanilla equivalent") .defineInRange("copperMaxDrops", 5, 0, 64); + copperOreEnableFortune = builder + .comment("Enable Fortune enchantment on Copper Ore (default: true) - vanilla equivalent") + .define("enableFortune", true); builder.pop(); oreExperienceDrops = builder diff --git a/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java b/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java index c6dc44231..bebd1ce1a 100644 --- a/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java +++ b/src/main/java/net/mcreator/customoregen/procedures/ConfigurableOreDropsProcedure.java @@ -80,13 +80,14 @@ public class ConfigurableOreDropsProcedure { 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(); - dropItem = new ItemStack(Items.GOLD_NUGGET); + dropItem = new ItemStack(Items.RAW_GOLD); break; case "ash_coal": @@ -98,7 +99,7 @@ public class ConfigurableOreDropsProcedure { case "impure_iron": minDrops = ModConfigs.DROPS.impureIronOreMinDrops.get(); maxDrops = ModConfigs.DROPS.impureIronOreMaxDrops.get(); - dropItem = new ItemStack(Items.IRON_NUGGET); // Use iron nugget until IMPUREIRON item is created + dropItem = new ItemStack(Items.RAW_IRON); break; case "impure_gold": @@ -110,12 +111,14 @@ public class ConfigurableOreDropsProcedure { 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; @@ -128,6 +131,7 @@ public class ConfigurableOreDropsProcedure { case "copper": minDrops = ModConfigs.DROPS.copperOreMinDrops.get(); maxDrops = ModConfigs.DROPS.copperOreMaxDrops.get(); + enableFortune = ModConfigs.DROPS.copperOreEnableFortune.get(); dropItem = new ItemStack(Items.RAW_COPPER); break; @@ -136,15 +140,23 @@ public class ConfigurableOreDropsProcedure { return; } - // Calculate drops - int dropCount = minDrops + random.nextInt(maxDrops - minDrops + 1); + // Calculate base drops + int dropCount = minDrops + (maxDrops > minDrops ? random.nextInt(maxDrops - minDrops + 1) : 0); // Apply fortune if enabled if (enableFortune && fortuneLevel > 0) { - int fortuneBonus = random.nextInt(fortuneLevel + 2) - 1; - if (fortuneBonus < 0) - fortuneBonus = 0; - dropCount += fortuneBonus; + if (oreType.equals("lapis") || oreType.equals("copper") || oreType.equals("redstone")) { + // Vanilla-like multiplier for multi-drop ores (Lapis, Copper, Redstone) + // Level 3: 2/5 chance of x1, 1/5 chance of x2, 1/5 chance of x3, 1/5 chance of x4 + int multiplier = random.nextInt(fortuneLevel + 2) - 1; + if (multiplier < 0) multiplier = 0; + dropCount *= (multiplier + 1); + } else { + // Vanilla-like bonus for discrete ores (Diamond, Coal, Emerald) + int fortuneBonus = random.nextInt(fortuneLevel + 2) - 1; + if (fortuneBonus < 0) fortuneBonus = 0; + dropCount += fortuneBonus; + } } // Drop the items @@ -161,15 +173,35 @@ public class ConfigurableOreDropsProcedure { } } - // Drop experience if configured - int expAmount = ModConfigs.DROPS.oreExperienceDrops.get(); - if (expAmount > 0 && entity instanceof Player) { - ExperienceOrb expOrb = new ExperienceOrb( - (ServerLevel) world, - x + 0.5, y + 0.5, z + 0.5, - expAmount - ); - ((ServerLevel) world).addFreshEntity(expOrb); + // Drop experience based on ore type (Vanilla 1.21 values) + int expAmount = 0; + if (entity instanceof Player) { + switch (oreType) { + case "concentrated_coal": + expAmount = random.nextInt(3); // 0-2 + break; + case "shard_diamond": + case "concentrated_diamond": + case "emerald": + expAmount = 3 + random.nextInt(5); // 3-7 + break; + case "lapis": + expAmount = 2 + random.nextInt(4); // 2-5 + break; + case "redstone": + expAmount = 1 + random.nextInt(5); // 1-5 + break; + // Iron, Gold, Copper ores don't drop XP in Vanilla 1.21 (only after smelting) + } + + if (expAmount > 0 && world instanceof ServerLevel) { + ExperienceOrb expOrb = new ExperienceOrb( + (ServerLevel) world, + x + 0.5, y + 0.5, z + 0.5, + expAmount + ); + ((ServerLevel) world).addFreshEntity(expOrb); + } } } } diff --git a/src/main/java/net/mcreator/customoregen/procedures/OreexperienceProcedure.java b/src/main/java/net/mcreator/customoregen/procedures/OreexperienceProcedure.java index e73d4cf35..dd5a432bc 100644 --- a/src/main/java/net/mcreator/customoregen/procedures/OreexperienceProcedure.java +++ b/src/main/java/net/mcreator/customoregen/procedures/OreexperienceProcedure.java @@ -32,8 +32,9 @@ public class OreexperienceProcedure { } if (!hasSilkTouch) { - if (world instanceof ServerLevel _level) - _level.addFreshEntity(new ExperienceOrb(_level, x, y, z, 2)); + // XP is now handled centrally in ConfigurableOreDropsProcedure to match Vanilla 1.21 values + // if (world instanceof ServerLevel _level) + // _level.addFreshEntity(new ExperienceOrb(_level, x, y, z, 2)); } } }