feat(hemp): wild patches in plains/forest + chest seed injection
Two new ways to find hemp seeds without already having any: 1. Worldgen: minecraft:scatter_feature spawns 1-3 mature silverlabs:hemp_crop blocks on grass/dirt in plains/forest/birch/ flower_forest biomes (~14% scatter chance per chunk surface pass). 2. Chest injection: 8% chance per chest first-open to plant 1-3 seeds in a random empty slot. Tracked per-chest via world dynamic property (rolling cap of 500 entries) so each chest only contributes once. Bedrock has no loot-table merge mechanism so we can't add seeds to vanilla village chests without losing their vanilla loot — script injection sidesteps that and stays version-independent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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.");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user