Overworld render test.

This commit is contained in:
2025-09-11 23:33:24 -05:00
parent b4d94c2cbe
commit 964a9f64f2
32 changed files with 249 additions and 1219 deletions

View File

@@ -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) {

View File

@@ -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.
*/ */

View File

@@ -41,6 +41,8 @@ errorret_t engineUpdate(void) {
inputUpdate(); inputUpdate();
consoleUpdate(); consoleUpdate();
assetManagerUpdate(); assetManagerUpdate();
rpgUpdate();
errorChain(displayUpdate()); errorChain(displayUpdate());
errorOk(); errorOk();

View File

@@ -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");

View File

@@ -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);

View File

@@ -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)

View File

@@ -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
) )

View File

@@ -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;
}
}

View File

@@ -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
);

View File

@@ -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;
}

View File

@@ -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
);

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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);
// // }
// }
} }

View File

@@ -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);

View File

@@ -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
)

View File

@@ -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;
}
}

View File

@@ -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
);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
} }

View File

@@ -10,4 +10,9 @@
/** /**
* Initializes the RPG subsystem. * Initializes the RPG subsystem.
*/ */
void rpgInit(); void rpgInit();
/**
* Updates the RPG subsystem.
*/
void rpgUpdate();

View File

@@ -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
) )

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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
View 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
View 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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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
};