1
Blocks
minjaesong edited this page 2025-11-24 21:24:45 +09:00
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Blocks

Audience: Module developers creating terrain, walls, and decorative blocks.

Blocks are the fundamental building units of Terrarum's voxel world. This guide covers the block system, properties, rendering, autotiling, and creating custom blocks.

Overview

Blocks provide:

  • Terrain tiles — Ground, stone, dirt, ores
  • Wall tiles — Backgrounds, structures, buildings
  • Physics properties — Collision, friction, density
  • Visual properties — Colour, luminosity, reflectance
  • Material properties — Strength, material composition
  • Autotiling — Automatic visual connections between adjacent blocks
  • Dynamic properties — Animated/flickering lights, seasonal variation

Block System Architecture

BlockProp Class

Note: You don't implement BlockProp yourself — it's automatically generated by ModMgr.GameBlockLoader at runtime from CSV data.

Each block is represented by a BlockProp instance containing all properties:

class BlockProp : TaggedProp {
    var id: ItemID = ""
    var numericID: Int = -1
    var nameKey: String = ""

    // Visual properties
    var shadeColR = 0f        // Red shade (0.0-1.0)
    var shadeColG = 0f        // Green shade
    var shadeColB = 0f        // Blue shade
    var shadeColA = 0f        // UV shade
    var opacity = Cvec()      // Opacity per channel

    // Luminosity
    internal var baseLumColR = 0f
    internal var baseLumColG = 0f
    internal var baseLumColB = 0f
    internal var baseLumColA = 0f

    // Physical properties
    var strength: Int = 0     // Mining difficulty (HP)
    var density: Int = 0      // Mass density (kg/m³)
    var isSolid: Boolean = false
    var isPlatform: Boolean = false
    var friction: Int = 0     // Horizontal friction

    // Material & drops
    var material: String = "" // 4-letter material code
    var drop: ItemID = ""     // Item dropped when mined
    var world: ItemID = ""    // Item used to place block

    // Advanced
    var dynamicLuminosityFunction: Int = 0  // 0=static, >0=animated
    var reflectance = 0f      // Light reflectance (0.0-1.0)
    var maxSupport: Int = -1  // Structural support limit

    var tags = HashSet<String>()
    val extra = Codex()       // Extra module data
}

BlockCodex Singleton

All blocks are registered in the global BlockCodex:

class BlockCodex {
    val blockProps = HashMap<ItemID, BlockProp>()
    val dynamicLights = SortedArrayList<ItemID>()
    val tileToVirtual = HashMap<ItemID, List<ItemID>>()
    val virtualToTile = HashMap<ItemID, ItemID>()

    operator fun get(id: ItemID?): BlockProp
    fun fromModule(module: String, path: String, blockRegisterHook: (BlockProp) -> Unit)
}

Access pattern:

val stone = BlockCodex["basegame:2"]
println("Strength: ${stone.strength}")
println("Density: ${stone.density} kg/m³")
println("Is solid: ${stone.isSolid}")

Block Types

Terrain Tiles

Solid blocks that make up the ground:

val dirt = BlockCodex["basegame:3"]
dirt.isSolid = true
dirt.isWall = false  // Terrain, not wall
dirt.material = "SOIL"

Properties:

  • isSolid = true — Blocks movement
  • Wall flag = 0 — Terrain tile
  • Friction affects horizontal movement
  • Density determines weight

Wall Tiles

Background blocks for structures:

val stoneWall = BlockCodex["basegame:131"]
stoneWall.isSolid = false   // Walls don't block movement
stoneWall.isWall = true     // Render behind terrain
stoneWall.material = "ROCK"

Properties:

  • isSolid = false — Passable
  • Wall flag = 1 — Background tile
  • Lower opacity than terrain
  • No collision physics

Platform Blocks

Special blocks that allow one-way passage:

val woodPlatform = BlockCodex["basegame:45"]
woodPlatform.isSolid = true
woodPlatform.isPlatform = true  // Can jump through from below

Behaviour:

  • Players can jump up through platforms
  • Players stand on top of platforms
  • Can pass through from below/sides

Actor Blocks

Blocks that are actually actors (trees, plants):

val tree = BlockCodex["basegame:80"]
tree.isActorBlock = true  // Not a real tile, just looks like one
tree.isSolid = false      // Actors handle collision

