Skip to content

Architecture

ComponentTechnology
LanguageRust
Game engineBevy 0.18 (ECS)
PhysicsAvian2D
Serializationserde + serde_json, RON
Noisenoise crate (Perlin FBM)
CLIclap (derive)

Systems run in a defined order via GameSet:

Input → Commands → Movement → Fov → Lighting → Barriers → Render
SetResponsibility
InputKeyboard, mouse, controller input
CommandsTCP server commands, overseer actions
MovementPlayer/unit movement, stairs, terrain sync
FovShadowcasting field of view computation
LightingLight source propagation
BarriersElevation barrier updates
RenderTile sprite rendering

Dependencies: Movement runs after Input. Fov runs after Movement. Lighting and Barriers run after Fov. Render runs last.

WorldMap
└─ HashMap<i32, Layer> z-level → Layer
└─ HashMap<IVec2, Chunk> chunk pos → Chunk (32×32)
├─ Vec<BlockVolume> dense block storage
├─ HashMap<(u8,u8), FloorCoverId> sparse floor covers
└─ HashMap<(u8,u8), CeilingCoverId> sparse ceiling covers
ConstantValue
CHUNK_SIZE32×32 tiles
TILE_SIZE_PX32.0 pixels
Z range-100 to +100
  • X = East (+) / West (-)
  • Y = North (+) / South (-)
  • Z = Vertical levels (up +)
  • Player at z=N stands on a solid block at z=N-1

BlockType is a u16 newtype (not an enum) for extensibility — supports up to 65,535 block types.

BlockRegistry uses a flat Vec<Option<BlockProps>> indexed by the u16 ID for O(1) lookup. Block properties include:

  • blocks_movement — whether the block blocks horizontal movement
  • supports_standing — whether units can stand on it
  • vision_transmittance — FOV light pass-through (0.0–1.0)
  • light_transmittance — lighting pass-through (0.0–1.0)
  • placeable, mineable — interaction rules
  • Sprite atlas coordinates and color fallback

Block and item definitions live in RON files, loaded at startup:

  • assets/blocks.ron — block types, properties, sprite mappings
  • assets/items.ron — item definitions (id, name, group, stack size)

Atlases: Natural.png (20×22 grid), Civilized.png (24×18 grid), People.png.

No recompilation needed to add or modify blocks and items.

A DirtyFlags resource tracks what needs recomputation:

  • render_dirty — tile sprites need refresh
  • lighting_dirty — light values changed
  • fov_dirty — visibility needs recalculation
  • barriers_dirty — elevation barriers changed
  • chunks_generated — new chunks were created

Systems check dirty flags and skip work when nothing changed.

GameCommand is a serde-tagged enum routed through a CommandQueue. Commands come from:

  • TCP server (kinswardctl)
  • Overseer mode UI
  • Internal game systems

The command executor processes the queue each frame in the Commands system set.

A global TaskQueue holds Build, Demolish, and Pickup tasks. Tasks are created by overseer commands or zone operations. Idle Colony units automatically pick the nearest pending task.

InputMode tracks the current game mode:

  • Adventure — direct player control
  • Overseer — RTS-style unit management

Mode switching triggers camera transitions and input handler swaps.

  • UiTheme — centralized dark-theme configuration (50+ color/size constants) applied across all panels
  • InputRegistry — replaces scattered input handling with 21 registered bindings across 5 contexts (Global, Adventure, Overseer, InventoryOpen, MapView); enables context-aware control hints
  • UiAnimator — panel transition animations (Fade, SlideUp, SlideUpHigh, SlideDown, SlideLeft, SlideRight) for floating panels
  • Scrollbar — vendored bevy_scrollbar 0.6.0 for mouse-wheel scrolling with visual thumb. Pattern: scrollable node + Scrollbar sibling in Row wrapper
  • Hover effectsCardBaseColor + Hovered component (CSS-like, includes descendants) for card hover feedback. ButtonBaseColor + Button for toolbar buttons. Both use Hovered from bevy_picking
  • UiHovered — centralized resource (src/ui/picking.rs) checking hover from HoverMap filtered by Node. Replaces all PickingInteraction queries
  • Observer clicks — all UI clicks use On<Pointer<Click>> observers with event.propagate(false). Legacy Interaction polling fully removed
  • UI picking — panels use direct PositionType::Absolute positioning (no fullscreen wrapper overlays). Sprite/mesh picking disabled via require_markers: true. Non-interactive child nodes use Pickable::IGNORE
  • UiSlotManager — centralized panel management system

Maps (x, y) columns to walkable z-levels. Used by pathfinding and grounding to determine where units can stand. Rebuilt when blocks change.

Tile-based rendering: one Bevy entity per visible tile. Sprites come from 10px-cell texture atlases scaled 3.2× to fill the 32px tile size.

Final tile color formula:

color = base_color × depth_factor × light_value × visibility
ModuleResponsibility
src/world/WorldMap, blocks, chunks, layers, surfaces, grounding
src/commands/GameCommand, executor, TCP server, region-map export
src/render/Viewport, sprites, FPS display
src/fov/Shadowcasting FOV, visibility maps
src/lighting/Light propagation
src/generation/Procedural generation, climate pipeline (16 modules)
src/units/Unit components (Race, Faction, UnitTraits), spawner, possession, movement
src/names/Name generation: NameGenerator, UnitName, WorldName, syllable tables
src/overseer/RTS mode: camera, selection, commands, pathfinding, tasks
src/companions/Companion behaviors
src/ui/UI system — UiTheme, InputRegistry, UiAnimator, floating panels
src/schedule.rsGameSet definition and ordering
BinaryPurpose
kinswardMain game
kinswardctlCLI debug tool
worldgenHeadless world generation