Skip to content

Engine Overview

angels95-wiki-sync edited this page Oct 27, 2026 · 6 revisions

Engine Overview

Source Tree

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)

Key Systems (b57–b101)

GameUI / SlotBar object-bar HUD (b82)

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.

AngelPlayer behaviour modules (b78–b80)

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 (b80/b82)

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.

SoundManager facade (b57, refactored b82)

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.

Master HTTPS uplink (b59/b70)

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.

LightningScript VM (b59)

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.

Mesh taxonomy + vertex-keyframe animation + wind (b74)

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.

Skybox and water (b101)

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.

Screenshot mode (b82)

--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.

Key Architecture Points

Single g++ invocation, no CMake

Every target is compiled and linked with a single g++ command. Flags: -O3 --std=c++20. The Makefile defines per-target object lists manually.

No raylib dependency for server

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.

WindowsCompat.hpp

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.

using namespace std

Used in PPGIO.hpp, Data.hpp, TextSystem.hpp.

#pragma pack(push,1)

Used for all network packet structs to ensure binary compatibility between client and server.

Package System (OzPackage)

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.

Particle System

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).

Pawn System

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).

Weapon System

Weapons are data-driven .ozls entities of type weapon. Two types:

  • Ranged — fires ProjectileNode objects with configurable speed, spread, damage, lifetime. Supports ammo (magazine stat), reload (reload_time), and cooldown-based auto-fire.
  • Melee — forward range check using reach stat; no projectile spawn. Runs on_swing/on_hit script 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).

Known Editor Gaps

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

Clone this wiki locally