Chapter 1.10: Enums & Preprocessor
Home | << Previous: Casting & Reflection | Enums & Preprocessor | Next: Error Handling >>
Goal: Understand enum declarations, enum reflection tools, bitflag patterns, constants, and the preprocessor system for conditional compilation.
Table of Contents
- Enum Declaration
- Using Enums
- Enum Reflection
- Bitflags Pattern
- Constants
- Preprocessor Directives
- Real-World Examples
- Common Mistakes
- Summary
- Navigation
Enum Declaration
Enums in Enforce Script define named integer constants grouped under a type name. They behave like int under the hood.
Explicit Values
enum EDamageState
{
PRISTINE = 0,
WORN = 1,
DAMAGED = 2,
BADLY_DAMAGED = 3,
RUINED = 4
};Implicit Values
If you omit values, they auto-increment from the previous value (starting at 0):
enum EWeaponMode
{
SEMI, // 0
BURST, // 1
AUTO, // 2
COUNT // 3 — common trick to get the total count
};Enum Inheritance
Enums can inherit from other enums. Values continue from the last parent value:
enum EBaseColor
{
RED = 0,
GREEN = 1,
BLUE = 2
};
enum EExtendedColor : EBaseColor
{
YELLOW, // 3
CYAN, // 4
MAGENTA // 5
};All parent values are accessible through the child enum:
int c = EExtendedColor.RED; // 0 — inherited from EBaseColor
int d = EExtendedColor.YELLOW; // 3 — defined in EExtendedColorNote: Enum inheritance is useful for extending vanilla enums in modded code without changing the original.
Using Enums
Enums act as int — you can assign them to int variables, compare them, and use them in switch statements:
EDamageState state = EDamageState.WORN;
// Compare
if (state == EDamageState.RUINED)
{
Print("Item is ruined!");
}
// Use in switch
switch (state)
{
case EDamageState.PRISTINE:
Print("Perfect condition");
break;
case EDamageState.WORN:
Print("Slightly worn");
break;
case EDamageState.DAMAGED:
Print("Damaged");
break;
case EDamageState.BADLY_DAMAGED:
Print("Badly damaged");
break;
case EDamageState.RUINED:
Print("Ruined!");
break;
}
// Assign to int
int stateInt = state; // 1
// Assign from int (no validation — any int value is accepted!)
EDamageState fromInt = 99; // No error, even though 99 is not a valid enum valueWarning: Enforce Script does not validate enum assignments. Assigning an out-of-range integer to an enum variable compiles and runs without error.
Enum Reflection
Enforce Script provides built-in functions to convert between enum values and strings.
typename.EnumToString
Convert an enum value to its name as a string:
EDamageState state = EDamageState.DAMAGED;
string name = typename.EnumToString(EDamageState, state);
Print(name); // "DAMAGED"This is invaluable for logging and UI display:
void LogDamageState(EntityAI item, EDamageState state)
{
string stateName = typename.EnumToString(EDamageState, state);
Print(item.GetType() + " is " + stateName);
}typename.StringToEnum
Convert a string back to an enum value. The function takes 2 parameters and returns int (the enum value, or -1 on failure):
int value = typename.StringToEnum(EDamageState, "RUINED");
Print(value.ToString()); // "4"This is used when loading enum values from config files or JSON:
// Loading from a config string
string configValue = "BURST";
int modeInt = typename.StringToEnum(EWeaponMode, configValue);
if (modeInt != -1)
{
EWeaponMode mode = modeInt;
Print("Loaded weapon mode: " + typename.EnumToString(EWeaponMode, mode));
}Bitflags Pattern
Enums with power-of-2 values create bitflags — multiple options combined in a single integer:
enum ESpawnFlags
{
NONE = 0,
PLACE_ON_GROUND = 1, // 1 << 0
CREATE_PHYSICS = 2, // 1 << 1
UPDATE_NAVMESH = 4, // 1 << 2
CREATE_LOCAL = 8, // 1 << 3
NO_LIFETIME = 16 // 1 << 4
};Combine with bitwise OR, test with bitwise AND:
// Combine flags
int flags = ESpawnFlags.PLACE_ON_GROUND | ESpawnFlags.CREATE_PHYSICS | ESpawnFlags.UPDATE_NAVMESH;
// Test a single flag
if (flags & ESpawnFlags.CREATE_PHYSICS)
{
Print("Physics will be created");
}
// Remove a flag
flags = flags & ~ESpawnFlags.CREATE_LOCAL;
// Add a flag
flags = flags | ESpawnFlags.NO_LIFETIME;DayZ uses this pattern extensively for object creation flags (ECE_PLACE_ON_SURFACE, ECE_CREATEPHYSICS, ECE_UPDATEPATHGRAPH, etc.).
Constants
Use const to declare immutable values. Constants must be initialized at declaration.
// Integer constants
const int MAX_PLAYERS = 60;
const int INVALID_INDEX = -1;
// Float constants
const float GRAVITY = 9.81;
const float SPAWN_RADIUS = 500.0;
// String constants
const string MOD_NAME = "MyMod";
const string CONFIG_PATH = "$profile:MyMod/config.json";
const string LOG_PREFIX = "[MyMod] ";Constants can be used as switch case values and array sizes:
// Array with const size
const int BUFFER_SIZE = 256;
int buffer[BUFFER_SIZE];
// Switch with const values
const int CMD_HELP = 1;
const int CMD_SPAWN = 2;
const int CMD_TELEPORT = 3;
switch (command)
{
case CMD_HELP:
ShowHelp();
break;
case CMD_SPAWN:
SpawnItem();
break;
case CMD_TELEPORT:
TeleportPlayer();
break;
}Note: There is no
constfor reference types (objects). You cannot make an object reference immutable.
Preprocessor Directives
The Enforce Script preprocessor runs before compilation, enabling conditional code inclusion. It works similarly to C/C++ preprocessor but with fewer features.
#ifdef / #ifndef / #endif
Conditionally include code based on whether a symbol is defined:
// Include code only if DEVELOPER is defined
#ifdef DEVELOPER
Print("[DEBUG] Diagnostics enabled");
#endif
// Include code only if a symbol is NOT defined
#ifndef SERVER
// Client-only code
CreateClientUI();
#endif
// If-else pattern
#ifdef SERVER
Print("Running on server");
#else
Print("Running on client");
#endif#define
Define your own symbols (no value — just existence):
#define MY_MOD_DEBUG
#ifdef MY_MOD_DEBUG
Print("Debug mode active");
#endifNote: Enforce Script
#defineonly creates existence flags. It does not support macro substitution (no#define MAX_HP 100— useconstinstead).
Common Engine Defines
DayZ provides these built-in defines based on build type and platform:
| Define | When Available | Use For |
|---|---|---|
SERVER | Running on dedicated server | Server-only logic |
DEVELOPER | Developer build of DayZ | Dev-only features |
DIAG_DEVELOPER | Diagnostic build | Diagnostic menus, debug tools |
PLATFORM_WINDOWS | Windows platform | Platform-specific paths |
PLATFORM_XBOX | Xbox platform | Console-specific UI |
PLATFORM_PS4 | PlayStation platform | Console-specific logic |
BUILD_EXPERIMENTAL | Experimental branch | Experimental features |
void InitPlatform()
{
#ifdef PLATFORM_WINDOWS
Print("Running on Windows");
#endif
#ifdef PLATFORM_XBOX
Print("Running on Xbox");
#endif
#ifdef PLATFORM_PS4
Print("Running on PlayStation");
#endif
}Custom Defines via config.cpp
Mods can define their own symbols in config.cpp using the defines[] array. These are available to all scripts loaded after this mod:
class CfgMods
{
class MyMod_MissionSystem
{
// ...
defines[] = { "MY_MISSIONS_LOADED" };
// ...
};
};Now other mods can detect whether your missions mod is loaded:
#ifdef MY_MISSIONS_LOADED
// Missions mod is loaded — use its API
MyMissionManager.Start();
#else
// Missions mod is not loaded — skip or use fallback
Print("Mission system not detected");
#endifReal-World Examples
Platform-Specific Code
string GetSavePath()
{
#ifdef PLATFORM_WINDOWS
return "$profile:MyMod/saves/";
#else
return "$saves:MyMod/";
#endif
}Optional Mod Dependencies
This is the standard pattern for mods that optionally integrate with other mods:
class MyModManager
{
void Init()
{
Print("[MyMod] Initializing...");
// Core features always available
LoadConfig();
RegisterRPCs();
// Optional integration with MyFramework
#ifdef MY_FRAMEWORK
Print("[MyMod] Framework detected — using unified logging");
RegisterWithCore();
#endif
// Optional integration with Community Framework
#ifdef JM_CommunityFramework
GetRPCManager().AddRPC("MyMod", "RPC_Handler", this, 2);
#endif
}
}Debug-Only Diagnostics
void ProcessAI(DayZInfected zombie)
{
vector pos = zombie.GetPosition();
float health = zombie.GetHealth("", "Health");
// Heavy debug logging — only in diagnostic builds
#ifdef DIAG_DEVELOPER
Print(string.Format("[AI] Zombie %1 at %2, HP: %3",
zombie.GetType(), pos.ToString(), health.ToString()));
// Draw debug sphere (only works in diag builds)
Debug.DrawSphere(pos, 1.0, Colors.RED, ShapeFlags.ONCE);
#endif
// Actual logic runs in all builds
if (health <= 0)
{
HandleZombieDeath(zombie);
}
}Server vs Client Logic
class MissionHandler
{
void OnMissionStart()
{
#ifdef SERVER
// Server: load mission data, spawn objects
LoadMissionData();
SpawnMissionObjects();
NotifyAllPlayers();
#else
// Client: set up UI, subscribe to events
CreateMissionHUD();
RegisterClientRPCs();
#endif
}
}Best Practices
- Add a
COUNTsentinel value as the last enum entry to easily iterate or validate ranges (e.g.,for (int i = 0; i < EMode.COUNT; i++)). - Use power-of-2 values for bitflag enums and combine them with
|; test with&; remove with& ~FLAG. - Use
constinstead of#definefor numeric constants -- Enforce Script#defineonly creates existence flags, not value macros. - Define a
defines[]array in your mod'sconfig.cppto expose cross-mod detection symbols (e.g.,"STARDZ_CORE"). - Always validate enum values loaded from external data (configs, RPCs) -- Enforce Script accepts any
intas an enum with no range check.
Observed in Real Mods
Patterns confirmed by studying professional DayZ mod source code.
| Pattern | Mod | Detail |
|---|---|---|
#ifdef for optional mod integration | Expansion / COT | Checks #ifdef JM_CF or #ifdef EXPANSIONMOD before calling cross-mod APIs |
| Bitflag enums for spawn options | Vanilla DayZ | ECE_PLACE_ON_SURFACE, ECE_CREATEPHYSICS etc. combined with | for CreateObjectEx |
typename.EnumToString for logging | Expansion / Dabs | Damage states and event types are logged as readable strings instead of raw ints |
defines[] in config.cpp | StarDZ Core / Expansion | Each mod declares its own symbol so other mods can detect it with #ifdef |
Theory vs Practice
| Concept | Theory | Reality |
|---|---|---|
| Enum assignment validation | Expect compiler to reject invalid values | EDamageState state = 999 compiles fine -- no range checking whatsoever |
#define MAX_HP 100 | Works like C/C++ macro | Enforce Script #define creates only existence flags; use const int for values |
switch case stacking | Multiple cases sharing one handler | Fall-through works in Enforce Script (same as C/C++) -- stack empty cases to share a handler |
Common Mistakes
1. Using enums as validated types
// PROBLEM — no validation, any int is accepted
EDamageState state = 999; // Compiles fine, but 999 is not a valid state
// SOLUTION — validate manually when loading from external data
int rawValue = LoadFromConfig();
if (rawValue >= 0 && rawValue <= EDamageState.RUINED)
{
EDamageState state = rawValue;
}2. Trying to use #define for value substitution
// WRONG — Enforce Script #define does NOT support values
#define MAX_HEALTH 100
int hp = MAX_HEALTH; // Compile error!
// CORRECT — use const instead
const int MAX_HEALTH = 100;
int hp = MAX_HEALTH;3. Nesting #ifdef incorrectly
// CORRECT — nested ifdefs are fine
#ifdef SERVER
#ifdef MY_FRAMEWORK
MyLog.Info("MyMod", "Server + Core");
#endif
#endif
// WRONG — missing #endif causes mysterious compile errors
#ifdef SERVER
DoServerStuff();
// forgot #endif here!4. Forgetting break when you do NOT want fall-through
// Enforce Script switch/case DOES fall through when break is omitted (same as C/C++).
// Vanilla code uses intentional fall-through (biossessionservice.c:182).
// Stacking cases to share a handler WORKS:
switch (state)
{
case EDamageState.PRISTINE:
case EDamageState.WORN:
Print("Good condition"); // Reached for BOTH PRISTINE and WORN
break;
}
// But forgetting break is a bug:
switch (state)
{
case EDamageState.PRISTINE:
Print("Pristine");
// Missing break! Falls through to WORN case
case EDamageState.WORN:
Print("Worn");
break;
}
// If state == PRISTINE, prints BOTH "Pristine" and "Worn"Summary
Enums
| Feature | Syntax |
|---|---|
| Declare | enum EName { A = 0, B = 1 }; |
| Implicit | enum EName { A, B, C }; (0, 1, 2) |
| Inherit | enum EChild : EParent { D, E }; |
| To string | typename.EnumToString(EName, value) |
| From string | int val = typename.StringToEnum(EName, "A") |
| Bitflag combine | `flags = A |
| Bitflag test | if (flags & A) |
Preprocessor
| Directive | Purpose |
|---|---|
#ifdef SYMBOL | Compile if symbol exists |
#ifndef SYMBOL | Compile if symbol does NOT exist |
#else | Alternate branch |
#endif | End conditional block |
#define SYMBOL | Define a symbol (no value) |
Key Defines
| Define | Meaning |
|---|---|
SERVER | Dedicated server |
DEVELOPER | Developer build |
DIAG_DEVELOPER | Diagnostic build |
PLATFORM_WINDOWS | Windows OS |
Custom: defines[] | Your mod's config.cpp |
Navigation
| Previous | Up | Next |
|---|---|---|
| 1.9 Casting & Reflection | Part 1: Enforce Script | 1.11 Error Handling |
