Files
brandling-safari/README.md

12 KiB

Brandling Safari

A small LAN-multiplayer creature-catching game set in a safari zone, rendered in the HD-2D style (Octopath Traveler / Triangle Strategy) with the Vibe-HD-2D engine. The creatures, Brandlings, are named after real companies: each one's sprite is generated by Company_Sprite_Gen from the company name, recolored with its logo's brand colors, and its type comes from the jev business classifier.

catching with a party in tow

title creator
Professor Henry night rain at the dock
dusk with a full party fishing from the dock
collection

Features

  • Meadow Safari Zone: terraced grass diorama with a north ridge and an east plateau, a lake with a fishing dock, a pond, eight tall-grass patches, an entrance gate and plaza with Professor Henry's field tent, lamps, fences and props (3D models built in Blender, pixel-art ground and foliage).
  • Character creator (persists server-side): masculine / feminine base body, 6 skin tones, 5 hair styles, 3 hats, 5 tops, 4 bottoms, 3 shoes, any color per slot. Every combination is animated by one shared walk cycle (see Character art).
  • Professor Henry greets new rangers with an intro, hands out 30 Safari Capsules and restocks them later.
  • Finding Brandlings: they burst out of tall grass around you as you walk (more often when running), swim in the lake and surface with splashes, wander the meadow, and bite on a fishing line cast from the shore or dock.
  • Catching: throw a Safari Capsule (closer and calmer = better odds; hooked ones are dazed); misses can make them flee. Caught Brandlings join your party (up to 6) and follow behind you in a chain, bouncing and turning to face their direction; the rest go to your box.
  • Multiplayer: dedicated authoritative server, LAN discovery, client-side prediction with server reconciliation, interpolated remote players and their parties, chat bubbles.
  • World state from the server: time of day (dawn, day, dusk, night with stars, moon and lantern light) and weather (clear, cloudy, rain, thunderstorm, mist) change the look on every client and which Brandlings appear.

Building

Requirements: CMake >= 3.21, a C++20 compiler, OpenGL 4.5, libepoxy, SQLite 3, and the engine checked out next to this repo (../Vibe-HD-2D; set HD2D_ENGINE_DIR otherwise, or leave it missing to fetch it from HD2D_ENGINE_GIT). GLFW, GLM, stb, cgltf and Dear ImGui come from the engine; nlohmann/json is used from the system or fetched, ENet is always fetched.

# Arch / CachyOS
sudo pacman -S cmake ninja glfw glm libepoxy stb sqlite nlohmann-json

cmake -S . -B build -G Ninja
ninja -C build                    # safari_client, safari_server, safari_bot

Server only (no GPU needed): cmake -S . -B build_srv -G Ninja -DBS_BUILD_CLIENT=OFF.

