Area basics

This commit is contained in:
2026-07-06 23:19:05 -05:00
parent 85bf455731
commit 988a0f2294
6 changed files with 277 additions and 1 deletions
+2
View File
@@ -11,6 +11,7 @@
#include "time/time.h"
#include "util/math.h"
#include "rpg/overworld/map.h"
#include "rpg/overworld/maparea.h"
#include "rpg/overworld/chunk.h"
#include "rpg/overworld/tile.h"
@@ -224,6 +225,7 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
entity->animation = ENTITY_ANIM_WALK;
entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking
entityUpdateChunk(entity);
mapAreaCheckEntity(entity);
}
void entityRun(entity_t *entity, const entitydir_t direction) {
+1
View File
@@ -8,6 +8,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
chunk.c
map.c
maparea.c
worldpos.c
tile.c
tileshape.c
+124
View File
@@ -0,0 +1,124 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "maparea.h"
#include "assert/assert.h"
#include "util/math.h"
#include "rpg/overworld/map.h"
maparea_t MAP_AREAS[MAP_AREA_COUNT_MAX];
void mapAreaInit(
maparea_t *area,
const worldpos_t min,
const worldpos_t max,
const mapareacallback_t callback,
const mapareanotify_t notify
) {
assertNotNull(area, "Map area pointer cannot be NULL");
assertNotNull(callback, "Map area callback cannot be NULL");
area->min.x = mathMin(min.x, max.x);
area->min.y = mathMin(min.y, max.y);
area->min.z = mathMin(min.z, max.z);
area->max.x = mathMax(min.x, max.x);
area->max.y = mathMax(min.y, max.y);
area->max.z = mathMax(min.z, max.z);
area->callback = callback;
area->notify = notify;
}
bool_t mapAreaIsInside(const maparea_t *area, const worldpos_t position) {
assertNotNull(area, "Map area pointer cannot be NULL");
return (
position.x >= area->min.x && position.x <= area->max.x &&
position.y >= area->min.y && position.y <= area->max.y &&
position.z >= area->min.z && position.z <= area->max.z
);
}
bool_t mapAreaIsChunkOverlappingOrInside(
const maparea_t *area,
const chunk_t *chunk
) {
assertNotNull(area, "Map area pointer cannot be NULL");
assertNotNull(chunk, "Chunk pointer cannot be NULL");
worldpos_t chunkMin, chunkMax;
chunkPosToWorldPos(&chunk->position, &chunkMin);
chunkMax.x = chunkMin.x + CHUNK_WIDTH - 1;
chunkMax.y = chunkMin.y + CHUNK_HEIGHT - 1;
chunkMax.z = chunkMin.z + CHUNK_DEPTH - 1;
return (
chunkMin.x <= area->max.x && area->min.x <= chunkMax.x &&
chunkMin.y <= area->max.y && area->min.y <= chunkMax.y &&
chunkMin.z <= area->max.z && area->min.z <= chunkMax.z
);
}
bool_t mapAreaCanUnload(const maparea_t *area) {
assertNotNull(area, "Map area pointer cannot be NULL");
for(chunkindex_t i = 0; i < MAP_LOADED_CHUNK_COUNT; i++) {
if(mapAreaIsChunkOverlappingOrInside(area, &MAP.chunks[i])) return false;
}
return true;
}
bool_t mapAreaShouldNotify(const maparea_t *area, const entity_t *entity) {
assertNotNull(area, "Map area pointer cannot be NULL");
assertNotNull(entity, "Entity pointer cannot be NULL");
switch(entity->type) {
case ENTITY_TYPE_PLAYER:
return (area->notify & MAP_AREA_NOTIFY_PLAYER) != 0;
case ENTITY_TYPE_NPC:
return (area->notify & MAP_AREA_NOTIFY_NPC) != 0;
default:
return false;
}
}
uint8_t mapAreaAdd(
const worldpos_t min,
const worldpos_t max,
const mapareacallback_t callback,
const mapareanotify_t notify
) {
for(uint8_t i = 0; i < MAP_AREA_COUNT_MAX; i++) {
if(MAP_AREAS[i].callback != NULL) continue;
mapAreaInit(&MAP_AREAS[i], min, max, callback, notify);
return i;
}
assertUnreachable("No available map area slots");
return 0xFF;
}
void mapAreaRemove(const uint8_t id) {
assertTrue(id < MAP_AREA_COUNT_MAX, "Map area ID is out of range");
MAP_AREAS[id].callback = NULL;
}
void mapAreaCheckEntity(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
for(uint8_t i = 0; i < MAP_AREA_COUNT_MAX; i++) {
maparea_t *area = &MAP_AREAS[i];
if(area->callback == NULL) continue;
if(!mapAreaIsInside(area, entity->position)) continue;
if(!mapAreaShouldNotify(area, entity)) continue;
area->callback(entity);
}
}
+133
View File
@@ -0,0 +1,133 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "worldpos.h"
typedef struct chunk_s chunk_t;
typedef struct entity_s entity_t;
typedef struct maparea_s maparea_t;
#define MAP_AREA_COUNT_MAX 32
typedef uint8_t mapareanotify_t;
#define MAP_AREA_NOTIFY_PLAYER ((mapareanotify_t)(1 << 0))
#define MAP_AREA_NOTIFY_NPC ((mapareanotify_t)(1 << 1))
#define MAP_AREA_NOTIFY_ALL ((mapareanotify_t)( \
MAP_AREA_NOTIFY_PLAYER | MAP_AREA_NOTIFY_NPC \
))
/**
* Callback invoked for a map area.
*
* @param entity Pointer to the entity associated with the callback.
*/
typedef void (*mapareacallback_t)(entity_t *entity);
typedef struct maparea_s {
worldpos_t min;
worldpos_t max;
mapareacallback_t callback;
mapareanotify_t notify;
} maparea_t;
extern maparea_t MAP_AREAS[MAP_AREA_COUNT_MAX];
/**
* Initializes a map area with the given bounds, callback and notify flags.
*
* @param area Pointer to the map area to initialize.
* @param min The minimum world position of the area.
* @param max The maximum world position of the area.
* @param callback The callback to invoke for this area. Must not be NULL.
* @param notify Bitwise MAP_AREA_NOTIFY_* flags for which entity types
* should trigger the callback.
*/
void mapAreaInit(
maparea_t *area,
const worldpos_t min,
const worldpos_t max,
const mapareacallback_t callback,
const mapareanotify_t notify
);
/**
* Checks whether a world position falls within a map area's bounds
* (inclusive on all axes).
*
* @param area Pointer to the map area to check.
* @param position The world position to check.
* @returns true if the position is inside the area.
*/
bool_t mapAreaIsInside(const maparea_t *area, const worldpos_t position);
/**
* Checks whether a chunk's full footprint overlaps a map area's bounds,
* rather than just testing the chunk's origin corner.
*
* @param area Pointer to the map area to check.
* @param chunk Pointer to the chunk to check.
* @returns true if the chunk overlaps the area.
*/
bool_t mapAreaIsChunkOverlappingOrInside(
const maparea_t *area,
const chunk_t *chunk
);
/**
* Checks whether a map area is safe to unload, i.e. no entity currently
* occupies a position within its bounds.
*
* @param area Pointer to the map area to check.
* @returns true if no entity is inside the area.
*/
bool_t mapAreaCanUnload(const maparea_t *area);
/**
* Checks whether an entity's type is included in a map area's notify flags.
*
* @param area Pointer to the map area to check.
* @param entity Pointer to the entity to check.
* @returns true if the entity's type should trigger this area's callback.
*/
bool_t mapAreaShouldNotify(const maparea_t *area, const entity_t *entity);
/**
* Adds a map area to the global MAP_AREAS list, in the first free slot.
* A slot is considered free if its callback is NULL.
*
* @param min The minimum world position of the area.
* @param max The maximum world position of the area.
* @param callback The callback to invoke for this area. Must not be NULL.
* @param notify Bitwise MAP_AREA_NOTIFY_* flags for which entity types
* should trigger the callback.
* @returns The ID of the newly added map area.
*/
uint8_t mapAreaAdd(
const worldpos_t min,
const worldpos_t max,
const mapareacallback_t callback,
const mapareanotify_t notify
);
/**
* Removes a map area from the global MAP_AREAS list, freeing its slot by
* clearing its callback to NULL.
*
* @param id The ID of the map area to remove.
*/
void mapAreaRemove(const uint8_t id);
/**
* Checks every active map area against an entity's current position,
* invoking each matching area's callback if the entity is inside it and
* its type is included in that area's notify flags.
*
* @param entity Pointer to the entity to check.
*/
void mapAreaCheckEntity(entity_t *entity);
+16
View File
@@ -9,6 +9,7 @@
#include "entity/entity.h"
#include "rpg/entity/npc/npcpath.h"
#include "rpg/overworld/map.h"
#include "rpg/overworld/maparea.h"
#include "rpg/cutscene/cutscenesystem.h"
#include "rpg/cutscene/scene/testcutscene.h"
#include "rpg/item/backpack.h"
@@ -17,9 +18,15 @@
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
#include "console/console.h"
void rpgTestAreaCallback(entity_t *entity) {
consolePrint("rpgTestAreaCallback");
}
errorret_t rpgInit(void) {
memoryZero(ENTITIES, sizeof(ENTITIES));
memoryZero(MAP_AREAS, sizeof(MAP_AREAS));
backpackInit();
cutsceneSystemInit();
@@ -41,6 +48,15 @@ errorret_t rpgInit(void) {
mapSpawnEntity(3, (worldpos_t){ 8, 8, 1 });
// TEST: Create a test map area.
uint8_t areaIndex = mapAreaAdd(
(worldpos_t){ 11, 3, 0 },
(worldpos_t){ 16, 9, 10 },
rpgTestAreaCallback,
MAP_AREA_NOTIFY_ALL
);
assertTrue(areaIndex != 0xFF, "No available map area slots!.");
// All Good!
errorOk();
}
+1 -1
View File
@@ -28,7 +28,7 @@ errorret_t rpgUpdate(void);
/**
* Dispose of the RPG subsystem.
*
*
* @return An error code.
*/
errorret_t rpgDispose(void);