Restore some map stuff
All checks were successful
Build Dusk / run-tests (push) Successful in 1m44s
Build Dusk / build-linux (push) Successful in 1m35s
Build Dusk / build-psp (push) Successful in 1m53s

This commit is contained in:
2026-02-03 15:40:56 -06:00
parent 22398ddcef
commit 13dba8b604
16 changed files with 682 additions and 169 deletions

View File

@@ -59,7 +59,7 @@ add_subdirectory(event)
add_subdirectory(input)
add_subdirectory(item)
add_subdirectory(locale)
# add_subdirectory(rpg)
add_subdirectory(map)
add_subdirectory(scene)
add_subdirectory(script)
add_subdirectory(story)

View File

@@ -14,7 +14,7 @@
#include "scene/scene.h"
#include "asset/asset.h"
#include "ui/ui.h"
// #include "rpg/rpg.h"
#include "map/map.h"
#include "script/scriptmanager.h"
#include "debug/debug.h"
#include "item/backpack.h"
@@ -35,7 +35,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(scriptManagerInit());
errorChain(displayInit());
errorChain(uiInit());
// errorChain(rpgInit());
errorChain(mapInit());
errorChain(sceneInit());
backpackInit();
@@ -53,9 +53,9 @@ errorret_t engineUpdate(void) {
timeUpdate();
inputUpdate();
// errorChain(rpgUpdate());
uiUpdate();
errorChain(sceneUpdate());
mapUpdate();
errorChain(displayUpdate());
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
@@ -69,6 +69,7 @@ void engineExit(void) {
errorret_t engineDispose(void) {
sceneDispose();
mapDispose();
localeManagerDispose();
uiDispose();
errorChain(displayDispose());

View File

@@ -1,11 +1,13 @@
# Copyright (c) 2025 Dominic Masters
#
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
mapchunk.c
map.c
mapentity.c
worldpos.c
maptile.c
)

271
src/map/map.c Normal file
View File

@@ -0,0 +1,271 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "map.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "asset/asset.h"
// #include "entity/entity.h"
#include "util/string.h"
#include "time/time.h"
map_t MAP;
errorret_t mapInit() {
memoryZero(&MAP, sizeof(map_t));
errorOk();
}
bool_t mapIsLoaded() {
return MAP.filePath[0] != '\0';
}
errorret_t mapLoad(const char_t *path, const chunkpos_t position) {
assertStrLenMin(path, 1, "Map file path cannot be empty");
assertStrLenMax(path, MAP_FILE_PATH_MAX - 1, "Map file path too long");
if(stringCompare(MAP.filePath, path) == 0) {
// Same map, no need to reload
errorOk();
}
chunkindex_t i;
// Unload all loaded chunks
if(mapIsLoaded()) {
for(i = 0; i < MAP_CHUNK_COUNT; i++) {
mapChunkUnload(&MAP.chunks[i]);
}
}
// Store the map file path
stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX);
// Determine directory path (it is dirname)
stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX);
char_t *last = stringFindLastChar(MAP.dirPath, '/');
if(last == NULL) errorThrow("Invalid map file path");
// Store filename, sans extension
stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX);
*last = '\0'; // Terminate to get directory path
last = stringFindLastChar(MAP.fileName, '.');
if(last == NULL) errorThrow("Map file name has no extension");
*last = '\0'; // Terminate to remove extension
// Reset map position
MAP.chunkPosition = position;
// Perform "initial load"
i = 0;
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
mapchunk_t *chunk = &MAP.chunks[i];
chunk->position.x = x + position.x;
chunk->position.y = y + position.y;
chunk->position.z = z + position.z;
MAP.chunkOrder[i] = chunk;
errorChain(mapChunkLoad(chunk));
i++;
}
}
}
// Execute map script.
// char_t scriptPath[MAP_FILE_PATH_MAX + 16];
// stringFormat(
// scriptPath, sizeof(scriptPath), "%s/%s.dsf",
// MAP.dirPath, MAP.fileName
// );
// if(assetFileExists(scriptPath)) {
// scriptcontext_t ctx;
// errorChain(scriptContextInit(&ctx));
// errorChain(scriptContextExecFile(&ctx, scriptPath));
// scriptContextDispose(&ctx);
// }
errorOk();
}
errorret_t mapPositionSet(const chunkpos_t newPos) {
if(!mapIsLoaded()) errorThrow("No map loaded");
const chunkpos_t curPos = MAP.chunkPosition;
if(mapChunkPositionIsEqual(curPos, newPos)) {
errorOk();
}
// Determine which chunks remain loaded
chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0};
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
uint32_t remainingCount = 0;
uint32_t freedCount = 0;
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
// Will this chunk remain loaded?
mapchunk_t *chunk = &MAP.chunks[i];
if(
chunk->position.x >= newPos.x &&
chunk->position.x < newPos.x + MAP_CHUNK_WIDTH &&
chunk->position.y >= newPos.y &&
chunk->position.y < newPos.y + MAP_CHUNK_HEIGHT &&
chunk->position.z >= newPos.z &&
chunk->position.z < newPos.z + MAP_CHUNK_DEPTH
) {
// Stays loaded
chunksRemaining[remainingCount++] = i;
continue;
}
// Not remaining loaded
chunksFreed[freedCount++] = i;
}
// Unload the freed chunks
for(chunkindex_t i = 0; i < freedCount; i++) {
mapchunk_t *chunk = &MAP.chunks[chunksFreed[i]];
mapChunkUnload(chunk);
}
// This can probably be optimized later, for now we check each chunk and see
// if it needs loading or not, and update the chunk order
chunkindex_t orderIndex = 0;
for(chunkunit_t zOff = 0; zOff < MAP_CHUNK_DEPTH; zOff++) {
for(chunkunit_t yOff = 0; yOff < MAP_CHUNK_HEIGHT; yOff++) {
for(chunkunit_t xOff = 0; xOff < MAP_CHUNK_WIDTH; xOff++) {
const chunkpos_t newChunkPos = {
newPos.x + xOff, newPos.y + yOff, newPos.z + zOff
};
// Is this chunk already loaded (was not unloaded earlier)?
chunkindex_t chunkIndex = -1;
for(chunkindex_t i = 0; i < remainingCount; i++) {
mapchunk_t *chunk = &MAP.chunks[chunksRemaining[i]];
if(!mapChunkPositionIsEqual(chunk->position, newChunkPos)) continue;
chunkIndex = chunksRemaining[i];
break;
}
// Need to load this chunk
if(chunkIndex == -1) {
// Find a freed chunk to reuse
chunkIndex = chunksFreed[--freedCount];
mapchunk_t *chunk = &MAP.chunks[chunkIndex];
chunk->position = newChunkPos;
errorChain(mapChunkLoad(chunk));
}
MAP.chunkOrder[orderIndex++] = &MAP.chunks[chunkIndex];
}
}
}
// Update map position
MAP.chunkPosition = newPos;
errorOk();
}
void mapUpdate() {
#if TIME_FIXED == 0
if(!TIME.dynamicUpdate) return;
#endif
}
void mapDispose() {
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
mapChunkUnload(&MAP.chunks[i]);
}
}
void mapChunkUnload(mapchunk_t* chunk) {
// for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
// if(chunk->entities[i] == 0xFF) break;
// entity_t *entity = &ENTITIES[chunk->entities[i]];
// entity->type = ENTITY_TYPE_NULL;
// }
for(uint8_t i = 0; i < chunk->meshCount; i++) {
if(chunk->meshes[i].vertexCount == 0) continue;
meshDispose(&chunk->meshes[i]);
}
}
errorret_t mapChunkLoad(mapchunk_t* chunk) {
if(!mapIsLoaded()) errorThrow("No map loaded");
char_t buffer[64];
// TODO: Can probably move this to asset load logic?
chunk->meshCount = 0;
memoryZero(chunk->meshes, sizeof(chunk->meshes));
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
// Get chunk filepath.
snprintf(buffer, sizeof(buffer), "%s/chunks/%d_%d_%d.dcf",
MAP.dirPath,
chunk->position.x,
chunk->position.y,
chunk->position.z
);
// Chunk available?
if(!assetFileExists(buffer)) {
memoryZero(chunk->tiles, sizeof(chunk->tiles));
errorOk();
}
// Load.
errorChain(assetLoad(buffer, chunk));
errorOk();
}
chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) {
if(!mapIsLoaded()) return -1;
chunkpos_t relPos = {
position.x - MAP.chunkPosition.x,
position.y - MAP.chunkPosition.y,
position.z - MAP.chunkPosition.z
};
if(
relPos.x < 0 || relPos.y < 0 || relPos.z < 0 ||
relPos.x >= MAP_CHUNK_WIDTH ||
relPos.y >= MAP_CHUNK_HEIGHT ||
relPos.z >= MAP_CHUNK_DEPTH
) {
return -1;
}
return chunkPosToIndex(&relPos);
}
mapchunk_t* mapGetChunk(const uint8_t index) {
if(index >= MAP_CHUNK_COUNT) return NULL;
if(!mapIsLoaded()) return NULL;
return MAP.chunkOrder[index];
}
maptile_t mapGetTile(const worldpos_t position) {
if(!mapIsLoaded()) return TILE_SHAPE_NULL;
chunkpos_t chunkPos;
worldPosToChunkPos(&position, &chunkPos);
chunkindex_t chunkIndex = mapGetChunkIndexAt(chunkPos);
if(chunkIndex == -1) return TILE_SHAPE_NULL;
mapchunk_t *chunk = mapGetChunk(chunkIndex);
assertNotNull(chunk, "Chunk pointer cannot be NULL");
chunktileindex_t tileIndex = worldPosToChunkTileIndex(&position);
return chunk->tiles[tileIndex];
}

