Skip to content

Chapter 1.10: Enums & Preprocessor ​


Goal: Understand enum declarations, enum reflection přílišls, bitflag patterns, constants, and the preprocessor system for conditional compilation.


Obsah ​


Enum Declaration ​

Enums in Enforce Script define named integer constants grouped under a type name. They behave like int under the hood.

Explicit Values ​

c
enum EDamageState
{
    PRISTINE  = 0,
    WORN      = 1,
    DAMAGED   = 2,
    BADLY_DAMAGED = 3,
    RUINED    = 4
};

Implicit Values ​

Pokud omit values, they auto-increment from the previous value (starting at 0):

c
enum EWeaponMode
{
    SEMI,       // 0
    BURST,      // 1
    AUTO,       // 2
    COUNT       // 3 — common trick to get the total count
};

Enum Inheritance ​

Enums can inherit from jiný enums. Values continue from the last parent value:

c
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:

c
int c = EExtendedColor.RED;      // 0 — inherited from EBaseColor
int d = EExtendedColor.YELLOW;   // 3 — defined in EExtendedColor

Poznámka: Enum inheritance is užitečný for extending vanilla enums in modded code without changing the original.


Using Enums ​

Enums act as int — můžete assign them to int variables, compare them, and use them in switch statements:

c
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 value

Varování: Enforce Script does not platnýate enum assignments. Assigning an out-of-range integer to an enum variable compiles and runs without error.


Enum Reflection ​

Enforce Script provides vestavěný functions to convert mezi enum values and strings.

typename.EnumToString ​

Convert an enum value to its name as řetězec:

c
EDamageState state = EDamageState.DAMAGED;
string name = typename.EnumToString(EDamageState, state);
Print(name);  // "DAMAGED"

This is invaluable for logging and UI display:

c
void LogDamageState(EntityAI item, EDamageState state)
{
    string stateName = typename.EnumToString(EDamageState, state);
    Print(item.GetType() + " is " + stateName);
}

typename.StringToEnum ​

Convert řetězec back to an enum value:

c
int value;
typename.StringToEnum(EDamageState, "RUINED", value);
Print(value.ToString());  // "4"

This is used when loading enum values from config files or JSON:

c
// Loading from a config string
string configValue = "BURST";
int modeInt;
if (typename.StringToEnum(EWeaponMode, configValue, modeInt))
{
    EWeaponMode mode = modeInt;
    Print("Loaded weapon mode: " + typename.EnumToString(EWeaponMode, mode));
}

Bitflags Pattern ​

Enums with power-of-2 values create bitflags — více options combined in a jeden integer:

c
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:

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


Konstanty ​

Use const to declare immutable values. Constants musí být initialized at declaration.

c
// 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 lze použít as switch case values and array sizes:

c
// 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;
}

Poznámka: There is no const for 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:

c
// 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 — jen existence):

c
#define MY_MOD_DEBUG

#ifdef MY_MOD_DEBUG
    Print("Debug mode active");
#endif

Poznámka: Enforce Script #define pouze creates existence flags. It does not support macro substitution (no #define MAX_HP 100 — use const místo toho).

Běžné Engine Defines ​

DayZ provides these vestavěný defines based on build type and platform:

DefineWhen AvailableUse For
SERVERRunning on dedicated serverServer-only logic
DEVELOPERDeveloper build of DayZDev-only features
DIAG_DEVELOPERDiagnostic buildDiagnostic menus, debug přílišls
PLATFORM_WINDOWSWindows platformPlatform-specific paths
PLATFORM_XBOXXbox platformConsole-specific UI
PLATFORM_PS4PlayStation platformConsole-specific logic
BUILD_EXPERIMENTALExperimental branchExperimental features
c
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 dostupný to all scripts loaded after this mod:

cpp
class CfgMods
{
    class MyMod_MissionSystem
    {
        // ...
        defines[] = { "MY_MISSIONS_LOADED" };
        // ...
    };
};

Now jiný mods can detect whether your missions mod is loaded:

c
#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");
#endif

Příklady z praxe ​

Platform-Specific Code ​

c
string GetSavePath()
{
    #ifdef PLATFORM_WINDOWS
        return "$profile:MyMod/saves/";
    #else
        return "$saves:MyMod/";
    #endif
}

Optional Mod Dependencies ​

Toto je standard pattern for mods that volitelnýly integrate with jiný mods:

c
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 ​

c
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 ​

c
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
    }
}

Osvědčené postupy ​

  • Přidejte a COUNT sentinel value as the last enum entry to easily iterate or platnýate 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 const místo #define for numeric constants -- Enforce Script #define pouze creates existence flags, not value macros.
  • Define a defines[] array in your mod's config.cpp to expose cross-mod detection symbols (e.g., "STARDZ_CORE").
  • Vždy platnýate enum values loaded from externí data (configs, RPCs) -- Enforce Script accepts jakýkoli int as an enum with no range check.

Pozorováno v reálných modech ​

Patterns confirmed by studying professional DayZ mod source code.

VzorModDetail
#ifdef for volitelný mod integrationExpansion / COTChecks #ifdef JM_CF or #ifdef EXPANSIONMOD before calling cross-mod APIs
Bitflag enums for spawn optionsVanilla DayZECE_PLACE_ON_SURFACE, ECE_CREATEPHYSICS etc. combined with | for CreateObjectEx
typename.EnumToString for loggingExpansion / DabsDamage states and dokoncet types are logged as readable strings místo raw ints
defines[] in config.cppStarDZ Core / ExpansionEach mod declares its own symbol so jiný mods can detect it with #ifdef

Teorie vs praxe ​

ConceptTheoryReality
Enum assignment platnýationExpect compiler to reject neplatný valuesEDamageState state = 999 compiles fine -- no range checking whatsoever
#define MAX_HP 100Works like C/C++ macroEnforce Script #define creates pouze existence flags; use const int for values
switch case stackingMultiple cases sharing one handlerNo fall-through in Enforce Script -- každý case is nezávislý; use if/|| místo toho

Časté chyby ​

1. Using enums as platnýated types ​

c
// 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 ​

c
// 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 insprávně ​

c
// 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 that switch/case has no fall-through ​

c
// In C/C++, cases fall through without break.
// In Enforce Script, each case is INDEPENDENT — no fall-through.

switch (state)
{
    case EDamageState.PRISTINE:
    case EDamageState.WORN:
        Print("Good condition");  // Only reached for WORN, not PRISTINE!
        break;
}

If potřebujete více cases to share logic, use if/else:

c
if (state == EDamageState.PRISTINE || state == EDamageState.WORN)
{
    Print("Good condition");
}

Shrnutí ​

Enums ​

FeatureSyntax
Declareenum EName { A = 0, B = 1 };
Implicitenum EName { A, B, C }; (0, 1, 2)
Inheritenum EChild : EParent { D, E };
To stringtypename.EnumToString(EName, value)
From stringtypename.StringToEnum(EName, "A", out val)
Bitflag combine`flags = A
Bitflag testif (flags & A)

Preprocessor ​

DirectivePurpose
#ifdef SYMBOLCompile if symbol exists
#ifndef SYMBOLCompile if symbol does NOT exist
#elseAlternate branch
#endifEnd conditional block
#define SYMBOLDefine a symbol (no value)

Key Defines ​

DefineMeaning
SERVERDedicated server
DEVELOPERDeveloper build
DIAG_DEVELOPERDiagnostic build
PLATFORM_WINDOWSWindows OS
Custom: defines[]Your mod's config.cpp

Released under CC BY-SA 4.0 | Code examples under MIT License