Made a big mess of the codebase
This commit is contained in:
1 parent
30232d1275
commit
af1329710d
44 files changed
+1363
-388
No files matched your search
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -30,7 +30,6 @@ add_subdirectory(assert)
|
|||||||
add_subdirectory(asset)
|
add_subdirectory(asset)
|
||||||
add_subdirectory(console)
|
add_subdirectory(console)
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
add_subdirectory(ecs)
|
|
||||||
add_subdirectory(engine)
|
add_subdirectory(engine)
|
||||||
add_subdirectory(error)
|
add_subdirectory(error)
|
||||||
add_subdirectory(input)
|
add_subdirectory(input)
|
||||||
|
|||||||
+30
-34
@@ -8,67 +8,63 @@
|
|||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "scene/node.h"
|
|
||||||
#include "display/framebuffer/framebuffer.h"
|
#include "display/framebuffer/framebuffer.h"
|
||||||
|
|
||||||
camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
|
camera_t CAMERA_DATA[CAMERA_COUNT_MAX] = { 0 };
|
||||||
ecscomponent_t CAMERA_COMPONENT = ecsComponentInit(
|
camera_t *CAMERA_MAIN = NULL;
|
||||||
CAMERA_DATA,
|
|
||||||
((ecscomponentcallbacks_t){
|
|
||||||
.init = NULL,
|
|
||||||
.entityAdd = cameraEntityAdded
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
ecsid_t CAMERA_MAIN = -1;
|
void cameraInit(camera_t *camera) {
|
||||||
|
assertNotNull(camera, "Not a camera component");
|
||||||
|
|
||||||
void cameraEntityAdded(const ecsid_t id) {
|
camera->type = CAMERA_TYPE_PERSPECTIVE
|
||||||
if(CAMERA_MAIN == -1) CAMERA_MAIN = id;
|
;
|
||||||
|
glm_mat4_identity(camera->transform);
|
||||||
|
camera->perspective.fov = 45.0f;
|
||||||
|
|
||||||
camera_t *cam = cameraGet(id);
|
camera->nearClip = 0.1f;
|
||||||
cam->type = CAMERA_TYPE_PERSPECTIVE;
|
camera->farClip = 100.0f;
|
||||||
cam->perspective.fov = glm_rad(90.0f);
|
glm_look(
|
||||||
cam->nearClip = 0.1f;
|
(vec3){ 3.0f, 3.0f, 3.0f },
|
||||||
cam->farClip = 1000.0f;
|
(vec3){ 0.0f, 0.0f, 0.0f },
|
||||||
|
(vec3){ 0.0f, 1.0f, 0.0f },
|
||||||
|
camera->transform
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cameraPush(const ecsid_t id) {
|
void cameraPush(camera_t *camera) {
|
||||||
assertTrue(cameraHas(id), "Not a camera component");
|
assertNotNull(camera, "Not a camera component");
|
||||||
|
|
||||||
camera_t *cam = cameraGet(id);
|
mat4 projection;
|
||||||
|
|
||||||
mat4 projection, view;
|
switch(camera->type) {
|
||||||
nodeMatrixGet(id, view);
|
|
||||||
|
|
||||||
switch(cam->type) {
|
|
||||||
case CAMERA_TYPE_ORTHOGRAPHIC:
|
case CAMERA_TYPE_ORTHOGRAPHIC:
|
||||||
glm_ortho(
|
glm_ortho(
|
||||||
cam->orthographic.left,
|
camera->orthographic.left,
|
||||||
cam->orthographic.right,
|
camera->orthographic.right,
|
||||||
cam->orthographic.bottom,
|
camera->orthographic.bottom,
|
||||||
cam->orthographic.top,
|
camera->orthographic.top,
|
||||||
cam->nearClip,
|
camera->nearClip,
|
||||||
cam->farClip,
|
camera->farClip,
|
||||||
projection
|
projection
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CAMERA_TYPE_PERSPECTIVE:
|
case CAMERA_TYPE_PERSPECTIVE:
|
||||||
glm_perspective(
|
glm_perspective(
|
||||||
cam->perspective.fov,
|
camera->perspective.fov,
|
||||||
(
|
(
|
||||||
(float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND) /
|
(float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND) /
|
||||||
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
|
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
|
||||||
),
|
),
|
||||||
cam->nearClip,
|
camera->nearClip,
|
||||||
cam->farClip,
|
camera->farClip,
|
||||||
projection
|
projection
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DISPLAY_SDL2
|
#if DISPLAY_SDL2
|
||||||
mat4 pv;
|
mat4 pv;
|
||||||
glm_mat4_mul(projection, view, pv);
|
glm_mat4_mul(projection, camera->transform, pv);
|
||||||
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
|||||||
+11
-16
@@ -6,9 +6,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "ecs/ecscomponent.h"
|
#include "dusk.h"
|
||||||
#include "display/color.h"
|
#include "display/color.h"
|
||||||
|
|
||||||
|
#define CAMERA_COUNT_MAX 4
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CAMERA_TYPE_PERSPECTIVE,
|
CAMERA_TYPE_PERSPECTIVE,
|
||||||
CAMERA_TYPE_ORTHOGRAPHIC
|
CAMERA_TYPE_ORTHOGRAPHIC
|
||||||
@@ -17,6 +19,8 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
cameraprojectiontype_t type;
|
cameraprojectiontype_t type;
|
||||||
|
|
||||||
|
mat4 transform;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
float_t fov;
|
float_t fov;
|
||||||
@@ -34,31 +38,22 @@ typedef struct {
|
|||||||
float_t farClip;
|
float_t farClip;
|
||||||
} camera_t;
|
} camera_t;
|
||||||
|
|
||||||
extern camera_t CAMERA_DATA[ECS_ENTITY_COUNT_MAX];
|
extern camera_t CAMERA_DATA[CAMERA_COUNT_MAX];
|
||||||
extern ecscomponent_t CAMERA_COMPONENT;
|
extern camera_t *CAMERA_MAIN;
|
||||||
extern ecsid_t CAMERA_MAIN;
|
|
||||||
|
|
||||||
#define cameraAdd(id) ((camera_t*)ecsComponentDataAdd(&CAMERA_COMPONENT, id))
|
|
||||||
#define cameraGet(id) ((camera_t*)ecsComponentDataGet(&CAMERA_COMPONENT, id))
|
|
||||||
#define cameraHas(id) ecsComponentDataHas(&CAMERA_COMPONENT, id)
|
|
||||||
#define cameraRemove(id) ecsComponentDataRemove(&CAMERA_COMPONENT, id)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback function called when a new entity is added to the camera component.
|
* Initializes a camera to default values.
|
||||||
* Initializes the camera data for the entity.
|
|
||||||
*
|
|
||||||
* @param id The ID of the newly added entity.
|
|
||||||
*/
|
*/
|
||||||
void cameraEntityAdded(const ecsid_t id);
|
void cameraInit(camera_t *camera);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pushes the camera's view matrix onto the matrix stack.
|
* Pushes the camera's view matrix onto the matrix stack.
|
||||||
*
|
*
|
||||||
* @param id The ID of the camera entity to use.
|
* @param id The ID of the camera entity to use.
|
||||||
*/
|
*/
|
||||||
void cameraPush(const ecsid_t id);
|
void cameraPushMatrix(camera_t* camera);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pops the camera's view matrix off the matrix stack.
|
* Pops the camera's view matrix off the matrix stack.
|
||||||
*/
|
*/
|
||||||
void cameraPop(void);
|
void cameraPopMatrix(void);
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "display/renderer.h"
|
#include "display/renderer.h"
|
||||||
#include "ecs/ecssystem.h"
|
|
||||||
#include "display/framebuffer/framebuffer.h"
|
#include "display/framebuffer/framebuffer.h"
|
||||||
|
|
||||||
#include "display/mesh/quad.h"
|
#include "display/mesh/quad.h"
|
||||||
@@ -84,7 +83,7 @@ errorret_t displayUpdate(void) {
|
|||||||
glViewport(0, 0, windowWidth, windowHeight);
|
glViewport(0, 0, windowWidth, windowHeight);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rendererRender(CAMERA_MAIN);
|
// rendererRender(CAMERA_MAIN);
|
||||||
|
|
||||||
#if DISPLAY_SDL2
|
#if DISPLAY_SDL2
|
||||||
SDL_GL_SwapWindow(DISPLAY.window);
|
SDL_GL_SwapWindow(DISPLAY.window);
|
||||||
|
|||||||
@@ -8,5 +8,4 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
mesh.c
|
mesh.c
|
||||||
quad.c
|
quad.c
|
||||||
meshrenderer.c
|
|
||||||
)
|
)
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "meshrenderer.h"
|
|
||||||
#include "time/time.h"
|
|
||||||
|
|
||||||
meshrenderer_t MESH_RENDERER_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
|
|
||||||
ecscomponent_t MESH_RENDERER_COMPONENT = ecsComponentInit(
|
|
||||||
MESH_RENDERER_DATA,
|
|
||||||
((ecscomponentcallbacks_t){
|
|
||||||
.init = NULL,
|
|
||||||
.entityAdd = NULL,
|
|
||||||
.entityRemove = NULL
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
void meshRendererDraw(const ecsid_t id) {
|
|
||||||
if(!meshRendererHas(id)) return;
|
|
||||||
meshrenderer_t *renderer = &MESH_RENDERER_DATA[id];
|
|
||||||
if(!renderer->mesh) return;
|
|
||||||
|
|
||||||
textureBind(renderer->texture);
|
|
||||||
meshDraw(renderer->mesh, 0, -1);
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "ecs/ecscomponent.h"
|
|
||||||
#include "display/mesh/mesh.h"
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
mesh_t *mesh;
|
|
||||||
texture_t *texture;
|
|
||||||
} meshrenderer_t;
|
|
||||||
|
|
||||||
extern meshrenderer_t MESH_RENDERER_DATA[ECS_ENTITY_COUNT_MAX];
|
|
||||||
extern ecscomponent_t MESH_RENDERER_COMPONENT;
|
|
||||||
|
|
||||||
#define meshRendererAdd(id) \
|
|
||||||
((meshrenderer_t*)ecsComponentDataAdd(&MESH_RENDERER_COMPONENT, id))
|
|
||||||
#define meshRendererGet(id) \
|
|
||||||
((meshrenderer_t*)ecsComponentDataGet(&MESH_RENDERER_COMPONENT, id))
|
|
||||||
#define meshRendererHas(id) \
|
|
||||||
(ecsComponentDataHas(&MESH_RENDERER_COMPONENT, id))
|
|
||||||
#define meshRendererRemove(id) \
|
|
||||||
ecsComponentDataRemove(&MESH_RENDERER_COMPONENT, id)
|
|
||||||
#define meshRendererGetAll(out) \
|
|
||||||
ecsComponentGetAll(&MESH_RENDERER_COMPONENT, out)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draw the mesh for the given entity.
|
|
||||||
*
|
|
||||||
* @param id The ID of the entity with the mesh renderer component.
|
|
||||||
*/
|
|
||||||
void meshRendererDraw(const ecsid_t id);
|
|
||||||
+20
-22
@@ -6,29 +6,27 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "display/mesh/meshrenderer.h"
|
|
||||||
#include "scene/node.h"
|
|
||||||
#include "display/framebuffer/framebuffer.h"
|
#include "display/framebuffer/framebuffer.h"
|
||||||
|
|
||||||
void rendererRender(const ecsid_t camera) {
|
// void rendererRender(const ecsid_t camera) {
|
||||||
if(camera == -1) return;
|
// if(camera == -1) return;
|
||||||
|
|
||||||
// Get the meshes.
|
// // Get the meshes.
|
||||||
uint32_t meshCount;
|
// uint32_t meshCount;
|
||||||
ecsid_t meshes[ECS_ENTITY_COUNT_MAX];
|
// ecsid_t meshes[ECS_ENTITY_COUNT_MAX];
|
||||||
ecsid_t id;
|
// ecsid_t id;
|
||||||
meshCount = meshRendererGetAll(meshes);
|
// meshCount = meshRendererGetAll(meshes);
|
||||||
|
|
||||||
frameBufferBind(NULL);
|
// frameBufferBind(NULL);
|
||||||
frameBufferClear(
|
// frameBufferClear(
|
||||||
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
|
// FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
|
||||||
COLOR_CORNFLOWER_BLUE
|
// COLOR_CORNFLOWER_BLUE
|
||||||
);
|
// );
|
||||||
cameraPush(camera);
|
// cameraPush(camera);
|
||||||
for(uint32_t i = 0; i < meshCount; i++) {
|
// for(uint32_t i = 0; i < meshCount; i++) {
|
||||||
id = meshes[i];
|
// id = meshes[i];
|
||||||
nodeMatrixPush(id);
|
// nodeMatrixPush(id);
|
||||||
meshRendererDraw(id);
|
// meshRendererDraw(id);
|
||||||
}
|
// }
|
||||||
cameraPop();
|
// cameraPop();
|
||||||
}
|
// }
|
||||||
@@ -13,4 +13,4 @@
|
|||||||
*
|
*
|
||||||
* @param camera The ID of the camera entity to render from.
|
* @param camera The ID of the camera entity to render from.
|
||||||
*/
|
*/
|
||||||
void rendererRender(const ecsid_t camera);
|
// void rendererRender(const ecsid_t camera);
|
||||||
@@ -10,8 +10,6 @@
|
|||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "ecs/ecssystem.h"
|
|
||||||
#include "scene/node.h"
|
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
|
|
||||||
#include "scene/test/scenetest.h"
|
#include "scene/test/scenetest.h"
|
||||||
@@ -31,7 +29,6 @@ errorret_t engineInit(void) {
|
|||||||
// Init systems. Order is important.
|
// Init systems. Order is important.
|
||||||
timeInit();
|
timeInit();
|
||||||
consoleInit();
|
consoleInit();
|
||||||
ecsSystemInit();
|
|
||||||
errorChain(assetInit());
|
errorChain(assetInit());
|
||||||
errorChain(displayInit());
|
errorChain(displayInit());
|
||||||
|
|
||||||
@@ -52,7 +49,6 @@ errorret_t engineUpdate(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
ecsSystemDispose();
|
|
||||||
errorChain(displayDispose());
|
errorChain(displayDispose());
|
||||||
assetDispose();
|
assetDispose();
|
||||||
consoleDispose();
|
consoleDispose();
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# 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
|
||||||
|
)
|
||||||
|
|
||||||
|
# Subdirs
|
||||||
|
add_subdirectory(entity)
|
||||||
|
add_subdirectory(item)
|
||||||
|
add_subdirectory(world)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# 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
|
||||||
|
direction.c
|
||||||
|
entity.c
|
||||||
|
player.c
|
||||||
|
npc.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
);
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* 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"
|
||||||
|
#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}, // 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");
|
||||||
|
memoryZero(entity, sizeof(entity_t));
|
||||||
|
|
||||||
|
// entity->type = ENTITY_TYPE_NULL;
|
||||||
|
// 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) {
|
||||||
|
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");
|
||||||
|
// assertNotNull(
|
||||||
|
// ENTITY_CALLBACKS[entity->type].update,
|
||||||
|
// "Entity type has no update callback"
|
||||||
|
// );
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "direction.h"
|
||||||
|
#include "player.h"
|
||||||
|
#include "npc.h"
|
||||||
|
|
||||||
|
#define ENTITY_COUNT_MAX 32
|
||||||
|
#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_PLAYER_INDEX 0
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ENTITY_TYPE_NULL = 0,
|
||||||
|
ENTITY_TYPE_PLAYER = 1,
|
||||||
|
ENTITY_TYPE_NPC = 2,
|
||||||
|
} 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;
|
||||||
|
|
||||||
|
entitytype_t type;
|
||||||
|
direction_t dir;
|
||||||
|
|
||||||
|
union {
|
||||||
|
npc_t npc;
|
||||||
|
playerentity_t player;
|
||||||
|
};
|
||||||
|
} entity_t;
|
||||||
|
|
||||||
|
// typedef struct {
|
||||||
|
// 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.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to initialize.
|
||||||
|
* @param source Pointer to the source entity data.
|
||||||
|
*/
|
||||||
|
void entityInit(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the entity's state.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
);
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
#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);
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* 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"
|
||||||
|
#include "input/input.h"
|
||||||
|
// #include "display/render.h"
|
||||||
|
// #include "world/world.h"
|
||||||
|
|
||||||
|
// #include "ui/uitextbox.h"
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
|
||||||
|
|
||||||
|
// TODO: make this just a method somewhere.
|
||||||
|
// if(UI_TEXTBOX.visible) return;
|
||||||
|
if(entityIsMoving(entity)) return;
|
||||||
|
|
||||||
|
const uint8_t moveSpeed = inputIsDown(INPUT_BIND_CANCEL) ? PLAYER_SPEED_RUN : 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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "item/inventory.h"
|
||||||
|
|
||||||
|
#define PLAYER_SPEED_WALK 1
|
||||||
|
#define PLAYER_SPEED_RUN 2
|
||||||
|
|
||||||
|
typedef struct _entity_t entity_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t nothing;
|
||||||
|
} playerentity_t;
|
||||||
|
|
||||||
|
#define PLAYER_ENTITY_ID (UINT32_MAX-1)
|
||||||
|
|
||||||
|
extern inventory_t PLAYER_INVENTORY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the player and all player-related entities.
|
||||||
|
*/
|
||||||
|
void playerInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the player entity.
|
||||||
|
*
|
||||||
|
* @param entity The entity to initialize.
|
||||||
|
* @param source The source entity to copy data from.
|
||||||
|
*/
|
||||||
|
void playerEntityLoad(entity_t *entity, const entity_t *source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the player entity.
|
||||||
|
*
|
||||||
|
* @param entity The entity to update.
|
||||||
|
*/
|
||||||
|
void playerEntityUpdate(entity_t *entity);
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# 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
|
||||||
|
)
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# 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
|
||||||
|
chunk.c
|
||||||
|
overworld.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
/**
|
||||||
|
* 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 "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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* 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/render.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 (((RENDER_WIDTH / TILE_WIDTH_HEIGHT)/CHUNK_WIDTH)+2)
|
||||||
|
#define CHUNK_MAP_HEIGHT (((RENDER_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);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "chunk.h"
|
||||||
|
#include "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;
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* 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/render.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
# Sources
|
# Sources
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
node.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
// Copyright (c) 2025 Dominic Masters
|
|
||||||
//
|
|
||||||
// This software is released under the MIT License.
|
|
||||||
// https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
#include "node.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/display.h"
|
|
||||||
|
|
||||||
node_t NODE_DATA[ECS_ENTITY_COUNT_MAX] = { 0 };
|
|
||||||
ecscomponent_t NODE_COMPONENT = ecsComponentInit(
|
|
||||||
NODE_DATA,
|
|
||||||
((ecscomponentcallbacks_t){
|
|
||||||
.init = nodeInit,
|
|
||||||
.entityAdd = nodeEntityAdded,
|
|
||||||
.entityRemove = nodeEntityRemoved
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
void nodeInit(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodeEntityAdded(const ecsid_t id) {
|
|
||||||
glm_mat4_identity(NODE_DATA[id].transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodeEntityRemoved(const ecsid_t id) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodeMatrixGet(const ecsid_t id, mat4 dest) {
|
|
||||||
node_t *node;
|
|
||||||
|
|
||||||
if(nodeHas(id)) {
|
|
||||||
node = &NODE_DATA[id];
|
|
||||||
} else {
|
|
||||||
node = nodeAdd(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm_mat4_copy(node->transform, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodeMatrixSet(const ecsid_t id, mat4 in) {
|
|
||||||
node_t *node;
|
|
||||||
|
|
||||||
if(nodeHas(id)) {
|
|
||||||
node = &NODE_DATA[id];
|
|
||||||
} else {
|
|
||||||
node = nodeAdd(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm_mat4_copy(in, node->transform);
|
|
||||||
|
|
||||||
// Extract position, scale, rotation from the matrix.
|
|
||||||
node->position[0] = in[3][0];
|
|
||||||
node->position[1] = in[3][1];
|
|
||||||
node->position[2] = in[3][2];
|
|
||||||
|
|
||||||
node->scale[0] = glm_vec3_norm((vec3){ in[0][0], in[0][1], in[0][2] });
|
|
||||||
node->scale[1] = glm_vec3_norm((vec3){ in[1][0], in[1][1], in[1][2] });
|
|
||||||
node->scale[2] = glm_vec3_norm((vec3){ in[2][0], in[2][1], in[2][2] });
|
|
||||||
|
|
||||||
// Remove scale from the matrix to extract rotation.
|
|
||||||
if(node->scale[0] != 0.0f) {
|
|
||||||
in[0][0] /= node->scale[0];
|
|
||||||
in[0][1] /= node->scale[0];
|
|
||||||
in[0][2] /= node->scale[0];
|
|
||||||
glm_vec3_copy(in[0], node->rotation);
|
|
||||||
glm_vec3_copy(in[1], node->rotation);
|
|
||||||
glm_vec3_copy(in[2], node->rotation);
|
|
||||||
node->rotation[1] = asinf(-in[0][2]);
|
|
||||||
if (cosf(node->rotation[1]) != 0.0f) {
|
|
||||||
node->rotation[0] = atan2f(in[1][2], in[2][2]);
|
|
||||||
node->rotation[2] = atan2f(in[0][1], in[0][0]);
|
|
||||||
} else {
|
|
||||||
node->rotation[0] = 0.0f;
|
|
||||||
node->rotation[2] = atan2f(-in[1][0], in[1][1]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
node->rotation[0] = 0.0f;
|
|
||||||
node->rotation[1] = 0.0f;
|
|
||||||
node->rotation[2] = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodePositionGet(const ecsid_t id, vec3 out) {
|
|
||||||
node_t *node;
|
|
||||||
|
|
||||||
if(nodeHas(id)) {
|
|
||||||
node = &NODE_DATA[id];
|
|
||||||
} else {
|
|
||||||
node = nodeAdd(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm_vec3_copy(node->position, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodeMatrixUpdate(const ecsid_t id) {
|
|
||||||
node_t *node;
|
|
||||||
|
|
||||||
if(nodeHas(id)) {
|
|
||||||
node = &NODE_DATA[id];
|
|
||||||
} else {
|
|
||||||
node = nodeAdd(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
glm_mat4_identity(node->transform);
|
|
||||||
mat4 rot;
|
|
||||||
glm_euler(node->rotation, rot);
|
|
||||||
glm_mat4_mul(node->transform, rot, node->transform);
|
|
||||||
// glm_scale(node->transform, node->scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodeMatrixPush(const ecsid_t id) {
|
|
||||||
assertTrue(nodeHas(id), "Not a node component");
|
|
||||||
node_t *node = nodeGet(id);
|
|
||||||
|
|
||||||
#if DISPLAY_SDL2
|
|
||||||
glPushMatrix();
|
|
||||||
glMultMatrixf((const GLfloat*)node->transform);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void nodeMatrixPop(void) {
|
|
||||||
#if DISPLAY_SDL2
|
|
||||||
glPopMatrix();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "ecs/ecscomponent.h"
|
|
||||||
|
|
||||||
#define SCENE_ITEM_CHILD_MAX 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
mat4 transform;
|
|
||||||
|
|
||||||
vec3 position;
|
|
||||||
vec3 scale;
|
|
||||||
vec3 rotation; // Euler angles in radians
|
|
||||||
} node_t;
|
|
||||||
|
|
||||||
extern node_t NODE_DATA[ECS_ENTITY_COUNT_MAX];
|
|
||||||
extern ecscomponent_t NODE_COMPONENT;
|
|
||||||
|
|
||||||
#define nodeHas(id) ecsComponentDataHas(&NODE_COMPONENT, id)
|
|
||||||
#define nodeGet(id) \
|
|
||||||
((node_t*)ecsComponentDataGet(&NODE_COMPONENT, id))
|
|
||||||
#define nodeAdd(id) \
|
|
||||||
((node_t*)ecsComponentDataAdd(&NODE_COMPONENT, id))
|
|
||||||
#define nodeRemove(id) ecsComponentDataRemove(&NODE_COMPONENT, id)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the node component.
|
|
||||||
*/
|
|
||||||
void nodeInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for when an entity is added to the ECS.
|
|
||||||
*
|
|
||||||
* @param id The ID of the entity being added.
|
|
||||||
*/
|
|
||||||
void nodeEntityAdded(const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for when an entity is removed from the ECS.
|
|
||||||
*
|
|
||||||
* @param id The ID of the entity being removed.
|
|
||||||
*/
|
|
||||||
void nodeEntityRemoved(const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the local transformation matrix of a node.
|
|
||||||
*
|
|
||||||
* @param id The ID of the node.
|
|
||||||
* @param out Pointer to a mat4 where the local matrix will be stored.
|
|
||||||
*/
|
|
||||||
void nodeMatrixGet(const ecsid_t id, mat4 out);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the local transformation matrix of a node.
|
|
||||||
*
|
|
||||||
* @param id The ID of the node.
|
|
||||||
* @param in Pointer to a mat4 containing the new local matrix.
|
|
||||||
*/
|
|
||||||
void nodeMatrixSet(const ecsid_t id, mat4 in);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the local position of a node.
|
|
||||||
*
|
|
||||||
* @param id The ID of the node.
|
|
||||||
*/
|
|
||||||
void nodeMatrixUpdate(const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push the node's transformation matrix onto the OpenGL matrix stack.
|
|
||||||
*
|
|
||||||
* @param id The ID of the node.
|
|
||||||
*/
|
|
||||||
void nodeMatrixPush(const ecsid_t id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pop the last transformation matrix from the OpenGL matrix stack.
|
|
||||||
*/
|
|
||||||
void nodeMatrixPop(void);
|
|
||||||
@@ -6,38 +6,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "scenetest.h"
|
#include "scenetest.h"
|
||||||
#include "scene/node.h"
|
|
||||||
#include "display/camera.h"
|
#include "display/camera.h"
|
||||||
#include "display/mesh/meshrenderer.h"
|
|
||||||
#include "display/mesh/quad.h"
|
#include "display/mesh/quad.h"
|
||||||
|
|
||||||
texture_t test;
|
texture_t test;
|
||||||
|
|
||||||
void sceneTestAdd(void) {
|
void sceneTestAdd(void) {
|
||||||
// Initialize the entity with a camera component
|
|
||||||
ecsid_t camera = ecsEntityAdd();
|
|
||||||
node_t *node = nodeAdd(camera);
|
|
||||||
camera_t *camData = cameraAdd(camera);
|
|
||||||
|
|
||||||
mat4 lookAt;
|
|
||||||
glm_lookat(
|
|
||||||
(vec3){ 3.0f, 3.0f, 3.0f },
|
|
||||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
||||||
lookAt
|
|
||||||
);
|
|
||||||
nodeMatrixSet(camera, lookAt);
|
|
||||||
|
|
||||||
// color4b_t pixels[4] = {
|
|
||||||
// COLOR_RED, COLOR_GREEN,
|
|
||||||
// COLOR_BLUE, COLOR_WHITE
|
|
||||||
// };
|
|
||||||
// textureInit(&test, 2, 2, TEXTURE_FORMAT_RGBA, pixels);
|
|
||||||
|
|
||||||
// Test cube
|
|
||||||
ecsid_t cube = ecsEntityAdd();
|
|
||||||
node = nodeAdd(cube);
|
|
||||||
meshrenderer_t *renderer = meshRendererAdd(cube);
|
|
||||||
renderer->mesh = &QUAD_MESH_SIMPLE;
|
|
||||||
renderer->texture = &test;
|
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "ecs/ecssystem.h"
|
|
||||||
#include "display/texture/texture.h"
|
#include "display/texture/texture.h"
|
||||||
|
|
||||||
extern texture_t test;
|
extern texture_t test;
|
||||||
|
|||||||
Reference in new issue
Block a user