103
src/map/map.h Normal file
View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "mapchunk.h"
#define MAP_FILE_PATH_MAX 128
typedef struct map_s {
char_t filePath[MAP_FILE_PATH_MAX];
char_t dirPath[MAP_FILE_PATH_MAX];
char_t fileName[MAP_FILE_PATH_MAX];
mapchunk_t chunks[MAP_CHUNK_COUNT];
mapchunk_t *chunkOrder[MAP_CHUNK_COUNT];
chunkpos_t chunkPosition;
} map_t;
extern map_t MAP;
/**
* Initializes the map.
*
* @return An error code.
*/
errorret_t mapInit();
/**
* Checks if a map is loaded.
*
* @return true if a map is loaded, false otherwise.
*/
bool_t mapIsLoaded();
/**
* Loads a map from the given file path.
*
* @param path The file path.
* @param position The initial chunk position.
* @return An error code.
*/
errorret_t mapLoad(const char_t *path, const chunkpos_t position);
/**
* Updates the map.
*/
void mapUpdate();
/**
* Disposes of the map.
*/
void mapDispose();
/**
* Sets the map position and updates chunks accordingly.
*
* @param newPos The new chunk position.
* @return An error code.
*/
errorret_t mapPositionSet(const chunkpos_t newPos);
/**
* Unloads a chunk.
*
* @param chunk The chunk to unload.
*/
void mapChunkUnload(mapchunk_t* chunk);
/**
* Loads a chunk.
*
* @param chunk The chunk to load.
* @return An error code.
*/
errorret_t mapChunkLoad(mapchunk_t* chunk);
/**
* Gets the index of a chunk, within the world, at the given position.
*
* @param position The chunk position.
* @return The index of the chunk, or -1 if out of bounds.
*/
chunkindex_t mapGetChunkIndexAt(const chunkpos_t position);
/**
* Gets a chunk by its index.
*
* @param chunkIndex The index of the chunk.
* @return A pointer to the chunk.
*/
mapchunk_t * mapGetChunk(const uint8_t chunkIndex);
/**
* 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.
*/
maptile_t mapGetTile(const worldpos_t position);

