Table of Contents
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 light1-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— AirDIRT— Dirt, sandROCK— Stone, oresWOOD— Wooden blocksWATR— 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, claySTONE— Rock, oresWOOD— Wooden planksMETAL— Metal blocks
Placement:
NATURAL— Generated naturallyARTIFICIAL— Player-createdINCONSEQUENTIAL— Doesn't affect world generation
Behaviour:
TREE— Tree blocks (special handling)ORE— Ore blocksFLUID— Liquid blocksNORANDTILE— 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 minedspawn— Item ID used to place blockname— Translation keyshdr/shdg/shdb/shduv— Shade colour (RGBA)str— Strength/HPdsty— 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 coefficientlumr/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:crystalitem when mined
Loading Custom Blocks
In your module's EntryPoint:
override fun invoke() {
// Load blocks
ModMgr.GameBlockLoader.invoke("mymod")
}
The loader automatically:
- Reads
blocks/blocks.csv - Parses CSV records
- Creates
BlockPropinstances from CSV data - Registers blocks in
BlockCodex - Creates corresponding block items via
makeNewItemObj()and registers them inItemCodex
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:
- Wall layer — Background walls
- Terrain layer — Solid terrain
- 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 pulse2— Torch flicker3— Fast flicker4-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
DORENDERtag 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:
- Creates
BlockPropinstances from CSV - Calls
blockRegisterHook()for each block - The hook generates a
GameItemviamakeNewItemObj() - Registers the item in
ItemCodexwith 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:2for 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
- Use appropriate strength values — Match mining difficulty to tier
- Set correct density — Affects physics and realism
- Tag comprehensively — Enable flexible queries
- Match drops to blocks — Logical item drops
- Use real material IDs — Don't create fake materials
- Test autotiling — Verify connections work correctly
- Balance luminosity — Don't make everything glow
- Consider friction — Ice should be slippery
- Namespace IDs — Use
modulename:idformat - 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
- Modules-Codex-Systems#BlockCodex — BlockCodex reference
- Modules-Setup — Creating modules with blocks
- Items — Block items and placement
- World — World generation with blocks
- Tile Atlas System — Block rendering and atlases
- Autotiling in Depth — Autotiling implementation