Loading
Personal Project
Eurekiel: Libra

Eurekiel: Libra is a 2D top-down tank combat game built on a custom C++ engine (Eurekiel). Players pilot a tank through procedurally generated maps, switching between projectile and flamethrower weapons to engage four distinct enemy types, each with unique physics interactions, movement rules, and combat behaviors. The entire game is data-driven: enemy attributes, tile properties, map layouts, and spawn rules are all defined in XML, enabling rapid iteration without recompilation.

Game Trailer

0:00
/0:00
Eurekiel: Libra, full gameplay trailer showcasing combat, enemy variety, and procedural maps

Player Tank Mechanics

The player tank features independent body and turret rotation. The tank body follows WASD directional input while the turret tracks toward the mouse cursor. This decoupled control scheme allows strafing and retreating while maintaining aim on a target, which is essential for kiting enemies with different movement speeds.

Tank body and turret rotate independently: body follows movement input, turret tracks the mouse

The tank carries two weapon systems: a rapid-fire projectile cannon and a short-range flamethrower. The projectile uses fast voxel 2D raycasting for hit detection, while the flamethrower applies continuous area damage with particle effects. Players switch between weapons depending on enemy range and type.

Dual weapon system: projectile cannon for ranged engagements, flamethrower for close-quarters combat

Fast Voxel 2D Raycast

Both the player’s bullets and enemy laser attacks rely on a fast voxel traversal algorithm adapted for the 2D tile grid. The raycast steps through tiles at a configurable resolution (RAYCAST_STEPS_PER_UNIT = 50), checking for solid tile collisions and entity intersections along the way. This same system powers line-of-sight checks for AI targeting decisions.

Fast voxel 2D raycast applied to both player bullets and enemy laser beams, with tile-accurate collision detection

Enemy Design & Physics Interactions

Each enemy type is defined entirely through GameConfig.xml. Drive speed, turn rate, shoot cooldown, health, and physics interaction rules are all designer-tunable without code changes. Every enemy has its own corrective physics state that determines whether it can be pushed or pulled by the player or by other enemy types, creating emergent tactical situations.

graph LR
    subgraph Enemies
        Leo["Leo - Infantry"]
        Aries["Aries - Shield"]
        Scorpio["Scorpio - Heavy"]
        Capricorn["Capricorn - Aquatic"]
    end

    subgraph Properties
        Push["Pushable by Player"]
        Pull["Pullable by Player"]
        Water["Can Cross Water"]
        Special["Special Ability"]
    end

    Leo -->|Yes| Push
    Leo -->|Yes| Pull
    Leo -.->|No| Water
    Leo -->|Standard AI| Special

    Aries -.->|No| Push
    Aries -.->|No| Pull
    Aries -.->|No| Water
    Aries -->|Reflective Shield| Special

    Scorpio -.->|No| Push
    Scorpio -.->|No| Pull
    Scorpio -.->|No| Water
    Scorpio -->|Immovable Body| Special

    Capricorn -->|Yes| Push
    Capricorn -->|Yes| Pull
    Capricorn -->|Yes| Water
    Capricorn -->|Tracking Missiles| Special

Leo: Pushable Infantry

Leo is the basic ground enemy. It patrols using distance-field navigation, chases the player on sight, and fires standard projectiles. Leo can be both pushed and pulled by the player’s tank, making it possible to shove Leos into hazards or use them as mobile cover against other enemies.

Leo can be pushed and pulled by the player, use physics interactions tactically

Aries: Reflective Shield Bearer

Aries carries a frontal energy shield that reflects incoming projectiles back at the player. Direct fire is ineffective. Players must flank Aries or use the flamethrower to bypass the shield. Aries cannot be pushed or pulled, forcing the player to reposition rather than brute-force the encounter.

Aries reflects projectiles with its frontal shield: flanking or flamethrower required

Scorpio: Immovable Heavy Tank

Scorpio is the heaviest enemy type. It cannot be pushed or pulled by any force, player or other enemies. Scorpio acts as a static obstacle that blocks pathways and forces the player to engage on Scorpio’s terms. Its high health pool and steady fire rate make it a priority target.

Scorpio cannot be pushed by the player, an immovable obstacle that demands direct engagement

Capricorn: Aquatic Missile Launcher

Capricorn is the only enemy that can traverse water tiles, giving it access to areas other enemies cannot reach. It fires tracking missiles that home in on the player’s position. Capricorn can be pushed and pulled, but its water-crossing ability means it often attacks from unexpected angles.

Capricorn crosses water tiles and fires tracking missiles that home in on the player

Procedural Map Generation