Uses:

  • Trees with complex behaviour
  • Plants that grow
  • Animated decorations
  • Blocks that need per-instance state

Block Properties

Visual Properties

Shade Colour

Tints the block texture:

"shdr";"shdg";"shdb";"shduv"
"0.6290";"0.6290";"0.6290";"0.6290"
  • RGB channels for visible light
  • UV channel for fluorescence
  • Values 0.0 (black) to 1.0 (full brightness)

Luminosity

Blocks can emit light:

"lumr";"lumg";"lumb";"lumuv"
"0.7664";"0.2032";"0.0000";"0.0000"

Example: Lava

  • Red: 0.7664 (bright orange-red)
  • Green: 0.2032 (dim)
  • Blue: 0.0 (none)
  • UV: 0.0 (none)

Dynamic luminosity:

var dynamicLuminosityFunction: Int = 0
  • 0 — Static light
  • 1-7 — Various flicker/pulse patterns
  • Used for torches, lava, glowstone

Reflectance

Surface reflectivity for lighting:

"refl"
"0.8"
  • 0.0 — Fully absorbing (dark surfaces)
  • 1.0 — Fully reflective (mirrors, metal)
  • Affects how light bounces off surfaces

Physical Properties

Strength (Mining Difficulty)

Hit points required to break block:

"str"
"120"

Common values:

  • 1 — Air (instant)
  • 50 — Dirt (easy)
  • 120 — Stone (normal)
  • 500 — Obsidian (hard)
  • 10000 — Bedrock (unbreakable)

Density

Mass per cubic metre:

"dsty"
"2600"

Common densities:

  • Air: 1 kg/m³
  • Wood: 800 kg/m³
  • Stone: 2600 kg/m³
  • Iron ore: 7800 kg/m³
  • Gold ore: 19300 kg/m³

Friction

Horizontal movement resistance:

"fr"
"16"

Values:

  • 0 — Frictionless (ice)
  • 4 — Low friction (polished)
  • 16 — Normal friction (stone, dirt)
  • 32 — High friction (rough)

Material Assignment

Every block has a material ID:

"mate"
"ROCK"

Materials define:

  • Physical properties (hardness, density)
  • Sound effects (footsteps, impacts)
  • Tool effectiveness
  • Thermal properties

Common materials:

  • AIIR — Air
  • DIRT — Dirt, sand
  • ROCK — Stone, ores
  • WOOD — Wooden blocks
  • WATR — Water

See Modules-Codex-Systems#MaterialCodex for full material properties.

Block Tags

Tags enable flexible queries and categorisation:

"tags"
"STONE,NATURAL,MINERAL"

Common Tags

Terrain types:

  • DIRT — Dirt, sand, clay
  • STONE — Rock, ores
  • WOOD — Wooden planks
  • METAL — Metal blocks

Placement:

  • NATURAL — Generated naturally
  • ARTIFICIAL — Player-created
  • INCONSEQUENTIAL — Doesn't affect world generation

Behaviour:

  • TREE — Tree blocks (special handling)
  • ORE — Ore blocks
  • FLUID — Liquid blocks
  • NORANDTILE — Disable autotiling randomisation

Usage:

// Find all stone blocks
val stoneBlocks = BlockCodex.blockProps.values.filter { it.hasTag("STONE") }

// Check if block is ore
if (block.hasTag("ORE")) {
    println("This is an ore block!")
}

// Check multiple tags
if (block.hasAllTagsOf("TREE", "NATURAL")) {
    println("Natural tree")
}

Creating Custom Blocks

Important: You don't write Kotlin/Java code to create blocks. You simply define block properties in CSV files, and the system automatically generates everything at runtime.

CSV Format

Blocks are defined in blocks/blocks.csv:

"id";"drop";"spawn";"name";"shdr";"shdg";"shdb";"shduv";"str";"dsty";"mate";"solid";"wall";"grav";"dlfn";"fv";"fr";"lumr";"lumg";"lumb";"lumuv";"refl";"tags"
"2";"basegame:2";"basegame:2";"BLOCK_STONE";"0.6290";"0.6290";"0.6290";"0.6290";"120";"2600";"ROCK";"1";"0";"N/A";"0";"0";"16";"0.0000";"0.0000";"0.0000";"0.0000";"0.0";"STONE,NATURAL,MINERAL"

