diff --git a/src/dusk/rpg/overworld/maparea.c b/src/dusk/rpg/overworld/maparea.c index 2758065d..2bdaff04 100644 --- a/src/dusk/rpg/overworld/maparea.c +++ b/src/dusk/rpg/overworld/maparea.c @@ -8,6 +8,7 @@ #include "maparea.h" #include "assert/assert.h" #include "util/math.h" +#include "util/memory.h" #include "rpg/overworld/map.h" maparea_t MAP_AREAS[MAP_AREA_COUNT_MAX]; @@ -17,7 +18,8 @@ void mapAreaInit( const worldpos_t min, const worldpos_t max, const mapareacallback_t callback, - const mapareanotify_t notify + const uint8_t notify, + const uint8_t trigger ) { assertNotNull(area, "Map area pointer cannot be NULL"); assertNotNull(callback, "Map area callback cannot be NULL"); @@ -32,6 +34,9 @@ void mapAreaInit( area->callback = callback; area->notify = notify; + area->trigger = trigger; + + memorySet(area->entities, 0, sizeof(area->entities)); } bool_t mapAreaIsInside(const maparea_t *area, const worldpos_t position) { @@ -94,11 +99,12 @@ uint8_t mapAreaAdd( const worldpos_t min, const worldpos_t max, const mapareacallback_t callback, - const mapareanotify_t notify + const uint8_t notify, + const uint8_t trigger ) { 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); + mapAreaInit(&MAP_AREAS[i], min, max, callback, notify, trigger); return i; } @@ -117,8 +123,23 @@ void mapAreaCheckEntity(entity_t *entity) { 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); + + bool_t wasInside = area->entities[entity->id] != 0; + bool_t isInside = mapAreaIsInside(area, entity->position); + uint8_t trigger = 0; + + if(isInside && !wasInside) { + area->entities[entity->id] = 1; + trigger = MAP_TRIGGER_ENTER; + } else if(isInside && wasInside) { + trigger = MAP_TRIGGER_STEP; + } else if(!isInside && wasInside) { + area->entities[entity->id] = 0; + trigger = MAP_TRIGGER_EXIT; + } + + if(trigger == 0 || !(area->trigger & trigger)) continue; + area->callback(entity, trigger); } } diff --git a/src/dusk/rpg/overworld/maparea.h b/src/dusk/rpg/overworld/maparea.h index b138d56c..181e05f0 100644 --- a/src/dusk/rpg/overworld/maparea.h +++ b/src/dusk/rpg/overworld/maparea.h @@ -7,39 +7,44 @@ #pragma once #include "worldpos.h" +#include "rpg/entity/entity.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 (1 << 0) +#define MAP_AREA_NOTIFY_NPC (1 << 1) +#define MAP_AREA_NOTIFY_ALL (MAP_AREA_NOTIFY_PLAYER | MAP_AREA_NOTIFY_NPC) -#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 \ -)) +#define MAP_TRIGGER_STEP (1 << 0) +#define MAP_TRIGGER_ENTER (1 << 1) +#define MAP_TRIGGER_EXIT (1 << 2) +#define MAP_TRIGGER_ALL (MAP_TRIGGER_STEP | MAP_TRIGGER_ENTER | MAP_TRIGGER_EXIT) /** * Callback invoked for a map area. * * @param entity Pointer to the entity associated with the callback. + * @param trigger Which MAP_TRIGGER_* condition invoked the callback. */ -typedef void (*mapareacallback_t)(entity_t *entity); +typedef void (*mapareacallback_t)(entity_t *entity, const uint8_t trigger); typedef struct maparea_s { worldpos_t min; worldpos_t max; mapareacallback_t callback; - mapareanotify_t notify; + uint8_t notify; + uint8_t trigger; + uint8_t entities[ENTITY_COUNT]; } maparea_t; extern maparea_t MAP_AREAS[MAP_AREA_COUNT_MAX]; /** - * Initializes a map area with the given bounds, callback and notify flags. + * Initializes a map area with the given bounds, callback, notify flags + * and trigger flags. * * @param area Pointer to the map area to initialize. * @param min The minimum world position of the area. @@ -47,13 +52,16 @@ extern maparea_t MAP_AREAS[MAP_AREA_COUNT_MAX]; * @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. + * @param trigger Bitwise MAP_TRIGGER_* flags for which conditions should + * invoke the callback. */ void mapAreaInit( maparea_t *area, const worldpos_t min, const worldpos_t max, const mapareacallback_t callback, - const mapareanotify_t notify + const uint8_t notify, + const uint8_t trigger ); /** @@ -106,13 +114,16 @@ bool_t mapAreaShouldNotify(const maparea_t *area, const entity_t *entity); * @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. + * @param trigger Bitwise MAP_TRIGGER_* flags for which conditions should + * invoke 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 + const uint8_t notify, + const uint8_t trigger ); /** @@ -125,8 +136,10 @@ 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. + * for areas whose notify flags include the entity's type. Invokes the + * area's callback with MAP_TRIGGER_ENTER the frame the entity first + * becomes inside, MAP_TRIGGER_STEP on every subsequent frame it remains + * inside, and MAP_TRIGGER_EXIT the frame it leaves. * * @param entity Pointer to the entity to check. */ diff --git a/src/dusk/rpg/rpg.c b/src/dusk/rpg/rpg.c index 5b97ba4d..37c68c54 100644 --- a/src/dusk/rpg/rpg.c +++ b/src/dusk/rpg/rpg.c @@ -20,8 +20,8 @@ #include "assert/assert.h" #include "console/console.h" -void rpgTestAreaCallback(entity_t *entity) { - consolePrint("rpgTestAreaCallback"); +void rpgTestAreaCallback(entity_t *entity, const uint8_t trigger) { + consolePrint("rpgTestAreaCallback: trigger=%u", trigger); } errorret_t rpgInit(void) { @@ -53,7 +53,8 @@ errorret_t rpgInit(void) { (worldpos_t){ 11, 3, 0 }, (worldpos_t){ 16, 9, 10 }, rpgTestAreaCallback, - MAP_AREA_NOTIFY_ALL + MAP_AREA_NOTIFY_ALL, + MAP_TRIGGER_ENTER | MAP_TRIGGER_EXIT ); assertTrue(areaIndex != 0xFF, "No available map area slots!.");