20
src/map/mapchunk.c Normal file
View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mapchunk.h"
uint32_t mapChunkGetTileindex(const chunkpos_t position) {
return (
(position.z * CHUNK_WIDTH * CHUNK_HEIGHT) +
(position.y * CHUNK_WIDTH) +
position.x
);
}
bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b) {
return (a.x == b.x) && (a.y == b.y) && (a.z == b.z);
}

38
src/map/mapchunk.h Normal file
View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "maptile.h"
#include "worldpos.h"
#include "display/mesh/quad.h"
typedef struct chunk_s {
chunkpos_t position;
maptile_t tiles[CHUNK_TILE_COUNT];
uint8_t meshCount;
meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
mesh_t meshes[CHUNK_MESH_COUNT_MAX];
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
} mapchunk_t;
/**
* Gets the tile index for a tile position within a chunk.
*
* @param position The position within the chunk.
* @return The tile index within the chunk.
*/
uint32_t mapChunkGetTileindex(const chunkpos_t position);
/**
* Checks if two chunk positions are equal.
*
* @param a The first chunk position.
* @param b The second chunk position.
* @return true if equal, false otherwise.
*/
bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b);

35
src/map/maptile.c Normal file
View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "maptile.h"
bool_t mapTileIsWalkable(const maptile_t tile) {
switch(tile) {
case TILE_SHAPE_NULL:
return false;
default:
return true;
}
}
bool_t mapTileIsRamp(const maptile_t tile) {
switch(tile) {
case TILE_SHAPE_RAMP_NORTH:
case TILE_SHAPE_RAMP_SOUTH:
case TILE_SHAPE_RAMP_EAST:
case TILE_SHAPE_RAMP_WEST:
case TILE_SHAPE_RAMP_NORTHEAST:
case TILE_SHAPE_RAMP_NORTHWEST:
case TILE_SHAPE_RAMP_SOUTHEAST:
case TILE_SHAPE_RAMP_SOUTHWEST:
return true;
default:
return false;
}
}

