Skip to main content

Overview

Wizard Duel is built using C++17 with raylib for graphics and ENet for networking. The game follows a state-based architecture with a single main loop handling all game states, rendering, and network operations.

Game states

The game uses a string-based state system stored in the state variable:
Local gameplay with AI opponent. Features:
  • Full game mechanics (movement, spells, health/mana)
  • Camera following local player
  • Collision detection with trees
  • Death and respawn system
Transitional state for creating a game server:
  • Calls SetupHost() to initialize ENet server
  • Waits for ENET_EVENT_TYPE_CONNECT event
  • Transitions to MULTIPLAYER when client connects
  • Displays “Waiting for player to join” message
Transitional state for joining a hosted game:
  • Calls SetupClient() to connect to server
  • Monitors connection status via enet_host_service
  • Transitions to MULTIPLAYER on successful connection
  • Shows “Connecting to host…” status
Full networked gameplay with position synchronization:
  • Sends PositionPacket when local player moves
  • Receives and applies opponent position updates
  • Displays ping/latency information
  • Handles both client and server network events

Main game loop

The game runs at 60 FPS (set via SetTargetFPS(60)) with a single while loop:
The game loop structure places input handling before rendering, with state-specific logic contained in if-else blocks within the main loop.

Camera system

The game uses raylib’s Camera2D for world-space rendering:

Key camera features

Dynamic tracking: Camera target updates each frame to follow the local player:
Zoom controls: Players can adjust zoom with Z/X keys (range: 1.5x to 10.0x) Mana drain mechanic: Zooming out below 5.0x drains mana progressively World-to-screen conversion: Mouse input is converted to world coordinates:

Asset loading system

Assets are loaded at startup and unloaded before window close:
The pattern of loading images, converting to textures, then immediately unloading images optimizes memory by keeping only GPU textures.

Character system

The Character struct manages player state:
Players are stored in std::vector<Character> all_players with index 0 always being the local player.

Spell system

Spells are represented by the Ball struct:

Spell types

  • KEY_ONE: Blood spell (red, slow, large radius, 35.0f radius, 2.0f speed)
  • KEY_TWO: Arcane spell (blue, fast, small radius, 25.0f radius, 4.0f speed)
Spells move toward their target position using normalized direction vectors and shrink over time (ball_r -= 0.1f per frame).

Collision detection

Two main collision systems: Player-tree collision:
Spell-tree collision: Uses CheckCollisionCircleRec to detect spell impacts, reducing tree positions to on destruction.

World generation

The game world is procedurally generated:
  • Map size: 2000x2000 units
  • Trees: 115 randomly placed trees using std::mt19937 random generation
  • Boundary checking: Players take damage when leaving map bounds (x/y < 0 or > 2000)

Performance target

The game targets 60 FPS using raylib’s frame limiting:
This ensures consistent physics, movement speed (0.5 units per frame), and spell decay rates across different hardware.
All movement and spell speeds are frame-dependent, making the 60 FPS target critical for gameplay consistency.