4.6 KiB
Entities
Source: src/dusk/rpg/entity/
Storage
Entities live in a single fixed global array:
entity_t ENTITIES[ENTITY_COUNT]; // ENTITY_COUNT = 64
A slot is "empty" when entity->type == ENTITY_TYPE_NULL. Never allocate entity memory dynamically — always find a free slot with entityGetAvailable(), which returns its index (0xFF if none free).
entity_t structure
typedef struct entity_s {
uint8_t id; // index in ENTITIES[]
entitytype_t type; // ENTITY_TYPE_NULL / PLAYER / NPC
entitytypedata_t data; // union: player_t | npc_t
entitydir_t direction; // facing direction (N/S/E/W)
fixed_t position[3]; // current sub-tile position (x, y, z)
fixed_t lastPosition[3]; // position before last move (for interpolation)
entityanim_t animation; // IDLE / TURN / WALK
fixed_t animTime; // countdown timer for current animation
entityinteract_t interact; // optional interact component
} entity_t;
Type system
Entity types are defined in entitytype.h using the enum+integer-typedef pattern:
typedef enum { ENTITY_TYPE_NULL, ENTITY_TYPE_PLAYER, ENTITY_TYPE_NPC, ENTITY_TYPE_COUNT } entitytype_enum_t;
typedef uint8_t entitytype_t;
Each type has a entitycallback_t entry in the ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] static table:
typedef struct {
void (*init)(entity_t *entity);
void (*movement)(entity_t *entity);
bool_t (*interact)(entity_t *player, entity_t *entity);
} entitycallback_t;
Callbacks not applicable to a type are NULL; entityUpdate() guards against this before calling.
Type-specific data sits in entitytypedata_t (a union of player_t and npc_t). Currently both are stubs (void *nothing).
Direction (entitydir.h)
ENTITY_DIR_NORTH / EAST / SOUTH / WEST
Aliases: UP = NORTH, DOWN = SOUTH, LEFT = WEST, RIGHT = EAST.
Utilities:
entityDirGetOpposite(dir)— returns the opposite direction.entityDirGetRelative(dir, &relX, &relY)— fills in the ±1 XY delta for that direction.assertValidEntityDir(dir, msg)— assertion macro.
Animation (entityanim.h)
ENTITY_ANIM_IDLE // standing still
ENTITY_ANIM_TURN // turning to a new direction (ENTITY_ANIM_TURN_DURATION = FIXED(0.06))
ENTITY_ANIM_WALK // mid-step (ENTITY_ANIM_WALK_DURATION = FIXED(0.1))
entityAnimUpdate(entity) decrements animTime each frame and transitions back to IDLE when it reaches zero.
entityCanWalk(entity) / entityCanTurn(entity) both return true only when animation == ENTITY_ANIM_IDLE.
The renderer interpolates between lastPosition and position using animTime / WALK_DURATION to produce smooth motion even at low frame rates.
Movement
entityWalk(entity, direction):
- Converts
entity->positionto aworldpos_t(truncates fractional part). - Applies the directional delta to get
newPos. - Checks the current and target tiles for ramp raise/fall logic (see world.md).
- Checks
ENTITIES[]for another entity occupyingnewPos— blocks if found. - On success: copies
positiontolastPosition, updatespositiontonewPos(viaworldPosToFixed), setsanimation = ENTITY_ANIM_WALK.
entityTurn(entity, direction): sets direction and starts a brief turn animation.
Interaction (entityinteract.h)
The entityinteract_t component is embedded in every entity. It is optional — set type = ENTITY_INTERACT_NULL for non-interactable entities.
typedef enum {
ENTITY_INTERACT_NULL,
ENTITY_INTERACT_CUTSCENE, // plays a cutscene_t *
ENTITY_INTERACT_PRINT, // prints a short message[32]
} entityinteracttype_t;
entityInteractWith(player, target) dispatches:
- If the interact component's
type != NULL, handles it (starts the cutscene or prints the message). - Otherwise falls back to
ENTITY_CALLBACKS[type].interactif set.
Player (player.h / player.c)
playerInit() is called via ENTITY_CALLBACKS[ENTITY_TYPE_PLAYER].init.
playerInput(entity) is the movement callback. It reads PLAYER_INPUT_DIR_MAP[] — a static table mapping input actions (INPUT_ACTION_UP/DOWN/LEFT/RIGHT) to entity directions — and calls entityWalk or entityTurn accordingly.
The player entity is normally ENTITIES[0] but there is no hardcoded assumption about its index beyond being initialized with ENTITY_TYPE_PLAYER.
NPC (npc.h / npc.c)
npcInit(), npcMovement(), and npcInteract() provide the NPC type callbacks. Currently stubs; movement does nothing, interact returns false.