28
src/map/maptile.h Normal file
View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskdefs.h"
// #include "rpg/entity/entitydir.h"
typedef uint8_t maptile_t;
/**
* Returns whether or not the given tile is walkable.
*
* @param tile The tile to check.
* @return bool_t True if walkable, false if not.
*/
bool_t mapTileIsWalkable(const maptile_t tile);
/**
* Returns whether or not the given tile is a ramp tile.
*
* @param tile The tile to check.
* @return bool_t True if ramp, false if not.
*/
bool_t mapTileIsRamp(const maptile_t tile);

97
src/map/worldpos.c Normal file
View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "worldpos.h"
#include "assert/assert.h"
bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b) {
return a.x == b.x && a.y == b.y && a.z == b.z;
}
void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out) {
assertNotNull(chunkPos, "Chunk position pointer cannot be NULL");
assertNotNull(out, "Output world position pointer cannot be NULL");
out->x = (worldunit_t)(chunkPos->x * CHUNK_WIDTH);
out->y = (worldunit_t)(chunkPos->y * CHUNK_HEIGHT);
out->z = (worldunit_t)(chunkPos->z * CHUNK_DEPTH);
}
void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out) {
assertNotNull(worldPos, "World position pointer cannot be NULL");
assertNotNull(out, "Output chunk position pointer cannot be NULL");
if(worldPos->x < 0) {
out->x = (chunkunit_t)((worldPos->x - (CHUNK_WIDTH - 1)) / CHUNK_WIDTH);
} else {
out->x = (chunkunit_t)(worldPos->x / CHUNK_WIDTH);
}
if(worldPos->y < 0) {
out->y = (chunkunit_t)((worldPos->y - (CHUNK_HEIGHT - 1)) / CHUNK_HEIGHT);
} else {
out->y = (chunkunit_t)(worldPos->y / CHUNK_HEIGHT);
}
if(worldPos->z < 0) {
out->z = (chunkunit_t)((worldPos->z - (CHUNK_DEPTH - 1)) / CHUNK_DEPTH);
} else {
out->z = (chunkunit_t)(worldPos->z / CHUNK_DEPTH);
}
}
chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos) {
assertNotNull(worldPos, "World position pointer cannot be NULL");
uint8_t localX, localY, localZ;
if(worldPos->x < 0) {
localX = (uint8_t)(
(CHUNK_WIDTH - 1) - ((-worldPos->x - 1) % CHUNK_WIDTH)
);
} else {
localX = (uint8_t)(worldPos->x % CHUNK_WIDTH);
}
if(worldPos->y < 0) {
localY = (uint8_t)(
(CHUNK_HEIGHT - 1) - ((-worldPos->y - 1) % CHUNK_HEIGHT)
);
} else {
localY = (uint8_t)(worldPos->y % CHUNK_HEIGHT);
}
if(worldPos->z < 0) {
localZ = (uint8_t)(
(CHUNK_DEPTH - 1) - ((-worldPos->z - 1) % CHUNK_DEPTH)
);
} else {
localZ = (uint8_t)(worldPos->z % CHUNK_DEPTH);
}
chunktileindex_t chunkTileIndex = (chunktileindex_t)(
(localZ * CHUNK_WIDTH * CHUNK_HEIGHT) +
(localY * CHUNK_WIDTH) +
localX
);
assertTrue(
chunkTileIndex < CHUNK_TILE_COUNT,
"Calculated chunk tile index is out of bounds"
);
return chunkTileIndex;
}
chunkindex_t chunkPosToIndex(const chunkpos_t* pos) {
assertNotNull(pos, "Chunk position pointer cannot be NULL");
chunkindex_t chunkIndex = (chunkindex_t)(
(pos->z * MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT) +
(pos->y * MAP_CHUNK_WIDTH) +
pos->x
);
return chunkIndex;
}

