Brandling Safari: LAN safari-zone creature catching on the HD-2D engine

- Authoritative 30 Hz server (ENet, SQLite): accounts, characters, wild AI, tall grass /
  water / wandering spawns, fishing, captures, follower chains, clock + weather, LAN beacon
- Client: title / join / character creator / play screens, prediction + reconciliation,
  interpolated players and parties, HD-2D UI (dialogue with Professor Henry, HUD, collection)
- Shared deterministic zone generator, movement, protocol, roster, appearance
- Paper-doll character art pipeline (PixelLab base walk + per-item animation edits)
- Brandling roster from the company list (logo colors, jev types), Blender safari props
- Headless test bot
This commit is contained in:
Gabe
2026-10-02 15:20:27 -05:00
commit 238cd9c6c3
3409 changed files with 100493 additions and 0 deletions

4
.env.example Normal file
View File

@@ -0,0 +1,4 @@
# Only needed to (re)generate art; the game itself needs no keys.
# The tools also read ../.env (the folder that holds this repo and its siblings).
PIXELLAB_API_KEY=
OPENROUTER_API_KEY=

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
* text=auto eol=lf
*.png binary
*.jpg binary
*.glb binary
*.ttf binary

36
.gitignore vendored Normal file
View File

@@ -0,0 +1,36 @@
# Secrets (PIXELLAB_API_KEY / OPENROUTER_API_KEY)
.env
.env.*
!.env.example
# Private source data: the ClickUp export holds client contact details and logins.
# Only the derived roster (names, domains, types) is committed.
*.csv
# Build output
build/
build_*/
build-*/
cmake-build-*/
compile_commands.json
# Runtime output
screenshots/
*.db
*.db-wal
*.db-shm
# Tool caches
art_src/.pending/
art_src/creatures/cache/logos/
art_src/preview/
__pycache__/
*.py[cod]
# Editors / OS
.cache/
.vscode/
.idea/
*.swp
*~
.DS_Store

93
CMakeLists.txt Normal file
View File