Key columns:

  • id — Numeric tile ID (unique within module)
  • drop — Item ID dropped when mined
  • spawn — Item ID used to place block
  • name — Translation key
  • shdr/shdg/shdb/shduv — Shade colour (RGBA)
  • str — Strength/HP
  • dsty — Density (kg/m³)
  • mate — Material ID (4-letter code)
  • solid — Is solid (1) or passable (0)
  • wall — Is wall (1) or terrain (0)
  • fr — Friction coefficient
  • lumr/lumg/lumb/lumuv — Luminosity (RGBA)
  • dlfn — Dynamic luminosity function (0-7)
  • refl — Reflectance (0.0-1.0)
  • tags — Comma-separated tags

Example: Custom Glowing Ore

"id";"drop";"spawn";"name";"shdr";"shdg";"shdb";"shduv";"str";"dsty";"mate";"solid";"wall";"grav";"dlfn";"fv";"fr";"lumr";"lumg";"lumb";"lumuv";"refl";"tags"
"200";"mymod:crystal";"mymod:200";"BLOCK_CRYSTAL_ORE";"0.7";"0.8";"0.9";"0.2";"150";"3200";"ROCK";"1";"0";"N/A";"0";"0";"16";"0.2";"0.5";"0.8";"0.0";"0.3";"STONE,ORE,GLOWING"

This creates:

  • Blue-tinted glowing crystal ore
  • Strength 150 (harder than normal stone)
  • Emits cyan light (0.2 red, 0.5 green, 0.8 blue)
  • Drops mymod:crystal item when mined

Loading Custom Blocks

In your module's EntryPoint:

override fun invoke() {
    // Load blocks
    ModMgr.GameBlockLoader.invoke("mymod")
}

The loader automatically:

  1. Reads blocks/blocks.csv
  2. Parses CSV records
  3. Creates BlockProp instances from CSV data
  4. Registers blocks in BlockCodex
  5. Creates corresponding block items via makeNewItemObj() and registers them in ItemCodex

You never manually create BlockProp instances — the system handles everything. You only define CSV data.

Block Rendering

Tile Atlas System

Blocks are rendered from texture atlases with autotiling.

Atlas structure:

  • 16×16 pixel tiles
  • Organised in sprite sheets
  • Multiple tiles per block (autotiling variants)
  • Six seasonal variations per tile

See Tile Atlas System for details on atlas generation and seasonal mixing.

Autotiling

Blocks automatically connect to adjacent similar blocks.

Tile connections:

  • 4-directional (cardinal)
  • 8-directional (cardinal + diagonal)
  • Corner pieces
  • Edge pieces

Subtiling:

Some blocks have internal variation using subtiles:

  • Random patterns (ore veins)
  • Gradients (stone texture)
  • Detail variations

See Autotiling in Depth for complete autotiling documentation.

Rendering Order

Blocks render in layers:

  1. Wall layer — Background walls
  2. Terrain layer — Solid terrain
  3. Overlay layer — Liquids, effects

Depth sorting:

  • Walls always behind terrain
  • Transparent blocks blend correctly
  • Liquids render with transparency

Advanced Features

Dynamic Luminosity

Blocks with dlfn > 0 have animated lighting:

var dynamicLuminosityFunction: Int = 2  // Torch flicker

Functions:

  • 0 — Static (no animation)
  • 1 — Slow pulse
  • 2 — Torch flicker
  • 3 — Fast flicker
  • 4-7 — Various patterns

Implementation:

The engine creates virtual tiles for each dynamic light block, pre-generating random luminosity variations. At runtime, blocks sample from these virtual tiles based on their world coordinates to create spatially-coherent animated lighting.

Structural Support

Some blocks have weight limits:

var maxSupport: Int = 100  // Can support 100 blocks above

Used for:

  • Collapsing structures
  • Realistic building constraints
  • Puzzle mechanics

Tile Connecting

Control how blocks connect to neighbours:

val isSolidForTileCnx: Boolean
    get() = if (tags.contains("DORENDER") || !isActorBlock) isSolid else false
  • Actor blocks normally don't connect
  • DORENDER tag forces connection
  • Used for trees, decorations

