# Coding Style All source is C11. Everything below is derived from the existing codebase — match it exactly. --- ## File structure ### Headers (`.h`) ```c /** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #pragma once #include "direct/dependency.h" ``` - `#pragma once` always, never `#ifndef` guards. - No blank line between the license block and `#pragma once`. - One blank line between `#pragma once` and the first `#include`. ### Sources (`.c`) ```c /** * Copyright (c) 2026 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "thisfile.h" #include "assert/assert.h" #include "util/memory.h" ``` - First include is always the matching `.h` for this `.c` file. - Remaining includes follow with no separator unless logically grouped (then one blank line between groups — see [Include order](#include-order)). --- ## Include order In `.c` files, include in this order with a blank line between each group: 1. The matching header (e.g. `#include "entity.h"`) 2. Core utilities (`assert/assert.h`, `util/memory.h`, `util/math.h`, etc.) 3. Engine subsystems (`display/...`, `input/...`, etc.) 4. Domain subsystems (`rpg/...`, `scene/...`, etc.) In `.h` files, only include what the header directly requires. Never include more than necessary to make the type definitions in that header compile. All include paths are relative to `src/dusk/` (the root include directory). Use the full path: ```c #include "rpg/overworld/map.h" // correct #include "map.h" // wrong ``` --- ## Line length 80-character limit. Break before it, not after. Multi-parameter function signatures break one param per line, with 2-space indent, closing `)` on its own line before `{`: ```c errorret_t textureInit( texture_t *texture, const int32_t width, const int32_t height, const textureformat_t format, const texturedata_t data ) { ``` Same rule for calls that don't fit on one line: ```c assertTrue( data.paletted.palette->count == mathNextPowTwo(data.paletted.palette->count), "Palette color count must be a power of 2" ); ``` --- ## Indentation and spacing - **2 spaces** per indent level. No tabs. - No space between a control keyword and its `(`: ```c if(x) { // correct if (x) { // wrong ``` - No space between a function name and its `(` in either declarations or calls. - Opening brace on the same line: ```c void entityUpdate(entity_t *entity) { if(x) { for(int i = 0; i < n; i++) { ``` - Closing brace always on its own line, except `} else {` and `} while(...)`. - One blank line between function definitions in `.c` files. - No trailing whitespace. --- ## Naming | Kind | Convention | Example | |---|---|---| | Types (struct/union/typedef) | `snake_case_t` | `entity_t`, `worldpos_t` | | Struct tags | `struct name_s` | `struct entity_s` | | Union tags | `union name_u` | `union texturedata_u` | | Enum tags (when typedef'd separately) | `name_enum_t` | `entitytype_enum_t` | | Functions | `subsystemVerb` (camelCase, noun-first) | `entityInit`, `mapGetTile` | | Macro constants | `UPPER_SNAKE_CASE` | `CHUNK_WIDTH`, `FIXED_ONE` | | Function-like macros | `camelCase` (same as functions) | `errorThrow`, `assertNotNull` | | Global subsystem instances | `UPPER_SNAKE_CASE` | `ENGINE`, `MAP`, `ENTITIES` | | Local variables | `camelCase` | `tileNew`, `spriteCount` | | Parameters | `camelCase` | `texture`, `worldPos` | Subsystem prefix always comes first in function names: `textureInit`, `shaderBind`, `spriteBatchFlush`. The verb describes the action: `Init`, `Update`, `Dispose`, `Get`, `Set`, `Is`, etc. --- ## Typedefs ### Structs ```c typedef struct { uint8_t id; entitytype_t type; } entity_t; ``` Use a named tag (`struct entity_s`) only when forward declaration is required: ```c typedef struct entity_s { // ... } entity_t; ``` ### Unions ```c typedef union texturedata_u { struct { uint8_t *indices; palette_t *palette; } paletted; color_t *rgbaColors; } texturedata_t; ``` ### Enums When the enum values need to be a compact integer (common for arrays and flags), declare the enum separately and typedef an integer type: ```c typedef enum { ENTITY_TYPE_NULL, ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_COUNT } entitytype_enum_t; typedef uint8_t entitytype_t; // actual type used everywhere ``` Always include `_NULL` as the first value (zero) and `_COUNT` as the last value. --- ## `#define` constants All-caps, underscores. Wrap multi-token expressions in parentheses: ```c #define CHUNK_WIDTH 16 #define CHUNK_HEIGHT CHUNK_WIDTH #define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH) ``` Multi-line macros: backslash continuation, body indented 2 spaces, closing line has no backslash: ```c #define errorThrow(message, ...) \ return errorThrowImpl(\ &ERROR_STATE, ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \ ##__VA_ARGS__ \ ) ``` --- ## `const` usage Mark every pointer and value parameter `const` unless the function modifies it: ```c void entityTurn(entity_t *entity, const entitydir_t direction); errorret_t textureInit(texture_t *texture, const int32_t width, ...); ``` `entity_t *entity` is non-const because the function writes to it; `direction` is const because it is read-only. --- ## `void` in no-argument functions Use `(void)` in definitions and declarations of zero-parameter functions: ```c errorret_t engineUpdate(void); errorret_t spriteBatchFlush(void); ``` --- ## Global subsystem state Each subsystem exposes a single global instance declared `extern` in the header and defined (once) in the `.c` file: ```c // entity.h extern entity_t ENTITIES[ENTITY_COUNT]; // entity.c entity_t ENTITIES[ENTITY_COUNT]; ``` Never define a subsystem global as `static` in a header. --- ## Assertions Place assertions at the very top of a function, before any logic: ```c void entityInit(entity_t *entity, const entitytype_t type) { assertNotNull(entity, "Entity pointer cannot be NULL"); assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type"); // ... actual logic } ``` Available assertion macros (from `assert/assert.h`): - `assertNotNull(ptr, msg)` - `assertNull(ptr, msg)` - `assertTrue(expr, msg)` - `assertFalse(expr, msg)` - `assertUnreachable(msg)` - `assertStringEqual(a, b, msg)` - `assertIsMainThread(msg)` / `assertNotMainThread(msg)` --- ## Error handling style Functions that can fail return `errorret_t`. Three patterns: ```c // Propagate a child call's failure and return from this function: errorChain(someCall()); // Return success: errorOk(); // Return failure: errorThrow("Descriptive message %s", variable); ``` `errorChain` is used inline — do not capture the result first: ```c errorChain(textureInitPlatform(texture, width, height, format, data)); // correct errorret_t r = textureInitPlatform(...); errorChain(r); // wrong ``` --- ## Struct initialization Use C99 designated initializers for any struct literal with more than one field: ```c static const entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = { [ENTITY_TYPE_NULL] = { NULL }, [ENTITY_TYPE_PLAYER] = { .init = playerInit, .movement = playerInput }, }; ``` ```c shadermaterial_t material = { .unlit = { .color = COLOR_WHITE, .texture = NULL } }; ``` --- ## Fixed-size array iteration Prefer pointer arithmetic with `do/while` over index loops for iterating through fixed global arrays: ```c entity_t *ent = ENTITIES; do { if(ent->type == ENTITY_TYPE_NULL) continue; // ... } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); ``` Use `for` loops when an index variable is actually needed. --- ## Comments Comments explain *why*, not *what*. One short inline comment is fine; multi-line block comments for non-obvious invariants only. ```c // Walking up a ramp — only the direction the ramp faces is valid. if(tileIsRamp(tileCurrent) && ...) { ``` Section labels inside long functions are acceptable: ```c // Chunks { ... } // Entities { ... } ``` Doc comments on public functions use Javadoc style with `@param` / `@return`: ```c /** * Gets the tile at the given world position. * * @param position The world position. * @return The tile at that position, or TILE_NULL if the chunk is unloaded. */ tile_t mapGetTile(const worldpos_t position); ``` --- ## Platform-conditional code Use the `DUSK_*` compile-definition macros set by `cmake/targets/.cmake`: ```c #ifdef DUSK_THREAD_PTHREAD #include "thread/thread.h" extern pthread_t ASSERT_MAIN_THREAD_ID; #endif ``` Never use `#ifdef __linux__`, `#ifdef _WIN32`, etc. directly — go through the engine macros.