1
World
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.

World

The GameWorld is the container for all environmental data in Terrarum, including terrain, fluids, lighting, weather, and time. Each GameWorld represents a single playable dimension or planet.

Overview

A GameWorld consists of:

  • Block Layers — Terrain, walls, ores, and fluids
  • World Time — Day/night cycle and calendar
  • Environmental Properties — Gravity, temperature, lighting
  • Metadata — Dimensions, spawn points, creation time
  • Weather System — Wind, precipitation, and atmospheric effects
  • Wirings — Electrical/conduit connections between blocks

Creating a World

World Construction

To create a new world programmatically:

val world = GameWorld(
    width = 8192,           // Width in tiles
    height = 2048,          // Height in tiles
    creationTIME_T = App.getTIME_T(),
    lastPlayTIME_T = App.getTIME_T()
)

World Dimensions

World dimensions are measured in tiles. Common world sizes:

  • Small: 4096 × 1024
  • Medium: 8192 × 2048
  • Large: 16384 × 4096
  • Massive: 32768 × 8192

The engine supports millions of tiles through its chunked loading system.

Important: Dimensions must be positive integers. Worlds cannot be resized after creation.

World Indices

Each world has a unique UUID:

val worldIndex: UUID  // Unique identifier for this world

This index distinguishes worlds in multi-world save games.

Block Layers

GameWorld uses multiple specialised layers to store different types of blocks:

Layer Types

layerTerrain

Foreground blocks that provide collision and form the main playable surface:

val layerTerrain: BlockLayerGenericI16
  • Stores 16-bit block IDs
  • Provides collision for actors
  • Rendered in front of walls

layerWall

Background wall blocks:

val layerWall: BlockLayerGenericI16
  • Stores 16-bit block IDs
  • Typically non-solid
  • Rendered behind terrain
  • Affects lighting propagation

layerOres

Ore deposits overlaid on terrain blocks:

val layerOres: BlockLayerOresI16I8
  • Stores 16-bit ore type + 8-bit ore coverage
  • Rendered as overlay on terrain blocks
  • Shares damage values with terrain

layerFluids

Fluid simulation data:

val layerFluids: BlockLayerFluidI16F16
  • Stores 16-bit fluid type + 16-bit fluid amount
  • Uses cellular automata for flow simulation
  • Fluids have viscosity, colour, and density

Accessing Blocks

Block layers use tile coordinates (integers):

// Get a terrain block
val blockID: ItemID = world.getTileFromTerrain(x, y)

// Set a terrain block
world.setTileTerrain(x, y, blockID, false)

// Get a wall block
val wallID: ItemID = world.getTileFromWall(x, y)

// Set a wall block
world.setTileWall(x, y, wallID, false)

Coordinates:

  • x — Horizontal tile position (0 to width-1)
  • y — Vertical tile position (0 to height-1)
  • Origin — Top-left corner is (0, 0)

Block Damage

Blocks can be partially damaged:

val terrainDamages: HashArray<Float>  // 0.0 = undamaged, 1.0 = destroyed
val wallDamages: HashArray<Float>

Damage is indexed by block address (computed from x, y coordinates).

Chunk System

Worlds are divided into chunks for efficient storage and generation:

Chunk Dimensions

val CHUNK_W = 128  // Chunk width in tiles
val CHUNK_H = 128  // Chunk height in tiles

Chunks enable:

  1. Lazy Generation — Generate terrain on-demand as players explore
  2. Memory Efficiency — Only load visible/nearby chunks
  3. Save Optimisation — Only save modified chunks

Chunk Flags

Each chunk has a byte of flags:

val chunkFlags: Array<ByteArray>

Chunk flags track:

  • Generation status
  • Modification state
  • Active/inactive state

The world generates partially during creation, then generates additional chunks as needed during gameplay, enabling fast world creation even for massive world sizes.

Tile Number Mapping

GameWorld maintains bidirectional mappings between block names (ItemIDs) and internal numbers:

// Name to number (for setting blocks)
val tileNameToNumberMap: HashMap<ItemID, Int>

// Number to name (for getting blocks)
val tileNumberToNameMap: HashArray<ItemID>

Special Tile Numbers

  • 0Block.AIR (empty space)
  • 1Block.UPDATE (triggers block update)
  • 65535Block.NOT_GENERATED (chunk not yet generated)

This mapping system allows worlds to persist blocks even when mods are added or removed, using the dynamicToStaticTable for remapping.

Spawn Points

Player Spawn

The tilewise coordinates where players initially appear:

var spawnX: Int
var spawnY: Int

// Convenience property
var spawnPoint: Point2i

Portal Point

Optional alternative spawn point (e.g., for teleportation):

var portalPoint: Point2i?

World Time

GameWorld includes a sophisticated time system:

val worldTime: WorldTime

Time Units

WorldTime uses seconds as the base unit:

const val SECOND_SEC = 1L
const val MINUTE_SEC = 60L
const val HOUR_SEC = 3600L
const val DAY_SEC = 86400L

Calendar System

The world uses a 360-day calendar:

  • 12 months of 30 days each
  • Days are 24 hours
  • Year 125 (EPOCH) is the default starting year

Time Properties

worldTime.timeDelta    // Seconds elapsed since last update
worldTime.TIME_T       // Total seconds since epoch

Celestial Calculations

WorldTime calculates sun/moon positions and phases:

