Battlezone 98 Redux

Battlezone 98 Redux

Not enough ratings
Battlezone 98 Redux: The Complete Modding Reference Guide
By GrizzlyOne95
The definitive encyclopedia for Battlezone 98 Redux modding, compiled from the collective knowledge of the community's most experienced modders and developers. This comprehensive reference covers every aspect of BZ98R modification from basic setup to advanced techniques.

WHAT'S COVERED:
- Map Creation & Terrain Systems
- Lua Scripting & AI Behavior
- 3D Modeling & Animation
- Texture Creation & Optimization
- Particle Effects & Visual Systems
- Audio Implementation
- Performance Optimization
- Advanced Modding Techniques
- Python Tool Documentation
- DLL injection
- Game Server Info
- Publishing & Distribution

WHO THIS IS FOR:
- Complete beginners starting their first mod
- Experienced modders seeking advanced techniques
- Anyone troubleshooting modding issues
- Developers creating custom mods or tools

HOW TO USE:
This is a REFERENCE GUIDE, not a tutorial. Use Ctrl+F to search for specific topics, techniques, or error solutions. Each section is self-contained with practical examples and real-world solutions tested by the community.

COMMUNITY SOURCED:
Combined knowledge from UltraKen, General BlackDragon, bluebanana, DeusExCeteri, ssuser, systeme, Business Lawyer, Commando, Janne, GrizzlyOne95, Goomba, VTRider, and many other dedicated community contributors.

I recommend bookmarking or favoriting this guide, as I suspect you'll reference it throughout your entire modding journey.
   
Award
Favorite
Favorited
Unfavorite
Introductory Modding Info
Beginner Guide to Battlezone 98 Redux Modding

---

Section 1: Set Up Your BZ Files for Modding

Extract stock game files (ODFs, sounds, etc.)
You’ll need to unpack bzone.zfs to access all the stock files.

Method 1: Extract directly to Edit folder
cd "C:\Program Files (x86)\Steam\steamapps\common\Battlezone 98 Redux\edit" makezfs x: bzone.zfs
⚠️ This will dump ~4000 files into the Edit folder. Not recommended unless it's empty!

Method 2: Extract into your own folder
1. Create a subfolder inside Edit, e.g. Edit\my_folder
2. Copy bzone.zfs into my_folder
3. Open CMD inside my_folder (tip: type
cmd
in the Explorer address bar)
4. Run:
..\MakeZFS.exe x: bzone.zfs
This keeps things organized. You can move the files elsewhere later if needed.

---

Launching the BZ Editor

Command prompt method:
battlezone98redux.exe misn01.bzn /startedit /win

Useful flags:
/startedit — Launch directly into edit mode
/edit — Enable toggling editor mode in mission
/win — Must use for saving in windowed mode

Editor controls:
• Toggle Editor: Ctrl + A
• Terrain Paint Mode: Ctrl + E
• Save: Ctrl + S
• Top-down View: Shift + F9
• Radar View: Shift + F10

---

Create a Custom Unit

1. Open a stock ODF (e.g. svtank.odf)
2. Change the weapon slot (e.g.
gc1 = gspstab
to use SP Stabber)
3. Add this line under GameObjectClass:
baseName = svtank
4. Save as a new ODF, like svtankcc.odf

---

Add Your Custom Unit to the Editor

1. Find a stock build menu ODF (e.g. b_amcmbt.odf)
2. Change an item to your custom unit (e.g.
#1 = svtankcc
)
3. Save the modified file to the addon folder
4. Also place your svtankcc.odf in addon
5. Open the editor and place your new unit!

---

Section 2: Useful Tools for Modding
  • Notepad++ for viewing and editing files, or scripting
  • Command Prompt
Core Game Assets
3D Models & Animations
  • .VDF (Vehicle Definition File) - Defines complete vehicles with mass, collision, animations, GEO positions, and Level of Detail settings
  • .SDF (Structure Definition File) - Same as VDF but for buildings/structures, uses only GEO collisions
  • .GEO (Geometry) - Individual 3D model parts that combine to form complete vehicles/structures
  • .MESH (OGRE Mesh) - Modern 3D geometry format using OGRE engine, contains vertices, faces, normals, UV coordinates
  • .SKELETON - OGRE animation format with bone hierarchy, must match GEO naming for proper sync
  • .MATERIAL - Defines surface properties, texture assignments, and shader properties for models
Textures & Visual Assets
  • .MAP - Legacy texture format (256x256 indexed color, various pixel formats)
  • .DDS - DirectDraw Surface textures, modern format replacing MAP files
  • .ACT/.PAL - Color palette files for indexed textures
  • .TGA - Uncompressed image format for certain effects
Map Files
Essential Map Components
  • .HG2 - Height map data (updated version of original BZ98 .hgt format)
  • .TRN - Terrain data including world info, sky settings, texture assignments, and map size
  • .MAT - Material information for ground textures
  • .BZN - Contains all game objects: units, buildings, people, powerups, scrap, spawnpoints, paths, and commands
  • .LGT - Lighting data that must be regenerated after terrain changes
Optional Map Files
  • .BMP - Preview image for multiplayer map selection
  • .DES - Map description text
  • .VXT - List of allowed player vehicles
  • .INI - Configuration file for map metadata and Workshop settings
Audio Files
  • .WAV - Uncompressed RIFF WAVE files for audio messages, sound effects, and voiceovers
  • Must be uncompressed format for game compatibility
Scripting & Code
  • .LUA - Lua 5.1 scripts for mission logic and AI behavior
Object Definition
  • .ODF (Object Definition File) - Defines unit properties, weapons, behaviors, and metadata
  • Must be 8 characters or less in filename
  • Contains all gameplay parameters for units
Archives & Compression
  • .ZFS - Compressed archive format using LZO compression
  • bzone.zfs - Main game assets (models, textures, sounds, ODF files)
  • bzone_en.zfs - Localized voiceovers
Modern vs Legacy
The game uses a hybrid system:
  • Legacy formats (VDF/SDF/GEO/MAP/ODF) for physics, collision, and game logic
  • Modern OGRE formats (MESH/SKELETON/MATERIAL/DDS) for visual rendering
  • This dual system allows enhanced graphics while maintaining original game mechanics
Troubleshooting Common Issues
Network and Multiplayer Issues

Multiplayer Tab Says "Not Ready" (Windows)
Problem: Multiplayer shows "Not Ready" status and won't connect
Solution: Uncheck IPv6 in network adapter settings in Windows

Steps:
1. Open Control Panel → Network and Internet → Network Connections
2. Right-click your network adapter → Properties
3. Uncheck "Internet Protocol Version 6 (TCP/IPv6)"
4. Click OK and restart the game

Alternative Solution:
• Use Windows Network Reset: Settings → Network & Internet → Status → Network Reset
• This will reset all network adapters and may resolve IPv6 conflicts

Line Ending Problems

TRN File Line Endings - CRITICAL
The #1 cause of texture loading problems

Rule: TRN files MUST use CRLF (Windows line endings)
Cannot handle: CR (Mac line endings) or mixed line endings
Result of wrong endings: Textures won't load, terrain appears white/black

Symptoms:
• Map loads but textures don't appear
• Terrain appears completely white or black
• "Invalid TRN data" error messages
• Textures work in editor but not in game

Solutions:
• Use text editors that preserve CRLF line endings (Notepad++, Visual Studio Code)
• When copying TRN data between systems, verify line endings
• Convert line endings: LF → CRLF or CR → CRLF
• Avoid Mac-based text editors that default to CR line endings

Text Editor Line Ending Settings:
Notepad++: Edit → EOL Conversion → Windows (CRLF)
Visual Studio Code: Bottom status bar shows line ending type, click to change
Sublime Text: View → Line Endings → Windows

Map Loading Issues

Map Won't Load/Crashes
Check these in order:
1. Verify all essential files present: .hg2, .trn, .mat, .bzn
2. Check TRN file line endings (must be CRLF)
3. Ensure map is in correct addon folder or subfolder with .ini file
4. Verify launch options syntax: mapname.bzn /startedit

Textures Not Loading
Primary cause: TRN file line ending issues (see above)
Other causes:
• Missing or corrupted .mat file
• World TRN data not properly copied
• Size section accidentally removed from TRN file, or two sections duplicated
• Wrong texture file paths in TRN

Editor Problems

AI Pathfinding Issues
• Check Cell Types view (yellow areas = AI cannot access)
• Terrain too steep for AI navigation (>45° slopes)
• Missing proper transition slopes between elevations
• Buildings placed on inaccessible terrain
• Use Slabs view to verify AI accessibility

Multiplayer Specific Issues

Map Not Appearing in Multiplayer List
• Missing spawnpoints (required for all multiplayer maps)
• Incorrect .ini file configuration
• Wrong mapType setting in .ini file

Powerups Not Spawning (DM Maps)
• Incorrect path naming format (should be: powerup_respawntime_number)
• Invalid ODF names in path names
• Paths placed in inaccessible locations
• Missing powerup ODF files

Players Can't Join/Sync Issues
• Host and clients have different mod versions
• Network ports blocked (check firewall)
• IPv6 conflicts (see network troubleshooting above)
• Map files still present in addon that cause a conflict

Workshop Upload Problems

Upload Fails
• Missing preview image (.jpg format, same filename as map)
• Missing or incorrect .ini configuration file
• Files not in same folder
• Preview image not square format
• File size too large for Steam Workshop

Cross-Platform Compatibility

Windows ↔ Mac/Linux Issues
• Always use CRLF line endings for TRN files
• Check file path separators when sharing (\\ vs /)
• Verify case sensitivity in filenames (Linux is case-sensitive)
• Test on target platform before publishing

Quick Diagnostic Checklist

When a map doesn't work, check these in order:
1. ✓ All required files present (.hg2, .trn, .mat, .bzn, .lgt)
2. ✓ TRN file uses CRLF line endings
3. ✓ Map in correct folder with .ini
4. ✓ Launch options correct
5. ✓ No special characters in filenames
6. ✓ Spawnpoints placed for multiplayer maps

Getting Help

When asking for help, include:
• Exact error messages
• Your operating system
• Game version (BZ98R Steam vs Gog etc.)
• Map type (SP, MP, DM, Strategy)
• Steps you've already tried
• Screenshots of errors when possible

Common Resources:
• Battlezone Discord servers
• GitHub repositories for tools
Linux Specific Info for BZ98R
Linux-Specific Gaming Issues
Running BZR on Linux:
- Recommended Distributions: Any modern distribution can easily run BZ, however there are more user-friendly options when first starting out. Listed in order of recommendation:
New Users:
PopOS!: As of July 2025 stick to 22.04 for a very stable X11 Linux gaming experience
Mint: Good option (better than Ubuntu, but KDE is a horrible workflow)
Bazzite
Nobara
CachyOS
Most stable gaming experience with highest FPS:
Arch (btw) w/ Hyprland
Debian Trixie w/ KDE, Gnome (or i3)
- Dual Boot Setup: Using separate hard drives for a dual boot setup ensures a new user won't accidentally wipe their system (disconnect Windows drive during Linux installation)
- Nvidia Graphics: No longer an issue
- Proton Compatibility: Battlezone works very well with Steam's Proton
- Shared Storage: NTFS partitions can be shared between Windows and Linux for game files however this will slow down performance and some distros do not support NTFS
Basic Mapmaking Guide Pt 1
Introduction to Mapmaking

Welcome to the Moon, Commander!

Mapmaking is the art of creating singleplayer missions and multiplayer maps for Battlezone. They are the most common type of mod for the game. With a little bit of time and effort, you can create something for you and your comrades to enjoy!

Essential Map Files

Every map needs at least three essential files that are automatically generated when you begin the mapmaking process:

Core Terrain Files:
.hg2 - The heightmap of your terrain (hills and valleys). Updated version of .hgt from original BZ98
.trn - Stores terrain data including world, sky, and map size
.mat - Material information for ground textures

Essential Game Files:
.bzn - Units, buildings, people, powerups, scrap, spawnpoints, and all objects. Also stores paths and commands
.lgt - Lighting data that needs regeneration after terrain changes

Optional Multiplayer Files:
.bmp - Preview image displayed in multiplayer map list
.des - Map description when selected
.vxt - List of allowed player vehicles

Types of Maps

Singleplayer Missions (Instant Actions/IAs):
• Range from simple one-objective skirmishes to complex dynamic scenarios
• Require AI setup and scripting

Multiplayer Maps:
Deathmatch (DM): 8-player maps focused on combat and powerup collection
Strategy (ST): 2-4 player base-building and destruction maps
King Of The Hill (KOTH): Control point focused gameplay
Multiplayer Instant (MPI): Instant action scenarios for multiple players
Multiplayer Action (MPA): Action-focused multiplayer scenarios

Creating a Map with MakeTRN

Location: Found in Edit folder of Battlezone installation
Usage: Console program run via Command Prompt

Navigate to directory:
cd C:/Program Files (x86)/Steam/steamapps/common/Battlezone 98 Redux/Edit

Basic command:
maketrn mapname /c /w=width /h=height

MakeTRN Parameters:
/c - Creates new map
/w=nnnn /h=nnnn - Width and height in meters (must be divisible by 1280)
/p=ParameterFile - Autopainter tool for texture placement
/e=EmptyElevation - Background terrain height

Common map sizes: 1280x1280, 2560x2560, 3840x3840, 5120x5120

Example:
maketrn multst69 /c /w=2560 /h=2560
Basic Mapmaking Guide Pt 2
Setting Up Your Map

Move files to Battlezone's addon folder
Select world by copying world .trn data from Edit/trn folder
Preserve Size section in your map's .trn file when copying world data
Set launch options in Steam: mapname.bzn /startedit

⚠️ CRITICAL: TRN File Line Endings

TRN files MUST use CRLF (Windows) line endings
Cannot handle CR (Mac) line endings
Mixed line endings will prevent texture loading
Use a text editor that preserves or allows setting CRLF line endings
Common issue when copying TRN data between different operating systems

Map Editor Controls
Key Bindings:

Ctrl+A - Start/stop game simulation (pause/unpause)
Ctrl+E - Enter Terrain Editor
Shift+F9 - Enter Object Editor
Shift+F1 - Exit editors and return to normal view

Terrain Editor
Access with Ctrl+E for birds-eye view of map.
Navigation:

Arrow keys rotate viewpoint
Mouse to screen edges moves view
W switches between texture and height modes
Tab changes wireframe colors in height mode

Texture Types:

Solids - Basic textures (lava, rock, etc.)
Caps - Transitions between textures
Diags - Diagonal transitions between textures

Texture Tools:

Eyedropper - Copy texture for paintbrush
Rotate - Rotate textures (right-click for clockwise)
Vertical/Horizontal Flip - Flip textures (H key for horizontal)
Paintbrush - Place copied texture (P key)
Zoom - Zoom in/out (+/- keys)
Cycle Texture - Cycle through solids (S key)
Cap Texture - Cycle through caps (C key)
Diag Texture - Cycle through diags (D key)
Variant Texture - Cycle through variants (A key)

Height Editing Tools:

Eyedropper - Copy height for flatten tool
Incremental Area - Raise/lower terrain in selection area
Incremental Circle - Raise/lower in circle with bell curve
Flatten Area - Flatten to copied height
Bell Tool - Raise/lower in bell shape
Incremental Point - Modify single point
Set Width/Depth/Height/Bell Width - Adjust tool parameters
Set Increment - Adjust increment amount
Smooth Tool - Smooth single terrain point

Object Editor
Access with Shift+F9 for top-down technical view.
Control Modes (Press 1 + number):

Find Path - Calculate distances between points
Edit Path - Place AI paths (left click place, right click deselect)
Edit Task - Set object commands and tasks
Edit Objects - Move objects (drag) and rotate (right-click)

Draw Modes (Press 2 + number):

Slabs - AI accessibility (Blue=easy, Grey=steep, Yellow=impossible)
Goals - Strategic AI decisions
Priorities - Strategic AI priorities
Paths - Display placed paths
Surface - Ground textures
Height - Relative elevation (Blue=low, White=high)
Slope - Terrain steepness (Blue=flat, White=steep)
Cell Type - AI accessibility at all locations
Cell Region - AI reachable areas (Grey=accessible, Black=not)

Other Controls:

Edit Mode (3+1/2) - Toggle simulation like Ctrl+A
Resources - Add/subtract scrap and pilots for teams
Build Menu - Place objects by team
Team Setting - Set team for placed objects

Multiplayer Map Requirements
All Multiplayer Maps Need:

Spawnpoints - Found in Buildings section of Build Menu

Strategy Maps Need:

Geysers - Resource generation points
Scrap - Collectible resources
Check AI accessibility with Cell Types/Slabs view

Deathmatch Maps Need:

Weapon/Powerup Spawns - Create paths with naming format:
powerup_respawntime_number
• Replace powerup with ODF name (apstab, apammo, aprepa, etc.)
• Replace respawntime with seconds to respawn
• Replace number with spawn point number

Map Configuration (.ini file)
Create configuration file with same name as map:
[DESCRIPTION] missionName = "Your Map Name" [WORKSHOP] mapType = "multiplayer" ; Options: "instant_action", "multiplayer", "mod" ; customtags = "tag1, tag2, tag3" [MULTIPLAYER] minPlayers = "2" maxPlayers = "4" gameType = "S" ; Options: D=Deathmatch, S=Strategy, K=KOTH, M=MPI, A=MPA

BZN File Configuration
Saving BZN Files:

Save your BZN with /asciisave flag
You will need to change the mission type in the .BZN depending on map type

Mission Type Setting:
name = LuaMission

Valid Mission Types:
  • Inst4XMission - Instant Action maps
  • MultDMMission - Multiplayer Deathmatch maps
  • MultSTMission - Multiplayer Strategy maps
  • LuaMission - Lua-scripted missions

Publishing to Steam Workshop
Prerequisites:

All map files in same folder
Square preview image (200x200+ pixels, .jpg format, same filename as map)
Configuration .ini file

Steps:

Open Steam Library → Tools
Launch "Battlezone 98 Redux - Uploader Tool"
Enter map name and description
Click "Create New Item"
Select map folder and preview image
Upload to Workshop

Generate Preview Image:

Use /shellmap launch flag to auto-generate .bmp preview
Image appears in Battlezone root directory

Mapmaking Tips and Best Practices
Strategy Maps:

Equal scrap distribution near all spawn points
Flat areas for base building near geysers
Strategic terrain features between spawn points
Consider early, mid, and late game scenarios

Deathmatch Maps:

Balanced powerup distribution relative to spawn points
Central areas typically see most combat
Varied terrain for tactical options
Fair respawn locations

Instant Action Maps:

Focus on mission objectives over balance
Create immersive environments appropriate to world
Consider AI pathing limitations on steep terrain

Technical Tips:

Use cap and diag textures for smooth texture transitions
Ensure AI can access intended areas (check Cell Types view)
Keep terrain relatively flat where AI needs to build or travel
Test map thoroughly before publishing
CRITICAL: Ensure TRN files use CRLF line endings

Background Terrain:
Fix ugly map edge dropoffs using:
maketrn mapname.trn /e=height
Or use edge_path for manual boundary setting.
AIP Files
AI Behavior and AIP Files

Introduction to AI Personalities

Battlezone's AI system is highly configurable through AIP files (Artificial Intelligence Personality files). These control how computer-controlled teams behave strategically - what they build, where they send troops, and how they react to different situations.

AIP File Structure

AIP files use C-style syntax with
//
comments and are divided into several key sections:

Basic AIP Template
//***************************************************************************** // Custom AI Personality //***************************************************************************** #include "aiPdef.h" // How often do we recompute strategy? int recompute_strategy_period = 100; // STRATEGIC PRIORITIES int threat_priority = 150; // How much to prioritize threats int distance_priority = -3; // Penalty for distance (negative = closer is better) int defend_buildings_priority = 30; // Priority for defending our buildings int attack_enemy_base_priority = 75; // Priority for attacking enemy bases int persistence_priority = 30; // Stick with current objectives int exploration_priority = 50; // Priority for exploring unknown areas int scripted_priority = 50; // Priority for scripted objectives // TROOP COMMITMENT double max_matching_force_ratio = 3.0; // Maximum force to commit to objective double min_matching_force_ratio = 1.0; // Minimum force required for objective

Strategic Priority Explanations

threat_priority = 150
- Higher values make AI more aggressive toward enemy concentrations
- Typical range: 100-200

distance_priority = -3
- Negative values prefer closer objectives
- Positive values make AI willing to travel farther
- Typical range: -10 to 10

attack_enemy_base_priority = 75
- How much AI prioritizes attacking enemy buildings
- Higher = more base-focused attacks

persistence_priority = 30
- Prevents AI from constantly switching objectives
- Higher values create more focused, committed behavior

Building and Unit Construction System (BUCS)

The BUCS controls what the AI builds through Accounts - separate budgets for different purposes:

Account Structure
// Define accounts and their budgets UNIT_CONSTRUCTION_PROGRAM unit_construction_program[MAX_ACCOUNT_COUNT]; #DATA // Account Name, Budget Percentage "Essential", UNLIMITED; // Critical buildings only "Base_building", 30; // Base infrastructure "Offensive", 50; // Combat units "Defense", 20; // Defensive structures #END_DATA

Account Elements
// Essential Account - Critical buildings with UNLIMITED budget ACCOUNT_ELEMENT Essential[MAX_ACCOUNT_ELEMENTS]; #DATA // Priority, Item Name, Build Method, Amount 9, "avrecy", NUMBER_TO_HAVE, 1; // Recycler 9, "avcnst", NUMBER_TO_HAVE, 2; // Constructors 8, "abspow", NUMBER_TO_HAVE, 1; // Power Generator #END_DATA // Offensive Account - Combat units ACCOUNT_ELEMENT Offensive[6]; #DATA // Priority, Item Name, Build Method, Amount 6, "avmuf", NUMBER_TO_HAVE, 1; // Factory 5, "avtank", RATIO_TO_BUILD, 2; // Tanks 5, "avscout", RATIO_TO_BUILD, 1; // Scouts 5, "svfigh", RATIO_TO_BUILD, 1; // Fighters #END_DATA

Build Methods Explained

NUMBER_TO_HAVE - Maintain specific count
-
NUMBER_TO_HAVE, 3
= Keep exactly 3 of this unit on map

NUMBER_TO_BUILD - Build specific amount once
-
NUMBER_TO_BUILD, 5
= Build 5 units then stop

RATIO_TO_BUILD - Continuous production in ratios
-
RATIO_TO_BUILD, 2
= Build in 2:1:1 ratio with other RATIO units at same priority

RATIO_TO_HAVE - Maintain ratio, then continue production
- Checks current units, builds to correct ratio, then continues

Practical AI Customization Examples

Defensive AI
// Priorities favor defense and close combat int threat_priority = 200; // Very reactive to threats int distance_priority = -8; // Strongly prefer nearby objectives int defend_buildings_priority = 100; // High building defense priority int attack_enemy_base_priority = 25; // Low offensive priority

Aggressive AI

// Priorities favor offense and expansion int threat_priority = 100; // Moderate threat response int distance_priority = 2; // Willing to travel for objectives int defend_buildings_priority = 15; // Low defensive priority int attack_enemy_base_priority = 150; // High offensive priority int exploration_priority = 100; // Explore aggressively

Turtle AI (Base Builder)
// Focus on building strong base before attacking int recompute_strategy_period = 200; // Think less frequently (more building time) int defend_buildings_priority = 80; // Protect buildings int attack_enemy_base_priority = 30; // Low aggression double min_matching_force_ratio = 2.0; // Require overwhelming force advantage

Setting AI for Your Maps