75
src/map/worldpos.h Normal file
View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "duskdefs.h"
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH)
#define MAP_CHUNK_WIDTH 3
#define MAP_CHUNK_HEIGHT 3
#define MAP_CHUNK_DEPTH 3
#define MAP_CHUNK_COUNT (MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT * MAP_CHUNK_DEPTH)
typedef int16_t worldunit_t;
typedef int16_t chunkunit_t;
typedef int16_t chunkindex_t;
typedef uint32_t chunktileindex_t;
typedef int32_t worldunits_t;
typedef int32_t chunkunits_t;
typedef struct worldpos_s {
worldunit_t x, y, z;
} worldpos_t;
typedef struct chunkpos_t {
chunkunit_t x, y, z;
} chunkpos_t;
/**
* Compares two world positions for equality.
*
* @param a The first world position.
* @param b The second world position.
* @return true if equal, false otherwise.
*/
bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b);
/**
* Converts a world position to a chunk position.
*
* @param worldPos The world position.
* @param out The output chunk position.
*/
void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out);
/**
* Converts a chunk position to a world position.
*
* @param worldPos The world position.
* @param out The output chunk position.
*/
void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out);
/**
* Converts a position in world-space to an index inside a chunk that the tile
* resides in.
*
* @param worldPos The world position.
* @return The tile index within the chunk.
*/
chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos);
/**
* Converts a chunk position to a world position.
*
* @param worldPos The world position.
* @param out The output chunk position.
*/
chunkindex_t chunkPosToIndex(const chunkpos_t* pos);

View File

