A 1v1 wizard duel game built with C++, Raylib, and ENet.
Two wizards fight in a procedural arena. Shoot fireballs, dodge spells, use trees for cover. Simple to learn, hard to master.
The game is architecturally complete. Core gameplay, multiplayer networking, match recording, and a companion website (leaderboard, stats, chat) all work. Further polishing is optional.
- g++ (C++17)
- raylib
- enet
- openssl
- libX11, libGL
Ubuntu/Debian:
sudo apt install build-essential libraylib-dev libenet-dev libssl-dev libx11-dev libxcursor-dev libxinerama-dev libxi-dev libxrandr-devmake runOr step by step:
make
./build/game- The game will ask for an API key on first launch.
- Go to the website, create an account, copy your API key.
- Paste the key into the game, or put it in a file called
wizard_api_key.txtnext to the game binary.
| Key | Action |
|---|---|
| WASD | Move |
| 1 + Left Click | Cast Blood Red (high damage, slow) |
| 2 + Left Click | Cast Dark Blue (low damage, fast) |
| Right Click | Mana Shield (heal, costs mana) |
| Z / X | Zoom out / in |
| R | Reset zoom |
- One player clicks HOST GAME. The game listens on port 7777.
- The other player clicks JOIN GAME. The host's IP is used to connect.
- On LAN, the host's local IP (like
192.168.x.x) works directly.
The game uses peer-to-peer connections, so both players need to reach each other. The easiest way is Tailscale — a free VPN that creates a private network between your devices.
- Both players install Tailscale and log in (free account works).
- Both devices appear on a shared virtual network (e.g.
100.x.x.x). - One player clicks HOST GAME.
- The other player clicks JOIN GAME and enters the host's Tailscale IP.
No port forwarding needed. Tailscale handles NAT traversal.
If you don't want to use Tailscale:
- The host forwards port 7777 (UDP) on their router to their local IP.
- The joining player connects to the host's public IP.
- You can find your public IP at https://api.ipify.org
Click FIND PLAYERS to see who's online. You can:
- PING someone to alert them
- CHAT to coordinate a game
Both players need to be registered on the website and have entered their API keys.
Right now, multiplayer is peer-to-peer — players connect directly to each other. The website server only handles accounts, stats, and player discovery. If the game gets enough players, a proper central relay server could be added for matchmaking and always-on lobbies.
The website (leaderboard, stats, chat) is not publicly deployed. Feel free to host your own instance — the server code is in game-server/. If you have a good host and want it to become the official server, open an issue on the repo.
- Singleplayer — Fight an AI opponent
- Host Game — Host a multiplayer match
- Join Game — Connect to a host
- Find Players — See who's online, ping or chat with them
game/ Game client (C++/Raylib/ENet)
src/
main.cpp Game loop, states, rendering
GameClient.h API client (auth, players, chat, matches)
UIHelper.h Menu UI, chat window, player list
SingleplayerAI.h AI opponent logic
MatchRecorder.h Local match recording
MatchUploader.h Upload matches to server
island_generator.h Procedural island generation
game-server/
backend/ API server (Bun + Hono + SQLite)
index.ts Auth, players, chat, matches, leaderboard
frontend/ Companion website (Vite)
src/ Login, dashboard, leaderboard, docs
See LICENSE.