Chapter 1.10: Enums & Preprocessor
Domů | << Předchozí: Přetypování a reflexe | Výčty a preprocesor | Další: Zpracování chyb >>
Goal: Understand enum declarations, enum reflection přílišls, bitflag patterns, constants, and the preprocessor system for conditional compilation.
Obsah
- Enum Declaration
- Using Enums
- Enum Reflection
- Bitflags Pattern
- Constants
- Preprocessor Directives
- Real-World Examples
- Běžné 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
Pokud 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 jiný 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 EExtendedColorPozná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:
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 valueVarová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:
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 řetězec back to an enum value:
int value;
typename.StringToEnum(EDamageState, "RUINED", value);
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;
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:
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.).
Konstanty
Use const to declare immutable values. Constants musí být 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 lze použít 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;
}Poznámka: 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 — jen existence):
#define MY_MOD_DEBUG
#ifdef MY_MOD_DEBUG
Print("Debug mode active");
#endifPoznámka: Enforce Script
#definepouze creates existence flags. It does not support macro substitution (no#define MAX_HP 100— useconstmísto toho).
Běžné Engine Defines
DayZ provides these vestavěný 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 přílišls |
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 dostupný to all scripts loaded after this mod:
class CfgMods
{
class MyMod_MissionSystem
{
// ...
defines[] = { "MY_MISSIONS_LOADED" };
// ...
};
};Now jiný 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");
#endifPříklady z praxe
Platform-Specific Code
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:
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
}
}Osvědčené postupy
- Přidejte a
COUNTsentinel 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
constmísto#definefor numeric constants -- Enforce Script#definepouze creates existence flags, not value macros. - Define a
defines[]array in your mod'sconfig.cppto 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
intas an enum with no range check.
Pozorováno v reálných modech
Patterns confirmed by studying professional DayZ mod source code.
| Vzor | Mod | Detail |
|---|---|---|
#ifdef for volitelný 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 dokoncet types are logged as readable strings místo raw ints |
defines[] in config.cpp | StarDZ Core / Expansion | Each mod declares its own symbol so jiný mods can detect it with #ifdef |
Teorie vs praxe
| Concept | Theory | Reality |
|---|---|---|
| Enum assignment platnýation | Expect compiler to reject neplatný values | EDamageState state = 999 compiles fine -- no range checking whatsoever |
#define MAX_HP 100 | Works like C/C++ macro | Enforce Script #define creates pouze existence flags; use const int for values |
switch case stacking | Multiple cases sharing one handler | No 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
// 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 insprávně
// 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
// 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:
if (state == EDamageState.PRISTINE || state == EDamageState.WORN)
{
Print("Good condition");
}Shrnutí
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 | typename.StringToEnum(EName, "A", out val) |
| 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 |
Navigace
| Previous | Up | Next |
|---|---|---|
| 1.9 Casting & Reflection | Part 1: Enforce Script | 1.11 Error Handling |