@@ -1,61 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "map.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
map_t MAP;
void mapInit(void) {
memoryZero(&MAP, sizeof(map_t));
}
mapentity_t * mapEntityAdd(const mapentitytype_t type) {
assertTrue(type != MAP_ENTITY_TYPE_NULL, "Type cannot be NULL");
assertTrue(type < MAP_ENTITY_TYPE_COUNT, "Type is out of range");
// Find any available entity.
mapentity_t *start = &MAP.entities[0];
mapentity_t *end = &MAP.entities[MAP_ENTITY_COUNT_MAX];
do {
if(start->type != MAP_ENTITY_TYPE_NULL) continue;
mapEntityInit(start, type);
return start;
} while(start->type != MAP_ENTITY_TYPE_NULL && ++start < end);
return NULL;
}
void mapEntityRemove(mapentity_t *ent) {
assertNotNull(ent, "Ent cannot be NULL");
assertTrue(ent->type != MAP_ENTITY_TYPE_NULL, "Ent is already NULL");
assertTrue(
ent >= &MAP.entities[0] && ent < &MAP.entities[MAP_ENTITY_COUNT_MAX],
"Ent is out of range"
);
ent->type = MAP_ENTITY_TYPE_NULL;
}
mapentity_t * mapEntityGetByName(const char_t *name) {
assertNotNull(name, "Name cannot be NULL");
assertStrLenMax(name, MAP_ENTITY_NAME_MAX_LENGTH, "Name is too long");
// Search for the entity by name.
mapentity_t *start = &MAP.entities[0];
mapentity_t *end = &MAP.entities[MAP_ENTITY_COUNT_MAX];
do {
if(start->type == MAP_ENTITY_TYPE_NULL) continue;
if(stringCompare(start->name, name) == 0) {
return start;
}
} while(++start < end);
return NULL;
}

View File

@@ -1,22 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "mapentity.h"
#define MAP_ENTITY_COUNT_MAX 32
typedef struct {
mapentity_t entities[MAP_ENTITY_COUNT_MAX];
} map_t;
extern map_t MAP;
/**
* Initialize the overworld map.
*/
void mapInit(void);

View File

@@ -1,23 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mapentity.h"
#include "util/memory.h"
#include "util/string.h"
void mapEntityInit(mapentity_t *ent, const mapentitytype_t type) {
memoryZero(ent, sizeof(mapentity_t));
ent->type = type;
}
void mapEntitySetName(mapentity_t *ent, const char_t *name) {
assertNotNull(ent, "Ent cannot be NULL");
assertNotNull(name, "Name cannot be NULL");
assertStrLenMax(name, MAP_ENTITY_NAME_MAX_LENGTH, "Name is too long");
stringCopy(ent->name, name, MAP_ENTITY_NAME_MAX_LENGTH);
}

View File

@@ -1,57 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define MAP_ENTITY_NAME_MAX_LENGTH 16
#define MAP_ENTITY_SCRIPT_MAX_LENGTH 64
typedef enum {
MAP_ENTITY_DIR_NORTH = 0,
MAP_ENTITY_DIR_EAST = 1,
MAP_ENTITY_DIR_SOUTH = 2,
MAP_ENTITY_DIR_WEST = 3,
MAP_ENTITY_DIR_COUNT = 4,
MAP_ENTITY_DIR_UP = MAP_ENTITY_DIR_NORTH,
MAP_ENTITY_DIR_RIGHT = MAP_ENTITY_DIR_EAST,
MAP_ENTITY_DIR_DOWN = MAP_ENTITY_DIR_SOUTH,
MAP_ENTITY_DIR_LEFT = MAP_ENTITY_DIR_WEST
} mapentitydir_t;
typedef enum {
MAP_ENTITY_TYPE_NULL,
MAP_ENTITY_TYPE_NPC,
MAP_ENTITY_TYPE_COUNT
} mapentitytype_t;
typedef struct {
char_t name[MAP_ENTITY_NAME_MAX_LENGTH];
// Position within the map
uint16_t x, y;
uint8_t layer;
// Rendering properties
mapentitydir_t dir;
mapentitytype_t type;
// Script to execute on interaction
char_t script[MAP_ENTITY_SCRIPT_MAX_LENGTH];
} mapentity_t;
/**
* Initialize a map entity to default values.
*
* @param ent The entity to initialize.
* @param type The type of the entity.
*/
void mapEntityInit(mapentity_t *ent, const mapentitytype_t type);

View File

@@ -22,6 +22,12 @@ errorret_t sceneInit(void) {
}
errorret_t sceneUpdate(void) {
#if TIME_FIXED == 0
if(!TIME.dynamicUpdate) {
errorOk();
}
#endif
lua_getglobal(SCENE.scriptContext.luaState, "sceneUpdate");
if(!lua_isfunction(SCENE.scriptContext.luaState, -1)) {
lua_pop(SCENE.scriptContext.luaState, 1);