Repository navigation
Engine Overview
Layout as of b77+ (source tree reorganized; legacy WDL dropped):
Angels95/
Source/
Main.cpp # Client entrypoint, game loop, rendering
Core.hpp # Engine init, splash, menu, world loading, render loop
Data.hpp # Game globals, models, sounds, flags
Settings.hpp # Runtime settings: fog, particles, debug, resolution
ClientSettings.hpp # Runtime client settings (System/Angels95.ini)
DebugFlags.hpp # Debug toggles
WindowsCompat.hpp # Win32/raylib name collision fixes (CloseWindow, etc.)
Log.cpp/.hpp # Logging system
IniConfig.hpp # INI config file reader
PPGIO.hpp # Save/config I/O helpers
WorldState.hpp # Scene/camera transition + toggle-flag state
OzPack.cpp # Standalone packer/unpacker CLI tool
Screenshot.hpp # --shot headless screenshot mode
Client/
Client.cpp/.hpp # Client networking layer
MasterList.hpp # Master server address loader + default URL
ProtocolHandler.hpp # angels95:// OS protocol handler (HKCU / .desktop)
JoinUri.hpp # angels95://join/<ip>:<port> URI parsing/validation
World/ # OZONE world module (post-b77 extraction)
LevelSettings.hpp # Level metadata, game rules, skybox paths
ZoneTypes.hpp # ZoneType enum, ZoneEnvOverrides, GameplaySoundProfile
ZoneManager.cpp/.hpp# Zone volume/portal storage + runtime queries
OzoneParser.cpp/.hpp# Pure text-to-primitive OZONE parser (no raylib)
OzOzoneLoader.cpp/.hpp # Geometry, heightmap, spatial chunk provider
OzoneFrustum.cpp/.hpp # Frustum culling math
OzoneHeightmap.cpp # Terrain mesh generation + height sampling
Server/
Server.cpp # Dedicated server; HTTP API; --bind/auth/admin flags
GameState.cpp/.hpp # Server-side world state, NPCs, pickups, saves
ServerHttp.cpp # HTTP /map + /status endpoints
ServerInternal.hpp # Shared server internals
Server/Network/
Network.cpp/.hpp # Custom UDP protocol, packed structs, LAN discovery
Server/Master/
Master.cpp # AngelMaster daemon
MasterClient.hpp # Server -> master heartbeat uplink thread
Network/
NetworkSession.hpp # Session/sequence state
MasterProtocol.hpp # Shared heartbeat + JSON list wire codec
MasterHttp.hpp # Minimal HTTP(S) client (WinHTTP on Windows, curl POSIX)
Script/
LightningScriptParser.cpp/.hpp # LightningScript parser
LightningScriptContext.cpp/.hpp # Script execution context (VM)
LightningEntityRegistry.cpp/.hpp # Entity type registry (.ozls)
LightningEntityManager.cpp/.hpp # Runtime entity management
LightningEntityDef.hpp # Entity definition structs
Pawn/
OzPawnSystem.cpp/.hpp # Dynamic NPC system, FSM, projectiles, wind sampling
Player.hpp # Player state/capabilities
PlayerMovement.hpp # Movement physics (sprint/crouch/stance)
PickupPawns.cpp/.hpp # Networked-pickup collect loop
Items.hpp # Item definitions
Pawn/AngelPlayer/ # Player behaviour modules (b78-b82)
GameUi.cpp/.hpp # GameUI object-bar bridge (data-only, never draws)
SlotBar.cpp/.hpp # SlotBar HUD renderer (atlas + slots, click-to-select)
InventoryBehaviour.cpp/.hpp # Inventory HUD/overlay + item/weapon hooks
WeaponBehaviour.cpp/.hpp # Fire/recoil/ADS + view-model clip triggers
PlayerController.cpp/.hpp # Toggles, vertical, game-over
Physics/
OzBsp.cpp/.hpp # CSG AABB boolean processor
WorldChunk.cpp/.hpp # Spatial partitioning for collision
PlayerPhysics.cpp/.hpp # Player collision resolution
PhysicsInfo.hpp # Per-zone physics overrides (oz::physics)
Renderer/
LitLightning.cpp/.hpp # Lighting renderer (per-frame light pass)
EngineBillboard.hpp # Billboard sprite rendering (pickups, icons)
TextSystem.hpp # Text rendering system
Video.hpp # Video playback support
WindShader.hpp # Wind sway shader uniforms
ViewModel.cpp/.hpp # First-person weapon view-model
CombatFX.hpp # Combat effects
Renderer/Mesh/ # oz::Mesh taxonomy (b74; namespaced vs raylib ::Mesh)
Mesh.cpp/.hpp # oz::Mesh base
StaticMesh.cpp/.hpp # Static mesh (OBJ/GLB)
SkeletalMesh.cpp/.hpp # Skeletal mesh with named clips (GLB/GLTF/IQM)
AnimatedMesh.cpp/.hpp # Vertex-keyframe .ozanim morph animation
MeshCache.cpp/.hpp # Shared model cache (intentionally leaked)
Particle/
OzParticleSimulationManager.cpp/.hpp # 4096-particle pool (sim + render)
Package/
PackageAssetLoader.hpp # Runtime asset loading from .oz* packages
OzPackage.hpp # OzPackage format reader/writer (zlib/miniz)
OzAssetMapper.cpp/.hpp # Engine/item texture mapper
Package/Anim/
OzAnimFormat.cpp/.hpp # .ozanim text format (raylib-free)
Audio/
SoundManager.cpp/.hpp # Single facade for every sound/music call
DspReverb.hpp # Schroeder reverb DSP (master mix)
Menu/
TitleMenu.hpp # Single-shell title menu (pages + actions)
MenuMetrics.hpp # Responsive layout math (raylib-free, tested)
MenuStyle.hpp # Shared palette/widgets with the in-game panel
CharacterRoster.hpp # characters.cfg parser (raylib-free, tested)
CharacterPreview.hpp # Real model portraits + live 3D preview
ServerBrowserModel.hpp # LAN/internet merge, sort/filter/favourites (tested)
PauseMenu.hpp # In-game pause overlay (shared metrics/style)
InternetBrowser.hpp # Async Internet server browser (parallel probes)
SkillTree.hpp # Ethereal skill tree (b78)
SettingsMenu.hpp # In-game settings panel (shared player settings)
UI/
UiHandler.cpp/.hpp # Win32 native menu bar (oz::ui::CreateNativeMenuBar)
Renderer/plmpeg/ # pl_mpeg MPEG1 video decoder
Renderer/rlights/ # raylib lights helper
Renderer/raygui/ # raygui UI library
miniz/ # Vendored miniz (OzPackage zlib compression)
AngelEd/ # Level editor (Win32 only; Makefile errors out on Linux)
Source/
Main.cpp # Editor entrypoint, toolbar, 3D viewport, selection
Editor.hpp # Editor state, camera, lighting, cached models
Core/ # EditorShell, EditorDispatcher, EditorState, EditorPanelState, EditorLog
Subsystems/ # Placement, PropsApply, SurfaceOps, LevelState, History (real TUs)
# + Selection, EntityOps, WorldIO, OzoneExport, WorldGraphBridge,
# AnimEditing (still unity fragments, #included by Main.cpp)
UI/ # Win32 native panels; UiShell.cpp is the single unity TU
Resources/ # AssetScope, PackageIO, AssetScan - shared by two panels each
EditorIcons.cpp/.hpp # Toolbar icon loader (AngelEd/UI/*.bmp)
PPGIO.hpp # Save/config I/O helpers (shared with Source/)
raygui/ # Bundled raygui (dark.h, raygui.c/.h)
UI/ # Toolbar icon .bmp files
Makefile # Separate editor Makefile
GameData/ # Loose assets, worlds, saves
Audio/ # every sound and music call lives under here
Sounds/
Entities/ # player/creature: jump, land, hurt, gait, aggro
Guns/ # weapon fire one-shots
Items/ # item pickup sounds (reserved)
Skills/ # spell/skill cast sounds
UI/ # menu clicks, dialogue text noise
Ambience/ # looping ambience beds
Music/ # music library (worlds keep their own Music/Main.mp3)
Tex/
Global/
Gun/<weapon>/ # weapon .ozls + model + textures, kept together
Models/<name>/ # gear/object .ozls + meshes + icons
Items/ # item icons + MedMeshes
Sky/ # skybox panoramas (levelinfo + skyzone defs)
EFX/ # portal/effect textures
Skills/ Lights/ # spell icons, light art (reserved)
Editor/ # entity gizmo icons + editor app icon
Engine/ # HUD atlas, bar font, TextBar, GameUI.ozls, app icons
Title/ Menu/ # title art, menu widget skin
Tileset/ # shared tile library (Ancient/Egypt/Iced)
World/ # shared world textures (reserved)
Pawn/
Pawns/ # pawn models + sprite fallbacks + Textures/
PawnDefs/*.cfg # Data-driven NPC definitions
Player/ # player character models + textures
Lights/Light.ozls # light defaults layer
Skills/*.ozls # skill-tree nodes + spell defs
Environment/
Rocks/ Plant/ Industrial/ Ethereal/ Greece/ Egypt/ # shared prop meshes
Anims/*.ozanim # vertex-keyframe clips
Shaders/ # .vs/.fs (System/Shaders is a build-time copy)
Worlds/
<WorldName>/
World.ozone # World description (OZONE format)
Models/ # world-local props (.glb, Skybox.png)
Music/ # per-world track (Main.mp3)
oztex/ # tileset slots + brush textures
Saves/ # Binary save files (gitignored)
System/ # Release directory (gitignored, assembled by scripts)
Angels95.exe / AngelServ.exe / AngelEd.exe / AngelMaster.exe / OzPack.exe
Data/*.oz* # Packaged assets
Cache/ # Runtime temp cache (model extraction)
angels95-serv.service # VPS systemd unit (AngelServ/AngelMaster)
GameData/Tex/Engine/GameUI.ozls (EntityType::GAMEUI) is the authored source
of the player's object bar: one atlas texture = plus a comma-separated
slot_rects stat (source-texture pixels, no spaces). GameUi.{hpp,cpp} is the
data-only bridge (SlotCount(), SlotRect(i), layout stats); SlotBar.{hpp,cpp}
is the single renderer — it maps the atlas to the screen, fits each occupied
cell's entity icon, draws slot numbers/selection, and does click-to-select. Both
call sites (LightningEntityManager::DrawHotbar and
InventoryBehaviour::DrawOverlay) share it, each keeping a plain-rectangle
fallback when GameUI.ozls is absent.
The player is assembled from Source/Pawn/AngelPlayer/ modules instead of one
monolithic block: PlayerController (toggles, vertical, game-over),
WeaponBehaviour (fire/recoil/ADS + view-model clip triggers),
InventoryBehaviour (HUD/overlay + item/weapon collect hooks) and the GameUi
bridge. Sprint (Shift) / crouch (Ctrl) and the resulting stance are
replicated to the server; PlayerPhysics + PhysicsInfo.hpp hold the movement
defaults.
ZoneManager owns ZoneVolumeNode, ZonePortal, PointRegion and every
runtime zone query (GetActiveZones, CheckZoneCollision,
CheckPortalCollision, UpdatePlayerRegion). Zone entry/volume detection and
env-override merging go through ZoneManager::Instance(), not PawnSystem.
ZoneEnvOverrides (fog/ambient/reverb) layer lowest-priority-first so the
highest-priority zone wins; GameplaySoundProfile is consumed by
SoundManager::UpdateSoundZones. Per-zone physics (gravity, jump,
terminal, water_*, ladder_speed, fly_mult) override
oz::physics::PhysicsInfo.
Source/Audio/SoundManager.{hpp,cpp} is the single entry point for every sound
and music call — no gameplay file touches raylib sound handles directly. It
owns world music, zone ambience, the reverb hook (DspReverb Schroeder reverb
attached to the master mix) and the script-sound cache. play_sound routes
through SoundManager::PlayScriptSound and is package-aware (.ozsnd
resolves). The old g_prevSoundZone/g_defaultWorldMusic/g_ambienceHandle
globals in Core.hpp are gone.
MasterHttp.hpp speaks TLS with no vendored crypto: WinHTTP/Schannel on
Windows, curl on POSIX (the Windows Makefile links -lwinhttp). The default
master is https://angels95.tribewarez.com/master (client browser GET
/master/api/servers, server uplink POST /master/api/heartbeat). See
Wiki/Master-Server.md.
The runtime became a real VM: $flag<idx> reads instance toggle flags,
assignments support RHS arithmetic ($x = $x - 1), if (...) accepts both
brace and endif styles, and toggle_flag/jump opcodes landed. Each
instance carries a per-instance stat resolver and its hotbar/equipment flags
persist across saves.
oz::Mesh/StaticMesh/SkeletalMesh/AnimatedMesh unify the ad-hoc mesh
paths behind one cache (MeshCache, intentionally leaked so GL teardown never
runs after CloseWindow). .ozanim is a raylib-free text morph format
(Source/Package/Anim/); AnimatedMesh samples clips and CPU-uploads
positions. Wind-affected meshes (wind=1) draw with Wind.vs (height-weighted
sine sway); oz::SetWindUniforms uploads the sway params per draw.
Source/Renderer/Skybox.hpp draws six quads, and GameData/Shaders/Sky.fs takes
the sky's vertical coordinate from world elevation
(clamp(dir.y * 0.5 + 0.5, 0, 1)) rather than from the quad's texcoord. The six
quads do not agree on their V mapping — measured in-client, adjacent walls at a
cube corner differed by 33–40 colour levels at the same screen row, and the outer
panels ramped horizontally within a row. Elevation belongs to the view ray, so
every face and the cap now agree by construction: corners fell to 1–5 levels and
the cap band from 50 to 1. The cost is that a wall samples only V ≥ 0.5, so legacy
panorama skies show their upper half (measured better, but worth knowing).
tools/gen_sky_textures.ps1 produces horizontally periodic, vertically symmetric
gradients (sky_grad_day.png, sky_grad_dusk.png) plus optional caps. Both
outdoor worlds pass the gradient as cap and sides, because a separate cap image
leaves a trapezoid where the cap meets the walls.
Water is paint, not simulation. tools/gen_water_textures.ps1 generates
water_pool.png, waterfall_streaks.png (alpha streaks with deliberately punched
gaps — the shader multiplies texture alpha by uAlpha) and foam.png. Cascades
are UNLIT|TRANSLUCENT|TWO_SIDED|PAN_V|NO_BSP_CUTS (268962944) sheets; pools are
UNLIT|PAN_U|TRANSLUCENT (264320) slabs. Surface.fs/LitFog.fs have no depth
texture, so refraction or depth tinting is a renderer project, not a shader tweak.
--shot <out.png> plus --shot-delay, --shot-res, --shot-cam (repeatable,
OZONE Z-up, yaw/pitch degrees) and --shot-hud capture deterministic,
HUD-free frames. It suppresses splash/menu/audio, forces vsync/MSAA/pixel/
jitter/fog/head-bob/debug off, freezes the camera via isNoClip, and exits when
every camera is captured. See Source/Screenshot.hpp.
Every target is compiled and linked with a single g++ command. Flags: -O3 --std=c++20. The Makefile defines per-target object lists manually.
AngelServ (dedicated server) uses raw POSIX/Winsock sockets only. No raylib headers or libraries are linked. The SERVER_CXX compiler is used for server-side code.
Included early in any file that touches both raylib and winsock2.h. Renames conflicting Windows symbols (CloseWindow, ShowCursor, Rectangle, DrawText) before #include <windows.h>, then #undefs them.
Used in PPGIO.hpp, Data.hpp, TextSystem.hpp.
Used for all network packet structs to ensure binary compatibility between client and server.
Assets can be distributed as loose files in GameData/ OR packaged into .oz* containers:
| Extension | Type | Magic | Contents |
|---|---|---|---|
.ozpak |
Generic | OZPK | Models, scripts, shaders |
.oztex |
Texture | OZTX | PNG textures |
.ozsnd |
Sound | OZSD | WAV/MP3/OGG |
.ozmux |
Music | OZMX | WAV/MP3 |
.ozone |
World | OZWN | World files |
Loading is handled by PackageAssetLoader::Instance().Init() which scans System/Data/*.oz*. Use *WithFallback wrappers (LoadTextureWithFallback, LoadModelWithFallback, etc.) which check the filesystem first, then search packages. A GameData/... path is matched by stripping that prefix, then by basename, then by stripping the first segment — which is why a packaged install resolves a reference like GameData/Audio/Sounds/Guns/machgf3b.wav.
build-data.ps1 packs each asset root under GameData/ into System/Data:
| Package | Contents |
|---|---|
gun_<weapon>.ozpak |
one per weapon folder (gun/, models.ozpak, player.ozpak, pawns.ozpak, pawns_defs.ozpak, skills.ozpak, lights.ozpak, environment.ozpak, engine.ozpak, shaders.ozpak) |
.oztex under Textures/
|
items, sky, efx, title, menu, editor_icons, tileset, world_<W>_tex
|
Sound/global.ozsnd, Music/{ambience,library}.ozmux
|
audio |
Zones/world_<W>.ozone |
the whole world folder (World.ozone, .ozls, Models/, Music/, oztex/) |
The script deletes the packages it owns first (everything except imported_*): a renamed source folder used to leave its old .oz* behind, and the loader opens every package under System/Data, so the stale copy kept resolving.
worldcheck --asset-refs audits every path-valued key in GameData's .ozls/.cfg files (and the levelinfo skybox pair) against the filesystem. It runs in make test and in CI, so a folder move that forgets a reference fails the build instead of silently rendering a missing asset. The pre-existing missing content is registered by basename in tools/assetrefs-allow.txt.
Models require a temp-file cache in System/Cache/ because raylib has no LoadModelFromMemory. A packaged .gltf also has its relative "uri" buffers extracted into the cache, or raylib fails on the missing .bin next to the extracted text.
Source/Particle/OzParticleSimulationManager.{hpp,cpp} replaces the legacy
50-particle ParticleDemon with a pool of 4096 particles (b74). Emitters
are authored as GameEngine.ParticleEmitter nodes (or OZONE
ParticleEmitter <type> ... lines) and boxed in PawnSystem. The manager is
ticked once per frame in Core.hpp after the weapon/NPC/projectile sim loop
and only reads emitter defs — it never calls gameplay code, so particles cannot
interrupt weapon mechanics or NPC ticks. Client-only/cosmetic (not networked).
NPC definitions are loaded from GameData/Pawn/PawnDefs/*.cfg (name, speed, aggroRange, attackRange, damage, maxHealth, sprite_path, scream_path). Fallback hardcoded defs: Walker, Skaarj, Brute, Floater.
FSM states: IDLE, PATROL, CHASE, RETURN, DEAD (no ATTACK state — see
Source/Pawn/OzPawnSystem.hpp:25). State transitions fire .ozls actions
(on_patrol/on_chase/on_return/on_death) only when a pawn-named def
exists (e.g. Walker.ozls). Server-owned NPCs run the FSM server-side and are
networked (npc_type in NpcStateUpdateData, networkControlled=true).
Animated pawns: .cfg keys mesh_type (static/skeletal) + anim_* clips.
A skeletal pawn uses oz::SkeletalMesh (GLB/GLTF/IQM) and maps PawnState to
a clip; yaw tracks movement for skeletal pawns, static/billboard pawns face the
camera.
NPCs attack via melee range check (no ranged NPC fire). Projectile damage is
detected client-side in PawnSystem::UpdateProjectiles() (collision radius
1.5) and server-side in GameState::tick_projectiles() (radius 2.0).
Weapons are data-driven .ozls entities of type weapon. Two types:
-
Ranged — fires
ProjectileNodeobjects with configurable speed, spread, damage, lifetime. Supports ammo (magazinestat), reload (reload_time), and cooldown-based auto-fire. -
Melee — forward range check using
reachstat; no projectile spawn. Runson_swing/on_hitscript actions.
Key source files:
| File | Role |
|---|---|
Source/Script/LightningEntityManager.cpp |
FireSelectedWeapon() — reads weapon stats, handles ammo/reload/cooldown, runs on_fire/on_swing/on_reload actions, dispatches ranged (projectile) or melee (range check) logic |
Source/Pawn/OzPawnSystem.cpp |
SpawnProjectile(), UpdateProjectiles() (movement + gravity + pawn collision), DrawProjectiles()
|
Source/Server/GameState.cpp |
spawn_projectile(), tick_projectiles() — server-authoritative projectile simulation with NPC and player collision |
Source/Pawn/AngelPlayer/WeaponBehaviour.cpp |
FireWeapon() — camera ray, trigger, recoil, muzzle flash, crosshair, ADS, view-model clip triggers |
Source/Renderer/ViewModel.cpp/.hpp |
First-person weapon view-model: attached to the camera, Idle/Fire/Reload clips + procedural recoil kick / reload dip |
Weapon stats may set viewmodel_mesh, viewmodel_texture,
viewmodel_offset/rot/scale, recoil, viewmodel_skeletal (1 = force
skeletal playback, -1 = force static, absent/0 = auto — auto enables it only
when oz::BoneIdsFitSkeleton() confirms every vertex bone index fits the
model's loaded skeleton, so a boneIds-with-no-skeleton mesh stays static),
and per-weapon projectile_mesh /
projectile_submesh / projectile_scale / projectile_color for drawn
tracers. play_clip also reaches the view-model when the command matches the
held weapon def. FBX art is converted via tools/convert_fbx.ps1 (FBX2glTF).
Weapons that have sibling animation GLBs (Pistol_01, Rifle_01, SMG_01,
Shotgun_01) take the animated path: tools/merge_glb_skins.py collapses
FBX2glTF's one-skin-per-mesh-part output into a single skin (raylib supports
one), then tools/merge_glb_anims.py merges the Fire/Reload clips and
synthesizes Idle. A reload authored as several takes is concatenated into the
one Reload clip the engine plays (Reload=a.glb+b.glb; the shotgun ships
ReloadStep + ReloadEnd that way). Everything else is flattened to a static mesh.
-NoAnimated forces the old flatten-everything behaviour and strips the clips.
Pickup icons come from each gun's _silhouette.png, which must be tinted dark
(tools/make_weapon_icon.ps1) — the pack ships it pure white, i.e. a white
billboard in every map.
Pickup types come from the LightningScript entity registry (.ozls definitions).
The editor is covered in detail by Wiki/Editor-Usage.md; the remaining
limitations (as of b111):
- Render meshes are not CSG-carved (CSG booleans process collision volumes only)
- Undo/redo covers world state only; camera and selection are not restored
- No test-play save prompts ("Reload world from playtest changes?")
- Lighting effects (watery, torch, fire, lamp) are UI-only — not rendered in the viewport
- Model/Texture preview rendering requires the raylib viewport to be focused
Angels95 - AngelTech Engine | repository | 5a75146
Built on raylib 5.5.