worldTime.solarNoonTime     // Time of solar noon today
worldTime.lunarPhase        // Current moon phase (0.0-1.0)
worldTime.eclipticLongitude // Sun's position on ecliptic

The time system simulates realistic seasons, equinoxes, and solstices.

Day/Night Cycle

The day/night cycle affects:

  1. Global lighting (world.globalLight)
  2. Actor behaviour (day/night AI changes)
  3. Block properties (dynamic light sources)

Environmental Properties

Gravity

Gravitational acceleration vector:

var gravitation: Vector2 = DEFAULT_GRAVITATION

Default gravity is approximately 9.8 m/s² downward. Currently, only downward gravity is supported.

Global Light

The baseline lighting level for the world:

var globalLight: Cvec  // RGB+UV colour vector

Global light represents:

  • Sunlight during day
  • Moonlight/starlight at night
  • Ambient light in caves (typically near-zero)

Global light is additive with block luminosity when calculating the final lightmap.

Average Temperature

The world's baseline temperature in Kelvin:

var averageTemperature: Float = 288f  // 15°C

This affects environmental generation and could be used for climate simulation.

Weather System

GameWorld includes a weather simulation:

var weatherbox: Weatherbox

Weatherbox

The weatherbox manages:

  • Current weather type (clear, rain, snow, fog, etc.)
  • Wind direction and speed (with temporal smoothing)
  • Precipitation intensity
  • Atmospheric effects

Weather types are defined in the WeatherCodex.

Wind

Wind is simulated with temporal coherence using control points:

weatherbox.windDir     // Wind direction (0.0-1.0 representing angles)
weatherbox.windSpeed   // Wind speed (m/s)

Both properties use a point-interpolation system (pM3, pM2, pM1, p0, p1, p2, p3) for smooth transitions.

Wiring and Conduits

GameWorld supports wire/conduit networks for connecting devices:

val wirings: HashedWirings

Wire Types

Multiple wire types can occupy the same tile position:

  • Electrical wires
  • Fluid pipes
  • Logic gates
  • Data cables

Each wire type is stored separately in the wirings hash map.

Wire Connections

Wires connect in four directions (up, down, left, right) using bit flags:

// Connection bit positions
WIRE_POS_MAP = [1, 2, 4, 8]  // Up, Right, Down, Left

Dynamic Items

Worlds can have dynamic item inventories that persist:

internal val dynamicItemInventory: ItemTable

This stores items that are unique to this world (e.g., procedurally generated equipment).

Game Rules

Arbitrary world-specific configuration:

val gameRules: KVHashMap

Game rules can store custom world settings such as:

  • Difficulty modifiers
  • Gameplay options
  • World-specific flags

World Lifecycle

Creation Time

internal var creationTime: Long  // Unix timestamp

Records when the world was first created.

Play Time

internal var lastPlayTime: Long   // Last time the world was played
internal var totalPlayTime: Long  // Cumulative play time in seconds

These track how long the world has been played.

Disposal

When switching worlds or exiting, dispose of the world properly:

world.dispose()

This frees:

  • Block layer memory
  • Chunk data
  • Cached resources

Warning: Once disposed, a world cannot be used again. Set world.disposed flag to prevent accidental access.

Advanced Topics

SimpleGameWorld

For small, fixed-size worlds (e.g., mini-games), use SimpleGameWorld:

class SimpleGameWorld(width: Int, height: Int) : GameWorld(width, height)

SimpleGameWorld stores all data in a single file rather than using chunked storage.

Random Seeds

Worlds store 128 random seeds for deterministic generation:

val randSeeds: LongArray  // 256 longs = 128 128-bit seeds

Specific ranges are reserved:

  • Seeds 0-1: Roguelike randomiser
  • Seeds 2-3: Weather mixer
  • Seeds 4+: Available for custom use

Generator Seed

The primary seed used for world generation:

var generatorSeed: Long

This seed determines the initial terrain, caves, ores, and structures.

Best Practises

  1. Always check bounds before accessing blocks: x in 0 until world.width
  2. Use spawn points rather than hardcoding coordinates
  3. Dispose worlds when switching or exiting to prevent memory leaks
  4. Update world time every frame using worldTime.timeDelta
  5. Respect chunk boundaries when implementing generation algorithms
  6. Use tile number mapping for mod compatibility
  7. Set appropriate global light for the world's environment

Common Patterns

Iterating Over Visible Tiles

val camera: WorldCamera = ...
for (y in camera.yTileStart until camera.yTileEnd) {
    for (x in camera.xTileStart until camera.xTileEnd) {
        val block = world.getTileFromTerrain(x, y)
        // Process block
    }
}

Finding Safe Spawn Point

fun findSafeSpawnY(world: GameWorld, x: Int): Int {
    for (y in 0 until world.height) {
        val terrain = world.getTileFromTerrain(x, y)
        val above = world.getTileFromTerrain(x, y - 1)
        if (BlockCodex[terrain].isSolid && !BlockCodex[above].isSolid) {
            return y - 1  // One tile above solid ground
        }
    }
    return world.spawnY  // Fallback
}

Checking Block Properties

fun isBlockSolid(world: GameWorld, x: Int, y: Int): Boolean {
    val blockID = world.getTileFromTerrain(x, y)
    return BlockCodex[blockID]?.isSolid ?: false
}

See Also