diff --git a/hemp-addon/hemp_BP/feature_rules/hemp_patch_rule.json b/hemp-addon/hemp_BP/feature_rules/hemp_patch_rule.json new file mode 100644 index 0000000..45125ad --- /dev/null +++ b/hemp-addon/hemp_BP/feature_rules/hemp_patch_rule.json @@ -0,0 +1,35 @@ +{ + "format_version": "1.21.0", + "minecraft:feature_rules": { + "description": { + "identifier": "silverlabs:hemp_patch_rule", + "places_feature": "silverlabs:hemp_patch_feature" + }, + "conditions": { + "placement_pass": "surface_pass", + "minecraft:biome_filter": [ + { + "any_of": [ + { "test": "has_biome_tag", "value": "plains" }, + { "test": "has_biome_tag", "value": "forest" }, + { "test": "has_biome_tag", "value": "birch" }, + { "test": "has_biome_tag", "value": "flower_forest" } + ] + }, + { "test": "has_biome_tag", "operator": "!=", "value": "monster" } + ] + }, + "distribution": { + "iterations": 1, + "scatter_chance": 14, + "x": { "distribution": "uniform", "extent": [0, 16] }, + "y": { + "distribution": "fixed_grid", + "extent": [0, 64], + "grid_offset": 0, + "step_size": 1 + }, + "z": { "distribution": "uniform", "extent": [0, 16] } + } + } +} diff --git a/hemp-addon/hemp_BP/features/hemp_patch_feature.json b/hemp-addon/hemp_BP/features/hemp_patch_feature.json new file mode 100644 index 0000000..2bdb66a --- /dev/null +++ b/hemp-addon/hemp_BP/features/hemp_patch_feature.json @@ -0,0 +1,12 @@ +{ + "format_version": "1.21.0", + "minecraft:scatter_feature": { + "description": { "identifier": "silverlabs:hemp_patch_feature" }, + "iterations": 3, + "scatter_chance": 65, + "x": { "distribution": "uniform", "extent": [-3, 4] }, + "y": 0, + "z": { "distribution": "uniform", "extent": [-3, 4] }, + "places_feature": "silverlabs:hemp_single_feature" + } +} diff --git a/hemp-addon/hemp_BP/features/hemp_single_feature.json b/hemp-addon/hemp_BP/features/hemp_single_feature.json new file mode 100644 index 0000000..89c435d --- /dev/null +++ b/hemp-addon/hemp_BP/features/hemp_single_feature.json @@ -0,0 +1,27 @@ +{ + "format_version": "1.21.0", + "minecraft:single_block_feature": { + "description": { "identifier": "silverlabs:hemp_single_feature" }, + "places_block": { + "name": "silverlabs:hemp_crop", + "states": { + "silverlabs:hemp_age": 4, + "silverlabs:hemp_top": false + } + }, + "enforce_survivability_rules": true, + "enforce_placement_rules": true, + "may_attach_to": { + "min_sides_must_attach": 1, + "top": [ + "minecraft:grass_block", + "minecraft:dirt", + "minecraft:podzol", + "minecraft:coarse_dirt" + ] + }, + "may_replace": [ + "minecraft:air" + ] + } +} diff --git a/hemp-addon/hemp_BP/scripts/main.js b/hemp-addon/hemp_BP/scripts/main.js index cffdb18..46415d7 100644 --- a/hemp-addon/hemp_BP/scripts/main.js +++ b/hemp-addon/hemp_BP/scripts/main.js @@ -681,6 +681,62 @@ world.afterEvents.itemCompleteUse.subscribe((event) => { } }); +// --- Chest loot injection: first-time-open chests sometimes contain seeds --- +// Bedrock has no merge-loot-table mechanism, so we hook chest opens and +// deposit hemp_seeds with low probability into a random empty slot. +// Tracking is per-chest via a world dynamic property holding a JSON list +// of "dim:x:y:z" keys; pruned to a rolling cap to bound storage. +const CHEST_TYPES = new Set(["minecraft:chest", "minecraft:trapped_chest"]); +const CHEST_SEED_CHANCE = 0.08; +const SEEDED_PROP = "hemp_seeded_chests_v1"; +const SEEDED_CAP = 500; + +function chestKey(block) { + const l = block.location; + return `${block.dimension.id}:${l.x}:${l.y}:${l.z}`; +} +function loadSeededChests() { + try { + const raw = world.getDynamicProperty(SEEDED_PROP); + return new Set(raw ? JSON.parse(raw) : []); + } catch (_) { return new Set(); } +} +function saveSeededChests(set) { + // Prune oldest if over cap (Set preserves insertion order in JS) + if (set.size > SEEDED_CAP) { + const arr = Array.from(set).slice(set.size - SEEDED_CAP); + set = new Set(arr); + } + try { + world.setDynamicProperty(SEEDED_PROP, JSON.stringify(Array.from(set))); + } catch (_) {} +} + +world.afterEvents.playerInteractWithBlock.subscribe((event) => { + const block = event.block; + if (!block || !CHEST_TYPES.has(block.typeId)) return; + // Only on the open hand (not while holding an item, to avoid placing-into events firing twice) + const stack = event.itemStack; + if (stack) return; + const key = chestKey(block); + const seeded = loadSeededChests(); + if (seeded.has(key)) return; + seeded.add(key); + saveSeededChests(seeded); + if (!chance(CHEST_SEED_CHANCE)) return; + // Try to insert into a random empty slot + let inv; + try { inv = block.getComponent("minecraft:inventory")?.container; } catch (_) { return; } + if (!inv) return; + const empties = []; + for (let i = 0; i < inv.size; i++) if (!inv.getItem(i)) empties.push(i); + if (empties.length === 0) return; + const slot = empties[rand(empties.length)]; + const count = 1 + rand(3); // 1-3 seeds + try { inv.setItem(slot, new ItemStack(SEEDS, count)); } catch (_) {} + // No chat ping — let the player discover the seeds organically. +}); + system.run(() => { world.sendMessage("§a[Hemp] §7Hemp pack loaded."); });