Maps are procedurally generated from XML-defined MapDefinition templates that specify dimensions, tile type distributions, worm counts, and worm lengths. The generator uses a worm algorithm to carve traversable paths through the tile grid, then runs a flood-fill reachability check to guarantee the player can always reach the exit. If the generated layout fails the reachability test, the generator retries (up to MAX_ATTEMPTS = 100) until a valid map is produced.

Each map theme (grass, tunnel, and water) uses different tile palettes and generation parameters, producing visually and mechanically distinct environments while sharing the same generation pipeline.

PCG Map: Grass Theme
Grass theme: open terrain with scattered obstacles, reachability from start to exit guaranteed
PCG Map: Tunnel Theme
Tunnel theme: narrow corridors force close-quarters combat, exit reachability verified
PCG Map: Water Theme
Water theme: water tiles restrict ground enemies but Capricorn can cross freely, goal still reachable

AI Navigation & Heat Map Debug

Enemy pathfinding is driven by distance-field heat maps computed over the tile grid. Each tile stores a heat value representing its distance to the target (player position). Enemies follow the gradient toward lower heat values, producing smooth, obstacle-aware navigation without explicit pathfinding graphs. The heat map is only recomputed when the player moves to a new tile, avoiding unnecessary recalculation.

The game ships with four heat map debug visualization modes, togglable at runtime, that overlay the distance field values directly on the tile grid. These modes visualize different aspects of the AI navigation data: raw distance values, directional flow, solid/non-solid classification, and entity-aware cost adjustments, making it straightforward to diagnose pathfinding issues during development.

Debug Mode: AI State and Collision
Debug draw mode showing AI state labels, collision bounds, and entity physics interactions
0:00
/0:00
Four heat map debug visualization modes: cycling through distance values, flow direction, solidity, and cost overlays

Data-Driven Architecture

The entire game is configured through XML files under Run/Data/. No gameplay-relevant constant is hardcoded. Everything from player speed to enemy spawn counts to tile properties is externalized into data definitions.

graph TD
    subgraph DataFiles["Run / Data"]
        GC["GameConfig.xml"]
        TD["TileDefinitions.xml"]
        MD["MapDefinitions.xml"]
        SD["SpriteSheetDefinitions.xml"]
    end

    subgraph Runtime
        TDef["TileDefinition"]
        MDef["MapDefinition"]
        Map["Map - Tile Grid"]
        Entity["Entity - Player and Enemy"]
    end

    GC --> Entity
    TD --> TDef
    MD --> MDef
    TDef --> Map
    MDef --> Map
    SD --> Entity
    Map --> Entity

GameConfig.xml controls all entity attributes. A designer can adjust leoDriveSpeed, scorpioShootCooldownSeconds, or capricornMissileSpeed and see the change on next launch. TileDefinitions.xml maps tile type names to solidity flags, sprite sheet references, and rendering properties. MapDefinitions.xml defines each map’s generation parameters: grid size, worm algorithm settings, tile type probability weights, and theme-specific overrides.

This separation means the game’s content (maps, enemies, tiles, animations) can be iterated entirely through data files, while the C++ engine code handles only systems and logic.


Design Philosophy

Data Over Code — Every tunable parameter lives in XML. Enemy stats, map generation rules, tile properties, and sprite definitions are all externalized. This enforces a clean boundary between engine systems (C++) and game content (data), enabling rapid iteration and reducing recompilation cycles to zero for balance changes.

Corrective Physics Per Entity — Rather than a one-size-fits-all physics model, each enemy type declares its own push/pull interaction rules. This creates a physics system where tactical depth emerges from entity composition — Leos can be shoved into Scorpios (who don’t budge), Capricorns can be pushed off water tiles, and Aries simply ignores all forces. The physics interactions become a core gameplay mechanic, not just collision resolution.

PCG with Guarantees — Procedural generation is only useful if it produces playable results. The worm-based map generator always validates reachability before accepting a layout, ensuring the player can never be softlocked. This “generate-then-validate” loop trades a small amount of generation time for absolute reliability.

Debug as a First-Class Feature — Four heat map visualization modes, entity state labels, collision bound rendering, and AI goal markers are all built into the game from the start — not bolted on after bugs appear. The debug draw system uses a clip mode that can isolate specific layers, making it practical to diagnose issues in complex multi-enemy scenarios.

Distance Fields Over Pathfinding Graphs — Instead of A* or navmesh, the AI uses distance-field heat maps computed over the tile grid. This approach naturally handles dynamic obstacles (breakable tiles, other entities), produces smooth movement without waypoint snapping, and scales linearly with map size. The heat map doubles as a debug visualization tool, closing the loop between runtime behavior and developer understanding.