NPCs
This commit is contained in:
@ -10,29 +10,18 @@
|
|||||||
#include "display/render.h"
|
#include "display/render.h"
|
||||||
|
|
||||||
#include "rpg/entity/entity.h"
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "rpg/world/maps/testmap.h"
|
||||||
|
|
||||||
int32_t main(const int32_t argc, const char **argv) {
|
int32_t main(const int32_t argc, const char **argv) {
|
||||||
inputInit();
|
inputInit();
|
||||||
randomInit();
|
randomInit();
|
||||||
renderInit();
|
renderInit();
|
||||||
|
|
||||||
entity_t *ent;
|
mapSet(TEST_MAP);
|
||||||
|
|
||||||
ent = &ENTITIES[0];
|
|
||||||
entityInit(ent, ENTITY_TYPE_PLAYER);
|
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
|
mapUpdate();
|
||||||
ent = ENTITIES;
|
|
||||||
do {
|
|
||||||
if(ent->type == ENTITY_TYPE_NULL) {
|
|
||||||
ent++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
entityUpdate(ent++);
|
|
||||||
} while(ent < (ENTITIES + ENTITY_COUNT));
|
|
||||||
|
|
||||||
if(!renderUpdate()) break;
|
if(!renderUpdate()) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,4 +8,5 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
entity.c
|
entity.c
|
||||||
player.c
|
player.c
|
||||||
|
npc.c
|
||||||
)
|
)
|
@ -8,20 +8,25 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
|
#include "rpg/world/map.h"
|
||||||
entity_t ENTITIES[ENTITY_COUNT];
|
#include "util/math.h"
|
||||||
|
|
||||||
entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
||||||
[ENTITY_TYPE_NULL] = { 0 },
|
[ENTITY_TYPE_NULL] = { 0 },
|
||||||
|
|
||||||
[ENTITY_TYPE_PLAYER] = {
|
[ENTITY_TYPE_PLAYER] = {
|
||||||
.init = playerInit,
|
.init = playerInit,
|
||||||
.update = playerUpdate
|
.update = playerUpdate,
|
||||||
|
.interact = NULL,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[ENTITY_TYPE_NPC] = {
|
||||||
|
.init = npcInit,
|
||||||
|
.update = npcUpdate,
|
||||||
|
.interact = npcInteract,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void entityInit(entity_t *entity, const entitytype_t type) {
|
void entityInit(entity_t *entity, const entitytype_t type) {
|
||||||
assertNotNull(entity, "Entity is NULL");
|
assertNotNull(entity, "Entity is NULL");
|
||||||
assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
||||||
@ -60,42 +65,128 @@ void entityTurn(entity_t *entity, const entitydir_t dir) {
|
|||||||
entity->dir = dir;
|
entity->dir = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
void entityWalk(entity_t *entity) {
|
void entityDirGetPosition(
|
||||||
assertNotNull(entity, "Entity cannot be NULL");
|
const entitydir_t dir,
|
||||||
assertFalse(entityIsWalking(entity), "Entity is already walking");
|
const uint8_t distance,
|
||||||
|
int8_t *outX,
|
||||||
|
int8_t *outY
|
||||||
|
) {
|
||||||
|
assertTrue(dir < ENTITY_DIR_COUNT, "Invalid entity direction");
|
||||||
|
assertNotNull(outX, "Output X cannot be NULL");
|
||||||
|
assertNotNull(outY, "Output Y cannot be NULL");
|
||||||
|
|
||||||
switch(entity->dir) {
|
switch(dir) {
|
||||||
case ENTITY_DIR_UP:
|
case ENTITY_DIR_UP:
|
||||||
entity->y--;
|
*outX = 0;
|
||||||
entity->subY = ENTITY_MOVE_SUBPIXEL;
|
*outY = -distance;
|
||||||
break;
|
break;
|
||||||
case ENTITY_DIR_DOWN:
|
case ENTITY_DIR_DOWN:
|
||||||
entity->y++;
|
*outX = 0;
|
||||||
entity->subY = -ENTITY_MOVE_SUBPIXEL;
|
*outY = distance;
|
||||||
break;
|
break;
|
||||||
case ENTITY_DIR_LEFT:
|
case ENTITY_DIR_LEFT:
|
||||||
entity->x--;
|
*outX = -distance;
|
||||||
entity->subX = ENTITY_MOVE_SUBPIXEL;
|
*outY = 0;
|
||||||
break;
|
break;
|
||||||
case ENTITY_DIR_RIGHT:
|
case ENTITY_DIR_RIGHT:
|
||||||
entity->x++;
|
*outX = distance;
|
||||||
entity->subX = -ENTITY_MOVE_SUBPIXEL;
|
*outY = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assertUnreachable("Invalid entity direction");
|
assertUnreachable("Invalid entity direction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entitydir_t entityGetLookDirection(
|
||||||
|
const entity_t *entity,
|
||||||
|
const uint8_t x,
|
||||||
|
const uint8_t y
|
||||||
|
) {
|
||||||
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
|
assertTrue(x < MAP.width, "X coordinate out of bounds");
|
||||||
|
assertTrue(y < MAP.height, "Y coordinate out of bounds");
|
||||||
|
|
||||||
|
int8_t dX = x - entity->x;
|
||||||
|
int8_t dY = y - entity->y;
|
||||||
|
|
||||||
|
// More horizontal movement or more vertical movement?
|
||||||
|
if(mathAbsI8(dX) > mathAbsI8(dY)) {
|
||||||
|
// More horizontal movement
|
||||||
|
if(dX < 0) return ENTITY_DIR_LEFT;
|
||||||
|
return ENTITY_DIR_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dY < 0) {
|
||||||
|
return ENTITY_DIR_UP;
|
||||||
|
}
|
||||||
|
return ENTITY_DIR_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void entityWalk(entity_t *entity) {
|
||||||
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
|
assertFalse(entityIsWalking(entity), "Entity is already walking");
|
||||||
|
assertTrue(entityCanWalk(entity, entity->dir, NULL), "Entity cannot walk");
|
||||||
|
|
||||||
|
int8_t tX, tY;
|
||||||
|
entityDirGetPosition(entity->dir, 1, &tX, &tY);
|
||||||
|
|
||||||
|
entity->y += tY;
|
||||||
|
entity->x += tX;
|
||||||
|
entity->subX = ENTITY_MOVE_SUBPIXEL * -tX;
|
||||||
|
entity->subY = ENTITY_MOVE_SUBPIXEL * -tY;
|
||||||
|
}
|
||||||
|
|
||||||
bool_t entityIsWalking(const entity_t *entity) {
|
bool_t entityIsWalking(const entity_t *entity) {
|
||||||
assertNotNull(entity, "Entity cannot be NULL");
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
return (entity->subX != 0 || entity->subY != 0);
|
return (entity->subX != 0 || entity->subY != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_t * entityGetAt(const uint8_t x, const uint8_t y) {
|
bool_t entityCanWalk(
|
||||||
entity_t *e = ENTITIES;
|
const entity_t *entity,
|
||||||
do {
|
const entitydir_t dir,
|
||||||
if(e->type && e->x == x && e->y == y) return e;
|
entity_t **entityInWay
|
||||||
e++;
|
) {
|
||||||
} while(e < (ENTITIES + ENTITY_COUNT));
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
return NULL;
|
assertTrue(dir < ENTITY_DIR_COUNT, "Invalid entity direction");
|
||||||
|
|
||||||
|
int8_t dX, dY;
|
||||||
|
entityDirGetPosition(dir, 1, &dX, &dY);
|
||||||
|
|
||||||
|
uint8_t tX = entity->x + dX;
|
||||||
|
if(tX < 0 || tX >= MAP.width) return false;
|
||||||
|
|
||||||
|
uint8_t tY = entity->y + dY;
|
||||||
|
if(tY < 0 || tY >= MAP.height) return false;
|
||||||
|
|
||||||
|
if(entityInWay == NULL) {
|
||||||
|
if(mapGetEntityAt(tX, tY) != NULL) return false;
|
||||||
|
} else {
|
||||||
|
*entityInWay = mapGetEntityAt(tX, tY);
|
||||||
|
if(*entityInWay != NULL) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void entityPositionSet(entity_t *entity, const uint8_t x, const uint8_t y) {
|
||||||
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
|
assertTrue(x < MAP.width, "X coordinate out of bounds");
|
||||||
|
assertTrue(y < MAP.height, "Y coordinate out of bounds");
|
||||||
|
entity->x = x;
|
||||||
|
entity->y = y;
|
||||||
|
entity->subX = 0;
|
||||||
|
entity->subY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t entityInteract(entity_t *interacted, entity_t *player) {
|
||||||
|
assertNotNull(interacted, "Interacted entity cannot be NULL");
|
||||||
|
assertNotNull(player, "Player entity cannot be NULL");
|
||||||
|
assertTrue(interacted->type != ENTITY_TYPE_NULL, "Interacted entity invalid");
|
||||||
|
assertTrue(player->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
|
||||||
|
|
||||||
|
if(ENTITY_CALLBACKS[interacted->type].interact != NULL) {
|
||||||
|
return ENTITY_CALLBACKS[interacted->type].interact(interacted, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
|
#include "npc.h"
|
||||||
|
|
||||||
#define ENTITY_WIDTH 16
|
#define ENTITY_WIDTH 16
|
||||||
#define ENTITY_HEIGHT 16
|
#define ENTITY_HEIGHT 16
|
||||||
@ -24,11 +25,14 @@ typedef enum {
|
|||||||
ENTITY_DIR_EAST = ENTITY_DIR_RIGHT
|
ENTITY_DIR_EAST = ENTITY_DIR_RIGHT
|
||||||
} entitydir_t;
|
} entitydir_t;
|
||||||
|
|
||||||
|
#define ENTITY_DIR_COUNT (ENTITY_DIR_RIGHT + 1)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ENTITY_TYPE_NULL = 0,
|
ENTITY_TYPE_NULL = 0,
|
||||||
ENTITY_TYPE_PLAYER = 1,
|
ENTITY_TYPE_PLAYER = 1,
|
||||||
|
ENTITY_TYPE_NPC = 2,
|
||||||
} entitytype_t;
|
} entitytype_t;
|
||||||
#define ENTITY_TYPE_COUNT (ENTITY_TYPE_PLAYER + 1)
|
#define ENTITY_TYPE_COUNT (ENTITY_TYPE_NPC + 1)
|
||||||
|
|
||||||
typedef struct _entity_t {
|
typedef struct _entity_t {
|
||||||
entitytype_t type;
|
entitytype_t type;
|
||||||
@ -39,15 +43,14 @@ typedef struct _entity_t {
|
|||||||
// Per type data
|
// Per type data
|
||||||
union {
|
union {
|
||||||
player_t player;
|
player_t player;
|
||||||
|
npc_t npc;
|
||||||
};
|
};
|
||||||
} entity_t;
|
} entity_t;
|
||||||
|
|
||||||
#define ENTITY_COUNT 16
|
|
||||||
extern entity_t ENTITIES[ENTITY_COUNT];
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (*init)(entity_t *entity);
|
void (*init)(entity_t *entity);
|
||||||
void (*update)(entity_t *entity);
|
void (*update)(entity_t *entity);
|
||||||
|
bool_t (*interact)(entity_t *self, entity_t *player);
|
||||||
} entitycallbacks_t;
|
} entitycallbacks_t;
|
||||||
extern entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
extern entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
||||||
|
|
||||||
@ -74,6 +77,36 @@ void entityUpdate(entity_t *entity);
|
|||||||
*/
|
*/
|
||||||
void entityTurn(entity_t *entity, const entitydir_t dir);
|
void entityTurn(entity_t *entity, const entitydir_t dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position of a specific direction at a given distance.
|
||||||
|
*
|
||||||
|
* @param dir The direction to get the position in.
|
||||||
|
* @param distance The distance to move in that direction.
|
||||||
|
* @param outX Pointer to store the resulting x-coordinate.
|
||||||
|
* @param outY Pointer to store the resulting y-coordinate.
|
||||||
|
*/
|
||||||
|
void entityDirGetPosition(
|
||||||
|
const entitydir_t dir,
|
||||||
|
const uint8_t distance,
|
||||||
|
int8_t *outX,
|
||||||
|
int8_t *outY
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the look direction for a given entity to be looking at the specified
|
||||||
|
* coordinates.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to get the look direction for.
|
||||||
|
* @param x The x-coordinate to look at.
|
||||||
|
* @param Y The y-coordinate to look at.
|
||||||
|
* @return The direction the entity should look towards.
|
||||||
|
*/
|
||||||
|
entitydir_t entityGetLookDirection(
|
||||||
|
const entity_t *entity,
|
||||||
|
const uint8_t x,
|
||||||
|
const uint8_t Y
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the entity walk in the current direction.
|
* Makes the entity walk in the current direction.
|
||||||
*
|
*
|
||||||
@ -90,10 +123,36 @@ void entityWalk(entity_t *entity);
|
|||||||
bool_t entityIsWalking(const entity_t *entity);
|
bool_t entityIsWalking(const entity_t *entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the entity at a given position.
|
* Checks if the entity can walk in a specific direction.
|
||||||
*
|
*
|
||||||
* @param x The x-coordinate of the entity.
|
* @param entity Pointer to the entity to check.
|
||||||
* @param y The y-coordinate of the entity.
|
* @param dir The direction to check for walking capability.
|
||||||
* @return Pointer to the entity at the specified position, or NULL.
|
* @param inWay Pointer to store any entity in the way, if applicable.
|
||||||
|
* @return true if the entity can walk in the specified direction, false otherwise.
|
||||||
*/
|
*/
|
||||||
entity_t * entityGetAt(const uint8_t x, const uint8_t y);
|
bool_t entityCanWalk(
|
||||||
|
const entity_t *entity,
|
||||||
|
const entitydir_t dir,
|
||||||
|
entity_t **inWay
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the position of the entity.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to set the position for.
|
||||||
|
* @param x The x-coordinate to set.
|
||||||
|
* @param y The y-coordinate to set.
|
||||||
|
*/
|
||||||
|
void entityPositionSet(entity_t *entity, const uint8_t x, const uint8_t y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles interaction between an entity and a player.
|
||||||
|
*
|
||||||
|
* @param self Pointer to the entity that is being interacted with.
|
||||||
|
* @param player Pointer to the player entity interacting with the entity.
|
||||||
|
* @return true if interaction happened.
|
||||||
|
*/
|
||||||
|
bool_t entityInteract(
|
||||||
|
entity_t *self,
|
||||||
|
entity_t *player
|
||||||
|
);
|
31
src/dusk/rpg/entity/npc.c
Normal file
31
src/dusk/rpg/entity/npc.c
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "entity.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
void npcInit(entity_t *ent) {
|
||||||
|
assertNotNull(ent, "NPC entity is NULL");
|
||||||
|
assertTrue(ent->type == ENTITY_TYPE_NPC, "Entity is not an NPC");
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcUpdate(entity_t *ent) {
|
||||||
|
assertNotNull(ent, "Entity is NULL");
|
||||||
|
assertTrue(ent->type == ENTITY_TYPE_NPC, "Entity is not an NPC");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t npcInteract(entity_t *self, entity_t *player) {
|
||||||
|
assertNotNull(self, "NPC entity cannot be NULL");
|
||||||
|
assertNotNull(player, "Player entity cannot be NULL");
|
||||||
|
assertTrue(self->type == ENTITY_TYPE_NPC, "Entity is not an NPC");
|
||||||
|
assertTrue(player->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
|
||||||
|
|
||||||
|
// Look at the player
|
||||||
|
entityTurn(self, entityGetLookDirection(self, player->x, player->y));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
37
src/dusk/rpg/entity/npc.h
Normal file
37
src/dusk/rpg/entity/npc.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
typedef struct _entity_t entity_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t nothing; // Placeholder for NPC-specific data
|
||||||
|
} npc_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an NPC entity.
|
||||||
|
*
|
||||||
|
* @param ent Pointer to the NPC entity to initialize.
|
||||||
|
*/
|
||||||
|
void npcInit(entity_t *ent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an NPC entity.
|
||||||
|
*
|
||||||
|
* @param ent Entity to update.
|
||||||
|
*/
|
||||||
|
void npcUpdate(entity_t *ent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles interaction between an NPC and a player.
|
||||||
|
*
|
||||||
|
* @param self Pointer to the NPC entity.
|
||||||
|
* @param player Pointer to the player entity interacting with the NPC.
|
||||||
|
*/
|
||||||
|
bool_t npcInteract(entity_t *self, entity_t *player);
|
@ -8,6 +8,7 @@
|
|||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
#include "rpg/world/map.h"
|
||||||
|
|
||||||
void playerInit(entity_t *player) {
|
void playerInit(entity_t *player) {
|
||||||
assertNotNull(player, "Player entity is NULL");
|
assertNotNull(player, "Player entity is NULL");
|
||||||
@ -15,28 +16,43 @@ void playerInit(entity_t *player) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void playerUpdate(entity_t *entity) {
|
void playerUpdate(entity_t *entity) {
|
||||||
|
entity_t *other;
|
||||||
assertNotNull(entity, "Entity is NULL");
|
assertNotNull(entity, "Entity is NULL");
|
||||||
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
|
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
|
||||||
|
|
||||||
if(!entityIsWalking(entity)) {
|
// Handle movement
|
||||||
entitydir_t dir = 0xFF;
|
if(entityIsWalking(entity)) {
|
||||||
if(inputIsDown(INPUT_UP)) {
|
return;
|
||||||
dir = ENTITY_DIR_UP;
|
|
||||||
} else if(inputIsDown(INPUT_DOWN)) {
|
|
||||||
dir = ENTITY_DIR_DOWN;
|
|
||||||
} else if(inputIsDown(INPUT_LEFT)) {
|
|
||||||
dir = ENTITY_DIR_LEFT;
|
|
||||||
} else if(inputIsDown(INPUT_RIGHT)) {
|
|
||||||
dir = ENTITY_DIR_RIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dir != 0xFF) {
|
|
||||||
if(dir != entity->dir) {
|
|
||||||
entityTurn(entity, dir);
|
|
||||||
} else {
|
|
||||||
entityWalk(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entitydir_t dir = 0xFF;
|
||||||
|
if(inputIsDown(INPUT_UP)) {
|
||||||
|
dir = ENTITY_DIR_UP;
|
||||||
|
} else if(inputIsDown(INPUT_DOWN)) {
|
||||||
|
dir = ENTITY_DIR_DOWN;
|
||||||
|
} else if(inputIsDown(INPUT_LEFT)) {
|
||||||
|
dir = ENTITY_DIR_LEFT;
|
||||||
|
} else if(inputIsDown(INPUT_RIGHT)) {
|
||||||
|
dir = ENTITY_DIR_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dir != 0xFF) {
|
||||||
|
if(dir != entity->dir) {
|
||||||
|
entityTurn(entity, dir);
|
||||||
|
} else {
|
||||||
|
if(!entityCanWalk(entity, dir, &other)) return;
|
||||||
|
entityWalk(entity);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle interaction
|
||||||
|
if(inputWasPressed(INPUT_ACTION)) {
|
||||||
|
int8_t dX, dY;
|
||||||
|
entityDirGetPosition(entity->dir, 1, &dX, &dY);
|
||||||
|
other = mapGetEntityAt(entity->x + dX, entity->y + dY);
|
||||||
|
|
||||||
|
assertTrue(other != entity, "Player trying to interact with itself?");
|
||||||
|
if(other != NULL && entityInteract(other, entity)) return;
|
||||||
|
}
|
||||||
}
|
}
|
@ -8,3 +8,6 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
map.c
|
map.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Subdirs
|
||||||
|
add_subdirectory(maps)
|
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
map_t MAP;
|
||||||
|
|
||||||
|
void mapSet(const mapinfo_t mapInfo) {
|
||||||
|
assertTrue(mapInfo.init != NULL, "Map initialization function cannot be NULL");
|
||||||
|
mapInfo.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapInit(const uint8_t width, const uint8_t height) {
|
||||||
|
memoryZero(&MAP, sizeof(map_t));
|
||||||
|
|
||||||
|
MAP.width = width;
|
||||||
|
MAP.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapUpdate() {
|
||||||
|
entity_t *ent = MAP.entities;
|
||||||
|
do {
|
||||||
|
if(ent->type != ENTITY_TYPE_NULL) {
|
||||||
|
entityUpdate(ent);
|
||||||
|
}
|
||||||
|
ent++;
|
||||||
|
} while(ent < MAP.entities + MAP_ENTITY_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity_t * mapGetEntityAt(const uint8_t x, const uint8_t y) {
|
||||||
|
if(x >= MAP.width || y >= MAP.height) return NULL;
|
||||||
|
entity_t *ent = MAP.entities;
|
||||||
|
do {
|
||||||
|
if(ent->type != ENTITY_TYPE_NULL && ent->x == x && ent->y == y) return ent;
|
||||||
|
ent++;
|
||||||
|
} while(ent < MAP.entities + MAP_ENTITY_COUNT);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
@ -6,11 +6,54 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
|
#define MAP_ENTITY_COUNT 16
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t width;
|
uint8_t width;
|
||||||
uint8_t height;
|
uint8_t height;
|
||||||
|
entity_t entities[MAP_ENTITY_COUNT];
|
||||||
} map_t;
|
} map_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
void (*init)(void);
|
||||||
|
} mapinfo_t;
|
||||||
|
|
||||||
extern map_t MAP;
|
extern map_t MAP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current map to the specified map information.
|
||||||
|
*
|
||||||
|
* This function initializes the map with the provided map information,
|
||||||
|
* including its name and initialization function.
|
||||||
|
*
|
||||||
|
* @param mapInfo Map information containing the name and initialization function.
|
||||||
|
*/
|
||||||
|
void mapSet(const mapinfo_t mapInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the map with the given width and height.
|
||||||
|
*
|
||||||
|
* @param width Width of the map.
|
||||||
|
* @param height Height of the map.
|
||||||
|
*/
|
||||||
|
void mapInit(const uint8_t width, const uint8_t height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the map, processing all entities.
|
||||||
|
*
|
||||||
|
* This function should be called every frame to update the state of the map
|
||||||
|
* and its entities.
|
||||||
|
*/
|
||||||
|
void mapUpdate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the entity at the specified coordinates.
|
||||||
|
*
|
||||||
|
* @param x X coordinate of the entity.
|
||||||
|
* @param y Y coordinate of the entity.
|
||||||
|
* @return Pointer to the entity at the specified coordinates or NULL.
|
||||||
|
*/
|
||||||
|
entity_t * mapGetEntityAt(const uint8_t x, const uint8_t y);
|
9
src/dusk/rpg/world/maps/CMakeLists.txt
Normal file
9
src/dusk/rpg/world/maps/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
|
PRIVATE
|
||||||
|
)
|
31
src/dusk/rpg/world/maps/testmap.h
Normal file
31
src/dusk/rpg/world/maps/testmap.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "rpg/world/map.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the test map.
|
||||||
|
*
|
||||||
|
* This function sets up a test map with predefined dimensions.
|
||||||
|
*/
|
||||||
|
void testMapInit() {
|
||||||
|
mapInit(10, 10);
|
||||||
|
|
||||||
|
entity_t *ent = MAP.entities;
|
||||||
|
entityInit(ent, ENTITY_TYPE_PLAYER);
|
||||||
|
|
||||||
|
ent++;
|
||||||
|
entityInit(ent, ENTITY_TYPE_NPC);
|
||||||
|
entityPositionSet(ent, 5, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapinfo_t TEST_MAP = {
|
||||||
|
.name = "Test Map",
|
||||||
|
.init = testMapInit
|
||||||
|
};
|
@ -9,4 +9,5 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
memory.c
|
memory.c
|
||||||
string.c
|
string.c
|
||||||
random.c
|
random.c
|
||||||
|
math.c
|
||||||
)
|
)
|
12
src/dusk/util/math.c
Normal file
12
src/dusk/util/math.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
|
int8_t mathAbsI8(const int8_t val) {
|
||||||
|
return (val > 0) ? val : -val;
|
||||||
|
}
|
17
src/dusk/util/math.h
Normal file
17
src/dusk/util/math.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute (ignoring sign) value of an 8-bit integer.
|
||||||
|
*
|
||||||
|
* @param val The 8-bit integer value to get the absolute value of.
|
||||||
|
* @return The absolute value of the input integer.
|
||||||
|
*/
|
||||||
|
int8_t mathAbsI8(const int8_t val);
|
@ -8,7 +8,7 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "display/render.h"
|
#include "display/render.h"
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
#include "rpg/entity/entity.h"
|
#include "rpg/world/map.h"
|
||||||
|
|
||||||
const uint16_t RENDER_WIDTH = 480;
|
const uint16_t RENDER_WIDTH = 480;
|
||||||
const uint16_t RENDER_HEIGHT = 270;
|
const uint16_t RENDER_HEIGHT = 270;
|
||||||
@ -29,7 +29,7 @@ bool_t renderUpdate() {
|
|||||||
ClearBackground(RAYWHITE);
|
ClearBackground(RAYWHITE);
|
||||||
DrawText("Hello, Dusk!", 10, 10, 20, BLACK);
|
DrawText("Hello, Dusk!", 10, 10, 20, BLACK);
|
||||||
|
|
||||||
entity_t *ent = ENTITIES;
|
entity_t *ent = MAP.entities;
|
||||||
do {
|
do {
|
||||||
if(ent->type == ENTITY_TYPE_NULL) {
|
if(ent->type == ENTITY_TYPE_NULL) {
|
||||||
ent++;
|
ent++;
|
||||||
@ -83,7 +83,7 @@ bool_t renderUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ent++;
|
ent++;
|
||||||
} while(ent < (ENTITIES + ENTITY_COUNT));
|
} while(ent < (MAP.entities + MAP_ENTITY_COUNT));
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user