Built files load assets from the source checkout (BS_ASSET_DIR and the engine's asset path are baked in at configure time), so run them where they were built.

Install MSYS2 and open the MSYS2 UCRT64 shell:

pacman -S --needed git mingw-w64-ucrt-x86_64-{gcc,cmake,ninja,pkgconf,glfw,glm,libepoxy,sqlite3,nlohmann-json}

git clone ssh://git@git.gabjimmy.com:30009/Gabe/Vibe-HD-2D.git
git clone <this repo> Brandling-Safari          # side by side with Vibe-HD-2D
cd Brandling-Safari
cmake -S . -B build -G Ninja
ninja -C build                                  # safari_client.exe, safari_server.exe, safari_bot.exe
./build/safari_client.exe

The executables find glfw3.dll, libepoxy-0.dll, libsqlite3-0.dll and libwinpthread-1.dll on the UCRT64 shell's PATH. To start the game from Explorer or a shortcut, copy them next to the executables once:

ldd build/safari_client.exe build/safari_server.exe | grep -i /ucrt64/ | awk '{print $3}' | sort -u | xargs -I{} cp {} build/

Windows (Visual Studio + vcpkg)

In an x64 Native Tools Command Prompt for VS 2022:

git clone https://github.com/microsoft/vcpkg C:\vcpkg && C:\vcpkg\bootstrap-vcpkg.bat
C:\vcpkg\vcpkg install glfw3 glm libepoxy sqlite3 nlohmann-json --triplet x64-windows

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo ^
      -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build
build\safari_client.exe

vcpkg copies the DLLs next to the executables. The MinGW build is verified (zero warnings); the MSVC build has not been compiled yet, so expect possible small fixes there.

Windows notes: the client has no console window. Your identity and a hosted game's world.db and server.log live in %APPDATA%\brandling-safari. The first time a server runs, allow it through Windows Defender Firewall on private networks so LAN players can join (UDP 27420 for the game, 27421 for discovery). The art tools in tools/ work on Windows with Python 3 (pip install requests pillow numpy); only Company_Sprite_Gen's part-library generator (pixellab_library.py) is Linux/macOS-only.

Playing

./build/safari_client             # title screen: Host & Play, or Join a LAN Game
  • Host & Play starts safari_server next to the client on port 27420 and joins it. Other players on the network see it under Join a LAN Game (UDP broadcast on 27421) or can type the host's address.
  • A dedicated server: ./build/safari_server [--port 27420] [--name "My Safari"].
Input Action
WASD / arrows Move (camera-relative); hold Shift to run
Space Throw a Safari Capsule at the targeted Brandling
F Cast a line toward the water you face / reel in when it bites
E Talk to Professor Henry (advances dialogue too)
Tab Your Brandlings: Enter moves one between party and box
Enter / T Chat
Q / E, right-drag, wheel Camera
Esc Menu
F1 / F12 Render tuning panels / screenshot

Your identity is a random token in ~/.local/share/brandling-safari/client.json; the server keys your ranger, Brandlings and dex to it. --profile NAME uses a separate identity (several clients on one machine).

Server

safari_server [--port N] [--name NAME] [--db PATH] [--seed N] [--zone meadow]
              [--roster PATH] [--manifest PATH] [--day-minutes M] [--hour H]
              [--weather clear|cloudy|rain|storm|fog]

Everything is saved in SQLite (~/.local/share/brandling-safari/world.db by default): players (name, look, position, capsules, intro), caught Brandlings, the per-player dex, the world clock and weather. Console commands: help, players, time <h>, weather <name>, give <player> capsules <n>, spawn <species>, save, quit.

safari_bot is a headless test client that walks into tall grass, throws capsules, fishes and talks to Henry, printing everything it receives.

Architecture

flowchart LR
  subgraph Server [safari_server, 30 Hz, authoritative]
    G[Game: players, wild AI, spawns,<br/>fishing, captures, followers,<br/>clock + weather]
    DB[(SQLite)]
    G <--> DB
  end
  subgraph Client [safari_client]
    N[NetClient] --> W[World: prediction,<br/>interpolation, effects]
    W --> R[HD-2D Renderer]
    A[App: screens + UI] --> N
  end
  N <-- "ENet UDP: inputs, actions /<br/>snapshots, events" --> G
  S[shared/: Protocol, Zone,<br/>Movement, Roster, Appearance] --- G
  S --- W
  • shared/ is compiled into both programs. Zone generates the whole zone deterministically from (zone id, seed), so the server (collision, spawn points, water, tall grass) and every client (terrain, props, decoration) agree on the map and only the id + seed go over the wire. Movement is the single player-movement function: the server applies it once per input command, the client predicts with it and replays unacknowledged inputs when a snapshot arrives.
  • Clients send only intents: movement inputs (unreliable, last 4 resent) and actions (throw, cast, reel, talk, party, chat). The server validates everything (ranges, capsules, speed budget, walkability) and broadcasts snapshots (unreliable, 30 Hz) and events (reliable: emerge, splash, throw, capture result, bite, hook, catch...).
  • Protocol: shared/Protocol.h. Bump kProtocolVersion on any wire change.
Path What
shared/ protocol, ENet wrapper + LAN discovery, zone generator, movement, roster + appearance
server/ dedicated server: game simulation, spawns, creatures, SQLite persistence
client/ App (screens, HUD, input), World (scene, entities, effects), CharacterSprite (paper-doll compositor), NetClient
bot/ headless test client
assets/ characters, creatures (roster + sprites), models (safari.glb), dialogue
tools/ art + data pipelines (below)
art_src/ raw PixelLab output, caches and reports the tools build from

Content pipelines

The game ships the generated assets; the tools are only needed to change them. API keys go in .env here or in the parent folder (see .env.example).

Brandlings (tools/build_roster.py)

python3 tools/build_roster.py --csv ../clickup_web_design_export.csv [--limit N] [--jobs 16]

Reads only the company name, domain, staging-site URL (logo search only, never stored), service list and logo-note colour words from the ClickUp export (the export itself holds private client data and is never committed). For each company: logo fetched from its domain, then its staging site (Company_Sprite_Gen/logo_fetch.py; a logo shared by 2+ companies is a template and is dropped), type from the jev classifier, sprite from Company_Sprite_Gen coloured from the logo (else the logo note's colour words, else a per-creature shade of the type colour), plus deterministic rarity, catch/flee rates and habitat. Writes assets/creatures/roster.json, assets/creatures/sprites/, a review contact_sheet.png and art_src/creatures/report.json (counts, logo hit rate by method, type histogram). Logos, classifier answers and renders are cached in art_src/creatures/cache/, so reruns only do new work. Later this will read from the ClickUp API instead of the CSV.

Species ids are permanent, because saved creatures store them and the loader requires id == index. The first run numbers species by slug; later runs keep every slug's id, append new companies after the last id, and keep companies that left the CSV as "retired": true entries (sprite kept) so no id is reused. A --limit N test run leaves unsampled species as they were. Retired Brandlings stay in players' collections but no longer spawn.

Character art

tools/character_art.py builds the player layer library and Professor Henry with PixelLab; see docs/CHARACTER_ART.md for how one base walk cycle animates every outfit and skin tone with 20 generations per item.

3D props

python3 tools/safari_textures.py           # painted textures -> assets/models/src/
blender -b -P tools/blender_safari.py      # -> assets/models/safari.glb

Gate, tent, fences, lamps, signposts, dock, crates, barrels, benches and hay bales, following the engine's glTF conventions (see the engine README). Trees, bushes and rocks come from the engine's scenery.glb.

Verification

Captures render in a hidden window into an offscreen target, so nothing appears on screen and keyboard input cannot disturb them:

./build/safari_server --port 27500 --db /tmp/test.db &
./build/safari_client --screenshot /tmp/play.png --frames 600 --connect 127.0.0.1:27500 \
    --auto-create --auto-dialogue --profile test --move 0.8,0.6 --run --key 450:space

--look body,skin,hair,hat,top,bottom,shoes[,colors] sets the look, --screen creator captures the creator, --hour / --weather set the title backdrop, --key frame:key presses space / e / f / tab / enter on a frame, --show makes the window visible.