Overworld render test.
This commit is contained in:
@@ -12,14 +12,15 @@
|
|||||||
#include "display/scene/scenemanager.h"
|
#include "display/scene/scenemanager.h"
|
||||||
#include "display/mesh/quad.h"
|
#include "display/mesh/quad.h"
|
||||||
#include "asset/assetmanager.h"
|
#include "asset/assetmanager.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
camera_t SCENE_OVERWORLD_CAMERA;
|
sceneoverworld_t SCENE_OVERWORLD;
|
||||||
asset_t *testAsset;
|
asset_t *testAsset;
|
||||||
ref_t testAssetRef;
|
ref_t testAssetRef;
|
||||||
|
|
||||||
void sceneOverworldInit(void) {
|
void sceneOverworldInit(void) {
|
||||||
cameraInit(&SCENE_OVERWORLD_CAMERA);
|
cameraInit(&SCENE_OVERWORLD.camera);
|
||||||
glm_vec3_copy((vec3){32.0f, 32.0f, 32.0f}, SCENE_OVERWORLD_CAMERA.lookat.position);
|
glm_vec3_copy((vec3){ 0.0f, 1.0f, 0.0f }, SCENE_OVERWORLD.camera.lookat.up);
|
||||||
|
|
||||||
scene_t *scene = &SCENE_MANAGER_SCENES[SCENE_TYPE_OVERWORLD];
|
scene_t *scene = &SCENE_MANAGER_SCENES[SCENE_TYPE_OVERWORLD];
|
||||||
scene->flags |= SCENE_FLAG_ACTIVE | SCENE_FLAG_VISIBLE;
|
scene->flags |= SCENE_FLAG_ACTIVE | SCENE_FLAG_VISIBLE;
|
||||||
@@ -31,24 +32,59 @@ void sceneOverworldUpdate(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sceneOverworldRender(void) {
|
void sceneOverworldRender(void) {
|
||||||
cameraPushMatrix(&SCENE_OVERWORLD_CAMERA);
|
const float_t camOffset = 12.0f;
|
||||||
|
const float_t fbWidth = frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||||
|
const float_t fbHeight = frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||||
|
const float_t aspect = fbWidth / fbHeight;
|
||||||
|
const float_t pixelPerfectOffset = tanf(
|
||||||
|
(glm_rad(180) - SCENE_OVERWORLD.camera.perspective.fov) / 2.0f
|
||||||
|
) * (fbHeight/ 2.0f);
|
||||||
|
|
||||||
|
glm_vec3_copy((vec3){
|
||||||
|
-100.0f, -100.0f, 0.0f
|
||||||
|
}, SCENE_OVERWORLD.camera.lookat.target);
|
||||||
|
glm_vec3_copy((vec3){
|
||||||
|
SCENE_OVERWORLD.camera.lookat.target[0],
|
||||||
|
SCENE_OVERWORLD.camera.lookat.target[1] + camOffset,
|
||||||
|
SCENE_OVERWORLD.camera.lookat.target[2] + pixelPerfectOffset
|
||||||
|
}, SCENE_OVERWORLD.camera.lookat.position);
|
||||||
|
|
||||||
|
cameraPushMatrix(&SCENE_OVERWORLD.camera);
|
||||||
|
|
||||||
|
sceneOverworldRenderMap(&testMap);
|
||||||
|
|
||||||
|
spriteBatchFlush();
|
||||||
|
cameraPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sceneOverworldRenderMap(const map_t *map) {
|
||||||
|
assertNotNull(map, "Map pointer cannot be NULL");
|
||||||
|
|
||||||
// Draw base layer
|
// Draw base layer
|
||||||
|
|
||||||
// Draw entities
|
// Draw entities
|
||||||
|
entity_t *start = &map->entities[0];
|
||||||
|
entity_t *end = &map->entities[map->entityCount];
|
||||||
|
while(start < end) {
|
||||||
|
// Render entity here.
|
||||||
|
sceneOverworldRenderEntity(start);
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
// Draw overlay layer.
|
// Draw overlay layer.
|
||||||
// renderTextDraw(0.0f, 0.0f, "Hello World", 0xFF, 0xFF, 0xFF);
|
}
|
||||||
|
|
||||||
// spriteBatchPush(
|
void sceneOverworldRenderEntity(const entity_t *entity) {
|
||||||
// &testAsset->paletteImage.texture,
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
// 0.0f, 0.0f, 12.0f, 12.0f,
|
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
||||||
// 0xFF, 0xFF, 0xFF, 0xFF,
|
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
|
||||||
// 0.0f, 0.0f, 1.0f, 1.0f
|
|
||||||
// );
|
|
||||||
spriteBatchFlush();
|
|
||||||
|
|
||||||
cameraPopMatrix();
|
// For now, just draw a placeholder quad.
|
||||||
|
spriteBatchPush(
|
||||||
|
&testAsset->paletteImage.texture,
|
||||||
|
entity->x, entity->y, entity->x + 32.0f, entity->y + 32.0f,
|
||||||
|
COLOR_WHITE, 0.0f, 0.0f, 0.125f, 0.125f
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sceneOverworldDispose(void) {
|
void sceneOverworldDispose(void) {
|
||||||
|
@@ -7,8 +7,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "display/camera.h"
|
#include "display/camera.h"
|
||||||
|
#include "rpg/world/map.h"
|
||||||
|
|
||||||
extern camera_t SCENE_OVERWORLD_CAMERA;
|
typedef struct {
|
||||||
|
camera_t camera;
|
||||||
|
} sceneoverworld_t;
|
||||||
|
|
||||||
|
extern sceneoverworld_t SCENE_OVERWORLD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the overworld scene.
|
* Initialize the overworld scene.
|
||||||
@@ -25,6 +30,20 @@ void sceneOverworldUpdate(void);
|
|||||||
*/
|
*/
|
||||||
void sceneOverworldRender(void);
|
void sceneOverworldRender(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a map in the overworld scene.
|
||||||
|
*
|
||||||
|
* @param map Pointer to the map to render.
|
||||||
|
*/
|
||||||
|
void sceneOverworldRenderMap(const map_t *map);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render an entity in the overworld scene.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to render.
|
||||||
|
*/
|
||||||
|
void sceneOverworldRenderEntity(const entity_t *entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispose of the overworld scene.
|
* Dispose of the overworld scene.
|
||||||
*/
|
*/
|
||||||
|
@@ -41,6 +41,8 @@ errorret_t engineUpdate(void) {
|
|||||||
inputUpdate();
|
inputUpdate();
|
||||||
consoleUpdate();
|
consoleUpdate();
|
||||||
assetManagerUpdate();
|
assetManagerUpdate();
|
||||||
|
|
||||||
|
rpgUpdate();
|
||||||
errorChain(displayUpdate());
|
errorChain(displayUpdate());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
|
@@ -108,6 +108,13 @@ bool_t inputReleased(const inputaction_t action) {
|
|||||||
return !inputIsDown(action) && inputWasDown(action);
|
return !inputIsDown(action) && inputWasDown(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
|
||||||
|
assertTrue(neg < INPUT_ACTION_COUNT, "Negative input action out of bounds");
|
||||||
|
assertTrue(pos < INPUT_ACTION_COUNT, "Positive input action out of bounds");
|
||||||
|
|
||||||
|
return inputGetCurrentValue(pos) - inputGetCurrentValue(neg);
|
||||||
|
}
|
||||||
|
|
||||||
void inputBind(const inputbutton_t button, const char_t *action) {
|
void inputBind(const inputbutton_t button, const char_t *action) {
|
||||||
assertNotNull(action, "Input action is null");
|
assertNotNull(action, "Input action is null");
|
||||||
assertStrLenMin(action, 1, "Input action is empty");
|
assertStrLenMin(action, 1, "Input action is empty");
|
||||||
|
@@ -80,6 +80,16 @@ bool_t inputPressed(const inputaction_t action);
|
|||||||
*/
|
*/
|
||||||
bool_t inputReleased(const inputaction_t action);
|
bool_t inputReleased(const inputaction_t action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value of an input axis, defined by two actions (negative and
|
||||||
|
* positive).
|
||||||
|
*
|
||||||
|
* @param neg The action representing the negative direction of the axis.
|
||||||
|
* @param pos The action representing the positive direction of the axis.
|
||||||
|
* @return The current value of the axis (-1.0f to 1.0f).
|
||||||
|
*/
|
||||||
|
float_t inputAxis(const inputaction_t neg, const inputaction_t pos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds an input button to an action. Will first check if a matching action
|
* Binds an input button to an action. Will first check if a matching action
|
||||||
* exists, otherwise it will be treated as a command.
|
* exists, otherwise it will be treated as a command.
|
||||||
@@ -87,4 +97,4 @@ bool_t inputReleased(const inputaction_t action);
|
|||||||
* @param button The input button to bind.
|
* @param button The input button to bind.
|
||||||
* @param action The name of the input action or command to bind the button to.
|
* @param action The name of the input action or command to bind the button to.
|
||||||
*/
|
*/
|
||||||
void inputBind(const inputbutton_t data, const char_t *action);
|
void inputBind(const inputbutton_t data, const char_t *action);
|
||||||
|
@@ -11,5 +11,4 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(item)
|
|
||||||
add_subdirectory(world)
|
add_subdirectory(world)
|
@@ -6,8 +6,6 @@
|
|||||||
# Sources
|
# Sources
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
direction.c
|
|
||||||
entity.c
|
entity.c
|
||||||
player.c
|
player.c
|
||||||
npc.c
|
|
||||||
)
|
)
|
@@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "direction.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
float_t directionToAngle(const direction_t dir) {
|
|
||||||
switch(dir) {
|
|
||||||
case DIRECTION_NORTH: return (M_PI_2);
|
|
||||||
case DIRECTION_SOUTH: return -(M_PI_2);
|
|
||||||
case DIRECTION_EAST: return 0;
|
|
||||||
case DIRECTION_WEST: return (M_PI);
|
|
||||||
default: return 0; // Should never happen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void directionGetCoordinates(
|
|
||||||
const direction_t dir,
|
|
||||||
int8_t *x, int8_t *y
|
|
||||||
) {
|
|
||||||
assertNotNull(x, "X coordinate pointer cannot be NULL");
|
|
||||||
assertNotNull(y, "Y coordinate pointer cannot be NULL");
|
|
||||||
|
|
||||||
switch(dir) {
|
|
||||||
case DIRECTION_NORTH:
|
|
||||||
*x = 0;
|
|
||||||
*y = -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_SOUTH:
|
|
||||||
*x = 0;
|
|
||||||
*y = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_EAST:
|
|
||||||
*x = 1;
|
|
||||||
*y = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_WEST:
|
|
||||||
*x = -1;
|
|
||||||
*y = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Invalid direction");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
DIRECTION_SOUTH = 0,
|
|
||||||
DIRECTION_EAST = 1,
|
|
||||||
DIRECTION_WEST = 2,
|
|
||||||
DIRECTION_NORTH = 3,
|
|
||||||
|
|
||||||
DIRECTION_UP = DIRECTION_NORTH,
|
|
||||||
DIRECTION_DOWN = DIRECTION_SOUTH,
|
|
||||||
DIRECTION_LEFT = DIRECTION_WEST,
|
|
||||||
DIRECTION_RIGHT = DIRECTION_EAST,
|
|
||||||
} direction_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a direction to an angle in float_t format.
|
|
||||||
*
|
|
||||||
* @param dir The direction to convert.
|
|
||||||
* @return The angle corresponding to the direction.
|
|
||||||
*/
|
|
||||||
float_t directionToAngle(const direction_t dir);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the relative coordinates for a given direction.
|
|
||||||
*
|
|
||||||
* @param dir The direction to get coordinates for.
|
|
||||||
* @param x Pointer to store the x coordinate.
|
|
||||||
* @param y Pointer to store the y coordinate.
|
|
||||||
*/
|
|
||||||
void directionGetCoordinates(
|
|
||||||
const direction_t dir,
|
|
||||||
int8_t *x, int8_t *y
|
|
||||||
);
|
|
@@ -8,116 +8,30 @@
|
|||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
// #include "world/world.h"
|
|
||||||
// #include "world/tiledata.h"
|
|
||||||
#include "time.h"
|
|
||||||
|
|
||||||
entity_t ENTITIES[ENTITY_COUNT_MAX] = {0};
|
entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
||||||
|
{ NULL, NULL }, // ENTITY_TYPE_NULL
|
||||||
|
{ playerInit, playerUpdate }, // ENTITY_TYPE_PLAYER
|
||||||
|
{ NULL, NULL }, // ENTITY_TYPE_NPC
|
||||||
|
};
|
||||||
|
|
||||||
// entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
void entityInit(entity_t *entity, const entitytype_t type) {
|
||||||
// {NULL}, // ENTITY_TYPE_NULL
|
|
||||||
// {
|
|
||||||
// .load = playerEntityLoad,
|
|
||||||
// .update = playerEntityUpdate,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// .load = npcLoad,
|
|
||||||
// .update = npcUpdate,
|
|
||||||
// .interact = npcInteract,
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
|
|
||||||
void entityInit(entity_t *entity) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
||||||
|
assertTrue(type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
|
||||||
|
assertNotNull(ENTITY_CALLBACKS[type].init, "Entity type has no init function");
|
||||||
|
|
||||||
memoryZero(entity, sizeof(entity_t));
|
memoryZero(entity, sizeof(entity_t));
|
||||||
|
entity->type = type;
|
||||||
|
|
||||||
// entity->type = ENTITY_TYPE_NULL;
|
ENTITY_CALLBACKS[type].init(entity);
|
||||||
// entity->x = 0;
|
|
||||||
// entity->y = 0;
|
|
||||||
// entity->dir = DIRECTION_SOUTH;
|
|
||||||
// entity->id = 0;
|
|
||||||
|
|
||||||
// ENTITY_CALLBACKS[entity->type].load(entity, source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void entityUpdate(entity_t *entity) {
|
void entityUpdate(entity_t *entity) {
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
|
||||||
// assertNotNull(
|
assertNotNull(ENTITY_CALLBACKS[entity->type].update, "enttype lacks update");
|
||||||
// ENTITY_CALLBACKS[entity->type].update,
|
|
||||||
// "Entity type has no update callback"
|
|
||||||
// );
|
|
||||||
|
|
||||||
// ENTITY_CALLBACKS[entity->type].update(entity);
|
ENTITY_CALLBACKS[entity->type].update(entity);
|
||||||
|
}
|
||||||
if(entity->subX > 0) {
|
|
||||||
entity->subX -= entity->moveSpeed;
|
|
||||||
} else if(entity->subX < 0) {
|
|
||||||
entity->subX += entity->moveSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(entity->subY > 0) {
|
|
||||||
entity->subY -= entity->moveSpeed;
|
|
||||||
} else if(entity->subY < 0) {
|
|
||||||
entity->subY += entity->moveSpeed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityMove(entity_t *entity, const uint8_t moveSpeed) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
assertFalse(
|
|
||||||
entityIsMoving(entity),
|
|
||||||
"Entity is already moving, cannot move again"
|
|
||||||
);
|
|
||||||
|
|
||||||
int8_t x = 0, y = 0;
|
|
||||||
directionGetCoordinates(entity->dir, &x, &y);
|
|
||||||
|
|
||||||
// entity in way?
|
|
||||||
entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
|
|
||||||
if(ent != NULL) return;
|
|
||||||
|
|
||||||
entity->x += x;
|
|
||||||
entity->y += y;
|
|
||||||
// entity->subX = TILE_WIDTH_HEIGHT * -x;
|
|
||||||
// entity->subY = TILE_WIDTH_HEIGHT * -y;
|
|
||||||
entity->moveSpeed = moveSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityTurn(entity_t *entity, const direction_t dir) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
assertTrue(
|
|
||||||
dir >= DIRECTION_SOUTH && dir <= DIRECTION_NORTH, "Invalid direction"
|
|
||||||
);
|
|
||||||
assertFalse(
|
|
||||||
entityIsMoving(entity), "Entity is already moving, cannot turn"
|
|
||||||
);
|
|
||||||
|
|
||||||
entity->dir = dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t entityIsMoving(const entity_t *entity) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
|
|
||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
return entity->subX != 0 || entity->subY != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_t * entityGetAt(
|
|
||||||
const int32_t tileX,
|
|
||||||
const int32_t tileY
|
|
||||||
) {
|
|
||||||
entity_t *entity = ENTITIES;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if(entity->type == ENTITY_TYPE_NULL) continue;
|
|
||||||
if(entity->x == tileX && entity->y == tileY) return entity;
|
|
||||||
} while((entity++) < &ENTITIES[ENTITY_COUNT_MAX - 1]);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
@@ -6,94 +6,51 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "direction.h"
|
// #include "direction.h"
|
||||||
#include "rpg/entity/player.h"
|
#include "rpg/entity/player.h"
|
||||||
#include "npc.h"
|
// #include "npc.h"
|
||||||
|
|
||||||
#define ENTITY_COUNT_MAX 32
|
// #define ENTITY_TURN_DURATION 0.075f // Duration for turning in seconds
|
||||||
#define ENTITY_TURN_DURATION 0.075f // Duration for turning in seconds
|
// #define ENTITY_MOVE_DURATION 0.1f // Duration for moving 1 tile, in seconds.
|
||||||
#define ENTITY_MOVE_DURATION 0.1f // Duration for moving 1 tile, in seconds.
|
// #define ENTITY_PLAYER_INDEX 0
|
||||||
#define ENTITY_PLAYER_INDEX 0
|
|
||||||
|
typedef struct {
|
||||||
|
void (*init)(entity_t *entity);
|
||||||
|
void (*update)(entity_t *entity);
|
||||||
|
} entitycallback_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ENTITY_TYPE_NULL = 0,
|
ENTITY_TYPE_NULL,
|
||||||
ENTITY_TYPE_PLAYER = 1,
|
ENTITY_TYPE_PLAYER,
|
||||||
ENTITY_TYPE_NPC = 2,
|
ENTITY_TYPE_NPC,
|
||||||
|
|
||||||
|
ENTITY_TYPE_COUNT
|
||||||
} entitytype_t;
|
} entitytype_t;
|
||||||
#define ENTITY_TYPE_COUNT 3
|
|
||||||
|
|
||||||
typedef struct _entity_t {
|
|
||||||
uint32_t id;// Completely unique ID for this entity.
|
|
||||||
int32_t x, y;
|
|
||||||
int8_t subX, subY;
|
|
||||||
uint8_t moveSpeed;
|
|
||||||
|
|
||||||
|
typedef struct entity_s {
|
||||||
|
// uint32_t id;// Completely unique ID for this entity.
|
||||||
|
float_t x, y;
|
||||||
entitytype_t type;
|
entitytype_t type;
|
||||||
direction_t dir;
|
// direction_t dir;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
npc_t npc;
|
player_t player;
|
||||||
playerentity_t player;
|
|
||||||
};
|
};
|
||||||
} entity_t;
|
} entity_t;
|
||||||
|
|
||||||
// typedef struct {
|
extern entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
||||||
// void (*load) (entity_t *entity, const entity_t *source);
|
|
||||||
// void (*update) (entity_t *entity);
|
|
||||||
// void (*interact)(entity_t *player, entity_t *self);
|
|
||||||
// } entitycallback_t;
|
|
||||||
|
|
||||||
extern entity_t ENTITIES[ENTITY_COUNT_MAX];
|
|
||||||
// extern entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an entity from the generated entity data.
|
* Initializes an entity structure.
|
||||||
*
|
*
|
||||||
* @param entity Pointer to the entity to initialize.
|
* @param entity Pointer to the entity structure to initialize.
|
||||||
* @param source Pointer to the source entity data.
|
* @param type The type of the entity.
|
||||||
*/
|
*/
|
||||||
void entityInit(entity_t *entity);
|
void entityInit(entity_t *entity, const entitytype_t type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the entity's state.
|
* Updates an entity.
|
||||||
*
|
*
|
||||||
* @param entity Pointer to the entity to update.
|
* @param entity Pointer to the entity structure to update.
|
||||||
*/
|
*/
|
||||||
void entityUpdate(entity_t *entity);
|
void entityUpdate(entity_t *entity);
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the entity by the specified x and y offsets.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to move.
|
|
||||||
* @param moveSpeed The speed at which to move the entity.
|
|
||||||
*/
|
|
||||||
void entityMove(entity_t *entity, const uint8_t moveSpeed);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns the entity to face the specified direction.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to turn.
|
|
||||||
* @param dir The direction to turn the entity to.
|
|
||||||
*/
|
|
||||||
void entityTurn(entity_t *entity, const direction_t dir);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether or not an entity is currently moving.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to check.
|
|
||||||
* @return True if the entity is moving, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t entityIsMoving(const entity_t *entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the entity at the specified tile coordinates.
|
|
||||||
*
|
|
||||||
* @param tileX The x coordinate of the tile to get the entity from.
|
|
||||||
* @param tileY The y coordinate of the tile to get the entity from.
|
|
||||||
* @return Pointer to the entity at the specified coordinates, or NULL if no
|
|
||||||
* entity exists there.
|
|
||||||
*/
|
|
||||||
entity_t *entityGetAt(
|
|
||||||
const int32_t tileX,
|
|
||||||
const int32_t tileY
|
|
||||||
);
|
|
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
// #include "ui/uitextbox.h"
|
|
||||||
// #include "locale/language.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void npcLoad(entity_t *entity, const entity_t *source) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
assertNotNull(source, "Source entity pointer cannot be NULL");
|
|
||||||
assertTrue(source->type == ENTITY_TYPE_NPC, "Source entity type must be NPC");
|
|
||||||
|
|
||||||
entity->npc = source->npc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void npcUpdate(entity_t *entity) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void npcInteract(entity_t *player, entity_t *self) {
|
|
||||||
assertTrue(self->type == ENTITY_TYPE_NPC, "Entity must be of type NPC");
|
|
||||||
|
|
||||||
switch(self->npc.interactType) {
|
|
||||||
case NPC_INTERACT_TYPE_NONE:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NPC_INTERACT_TYPE_TEXT:
|
|
||||||
// uiTextboxSetText(languageGet(self->npc.text));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NPC_INTERACT_TYPE_CONVO:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NPC_INTERACT_TYPE_EVENT:
|
|
||||||
// eventSetActive(self->npc.eventData);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Unknown NPC interaction type");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef struct _entity_t entity_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NPC_INTERACT_TYPE_NONE = 0,
|
|
||||||
NPC_INTERACT_TYPE_TEXT = 1,
|
|
||||||
NPC_INTERACT_TYPE_CONVO = 2,
|
|
||||||
NPC_INTERACT_TYPE_EVENT = 3,
|
|
||||||
} npcinteracttype_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
npcinteracttype_t interactType;
|
|
||||||
|
|
||||||
union {
|
|
||||||
const char_t* text;
|
|
||||||
// const eventdata_t *eventData;
|
|
||||||
};
|
|
||||||
} npc_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the NPC entity.
|
|
||||||
*
|
|
||||||
* @param entity The entity to initialize.
|
|
||||||
* @param source The source entity to copy data from.
|
|
||||||
*/
|
|
||||||
void npcLoad(entity_t *entity, const entity_t *source);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the NPC entity.
|
|
||||||
*
|
|
||||||
* @param entity The entity to update.
|
|
||||||
*/
|
|
||||||
void npcUpdate(entity_t *entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles interaction between the player and the NPC.
|
|
||||||
*
|
|
||||||
* @param player The player entity interacting with the NPC.
|
|
||||||
* @param self The NPC entity being interacted with.
|
|
||||||
*/
|
|
||||||
void npcInteract(entity_t *player, entity_t *self);
|
|
@@ -7,81 +7,23 @@
|
|||||||
|
|
||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
|
#include "time/time.h"
|
||||||
#include "input/input.h"
|
#include "input/input.h"
|
||||||
// #include "display/render.h"
|
|
||||||
// #include "world/world.h"
|
|
||||||
|
|
||||||
// #include "ui/uitextbox.h"
|
void playerInit(entity_t *entity) {
|
||||||
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
inventory_t PLAYER_INVENTORY;
|
|
||||||
|
|
||||||
void playerInit() {
|
|
||||||
entity_t *ent = &ENTITIES[ENTITY_PLAYER_INDEX];
|
|
||||||
entityInit(ent);
|
|
||||||
inventoryInit(&PLAYER_INVENTORY, INVENTORY_SIZE_MAX);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void playerEntityLoad(entity_t *entity, const entity_t *source) {
|
void playerUpdate(entity_t *entity) {
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
assertNotNull(source, "Source entity pointer cannot be NULL");
|
|
||||||
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
|
|
||||||
assertTrue(source->type == entity->type, "Source/Entity type mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
void playerEntityUpdate(entity_t *entity) {
|
// testing only
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
float_t move = TIME.delta * 128.0f; // tiles per second
|
||||||
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
|
vec2 dir = {
|
||||||
|
inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT),
|
||||||
// TODO: make this just a method somewhere.
|
inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN)
|
||||||
// if(UI_TEXTBOX.visible) return;
|
};
|
||||||
if(entityIsMoving(entity)) return;
|
glm_vec2_normalize(dir);
|
||||||
|
entity->x += move * dir[0];
|
||||||
// const uint8_t moveSpeed = inputIsDown(INPUT_BIND_CANCEL) ? PLAYER_SPEED_RUN : PLAYER_SPEED_WALK;
|
entity->y -= move * dir[1];
|
||||||
const uint8_t moveSpeed = PLAYER_SPEED_WALK;
|
|
||||||
|
|
||||||
// if(inputIsDown(INPUT_BIND_UP)) {
|
|
||||||
// if(entity->dir != DIRECTION_NORTH) {
|
|
||||||
// entityTurn(entity, DIRECTION_NORTH);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// entityMove(entity, moveSpeed);
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// } else if(inputIsDown(INPUT_BIND_DOWN)) {
|
|
||||||
// if(entity->dir != DIRECTION_SOUTH) {
|
|
||||||
// entityTurn(entity, DIRECTION_SOUTH);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// entityMove(entity, moveSpeed);
|
|
||||||
// return;
|
|
||||||
// } else if(inputIsDown(INPUT_BIND_LEFT)) {
|
|
||||||
// if(entity->dir != DIRECTION_WEST) {
|
|
||||||
// entityTurn(entity, DIRECTION_WEST);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// entityMove(entity, moveSpeed);
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// } else if(inputIsDown(INPUT_BIND_RIGHT)) {
|
|
||||||
// if(entity->dir != DIRECTION_EAST) {
|
|
||||||
// entityTurn(entity, DIRECTION_EAST);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// entityMove(entity, moveSpeed);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Interact
|
|
||||||
// if(inputPressed(INPUT_BIND_ACTION)) {
|
|
||||||
// int8_t x, y;
|
|
||||||
// directionGetCoordinates(entity->dir, &x, &y);
|
|
||||||
// entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
|
|
||||||
|
|
||||||
// // if(ent != NULL && ENTITY_CALLBACKS[ent->type].interact != NULL) {
|
|
||||||
// // assertTrue(ent->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
|
|
||||||
// // ENTITY_CALLBACKS[ent->type].interact(entity, ent);
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
}
|
}
|
@@ -7,37 +7,23 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
#include "rpg/item/inventory.h"
|
|
||||||
|
|
||||||
#define PLAYER_SPEED_WALK 1
|
typedef struct entity_s entity_t;
|
||||||
#define PLAYER_SPEED_RUN 2
|
|
||||||
|
|
||||||
typedef struct _entity_t entity_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t nothing;
|
void *nothing;
|
||||||
} playerentity_t;
|
} player_t;
|
||||||
|
|
||||||
#define PLAYER_ENTITY_ID (UINT32_MAX-1)
|
|
||||||
|
|
||||||
extern inventory_t PLAYER_INVENTORY;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the player and all player-related entities.
|
* Initializes a player entity.
|
||||||
*/
|
|
||||||
void playerInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the player entity.
|
|
||||||
*
|
*
|
||||||
* @param entity The entity to initialize.
|
* @param entity Pointer to the entity structure to initialize.
|
||||||
* @param source The source entity to copy data from.
|
|
||||||
*/
|
*/
|
||||||
void playerEntityLoad(entity_t *entity, const entity_t *source);
|
void playerInit(entity_t *entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the player entity.
|
* Updates a player entity.
|
||||||
*
|
*
|
||||||
* @param entity The entity to update.
|
* @param entity Pointer to the entity structure to update.
|
||||||
*/
|
*/
|
||||||
void playerEntityUpdate(entity_t *entity);
|
void playerUpdate(entity_t *entity);
|
@@ -1,10 +0,0 @@
|
|||||||
# 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
|
|
||||||
inventory.c
|
|
||||||
)
|
|
@@ -1,68 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "inventory.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void inventoryInit(inventory_t *inventory, const uint8_t size) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(size <= INVENTORY_SIZE_MAX, "size exceeding INVENTORY_SIZE_MAX");
|
|
||||||
assertTrue(size > 0, "size must be greater than 0");
|
|
||||||
|
|
||||||
memoryZero(inventory, sizeof(inventory_t));
|
|
||||||
inventory->size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t inventoryItemIndexByType(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
|
|
||||||
|
|
||||||
uint8_t item = inventory->itemCount;
|
|
||||||
while(item--) {
|
|
||||||
if(inventory->items[item].type == type) return item;
|
|
||||||
}
|
|
||||||
return INVENTORY_SIZE_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t inventoryItemCount(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
|
|
||||||
|
|
||||||
const uint8_t index = inventoryItemIndexByType(inventory, type);
|
|
||||||
if(index == INVENTORY_SIZE_MAX) return 0;
|
|
||||||
return inventory->items[index].count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void inventoryItemSet(
|
|
||||||
inventory_t *inventory,
|
|
||||||
const itemtype_t type,
|
|
||||||
const uint8_t count
|
|
||||||
) {
|
|
||||||
assertNotNull(inventory, "inventory must not be NULL");
|
|
||||||
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
|
|
||||||
assertTrue(count > 0, "count must be greater than 0");
|
|
||||||
|
|
||||||
const uint8_t index = inventoryItemIndexByType(inventory, type);
|
|
||||||
|
|
||||||
if(index == INVENTORY_SIZE_MAX) {
|
|
||||||
// Item does not exist, add it
|
|
||||||
assertTrue(inventory->itemCount < inventory->size, "inventory is full");
|
|
||||||
inventory->items[inventory->itemCount].type = type;
|
|
||||||
inventory->items[inventory->itemCount].count = count;
|
|
||||||
inventory->itemCount++;
|
|
||||||
} else {
|
|
||||||
// Item exists, update the count
|
|
||||||
inventory->items[index].count = count;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,63 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "itemstack.h"
|
|
||||||
|
|
||||||
#define INVENTORY_SIZE_MAX UINT8_MAX
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
itemstack_t items[INVENTORY_SIZE_MAX];
|
|
||||||
uint8_t itemCount;
|
|
||||||
uint8_t size;
|
|
||||||
} inventory_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes an inventory with a specified size.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to initialize.
|
|
||||||
* @param size The size of the inventory (maximum is INVENTORY_SIZE_MAX).
|
|
||||||
*/
|
|
||||||
void inventoryInit(inventory_t *inventory, const uint8_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the index of the item of a specified type in the inventory.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to search.
|
|
||||||
* @param type The type of item to find.
|
|
||||||
* @return The index of the item, or INVENTORY_SIZE_MAX if not found.
|
|
||||||
*/
|
|
||||||
uint8_t inventoryItemIndexByType(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the count of items of a specified type in the inventory.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to check.
|
|
||||||
* @param type The type of item to count.
|
|
||||||
* @return The count of items of the specified type.
|
|
||||||
*/
|
|
||||||
uint8_t inventoryItemCount(
|
|
||||||
const inventory_t *inventory,
|
|
||||||
const itemtype_t type
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the count of items of a specified type in the inventory.
|
|
||||||
* If the item does not exist, it will be added.
|
|
||||||
*
|
|
||||||
* @param inventory Pointer to the inventory to modify.
|
|
||||||
* @param type The type of item to set.
|
|
||||||
* @param count The count of items to set.
|
|
||||||
*/
|
|
||||||
void inventoryItemSet(
|
|
||||||
inventory_t *inventory,
|
|
||||||
const itemtype_t type,
|
|
||||||
const uint8_t count
|
|
||||||
);
|
|
@@ -1,14 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "itemtype.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
itemtype_t type;
|
|
||||||
uint8_t count;
|
|
||||||
} itemstack_t;
|
|
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ITEM_TYPE_NULL = 0,
|
|
||||||
|
|
||||||
// MEDICINE
|
|
||||||
ITEM_TYPE_POTION,
|
|
||||||
|
|
||||||
// INGREDIENTS
|
|
||||||
ITEM_TYPE_ONION,
|
|
||||||
ITEM_TYPE_SWEET_POTATO,
|
|
||||||
ITEM_TYPE_CARROT,
|
|
||||||
|
|
||||||
// COOKED FOOD
|
|
||||||
ITEM_TYPE_BAKED_SWEET_POTATO,
|
|
||||||
} itemtype_t;
|
|
@@ -6,8 +6,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rpg.h"
|
#include "rpg.h"
|
||||||
#include "rpg/entity/player.h"
|
|
||||||
|
#include "rpg/world/map.h"
|
||||||
|
|
||||||
|
map_t testMap;
|
||||||
|
|
||||||
void rpgInit() {
|
void rpgInit() {
|
||||||
|
mapInit(&testMap);
|
||||||
|
|
||||||
|
entity_t *ent = mapEntityAdd(&testMap);
|
||||||
|
entityInit(ent, ENTITY_TYPE_PLAYER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpgUpdate() {
|
||||||
|
mapUpdate(&testMap);
|
||||||
}
|
}
|
@@ -10,4 +10,9 @@
|
|||||||
/**
|
/**
|
||||||
* Initializes the RPG subsystem.
|
* Initializes the RPG subsystem.
|
||||||
*/
|
*/
|
||||||
void rpgInit();
|
void rpgInit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the RPG subsystem.
|
||||||
|
*/
|
||||||
|
void rpgUpdate();
|
@@ -1,11 +1,10 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
# Copyright (c) 2025 Dominic Masters
|
||||||
#
|
#
|
||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
# Sources
|
# Sources
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
chunk.c
|
map.c
|
||||||
overworld.c
|
|
||||||
)
|
)
|
@@ -1,321 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "chunk.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "rpg/world/world.h"
|
|
||||||
|
|
||||||
void renderChunkUpdated(chunk_t *chunk);
|
|
||||||
|
|
||||||
chunkmap_t CHUNK_MAP;
|
|
||||||
|
|
||||||
void chunkMapInit() {
|
|
||||||
memoryZero(&CHUNK_MAP, sizeof(chunkmap_t));
|
|
||||||
|
|
||||||
// Load default chunks, YX order.
|
|
||||||
uint16_t i = 0;
|
|
||||||
chunk_t *chunk;
|
|
||||||
for(uint8_t y = 0; y < CHUNK_MAP_HEIGHT; y++) {
|
|
||||||
for(uint8_t x = 0; x < CHUNK_MAP_WIDTH; x++) {
|
|
||||||
assertTrue(i < CHUNK_MAP_COUNT, "Chunk index out of bounds");
|
|
||||||
|
|
||||||
chunk = CHUNK_MAP.chunks + i;
|
|
||||||
CHUNK_MAP.chunkOrder[i] = chunk;
|
|
||||||
|
|
||||||
chunkLoad(chunk, x, y);
|
|
||||||
assertTrue(
|
|
||||||
chunk->x == x && chunk->y == y,
|
|
||||||
"Chunk coordinates do not match expected values"
|
|
||||||
);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkMapShift(const int16_t x, const int16_t y) {
|
|
||||||
if(x == 0 && y == 0) assertUnreachable("ChunkMapShift called with no shift");
|
|
||||||
|
|
||||||
chunk_t *newChunkOrder[CHUNK_MAP_COUNT];
|
|
||||||
chunk_t *unloadedChunks[CHUNK_MAP_COUNT];
|
|
||||||
chunk_t *chunk;
|
|
||||||
uint8_t i, j;
|
|
||||||
uint16_t
|
|
||||||
/** New Map Coordinates */
|
|
||||||
newX, newY,
|
|
||||||
newChunkX, newChunkY
|
|
||||||
;
|
|
||||||
|
|
||||||
// Calculate the new map coordinates
|
|
||||||
newX = CHUNK_MAP.topLeftX + x;
|
|
||||||
newY = CHUNK_MAP.topLeftY + y;
|
|
||||||
|
|
||||||
// Zero the new chunk order
|
|
||||||
memoryZero(newChunkOrder, sizeof(newChunkOrder));
|
|
||||||
|
|
||||||
// For each chunk...
|
|
||||||
j = 0;
|
|
||||||
chunk = CHUNK_MAP.chunks;
|
|
||||||
do {
|
|
||||||
// Is this chunk still going to be within the map bounds?
|
|
||||||
if(
|
|
||||||
chunk->x < newX || chunk->y < newY ||
|
|
||||||
chunk->x >= newX + CHUNK_MAP_WIDTH ||
|
|
||||||
chunk->y >= newY + CHUNK_MAP_HEIGHT
|
|
||||||
) {
|
|
||||||
// No, it's not, let's unload it and make it available for reuse.
|
|
||||||
chunkUnload(chunk);
|
|
||||||
assertTrue(
|
|
||||||
j < CHUNK_MAP_COUNT,
|
|
||||||
"Unloaded chunk index out of bounds"
|
|
||||||
);
|
|
||||||
unloadedChunks[j++] = chunk;
|
|
||||||
chunk++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Yes it is still valid, determine the new index that it will be at
|
|
||||||
i = (chunk->y - newY) * CHUNK_MAP_WIDTH + (chunk->x - newX);
|
|
||||||
assertTrue(
|
|
||||||
i < CHUNK_MAP_COUNT,
|
|
||||||
"Chunk index out of bounds after shifting"
|
|
||||||
);
|
|
||||||
assertNull(
|
|
||||||
newChunkOrder[i],
|
|
||||||
"New chunk order index is already occupied"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set the new chunk order
|
|
||||||
newChunkOrder[i] = chunk;
|
|
||||||
chunk++;
|
|
||||||
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
|
|
||||||
|
|
||||||
// Now check the new chunk order list for missing chunks.
|
|
||||||
i = 0;
|
|
||||||
do {
|
|
||||||
assertTrue(
|
|
||||||
i < CHUNK_MAP_COUNT,
|
|
||||||
"New chunk order index out of bounds after shifting"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Is this chunk loaded still?
|
|
||||||
chunk = newChunkOrder[i];
|
|
||||||
if(chunk != NULL) {
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the new chunk coordinates.
|
|
||||||
newChunkX = i % CHUNK_MAP_WIDTH + newX;
|
|
||||||
newChunkY = i / CHUNK_MAP_WIDTH + newY;
|
|
||||||
assertTrue(
|
|
||||||
newChunkX >= newX && newChunkX < newX + CHUNK_MAP_WIDTH,
|
|
||||||
"New chunk X coordinate out of bounds after shifting"
|
|
||||||
);
|
|
||||||
assertTrue(
|
|
||||||
newChunkY >= newY && newChunkY < newY + CHUNK_MAP_HEIGHT,
|
|
||||||
"New chunk Y coordinate out of bounds after shifting"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Pop a chunk from the unloaded chunks list.
|
|
||||||
assertTrue(j > 0, "No unloaded chunks available to reuse");
|
|
||||||
chunk = unloadedChunks[--j];
|
|
||||||
assertNotNull(chunk, "Unloaded chunk pointer is null");
|
|
||||||
|
|
||||||
// Load the chunk at the new coordinates.
|
|
||||||
chunkLoad(chunk, newChunkX, newChunkY);
|
|
||||||
assertTrue(
|
|
||||||
chunk->x == newChunkX && chunk->y == newChunkY,
|
|
||||||
"Chunk coordinates do not match expected values after shifting"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set it in order.
|
|
||||||
newChunkOrder[i] = chunk;
|
|
||||||
i++;
|
|
||||||
} while(i < CHUNK_MAP_COUNT);
|
|
||||||
|
|
||||||
// Update Absolutes.
|
|
||||||
CHUNK_MAP.topLeftX = newX;
|
|
||||||
CHUNK_MAP.topLeftY = newY;
|
|
||||||
|
|
||||||
// Update the chunk order.
|
|
||||||
memoryCopy(
|
|
||||||
CHUNK_MAP.chunkOrder,
|
|
||||||
newChunkOrder,
|
|
||||||
sizeof(CHUNK_MAP.chunkOrder)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkMapSetPosition(const uint16_t x, const uint16_t y) {
|
|
||||||
if(x == CHUNK_MAP.topLeftX && y == CHUNK_MAP.topLeftY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t shiftX = x - CHUNK_MAP.topLeftX;
|
|
||||||
int16_t shiftY = y - CHUNK_MAP.topLeftY;
|
|
||||||
|
|
||||||
// Are we shifting the entire map?
|
|
||||||
if(
|
|
||||||
shiftX >= CHUNK_MAP_WIDTH || shiftX < -CHUNK_MAP_WIDTH ||
|
|
||||||
shiftY >= CHUNK_MAP_HEIGHT || shiftY < -CHUNK_MAP_HEIGHT
|
|
||||||
) {
|
|
||||||
printf("Shifting chunk map to new position (%u, %u)\n", x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift the chunk map by the specified offsets.
|
|
||||||
chunkMapShift(shiftX, shiftY);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY) {
|
|
||||||
assertTrue(
|
|
||||||
chunkX < WORLD_WIDTH && chunkY < WORLD_HEIGHT,
|
|
||||||
"Chunk coordinates out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
chunk_t *chunk = CHUNK_MAP.chunks;
|
|
||||||
do {
|
|
||||||
if(chunk->x == chunkX && chunk->y == chunkY) return chunk;
|
|
||||||
chunk++;
|
|
||||||
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y) {
|
|
||||||
assertNotNull(chunk, "Chunk pointer is null");
|
|
||||||
|
|
||||||
// Zero out the chunk data.
|
|
||||||
memoryZero(chunk, sizeof(chunk_t));
|
|
||||||
|
|
||||||
// Set the chunk coordinates.
|
|
||||||
chunk->x = x;
|
|
||||||
chunk->y = y;
|
|
||||||
|
|
||||||
// Only load data if the chunk is within bounds.
|
|
||||||
if(x >= WORLD_WIDTH || y >= WORLD_HEIGHT) {
|
|
||||||
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
|
|
||||||
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is chunk data defined?
|
|
||||||
const chunkdata_t *chunkData = WORLD_CHUNKS[y * WORLD_WIDTH + x];
|
|
||||||
if(chunkData == NULL) {
|
|
||||||
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
|
|
||||||
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load tile data into chunk
|
|
||||||
// printf("Loading chunk at (%u, %u)\n", x, y);
|
|
||||||
memoryCopy(
|
|
||||||
chunk->tilesBase,
|
|
||||||
chunkData->layerBase,
|
|
||||||
sizeof(chunk->tilesBase)
|
|
||||||
);
|
|
||||||
memoryCopy(
|
|
||||||
chunk->tilesBaseOverlay,
|
|
||||||
chunkData->layerBaseOverlay,
|
|
||||||
sizeof(chunk->tilesBaseOverlay)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load chunk entities
|
|
||||||
const entity_t *data;
|
|
||||||
entity_t *entity;
|
|
||||||
data = chunkData->entities;
|
|
||||||
while(data < chunkData->entities + CHUNK_ENTITY_COUNT_MAX) {
|
|
||||||
if(data->type == ENTITY_TYPE_NULL) break;
|
|
||||||
|
|
||||||
// Store that this chunk owns this entity ID.
|
|
||||||
chunk->entityIDs[chunk->entityCount++] = data->id;
|
|
||||||
|
|
||||||
// Check entity isn't loaded (still).
|
|
||||||
entity = ENTITIES;
|
|
||||||
do {
|
|
||||||
if(entity->type != ENTITY_TYPE_NULL && entity->id == data->id) break;
|
|
||||||
entity++;
|
|
||||||
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
|
|
||||||
|
|
||||||
if(entity != ENTITIES + ENTITY_COUNT_MAX) {
|
|
||||||
// Entity is already loaded, skip it.
|
|
||||||
printf("Entity ID %u already loaded, skipping...\n", data->id);
|
|
||||||
data++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find an empty entity slot.
|
|
||||||
entity = ENTITIES;
|
|
||||||
while(true) {
|
|
||||||
assertTrue(
|
|
||||||
entity < ENTITIES + ENTITY_COUNT_MAX,
|
|
||||||
"Out of available entities"
|
|
||||||
);
|
|
||||||
|
|
||||||
if(entity->type == ENTITY_TYPE_NULL) break;
|
|
||||||
entity++;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load this entity.
|
|
||||||
// entityLoad(entity, data);
|
|
||||||
data++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the rendering platform to know this chunk is loaded.
|
|
||||||
// renderChunkUpdated(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunkUnload(chunk_t *chunk) {
|
|
||||||
uint8_t i;
|
|
||||||
entity_t *entity;
|
|
||||||
uint32_t id;
|
|
||||||
assertNotNull(chunk, "Chunk pointer is null");
|
|
||||||
|
|
||||||
// Iterate over each entity this chunk owns.
|
|
||||||
i = 0;
|
|
||||||
while(i < chunk->entityCount) {
|
|
||||||
id = chunk->entityIDs[i++];
|
|
||||||
|
|
||||||
// Now, do we need to unload this entity?
|
|
||||||
bool_t shouldUnload = false;
|
|
||||||
|
|
||||||
// Now, find the entity loaded with this ID. It should be impossible for
|
|
||||||
// this entity to be unloaded (but may change in future).
|
|
||||||
entity = ENTITIES;
|
|
||||||
do {
|
|
||||||
if(entity->type != ENTITY_TYPE_NULL && entity->id == id) break;
|
|
||||||
entity++;
|
|
||||||
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
|
|
||||||
|
|
||||||
assertTrue(
|
|
||||||
entity < ENTITIES + ENTITY_COUNT_MAX,
|
|
||||||
"Entity ID not found in ENTITIES array, cannot unload"
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the entity is still within our chunk bounds, it's getting unloaded
|
|
||||||
if(
|
|
||||||
floorf(entity->x) >= chunk->x * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
|
|
||||||
ceilf(entity->x) < (chunk->x + 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
|
|
||||||
floorf(entity->y) >= chunk->y * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT &&
|
|
||||||
ceilf(entity->y) < (chunk->y + 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT
|
|
||||||
) {
|
|
||||||
shouldUnload = true;
|
|
||||||
} else {
|
|
||||||
assertUnreachable(
|
|
||||||
"Entity has left its chunk bounds, we should not be unloading it but "
|
|
||||||
"I have yet to implement that properly. It will need to self-manage "
|
|
||||||
"its own unloading somehow, and also not be in a null chunk "
|
|
||||||
"when it does so."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This entity is still in use, leave it loaded.
|
|
||||||
if(!shouldUnload) continue;
|
|
||||||
|
|
||||||
// NULL the entity type, effectively unloading it.
|
|
||||||
entity->type = ENTITY_TYPE_NULL;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "tile.h"
|
|
||||||
#include "display/display.h"
|
|
||||||
|
|
||||||
#define CHUNK_WIDTH 8
|
|
||||||
#define CHUNK_HEIGHT 8
|
|
||||||
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT)
|
|
||||||
#define CHUNK_ENTITY_COUNT_MAX 8
|
|
||||||
|
|
||||||
#define CHUNK_MAP_WIDTH (((DISPLAY_WIDTH / TILE_WIDTH_HEIGHT)/CHUNK_WIDTH)+2)
|
|
||||||
#define CHUNK_MAP_HEIGHT (((DISPLAY_HEIGHT / TILE_WIDTH_HEIGHT)/CHUNK_HEIGHT)+2)
|
|
||||||
#define CHUNK_MAP_COUNT (CHUNK_MAP_WIDTH * CHUNK_MAP_HEIGHT)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint16_t x, y;
|
|
||||||
tile_t tilesBase[CHUNK_TILE_COUNT];
|
|
||||||
tile_t tilesBaseOverlay[CHUNK_TILE_COUNT];
|
|
||||||
uint32_t entityIDs[CHUNK_ENTITY_COUNT_MAX];
|
|
||||||
uint8_t entityCount;
|
|
||||||
} chunk_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
chunk_t chunks[CHUNK_MAP_COUNT];
|
|
||||||
chunk_t *chunkOrder[CHUNK_MAP_COUNT];
|
|
||||||
|
|
||||||
uint16_t topLeftX;
|
|
||||||
uint16_t topLeftY;
|
|
||||||
} chunkmap_t;
|
|
||||||
|
|
||||||
extern chunkmap_t CHUNK_MAP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the chunk map.
|
|
||||||
*/
|
|
||||||
void chunkMapInit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shifts the chunk map by the specified x and y offsets.
|
|
||||||
*
|
|
||||||
* @param x The x offset to shift the chunk map.
|
|
||||||
* @param y The y offset to shift the chunk map.
|
|
||||||
*/
|
|
||||||
void chunkMapShift(const int16_t x, const int16_t y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the position of the chunk map to the specified coordinates.
|
|
||||||
*
|
|
||||||
* @param x The x coordinate of the top-left chunk.
|
|
||||||
* @param y The y coordinate of the top-left chunk.
|
|
||||||
*/
|
|
||||||
void chunkMapSetPosition(const uint16_t x, const uint16_t y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the chunk at the specified chunk coordinates.
|
|
||||||
*
|
|
||||||
* @param chunkX The x coordinate of the chunk.
|
|
||||||
* @param chunkY The y coordinate of the chunk.
|
|
||||||
* @return A pointer to the chunk at the specified chunk coordinates, or NULL if
|
|
||||||
* no chunk exists at those coordinates.
|
|
||||||
*/
|
|
||||||
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a chunk at the specified coordinates.
|
|
||||||
*
|
|
||||||
* @param chunk The chunk to load.
|
|
||||||
* @param x The x coordinate of the chunk.
|
|
||||||
* @param y The y coordinate of the chunk.
|
|
||||||
*/
|
|
||||||
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unloads a chunk (that is currently loaded).
|
|
||||||
*
|
|
||||||
* @param chunk The chunk to unload.
|
|
||||||
*/
|
|
||||||
void chunkUnload(chunk_t *chunk);
|
|
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "chunk.h"
|
|
||||||
#include "rpg/entity/entity.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t layerBase[CHUNK_TILE_COUNT];
|
|
||||||
uint8_t layerBaseOverlay[CHUNK_TILE_COUNT];
|
|
||||||
entity_t entities[CHUNK_ENTITY_COUNT_MAX];
|
|
||||||
} chunkdata_t;
|
|
33
src/rpg/world/map.c
Normal file
33
src/rpg/world/map.c
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
void mapInit(map_t *map) {
|
||||||
|
assertNotNull(map, "Map cannot be NULL");
|
||||||
|
memoryZero(map, sizeof(map_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapUpdate(map_t *map) {
|
||||||
|
assertNotNull(map, "Map cannot be NULL");
|
||||||
|
|
||||||
|
entity_t *start = &map->entities[0];
|
||||||
|
entity_t *end = &map->entities[map->entityCount];
|
||||||
|
while(start < end) {
|
||||||
|
entityUpdate(start++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity_t * mapEntityAdd(map_t *map) {
|
||||||
|
assertNotNull(map, "Map cannot be NULL");
|
||||||
|
assertTrue(map->entityCount < MAP_ENTITY_COUNT_MAX, "Map entities full");
|
||||||
|
|
||||||
|
entity_t *entity = &map->entities[map->entityCount++];
|
||||||
|
return entity;
|
||||||
|
}
|
40
src/rpg/world/map.h
Normal file
40
src/rpg/world/map.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
#define MAP_ENTITY_COUNT_MAX 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
entity_t entities[MAP_ENTITY_COUNT_MAX];
|
||||||
|
uint8_t entityCount;
|
||||||
|
} map_t;
|
||||||
|
|
||||||
|
extern map_t testMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a map structure.
|
||||||
|
*
|
||||||
|
* @param map Pointer to the map structure to initialize.
|
||||||
|
*/
|
||||||
|
void mapInit(map_t *map);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the map and its entities.
|
||||||
|
*
|
||||||
|
* @param map Pointer to the map structure to update.
|
||||||
|
*/
|
||||||
|
void mapUpdate(map_t *map);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds (but does not initialize) an entity on the map.
|
||||||
|
*
|
||||||
|
* @param map Pointer to the map structure.
|
||||||
|
* @return Pointer to the added entity.
|
||||||
|
*/
|
||||||
|
entity_t * mapEntityAdd(map_t *map);
|
@@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "overworld.h"
|
|
||||||
#include "chunk.h"
|
|
||||||
#include "display/display.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "rpg/entity/entity.h"
|
|
||||||
|
|
||||||
uint32_t OVERWORLD_CAMERA_X;
|
|
||||||
uint32_t OVERWORLD_CAMERA_Y;
|
|
||||||
overworldcameratype_t OVERWORLD_CAMERA_TYPE;
|
|
||||||
|
|
||||||
void overworldInit(void) {
|
|
||||||
playerInit();
|
|
||||||
chunkMapInit();
|
|
||||||
|
|
||||||
OVERWORLD_CAMERA_X = 0;
|
|
||||||
OVERWORLD_CAMERA_Y = 0;
|
|
||||||
OVERWORLD_CAMERA_TYPE = OVERWORLD_CAMERA_TYPE_CENTERED_POSITION;
|
|
||||||
}
|
|
||||||
|
|
||||||
void overworldUpdate() {
|
|
||||||
entity_t *entity;
|
|
||||||
|
|
||||||
assertTrue(
|
|
||||||
OVERWORLD_CAMERA_X < OVERWORLD_CAMERA_LIMIT_X,
|
|
||||||
"Camera position limit (just because I haven't tested properly)"
|
|
||||||
);
|
|
||||||
assertTrue(
|
|
||||||
OVERWORLD_CAMERA_Y < OVERWORLD_CAMERA_LIMIT_Y,
|
|
||||||
"Camera position limit (just because I haven't tested properly)"
|
|
||||||
);
|
|
||||||
|
|
||||||
entity = ENTITIES;
|
|
||||||
do {
|
|
||||||
entityUpdate(entity++);
|
|
||||||
} while(entity->type != ENTITY_TYPE_NULL);
|
|
||||||
|
|
||||||
// Testing, follow player
|
|
||||||
entity = &ENTITIES[0]; // Player entity
|
|
||||||
assertTrue(
|
|
||||||
entity->type == ENTITY_TYPE_PLAYER,
|
|
||||||
"First entity must be player"
|
|
||||||
);
|
|
||||||
OVERWORLD_CAMERA_X = entity->x * TILE_WIDTH_HEIGHT + entity->subX;
|
|
||||||
OVERWORLD_CAMERA_Y = entity->y * TILE_WIDTH_HEIGHT + entity->subY;
|
|
||||||
|
|
||||||
uint16_t x, y;
|
|
||||||
uint16_t halfWidth, halfHeight;
|
|
||||||
halfWidth = ((CHUNK_MAP_WIDTH - 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT) / 2;
|
|
||||||
halfHeight = ((CHUNK_MAP_HEIGHT - 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT) / 2;
|
|
||||||
|
|
||||||
// Calculate the chunk map position based on the camera position.
|
|
||||||
if(OVERWORLD_CAMERA_X < halfWidth) {
|
|
||||||
x = 0;
|
|
||||||
} else {
|
|
||||||
x = (OVERWORLD_CAMERA_X - halfWidth) / (CHUNK_WIDTH*TILE_WIDTH_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(OVERWORLD_CAMERA_Y < halfHeight) {
|
|
||||||
y = 0;
|
|
||||||
} else {
|
|
||||||
y = (OVERWORLD_CAMERA_Y - halfHeight) / (CHUNK_HEIGHT*TILE_WIDTH_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkMapSetPosition(x, y);
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
OVERWORLD_CAMERA_TYPE_CENTERED_POSITION,
|
|
||||||
} overworldcameratype_t;
|
|
||||||
|
|
||||||
extern uint32_t OVERWORLD_CAMERA_X;
|
|
||||||
extern uint32_t OVERWORLD_CAMERA_Y;
|
|
||||||
extern overworldcameratype_t OVERWORLD_CAMERA_TYPE;
|
|
||||||
|
|
||||||
#define OVERWORLD_CAMERA_LIMIT_X (UINT32_MAX / 4)
|
|
||||||
#define OVERWORLD_CAMERA_LIMIT_Y (UINT32_MAX / 4)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the overworld.
|
|
||||||
*/
|
|
||||||
void overworldInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the overworld.
|
|
||||||
*/
|
|
||||||
void overworldUpdate(void);
|
|
@@ -1,26 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
#define TILE_WIDTH_HEIGHT 16
|
|
||||||
|
|
||||||
typedef uint8_t tile_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
TILE_SOLID_NONE = 0,
|
|
||||||
TILE_SOLID_FULL = 1,
|
|
||||||
TILE_SOLID_TRIANGLE_TOP_RIGHT = 2,
|
|
||||||
TILE_SOLID_TRIANGLE_TOP_LEFT = 3,
|
|
||||||
TILE_SOLID_TRIANGLE_BOTTOM_RIGHT = 4,
|
|
||||||
TILE_SOLID_TRIANGLE_BOTTOM_LEFT = 5,
|
|
||||||
} tilesolidtype_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
tilesolidtype_t solidType;
|
|
||||||
} tilemetadata_t;
|
|
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "rpg/world/chunkdata.h"
|
|
||||||
|
|
||||||
#define WORLD_WIDTH 1
|
|
||||||
#define WORLD_HEIGHT 1
|
|
||||||
|
|
||||||
static const chunkdata_t *WORLD_CHUNKS[WORLD_HEIGHT * WORLD_WIDTH] = {
|
|
||||||
NULL
|
|
||||||
};
|
|
Reference in New Issue
Block a user