Skip to content

API Worldgen Features and Placements

Piggo41 edited this page Oct 12, 2026 · 10 revisions

Features and placements

Lua API / World generation / Features and placements

Features describe reusable bounded terrain changes. Placements decide when, where, and in what order a feature is attempted in newly populated chunks.

betamoon.worldgen.features:add(definition)

Compiles a feature and returns a reference. Keys normalize to namespace:feature/path; get returns a reference or nil, and getRequired raises an error.

local clayPatch = betamoon.worldgen.features:add {
    key = "mymod:clay_patch",
    type = "block_patch",
    block = 82,
    radius = 4,
    tries = 32,
    replace = { 12, 13 }
}

Feature definitions

Type Fields and behavior
ore_vein Required block, size 1–128, and replace; optional metadata = 0.
block_patch Required block; radius = 4, tries = 64, replace = { air }, metadata = 0.
column Required block and height 1–32; direction = "up" or "down", replace = { air }, metadata = 0. Invalid direction strings are rejected.
disk Required block, radius, and replace; halfHeight = 1 accepts 0–8; metadata = 0.
weighted Required nonempty features = { { feature, weight }, ... }; one child is selected per invocation. Every weight is positive.
sequence Required nonempty features; every child plans in declaration order into the same atomic plan.
no_op Makes no changes; useful as an explicit disabled target.

Integer/range fields accept an integer or { min = n, max = n }. Composite maxBlocks and maxRadius fields are required safety declarations when the automatic limits are unsuitable; they are not measurements calculated from the children. A sequence child failure rejects the complete combined plan. A weighted feature runs only its selected child.

A local feature may change at most 8192 blocks within radius 32. Unknown dependencies and feature cycles reject the mod's new declarations while the previously active declarations remain available. References expose getKey() and can be reused by placements and composites.

betamoon.worldgen.placements:add(definition)

Schedules a feature and returns a placement reference.

betamoon.worldgen.placements:add {
    key = "mymod:clay_patch",
    feature = clayPatch,
    stage = "underground_features",
    dimensions = { "overworld" },
    attempts = {
        perChunk = { min = 1, max = 2 },
        extraChance = 0.25,
        rarity = 3
    },
    successLimit = 2,
    position = {
        horizontal = "chunk",
        height = { type = "ocean_floor" }
    },
    conditions = {
        ground = { 12, 13 },
        water = true
    }
}

Placement field definitions

Field Default / behavior
key Required stable key, normalized to namespace:placement/path.
feature Required feature, tree, or local-structure reference/key.
stage surface_features; also underground_features, entities, and post_process.
dimensions Overworld; accepts canonical names and overworld, nether/hell, both, or all.
attempts 1; integer/range 0–256 or the extended table below.
successLimit 256; accepts 1–256 and stops further attempts after that many successful placements.
position Horizontal and height sampling.
conditions Origin block, medium, light, sky, biome, and optional structure-site checks.
biomes List or { include, exclude, includeTags, excludeTags }.
before, after Same-stage placement dependencies. Cross-stage edges and cycles are rejected.
priority 0; lower values run first after dependency ordering.
salt 0; extra deterministic seed input.

Placement references expose getKey() and getFeature(). The registry provides get and getRequired.

Attempt order

attempts = {
    perChunk = { min = 2, max = 4 },
    extraChance = 0.3,
    probability = 0.5,
    rarity = 8
}

For each chunk:

  1. Sample the probability or rarity gate once.
  2. Sample perChunk once when it is a range.
  3. extraChance may add one attempt.
  4. Sample a position and conditions independently for each attempt.
  5. Stop after successLimit successful placements.

extraChance accepts 0 inclusive through 1 exclusive. probability accepts 0–1. rarity = n means probability 1/n; explicit probability takes precedence if both are present. Zero attempts are valid and keep a placement registered without generating it.

Horizontal and height sampling

horizontal = "chunk" samples random X/Z coordinates in the chunk. "grid" uses deterministic cells separated by gridSpacing 1–16. Attempts may revisit a grid cell when the attempt count exceeds the available cells.

Height type Defaults and behavior
uniform Uniform integer from min = 0 through max = 127.
triangular / normal Average of two uniform samples, concentrating results toward the middle.
fixed Uses value; when omitted, uses min.
surface / world_surface Samples the world surface plus optional offset.
solid_surface Finds the first position above solid terrain, ignoring fluids.
ocean_floor Finds the floor beneath a fluid column.
fluid_surface Finds the first position above the fluid column.
underground Samples 8–32 blocks below solid_surface by default; configure minDepth and maxDepth.
cave_floor Searches Y 1–96 by default for a floor with minimumClearance = 3.
ceiling Scans downward for air directly below a non-air block; accepts min and max.