In Mission Scripts
-- Set AI personality file for team 2 SetAIP("custom_aggressive.aip", 2) At the top of your script: SetAIControl(team#)

In Map Configuration
Reference AIP files in your mission's setup to give computer opponents distinct personalities suited to your map's design and intended difficulty.
AIP Files - Sample
#include "aipdef.h" int VERBOSE_SCHEDULER = 0; // How often do we recompute the strategy? int recompute_strategy_period = 10; // PRIORITIES int escort_priority = 1000; int min_escort_force = 3; int max_escort_force = 3; int ground_unit_threat = 50; //50 int threat_priority = 1000; int distance_priority = -1; // was -1 // How important is distance (?) int defend_buildings_priority = 100; int attack_enemy_base_priority = 3000; //3000; //50 // How often to attack enemy base? int persistence_priority = 400; //1000 // How often to keep the pressure on? int exploration_priority = 100; //800; //50 int scripted_priority = 0; int single_use_group_priority = 30; int perimeter_priority = 400; //50 // TROOP COMMITMENT STUFF double min_matching_force_ratio = 1.0; //0.2; double max_matching_force_ratio = 3.0; //3.0; // When generic troops are called for, what should be ratio of troops that can // shoot ground targets to troops that can shoot air targets? double generic_ground_ratio = 1.0; int min_building_defense_force = 0; int max_building_defense_force = 2; int min_exploration_force = 1; int max_exploration_force = 2; int min_perimeter_force = 1; int max_perimeter_force = 4; int min_attack_enemy_base_force = 5; int max_attack_enemy_base_force = 14; // RELAXATION STUFF int relaxation_cycles = 0; //0; float relaxation_coefficient = 0.5; //.5; // What percentage of the time are we engaged? // Scheduler goals stuff // -1 = unlimited #define MAX_DEFEND_BASE_GOALS 0 // Matches defend_buildings_priority #define MAX_ATTACK_TROOPS_GOALS -1 // Matches attack_enemy_base_priority #define MAX_SEIGE_GOALS 0 // Matches persistence_priority #define MAX_EXPLORATION_GOALS 2 // How many units can be assigned to explore? #define MAX_SCRIPTED_GOALS -1 // How many units can be assigned scripted goals by .bzn (Not used?) #define MAX_PERIMETER_GOALS 2 // How many units can be assigned to patrol outside of base (area-defend) /////////////////////////////////////////////////////////////////////////////// // New Construction Program stuff /////////////////////////////////////////////////////////////////////////////// // The UCP Data // -------------------- // This specifies what type of units to build. UNIT_CONSTRUCTION_PROGRAM unit_construction_program[MAX_UCP_LENGTH]; #DATA // Which Account BUDGET BUDGET CAP //---------------------------------------------------- "Slush", UNLIMITED, UNLIMITED; "Defense", UNLIMITED, UNLIMITED; "Offense", UNLIMITED, UNLIMITED; #END_DATA /////////////////////////////////////////////////////////////////////////////// // ACCOUNT NOTES. // -------------- // For each line item in an account, you must specify a build type from one of // the following... NUMBER_TO_HAVE, NUMBER_TO_BUILD, RATIO_TO_BUILD, or RATIO_TO_HAVE. // // The "RATIO" items can only occur in the lowest priority level of an account. // Also, all line-items with the same priority must have the same build type /////////////////////////////////////////////////////////////////////////////// // Slush // -------------------- // This specifies the baseline super-critical account ACCOUNT Slush[MAX_ACCOUNT_LENGTH]; #DATA // Priority Item Name Build Type Build Amount //-------------------------------------------------------------------- 21, "bvscav", NUMBER_TO_HAVE, 6; 20, "bvmufia", NUMBER_TO_HAVE, 1; 19, "bvcnstia", NUMBER_TO_HAVE, 1; 18, "bvslf", NUMBER_TO_HAVE, 1; #END_DATA /////////////////////////////////////////////////////////////////////////////// // DEFENSE // -------------------- // What sort of offensive units and support structures do we want ACCOUNT Defense[MAX_ACCOUNT_LENGTH]; #DATA // Priority Item Name Build Type Build Amount //-------------------------------------------------------------------- 17, "bvfigh", NUMBER_TO_HAVE, 2; 16, "bvturr", NUMBER_TO_HAVE, 6; // 15, "bvmine", NUMBER_TO_HAVE, 1; #END_DATA /////////////////////////////////////////////////////////////////////////////// // OFFENSE // -------------------- // What sort of offensive units and support structures do we want ACCOUNT Offense[MAX_ACCOUNT_LENGTH]; #DATA // Priority Item Name Build Type Build Amount //-------------------------------------------------------------------- 14, "bvrckt", NUMBER_TO_HAVE, 8; 13, "bvhraz", NUMBER_TO_HAVE, 4; // 8, "bvtank", NUMBER_TO_HAVE, 6; 9, "bvfigh", NUMBER_TO_HAVE, 4; 10, "bvrdev", NUMBER_TO_HAVE, 1; 12, "bvltnk", NUMBER_TO_HAVE, 5; // 10, "bvapc", NUMBER_TO_HAVE, 2; // 7, "bvwalk", NUMBER_TO_HAVE, 1; // 6, "bvartl", NUMBER_TO_HAVE, 1; #END_DATA /////////////////////////////////////////////////////////////////////////////// // FORCE MATCHING // -------------- // Which units do we want to emphasize & target with this aip? // Note that this can be used for BOTH the AI team's unit strengths and // the opponents' FORCE_MATCHING My_Matchings[MAX_FORCE_MATCHING]; #DATA // Unit Name Multiplier //---------------------------------- // "bvfigh", 3.0; // "bvturr", 1.0; "bvscav", 0.2; // "bvtank", 2.5; // "bvscav", 2.5; // "bvrecyia", 3.0; #END_DATA BUILDING_MATCHING My_Building_Matchings[MAX_BUILDINGS]; #DATA // Building Name Multiplier Where to Build //------------------------------------------------------------------ // "bvmufia", 2.00, CENTER_OF_BASE; // "bvrecyia", 2.00, CENTER_OF_BASE; #END_DATA
ODF Parameters Pt 1
Object Definition File (ODF) Class Reference

This comprehensive reference covers all available ODF class parameters for Battlezone 98 Redux modding. Each class inherits from base classes and adds specific functionality for different unit types.

---

Core Unit Classes

AmmoPowerupClass
Purpose: Ammunition pickup items
ammoUp ; Amount of ammunition restored

APCClass
Purpose: Armored Personnel Carriers that deploy soldiers
soldierCount = 1 ; Number of soldiers to deploy soldierDelay = 0.2 ; Time between soldier deployments reloadDelay = 0.02 ; Time between reloading soldiers

CraftClass
Purpose: Base class for all vehicles and movable units

Scanning & Detection:
rangeScan ; Radar scanning range periodScan ; Time between radar scans velocJam ; Velocity threshold for jamming

Pilot Ejection:
fPersonEjectRatio ; Ratio for pilot ejection probability

Voice Messages:
selectWaitMsg ; Message when unit selected and waiting selectGoMsg ; Message when given movement order selectFollowMsg ; Message when ordered to follow selectAttackMsg ; Message when ordered to attack selectPickupMsg ; Message when ordered to pickup selectDropoffMsg ; Message when ordered to drop off selectDeployMsg ; Message when ordered to deploy selectUser1Msg ; Custom user message 1 selectUser2Msg ; Custom user message 2 selectOtherMsg ; Default selection message goMsg ; Message when moving to location goObjectMsg ; Message when moving to object followMsg ; Message when following unit followMeMsg ; Message when ordered to follow player attackMsg ; Message when attacking repairMsg ; Message when repairing reloadMsg ; Message when reloading rescueMsg ; Message when rescuing pilot recycleMsg ; Message when being recycled user1Msg ; Custom user message 1 response user2Msg ; Custom user message 2 response otherMsg ; Default response message deployedMsg ; Message when deployed packedMsg ; Message when packed up killedMsg ; Message when destroyed diedMsg ; Message when pilot dies

WalkerClass
Purpose: Walking mechs with detailed movement physics

Grounded Movement (Run Mode):
alphaDampRun = 8.0 ; Damping strength for orientation alphaTrackRun = 15.0 ; Torque for reaching target orientation pitchPitchRun = 0.25 ; Visual pitch when looking up/down pitchThrustRun = 0.1 ; Visual pitch when moving forward/back rollStrafeRun = 0.1 ; Visual roll when strafing velocForwardRun = 4.0 ; Maximum forward speed velocReverseRun = 3.0 ; Maximum reverse speed velocStrafeRun = 3.0 ; Maximum strafe speed accelThrustRun = 20.0 ; Movement acceleration omegaSpinRun = 3.0 ; Max yaw speed when stationary omegaTurnRun = 2.0 ; Max yaw speed when moving alphaSteerRun = 5.0 ; Turning torque velocJumpRun = 5.0 ; Jump velocity

Airborne Movement (Fly Mode):
alphaDampFly = 2.0 ; Air damping strength alphaTrackFly = 5.0 ; Air orientation tracking pitchPitchFly = 0.35 ; Air pitch response pitchThrustFly = 0.15 ; Air thrust pitch rollStrafeFly = 0.15 ; Air strafe roll velocForwardFly = 15.0 ; Air forward speed velocReverseFly = 10.0 ; Air reverse speed velocStrafeFly = 10.0 ; Air strafe speed accelThrustFly = 5.0 ; Air acceleration omegaSpinFly = 2.0 ; Air spin rate omegaTurnFly = 1.5 ; Air turn rate alphaSteerFly = 2.0 ; Air steering torque velocJumpFly ; Air jump behavior

Audio:
jumpSound ; Sound when jumping landSound ; Sound when landing stepSound ; Sound when walking soundFly ; Sound when vehicle is airborne animRate ; Animation playback rate

---

Building Classes

ArmoryClass
Purpose: Weapon upgrade buildings
cannonItem ; Cannon weapon to provide rocketItem ; Rocket weapon to provide mortarItem ; Mortar weapon to provide specialItem ; Special weapon to provide

BarracksClass
Purpose: Pilot training facilities
pilotHold ; Number of pilots that can be stored pilotClass ; Type of pilot to train

BuildingClass
Purpose: Base class for all structures
soundAmbient ; Ambient sound loop for building

CommTowerClass
Purpose: Communication and radar buildings
rangeScan ; Radar scanning range periodScan ; Time between radar scans

RepairDepotClass
Purpose: Vehicle repair facilities
repairRange = 50.0 ; Range for auto-repair repairDelay = 0.02 ; Time between repair pulses repairAmount = 1.0 ; Health restored per pulse repairSound ; Sound effect during repair

ScrapSiloClass
Purpose: Resource storage buildings
scrapHold ; Maximum scrap storage capacity

ShieldTowerClass
Purpose: Defensive shield generators
shieldBox ; Shield area dimensions objPush ; Force applied to objects objDrag ; Drag applied to objects ordPush ; Force applied to ordnance ordDrag ; Drag applied to ordnance

SupplyDepotClass
Purpose: Ammunition resupply buildings
supplyRange = 50.0 ; Range for auto-supply supplyDelay = 0.02 ; Time between supply pulses supplyAmount = 1.0 ; Ammunition restored per pulse supplySound ; Sound effect during resupply

---

Weapon Classes

BeamClass
Purpose: Continuous beam weapons
segmentRadius ; Thickness of beam segments segmentLength ; Length of each beam segment segmentVariance ; Random variation in segments spriteIndex ; Texture sprite index (legacy)

BoltClass
Purpose: Lightning/electrical weapons
segmentRadius ; Thickness of bolt segments segmentLength ; Length of each bolt segment segmentVariance ; Electrical arc variation spriteIndex ; Texture sprite index (legacy) effectDuration ; How long effect persists

CannonClass
Purpose: Direct-fire projectile weapons
shotDelay = 0.5 ; Time between individual shots salvoCount = 1 ; Number of shots per salvo salvoDelay = 3.0 ; Time between salvos shotVariance = 0.0 ; Accuracy spread (degrees) shotPitch = 0.0 ; Vertical firing angle adjustment

ChargeGunClass
Purpose: Weapons that charge up before firing
shotDelay ; Base time between shots salvoCount ; Shots per salvo salvoDelay ; Time between salvos shotVariance ; Firing spread fireSound ; Sound when firing ordnanceClass ; Projectile type to fire wpnReticle ; Weapon reticle sprite ammoCost ; Ammunition cost per shot startRate ; Initial charge rate deltaRate ; Change in charge rate startVolume ; Initial sound volume deltaVolume ; Volume change during charge ordnanceCount ; Number of projectiles ordnanceData ; Additional projectile data
ODF Parameters Pt 2
DispenserClass
Purpose: Rapid-fire projectile dispensers
shotDelay ; Time between dispensed projectiles

RemoteDetonatorClass
Purpose: Remotely triggered explosive weapons
armedReticle ; Reticle shown when weapon is armed

TargetingGunClass
Purpose: Lock-on guided weapons
leaderSound ; Sound during target acquisition leaderClass ; Targeting beam/laser class shotDelay ; Time between shots firstDelay ; Delay before first shot salvoDelay ; Time between salvos salvoCount ; Shots per salvo shotVariance ; Firing accuracy spread lockingReticle ; Reticle while acquiring target lockedReticle ; Reticle when target locked

---

Projectile Classes

AnchorRocketClass
Purpose: Rockets that anchor/tether to targets
accelDrag ; Drag coefficient during acceleration alphaDrag ; Rotational drag anchorTime ; Duration of anchor effect

DayWreckerClass
Purpose: Devastating explosive projectiles
damageValue ; Base damage amount damageTypes ; Types of damage dealt xplClass ; Explosion class to trigger craterDepth ; Depth of crater created

RocketClass
Purpose: Self-propelled explosive projectiles
flameRadius ; Size of engine flame flameLength ; Length of flame trail flameIndex ; Flame sprite index flameCount ; Number of flame particles flareRadius ; Size of flare effects flareIndex ; Flare sprite index flareCount ; Number of flare particles smokeDevRadial ; Radial smoke deviation smokeDevAxial ; Axial smoke deviation smokeInherit ; Velocity inheritance for smoke smokeRadius ; Size of smoke particles smokePause ; Pause between smoke emissions smokeIndex ; Smoke sprite index smokeCount ; Number of smoke particles smokeRate ; Rate of smoke generation smokeEmitter ; Smoke emitter class

ThermalMissileClass
Purpose: Heat-seeking guided missiles
coneAngle ; Search cone angle for heat targets

TorpedoClass
Purpose: Self-guided terrain-following projectiles
setAltitude ; Flight altitude above terrain alphaTrack ; Target tracking strength alphaDamp ; Orientation damping velocForward ; Forward velocity accelThrust ; Acceleration rate omegaTurn ; Turn rate alphaSteer ; Steering responsiveness lifeSpan ; Maximum flight time damageValue ; Damage on impact damageTypes ; Damage type flags xplBlast ; Explosion class soundThrust ; Engine sound effect

TracerClass
Purpose: Visual bullet trail effects
tracerIndex ; Tracer sprite index shotColor ; Color of tracer lodShift ; Level-of-detail adjustment tracerLength ; Length of tracer trail tracerRadius ; Thickness of tracer

---

Specialized Unit Classes

CameraPodClass
Purpose: Nav Beacons
omegaSpin ; Rotation speed for scanning rangeScan ; Maximum scanning range periodScan ; Time between scan cycles

SAVClass
Purpose: Special Assault Vehicles with flight capability
flightAltitude ; Altitude for flight mode

ScavengerClass
Purpose: Resource collection units
maxScrap = 40 ; Maximum scrap carrying capacity soundPickup ; Sound when collecting scrap deployMsg ; Message when deploying to collect foundMsg ; Message when scrap is found notFoundMsg ; Message when no scrap found noDropMsg ; Message when cannot drop scrap

TeamSwitcherClass
Purpose: Weapon that can change team allegiance
switchTime ; Time on switched team

TugClass
Purpose: Units that can tow/transport other units
dockSpeed ; Speed when docked to another unit

TurretTankClass
Purpose: Deployable turret vehicles
omegaTurret ; Turret rotation speed timeDeploy ; Time to deploy from mobile to turret timeUndeploy ; Time to pack up from turret to mobile

---

Weapon Systems

FlamePuffClass
Purpose: Flame thrower effects
flameRadius ; Size of flame effect frameDelay ; Animation frame delay flameFirst ; First frame of flame animation flameLast ; Last frame of flame animation

SprayBombClass
Purpose: Area-effect spray weapons
sprayClass ; Type of spray effect bounceRatio ; Bounce coefficient on impact soundBounce ; Sound when bouncing

SprayBuildingClass
Purpose: Building-mounted spray weapons
payloadClass ; Type of payload to spray fireSound ; Sound when firing triggerDelay ; Delay before triggering shotDelay ; Time between shots setAltitude ; Firing altitude omegaSpin ; Splinter spin rate anglePitch ; Firing angle

WeaponMineClass
Purpose: Proximity-triggered explosive mines
searchRadius ; Detection range for targets heightScale ; Vertical detection scaling weaponClass ; Weapon to trigger on detection checkSight ; Whether line-of-sight is required

WeaponPowerupClass
Purpose: Weapon upgrade pickups
weaponClass ; Weapon type to provide

---

Advanced Systems

ExplosionClass
Purpose: Explosion effects and damage
frameCount ; Number of animation frames frameRate ; Animation playback rate explRadius ; Visual explosion radius explSound ; Explosion sound effect damageRadius ; Damage effect radius damageValue ; Base damage amount damageTypes ; Damage type flags omegaShake ; Rotational screen shake deltaShake ; Screen shake intensity renderClass ; Visual rendering class particleTypes ; Types of particles particleCount ; Number of particles particleVeloc ; Particle velocity particleBias ; Particle direction bias particleClass ; Particle effect class

QuakeBlastClass
Purpose: Seismic wave attacks
quakeCount ; Number of seismic waves quakeClass ; Seismic wave effect class

RadarLauncherClass
Purpose: Radar-guided projectile launchers
objectClass ; Type of object to launch

Usage Notes

Parameter Types
  • Float values: Most parameters accept decimal numbers
  • Integer values: Counts and indices typically use whole numbers
  • String values: Sound files, class names, and messages use quoted strings
  • Boolean values: Use
    true
    or
    false
    for on/off settings
Lua Scripting Basics Pt 1
Core Engine Behaviors

Important Function Limitations

DeleteObject() Behavior:
DeleteObject() makes all properties of that object inaccessible the moment a unit is flagged as destroyed. The only exception is the handle and objective name.

DisplayMessage and Command Limitations:
DisplayMessage and /Command only work in Multiplayer
• They do NOT work in single player/offline
For debugging: Always use print
Status updates: DisplayMessage is acceptable for multiplayer use

Broken Functions:
iterator ObjectiveObjects() is broken as an iterator and only returns the first result (engine bug)
Recommendation: Do not use this function

Basic Script Structure

Your basic Lua script has six essential components:

-- Mission header local M = { -- Variables in table format to avoid 60-variable limit myrecy = nil, timer1 = 120, event_done = false } function Save() return M end function Load(...) if select('#', ...) > 0 then M = ... end end function PostLoad() -- Called after loading, use for handle restoration end function Start() -- Initialize handles and starting conditions M.myrecy = GetHandle("avrecy0_recycler") SetScrap(1, 30) SetPilot(1, 30) SetAIP("misn05.aip", 2) end function Update() -- Main game logic runs every frame M.player = GetPlayerHandle() -- Always first line -- Event-driven logic here if not M.event_done then if GetTime() >= M.timer1 then -- Do something M.event_done = true end end end

Essential Handle Functions

-- Dynamic handles (refresh automatically) M.player = GetPlayerHandle() M.myrecy = GetRecyclerHandle() M.myfactory = GetFactoryHandle() M.myarmory = GetArmoryHandle() M.mycons = GetConstructorHandle() -- For enemy teams, specify team number M.enemyrecy = GetRecyclerHandle(2) M.enemyfactory = GetFactoryHandle(2) -- Static handles (set once) M.building = GetHandle("mybuilding") M.unit = GetHandle("specific_unit_name")

Object Spawning

-- Spawn single object M.attacker = BuildObject("svtank", 2, "spawn_path") Goto(M.attacker, "attack_path") -- Spawn attack group for i = 1, 4 do M.attacker = BuildObject("svfigh", 2, "spawn_path") Attack(M.attacker, M.player) end -- Random spawning locations local SpawnTable = {"spawn1", "spawn2", "spawn3", "spawn4"} M.unit = BuildObject("svtank", 2, SpawnTable[math.random(#SpawnTable)]) -- Build at specific coordinates local pos = {x = 100, y = 0, z = 200} M.unit = BuildObject("avtank", 1, pos)

Timer Systems

-- Resource timer for AI if GetTime() >= M.resource_timer then AddPilot(2, 25) AddScrap(2, 100) M.resource_timer = M.resource_timer + 300 -- Every 5 minutes end -- Cockpit timer StartCockpitTimer(600, 180, 60) -- 10 min total, yellow at 3 min, red at 1 min if GetCockpitTimer() <= 0 then -- Time's up end StopCockpitTimer() HideCockpitTimer() -- Simple delay timer if not M.delay_started then M.delay_timer = GetTime() + 30 -- 30 second delay M.delay_started = true end if GetTime() >= M.delay_timer and M.delay_started then -- Execute delayed action M.delay_started = false end

Mission Win/Fail Conditions

if not M.missionDone then -- Win condition if not M.win1 and not IsAlive(M.enemy_base) then M.win1 = true ClearObjectives() AddObjective("victory.otf", "GREEN", 10) M.audmsg = AudioMessage("victory.wav") end -- Fail condition if not M.fail1 and not IsAlive(M.myrecy) then M.fail1 = true ClearObjectives() AddObjective("defeat.otf", "RED", 10) M.audmsg = AudioMessage("defeat.wav") end -- Execute endings if M.win1 and IsAudioMessageDone(M.audmsg) then M.missionDone = true SucceedMission(GetTime() + 4, "victory.des") end if M.fail1 and IsAudioMessageDone(M.audmsg) then M.missionDone = true FailMission(GetTime() + 4, "defeat.des") end end

Advanced Scripting Techniques

Camera Cutscenes
-- Trigger cutscene if M.trigger_cutscene then M.camera1 = true CameraReady() M.trigger_cutscene = false end -- Run cutscene if M.camera1 then if CameraPath("cam_path", 1200, 1000, M.target) or CameraCancelled() then M.camera1 = false CameraFinish() end end

Airborne Units
-- Spawn paratroopers local spawnPosition = GetPosition("drop_zone") spawnPosition.y = GetTerrainHeightAndNormal(spawnPosition) spawnPosition.y = spawnPosition.y + 400 -- 400m altitude M.paratrooper = BuildObject("sssold", 2, spawnPosition) Attack(M.paratrooper, M.target)

Walking Snipers
-- Spawn walking soldier, convert to sniper on arrival M.sniper = BuildObject("sssold", 2, "sniper_spawn") Goto(M.sniper, "sniper_path") -- Check for arrival and convert if IsAlive(M.sniper) and not M.sniper_deployed then if GetDistance(M.sniper, "deploy_point") <= 10 then local sniperTransform = GetTransform(M.sniper) RemoveObject(M.sniper) M.sniper = BuildObject("sssnip", 2, sniperTransform) M.sniper_deployed = true end end

Patrolling Units
-- Set up patrol path SetPathLoop("patrol_path") M.patrol_unit = BuildObject("svtank", 2, "patrol_start") Patrol(M.patrol_unit, "patrol_path") -- Check if patrol unit is still alive and reset if needed if not IsAlive(M.patrol_unit) and not M.patrol_respawn_timer then M.patrol_respawn_timer = GetTime() + 60 -- Respawn in 1 minute end if M.patrol_respawn_timer and GetTime() >= M.patrol_respawn_timer then M.patrol_unit = BuildObject("svtank", 2, "patrol_start") Patrol(M.patrol_unit, "patrol_path") M.patrol_respawn_timer = nil end

Day Wreckers
-- Simple spawn explosion M.wrecker = BuildObject("apwrck", 2, "target_path") -- Armory launched (requires special setup) if M.launch_wrecker then if IsAlive(M.enemy_armory) then AddScrap(2, 25) -- Pay for the wrecker BuildAt(M.enemy_armory, "apwrck", "target_path", 0) M.launch_wrecker = false end end

Utility Functions

Distance Checking
-- Check if units are close enough if GetDistance(M.unit1, M.unit2) <= 50 then -- Units are within 50 meters end -- Check if unit reached a path point if GetDistance(M.unit, "waypoint_name") <= 20 then -- Unit has reached the waypoint end

Unit State Checking
-- Check if unit is alive if IsAlive(M.unit) then -- Unit is still active end -- Check if unit is selected if IsSelected(M.unit) then -- Player has selected this unit end -- Get unit health local health = GetHealth(M.unit) if health <= 50 then -- Unit is badly damaged end
Lua Scripting Basics Pt 2
String Handling Issues and Fixes

GetPlayerHandle Team Number Issue:
Passing a team number into GetPlayerHandle does not work properly and will always return nil instead of the remote player's handle.

Workaround: You get around it by collecting handles matched to teams on player join.

String Null Character Problem: Some Battlezone Lua functions return strings with invisible null characters that break string comparisons.

Affected Functions:
  • GetOdf()
  • GetPilotClass()
  • GetWeaponClass()
  • GetClassSig()
  • GetBase()

Solution: Add this code at the top of your Lua script to automatically fix all affected functions:

for _, fname in ipairs({"GetOdf", "GetPilotClass", "GetBase", "GetWeaponClass"}) do local func = _G[fname] _G[fname] = function(...) local success, result = pcall(func, ...) if success then if result then local nullcharpos = result:find("\0") return nullcharpos and result:sub(1, nullcharpos - 1) or result end else error(result:gsub("?", fname), 2) end end end

What this does:
  • Automatically strips null characters (\0) from function returns
  • Maintains proper error handling with correct line numbers
  • Works transparently - no changes needed to existing code
  • Fixes string comparison issues that plague Battlezone modding

Usage: Simply paste this at the top of any mission script and all string comparisons with these functions will work correctly.

Resource Management
-- Check and manage resources local scrap = GetScrap(1) -- Team 1's scrap local pilots = GetPilots(1) -- Team 1's pilots if scrap < 100 then AddScrap(1, 50) -- Give some scrap end if pilots < 10 then AddPilot(1, 5) -- Give some pilots end -- Set specific amounts SetScrap(1, 200) SetPilot(1, 30)
Common Scripting Patterns
Wave-Based Attacks
-- Wave spawning system if not M.wave_active and GetTime() >= M.next_wave_time then M.wave_active = true M.wave_units = {} -- Spawn wave for i = 1, 3 do local unit = BuildObject("svtank", 2, "enemy_spawn") Attack(unit, M.player) table.insert(M.wave_units, unit) end end -- Check if wave is defeated if M.wave_active then local alive_count = 0 for i, unit in ipairs(M.wave_units) do if IsAlive(unit) then alive_count = alive_count + 1 end end if alive_count == 0 then M.wave_active = false M.next_wave_time = GetTime() + 120 -- Next wave in 2 minutes M.wave_units = {} end end
Progressive Difficulty
-- Increase difficulty over time M.difficulty_level = math.floor(GetTime() / 300) + 1 -- Every 5 minutes -- Spawn appropriate units for difficulty local unit_types = {"svfigh", "svtank", "svhowz", "svhowz"} local spawn_type = unit_types[math.min(M.difficulty_level, #unit_types)] if M.should_spawn_enemy then M.enemy_unit = BuildObject(spawn_type, 2, "enemy_spawn") Attack(M.enemy_unit, M.player) M.should_spawn_enemy = false end
Debugging Tips
-- Always use print for debugging (not DisplayMessage in SP) print("Debug: Player health = " .. GetHealth(M.player)) print("Debug: Current time = " .. GetTime()) -- Check if handles are valid if M.unit == nil then print("ERROR: Unit handle is nil!") end -- Track important events if M.debug_mode then print("Event triggered: Enemy base destroyed") print("Player position: " .. GetPosition(M.player).x .. ", " .. GetPosition(M.player).z) end
Custom Campaign
Introduction to Custom Campaigns
Custom campaigns allow you to create connected series of missions that tell a complete story. Unlike standalone maps, campaigns feature mission progression, cutscenes, custom briefings, and persistent narrative elements across multiple levels.
Campaign File Structure
Required Files
Core Campaign Files:

.ini - Campaign configuration file (defines mission order, settings, and metadata)
.bzn - Individual mission files for each level
.bmp - Campaign preview image displayed in custom campaign menu

Optional Enhancement Files:

.ogv - Cutscene videos played after missions
.png/.dds - Custom loading screens for missions
.wav - Custom voice files and audio
Custom world images or videos for planet selection screen

Campaign INI Configuration
The campaign INI file controls the entire campaign structure and presentation:
[DESCRIPTION] missionName = "Your Campaign Name" [WORKSHOP] mapType = "campaign" customtags = "campaign, story, custom" [MISSION1] missionName = "Mission 1 Title" missionBZN = "mission01.bzn" missionBG = "campaign_splash.png" planet = "moon" voice = "briefing01.wav" descColor = "180, 180, 0" gift = "cutscene01.ogv" loadingBG = "loading01.png" [MISSION2] missionName = "Mission 2 Title" missionBZN = "mission02.bzn" missionBG = "campaign_splash.png" planet = "mars" voice = "briefing02.wav" descColor = "195, 195, 0" loadingBG = "loading02.dds"
Mission Configuration Parameters
Required Parameters

missionName - Display name for the mission
missionBZN - Filename of the mission's BZN file
planet - Planet environment (moon, mars, venus, io, europa, titan)
missionBG - Background image for mission briefing

Optional Parameters

voice - Audio file for mission briefing narration
descColor - RGB color values for mission text (format: "R, G, B")
gift - Cutscene video played after mission completion
loadingBG - Custom loading screen image for the mission

Campaign Preview Image Requirements
⚠️ CRITICAL: BMP Image Specifications
The campaign preview image has strict requirements:

Format: BMP (bitmap) format only
Bit Depth: Must be saved as 24-bit
Color Space: Save with "Do not write color space information" option
Failure to follow these specs will crash the game

Image Guidelines:

Use clear, representative artwork for your campaign
Keep text readable at various sizes
Consider the campaign's theme and story
This image is separate from your Steam Workshop .jpg preview

Cutscenes and Video Integration
Video Requirements
Campaign cutscenes use the gift parameter and have strict technical requirements:
Format Specifications:

Container: OGV (Ogg Video) format only
Frame Rate: Exactly 30 FPS
Audio Codec: OggVorbis
Video Codec: Theora (standard for OGV)

Creating OGV Files
Recommended Tools:

FFmpeg (command line)
VLC Media Player (can convert to OGV)

Text and Localization
Text Encoding Requirements
⚠️ IMPORTANT: Use ANSI Encoding

All text files must use ANSI/ASCII encoding
UTF-8 or Unicode encoding will cause wrong character display
Avoid special characters that aren't in standard ASCII
Test all text displays in-game

Briefing Screen Limitations
Text Scrolling Issues:

Keep briefing and debriefing text compact
Long text blocks may break the scrolling system
Text may not display properly if too lengthy
Test scrolling behavior with your content

Custom World Integration
Planet Selection Screen
You can customize the planet selection screen beyond the standard planets:
Custom World Images:

Replace default planet spinning animations
Use custom static images for unique worlds
Follow standard planet image formats

Custom World Videos:

Create custom .ogv videos for planet selection
Same technical requirements as cutscenes (30 FPS, OggVorbis)
Can show animated environments or custom worlds

Example Campaign Structure
Based on the "Rise of the Black Dogs" campaign:
[DESCRIPTION] missionName = "Battlezone: Rise Of The Black Dogs" [WORKSHOP] mapType = "campaign" customtags = "Battlezone: Rise of the Black Dogs, ROTBD" [MISSION1] missionName = "Sniper Training" missionBZN = "rotbdsnp.bzn" missionBG = "rotbdsplash.png" planet = "moon" voice = "rload.wav" descColor = "180, 180, 0" gift = "cutscn01.ogv" loadingBG = "loadpng2.png"
Terrain Textures and Visual Customization Pt 1
Redux Terrain File System

Battlezone 98 Redux uses a modern terrain system with the following file types:

Core Files:
.DDS - Texture atlas containing all terrain textures (DXT1 compression, 1024x1024 or larger)
.CSV - Bridge file mapping texture names to atlas coordinates
.TRN - World configuration file referencing texture names
.material - Material definition linking DDS files

File Naming Convention

MAP File References (in TRN):
The .MAP filename structure determines texture usage:
Planet Code (2 letters): EU, IO, VE, etc.
Transition Code (2 digits):
00 = texture 0 to texture 0 (solid, non-transitional)
01 = texture 0 to texture 1 (transitional)
23 = texture 2 to texture 3 (transitional)
Type Letter:
S = Solid texture
C = Cap (edge transition)
D = Diagonal transition
Variant Letter: A, B, C, D (texture variations)
LOD Number: Always 0 for Redux

Examples:
EU00SA0 = Europa solid texture 0, variant A
EU01CA0 = Europa cap transition from texture 0 to 1, variant A
EU23DA0 = Europa diagonal transition from texture 2 to 3, variant A
EU33SA0 = Europa solid texture 3, variant A (use same digit for solids)

Redux Implementation Process

1. Create Atlas Image
• Arrange texture squares in logical order (sand → dirt → rock)
• Use 2048x2048 or larger canvas
• Export as PNG first, then convert to DDS (DXT1 compression)

2. Generate CSV File
FILENAME,x_coord,y_coord,width,height EU00SA0,0,0,256,256 EU01CA0,256,0,256,256 EU23DA0,512,0,256,256 EU11SA0,768,0,256,256 EU22SA0,0,256,256,256 EU33SA0,256,256,256,256

3. Create Material File
import BZSprite/AlphaHUD from "sprites.material" material terrain_atlas_D : BZSprite/AlphaHUD { set_texture_alias DiffuseMap terrain_atlas.dds }

4. Configure TRN File
• Set atlas name under [Atlases] section
• Reference texture names in [TextureType] sections

[Atlases] atlas_name = terrain_atlas [TextureType0] SolidA0 = EU00SA0.map SolidA1 = EU00SA0.map SolidA2 = EU00SA0.map SolidA3 = EU00SA0.map [TextureType1] SolidA0 = EU11SA0.map CapTo0_A0 = EU01CA0.map DiagonalTo0_A0 = EU01DA0.map

Important: The .MAP files themselves are not needed in Redux - they're just references in the TRN file to coordinates in the CSV file, which point to the atlas image.

Traditional Terrain Texture Tutorial

Tools Required
• Image editing software (Photoshop, GIMP, Paint Shop Pro)
PEdit - Palette editor for ACT/PAL files

Texture Types Overview

Solids: Homogenous textures that border with same-type textures or variants
Caps: Mostly solid with one edge blending to different texture
Diagonals: Equally divided between two textures along diagonal axis
Sky Textures

Custom Sky Texture Checklist

Files:
.MAP File - Original texture reference, not needed in Redux
.DDS File - DXT1 compressed texture (1024x1024, 1 layer, sRGB)
.Material File - References DDS and MAP (use stock ganymede file as template)
TRN File - References the .MAP file
Subfolder Structure - Place in addon with .ini file specifying it's a mod

TRN Sky Configuration

Individual Sky Objects:
[Sky] Texture00 = earth.map Alpha00 = 0 Size00 = 200 ; Object size Azimuth00 = 150 ; Compass heading (1-360, NOT 0) Elevation00 = 10 ; Height in sky (10-35 typical) Roll00 = 300 ; Rotation of texture

Full Sky Coverage:
[Sky] SunTexture = sun.0 SkyType = 0 ; 0=moving sky, 1=stationary SkyHeight = 110 ; Must be reasonable value SkyTexture = venus.map

Color and Palette System

Battlezone Palette Files
Important Indexing Difference:
PEdit: Starts at index 1
BZ Game: Reads from index 0

Conversion Rule: To adjust index #223 in BZ, change #224 in PEdit

ACT Palette Structure
Format: byte [256][3] (color index → R G B)
Name: Read from .TRN [Color] Palette property

Color Index Assignments:
Index #0-95: Objects
Index #96-223: Planet-specific colors
Index #224-255: Objects (secondary range)

Special Color Indices:
Index #223: Controls sky color and sniperscope color
Index #209: Fog color

Important Note: The fog color affects some sky coloring on certain planets, but other color files control the extent of fog color usage for sky rendering. Always use index #223 for sky color in all cases.
Terrain Textures and Visual Customization Pt 2
Autopainter System
The autopainter automatically applies textures based on elevation and slope parameters, eliminating manual texture placement.
Setup Process

Create Shortcut:
Target: "Battlezone.exe" mapname.bzn /startedit
Analyze Terrain:
• Enter terrain editor (Ctrl+E)
• Switch to texture view to identify existing textures
• Switch to height view (Z key) to record elevations
• Note slope angles (estimate visually or calculate)
Create INI File:
Name it uniquely (e.g., mapname.ini or custom_terrain.ini). You can actually use whatever file extension you want here, as long as it uses .ini formatting. So I will often use myworld.paint so it doesn't interfere with mod .ini definition files.

INI File Structure
[Layer0] ; Header - Layer number ElevationStart = 0 ; Minimum elevation for this texture ElevationEnd = 147 ; Maximum elevation for this texture SlopeStart = 0 ; Minimum slope angle (degrees) SlopeEnd = 15 ; Maximum slope angle (degrees) Material = 0 ; Texture number from TRN (0-7) [Layer1] ElevationStart = 0 ElevationEnd = 1000 ; Use high number for "all elevations" SlopeStart = 15 ; Start where previous layer ended SlopeEnd = 90 ; Maximum possible slope Material = 1 [Layer2] ElevationStart = 147 ; Start where elevation ended above ElevationEnd = 1000 SlopeStart = 0 SlopeEnd = 15 Material = 2
Key Principles
Material Numbers:

Correspond to [TextureType] order in TRN file
First texture = Material 0, second = Material 1, etc.
Independent of actual [TextureTypeX] numbers

Coverage Requirements:

All elevations and slopes must be covered
Gaps cause makeTRN to fail with "missing parameters" error
Overlaps cause similar errors
Use consecutive numbers (end one layer where next begins)

Execution
maketrn mapname.trn /p="custom.ini"
Success Indicators:

"Computing materials" message
"Writing out material file" confirmation
No error messages about missing coverage

TRN File Structure
[TextureType0] ; First texture layer FlatColor = 128 SolidA0 = texture_name_A0.map SolidA1 = texture_name_A1.map SolidA2 = texture_name_A2.map SolidA3 = texture_name_A3.map ; Transition to next texture CapTo1_A0 = cap_texture_01_A0.map CapTo1_A1 = cap_texture_01_A1.map CapTo1_A2 = cap_texture_01_A2.map CapTo1_A3 = cap_texture_01_A3.map DiagonalTo1_A0 = diag_texture_01_A0.map DiagonalTo1_A1 = diag_texture_01_A1.map DiagonalTo1_A2 = diag_texture_01_A2.map DiagonalTo1_A3 = diag_texture_01_A3.map
Variant Ratios:
If you list the same variant multiple times in different Solid slots, maketrn will paint them in that ratio:
SolidA0 = texture_varA0.map ; Variant A SolidB0 = texture_varA0.map ; Variant A again SolidC0 = texture_varA0.map ; Variant A third time SolidD0 = texture_varB0.map ; Variant B once
Results in 3:1 ratio of variant A to variant B.
Planet Creation from Existing BZ Format
1. Extract Assets

Copy all .map files to working folder
Include planet.act palette file
Add makemap.exe to folder

2. Convert to Images
makemap -pal planet.act -bmp *.map
Converts all MAP files ending in 0 to BMP format.
3. Create New Arrangements

Use texture files ending in 0 (first mipmap)
Arrange in logical progression
Maintain 256x256 individual texture size

Custom Palette Creation
Color Index Guidelines:

Indices 0-95: Reserved for objects
Indices 96-223: Planet-specific colors
Indices 224-255: Object colors (secondary)
Index 223: Sky and sniperscope color
Index 209: Fog color

Best Practices:

Use common color schemes (browns, reds, etc.)
Reserve more colors for light/medium textures
Use fewer colors for dark textures
Test under different lighting conditions (Time = 900 vs 1200 in TRN)

Sky Texture Creation Tips

Seamless Tiling: For moving skies, apply seamless tiling filter
Alpha Channels: Use for transparency effects in 16/32-bit textures
Size Considerations: Larger textures for better quality, but watch file sizes
Color Matching: Ensure sky color complements terrain palette
Custom Sprites
Working With Custom Sprite Tables

If you're adding new reticles, particle effects, or explosion animations to Battlezone 98 Redux, you can do so by using a custom sprite table file.

TL;DR: Just include a
spritea.sta
file in your mod folder. The game will load it automatically. It defines which sprite names point to what part of which texture, using pixel-based coordinates.

How Sprite Tables Work

Battlezone uses
spritea.st
to define sprite frames used in the HUD, weapon reticles, effects, explosions, etc. Feel free to reference this stock file.

Each sprite entry tells the game:
  • What the sprite is named (referenced in an ODF or effect),
  • Which material it uses (links to a .material file),
  • Where to find it on the texture (start X/Y, width/height),
  • What the full image resolution is (ref width/height),
  • And any flags for special rendering.

Basic Setup Steps
  1. Include a file named
    spritea.sta
    in your mod's root folder.
  2. Create one or more
    .material
    files that reference your texture files.
  3. Use the sprite names in your ODFs or effect definitions (e.g.
    wpnReticle = gacidsta
    ).

Example Sprite Entry

"gacidsta" reticlesheet1 0 0 256 256 1024 1024 0x00000000

This means:
  • gacidsta is the sprite name used in-game (such as in an ODF)
  • reticlesheet1 is the material file (must be named
    reticlesheet1.material
    )
  • Starts at X=0 Y=0 on the image
  • Uses a 256×256 pixel region
  • The full image is 1024×1024 pixels
  • Flags are
    0x00000000
    (no special behavior)

Material File Example

Your
reticlesheet1.material
file should look like this:

import * from "sprites.material" material reticlesheet1 : BZSprite/Additive { set_texture_alias DiffuseMap reticlesheet1.dds }

You can change
blend
and
transparent
depending on the look you want (e.g. for additive glowing effects).

Helpful Notes
  • All coordinates and sizes are in pixels, not normalized UVs.
  • Think of the image like a grid — a 1024×1024 texture divided into 4 rows × 4 columns = 256×256 blocks.
  • Use GIMP, Photoshop, or paint.net to measure pixel positions.
  • You can animate effects by naming frames like
    dust.0
    ,
    dust.1
    , etc.
  • Don’t forget to include your material and texture files with your mod.
Ogre Material Files
Working With Ogre Material Scripts in Battlezone 98 Redux

Battlezone Redux uses Ogre3D material scripts[wiki.ogre3d.org] to define how textures are rendered, including properties like glow, transparency, scrolling, and multi-pass effects.

Material scripts define how a texture looks in-game, including lighting, blending, emissive glow, and animation.

Basic Format

Material files use this general structure:

material your_material_name { technique { pass { // Rendering settings texture_unit { texture your_texture.dds } } } }

Example 1: Scrolling Emissive Texture

This material scrolls a diffuse texture vertically and adds a glowing (emissive) layer:

material shield1_BZBase_fvtank00 { technique { pass { shading phong ambient 0 0 0 1 diffuse 1 1 1 1 specular 0.5 0.5 0.5 25.6 emissive 1 1 1 scene_blend add depth_write off texture_unit { texture cbdata00_D.dds tex_coord_set 0 colour_op modulate scroll_anim 0.0 0.4 rotate 0 } texture_unit emissiveMap { texture_alias EmissiveMap texture cbdata00_D.dds } } } }

Key Features:
  • scroll_anim 0.0 0.4
    → Scrolls the texture vertically over time
  • emissiveMap
    → Adds a glow layer (usually for cockpit lights or effects)
  • scene_blend add
    → Makes the glow additive
  • depth_write off
    → Prevents Z-fighting on effects

Example 2: Double-Sided Transparent Texture

Great for objects like leaves or smoke:

material MyMaterial { technique { pass { cull_hardware none cull_software none depth_write off scene_blend alpha_blend texture_unit { texture my_texture.dds } } } }

Key Features:
  • alpha_blend
    → Uses the alpha channel for transparency
  • cull_hardware none
    → Renders both sides of the mesh
  • depth_write off
    → Optional; helps with layering

This setup is ideal for particle textures, banners, and foliage.

Redux Material Format (BZBase Inheritance)

Battlezone Redux uses inheritance to simplify standard materials:

import * from "BZBase.material" material svapcbed_BZBase_cockpit : BZBase { set_texture_alias DiffuseMap soviet_units_cockpit_D.dds set_texture_alias NormalMap soviet_units_cockpit_N.dds set_texture_alias SpecularMap soviet_units_cockpit_S.dds set_texture_alias EmissiveMap soviet_units_cockpit_E.dds set $diffuse "1.5 1.5 1.5" set $ambient "1.5 1.5 1.5" set $specular "1 1 1" set $shininess "127" set $glow "1 1 1" }

How It Works:
  • import * from "BZBase.material"
    → Inherits from a common template
  • set_texture_alias
    → Replaces the default texture used for each map
  • $diffuse
    ,
    $glow
    , etc. tweak the lighting behavior

Use this format for most standard vehicles and buildings in Redux.

More Info
Full Ogre Material Scripting Reference:
https://wiki.ogre3d.org/Materials
Cubemap Skyboxes for BZR
Custom TRN Cubemaps (Skyboxes) in Battlezone 98 Redux
Updated process for using HDRI, PNG, or JPG equirectangular panoramas

Step-by-Step Guide

Step 1: Get a Panorama
Your image must be either:
  • 2:1 equirectangular (e.g. 4096×2048)
  • 4:1 extended panoramas (e.g. 4096×1024 — see special notes below)

Acceptable formats: .png, .jpg, etc.

Step 2: Convert to Cubemap Faces
Use any standard cubemap generator to convert your 2:1 image into 6 cube faces:
px (Positive X) nx (Negative X) py (Positive Y / top) ny (Negative Y / bottom) pz (Positive Z / front) nz (Negative Z / back)

Tools like “Panorama to Cubemap” or Blender + Bake Cubemap can do this.
Output resolution should be consistent (e.g. 1500×1500 each face).

Step 3: Create Material Files for Each Face
Each .map or .dds texture must be defined in a .material file like this:

material PZ.MAP { technique { pass { vertex_program_ref Sky_vertex {} fragment_program_ref Sky_fragment {} cull_hardware none cull_software none lighting off fog_override true none scene_blend add depth_write off diffuse vertexcolour texture_unit { tex_address_mode clamp texture pz.png } } } }

Repeat for all six sides, naming the materials
PX.MAP
,
NX.MAP
, etc. to match the filenames.

Step 4: Add to .TRN File

Inside your .trn file, add the following section:

[Stars] Radius = 750 Texture00 = px.map Alpha00 = 0 Size00 = 1500 Azimuth00 = 90 Elevation00 = 0 Roll00 = 180 Texture01 = pz.map Alpha01 = 0 Size01 = 1500 Azimuth01 = 0 Elevation01 = 0 Roll01 = 180 Texture02 = py.map Alpha02 = 0 Size02 = 1500 Azimuth02 = 0 Elevation02 = 90 Roll02 = 180 Texture03 = nx.map Alpha03 = 0 Size03 = 1500 Azimuth03 = 270 Elevation03 = 0 Roll03 = 180 Texture04 = nz.map Alpha04 = 0 Size04 = 1500 Azimuth04 = 180 Elevation04 = 0 Roll04 = 180

Adjust:

Size: the width of your cubemap faces

Radius: typically
1/2 of your Size

Azimuth: sets direction for each face (0° = north)

Roll: used to align seams. 180° usually fixes top (PY) alignment

Important: You must put all .map, .material, and .png/.dds files into a subfolder in your
addon
directory for the game to load them properly.

Skyboxes from 4:1 Panoramas

You can convert a 4:1 aspect ratio image into a valid 2:1 panorama using this trick:

  • Open the image in Photoshop, GIMP, or other editor
  • Double the canvas height (make it 4:2) — leave bottom half blank or mirror the top so it blends naturally
  • Export and convert to cubemap as usual

Tips:

Put visible text or a landmark in the empty bottom half — helps you identify which face may be flipped

You may need to flip or rotate some faces manually (e.g. swap pz ↔ nz, rotate side faces by 180°)

Need a Tool?
Try these options:
Audio & Visual Info
Audio Configuration

WAV File Specifications
Format: Microsoft WAVEX
Bit Depth: 8-bit unsigned
Sample Rate: 22050 Hz
Existing Files: 8-bit mono 11025 Hz (system supports other rates/depths)
Music: Only music system can use OGG files

Audio File Placement
• Place WAV files in addon/[yourmod]/ folder
• Reference in ODF files using relative paths
• Test audio files in-game to verify proper format

Common Audio Issues
Sounds not playing: Check file format (must be Microsoft WAVEX)
Audio crackling: Sample rate too high, use 22050 Hz or lower
File too large: Use 8-bit compression for smaller file sizes
Wrong path: Verify file path in ODF matches actual file location

Lighting and Visual Effects

TRN Lighting Parameters

Sunlight Configuration:
[Sun_Ambient] Red = 0.3f Green = 0.3f Blue = 0.3f [Sun_Diffuse] Red = 0.3f Green = 0.3f Blue = 0.3f [Sun_Specular] Red = 0.3f Green = 0.3f Blue = 0.3f

Lighting Tips
Ambient: Overall brightness level, affects shadows
Diffuse: Main light color, affects object coloring
Specular: Highlight color, affects shiny surface reflections
Values: Range from 0.0 (black) to 1.0 (full brightness)
Color balance: Adjust RGB ratios for different moods (red for Mars, blue for ice worlds)

Time of Day Effects
[World] Time = 1200 ; Noon lighting (bright) Time = 600 ; Dawn lighting (dim) Time = 1800 ; Dusk lighting (orange/red) Time = 0 ; Midnight lighting (very dark)

Animation System

Redux Animation Indices
Redux added new animation indices for pilots:
idleParachute: index 9
landParachute: index 10
jump: index 11

Pilot Animation Speeds
Important: Pilot animation speeds are hardcoded in BZR. Looping animations and their speed are hardcoded depending on the name of the animation. Pilots are the only objects that do NOT use .VDF animation, and instead use Ogre's skeletal animation.

Animation File Formats
.skeleton - Bone structure for animated models
.mesh - 3D model with animation data

Custom Animation Tips
• Use existing skeleton files when possible
• Match bone names exactly to original models
• Test animations at different speeds
• Ensure looping animations seamlessly connect end-to-start

Rendering Effects

Available Draw Types in BZ98R
draw_bolt - Lightning/electrical effects
draw_emit - Particle emission effects
draw_light - Light source effects
draw_multi - Multiple layered effects
draw_null - Invisible placeholder
draw_planar - Flat sprite effects
draw_shock - Shockwave/explosion rings
draw_sphere - Spherical effects
draw_sprite - 2D sprite effects
draw_tracer - Bullet trail effects
draw_twirl - Spinning/rotating effects
draw_twirl_trail - Spinning trail effects

Physics Effects

SeismicWaveClass Parameters

[SeismicWaveClass] waveRadius = 50.0 ; Radius of the wave waveHeight = 8.0 ; Height of the wave startOffset = 0.0 ; Forward spawn distance for wave rampUpTime = 5.0 ; Seconds spent ramping up after creation rampDownTime = 2.0 ; Seconds spent ramping down before end sweepOmega = 100.0 ; Torque applied like rug being pulled sweepVeloc = 50.0 ; Force in wave direction and vertically shakeFrequency = 3.0 ; Rapidity of shaking shakeOmega = 2.0 ; Rotational wobbling strength shakeVeloc = 4.0 ; Horizontal shoving strength dampOmega = 0.0 ; Dampens rotational velocity levelOmega = 0.0 ; Torque to level off object orientation walkerScale = 1.0 ; Scales sweepOmega and shakeOmega towerScale = 1.0 ; Scales sweepOmega and shakeOmega buildingScale = 1.0 ; Scales damage

Explosion System

Global Explosion Classes
The game defines several global explosion classes that serve as defaults:

// predefined explosions ExplosionClass *xplBuilding = NULL; // building explosion ExplosionClass *xplVehicle = NULL; // vehicle initial explosion ExplosionClass *xplSecondary = NULL; // secondary explosion ExplosionClass *xplDetonate = NULL; // vehicle final explosion ExplosionClass *xplGround = NULL; // vehicle ground splash

Explosion Usage by Object Type

Building Death:
• Uses ODF explosion class if available
• Otherwise: xplBuilding if radius > 5m, xplVehicle if 2-5m, xplSecondary if ≤ 2m

Craft Death:
• Initial death: uses ODF class or xplVehicle
• Sniped pilots: uses xplSecondary if not set in ODF
• Dying explosions: uses xplSecondary if not set in ODF
• Final explosion: uses xplDetonate if not set in ODF
• Ground splash: uses xplGround

Person:
• Uses ODF class or xplSecondary

PowerUp:
• Uses ODF class or xplVehicle if radius > 3m, xplSecondary if ≤ 3m

Custom Explosion Configuration
[GameObjectClass] explosionName = "initialExplosion" [CraftClass] xplDetonate = "detonateExplosion" xplSecondary = "secondaryExplosion" xplSnipe = "snipedExplosion"


Visual Props and Mesh Editing

Adding Visual Props to Maps

Prerequisites:
• Kenshi IO Continued Blender Plugin
• Blender with basic knowledge
• Ogre Command Line Tools
• VDF/SDF Blender Plugin from Commando

Basic Process:
1. Create base object in Blender
2. Export as SDF
3. Add to map in hidden area
4. Configure BZN file
5. Import terrain for reference
6. Place and arrange props
7. Export final mesh

Mesh Export Settings
• Use Kenshi exporter with: tangents, apply transform, apply modifiers
• Enable "Apply Transform" and "Apply Modifiers"
• Run resulting .mesh through Ogre Mesh Upgrader
• Place final .mesh in mod folder
Custom HUDs & Object Classes
Creating Custom Faction HUDs

If you're working on a custom race/faction and want a fully custom HUD, you'll need the following components:

Requirements

1. Letter Selection
• Choose a unique letter identifier
Cannot use: a, s, b, c, or h (reserved for existing HUDs)
• This letter will be used in the material file naming

2. HUD Image
• Create your custom HUD image (e.g., RaceHUD.png)
• Standard HUD image format and dimensions
• Use existing HUD as template for sizing and layout

3. Material File
• Create a .material file that associates your HUD image to the race letter
Filename: Must match your HUD image filename (e.g., RaceHUD.material)

Material File Template

import BZSprite/AlphaHUD from "sprites.material" material HUDcombX : BZSprite/AlphaHUD { set_texture_alias DiffuseMap RaceHUD.png }

Implementation Steps

1. Choose Race Letter: Select an unused letter (not a, s, b, c, or h)

2. Create Material File:
• Copy the template above
• Replace X in HUDcombX with your chosen race letter
• Replace RaceHUD.png with your actual HUD image filename
• Save as [YourHUDImageName].material

3. Important Notes:
• Keep the import line exactly as shown - it tells the game how to process the HUD
• The material file must have the same base filename as your HUD image
• Remove all comments (lines starting with //) from the final file
• Stock BZ already uses some prefixes, they include i, s, f, b, c, a.

Example Implementation

For a custom race using letter z with HUD image MyCustomHUD.png:

File: MyCustomHUD.material
import BZSprite/AlphaHUD from "sprites.material" material HUDcombz : BZSprite/AlphaHUD { set_texture_alias DiffuseMap MyCustomHUD.png }

HUD Design Guidelines

Image Specifications:
• Use standard HUD dimensions (typically power of 2)
• PNG format recommended for transparency support
• Consider different screen resolutions

Design Elements:
• Radar area (circular or rectangular)
• Weapon display areas
• Health/damage indicators
• Ammunition counters
• Mini-map space
• Status indicators

Color Schemes:
• Match your faction's theme colors
• Ensure good contrast for visibility
• Consider colorblind accessibility
• Test in different lighting conditions

Testing Your Custom HUD

1. Place files in addon folder
2. Set your units to use the custom race letter
3. Test in-game to verify HUD appears correctly
4. Check all HUD elements function properly
5. Test with different screen resolutions

Object Classes and Geo Types

Complete Geo Type List

CLASS_ID_NONE = 0 // Does nothing, part of main object CLASS_ID_HELICOPTER = 1 // Crashes game as both vdf and sdf CLASS_ID_STRUCTURE1 = 2 // "Wooden Structures" - does nothing CLASS_ID_POWERUP = 3 // Crashes game on sdf or vdf CLASS_ID_PERSON = 4 // Unknown, Untested CLASS_ID_SIGN = 5 // Unknown, Untested CLASS_ID_VEHICLE = 6 // Unknown, Untested. CLASS_ID_SCRAP = 7 // Scrap material CLASS_ID_BRIDGE = 8 // Structure containing floor, Untested. Likely does nothing. CLASS_ID_FLOOR = 9 // Floor in a bridge, Untested. Likely does nothing. CLASS_ID_STRUCTURE2 = 10 // Metal Structures, Untested. Likely does nothing. CLASS_ID_SCROUNGE = 11 // Faces that geo toward camera CLASS_ID_SPINNER = 15 // Can be used on VDFs with hex editing, and buildings by standard CLASS_ID_RADAR = 34 // Unknown, Untested. Likely does nothing. CLASS_ID_HEADLIGHT_MASK = 38 // Headlight bone in Redux CLASS_ID_EYEPOINT = 40 // POV CLASS_ID_COM = 42 // Center of mass. Unused. CLASS_ID_WEAPON = 50 // Weapon geometry CLASS_ID_ORDNANCE = 51 // Ordnance geometry CLASS_ID_EXPLOSION = 52 // Explosion geometry CLASS_ID_CHUNK = 53 // Chunk geometry CLASS_ID_SORT_OBJECT = 54 // Sorting object CLASS_ID_NONCOLLIDABLE = 55 // Non-collidable geometry CLASS_ID_VEHICLE_GEOMETRY = 60 // Vehicle geometry CLASS_ID_STRUCTURE_GEOMETRY = 61 // Structure geometry CLASS_ID_WEAPON_GEOMETRY = 63 // Weapon geometry CLASS_ID_ORDNANCE_GEOMETRY = 64 // Ordnance geometry CLASS_ID_TURRET_GEOMETRY = 65 // X/Y turret rotators, gun towers CLASS_ID_ROTOR_GEOMETRY = 66 // Rotating on A or D thrust CLASS_ID_NACELLE_GEOMETRY = 67 // Rotating on steering, W/A/S/D thrust, emits flame from origin CLASS_ID_FIN_GEOMETRY = 68 // Rotating on steering CLASS_ID_COCKPIT_GEOMETRY = 69 // Cockpit geometry CLASS_ID_WEAPON_HARDPOINT = 70 // Asterisk hardpoint, no default powerups. Can be used as a custom hardpoint CLASS_ID_CANNON_HARDPOINT = 71 // Cannon hardpoint CLASS_ID_ROCKET_HARDPOINT = 72 // Rocket hardpoint CLASS_ID_MORTAR_HARDPOINT = 73 // Mortar hardpoint CLASS_ID_SPECIAL_HARDPOINT = 74 // Special hardpoint CLASS_ID_FLAME_EMITTER = 75 // Shows on full forward thrust CLASS_ID_SMOKE_EMITTER = 76 // Smoke emitter CLASS_ID_DUST_EMITTER = 77 // Dust emitter CLASS_ID_PARKING_LOT = 81 // Used as center of effect for hangars/supply pads.
Game Class Parameters & Physics
APCClass Parameters

The APCClass controls how Armored Personnel Carriers deploy and manage soldiers.

[APCClass] soldierType = ; REQUIRED - ODF name of soldiers to deploy soldierCount = 1 ; Number of soldiers deployed from APC soldierDelay = 0.2 ; Time between soldier deployments reloadRange = 50.0 ; Distance from reload point to start reloading reloadDelay = 0.02 ; Delay between each soldier reloading reloadReset = 5.0 ; Time before APC can reload after last soldier deployed

APCClass Usage Tips
soldierType must reference a valid soldier ODF file
• Higher soldierCount allows more deployments before reloading
• Lower soldierDelay creates faster deployment waves
reloadRange determines how close APC must be to reload points
• Adjust reloadReset to balance deployment frequency

PersonClass Physics Parameters

PersonClass controls how pilots and soldiers move and behave.

Grounded (Not Crouched)
[PersonClass] alphaDampRun = 8.0 ; Damping strength to target orientation alphaTrackRun = 15.0 ; Torque strength to reach target orientation pitchPitchRun = 0.25 ; Pitch amount when looking up/down pitchThrustRun = 0.1 ; Pitch amount when running forward/backward rollStrafeRun = 0.1 ; Roll amount when strafing velocForwardRun = 4.0 ; Max forward speed (doubled for turbo) velocReverseRun = 3.0 ; Max backward speed velocStrafeRun = 3.0 ; Max sideways speed accelThrustRun = 20.0 ; Acceleration when moving omegaSpinRun = 3.0 ; Max yaw angular velocity when not thrusting omegaTurnRun = 2.0 ; Max yaw angular velocity when thrusting alphaSteerRun = 5.0 ; Yaw torque velocJumpRun = 5.0 ; Upward jump velocity

Grounded and Crouched (Sniper Position)
alphaDampCrawl = 5.0 alphaTrackCrawl = 10.0 pitchPitchCrawl = 0.2 pitchThrustCrawl = 0.1 rollStrafeCrawl = 0.1 velocForwardCrawl = 0.0 ; No movement while crouched velocReverseCrawl = 0.0 velocStrafeCrawl = 0.0 accelThrustCrawl = 20.0 omegaSpinCrawl = 0.5 ; Reduced turning speed omegaTurnCrawl = 0.5 alphaSteerCrawl = 5.0 velocJumpCrawl = 0.0 ; Cannot jump while crouched

In Air (Upright or Crouched)
alphaDampFly = 2.0 alphaTrackFly = 5.0 pitchPitchFly = 0.35 pitchThrustFly = 0.15 rollStrafeFly = 0.15 velocForwardFly = 15.0 ; Higher air speeds velocReverseFly = 10.0 velocStrafeFly = 10.0 accelThrustFly = 5.0 ; Reduced air acceleration omegaSpinFly = 2.0 omegaTurnFly = 1.5 alphaSteerFly = 2.0 velocJumpFly = ; Unknown - no double jumping

PersonClass Parameter Explanations

Movement Control:
velocForward/Reverse/Strafe: Maximum movement speeds in each direction
accelThrust: How quickly the unit accelerates to max speed
velocJump: Upward velocity applied when jumping

Orientation Control:
alphaDamp: How quickly rotation dampens to prevent overshooting
alphaTrack: Torque strength for reaching target orientation
alphaSteer: Yaw torque for turning left/right

Visual Effects:
pitchPitch: Visual pitch when looking up/down
pitchThrust: Visual pitch when moving forward/backward
rollStrafe: Visual roll when strafing left/right

Turning:
omegaSpin: Max yaw speed when standing still
omegaTurn: Max yaw speed when moving

Physics and Movement Formulas

Battlezone 1998 Turbo/Drag Formula
The true speed calculation for any unit:

v_max = min(2 * velocForward, sqrt(accelThrust / coeffDrag))

This formula gives you the actual maximum velocity a unit can achieve, taking into account both the turbo multiplier and drag coefficient limitations.

Formula Breakdown
2 * velocForward: Turbo speed (double normal max speed)
sqrt(accelThrust / coeffDrag): Physics-limited max speed
min(): The actual top speed is whichever is lower

Practical Applications
• Increase accelThrust for faster acceleration AND higher top speed
• Decrease coeffDrag for higher top speed without affecting acceleration
velocForward sets the turbo speed limit
• Balance all three for desired vehicle feel
Weapon Systems Overview Pt1
How to Craft the Ideal Weapon in Battlezone - Complete Redux Edition

Based on the original comprehensive guide by système, updated with Redux-specific parameters

Introduction

Thus far, we have seen how to make a map, how to texture it, and how to import a customized model into the game. Yet, unless we want to make a utility unit or ram into the enemies until they explode, we have yet one fundamental element to craft: a weapon. Battlezone offers a plethora of assets to assist units in combat, no matter what their category is. These assets can range from the deadliest weapon to the friendliest helper. But can we choose with what we must equip our loyal steed? That is up to you. However, it is down to us to figure out precisely what every piece of our arsenal is capable of achieving.




Part 1: Weapon Classes

Class 1: Cannon

Cannon is the most basic and versatile weapon class. You press the left button and an ordnance is summoned. Any ordnance can be launched by a cannon. AI is very skilled with cannons as they can aim and fire continuously. Used for AT-Stabber, SP-Stabber, Blast Cannon, and many more.

Redux Enhancement: Cannons now support advanced parameters for burst fire and accuracy control.

[WeaponClass] classLabel = "cannon" ordName = "anchor" wpnName = "Sandbag" wpnCategory = "ROCK" [CannonClass] shotDelay = 1.5 shotVariance = 0.05 # Redux: Accuracy (0.0 = perfect) salvoCount = 1 # Redux: Shots per trigger salvoDelay = 0.0 # Redux: Time between salvo shots shotPitch = 0 # Redux: Vertical aim adjustment (undocumented)

Class 2: Machinegun

Machinegun's main difference is it plays firing sound continuously while firing, stopping when you release the trigger. Perfect for rapid-fire weapons like the Rave Gun with long audio files.

[WeaponClass] classLabel = "machinegun" # Loops audio continuously wpnCategory = "MORT" # Redux: Can use cannon parameters

Class 3: Beamgun

For continuous beam weapons like the Flash Cannon. Does not require shotDelay and calculates damage per second automatically. Only works with "beam" ordnance.

[WeaponClass] classLabel = "beamgun" ordName = "beam"

Class 4: Chargegun

Allows multiple ordnances with the same weapon. Different ordnances fire based on how long you hold the trigger (like the MAG Cannon). Each charge level has different ordName.

[WeaponClass] classLabel = "chargegun" ordName = "flame1" # First charge level ordName2 = "flame2" # Second charge level

Class 5: Mortar

Calculates trajectory arcs for indirect fire. Can hit targets behind cover using ballistic paths.

[WeaponClass] classLabel = "mortar" wpnLadder = "gmortar.L0" # Trajectory indicator [CannonClass] # Uses CannonClass parameters shotDelay = 7.0 shotVariance = 0.1 # Redux: Works with mortars salvoCount = 5 # Redux: Multiple shots

Class 6: Detonator

For manually detonated weapons like the MDM Mortar. Fire to launch, fire again to detonate all active projectiles.

[WeaponClass] classLabel = "detonator" ordName = "bouncebm" [RemoteDetonatorClass] armedReticle = "gmdmgun.1" # Crosshair when ready to detonate

Class 7: Poppergun

Special mortar that deploys a secondary ordnance when it detects enemies. Complex weapon requiring fireAngle for proper arc calculation.

Class 8: Targeting

Tag-then-salvo system like the TAG Cannon. First shot tags target, subsequent shots fire guided missiles at the tagged location.

[WeaponClass] classLabel = "targeting" ordName = "tag" [TargetingClass] salvoCount = 12 # Missiles in salvo lockingReticle = "locking.0" lockedReticle = "locked.0"

Class 9: Launchers

Three types for direct missile guidance without tagging:

  • imagelauncher: Uses visual targeting (Shadower Missile)
  • thermallauncher: Tracks heat signatures (Hornet Missile)
  • launcher: Basic missile guidance (unused in stock game)

[LauncherClass] lockRange = 200.0 # Maximum lock distance shotDelay = 1.0 # Lock time delay coneAngle = 0.8 # Thermal launcher only targetCount = 3 # Reticle sprites during lock

Class 10: Radarlauncher

Launches objects (not ordnance) that seek radar signatures. Used by Comet Cruise Missile to deploy tracking objects.

Class 11: Dispenser

Spawns any valid physical object in the game - mines, torpedoes, even units! Objects spawn with no initial velocity and may get stuck in the caster.

[WeaponClass] classLabel = "dispenser" [DispenserClass] shotDelay = 1.0 objectClass = "proxmin" # Object to spawn (can be anything!)

Class 12: SpecialItem

Does nothing but drain ammo automatically. Used for special effects that require possession indication without projectiles.

[SpecialItemClass] ammoCost = 10 # Ammo per second triggerDelay = 2.0 # On/off delay activeSound = "active.wav" expireSound = "expire.wav" reticleCount = 2 # Multiple crosshairs

Utility Classes
  • terrainexpose: SITE Camera terrain scanning
  • radardamper: RED Field radar jamming
  • imagerefract: Phantom VIR cloaking



Part 2: Ordnance Classes

Class 1: FlamePuff

Simplest ordnance - displays flame sprites in sequence. Limited versatility, sprites don't loop.

[OrdnanceClass] classLabel = "flamepuff" [FlamePuffClass] flameRadius = 28 flameDelay = 0.1 flameTexture = "bpuff.0" flameFrames = 1

Class 2: Tracer

Single sprite projectile with adjustable length and radius. Simple but effective for bullets and small projectiles.

[TracerClass] tracerTexture = "trail.0" tracerLength = 17 tracerRadius = 3 shotColor = "255 255 255"

Class 3: Rocket

Most versatile projectile with flame trail, flare effects, and smoke. Combines features of other classes.

[OrdnanceClass] classLabel = "rocket" [RocketClass] flameRadius = 0.15 # Flame trail size flameLength = 1.5 # Trail length flameTexture = "exhaust_r.0" flameFrames = 4 flareRadius = 1.0 # Lens flare effect smokeRadius = 0.3 # Smoke trail smokeDelay = 0.08 smokeTexture = "rsmoke.0" smokeFrames = 16

Class 4: Bullet

Basic projectile used by cannon-type weapons like AT-Stabber. Simple and efficient.

Class 5: Grenade

Ballistic projectile affected by gravity. Shot speed determines initial velocity - higher speed = longer range but requires precise timing.

[OrdnanceClass] classLabel = "grenade" shotSpeed = 50.0 # Initial throw velocity damageConcussion = 500 # Blast damage [RocketClass] # Can use rocket effects smokeRadius = 0.4 smokeTexture = "smoke.0"

Class 6: BounceBomb

Bounces off surfaces and can be manually detonated. Used with detonator weapons. Only ordnance that explodes at end of lifespan.

Class 7: SprayBomb

Splits into multiple sub-munitions upon impact or expiration. Each fragment has its own trajectory and damage.

Class 8: Bolt

Instant-hit beam weapon. Creates segmented beam between firing point and target/maximum range.

[OrdnanceClass] classLabel = "bolt" [BoltClass] segmentLength = 5.0 # Length of each beam segment segmentRadius = 0.5 # Beam thickness segmentVariance = "0.5 0.5 0.5" # Spiral effect segmentTexture = "bolt.0"

Class 9: Beam

Continuous damage beam for beamgun weapons. Damage applied per second rather than per hit.
Weapon Systems Overview Pt2
Class 10: Missile Types
Basic Missile: Guided projectile with turning capability
[MissileClass] omegaTurn = 1.5 # Turn rate shotAccel = 75.0 # Acceleration
ImageMissile: Tracks visual signature of target
ThermalMissile: Follows heat signature within cone angle
Class 11: Popper
Deploys secondary ordnance when enemies enter scan range. Complex behavior with multiple phases.
[PopperClass] scanRange = 100.0 # Detection sphere radius launchXpl = "xpopfir" # Launch explosion launchOrd = "poproc" # Secondary ordnance
Class 12: Seismic
Creates earthquake effects and ground-based damage patterns. Used by Thumper and Day Wrecker.


Part 3: Visual Effects System
Render System Overview
[Render] renderBase = "draw_multi" renderCount = 6 renderName1 = "weapon.Light" renderName2 = "weapon.Trail" renderName3 = "weapon.Smoke" renderName4 = "weapon.Sparks" renderName5 = "weapon.Flash" renderName6 = "weapon.Glow"
Available Render Bases
  • draw_null: No visual effect
  • draw_bolt: Lightning zigzag trail
  • draw_emit: Particle emitter
  • draw_light: Dynamic light source
  • draw_multi: Combines multiple renders
  • draw_planar: Ground-projected texture
  • draw_sphere: 3D sphere effect
  • draw_sprite: 2D billboard
  • draw_tracer: Straight line trail
  • draw_trail: Projectile trail
  • draw_twirl: Rotating sprite
  • draw_twirl_trail: Trail of rotating sprites
Simulate Bases (Physics)
  • sim_null: Stationary particles
  • sim_chunk: Bouncing debris
  • sim_dust: Ground-spawned particles
  • sim_ember: Falling sparks
  • sim_spray: Collision-aware particles
  • sim_smoke: Free-floating smoke


Part 4: Explosions
[ExplosionClass] classLabel = "explosion" explSound = "xbombf.wav" damageRadius = 15.0 damageBallistic = 0 damageConcussion = 100 omegaShake = 5.0 particleCount = 8 particleClass1 = "explosion.flash" particleClass2 = "explosion.smoke" particleClass3 = "explosion.debris" ... up to particleClass16 For each particle type: particleVeloc1 = "10.0 5.0 10.0" # Velocity variance particleBias1 = "0.0 8.0 0.0" # Base velocity particleInherit1 = "0.5 0.5 0.5" # Inherited velocity % particlePosVar1 = "2.0 2.0 2.0" # Position variance


Part 5: Critical Limitations
8-Character Filename Restrictions
  • Ordnance filenames:
    atstab_c.odf

  • shotGeometry:
    iostba00.geo

  • shotSound:
    weapon01.wav

  • xplGround/Vehicle/Building names:
    xstabgnd
NOT Limited to 8 Characters
  • Render names:
    atstab_c.BlueFlameTrail
  • Texture names: Any length
  • Material names: Any length
Header Restrictions
  • Only ONE main header per file (e.g., one [ExplosionClass])
  • Each render section needs unique names within file
  • Headers like [Light], [Smoke] must be unique in same file


Part 6: Redux Advanced Features
Salvo System Examples
Shotgun Effect:
[CannonClass] salvoCount = 8 # 8 pellets salvoDelay = 0.0 # All at once shotVariance = 0.35 # Wide spread
Burst Fire:
[CannonClass] salvoCount = 3 # 3-round burst salvoDelay = 0.1 # 0.1s between shots shotVariance = 0.02 # Tight grouping
Audio Looping Trick
Use machinegun with mortar category for looped audio + advanced parameters:
[WeaponClass] classLabel = "machinegun" # Continuous audio wpnCategory = "MORT" # Enables cannon parameters [CannonClass] shotVariance = 0.1 # Now available! salvoCount = 2


Quick Reference
Redux Parameters
shotDelay = 1.0 # Time between shots/bursts shotVariance = 0.05 # Accuracy (0.0 = perfect) salvoCount = 1 # Shots per trigger pull salvoDelay = 0.0 # Time between salvo shots shotPitch = 0 # Vertical aim offset (undocumented)
Deep Dive: Splinters
Complete Guide to Splinter Bombs in Battlezone

Splinters are extremely useful for modding purposes, and their usefulness is about as unlimited as your imagination.

Splinter bombs are complex dual-component weapons consisting of a bouncing ordnance (spraybomb) that transforms into a stationary turret (spraybuilding) upon landing. Here's the complete ODF structure and how to modify it:

[OrdnanceClass] classLabel = "spraybomb" shotGeometry = "spintbm.geo" shotSound = "ordin_c.wav" xplGround = "gasxpl" xplVehicle = "gasxpl" xplBuilding = "gasxpl" ammoCost = 100 lifeSpan = 1e30 shotSpeed = 30.0 damageBallistic = 0 damageConcussion = 500 damageFlame = 0 damageImpact = 0 [RocketClass] smokeRadius = 0.4 smokeDelay = 0.05 smokePause = 0.01 smokeTexture = "ismoke.0" smokeFrames = 15 [GameObjectClass] scrapCost = 0 scrapValue = 0 maxHealth = 150 maxHeat = 0 maxAmmo = 100 unitName = "Splinter" [SprayBuildingClass] payloadName = "gblblb" jumpSound = "gspl101.wav" fireSound = "gspl104.wav" triggerDelay = 1.0 shotDelay = 0.01 setAltitude = 20.0 omegaSpin = 10.0 anglePitch = 0.25

Key Parameters Explained

Hidden/Special Parameters:
  • Bounceratio = x - Controls bouncing behavior (0 = instant stop, 0.7 = default, 1+ = infinite bouncing)
  • soundBounce = "filename.wav" - Sound when hitting ground (use NULL for silence)

Spraybomb (Mortar) Parameters:
  • shotGeometry/shotSound - Model and sound of the flying projectile
  • xplGround/Vehicle/Building - Explosion effects for different impact types
  • ammoCost - Ammo consumed per shot (negative values give ammo back)
  • lifeSpan - Set to 1e30 for effective immortality
  • shotSpeed - Initial projectile velocity (affected by gravity simulation)
  • damage[Ballistic/Concussion/Flame/Impact] - Damage values (negative = healing)
  • Rocket parameters - flame, frame, smoke effects with texture, radius, and frame count

SprayBuilding (Turret) Parameters:
  • payloadName - ODF of ordnance to fire (e.g., "splinter", "rocket")
  • triggerDelay - Delay before firing starts (seconds)
  • shotDelay - Time between individual shots (seconds)
  • setAltitude - Height above ground (0 to infinity)
  • omegaSpin - Rotation speed (0 = single direction, negative = reverse)
  • anglePitch - Vertical firing angle (0 = flat disk, high values = sphere)
  • maxAmmo - Total shots before self-destruction
  • maxHeat - Heat resistance (useful against hornets)

Critical Multiplayer Warning

IMPORTANT: Splinter effectiveness multiplies by player count in multiplayer! Each client spawns its own ordnances, so 2 players = 2x damage, 3 players = 3x damage, etc. Avoid self-replicating splinters as they create exponentially increasing lag.

Single-Shot Solution: Set ordnance ammoCost equal to spraybuilding maxAmmo to ensure exactly one shot per splinter.

Advanced Tips

  • SprayBuildings can spawn ANY building type, not just ordnances - making them versatile for creative modding
  • Unlike flares/mines, SprayBuildings are "forgettable objects" that can't be tracked via Lua but won't be auto-removed
  • They're virtually immortal - health reaching 0 doesn't destroy them, only running out of ammo does
  • Set lifeSpan to 1e30 (exceeds solar system lifespan) for practical immortality
  • Use negative damage values to create healing ordnances
  • Splinters rarely trigger XplBuilding events; they use XplGround instead when stuck on buildings

Master the splinter, sow chaos among your enemies!
Deep Dive: QuakeBlastClass
The Complete Guide to Quake Blasts in Battlezone Modding

What are Quake Blasts?
Quake Blasts are special explosions that spawn additional ordnance, or effects when they detonate. Perfect for cluster munitions, swarm missiles, fragmentation effects, and chain reactions.

Basic Setup
Every quake blast needs two sections:
[ExplosionClass] classLabel = "quakeblast" // Standard explosion properties [QuakeBlastClass] quakeCount = X // How many things to spawn quakeClass = "name" // What to spawn (≤8 characters!) quakeMagnitude = X // Magnitude parameter, not needed quakeTime = X // Delay before spawning, not needed/used

Critical Rules: ODF filename and quakeClass value must be 8 characters or less!

Parameters Explained

quakeCount - Number to spawn (1-50+, watch performance)
quakeClass - Which ODF file to spawn (must exist, ≤8 chars)

Examples

Basic Cluster Missile
[ExplosionClass] classLabel = "quakeblast" explSound = "cluster.wav" damageRadius = 20.0 damageConcussion = 100 omegaShake = 15 [QuakeBlastClass] quakeCount = 6 quakeClass = "submini" quakeMagnitude = 0 quakeTime = 0.1

Swarm System
[ExplosionClass] classLabel = "quakeblast" explSound = "swarmexp.wav" damageRadius = 15.0 damageConcussion = 50 [QuakeBlastClass] quakeCount = 12 quakeClass = "swrmlet" // Heat-seeking submunitions quakeMagnitude = 0 quakeTime = 0

Nuclear Radiation
[ExplosionClass] classLabel = "quakeblast" explSound = "nuke.wav" damageRadius = 150.0 damageFlame = 2000 omegaShake = 100 [QuakeBlastClass] quakeCount = 12 quakeClass = "nukradi" // Persistent radiation quakeMagnitude = 50 quakeTime = 0.2

Advanced Techniques

Chain Reactions - Have spawned ordnance also be quake blasts
Directional Effects - Use different quakeClass values for patterns
Timed Sequences - Stagger with different quakeTime values
Mixed Spawning - Combine missiles, flames, debris, etc.

What Can Be Spawned
Basically anything that a typical weapon in a tank can fire. Not objects!
  • Missiles (heat-seeking, guided, dumb-fire)
  • Grenades (bouncing, proximity, timed)
  • Flames (persistent fire effects)
  • Radiation (area denial)
  • Seismic effects (ground-shaking)
  • Debris (visual fragments)

Performance & Safety

Optimization:
  • Avoid infinite loops (quake blast spawning itself)
  • Limit cascade depth
  • Use efficient textures
  • Balance visuals vs performance

Common Mistakes
  • ❌ Names >8 characters → ✅ Keep ≤8 chars
  • ❌ Missing referenced ODF files → ✅ Verify files exist
  • ❌ Too many spawns → ✅ Start small, scale up
  • ❌ Infinite loops → ✅ Linear progression A→B→C

Testing Process
  • Start with quakeCount = 1
  • Verify spawned ordnance works independently
  • Gradually increase count
  • Monitor performance impact
  • Refine timing and effects

Integration
Connect to weapons via ordnance impact explosions:
[OrdnanceClass] xplGround = "yourquake" // Ground impact xplVehicle = "yourquake" // Vehicle impact xplBuilding = "yourquake" // Building impact

Creative Applications
  • Weapons: Cluster bombs, MIRV missiles, shotgun spreads
  • Area Denial: Minefields, radiation zones, flame barriers
  • Visuals: Fireworks, debris clouds, energy cascades

Final Tips
  • Start by modifying existing stock quake blasts
  • Use clear, memorable 8-character names
  • Always backup files before experimenting
  • Test thoroughly in different scenarios
  • Share creations with the community

Quake blasts unlock incredible weapon design possibilities in Battlezone. From simple clusters to complex multi-stage systems, they're essential for spectacular ordnance. Experiment safely and create devastating new battlefield weapons!

Remember: Always backup your files first!
Deep Dive: Popper Class
The Complete Guide to Poppers in Battlezone Modding

What are Poppers?
Poppers are intelligent ordnance that act as proximity sensors, automatically launching other ordnance when enemies come within range. They're the Swiss Army knife of Battlezone modding - perfect for smart mines, auto-targeting systems, DOT effects, flak bursts, and complex multi-stage weapons.

Basic Setup
Every popper needs these two sections:
[OrdnanceClass] classLabel = "popper" // Standard ordnance properties [PopperClass] scanRange = X // Detection radius in meters launchXpl = "name" // Explosion when target found (≤8 chars) launchOrd = "name" // Ordnance to spawn (≤8 chars)

Key Rules: All ODF filenames must be 8 characters or less!

PopperClass Parameters

scanRange - Detection radius in meters (10-500+ range)
launchXpl - Explosion effect when target detected (can be NULL)
launchOrd - Which ordnance to fire at target (≤8 characters)

Popper Applications

1. Proximity Mines
// proxmin.odf - Basic proximity mine [OrdnanceClass] classLabel = "popper" lifeSpan = 30 // Stays active for 30 seconds shotSpeed = 0 // Stationary damageConcussion = 50 // Damage if directly hit [PopperClass] scanRange = 15 // 15m trigger radius launchXpl = "xminexp" // Big explosion launchOrd = NULL // No additional ordnance

2. Auto-Targeting Turret
// autotur.odf - Fires bullets at nearby enemies [OrdnanceClass] classLabel = "popper" lifeSpan = 10 shotSpeed = 0 [PopperClass] scanRange = 100 // Long-range detection launchXpl = "xmuzflsh" // Muzzle flash effect launchOrd = "autobul" // Fires bullets at target

3. DOT (Damage Over Time) System
// aciddot.odf - Multi-stage acid damage [OrdnanceClass] classLabel = "popper" lifeSpan = 2 shotSpeed = 0.5 damageFlame = 15 // Initial damage [PopperClass] scanRange = 30 launchXpl = "xaciddt2" // Acid splash launchOrd = "aciddmg2" // Next DOT stage

4. Smart Flak Burst
// flakpop.odf - Explodes near aircraft [OrdnanceClass] classLabel = "popper" lifeSpan = 0.5 // Brief window shotSpeed = 0 [PopperClass] scanRange = 10 // Close proximity trigger launchXpl = "xflak3" // Shrapnel explosion launchOrd = "flakbul3" // Fires minigun rounds

5. Tesla Chain System
// teslapop.odf - Lightning weapon [OrdnanceClass] classLabel = "popper" lifeSpan = 3 shotSpeed = 20 damageFlame = 25 [PopperClass] scanRange = 50 // Medium range launchXpl = "testrig" // Lightning flash launchOrd = "tesbolt" // Lightning bolt

6. Freeze Field
// crypopr.odf - Cryo weapon popper [OrdnanceClass] classLabel = "popper" lifeSpan = 0.3 shotSpeed = 0 [PopperClass] scanRange = 25 launchXpl = NULL // No flash launchOrd = "cryfreez" // Freeze projectile

Advanced Techniques

Chain DOT Effects
Create multi-stage damage by chaining poppers:
Stage 1 → Stage 2 → Stage 3 → Final Damage

Area Denial Fields
Multiple poppers create overlapping danger zones:
  • Radiation fields with periodic damage
  • Cryo zones that slow enemies

Staged Explosions
Combine with short lifeSpan for timed effects:
lifeSpan = 0.5 // Half-second delay scanRange = 5 // Very close range xplExpire = "explosionWhenitExpires" // for further chaining capability

Creative Applications

Defensive Systems
  • Perimeter Mines: Standard proximity explosives
  • Smart Barriers: Activate defenses when breached
  • Automated Turrets: Fire at approaching enemies
  • Warning Systems: Alert effects when area entered

Offensive Weapons
  • Guided Warheads: Home in on nearby targets
  • Flak Cannons: Explode near aircraft
  • Cluster Bombs: Release submunitions over targets

Performance & Tips

Performance Considerations:
  • Use appropriate lifeSpan to limit active time
  • NULL explosions save rendering overhead

Design Guidelines:
  • Match scanRange to intended use
  • Short lifeSpan for temporary effects
  • Long lifeSpan for persistent fields
  • Consider shotSpeed for mobile poppers

Common Popper Types

Stationary (shotSpeed = 0):
  • Proximity mines
  • Area denial fields
  • Defensive turrets
  • Environmental triggers

Mobile (shotSpeed > 0):
  • Guided warheads
  • Seeking clusters
  • Patrol systems
  • Roving sensors

Timed (short lifeSpan):
  • Delayed explosions
  • Staged effects
  • Flash-bang systems
  • Temporary shields

Persistent (long lifeSpan):
  • Radiation zones
  • Monitoring systems
  • Base defenses

Integration Examples

With Weapons:
// In weapon's ordnance file xplGround = "proxpop" // Deploys proximity popper xplVehicle = "dotpop" // Starts DOT sequence

With Quake Blasts:
// Quake blast spawns multiple poppers [QuakeBlastClass] quakeCount = 8 quakeClass = "minepop" // Deploys minefield

Chain Reactions:
Weapon → Popper1 → Popper2 → Popper3 → Final Effect

Troubleshooting

Popper Not Triggering:
  • Check scanRange isn't too small
  • Verify lifeSpan gives enough time
  • Ensure launchOrd file exists
  • Test with visible render effects

Performance Issues:
  • Reduce scanRange values
  • Limit number of active poppers
  • Use shorter lifeSpan values
  • Remove unnecessary explosions

Final Tips
  • Start with simple proximity mines before complex systems
  • Use clear, descriptive 8-character names
  • Test scanRange values thoroughly
  • Combine with other systems for maximum effect
  • Document complex chain reactions

Poppers are incredibly versatile tools that add intelligence to your weapons. From simple proximity mines to complex multi-stage DOT systems, they bring tactical depth and strategic gameplay to Battlezone. Experiment with different combinations to create unique and memorable battlefield experiences!

Remember: Poppers detect enemy units in range and only trigger on those units, not friendlies.
Example Complex Weapon: Incendiary Stabber
A Complete Modding Guide Using the Incendiary Stabber Example

Understanding DOT Weapons

DOT (Damage Over Time) weapons in Battlezone work by creating a chain reaction of explosions and ordnance that continue to damage targets long after the initial impact. Unlike simple weapons that deal damage once, DOT weapons use Battlezone's QuakeBlast system to spawn additional ordnance that can bounce, persist, and explode multiple times.

Key Components:
  • Primary Weapon: The initial weapon that fires the ordnance
  • Primary Ordnance: The projectile that hits the target
  • Chain Explosions: QuakeBlast explosions that spawn secondary ordnance
  • Secondary Ordnance: Persistent damage dealers (napalm, fire puffs, etc.)
  • Final Effects: Long-lasting area denial or continuous damage




The DOT Chain System

The DOT system works through cascading file references:

Weapon ODF → Ordnance ODF → Explosion ODF → Secondary Ordnance → Secondary Explosion → etc.

Critical Rules:
  • All ordnance files must be 8 characters or less
  • Explosion names (xplGround, xplVehicle, etc.) must be 8 characters or less
  • Only one Header per file (e.g., one
    [ExplosionClass]
    per explosion file)
  • Render names can be longer than 8 characters




The Incendiary Stabber Example

Let's trace through the complete gfirstab (Incendiary Stabber) weapon chain to understand how DOT weapons work:

1. gfirstab.odf - The Weapon
[WeaponClass] classLabel = "cannon" ordName = "firestab" # Points to firestab.odf wpnName = "Incendiary Stabber" fireSound = "firestab.wav" wpnReticle = "gfirstab" wpnCategory = "CANN" wpnPriority = 5 [CannonClass] shotDelay = 0.9

2. firestab.odf - Primary Ordnance
[OrdnanceClass] classLabel = "rocket" shotGeometry = "firestab.mesh" shotSound = "firefly.wav" renderName = "firestab.render" xplGround = "xflmveh" # ALL explosions point to xflmveh xplVehicle = "xflmveh" xplBuilding = "xflmveh" xplExpire = "" ammoCost = 15 lifeSpan = 1.3 shotSpeed = 130.0 damageBallistic = 0 damageConcussion = 0 damageFlame = 75 # Initial flame damage damageImpact = 0 [Render] renderBase = "draw_multi" # Multiple visual effects renderCount = 8 renderName1 = "firestab.Light" renderName2 = "firestab.FlameTrail" # ... more render effects

3. xflmveh.odf - Primary Explosion (QuakeBlast)
[ExplosionClass] classLabel = "quakeblast" # KEY: This enables spawning secondary ordnance explSound = "flamexp.wav" damageRadius = 10 damageBallistic = 0 damageConcussion = 0 damageFlame = 150 # Additional flame damage damageImpact = 0 omegaShake = 15 # Particle effects for visual explosion particleTypes = 7 particleClass1 = "xflmveh.light" # ... more particle effects [QuakeBlastClass] # THE MAGIC HAPPENS HERE quakeCount = 2 # Spawn 2 instances quakeClass = "napalm" # Spawn napalm.odf ordnance quakeMagnitude = 0 quakeTime = 0 # Spawn immediately

4. napalm.odf - Secondary Ordnance (Falling Fire)
[OrdnanceClass] classLabel = "grenade" # Uses grenade physics for arcing downward shotGeometry = "" shotSound = NULL xplGround = "xfirebst" # Different explosion when expires xplVehicle = "xfirebst" xplBuilding = "xfirebst" xplExpire = "xfirebst" ammoCost = 0 # No additional ammo cost shotSpeed = 15 lifeSpan = 4 # Exists for 4 seconds damageFlame = 300 # High flame damage per hit renderName = "napalm.render" # Fire trail effects while bouncing

5. xfirebst.odf - Secondary Explosion
[ExplosionClass] classLabel = "quakeblast" # Another QuakeBlast for more spawning explSound = "burn2.wav" damageRadius = 6 damageBallistic = 0 damageConcussion = 0 damageFlame = 15 damageImpact = 0 particleTypes = 4 # Fire and smoke effects [QuakeBlastClass] quakeCount = 1 # Spawn 1 instance quakeClass = "burnlast" # Spawn burnlast.odf quakeMagnitude = 0 quakeTime = 0

6. burnlast.odf - Final DOT Effect
[OrdnanceClass] classLabel = "flamepuff" # Special flame puff class - this will create a ring of fire projectiles traveling along the ground outwards, simulating burning shotGeometry = NULL shotSound = "fire2.wav" xplGround = NULL xplVehicle = "xburnlst" # Final explosion when it hits something xplBuilding = "xburnlst" renderName = "burnlast.render" # Continuous fire visual effects ammoCost = 0 lifeSpan = 10 # Lasts 10 seconds! shotSpeed = 1 # Moves slowly damageBallistic = 0 damageConcussion = 0 damageFlame = 200 # High continuous flame damage damageImpact = 0 heatSignature = 100.0 # Shows up on thermal




QuakeBlast System

The QuakeBlast system is the core of DOT weapons. It allows explosions to spawn additional ordnance:

QuakeBlast Properties:
[ExplosionClass] classLabel = "quakeblast" # REQUIRED for spawning ordnance [QuakeBlastClass] quakeCount = X # How many ordnance to spawn (1-20+ typical) quakeClass = "filename" # Which ordnance ODF to spawn (8 chars max) quakeMagnitude = 0 # Usually 0 for DOT weapons quakeTime = 0.0 # Delay before spawning (0 = immediate)

This is a small example, for more effective DOT effects and with enough patience, you could have a 10+ stage of poppers, quakeblasts, and more but it gets ever more complicated to manage as you add stages.
Advanced Modding Techniques
Advanced VDF Editing

Adding Spinner to VDF

⚠️ WARNING: This involves hex editing VDF files. Always backup your files before editing.

Requirements:
• HxD Hex Editor or similar
• VDF file with target geometry and a dummy geometry

Process:

1. Setup Dummy Geometry:
• In your VDF, add a dummy geo directly after the geo you want to spin
• Example: Main geo = ixx11bar, dummy geo = ixx11bat
• The dummy geo will not be used in the model/game

2. Locate and Modify Object Class:
• Open VDF in HxD hex editor
• Find first entry for your main geo (e.g., ixx11bar)
• Locate dummy geo name after it
• Find object class byte (3C/0F) - it's 8 bytes to the left of dummy geo's name
Note: Parent name appears between main geo and dummy geo
• If exported as standard geo type 60, it will show as 3C
• Change 3C to 0F (which equals 15 - spinner geo type)

3. Configure Spin Axes:
• Overwrite first 4 bytes of dummy geo name
• Next 3 groups of 4 bytes control spin axes
X-axis spin: Change first group to 1
Z-axis spin: Change last group (useful for minigun barrels)
• Spinner rotates around vector's axis with angular speed equal to vector's magnitude in radians per second

4. Finalize:
• Save VDF file
• Test in game

Scaling Inside VDF (Making Jets Bigger)

Requirements:
• HxD Hex Editor
• Target VDF file

Process:

1. Locate Target Geometry:
• Open VDF in HxD
• Find geo name to scale (e.g., 1aa11je1 for jet 1)

2. Understand Transform Structure:
• Bytes after geo name control transform matrix
• Every 4 bytes = one component:
Bytes 1-4: right_x
Bytes 5-8: right_y
Bytes 9-12: right_z
Bytes 13-16: up_x
Bytes 17-20: up_y
Bytes 21-24: up_z
Bytes 25-28: front_x
Bytes 29-32: front_y
Bytes 33-36: front_z
Bytes 37-40: posit_x
Bytes 41-44: posit_y
Bytes 45-48: posit_z

3. Modify Scale Values:
• Transform data spans from geo name to parent name below it
Example: To make jet longer along vehicle length: change right_z (3rd group of 4 bytes)
Example: To make jet wider: change up_x
• Enter values as single float with little endian byte order

4. Testing Process:
• Count by groups of 4 bytes after geo name
• Change desired values
• Save file
• Boot game and test appearance
• Repeat adjustments as needed

Note: You can scale any geo like flames above, some give effects, others might not. Hardpoint scaling increases the knockback of the weapon fired out of them, and scaling production unit GS1 hardpoints can launch units out faster.

Tips:
• Different vehicles may require different axis adjustments
• Start with small changes and test incrementally
• Keep backups of working configurations

Multiplayer Scripting Behaviors

⚠️ Note: The following behaviors apply only to Multiplayer scripting.

Local vs Network Effects

1. MakeExplosion() - Fully Local
• 100% local effect (beneficial for environmental effects)
• Enables: water splashing, rain effects, fog interaction, visual speed lines
• Even damage-causing explosions won't harm opponents
• Only damages local objects

2. SetVelocity() - Fully Synced
• Completely synchronized across all players
• Can create speed/jump pads and teleporters for DM maps
• Velocity multipliers (e.g., ×12) visible to all players

Object Creation and Visibility

3. Building Objects via Lua
• Objects created by host player are visible to all players
• Objects created by non-host players are not visible to others
• Non-host vehicles won't function (can't fire or lay mines)

4. Object Removal
• Deleting non-local objects causes them to "respawn"
Proper method: Use SetLocal() first, then remove
Result: Object disappears for initiating player, explodes for observers
No scrap is left behind
To avoid explosions: Move object far away before deletion

AI and Control Issues

5. SetLocal() and AI Units
• Using SetLocal() on non-controlled AI units breaks their AI completely
• Opposing player's machine must build objects they control directly
• Buildings don't require this restriction

5.1. AI Control Workaround
• After SetLocal(): kill pilot with Lua
• Spawn new pilot on top of unit
• Gives proper control to target player

Position and Property Synchronization

6. SetPosition() on Non-Local Objects
• Behaves identically to deletion attempts
• Same SetLocal() rules apply

7. SetName() Synchronization
• Only affects the executing machine
Not visible to other players

8. Object Hiding
• Requires SetLocal() for proper function
Both machines must execute command for true hiding
Partial hiding effects:
• Vehicle invisible to both, but AI behaves as if visible for non-executing player
• Buildings remain visible but targetable with radar signature
Note: SetLocal() not required for syncing controlling player's state
• Smart reticule still functions on hidden objects

9. SetTeam() Requirements
• Requires SetLocal() to affect other players' units

Network Performance Considerations

10. Building vs Vehicle Network Evaluation
• Buildings evaluate less frequently on network than vehicles
For frame-by-frame movement: Use vehicles for better synchronization

Player Respawn Detection

11. Multiplayer Respawn Detection
Method: Monitor CreateObject() callback
Logic: If player object created at spawn point = player death/respawn
• Only applicable to multiplayer scenarios

Unit Transfer Between Players

12. Handing Off Units
No smooth transfer method exists
Process:
1. Record all vehicle properties
2. Delete original object
3. Message target player to recreate unit
4. Target player builds identical unit on their team
Recommendation: Use MakeExplosion() effect on both sides for visual continuity

Objective and Display Synchronization

13. Objective Markers
SetObjectiveOn(h) and similar functions not synced by default

14. Non-Host Unit Building
• Vehicle AI only works for the building player
Required: Use SetLocal() after every build
SetLocal() forces non-host vehicles to sync properly

15. DisplayMessage Variable Display
• Often fails unless values converted to string
• Shows <null> instead of variable values
Solution: Always convert variables to strings before display

16. Save/Load Functions
Never used or needed in multiplayer-only maps

Advanced Mesh Editing
Editing BZ98R Meshes
Steps for Mesh Editing:
1. Import and Prepare:

Import pilot mesh (e.g., aspilo.mesh) using Kenshi IO Continued
Import new mesh data
Name UV map of new mesh same as existing pilot mesh UV map

2. Mesh Integration:

Position and scale new mesh next to pilot mesh
Join meshes, leaving space to select geometry separately
In edit mode: select vertex group, delete faces, assign new geometry to same vertex group

3. Adding Objects with Materials:

Join meshes and assign to vertex group
Separate vertices by material
Clear all modifier/vertex group data when copying from other objects
Copy only mesh/material data, nothing else

4. Export Settings:

Use Kenshi exporter with: tangents, apply transform, apply modifiers, export skeleton, export animation
Select entire pilot/gun when exporting
Run through OgreMeshUpgrader
Change skeleton reference to aspilo.skeleton unless using custom animations
Custom Worlds
Custom Planet Creation
Modern Redux Pipeline
Atlas Creation Process:
1. Extract Original Textures:
makemap -pal planet.act -bmp *.map
Converts all MAP files ending in 0 to BMP format.
2. Arrange Texture Atlas:

Create 2048x2048 (or larger) canvas
Arrange texture squares logically: sand → dirt → rock
Use first square as bright color to spot errors
Ensure no background showing between textures

3. Export Atlas:

Save as PNG first for editing
Export to DDS using GIMP:
• Compression: DXT1
• Generate Mipmaps: Yes
• Format: sRGB

CSV Bridge File
Maps texture names to atlas coordinates:
FILENAME,x_coordinate,y_coordinate,x_size,y_size ca00sa0,0,0,256,256 ca01ca0,256,0,256,256 ca01da0,512,0,256,256 ca11sa0,768,0,256,256 ca22sa0,0,256,256,256 ca33sa0,256,256,256,256
Naming Convention:

First 2 letters: Planet identifier
00/01: Texture relationship (00=same, 01=transition 0→1)
S/C/D: Solid/Cap/Diagonal
A/B/C: Variation type
Last digit: Mipmap level (always 0 for Redux)

Material File Creation
import BZSprite/AlphaHUD from "sprites.material" material planet_atlas_D : BZSprite/AlphaHUD { set_texture_alias DiffuseMap planet_atlas.dds }
Additional Texture Types:

_N.dds - Normal maps (use SmartNormal2.0 tool)
_S.dds - Specular maps (desaturate and invert for reflections)
_E.dds - Emissive maps (black for non-glow, color for glow)
Converting Heightmaps
---

Understanding Battlezone Heightmaps

What are Heightmaps?
Heightmaps in Battlezone are grayscale data files that define the terrain elevation across your map. Each pixel represents a height value, with:
  • Black (0): Lowest elevation
  • White (255): Highest elevation
  • Gray values: Intermediate elevations

File Formats
  • .hg2: Battlezone Redux heightmap format
  • .hgt: Original Battlezone 98 heightmap format (legacy)
  • .png/.bmp: Standard image formats for editing

---

Heightmap Conversion Tool

Required Tool
Heightmap-to-Image Converter

Installation
  • Download the latest release from GitHub
  • Extract to a convenient folder

---

Image Size Calculation

Size Formula
Critical: Your heightmap image size must match this exact formula:

Image Size = (Map Size ÷ 10) × 2

Common Map Size Conversions
Map Size
Calculation
Required Image Size
1280×1280
(1280 ÷ 10) × 2 = 256
256×256 pixels
2560×2560
(2560 ÷ 10) × 2 = 512
512×512 pixels
3840×3840
(3840 ÷ 10) × 2 = 768
768×768 pixels
5120×5120
(5120 ÷ 10) × 2 = 1024
1024×1024 pixels
6400×6400
(6400 ÷ 10) × 2 = 1280
1280×1280 pixels

Why This Size Matters
  • Incorrect size: Will cause conversion errors or map corruption
  • Exact match required: Battlezone expects precise heightmap dimensions
  • Square images only: Width and height must be identical

---


---

Importing Heightmaps (Image → Battlezone)

Step 1: Prepare Your Image
Image Requirements:
  • Format: PNG or BMP (PNG recommended)
  • Color Mode: Grayscale (8-bit)
  • Size: Must match exact formula above
  • Content: Black = low, white = high elevation

External Editing Workflow

Recommended Software
  • GIMP: Free, powerful tools for heightmap editing
  • Photoshop: Professional editing with terrain brushes
  • World Machine: Procedural terrain generation
  • L3DT: Specialized terrain editing software
Development Tools & Publishing
Development Tools & Workshop Publishing

Development Tools and Launch Options

BZ 1.5 Launch Options
console ; Enable console startedit ; Start in edit mode edit ; Edit mode showreg ; Show registration netdbg ; Network debugging nods3d ; Disable 3D sound nods ; Disable DirectSound forcefeed ; Force feedback nointro ; Skip intro nobodyhome :%d ; No body home with parameter largemap :%d,%d ; Large map with dimensions shellmap ; Shell map binarysave ; Binary save format asciisave ; ASCII save format resave ; Resave files flagfile: ; Flag file specification gamesetup ; Game setup noshell ; No shell SW ; Software rendering norawinput ; Disable raw input rawinput ; Enable raw input win ; Windows mode gdi ; GDI rendering comm ; Communications server ; Server mode transport ; Transport layer players ; Player settings deathmatch ; Deathmatch mode load ; Load game odf ; ODF mode join ; Join game net ; Network mode netshell ; Network shell soviet ; Soviet faction

BZ Redux Launch Options
console ; Enable console startedit ; Start in edit mode edit ; Edit mode saveafterload ; Save after loading exitafterload ; Exit after loading nointro ; Skip intro nobodyhome :%d ; No body home with parameter netpktlog ; Network packet logging noiorecord ; Disable IO recording iorecord ; Enable IO recording nickname= ; Set nickname connect-galaxy-lobby ; Connect to galaxy lobby connect_lobby ; Connect to lobby showunloc ; Show unlocked content develop ; Development mode (enables drawcoll and drawguns) platform: ; Platform specification renderer= ; Renderer specification (alternative syntax) renderer: ; Renderer specification bzr ; BZR mode cp ; Checkpoint net= ; Network specification (alternative syntax) net: ; Network specification flagfile: ; Flag file specification largemap :%d,%d ; Large map with dimensions shellmap ; Shell map binarysave ; Binary save format asciisave ; ASCII save format resave ; Resave files osx ; macOS specific platform= ; Platform specification (alternative syntax) zixlogindex ; Zix log index datadir= ; Data directory specification nohgtsmoothing ; Disable height smoothing nonetlog ; Disable network logging netlog= ; Network log specification netlog ; Enable network logging nobzrnetlog ; Disable BZR network logging bzrnetlog= ; BZR network log specification bzrnetlog ; Enable BZR network logging nonetpktlog ; Disable network packet logging bzrnetport= ; BZR network port specification bzrserver= ; BZR server specification ipdirect ; Direct IP connection iprelay ; IP relay connection disablemods ; Disable modifications disablerenderselection ; Disable render selection enablerenderselection ; Enable render selection dohgtsmoothing ; Enable height smoothing

Essential Launch Options for Modders

For Map Development:
console - Enable console for debugging
startedit - Launch directly into map editor
shellmap - Generate preview images automatically

For Testing:
nointro - Skip intro videos for faster testing
asciisave - Save files in readable ASCII format
nobodyhome - Disable certain AI behaviors for testing

Setting Launch Options in Steam

1. Right-click Battlezone 98 Redux in Steam Library
2. Select "Properties"
3. In "General" tab, find "Launch Options"
4. Enter desired options (e.g., console startedit develop)
5. Click OK and launch game

Special Development Features

The "develop" Flag:
• Enables drawcoll (collision visualization)
• Enables drawguns (weapon visualization)

Steam Workshop Upload

Standard Workshop Upload Process

Prerequisites:
1. All mod files in same folder
2. Square preview image (200x200+ pixels, .jpg format, same filename as map)
3. Configuration .ini file
4. Tested and working mod

Steps:
1. Open Steam Library → Tools
2. Launch "Battlezone 98 Redux - Uploader Tool"
3. Enter mod name and description
4. Click "Create New Item"
5. Select mod folder and preview image
6. Upload to Workshop

Workshop .ini Configuration

For Maps:
[DESCRIPTION] missionName = "Your Map Name" [WORKSHOP] mapType = "multiplayer" ; Options: "instant_action", "multiplayer", "mod" customtags = "strategy, 4-player, desert" [MULTIPLAYER] minPlayers = "2" maxPlayers = "4" gameType = "S" ; Options: D=Deathmatch, S=Strategy, K=KOTH, M=MPI, A=MPA

For General Mods:
[DESCRIPTION] modName = "My Custom Mod" description = "Detailed description of mod features" [WORKSHOP] mapType = "mod" customtags = "units, weapons, graphics" [MOD] version = "1.0" author = "Your Name" requires = "bzr" ; BZR or BZCC

Bypassing BZR/BZCC Uploaders with SteamCMD

⚠️ WARNING: Only bypass uploader if extensively tested. Could crash games or require reinstallation.

When to Use SteamCMD:
• Official uploader isn't working
• Need more control over upload process
• Uploading from automated build systems
• Beta testing private uploads

SteamCMD Process:
1. Download and run SteamCMD in its own folder
2. Place mod files in separate folder
3. Create upload.vdf file with proper configuration
4. Create preview image in mod directory
5. Login to SteamCMD with credentials
6. Execute: workshop_build_item "path\to\your\mod\upload.vdf"

Example upload.vdf:
"workshopitem" { "appid" "301650" "publishedfileid" "0" "contentfolder" "C:\MyMod\files" "previewfile" "C:\MyMod\preview.jpg" "visibility" "2" "title" "My Custom Mod" "description" "Detailed mod description with features and credits" "changenote" "Initial Release" "tags" "maps,multiplayer,strategy" }

SteamCMD Configuration Notes:
publishedfileid: Set to 0 for new items, use existing ID to update
visibility: 0 = public, 1 = friends only, 2 = hidden (recommended during development)
appid: Use 301650 for BZR, change for BZCC if needed
tags: Comma-separated list of relevant tags
• Leave out fields to not add/update them on the item
BZR Model Info Overview
Battlezone 98 Redux General Model Formats

Overview

Battlezone 98 Redux utilizes a dual-model system where vehicles and structures have 2 different model files, a VDF/SDF that defines the collision, weapon placement, and animation, and an OGRE Mesh that defines the visual appearance of the object.

Dual-Component Model System

Battlezone 98 Redux uses a sophisticated dual-component system separating physics/gameplay logic from visual rendering:

Physics/Legacy Component

VDF Files (Vehicle Definition Files)
  • Purpose: Defines physics properties, collision detection, weapon hardpoints, and animation behavior for vehicles
  • File Extension: .vdf
  • Content: Vehicle-specific physics properties, collision meshes, hardpoint locations, animation sequences

SDF Files (Structure Definition Files)
  • Purpose: Defines physics properties, collision detection, and animation behavior for structures
  • File Extension: .sdf
  • Content: Structure-specific physics properties, collision meshes, effect locations, animation sequences

GEO Files (Geometry Components)
  • Purpose: Individual components of a VDF or SDF file
  • File Extension: .geo
  • Function: Define individual movable/animated parts within vehicles and structures

Visual/Animation Component

OGRE Mesh Files
  • Purpose: Defines the visual appearance and rendering geometry
  • File Extension: .mesh
  • Engine: OGRE Xalafu 1.10
  • Content: Vertices, faces, normals, UV coordinates, skinweights, tangents, vertex colors
  • Tools: Utilities exist for modifying Mesh files here: Link[www.ogre3d.org]

OGRE Skeleton Files
  • Purpose: Defines bone hierarchy and animations for visual components
  • File Extension: .skeleton
  • Critical Detail: Skeleton files use bones named the same as geos with the same orientation/pivot points so they behave exactly as the non-rendered geos do (e.g., a wing on a tank is actually following the invisible geo pivoting)
  • Content: Bone hierarchy, animation sequences, bone transformations

Material Files
  • Purpose: Define surface properties and texture assignments
  • Content: Material definitions, texture references, shader properties
  • Associated: Texture files for visual appearance

Asset Archive System

ZFS Files
Battlezone 98 Redux stores most legacy game assets in ZFS (compressed archive) files:

Main Archives:
  • bzone.zfs - Contains models, textures, sound effects, and ODF files
  • bzone_en.zfs - Contains all the voiceovers (specifically the english ones)

Compression: Uses LZO compression

Extraction Tools

MakeZFS (Recommended)
  • Location: Located in the Edit folder of your install
  • Usage: Command-line tool accessed via terminal/command prompt
  • Command: MakeZFS x bzone.zfs (to extract)

File Structure and Locations

Game Directory Structure
Battlezone 98 Redux/ ├── Edit/ # Contains MakeZFS.exe and other tools ├── BZ_ASSETS/common │ ├── materials/ # Material files │ ├── textures/ # Textures for models | └── models/ # OGRE Mesh/Skeleton Files ├── addon/ # Custom mod files location ├── bzone.zfs # Main game assets archive, includes VDF/SDF/Geo Files └── bzone_en.zfs # Localized audio assets

Asset Organization
  • Files under BZ_ASSETS are used for appearance in game
  • VDFs and GEOs are still required, and are used for physics, collision and are also referenced in the .skeleton files

Modding Tools and Compatibility

3D Modeling Software

3ds Max
  • Compatibility: Tools exist for older versions (Max 2016 and earlier)
  • Plugins: Legacy plugins available but require porting to newer versions

Blender
  • Status: There are up to date tools to handle both Ogre Mesh and VDF/SDF with Blender. See VDF/SDF Tool Section.

Command Line Tools
  • OGRE CLI Tools : Command line tools to convert Ogre files to XML or back, or upgrade Meshes

Technical Specifications

OGRE Engine Details
  • Engine Version: OGRE Xalafu 1.10
  • Mesh Version Compatibility: MUST use Ogre Command Line Tools 1.11.6 for BZR compatibility

Critical Component Relationship
Skeleton-GEO Synchronization: The most important technical aspect of BZR's model system is that skeleton bones must be named identically to their corresponding GEO components and maintain the same orientation and pivot points. This ensures that:

  • Visual animations perfectly match physics behavior
  • Animated parts (turrets, wings, etc.) move in sync between visual and physics systems
  • Collision detection remains accurate during animations

File Dependencies
Model files in Battlezone 98 Redux are interconnected:
  • VDF/SDF files contain GEO components for physics behavior
  • Skeleton files must mirror GEO naming and pivot structure
  • OGRE Mesh files provide visual geometry linked to skeleton bones
  • Material files define surface properties for mesh rendering
  • ODF (Object Definition Files) coordinate both visual and physics properties
Adding models to BZR Overview
High level, basic workflow I've used and recommend for max efficiency:
  • Create your model
  • Import it to Blender
  • Make sure you have the VDF/SDF Blender Addon by Commando950 installed and enabled (link in final section of this guide)
  • Export the model as VDF (vehicle), SDF (building) or GEO (single model component, like for shotGeometry)
  • Drag that VDF, SDF or Geo onto the BZR Model Porter python script (link in final section of this guide)
  • You now should have the legacy required files (VDF,SDF, or Geo) and the Redux files (Mesh, skeleton, DDS, material). Now just create an ODF, and you can add it to the game. Ensure your material file points to the correct texture names.
  • NOTE: Commando950's plugin by default exports the model with incorrect normals for Battlezone 98 Redux. You can fix it with Ogre Command Line Tools (link in last section) and a python script.

Ogre CLI Tools Documentation

Overview

The Ogre CLI tools are essential utilities for post-processing Battlezone 98 Redux models after they've been converted with port_models.py. These tools specifically address vertex normal issues that can cause lighting problems in-game, ensuring your models look correct under all lighting conditions.

Tool Set

Primary Tools
  • OgreXMLConverter.exe - Official Ogre tool for converting between .mesh and .xml formats
  • recalculate_normals.py - Custom Python script for recalculating vertex normals
  • ogre_normal_batch.bat - Automated batch processing script

Requirements
  • Python 3.x - For running recalculate_normals.py
  • Ogre Command Line Tools 1.11.6 - Critical for BZR compatibility
  • Windows Command Prompt - For running batch scripts

When to Use These Tools

Use the Ogre CLI tools when your converted models have:
  • Incorrect lighting - Models appear too dark or bright in certain areas
  • Shading artifacts - Strange shadows or lighting discontinuities
  • Faceted appearance - Models look angular when they should be smooth
  • Normal vector issues - Detected during model validation

Workflow Integration

Complete Model Conversion Process
1. Use port_models.py to convert vdf/sdf/odf → mesh/skeleton/material 2. Use Ogre CLI tools to fix normals in .mesh files 3. Deploy all files to your mod folder

recalculate_normals.py

Purpose
A specialized Python script that recalculates vertex normals from face geometry data in Ogre mesh XML files. This fixes lighting issues by ensuring normals point in the correct directions.

Usage
python recalculate_normals.py <mesh.xml file>

How It Works
  • Parses Ogre mesh XML files
  • Calculates face normals using cross products of triangle edges
  • Accumulates and averages normals for each vertex
  • Normalizes vectors to unit length
  • Updates the XML with corrected normal data

Example Usage
python recalculate_normals.py mytank.mesh.xml

Output
Processing: mytank.mesh.xml Processing submesh 1... Updated 1247 normals from 582 faces Successfully recalculated normals for 1247 vertices Normal recalculation completed successfully

Technical Details
  • Algorithm: Face-weighted normal averaging
  • Precision: 6 decimal places for normal coordinates
  • Validation: Handles degenerate triangles and invalid indices
  • Fallback: Uses (0,1,0) up vector for vertices without faces

ogre_normal_batch.bat

Purpose
Automated batch processing script that processes all .mesh files in a directory, handling the complete workflow from mesh conversion to normal recalculation.

Features
  • Automatic backup - Creates backup/ directory with original files
  • Batch processing - Handles all .mesh files in current directory
  • Error handling - Continues processing even if individual files fail
  • Progress tracking - Shows detailed status for each file
  • File validation - Checks output file sizes for corruption
  • Cleanup - Removes temporary .xml files after processing

Usage
  • Place the script in the directory containing your .mesh files
  • Ensure recalculate_normals.py is in the same directory
  • Ensure OgreXMLConverter.exe is in your PATH or same directory
  • Double-click the .bat file or run from command prompt

Automated Workflow
For each .mesh file, the script:
1. Creates backup → backup/filename.mesh.backup 2. Converts mesh to XML → OgreXMLConverter.exe file.mesh file.mesh.xml 3. Recalculates normals → python recalculate_normals.py file.mesh.xml 4. Converts back to mesh → OgreXMLConverter.exe file.mesh.xml file.mesh 5. Validates output file size 6. Cleans up temporary XML file

Script Sources
Mesh Normal Correction
GrizzlyOne95's Github[github.com]
Extracting models from BZR
How to Import and Play Animations for a Model in Blender

It's quite simple really. Follow these steps to import models and play animations for any .mesh model.

1. Install and Activate the Blender Addon

  • Make sure you have Blender installed.
  • Download Kindrad's addon (from the sources listed in guide).
  • In Blender, go to:
    Edit > Preferences > Add-ons > Install…
    Select the addon .zip or .py file.
  • Check the box next to the addon to activate it.

2. Import the Model

  • Go to: File > Import > .mesh
  • Navigate to the folder containing your model (e.g., avtank.mesh) and import it.
  • This will bring in:
    • Mesh – the 3D model
    • Skeleton/Armature
    • Animations – if included in the skeleton file

3. Locate the Animations

  • Switch to the Animation Workspace in Blender (top tabs).
  • Open the Dope Sheet or Action Editor.
  • In the dropdown menu, you should see a list of Actions — these are your animations (walk, idle, attack, etc.).

4. Play an Animation

  • Select the skeleton/armature in the 3D Viewport.
  • In the Action Editor, pick the animation you want to preview.
  • Press the Play button in the timeline (or spacebar) to see it in action.

5. Optional: Adjust Playback

  • Change the frame start/end to match the length of the animation.
  • Scrub the timeline to see individual frames.
  • Loop the animation by enabling the loop option in the timeline.

Tip: If no animations appear, make sure the imported .mesh actually contains animation data. Some units may have separate .anm or .skeleton files that also need importing.
General Battlezone Modeling Principles
Based on the original workflow by système, simplified for Blender and modern tools

This guide explains what you need to know to make your 3D models compatible with Battlezone 98 Redux, especially if you’re modeling in something like Blender. Make sure to check out the following VDF/SDF/Geo tool sections for further info.

General Requirements

  • Models must be broken into geos (sub-objects), each with an 8-character name limit.
  • Geos must be parented correctly to form a hierarchy.
  • The origin pivot and rotation of each geo matter for how the object appears and animates.
  • Collision requires a special hidden geo for inner and outer collission

Naming Conventions

Geos follow a strict naming pattern:

uuuLTxxy

Where:

uuu
= 3-letter unit name prefix

LT
= Level and Type (e.g. 11bd = Level 1 Body)

xy
= optional identifier (like
gc1
for Gun Cannon 1)

Common Geo Types:
  • bdN
    = Body
  • txN / tyN
    = Turret rotation axes (up/down or left/right)
  • gcN
    = Gun cannon
  • pov
    = Player viewpoint inside cockpit

Pivot Orientation & Hierarchy

  • Set pivots carefully; the pivot controls rotation and position in-game.
  • Align internal geos’ (LOD 2) pivots with external counterparts (LOD 1) (e.g. cockpit with hull).
  • View and edit hierarchy before export.

Collision

  • Required for units only (not producers/buildings — those generate collision automatically).
  • Use a geo called
    XXX1_col
    — import from another model and reshape. You can also use different names if using commando950's blender tool.
  • This geo is invisible and doesn't render.

Animation

  • Only position and rotation animations are supported.
  • Ideal range: 10–20 frames per animation, 25 geos max.
  • Loop animations (walk cycles, idle) must end on same pose as frame 0.
  • Animated geos auto-affect child geos (e.g. moving an arm also moves its gun).
  • Not all turrets need actual animation — turret geos (
    tx/ty
    ) rotate automatically.
Blender VDF/SDF Tool Part 1
3D Modeling with Blender Add-on

Introduction to the Battlezone Blender Add-on

What is this add-on?

New and old Battlezone players and modders can all remember the time we done without or had to make do with something. Maybe it was because of a lack of tools, support, or maybe just super inefficient tools.

With Battlezone models much has been mysterious and confusing. More importantly, its hard to really know everything there is about Battlezone much less modeling itself. Custom Battlezone models required an immense amount of knowledge and just completely unreasonable workflows that could involve 2-3 programs along with a bunch of files you need other external programs to generate.

Well lucky for you, this add-on is here to change that. In fact its not only here to add a new option for making models, it is here to change entirely how you make models for Battlezone and make your job a hell of a lot easier in the process!

Key Benefits:
MakeOBJ is obsolete and not required when using this add-on
• No confusing .txt files or undocumented nonsense
• Import/Export GEO/VDF/SDF files with all their information including animations
• Collisions are as simple as making two boxes or clicking a button
• Many useful features that make life easier

What do I need to have/know to begin?

The correct version of Blender for the add-on of your choosing
• The add-on must be installed and activated
• You should have knowledge of Blender or at least willingness to learn
• If you have "done this before", you won't be doing it the same now at all

Important Changes from Old Workflow:
Regular collision boxes are not supported at all - easier collision creation exists
• You do not need a .txt file for SDF/VDF properties
• SDF/VDF properties, animations, and GEO types are set inside Blender itself
You do not need any external programs including the old MakeObj program
• You can set a separate texture per every material in Blender
You can't and should not mix the new and old workflows

SDF/VDF/GEO File Formats

Battlezone has three formats directly related to its models. Battlezone's game engine was derived from Interstate 76 which used damage levels and level of detail to keep the game running smoothly.

File Format Overview

GEO (Geometry):
• Is a single part of a vehicle/structure
• Many are used to make a single vehicle/structure
• Is a model with model data
• Keeps track of the texture of every face and flat texture colors for low detail

VDF (Vehicle Definition File):
• Keeps track of special information about vehicle mass, Level of Detail distance
• Keeps track of GEOs and their positions
• Keeps track of animations for all vehicle GEOs
• Collision data is stored in the VDF

SDF (Structure Definition File):
• Keeps track of special information about the structure
• Keeps track of GEOs and their positions
• Keeps track of animations for all structure GEOs
• Unlike vehicles, uses only GEO collisions

Importing and Exporting

Import Options

File > Import > GEO
• Import only one GEO instead of an entire VDF and its GEOs
• Useful for looking at individual parts

File > Import > VDF
• Import all of a Battlezone VDF including:
• VDF Properties
• VDF Animations
• VDF Animation Element Data
• All GEOs associated with the VDF
• Pretty much everything

⚠️ Warning: If you import a new VDF, all current animation elements and VDF properties are lost and replaced by the current VDF.

File > Import > SDF
• Import all of a Battlezone SDF including:
• SDF Properties
• SDF Animations
• SDF Animation Element Data
• All GEOs associated with the SDF
• Pretty much everything

⚠️ Warning: If you import a new SDF, all current animation elements and SDF properties are lost and replaced by the current SDF.

Export Options

File > Export > GEO
• Only one model (the active one in the scene) will be exported
• By itself this .GEO is mostly useless but can replace existing unit GEOs

File > Export > VDF
• All VDF properties and all objects in the scene with correct naming will be converted
• Creates a complete Battlezone .VDF with all its models
⚠️ Warning: Will create TONS of GEO files and overwrite existing files without warning

File > Export > SDF
• All SDF properties and all objects in the scene with correct naming will be converted
• Creates a complete Battlezone .SDF with all its models
⚠️ Warning: Will create TONS of GEO files and overwrite existing files without warning

GEO Naming Rules

Battlezone GEOs are named in a very specific pattern and this add-on expects you to keep it that way.

Naming Structure

Names must begin with a 3 letter prefix, followed by the level of detail, the number 1, and a suffix.

Format: ABC11XYZ

Prefix (ABC):
• Used for identifying the geo set
• AGR for example denotes American Grizzly
• Don't skip out and put only one or two letters - get the entire 3 letters

Level of Detail (1):
• 1 = Close up views of the model (maximum detail)
• 2 = Cockpit view aka first person
• 3 = Seeing the model from far away

⚠️ Warning: This add-on expects you to have the first level of detail for all level of detail 2/3 objects. If you are missing the level of detail 1 version of a GEO, it will NOT be exported! LOD 3 is not necessary.

Number 1:
• Likely a leftover from I76 damage system
• Just keep it as one

Suffix (XYZ):
• Can be from 0-3 letters
• Recommended to give it a three letter name
Exceptions:
• Weapon hardpoints should use suffixes like gc1 or gc2
• Turrets should use tx1 or ty2 to define rotation direction

⚠️ Warning: Getting the naming wrong will result in it not exporting.

Properties Panels

SDF/VDF Properties

Click the scene icon in the properties tab. You'll find SDF/VDF Properties there. You can hover over the properties for more information.

SDF/VDF Animation Elements

Animation Elements control how animations will be used in game. They control:
• Index type of an animation
• What frame it starts on
• Length of the animation in frames
• Times it should loop
• Frame speed

Find the SDF/VDF Animations panel below the SDF/VDF Properties panel in the scene properties.

GEO Properties

GEO Properties let you define things specifically for each GEO. The most important is GEO Type - this determines what a GEO is and how it acts.

Click the object icon in the properties tab after selecting an object to find GEO Properties.

Collision System

Vehicle Collisions

There are three types of collisions for vehicles:

Outer Collision:
• What makes vehicles collide and get hit by projectiles
• Create with Add > Mesh > Cube
• Name it: outer_col, outercol, outer_collision, or outercollision

Inner Collision:
• Smaller than outer collisions
• Generally used for really hard clipping with the vehicle
• Create with Add > Mesh > Cube
• Name it: inner_col, innercol, inner_collision, or innercollision

Collision Creation Tips:
• You don't have to use perfect cubes - can use modified cubes, two points, or edges
• The system uses minimum and maximum positions from all points of the collision objects
Never try to make inner/outer collisions into the same object

⚠️ Warning: If you do not use a correct object name for a collision object it will not be used.

Structure/GEO Collisions

⚠️ Warning: Structures DO NOT use inner or outer collision boxes like vehicles.

Both Vehicles and Structures can have GEO based collisions. These give much more realistic and natural feeling hitboxes.
Blender VDF/SDF Tool Part 2
Automatically Generating GEO Collisions:

By default "Automatically Generate Collisions" is enabled on every object
When you export, all collisions will be generated for you
All settings will be lost and overwritten on the GEO collision data

Manually Creating GEO Collisions:

Uncheck "Automatically Generate Collisions"
Click "Generate Collisions" for a good starting point
Modify GEO Center, Projectile Collision Box, and Sphere Radius manually

GEO Collision Properties:

GEO Center: Center of the GEO projectile hitbox (usually middle of the GEO)
GEO Projectile Collision Box: Sets the projectile box size based on GEO Center position
GEO Sphere Radius: Everything inside this radius can be collided with

⚠️ Remember: Y and Z are swapped to Battlezone. Y is height!
Textures
Setting Textures (Version 0.9.3+)
Textures are now set in materials separately from just using Material name.
Process:

Assign materials to faces in Blender
In the materials tab, scroll down to the Texture text field
Enter the name of the texture you'd like to use for each material
If you use avtank00.map for example, write avtank00 without the .map extension

⚠️ Warning: If you set a texture name that does not exist, it will result in Battlezone creating an error and lock up the game immediately.
Hint: It is totally fine to use more than one texture per object as you can assign a different material per every face in Blender.
Animation System
Battlezone Animation Support
Battlezone animations are used in buildings, turrets, walkers, howitzers, and constructors. Battlezone supports both location and rotation based animations, and both can be used at the same time.
⚠️ Warning: Text file configured animations and SDF/VDF Properties are not used at all in this Blender add-on. Everything regarding animations must be done inside Blender.
Animation Workflow

Animate your Battlezone unit - best to do this last after model/textures are finished
Create animation elements for each animation you will use
Find the Battlezone Animations panel by clicking the scene tab
Click New Element to create a new animation
Click Delete Element to remove one

Animation Properties
Index:

Special property that determines what type of animation it is
See Animation Index Reference below

Start Frame:

What frame the animation begins in
Frame 0 in Blender = frame 0 for the element editor

Length:

How long the animation is
Determines when the animation ends and what direction it plays
Can be negative values to play animation in reverse

Loop Count:

How many times an animation should loop before stopping
Often set to 0 instead of 1
Setting to 0 will play the animation forever until it switches animations

Speed:

How fast an animation will play
Smaller number = slower animation
Larger number = faster animation

Animation Index Reference
Battlezone has animation indexes unique to each classlabel. This list helps decide what each animation is used for.
Recycler ClassLabel
Index 0: Deploy Index 1: Undeploy
Factory ClassLabel
Index 0: Deploy Index 1: Undeploy Index 2: Idle Index 3: Deployed Idle Index 4: Deployed & Ready Idle
Armory ClassLabel
Index 0: Launch Index 1: Launch (Reverse)
ConstructionRig ClassLabel
Index 0: Deploying/Starting Construction Index 1: Undeploying/Finished Construction Index 4: Deployed & Currently Constructing
Person ClassLabel
Index 0: Stand to Snipe Index 1: Snipe to Stand Index 2: Standing/Idle Index 3: Sniping/Idle Index 4: Walk Forwards Index 5: Walk Backwards Index 6: Strafe Left Index 7: Strafe Right Index 8: Sniped
Scavenger ClassLabel
Index 0: Deploy Index 1: Undeploy Index 2: Idle Index 3: Deployed Idle Index 4: Undeploy
Tug ClassLabel
Index 0: Deploy Index 1: Undeploy
Howitzer ClassLabel
Index 0: Deploy Index 1: Undeploy
TurretTank ClassLabel
Index 0: Deploy Index 1: Undeploy
Walker ClassLabel
Index 0: Vehicle Get Out Index 1: Vehicle Get In Index 2: Stand/Idle Index 3: No Pilot/Idle Index 4: Walk Forwards Index 5: Walk Backwards Index 6: Strafe Left Index 7: Strafe Right
Best Practices and Tips
General Workflow

Look at existing SDFs and VDFs for reference
Import existing Battlezone units to see examples of collisions and setup
Animate last after the unit is completely finished with model/textures
Experiment with animation settings, but check existing files for guidance

Troubleshooting

Object not exporting: Check naming convention
Textures not working: Verify texture names exist in game
Collisions not working: Check collision object naming
Animations not working: Verify animation index is correct for unit type

File Management

Keep backups before major changes
Export will overwrite existing files without warning
Test exported models in-game frequently
Use consistent naming conventions across your project

Advanced Features
Level of Detail (LOD)

Create multiple versions of the same GEO for different detail levels
LOD 1: Full detail for close viewing
LOD 2: Simplified for cockpit view
LOD 3: Very simplified for distant viewing

Multiple Textures

Each material can have its own texture
Assign different materials to different faces
Allows for complex texture setups on single models

Animation Reuse

Use negative Length values to reverse animations
Saves time by reusing walking left animation for walking right
Many stock Battlezone units use this technique
BZR Model Port Script
port_models.py Documentation

Overview

port_models.py is a Python-based model porting utility for Battlezone 98 Redux that converts legacy Battlezone model formats (.vdf, .sdf, .geo, .map, .odf) to the newer .mesh format used by the Redux version. The tool handles geometry, textures, animations, and special features like headlights and sniper scopes. You can find a download for it in the last section of this guide. It saves a TON of time adding new models to Battlezone 98 Redux!

Requirements

Python Packages Required:
  • numpy - For managing large data arrays efficiently
  • pillow - Python image library for creating DDS texture files

Install with:
pip install numpy pillow

Usage

Basic Usage
python port_models.py [options] file [file ...]

Drag & Drop
You can drag and drop .vdf, .sdf, or .odf files directly onto the script for quick conversion.

Using ODF Files (Recommended)
Using .odf files is preferred because they contain metadata about whether the model is a pilot, turret, etc., which cannot be reliably detected from .vdf files alone.

Command Line Arguments

File Input
  • file [file ...] - Path to the file(s) you want to port
  • Supported formats: .vdf, .sdf, .geo, .map, .odf

Basic Options

--name NAME Name to give the final model files (default: filename) --suffix SUFFIX Suffix appended to material file names (default: "_port") --dest DEST Destination directory to write files into (default: current dir)

Note: Use --suffix "" for no material name suffix.

Model Type Flags

All model type flags support ternary values: Auto, True, False

--person {Auto,True,False} Force this object to be flagged as a person --turret {Auto,True,False} Force this object to be flagged as a turret --cockpit {Auto,True,False} Force creation/suppression of separate cockpit model files --skeletalanims {Auto,True,False} Enable/disable skeletal animations

Special Features

--headlights Enable automatic creation of headlights - detects a GEO type 38 on the model --nopovrots Remove POV rotation keyframes from directional movement animations --flatcolors Force the use of flat per-face color texturing

Scope Configuration

--scope {Auto,True,False} Enable/disable sniper scope creation --scopetype {AUTO,FIXED,ATTACHED,GEOMETRY} Type of scope to create --scopenation SCOPENATION Nation-specific scope configuration --scopescreen X Y Z SCALE BEHIND_DIST Scope screen positioning (5 float values) --scopegun SCOPEGUN Gun name for scope attachment --scopetransform RX RY RZ UX UY UZ FX FY FZ PX PY PZ Scope transform matrix (12 floats) --scopetexture SCOPETEXTURE Texture name for scope

Advanced Options

--boundsmult X Y Z Scale factors for the mesh bounds (3 float values) --act ACT .act color palette file for indexed color .map textures --config CONFIG Custom configuration file path --onlyonce Skip files when mesh already exists in target directory --nowrite Suppress file writing (for testing)

Configuration File (config.cfg)

Create a config.cfg file in the same directory as port_models.py or specify a custom path with --config.

Format
path/to/palette.act path/to/resource/directory1 path/to/resource/directory2 path/to/resource/directory3 ...

Structure
  • Line 1: Path to .act color palette file for indexed images
  • Line 2+: Directory paths for asset search locations
  • The script searches the target file's directory first, then these paths in order

Example config.cfg
...\bzone\moon.act ...\bzhw16q ...\bz15hw16l ...\bz15hw16 ...\bz15hw ...\bz15sw ...\bzone152

Supported File Types

Input Formats
  • .vdf - Vehicle Definition Files (vehicles with cockpits)
  • .sdf - Structure Definition Files (buildings, static objects)
  • .geo - Geometry files
  • .map - Map texture files
  • .odf - Object Definition Files (preferred - contains metadata)

Output Files
The tool generates multiple output files:
  • .mesh - Main geometry file
  • .material - Material definitions
  • .skeleton - Animation skeleton (if applicable)
  • .dds - Texture files

Automatic Detection

When using .odf files, the tool automatically detects:

Vehicle Types
apc, hover, howitzer, minelayer, sav, scavenger, tug, turrettank, walker, wingman armory, constructionrig, factory, producer, recycler, turret person, ammopack, camerapod, daywrecker, powerup, repairkit, dropoff, torpedo, wpnpower

Building Types
animbuilding, artifact, barracks, commtower, geyser, i76building, portal, powerplant repairdepot, scrapfield, scrapsilo, shieldtower, supplydepot, i76building2 flare, i76sign, magnet, proximity, spawnpnt, spraybomb, weaponmine, scrap

Special Classifications
  • Person models: Automatically enables --person True and --turret False
  • Turret models: Classes turret, turrettank, howitzer enable --turret True
  • Pilots: Auto-detected when model name has 's' as second character (e.g., "bspilo")

Example Batch Files

Basic Vehicle with Headlights
@echo off python .\port_models.py --headlights %* "C:\Path\To\bvhscout.vdf"

Pilot with Scope (Geometry Type)
@echo off python .\port_models.py --person True --cockpit True --scopetype GEOMETRY --scopetexture SCOPE %* "C:\Path\To\issmg.vdf"

Pilot with Attached Scope
@echo off python .\port_models.py --person True --cockpit True --headlights --scopetype ATTACHED %* "C:\Path\To\furycore.vdf"

Tips and Best Practices

  • Use .odf files when possible - They contain the most metadata for automatic detection
  • Set up config.cfg - Saves time by automatically finding required assets
  • Use --onlyonce - Prevents re-processing models that already exist
  • Test with --nowrite - Verify settings before actual conversion
  • Organize output - Use --dest to keep converted files organized
  • Suffix management - Use empty suffix (--suffix "") to avoid filename clutter

Troubleshooting

Common Issues
  • Missing dependencies: Install numpy and pillow
  • File not found: Check config.cfg paths and ensure all required assets are accessible
  • Invalid file type: Only vdf/sdf/geo/map/odf files are supported
  • Permission errors: Ensure write access to destination directory

Debug Mode
Use --nowrite to test configurations without creating files.

Technical Notes

  • Models with the same name won't be re-ported if using --onlyonce
  • Cockpit separation is determined by examining animation data
  • Headlights create bones named "HLGT0_ff0000", "HLGT1_ff0000", etc.
  • Scope geometry is automatically generated for non-geometry scope types
  • File extensions are case-insensitive
Basic Particle Effects
What are Particle Renders?
Particle Renders are visual effects in Battlezone that create explosions, weapon effects, smoke, sparks, and other dynamic visuals. They're defined in ODF files and referenced by weapons, explosions, and other game objects.

What BZ98R has:
  • draw_bolt
  • draw_emit
  • draw_light
  • draw_multi
  • draw_null
  • draw_planar
  • draw_shock
  • draw_sphere
  • draw_sprite
  • draw_tracer
  • draw_twirl
  • draw_twirl_trail

Basic Setup Format
[RenderHeader] renderBase = "draw_sprite" ; Type of effect textureName = "explosion.tga" startColor = "255 255 255 255" ; RGBA format finishColor = "255 100 0 0" startRadius = 1.0 finishRadius = 3.0 lifeTime = 1.0 animateTime = 0.8
draw_sprite - 2D Sprite Effects
Most common particle type. Perfect for explosions, smoke, muzzle flashes, and sparks.
Key Properties
[RenderHeader] renderBase = "draw_sprite" textureName = "smoke.tga" startColor = "150 150 150 255" ; Gray, opaque finishColor = "100 100 100 0" ; Darker, transparent startRadius = 1.0 ; Starting size finishRadius = 4.0 ; Ending size (grows) lifeTime = 2.0 ; Total duration animateTime = 1.8 ; Color/size transition time spriteCenter.x = 0.0 ; Texture offset X spriteCenter.y = 0.0 ; Texture offset Y
Examples
Muzzle Flash:
[MuzzleFlash] renderBase = "draw_sprite" textureName = "muzzleflash.tga" startColor = "255 255 200 255" finishColor = "255 100 0 0" startRadius = 1.5 finishRadius = 2.5 lifeTime = 0.2 animateTime = 0.15
Smoke Puff:
[SmokePuff] renderBase = "draw_sprite" textureName = "smoke.tga" startColor = "120 120 120 200" finishColor = "80 80 80 0" startRadius = 0.8 finishRadius = 3.0 lifeTime = 3.0 animateTime = 2.5
draw_null - Invisible Placeholder
Creates invisible effects. Useful for timing, positioning, or as containers for other effects.
Properties
[RenderHeader] renderBase = "draw_null" lifeTime = 1.0 StartDelay = 0.5 ; Delay before starting
Usage

Timing delays in complex effects
Invisible emitters for particles
Placeholder effects during development
Sound-only effects (with SimulateSoundEffect)

draw_emit - Particle Emission Effects
Spawns other particle effects. Creates complex systems by emitting multiple particles over time.
Key Properties
[RenderHeader] renderBase = "draw_emit" emitName = "debris.particle" ; What to emit emitDelay = 0.1 ; Time between emissions emitDelayVar = 0.05 ; Random variance emitPosVariance = "1.0 1.0 1.0" ; Position scatter emitVelocity = "5.0 10.0 5.0" ; Initial velocity emitVariance = "3.0 2.0 3.0" ; Direction variance emitInherit = "1.0 1.0 1.0" ; Inherit parent velocity lifeTime = 2.0 ; How long to emit maxCount = 20 ; Max simultaneous particles
Emitted Particles Need simulateBase
[debris.particle] renderBase = "draw_sprite" simulateBase = "sim_chunk" ; Physics behavior textureName = "debris.tga" startColor = "200 150 100 255" finishColor = "150 100 50 0" lifeTime = 3.0
Simulate Bases

sim_null - No movement
sim_chunk - Falls and bounces
sim_dust - Ground-based particles
sim_ember - Falls, no rotation
sim_spray - Falls, respects collisions
sim_smoke - Free movement

Example: Explosion Debris
[ExplosionDebris] renderBase = "draw_emit" emitName = "explosion.chunk" emitDelay = 0.02 emitDelayVar = 0.03 lifeTime = 0.5 maxCount = 25 emitPosVariance = "2.0 1.0 2.0" emitVelocity = "15.0 20.0 15.0" emitVariance = "10.0 5.0 10.0" [explosion.chunk] renderBase = "draw_sprite" simulateBase = "sim_chunk" textureName = "debris.tga" startColor = "255 200 150 255" finishColor = "200 150 100 0" startRadius = 0.5 finishRadius = 0.3 lifeTime = 4.0
draw_multi - Multiple Layered Effects
Combines multiple effects simultaneously. Perfect for complex explosions or weapon effects.
Properties
[RenderHeader] renderBase = "draw_multi" renderCount = 3 ; Number of effects renderName1 = "explosion.flash" renderName2 = "explosion.smoke" renderName3 = "explosion.sparks"
Example: Complex Explosion
[BigExplosion] renderBase = "draw_multi" renderCount = 3 renderName1 = "explosion.flash" renderName2 = "explosion.debris" renderName3 = "explosion.smoke" [explosion.flash] renderBase = "draw_sprite" textureName = "explosion_flash.tga" startColor = "255 255 200 255" finishColor = "255 100 0 0" startRadius = 3.0 finishRadius = 8.0 lifeTime = 0.4 animateTime = 0.3 [explosion.debris] renderBase = "draw_emit" emitName = "explosion.debris_bit" emitDelay = 0.01 lifeTime = 0.3 maxCount = 20 emitVelocity = "20.0 25.0 20.0" emitVariance = "15.0 10.0 15.0" [explosion.debris_bit] renderBase = "draw_sprite" simulateBase = "sim_chunk" textureName = "debris.tga" startColor = "200 150 100 255" finishColor = "100 75 50 0" startRadius = 0.4 finishRadius = 0.2 lifeTime = 3.0 [explosion.smoke] renderBase = "draw_sprite" textureName = "smoke.tga" startColor = "100 100 100 200" finishColor = "60 60 60 0" startRadius = 2.0 finishRadius = 10.0 lifeTime = 8.0 animateTime = 6.0
Common Properties Explained
Colors

Format: "Red Green Blue Alpha" (0-255 each)
Alpha: 0 = transparent, 255 = opaque
Example: "255 100 50 200" = bright orange, mostly opaque

Size and Animation

startRadius/finishRadius - Size in world units
lifeTime - Total effect duration
animateTime - Time to transition from start to finish values
StartDelay - Delay before effect begins

Performance Settings

maxCount - Limits simultaneous particles (draw_emit)
minLOD/maxLOD - Controls visibility at different detail levels
Keep values reasonable for good performance

Quick Reference
Effect Types

Simple visual: draw_sprite
No visual: draw_null
Particle spawner: draw_emit (needs simulateBase on spawned particles)
Combined effects: draw_multi
Advanced Particle Effects
draw_bolt - Lightning/Electrical Effects

Creates zigzagging electrical arcs. Perfect for lightning, energy weapons, and electrical effects.

Key Properties
[RenderHeader] renderBase = "draw_bolt" segmentLength = 2.0 ; Length of each segment segmentVariance = "1.0 1.0 1.0" ; X Y Z variance per segment textureRate = 1.0 ; Texture repeat rate textureSpeed = 5.0 ; UV animation speed animateTime = 0.1 ; Default animation time

Example: Lightning Strike
[Lightning] renderBase = "draw_bolt" textureName = "lightning.tga" textureBlend = "srcalpha one add" ; Additive blending for brightness startColor = "200 200 255 255" finishColor = "100 100 200 0" startRadius = 0.3 finishRadius = 0.1 segmentLength = 1.5 segmentVariance = "0.8 0.8 0.8" textureSpeed = 10.0 lifeTime = 0.2

draw_light - Light Source Effects

Creates dynamic lighting. Use sparingly as lights are performance-expensive.

Key Properties
[RenderHeader] renderBase = "draw_light" lightType = "point" ; "point" or "spot" attenuateConstant = 1.0 ; Base attenuation attenuateLinear = 0.0 ; Linear falloff attenuateQuadratic = 15.0 ; Quadratic falloff lensFlare = true ; Enable lens flare sprites

Important Notes
Alpha in colors controls light intensity
Radius controls light range
• Higher attenuation values = shorter range
• Lens flare size controlled by color alpha

Example: Explosion Light
[ExplosionLight] renderBase = "draw_light" lightType = "point" startColor = "255 200 100 255" ; Bright orange, full intensity finishColor = "200 100 50 50" ; Dimmer orange, low intensity startRadius = 12.0 ; Large light range finishRadius = 4.0 ; Shrinks as it dims attenuateConstant = 1.0 attenuateLinear = 0.1 attenuateQuadratic = 8.0 lensFlare = true lifeTime = 1.0 animateTime = 0.8

draw_sphere - Spherical Effects

Renders 3D spheres. Great for energy shields, force fields, and bubble effects.

Properties
[RenderHeader] renderBase = "draw_sphere" InitialPitch = 0.0 ; Starting rotation (degrees) InitialYaw = 0.0 InitialRoll = 0.0 AddPitch = 0.0 ; Rotation speed (degrees/second) AddYaw = 0.0 AddRoll = 0.0

Example: Energy Shield
[EnergyShield] renderBase = "draw_sphere" textureName = "energy_field.tga" textureBlend = "srcalpha one add" startColor = "100 150 255 128" finishColor = "100 150 255 128" ; Constant color startRadius = 6.0 finishRadius = 6.0 ; Constant size AddYaw = 45.0 ; Slow rotation AddPitch = 20.0 lifeTime = 1e30 ; Continuous effect animateTime = 1e30

draw_planar - Flat Sprite Effects

Renders textures flat on the ground. Perfect for explosion rings, impact decals, and ground effects.

Properties
[RenderHeader] renderBase = "draw_planar" rotationRate = 0.0 ; Rotation speed (degrees/second)

Example: Explosion Ring
[ExplosionRing] renderBase = "draw_planar" textureName = "explosion_ring.tga" startColor = "255 255 200 200" finishColor = "255 100 50 0" startRadius = 1.0 finishRadius = 15.0 rotationRate = 90.0 lifeTime = 1.2 animateTime = 1.0

draw_shock - Shockwave/Explosion Rings

Creates expanding shockwave effects. Similar to draw_planar but optimized for shockwaves.

Example: Shockwave
[Shockwave] renderBase = "draw_shock" textureName = "shockwave.tga" startColor = "255 255 255 150" finishColor = "200 200 255 0" startRadius = 0.5 finishRadius = 25.0 lifeTime = 1.5 animateTime = 1.2

draw_tracer - Bullet Trail Effects

Creates linear projectile trails. Perfect for bullet paths, laser beams, and missile trails.

Properties
[RenderHeader] renderBase = "draw_tracer" tracerLength = 10.0 ; Length of the trail

Example: Bullet Tracer
[BulletTracer] renderBase = "draw_tracer" textureName = "bullet_trail.tga" textureBlend = "srcalpha one add" startColor = "255 255 200 200" finishColor = "255 200 100 0" startRadius = 0.08 finishRadius = 0.04 tracerLength = 12.0 lifeTime = 0.3 animateTime = 0.25

draw_twirl - Spinning/Rotating Effects

Creates rotating ground-based effects. Perfect for whirlwinds, dust devils, and portal effects.

Properties
[RenderHeader] renderBase = "draw_twirl" rotationRate = 0.0 ; Random rotation (degrees/second) BottomInteractsWithTerrain = true ; Avoid clipping through ground

Example: Dust Devil
[DustDevil] renderBase = "draw_twirl" textureName = "dust_swirl.tga" startColor = "200 180 150 180" finishColor = "150 130 100 0" startRadius = 1.5 finishRadius = 3.5 rotationRate = 180.0 BottomInteractsWithTerrain = true lifeTime = 4.0 animateTime = 3.5

draw_twirl_trail - Spinning Trail Effects

Creates trails of spinning effects. Combines trail movement with twirl rotation.

Key Properties
[RenderHeader] renderBase = "draw_twirl_trail" emitDelay = 0.1 ; Time between trail segments emitLife = 1.0 ; Life of each trail segment emitPosVariance = "0.2 0.2 0.2" ; Position variance maxDist = 400.0 ; Max render distance groupDelay = 0.1 ; Delay between groups

Example: Magic Trail
[MagicTrail] renderBase = "draw_twirl_trail" textureName = "magic_sparkle.tga" textureBlend = "srcalpha one add" startColor = "255 150 255 255" finishColor = "200 100 200 0" startRadius = 0.4 finishRadius = 0.8 rotationRate = 360.0 emitDelay = 0.08 emitLife = 1.5 emitPosVariance = "0.15 0.15 0.15" maxDist = 300.0 lifeTime = 1e30

Texture Blending Modes

Common Blend Modes
textureBlend = "srcalpha one add" ; Additive (bright effects) textureBlend = "srcalpha invsrcalpha" ; Standard alpha blending textureBlend = "one one add" ; Full additive

When to Use
Additive ("srcalpha one add"): Bright effects like lightning, energy, muzzle flashes
Alpha blend: Standard smoke, dust, debris
Full additive: Very bright effects, lens flares
Explosion Class Particles
What is ExplosionClass?

ExplosionClass creates instant damage with distance falloff, combined with particle effects. When an explosion occurs, it can spawn up to 16 different particle systems simultaneously.

Class Tree: Entity→Explosion

Description: ExplosionClass handles both the damage mechanics and visual particle effects of explosions in a single system.

Basic Explosion Setup

Core Structure
[ExplosionClass] classLabel = "explosion" damageRadius = 15.0 damage = 100 particleCount = 3 ; Number of particle effects (max 16) particleClass1 = "explosion.flash" particleClass2 = "explosion.debris" particleClass3 = "explosion.smoke"

How It Works
1. Explosion occurs (from weapon, destroyed object, etc.)
2. Damage is applied to objects within damageRadius
3. Each particleClass is spawned simultaneously
4. Particles inherit explosion properties (position, velocity, etc.)

Particle Properties

Core Particle Settings
[ExplosionClass] particleCount = 4 ; Number of effects (1-16) ; For each particle (1 through particleCount): particleClass1 = "explosion.main_flash" ; Render name particlePosVar1 = "1.0 0.5 1.0" ; Position variance particleBias1 = "0.0 5.0 0.0" ; Base velocity particleVeloc1 = "3.0 8.0 3.0" ; Velocity variance particleInherit1 = "1.0 0.5 1.0" ; Inherited velocity % particleClass2 = "explosion.debris_shower" particlePosVar2 = "2.0 1.0 2.0" particleBias2 = "0.0 15.0 0.0" particleVeloc2 = "10.0 5.0 10.0" particleInherit2 = "0.8 0.3 0.8" ; ... continue for each particle

Property Explanations

particleClass# = ""
• The Render Name for this Particle emitter
• References particle effects created with renderBase
• Example: "explosion.debris_emit" points to [debris_emit] render section

particlePosVar# = "0.0 0.0 0.0"
• Particle starting Position variance in X Y Z format
• Creates scatter around explosion center
• Example: "2.0 1.0 2.0" = ±2 units X/Z, ±1 unit Y

particleBias# = "0.0 0.0 0.0"
• Particle base Velocity in X Y Z format
• Constant velocity applied to all particles of this type
• Example: "0.0 10.0 0.0" = upward velocity

particleVeloc# = "0.0 0.0 0.0"
• Particle Velocity variance applied in addition to particleBias
• Random velocity within this range
• Example: "5.0 3.0 5.0" = random velocity up to ±5 X/Z, ±3 Y

particleInherit# = "0.0 0.0 0.0"
• Percent of inherited velocity applied (0.0 - 1.0 for each axis)
• 1.0 = 100% inheritance from explosion source
• Useful for moving explosions (missiles, etc.)

Complete Explosion Example

Main Explosion Definition
[BigExplosion] classLabel = "explosion" damageRadius = 20.0 damage = 150 particleCount = 4 ; Main flash particleClass1 = "explosion.flash" particlePosVar1 = "0.5 0.5 0.5" particleBias1 = "0.0 0.0 0.0" particleVeloc1 = "0.0 0.0 0.0" particleInherit1 = "0.0 0.0 0.0" ; Debris shower particleClass2 = "explosion.debris" particlePosVar2 = "3.0 1.0 3.0" particleBias2 = "0.0 12.0 0.0" particleVeloc2 = "15.0 8.0 15.0" particleInherit2 = "0.5 0.2 0.5" ; Smoke cloud particleClass3 = "explosion.smoke" particlePosVar3 = "2.0 1.0 2.0" particleBias3 = "0.0 3.0 0.0" particleVeloc3 = "5.0 5.0 5.0" particleInherit3 = "0.3 0.1 0.3" ; Ground ring particleClass4 = "explosion.ring" particlePosVar4 = "0.0 0.0 0.0" particleBias4 = "0.0 0.0 0.0" particleVeloc4 = "0.0 0.0 0.0" particleInherit4 = "0.0 0.0 0.0"

Referenced Particle Effects
; Bright explosion flash [explosion.flash] renderBase = "draw_sprite" textureName = "explosion_flash.tga" startColor = "255 255 200 255" finishColor = "255 100 0 0" startRadius = 4.0 finishRadius = 12.0 lifeTime = 0.5 animateTime = 0.4 ; Debris particle emitter [explosion.debris] renderBase = "draw_emit" emitName = "explosion.debris_chunk" emitDelay = 0.02 emitDelayVar = 0.03 lifeTime = 0.4 maxCount = 30 ; Individual debris pieces [explosion.debris_chunk] renderBase = "draw_sprite" simulateBase = "sim_chunk" textureName = "debris.tga" startColor = "200 150 100 255" finishColor = "150 100 75 0" startRadius = 0.6 finishRadius = 0.4 lifeTime = 4.0 ; Smoke cloud [explosion.smoke] renderBase = "draw_sprite" textureName = "smoke.tga" startColor = "120 120 120 200" finishColor = "80 80 80 0" startRadius = 3.0 finishRadius = 15.0 lifeTime = 10.0 animateTime = 8.0 ; Ground impact ring [explosion.ring] renderBase = "draw_planar" textureName = "explosion_ring.tga" startColor = "255 255 200 180" finishColor = "255 150 100 0" startRadius = 1.0 finishRadius = 18.0 lifeTime = 1.5 animateTime = 1.2

Specialized Explosion Types

Vehicle Explosion
[VehicleExplosion] classLabel = "explosion" damageRadius = 12.0 damage = 75 particleCount = 2 ; Fire burst particleClass1 = "vehicle.fire_burst" particlePosVar1 = "1.0 1.0 1.0" particleBias1 = "0.0 8.0 0.0" particleVeloc1 = "6.0 4.0 6.0" particleInherit1 = "1.0 0.3 1.0" ; Inherit vehicle movement ; Metal debris particleClass2 = "vehicle.metal_debris" particlePosVar2 = "2.0 1.0 2.0" particleBias2 = "0.0 15.0 0.0" particleVeloc2 = "12.0 6.0 12.0" particleInherit2 = "0.8 0.2 0.8"

Building Explosion
[BuildingExplosion] classLabel = "explosion" damageRadius = 25.0 damage = 200 particleCount = 3 ; Large flash particleClass1 = "building.big_flash" particlePosVar1 = "2.0 2.0 2.0" particleBias1 = "0.0 0.0 0.0" particleVeloc1 = "0.0 0.0 0.0" particleInherit1 = "0.0 0.0 0.0" ; Buildings don't move ; Concrete chunks particleClass2 = "building.concrete_debris" particlePosVar2 = "4.0 2.0 4.0" particleBias2 = "0.0 18.0 0.0" particleVeloc2 = "20.0 10.0 20.0" particleInherit2 = "0.0 0.0 0.0" ; Dust cloud particleClass3 = "building.dust_cloud" particlePosVar3 = "3.0 1.0 3.0" particleBias3 = "0.0 2.0 0.0" particleVeloc3 = "3.0 8.0 3.0" particleInherit3 = "0.0 0.0 0.0"

Advanced Techniques

Velocity Inheritance Examples

Moving Missile Explosion:
particleInherit1 = "1.0 0.5 1.0" ; Keep horizontal momentum, reduce vertical

Stationary Building:
particleInherit1 = "0.0 0.0 0.0" ; No inheritance

Partial Inheritance:
particleInherit1 = "0.3 0.1 0.3" ; Some momentum carried forward

Position Variance Strategies

Tight Explosion:
particlePosVar1 = "0.5 0.5 0.5" ; Concentrated effect

Wide Scatter:
particlePosVar1 = "5.0 2.0 5.0" ; Spread out debris

Ground-Level Only:
particlePosVar1 = "3.0 0.1 3.0" ; Horizontal spread, minimal vertical

Performance and Limits

Important Limits
Maximum 16 particle effects per explosion
• Each particle effect can spawn many individual particles
• Complex explosions can impact performance significantly
DLL Dynamic Loading
Core Concept
Battlezone's Lua environment still has access to Lua's built-in module system through package.path and package.cpath. You can manually modify these to enable DLL loading.

The Manual Process

1. Parse Current Paths
Extract the game directory from existing package paths:
-- Get current paths local defaultPath = package.path local defaultCPath = package.cpath -- Parse game directory from package.cpath local function getGameDirectory() local paths = {} for path in string.gmatch(package.cpath, '([^;]+)') do table.insert(paths, path) end -- Game directory is typically the second path, remove the "?" part local gamePath = paths[2] if gamePath then return string.match(gamePath, "(.*)\\%?") end return nil end

2. Construct Workshop/Mod Directories
Build paths to where your DLLs are located:
local gameDirectory = getGameDirectory() -- For Steam Workshop local workshopPath = gameDirectory .. "\\steamapps\\workshop\\content\\301650\\YourWorkshopID" -- For GOG versions local gogPath = gameDirectory .. "\\mods\\YourModFolder"

3. Modify Package Paths
Add your directories to Lua's search paths:
-- Add Lua file search paths package.path = package.path .. ";" .. workshopPath .. "\\?.lua" -- Add DLL search paths package.cpath = package.cpath .. ";" .. workshopPath .. "\\?.dll"

4. Load Your DLL
Now require() will find your DLL:
local myDLL = require("YourDLLName")

Complete Minimal Example
-- Manual DLL loading without RequireFix local function enableDLLLoading(workshopID) -- Parse game directory from existing cpath local paths = {} for path in string.gmatch(package.cpath, '([^;]+)') do table.insert(paths, path) end local gameDir = string.match(paths[2] or "", "(.*)\\%?") if not gameDir then print("Could not determine game directory") return false end -- Build workshop directory path local workshopDir = gameDir .. "\\steamapps\\workshop\\content\\301650\\" .. workshopID -- Modify search paths package.path = package.path .. ";" .. workshopDir .. "\\?.lua" package.cpath = package.cpath .. ";" .. workshopDir .. "\\?.dll" print("DLL loading enabled for: " .. workshopDir) return true end -- Usage if enableDLLLoading("YourWorkshopID") then local myDLL = require("YourDLLName") -- Use your DLL functions here end

What RequireFix Actually Does
RequireFix automates this process by:

  • Auto-detecting paths: Handles Steam Workshop vs GOG installations automatically
  • Multiple workshop support: Can add multiple mod directories at once
  • Error handling: Graceful fallbacks when directories don't exist
  • Cross-platform logic: Handles different installation patterns

Key Variables
  • package.path - Where Lua looks for .lua files (semicolon-separated)
  • package.cpath - Where Lua looks for .dll files (semicolon-separated)
  • Path format: DirectoryPath\\?.extension where ? is replaced with module name

The Bottom Line
RequireFix is essentially a sophisticated wrapper around manual package.path and package.cpath modification that handles the complex directory detection and multiple-workshop scenarios automatically. RequireFix is available on the Steam Workshop.

Security Warning
Only load DLLs from trusted sources. Custom DLLs have full system access and can potentially harm your computer or compromise your data.
DLL Proxying/Shimming
Alternative Method: DLL Proxying/Shimming

What is DLL Proxying?
Battlezone 98 Redux attempts to load winmm.dll (Windows Multimedia API), but this DLL isn't actually used by the game. This creates an opportunity for DLL injection through Windows' DLL search order.

How Windows DLL Loading Works
When an application requests a DLL, Windows searches in this order:
  • The application's root directory
  • System directories (System32, etc.)

The Proxy Method
  • Place a custom winmm.dll in Battlezone's root directory
  • Windows loads your DLL instead of the system one
  • Your DLL can inject code and forward calls to the real winmm.dll
  • Technically any DLL can work, but winmm.dll is confirmed to be a working and useful example

Advantages Over Package Path Method
  • Automatic loading: No Lua code modifications needed
  • Earlier injection: Loads before Lua environment starts
  • Global access: Can hook any part of the application
  • No workshop dependency: Works with any installation method

Implementation Example
// In your custom winmm.dll (C/C++) #include <windows.h> HMODULE hRealDLL = NULL; BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) { if (reason == DLL_PROCESS_ATTACH) { // Load the real winmm.dll from system directory hRealDLL = LoadLibraryA("C:\\Windows\\System32\\winmm.dll"); // Your custom initialization code here // Hook functions, inject Lua modules, etc. } return TRUE; } // Forward all winmm.dll functions to the real DLL // (Export forwarding or manual GetProcAddress calls)

File Placement
Place your custom winmm.dll in:
Steam: steamapps\common\Battlezone 98 Redux\winmm.dll GOG: Battlezone 98 Redux\winmm.dll

Comparison of Methods

Package Path Method:
  • Requires Lua code changes
  • Works within Lua environment only
  • Easier to distribute via workshop
  • More portable across installations

DLL Proxy Method:
  • No Lua modifications needed
  • Deeper system access
  • Automatic loading
  • Requires direct file placement

Security Considerations
IMPORTANT: DLL proxying gives complete system access and loads automatically. Only use DLLs from absolutely trusted sources. Malicious DLLs can:
  • Access your entire computer
  • Steal personal information
  • Install malware
  • Corrupt game files

Detection and Removal
To check if a proxy DLL is installed:
  • Look for winmm.dll in Battlezone's root directory
  • The real winmm.dll should only be in System32
  • Delete the file from Battlezone's directory to remove

Which Method to Choose?
  • Use Package Path for: Lua extensions, workshop mods, portable solutions
  • Use DLL Proxy for: Deep system hooks, automatic loading, advanced modifications
Example BZR DLL
For Developers: Creating Custom DLLs

Project Structure Requirements
Based on the ExtraUtilities source code, you need:

  • Two Projects: Custom Lua library + Your DLL
  • Lua5.1-BZR.vcxproj: Static library (.lib) for custom Lua
  • YourDLL.vcxproj: Dynamic library (.dll) linking to custom Lua
  • Target: 32-bit Windows (Win32 platform)
  • Language: C++20 with C17 for Lua sources

Critical Build Configuration
Configuration: Release (or Debug for development) Platform: Win32 (32-bit only - BZR is 32-bit) Platform Toolset: v143 (Visual Studio 2022) Runtime Library: MultiThreadedDLL (/MD) for Release, MultiThreadedDebugDLL (/MDd) for Debug Character Set: MultiByte (for DLL), Unicode (for Lua library) - MISMATCH REQUIRED Language Standard: stdcpp20 Additional Dependencies: - Debug: Lua5.1-BZR-debug.lib;User32.lib - Release: Lua5.1-BZR.lib;User32.lib

Required Entry Point
Your DLL must export a specific function for Lua loading:
extern "C" int __declspec(dllexport) luaopen_yourmodulename(lua_State* L) { const luaL_Reg EXPORT[] = { // Your function exports here { "YourFunction", &YourNamespace::YourFunction }, { "AnotherFunction", &YourNamespace::AnotherFunction }, { 0, 0 } // Terminator (use 0, 0 not NULL, NULL) }; // Register the library luaL_register(L, "yourmodulename", EXPORT); // Optional: Additional initialization // YourInit(L); return 0; // ExtraUtilities returns 0, not 1 }

DllMain Template
#include <Windows.h> static_assert(_WIN32); // BZR is 32 bit BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID) { switch (fdwReason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hModule); #ifdef _DEBUG // Optional: Debug console for development AllocConsole(); freopen("CONOUT$", "w", stdout); SetConsoleTitle("Your DLL Debug Console"); #endif // Your initialization code here break; case DLL_PROCESS_DETACH: #ifdef _DEBUG FreeConsole(); #endif // Your cleanup code here break; } return TRUE; }

Character Set Configuration Issue
IMPORTANT: There's a character set mismatch in ExtraUtilities:
  • ExtraUtilities.vcxproj: Uses MultiByte character set
  • Lua5.1-BZR.vcxproj: Uses Unicode character set

This mismatch exists in the actual codebase and appears to work, so follow this pattern exactly.

Project Dependencies & Naming
Your DLL project must:
  • Link against the correctly named Lua library:
  • Debug builds: Link to Lua5.1-BZR-debug.lib
  • Release builds: Link to Lua5.1-BZR.lib
  • Output to $(SolutionDir)\lib\ directory
  • Use identical compiler settings as the original projects

Target Names
  • ExtraUtilities outputs as: exu.dll (not ExtraUtilities.dll)
  • Your DLL should output as: yourmodulename.dll

File Organization
YourProject/ ├── Lua5.1-BZR/ │ ├── src/ (all Lua 5.1 source files + custom luaconf.h) │ └── Lua5.1-BZR.vcxproj ├── YourDLL/ │ ├── src/ │ │ ├── dllmain.cpp │ │ ├── LuaExport.cpp │ │ └── YourFeatures.cpp │ └── YourDLL.vcxproj ├── lib/ (output directory - both projects output here) └── YourProject.sln

Solution Configuration
The solution file should be configured for:
  • Platforms: Debug|x86 and Release|x86 (NOT Win32 in solution)
  • Project Dependencies: Your DLL depends on Lua5.1-BZR

Testing Your DLL
  • Build both projects in same configuration
  • Files output to lib/ directory automatically
  • Copy yourmodulename.dll to workshop directory
  • Test with RequireFix: local yourmod = require("yourmodulename")
  • Verify functions work: yourmod.YourFunction("test", 123)
  • Check debug console output in debug builds

Common Pitfalls
  • Using { NULL, NULL } instead of { 0, 0 } for terminator
  • Returning 1 instead of 0 from luaopen_ function
  • Not matching the exact character set configuration (MultiByte vs Unicode)
  • Wrong library names (missing -debug suffix for debug builds)
  • 32-bit/64-bit mismatch (BZR is 32-bit only)
  • Not using the custom Lua library
  • Mixing Debug/Release runtimes causes heap corruption

IMPORTANT: Build From Source
Always compile the project yourself - do not use pre-built binaries!

  • MSVC updates frequently and breaks binary compatibility
  • Different compiler versions create incompatible runtime libraries
  • Heap allocation compatibility requires matching build environments
  • Custom Lua library must be compiled with same toolchain

Recommended Development Process
  • Clone ExtraUtilities: https://github.com/VTrider/ExtraUtilities
  • Copy the project structure as your starting template
  • Use the same build configuration (compiler flags, runtime library, etc.)
  • Compile everything from source with your current MSVC version
  • Never mix binaries from different compiler versions

Why Source-Only Approach
  • MSVC runtime library changes break compatibility
  • Heap corruption from mismatched runtimes
  • Custom Lua patches may not work with different builds
  • Game engine expects specific ABI conventions
  • Debug vs Release configurations must match usage
Server Protocol Documentation
Overview

This section provides technical information about the Battlezone 98 Redux server communication protocol, enabling developers to create their own tools and interfaces that interact with the BZ98 multiplayer system.

Server Infrastructure

Primary Server Endpoint
Host: battlezone98mp.webdev.rebellion.co.uk Port: 1337 Protocol: WebSocket (WS/WSS)

Connection URLs
  • Non-secure: ws://battlezone98mp.webdev.rebellion.co.uk:1337/
  • Secure: wss://battlezone98mp.webdev.rebellion.co.uk:1337/

Network Architecture
The BZ98 server uses a hybrid approach:
  • WebSocket: For lobby management, chat, and external tool communication
  • P2P UDP: For actual game traffic during matches
  • Centralized matchmaking: Server coordinates lobby creation and joining

WebSocket Protocol

Connection Requirements
  • Standard WebSocket connection (RFC 6455)
  • JSON message format
  • No additional subprotocols required
  • Supports both secure (WSS) and non-secure (WS) connections

Connection Process
  • Establish WebSocket connection to server
  • Send authentication message
  • Receive authorization response
  • Begin normal message exchange

Connection States
Monitor WebSocket.readyState:
  • 0 (CONNECTING): Connection being established
  • 1 (OPEN): Connection ready for communication
  • 2 (CLOSING): Connection being closed
  • 3 (CLOSED): Connection closed

Authentication Flow

Initial Authentication
Send immediately after connection opens:

{ "type": "DoAuth", "content": { "displayName": "YourDisplayName", "clientVersion": "2.2.301", "gameVersion": "2.2.301" } }

Server Response
{ "type": "OnAuthorization", "data": { "userId": "unique_user_id", "success": true } }

Important: Save the userId - this identifies your session and is used in subsequent operations.

Version Requirements
  • clientVersion: Should match current BZ98 version (2.2.301 as of writing)
  • gameVersion: Must match server expectations
  • displayName: 20 character limit

Message Structure

Outgoing Message Format
{ "type": "MessageType", "content": <data> }

Incoming Message Format
{ "type": "MessageType", "data": <data> }

Note: Outgoing messages use "content", incoming use "data".

Message Transmission
  • All messages are JSON strings
  • Send via WebSocket.send(JSON.stringify(message))
  • Receive via WebSocket message event handler

Core Message Types

Chat System

Send Chat Message
{ "type": "DoSendChat", "content": "Your message text" }

Receive Chat Message
{ "type": "OnChatMessage", "data": { "text": "Message content", "speakerId": "user_id", "time": "2025-08-26 21:48:24" } }

Note: Time format is "YYYY-MM-DD HH:MM:SS" in 24-hour format.

Lobby Management

Create Lobby
{ "type": "DoCreateLobby", "content": { "name": "Lobby Name", "isPrivate": false, "memberLimit": 20000 } }

Join Lobby
{ "type": "DoJoinLobby", "content": lobby_id }

Exit Lobby
{ "type": "DoExitLobby", "content": lobby_id }

Lobby Updates
{ "type": "OnLobbyUpdate", "data": { "id": "lobby_id", "metadata": { "name": "Lobby Name", "userCount": 5, "memberLimit": 20 }, "users": { "user_id": { "name": "PlayerName", "id": "S1234567890 " } }, "isPrivate": false, "isLocked": false, "owner": "user_id" } }

Session Management

Disconnect
{ "type": "DoExitLounge", "content": true }

User ID System

ID Format Patterns
  • Steam Users: 1234567890 (S + 64-bit Steam ID)
  • Bots/System: B000000000 (B prefix)
  • Other Platforms: Various prefixes depending on authentication method

Steam Integration
Steam IDs can be converted to profile URLs:

Lobby System

Lobby Types
The server supports different lobby purposes:
  • Game Lobbies: For actual BZ98 matches
  • Chat Lobbies: For community chat (often contain "~chat~" in name)

Lobby Metadata
Lobbies contain various metadata fields:
  • name: Display name
  • userCount: Current user count
  • memberLimit: Maximum users (typically 20000 for chat lobbies)
  • isPrivate: Whether lobby is private
  • isLocked: Whether lobby is locked to new joins
  • owner: User ID of lobby creator

User Presence
Track users in lobbies via the users object:
"users": { "S1234567890": { "name": "PlayerName", "id": "S1234567890" } }

Game Startup & Networking

BZ98 Game Initialization Sequence
Based on game logs, BZ98 follows this startup pattern:

1. Game executable starts 2. BZRNet P2P Socket opens (large buffers: 1MB receive, 32KB send) 3. Display system initializes 4. Language and localization loads 5. BZR Network system initializes 6. CNetGameLobby (matchmaking) starts 7. Audio and UI systems load 8. BZRNetInterface connects to WebSocket server

Network Buffer Sizes
The game uses specific buffer configurations:
  • Receive Buffer: 1,048,576 bytes (1MB)
  • Send Buffer: 32,768 bytes (32KB)
  • P2P Port: Dynamic assignment (e.g., port 49936)

Dual Network Stack
BZ98 operates with two network layers:
  • WebSocket Interface: Lobby management, chat, matchmaking
  • P2P UDP: Direct game traffic during matches

Implementation Notes

Connection Best Practices
  • Always check WebSocket state before sending messages
  • Implement reconnection logic for dropped connections
  • Handle both secure and non-secure WebSocket variants
  • Store user ID from authentication for session management

Message Handling
  • Parse incoming JSON carefully - structure may vary
  • Handle both "data" and "content" fields in responses
  • Implement timeout handling for server responses
  • Log unknown message types for debugging

CORS Considerations
When developing web-based tools:
  • HTTPS pages cannot connect to ws:// (use wss://)
  • Consider CORS restrictions for Steam API integration
  • Some browsers may block mixed content scenarios

Error Handling
Common connection issues:
  • Server unavailable (connection timeout)
  • Version mismatch (authentication failure)
  • Network firewall blocking WebSocket ports
  • Mixed content security restrictions

Protocol Limitations

Known Constraints
  • Display names limited to 20 characters
  • Lobby member limits vary by type
  • Message rate limiting may be enforced server-side
  • Some message types may be game-version specific

Future Compatibility
  • Protocol may evolve with game updates
  • Version checking is enforced during authentication
  • Monitor game logs for protocol changes in new releases

Protocol Version: BZ98 Redux 2.2.301
Last Updated: September 2025
Optimization
Performance Optimization

Memory Limitations (CRITICAL)

Battlezone 98 Redux is a 32-bit application limited to 4GB of memory. This affects all modding decisions:

Texture Format Requirements
- Always use .DDS format for textures when possible
- DDS provides better compression and faster loading
- Reduces memory usage significantly compared to PNG/TGA
- Use DXT1 compression for textures without alpha
- Use DXT5 compression for textures with alpha channels
- Add mipmaps for help with optimization, though it does increase file size

DDS Conversion Process
# Using GIMP DDS Plugin 1. Open texture in GIMP 2. Export As → filename.dds 3. Compression: DXT1 (no alpha) or DXT5 (with alpha) 4. Generate Mipmaps: Yes 5. Format: sRGB (for diffuse textures)

Model Optimization

Most BZR Models are between 2000 and 10000 tris. Try to stay on the lower end of that.

Texture Size Recommendations
Unit Textures: 1024x1024 Building Textures: 1024x1024 or 2048x2048 maximum

Map Optimization

Terrain Optimization
- Limit texture variety: Each unique texture uses memory
- Use texture atlases: Combine multiple textures into single files

Object Placement
MAX_ENTITY is 5120 (allowing 1024 non-GameObject ENTITY entries beyond the 4096 GameObject limit). You may start running into problems if you push GameObject counts high enough even if you don't reach the 4096 limit.

Scripting Performance
Don't run expensive function calls in Update() like AllObjects() or AllCraft()
Instead, use timers to stagger out the calls if they are needed often.

Audio Optimization

File Format Requirements
- Use WAV format: 8-bit unsigned, 22050 Hz maximum
- Avoid large audio files: Keep under 1MB when possible
- Use compression: For music, OGG format supported
- Limit simultaneous sounds: Too many can cause stuttering

Testing Performance

Performance Testing Checklist
1. Monitor frame rate with large unit counts
2. Test on lower-end hardware when possible
3. Check memory usage during extended play
4. Verify loading times for maps and assets
5. Test multiplayer sync with multiple players

Common Performance Issues
- Too many high-poly models visible simultaneously
- Excessive particle effects in combat
- Large uncompressed textures eating memory
- Complex Lua scripts running every frame
- Too many simultaneous audio sources

Best Practices Summary
- Use .DDS for all textures when possible
- Compress textures to save on file space, if it doesn't affect visual quality too badly
- Ensure Lua scripts don't call things every frame in Update() when possible
- Keep models to a reasonable tri count like <10000
BattleZone 98 Redux Known Issues
Compiled For BattleZone 98 Redux, Version 2.2.301, PC Steam

This is a comprehensive list of known bugs in Battlezone 98 Redux, compiled from community reports and testing. This list is intended to help players understand current issues and find workarounds where possible.
Critical Crash Bugs
Unit weaponMask Crash
Description: Units with
weaponMask=00000
told to follow an allied unit causes instant game crash.
Workaround: Avoid setting weaponMask to all zeros when using follow commands.
Pilot Hardpoint Crash
Description: Having pilot hardpoints under craft class header in ODF files causes hard crash.
Workaround: Move hardpoints ABOVE craft class header in ODF files.
renderCount Allocation Crash
Description: When renderCount is nil or isn't read properly, leads to "invalid allocation size" crash in ParameterDB system.
Random Crashes
Description: Game crashes seemingly at random, even in stock content. No errors given in log files.
Factors that increase crash frequency:
  • Shadows on max setting
  • Alt+Tab or Alt+Enter
  • Non-fullscreen mode
  • DX11 renderer
  • Non-English localization
Custom Campaign BMP Crash
Description: Some BMP formats cause hard crash when launching Custom Campaigns.
Workaround: Use 24-bit BMP format with "do not write color space info" setting.
Division by Zero - MagnetClass
Description: Division by zero error in MagnetClass when the range is zero.
Editor Bugs
Editor State Bug
Description: First Ctrl+E works fine, but quitting to menu and launching again causes bugged state (empty minimap). 100% crash when trying to change tiles.
Workaround: Restart game completely.
Object Placement Bug
Description: Cannot place objects in Shift+F10 mode in editor.
Workaround: Pause the game using keyboard Pause button first.
Edit Task Mode Crash
Description: Clicking on units in Shift+F9 "edit task" mode with mismatching class label and AI process crashes game.
Multiplayer/Network Bugs
Network Lag Issues
Description: Severe lag problems with packet loss and connection drops. Much worse than BZ 1.5 netcode.
Factors: Large amounts of scrap, deploying multiple armory powerups simultaneously.
Partial Workaround: Use community workshop items that partially mitigate lag.
Host Leaving Bug
Description: When MPI host leaves, game becomes unstable.
Freecam Exploit
Description: Freecam mode is possible in multiplayer, providing unfair advantage.
Invite with Password Bug
Description: Friend invites with password lobbies don't work - password isn't included in invite string.
Map List Auto-Refresh
Description: Map list refreshes every few seconds, making scrolling difficult.
Note: Steam Workshop auto-refresh issue. GOG version doesn't have this problem.
Multiplayer Sync Issues
Description: Various multiplayer synchronization problems:
  • Building objects via Lua only appears for hosting player when built by non-host
  • Removing non-local objects causes them to "respawn"
  • SetName() only affects the machine that executed it
  • Objective markers not synced by default
  • GetPlayerHandle with team number always returns nil
  • Weapon changes require Send/Receive to sync properly
Unit Behavior Bugs
WeaponMines Friendly Fire
Description: WeaponMines attack allied ships when pilots hop out briefly.
Minelayer Hardpoint Bug
Description: Minelayers only use their first hardpoint for "Lay Mines" command, regardless of weaponMask or Lua settings.
Howitzer Bugs
  • Retaliation Bug: Howitzers fire undeployed at enemy pilots who snipe them while on follow/go orders
  • Weapon Mask Bug: Howitzers only use first hardpoint regardless of weaponMask setting
APC Deployment Bug
Description: Cannot deploy soldiers when targeting allied unit while near enemies.
Workaround: Untarget the allied unit first.
Neutral Unit Behavior
Description: Neutral units attack you but you cannot order attacks on them (should be opposite).
Constructor Remote Build
Description: AI constructors can build structures from kilometers away after being killed and rebuilt.
Tugs Cargo Bug
Description: Tugs that start missions with cargo or load cargo after save/load cannot drop it.
Jump Sniping Exploit
Description: No weapon switch required to keep jumping, making hop sniping too easy compared to BZ 1.5.
AIP Custom/Stock Mix Bug
Description: When mixing custom and stock units in custom producer, AI only builds custom units even when stock units are in AIP.
Workaround: Copy stock ODF files to make them "custom" or use Lua to force AI building.
Graphics/Rendering Bugs
DX11 Renderer Issues
Description: Very unstable, causes crashes or fails to render textures, especially custom ones.
Terrain Texture Deformation
Description: Terrain tile texture type 0 gets deformed differently based on orientation. Transitions between type 0 and others also appear deformed.
Walker Cockpit Jitter
Description: Walker cockpits jitter when walking/jumping.
Workaround: Remove cockpit parent and parent to world (doesn't inherit movement but stops jitter).
Rotation Issues
Description: Various turret cockpits have wrong rotation. Redux cockpits inherit rotation from entire hierarchy.
Workaround: Add dummy bones or redo hierarchy.
NSDF Razor Material
Description: NSDF Razor material file makes the unit very dim.
Missing Textures
Description: Missing
bbcom2_n.dds
image in stock files.
Headlight Inconsistencies
Description: Some units missing headlights or have mixed headlight models/lamp bones.
Satellite View Bug
Description: Can see all objects via satellite even if not visible on radar (fog of war). This differs from BZ 1.5 behavior.
Targeting Camera Bug
Description: Entering satellite/F9 view while targeting causes target camera to remain on screen but not follow target.
Heightmap Smoothing
Description: Heightmap smoothing is ON by default, changing terrain and pathing including stock missions.
Animation Bugs
Skeleton Animation Issues
  • Loop Bug: Some animations only play once even if VDF/skeleton has them set to loop
  • Speed Bug: Animation speeds are hardcoded and don't respect VDF speed values or ODF animRate
NSDF Hangar Animation
Description: Door opening animation doesn't work on build.
Workaround: Fix skeleton.
File Format Bugs
TRN Line Feed Issue
Description: Saving or editing TRN files sometimes converts line feeds to wrong format. Game ignores text with incorrect line feed format.
Workaround: Convert TRN files to ANSI and force line feeds to CR LF format.
String Function Bug
Description: Functions like
GetOdf()
,
GetPilotClass()
,
GetWeaponClass()
,
GetClassSig()
, and
GetBase()
return strings with fixed character length including null characters.
Workaround: Use
string.gmatch()
to handle properly.
BattleZone 98 Redux Known Issues Pt 2
Lua/Scripting Issues
ObjectiveObjectives Iterator Bug
Description:
ObjectiveObjectives()
iterator is broken and only returns first result due to engine bug.

Mission/Campaign Bugs
Window Focus Bug
Description: Window loses focus and minimizes on end mission screen in custom campaigns.
TRO Mission Softlocks
Description: Various TRO missions have bugs that prevent completion.
Mission Briefing Text
Description: Briefing text gets cut off, requiring 10-15 line breaks for proper scroll display.
Inst4XMission Save Bug
Description: Inst4XMission type crashes on save game loads frequently.
Earthquake/Dayquake Save Bug
Description: Saves during earthquake/dayquake effects repeat those effects endlessly on load.
Camera Cinematic Bug
Description: Cinematic camera appears zoomed in incorrectly when triggered during satellite mode.
Splinter Bugs
  • Collision Bug: Violent collision with deployed sprayBuildingClass
  • Undead Bug: Destroyed splinters persist until finishing all bullets
  • Multiplayer Duplication: Splinters create multiple bullet copies for each player
Hardpoint Issues
Unit Hardpoint Misalignment
Description: Some units (NSDF scout/bomber) have hardpoints tied to strafe geos that throw off aim.
Workaround: Remove hardpoints from moving geo parents.
CCA Golem Reticle Bug
Description: Cannon reticle doesn't match hardpoint due to VDF parenting causing sway during movement.
Sniper Misalignment
Description: Sniper occasionally misaligned with reticle after ejection.
Map/Terrain Issues
Daywrecker Terrain Bug
Description: Terrain spire issues in multiplayer.
Mod/Workshop Issues
Mod File Loading
Description: Game loads ALL files from mods when entering menu, not just when launching. Duplicate material names cause crashes.
Armory Issues
Multiplayer Armory Glitches
Description: Various armory glitches in multiplayer including double/triple day wrecker detonations.
Day Wrecker Duplication
Description: Non-host players can be hit by multiple day wrecker explosions for single launch.
Sources & Credits
Acknowledgments
This comprehensive Battlezone modding reference guide was compiled from numerous sources within the Battlezone community. The information contained here represents decades of collective knowledge, experimentation, and documentation by dedicated community members.

Primary Contributors

General BlackDragon's Steam Guides
bluebanana's Steam Guides
DeusExCeteri's Steam Guides and extensive testing
ssuer's Terrain Painting and Lua Scripting Guides
systeme's 3d Modeling and Terrain Painting Tutorial
Piercing's "BZR on Linux" Guide
Business Lawyer's extensive testing
VTRider's extensive testing
DivisionByZero's extensive testing
Janne/Mario's extensive testing
GoombaBZ's extensive testing


UltraKen
• Original developer insights and technical knowledge
• Advanced understanding of Battlezone's engine architecture
• Creator of essential modding tools including makemap
• Deep knowledge of texture systems and terrain generation
Battlezone Lua Documentation[www.videoventure.org]

Essential Modding Tools

Official Tools (Included with BZ Install):
MakeZFS - Archive creation and management utility
MakeMAP - Terrain texture conversion and processing
MakeTRN - Terrain file generation and world creation

Community-Created Tools:
Business Lawyer's BZMAPIO - Blender Terrain Editor Addon for heightmap editing. See here as a starting point:
Kenshi IO Continued - Blender plugin for mesh editing and import/export
CodeBerg Link[codeberg.org]
VDF/SDF Blender Plugin by Commando - Complete 3D model workflow integration
Commando's Site[commando950.neocities.org]
DivisionByZero's VDF/SDF/Geo to Mesh Porting Script - Model conversion utilities
Available on Goomba's Site[bzmaps.net]
PEdit - Palette editor for color management and ACT file editing
Available on Goomba's Site[bzmaps.net]
Ogre Command Line Tools - Mesh processing and upgrader utilities
Available on Goomba's Site[bzmaps.net]
Mesh Normal Correction
GrizzlyOne95's Github[github.com]


Other Tools:
SmartNormal2.0 - Normal map generation tool[www.smart-page.net]
HxD Hex Editor - For advanced VDF editing and spinner modifications[mh-nexus.de]
Python - Needed for some tools and automation[www.python.org]
Blender. Use 3.6 LTS for commando950's plugin and 4.1+ for Business Lawyer's Terrain Plugin.[www.blender.org]
GIMP is a free image editing software that natively handles DDS[www.gimp.org]
Audacity - Free Audio Editing/Conversion/Recording Tool[www.audacityteam.org]
VLC - Can be used to convert to OGV video[www.videolan.org]


Disclaimer

Information Accuracy
While every effort has been made to ensure the accuracy of the information in this guide, some details may be incomplete or subject to change. The Battlezone modding community continues to discover new techniques and refine existing knowledge.

Tool Compatibility
Tool instructions and compatibility information were accurate at the time of compilation. Software updates, operating system changes, and hardware variations may affect functionality.

Contributing

How to Help

Community Contribution
• Share discoveries and techniques with the community
• Document your modding experiences and solutions
• Test and provide feedback on new tools and techniques
• Help newcomers learn modding skills

Knowledge Preservation
• Archive successful mod configurations
• Document troubleshooting solutions
• Maintain compatibility information
• Update guides with new discoveries

Contact Information

Community Channels
• Join Battlezone Discord servers for real-time discussion
• Participate in Steam Community discussions
• Share findings through Steam Guides
• Contribute to community wikis and resources

Final Thanks

Special appreciation to the entire Battlezone modding community for their dedication to preserving and expanding this classic game. The collaborative spirit and willingness to share knowledge has made comprehensive guides like this possible.

The passion and creativity of the community continues to bring new life to Battlezone, creating content that extends far beyond what was originally envisioned. This guide represents the collective wisdom of that community effort.

This guide stands on the shoulders of every community member who has ever shared a tip, created a tool, documented a discovery, or helped a fellow modder solve a problem.

---

Last Updated: 2025
Community contributions ongoing