@@ -0,0 +1,93 @@
cmake_minimum_required(VERSION 3.21)
project(brandling_safari LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "" FORCE)
endif()
option(BS_BUILD_CLIENT "Build the game client (needs OpenGL + the HD-2D engine)" ON)
option(BS_BUILD_SERVER "Build the dedicated server" ON)
set(HD2D_ENGINE_DIR "${CMAKE_SOURCE_DIR}/../Vibe-HD-2D" CACHE PATH "Path to the Vibe-HD-2D engine checkout")
set(HD2D_ENGINE_GIT "ssh://git@git.gabjimmy.com:30009/Gabe/Vibe-HD-2D.git" CACHE STRING "Engine repo used when HD2D_ENGINE_DIR is missing")
include(FetchContent)
# Old third-party CMakeLists (ENet) still declare cmake_minimum_required < 3.5.
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
# --- Dependencies -------------------------------------------------------------
find_package(glm QUIET)
if(NOT TARGET glm::glm)
FetchContent_Declare(glm GIT_REPOSITORY https://github.com/g-truc/glm.git GIT_TAG 1.0.1)
FetchContent_MakeAvailable(glm)
endif()
find_package(nlohmann_json 3 QUIET)
if(NOT TARGET nlohmann_json::nlohmann_json)
FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz)
FetchContent_MakeAvailable(json)
endif()
FetchContent_Declare(enet GIT_REPOSITORY https://github.com/lsalzman/enet.git GIT_TAG v1.3.18)
FetchContent_MakeAvailable(enet)
target_include_directories(enet PUBLIC ${enet_SOURCE_DIR}/include)
# --- Shared game core (no graphics) ---------------------------------------------
add_library(bs_shared STATIC
shared/Appearance.cpp shared/Net.cpp shared/Protocol.cpp shared/Roster.cpp shared/Zone.cpp)
target_include_directories(bs_shared PUBLIC shared)
target_link_libraries(bs_shared PUBLIC glm::glm nlohmann_json::nlohmann_json enet)
target_compile_definitions(bs_shared PUBLIC GLM_FORCE_RADIANS GLM_ENABLE_EXPERIMENTAL
BS_ASSET_DIR="${CMAKE_SOURCE_DIR}/assets")
if(MSVC)
target_compile_options(bs_shared PRIVATE /W4)
target_link_libraries(bs_shared PUBLIC ws2_32 winmm)
else()
target_compile_options(bs_shared PRIVATE -Wall -Wextra -Wno-unused-parameter)
endif()
# --- Server -------------------------------------------------------------------
if(BS_BUILD_SERVER)
find_package(SQLite3 REQUIRED)
find_package(Threads REQUIRED)
file(GLOB BS_SERVER_SOURCES CONFIGURE_DEPENDS server/*.cpp)
add_executable(safari_server ${BS_SERVER_SOURCES})
target_link_libraries(safari_server PRIVATE bs_shared SQLite::SQLite3 Threads::Threads)
if(NOT MSVC)
target_compile_options(safari_server PRIVATE -Wall -Wextra -Wno-unused-parameter)
endif()
endif()
# --- Headless test bot (no graphics) ------------------------------------------
add_executable(safari_bot bot/main.cpp)
target_link_libraries(safari_bot PRIVATE bs_shared)
if(NOT MSVC)
target_compile_options(safari_bot PRIVATE -Wall -Wextra -Wno-unused-parameter)
endif()
# --- Client -------------------------------------------------------------------
if(BS_BUILD_CLIENT)
if(NOT EXISTS "${HD2D_ENGINE_DIR}/CMakeLists.txt")
FetchContent_Declare(hd2d_engine GIT_REPOSITORY ${HD2D_ENGINE_GIT} GIT_TAG main)
FetchContent_GetProperties(hd2d_engine)
if(NOT hd2d_engine_POPULATED)
FetchContent_Populate(hd2d_engine)
endif()
set(HD2D_ENGINE_DIR ${hd2d_engine_SOURCE_DIR})
endif()
set(HD2D_BUILD_DEMO OFF CACHE BOOL "" FORCE)
add_subdirectory(${HD2D_ENGINE_DIR} hd2d)
file(GLOB BS_CLIENT_SOURCES CONFIGURE_DEPENDS client/*.cpp)
add_executable(safari_client ${BS_CLIENT_SOURCES})
target_link_libraries(safari_client PRIVATE bs_shared hd2d)
if(NOT MSVC)
target_compile_options(safari_client PRIVATE -Wall -Wextra -Wno-unused-parameter)
endif()
# The client launches the dedicated server for "Host game" from its own folder.
if(BS_BUILD_SERVER)
add_dependencies(safari_client safari_server)
endif()
endif()

202
README.md Normal file
View File

@@ -0,0 +1,202 @@
# 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](../Vibe-HD-2D) engine. The creatures, **Brandlings**, are named after real
companies: each one's sprite is generated by [Company_Sprite_Gen](../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](docs/catch.jpg)
| | |
| --- | --- |
| ![title](docs/title.jpg) | ![creator](docs/creator.jpg) |
| ![Professor Henry](docs/henry.jpg) | ![night rain at the dock](docs/night.jpg) |
| ![dusk with a full party](docs/dusk.jpg) | ![fishing from the dock](docs/dock.jpg) |
| ![collection](docs/collection.jpg) | |
## 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](#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.
```sh
# 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`.
## Playing
```sh
./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
```mermaid
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`)
```sh
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](docs/CHARACTER_ART.md) for how one base walk cycle animates every
outfit and skin tone with 20 generations per item.
### 3D props
```sh
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:
```sh
./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.

View File

@@ -0,0 +1,19 @@
{
"character_id": "8da7fe51-6fa3-4e15-b632-ccc8d58a0a8e",
"payload": {
"description": "young adult woman, bald shaved head, kind smiling face with big friendly eyes and eyelashes, plain white t-shirt, plain grey shorts, barefoot, light skin, pokemon trainer overworld sprite style",
"image_size": {
"width": 48,
"height": 48
},
"view": "low top-down",
"proportions": {
"type": "preset",
"name": "chibi"
},
"outline": "single color black outline",
"shading": "basic shading",
"detail": "medium detail",
"seed": 2202
}
}

View File

@@ -0,0 +1,4 @@
{
"description": "change her grey shorts into baggy bright pure green cargo pants with side pockets that reach the ankles; keep the face, body, pose and everything else exactly identical",
"seed": 93780
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

View File

@@ -0,0 +1,4 @@
{
"description": "change her grey shorts into bright pure green long straight pants that reach the ankles; keep the face, body, pose and everything else exactly identical",
"seed": 11238
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

View File

@@ -0,0 +1,4 @@
{
"description": "change her grey shorts into a bright pure green pleated knee-length skirt; keep the face, body, pose and everything else exactly identical",
"seed": 26568
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

View File

@@ -0,0 +1,4 @@
{
"description": "give her big round curly afro hair colored bright pure green; keep the face, body, pose and everything else exactly identical",
"seed": 20341
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,4 @@
{
"description": "give her long straight hair down past the shoulders, colored bright pure green; keep the face, body, pose and everything else exactly identical",
"seed": 21166
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

View File

@@ -0,0 +1,4 @@
{
"description": "give her hair tied back in a high ponytail, colored bright pure green; keep the face, body, pose and everything else exactly identical",
"seed": 40553
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

Some files were not shown because too many files have changed in this diff Show More