When a height provider cannot find a candidate, that attempt is skipped.

Conditions

ground checks the block immediately below the sampled origin. air, water, and lava are booleans: true requires that medium at the origin and false rejects it. minLight and maxLight accept 0–15.

Ordinary requireSky supports requiring sky access. To require no sky across a structure footprint or clearance mask, use conditions.site.requireSky = false; see structure terrain placement.

placement:locateCandidate(world, x, z, maxChunks)

Returns the nearest chunk whose deterministic probability/rarity roll permits the placement, or nil. It is read-only and does not load or generate chunks.

local candidate = shrinePlacement:locateCandidate(ctx.world, ctx.position.x, ctx.position.z, 64)
if candidate then
    betamoon.chat:send("Eligible chunk center: %i, %i", candidate.x, candidate.z)
end

Result fields

Field Meaning
x, z Candidate chunk-center block coordinates.
chunkX, chunkZ Chunk coordinates.
loaded Whether the chunk is currently loaded, not whether placement succeeded.

maxChunks defaults to 32 and accepts 0–128. The lookup reproduces only the chunk gate. Terrain, positions, conditions, feature planning, and collisions may still reject every attempt. Ordinary placements are not saved or indexed as structure starts.

feature:preview(world, x, y, z, options)

Builds and validates the same plan as place without mutating the world.

local result = structure:preview(ctx.world, x, y, z, {
    seed = 42,
    rotation = "clockwise_90",
    mirror = "left_right"
})

Option definitions

Field Behavior
seed Additional deterministic salt. Reuse it for a matching place.
rotation Optional transform override for structures and structure-backed trees.
mirror Optional mirror override for structures and structure-backed trees.

World changes between preview and placement can still change or reject the later plan.

feature:place(world, x, y, z, options)

Plans and commits the complete feature from an active authoritative callback. The world must be that callback's live action-world handle, coordinates are separate integers, and Y must be 0–127. Successful runtime placement publishes render and neighbor updates only after the complete block transaction has committed.

The result contains placed, blocksChanged, optional reason, optional bounds = { min, max }, and optional terrain details. Common details include the requested/resolved anchor, sampled height range, support and site ratios, terrain mode/surface, and planned write totals. Structure terrain policies add focused aggregates:

Policy Detail fields
Foundation foundationCoreCells, foundationCoreWrites, foundationEdgeWrites, foundationMaximumDepth, foundationBounds
Terrace terraceCoreCells, terraceTransitionCells, terraceCutBlocks, terraceFillBlocks, terraceBounds
Conform conformSource, conformSupportColumns, conformSelectedColumns, conformComponents, conformMinimumDisplacement, conformMaximumDisplacement
Excavation excavationVolumeCells, excavationRemoved, excavationPreserved, excavationAdaptationOverlaps
Combined plan terrainWritesPlanned, terrainStructureOverlaps, structureWritesPlanned, totalWritesPlanned, and the first terrainRejectedPosition when relevant

Rejection reasons

Reason group Values
General blocked, out_of_bounds, unloaded_chunk, invalid_ground, protected, budget_exceeded, no_changes, runtime_error
Terrain fitting terrain_unreadable, terrain_slope, terrain_step, terrain_support, terrain_protected_block, terrain_tile_collision, terrain_mutation_budget
Foundation foundation_invalid_footprint, foundation_too_deep, foundation_protected_block, foundation_tile_collision, foundation_fluid_collision
Terrace terrace_invalid_footprint, terrace_cut_limit, terrace_fill_limit, terrace_grade_limit, terrace_protected_block, terrace_tile_collision, terrace_fluid_collision
Conform conform_path_failed, conform_empty_selection, conform_unknown_mask, conform_selector_invalid, conform_displacement_limit, conform_step_limit, conform_column_collision
Excavation excavation_invalid_volume, excavation_too_large, excavation_unreadable, excavation_protected_block, excavation_tile_collision, excavation_fluid_collision
Site site_wrong_surface_relation, site_wrong_medium, site_fluid_coverage, site_fluid_depth, site_depth, site_insufficient_cover, site_insufficient_cavity, site_sky_mismatch
Loot loot_target_missing, loot_inventory_size, loot_existing_contents, loot_overflow, loot_mutation_failed

Loot reasons are explained under structure inventory loot. A rejected local plan makes no block changes and does not load neighboring chunks.

Examples: simple feature placement, direct feature placement, and composite features.

Clone this wiki locally