Block Extra Data

Store custom module data:

block.extra["mymod:special_property"] = "custom value"
block.extra["mymod:spawn_rate"] = 0.5

Block Items

Every block automatically becomes a placeable item.

Automatic registration:

When ModMgr.GameBlockLoader.invoke() runs, it:

  1. Creates BlockProp instances from CSV
  2. Calls blockRegisterHook() for each block
  3. The hook generates a GameItem via makeNewItemObj()
  4. Registers the item in ItemCodex with the same ID as the block
// Internal process (you don't write this)
fun blockRegisterHook(tile: BlockProp) {
    // makeNewItemObj() creates appropriate block item
    val item = makeNewItemObj(tile, isWall = tile.isWall)
    ItemCodex[tile.id] = item
}

You never manually create block items — defining a block in CSV automatically creates its placeable item counterpart.

Generated block item properties:

  • Same ID as block (basegame:2 for both block and item)
  • Mass calculated from block density
  • Stackable (up to 999)
  • Primary use places the block in world

Placement:

// Place terrain block
INGAME.world.setTileTerrain(x, y, blockID, true)

// Place wall block
INGAME.world.setTileWall(x, y, wallID, true)

Querying Blocks

By ID

val stone = BlockCodex["basegame:2"]
val customOre = BlockCodex["mymod:200"]

By Tags

// All ore blocks
val ores = BlockCodex.blockProps.values.filter { it.hasTag("ORE") }

// All glowing blocks
val glowing = BlockCodex.blockProps.values.filter {
    it.baseLumColR > 0 || it.baseLumColG > 0 || it.baseLumColB > 0
}

// All natural stone
val naturalStone = BlockCodex.blockProps.values.filter {
    it.hasAllTagsOf("STONE", "NATURAL")
}

By Property

// All blocks stronger than iron
val hardBlocks = BlockCodex.blockProps.values.filter { it.strength > 150 }

// All high-density blocks
val heavyBlocks = BlockCodex.blockProps.values.filter { it.density > 5000 }

// All reflective surfaces
val mirrors = BlockCodex.blockProps.values.filter { it.reflectance > 0.7 }

World Interaction

Getting Blocks

// Get terrain block at position
val terrainTile = world.getTileFromTerrain(x, y)

// Get wall block at position
val wallTile = world.getTileFromWall(x, y)

// Check if solid
val isSolid = BlockCodex[terrainTile].isSolid

Setting Blocks

// Place terrain
world.setTileTerrain(x, y, "basegame:2", true)  // true = update neighbours

// Place wall
world.setTileWall(x, y, "basegame:131", true)

// Remove block (place air)
world.setTileTerrain(x, y, Block.AIR, true)

Mining Blocks

val tile = world.getTileFromTerrain(x, y)
val prop = BlockCodex[tile]

// Calculate mining time
val toolStrength = player.toolStrength
val miningTime = prop.strength / toolStrength

// Drop item when destroyed
val dropItem = prop.drop
if (dropItem.isNotEmpty() && Math.random() < dropProbability) {
    world.spawnDroppedItem(x, y, dropItem)
}

// Remove block
world.setTileTerrain(x, y, Block.AIR, true)

Best Practises

  1. Use appropriate strength values — Match mining difficulty to tier
  2. Set correct density — Affects physics and realism
  3. Tag comprehensively — Enable flexible queries
  4. Match drops to blocks — Logical item drops
  5. Use real material IDs — Don't create fake materials
  6. Test autotiling — Verify connections work correctly
  7. Balance luminosity — Don't make everything glow
  8. Consider friction — Ice should be slippery
  9. Namespace IDs — Use modulename:id format
  10. Test in all seasons — Verify seasonal atlas variations

Common Pitfalls

  • Wrong solid/wall flags — Blocks behave incorrectly
  • Missing drop items — Blocks vanish when mined
  • Excessive luminosity — Blindingly bright blocks
  • Zero friction — Players slide uncontrollably
  • Inconsistent density — Wood heavier than stone
  • Missing tags — Can't query blocks properly
  • Wrong material — Incorrect sounds and behaviour
  • Forgetting neighbours — Autotiling breaks
  • Numeric ID conflicts — Multiple blocks with same ID
  • Not testing placement — Block items don't work

See Also