From c4c43b23add69ec276b641ffddf343290a2c07aa Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Thu, 9 Oct 2025 15:07:07 -0500 Subject: [PATCH] prog --- src/rpg/CMakeLists.txt | 1 + src/rpg/entity/entity.c | 3 ++- src/rpg/entity/entity.h | 5 +++-- src/rpg/entity/player.c | 8 +++++--- src/rpg/entity/player.h | 2 +- src/rpg/rpg.c | 24 ++++++++++++++++++------ src/rpg/rpgcamera.c | 34 ++++++++++++++++++++++++++++++++++ src/rpg/rpgcamera.h | 37 +++++++++++++++++++++++++++++++++++++ src/rpg/world/worldunit.c | 30 ++++++++++++++++++++++++++---- src/rpg/world/worldunit.h | 30 ++++++++++++++++++++++-------- src/scene/scene/scenemap.c | 20 +++++++++++++------- 11 files changed, 162 insertions(+), 32 deletions(-) create mode 100644 src/rpg/rpgcamera.c create mode 100644 src/rpg/rpgcamera.h diff --git a/src/rpg/CMakeLists.txt b/src/rpg/CMakeLists.txt index 4178186..652b03e 100644 --- a/src/rpg/CMakeLists.txt +++ b/src/rpg/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE rpg.c + rpgcamera.c ) # Subdirs diff --git a/src/rpg/entity/entity.c b/src/rpg/entity/entity.c index 87acd56..0c97934 100644 --- a/src/rpg/entity/entity.c +++ b/src/rpg/entity/entity.c @@ -24,6 +24,7 @@ void entityInit(entity_t *entity, const entitytype_t type) { ); memoryZero(entity, sizeof(entity_t)); + entity->id = (uint8_t)(entity - ENTITIES); entity->type = type; // Init. I did use a callback struct but it was not flexible enough. @@ -54,7 +55,7 @@ void entityUpdate(entity_t *entity) { for(uint8_t i = 0; i < WORLD_DIMENSIONS; i++) { if(entity->velocity[i] == 0) continue; - worldChunkPosAdd(&entity->position[i], entity->velocity[i]); + worldPosAddSubtile(&entity->position[i], entity->velocity[i]); // Friction worldsubtile_t v = entity->velocity[i]; diff --git a/src/rpg/entity/entity.h b/src/rpg/entity/entity.h index 80a7cfe..0d7ed36 100644 --- a/src/rpg/entity/entity.h +++ b/src/rpg/entity/entity.h @@ -12,7 +12,7 @@ #include "physics/physics.h" #include "rpg/world/worldunit.h" -#define ENTITY_FRICTION 1 +#define ENTITY_FRICTION 2 #define ENTITY_MIN_VELOCITY 1 #define ENTITY_COUNT 256 @@ -27,9 +27,10 @@ typedef enum { } entitytype_t; typedef struct entity_s { + uint8_t id; entitytype_t type; direction_t direction; - worldchunkpos_t position[WORLD_DIMENSIONS]; + worldpos_t position[WORLD_DIMENSIONS]; worldsubtile_t velocity[WORLD_DIMENSIONS]; union { diff --git a/src/rpg/entity/player.c b/src/rpg/entity/player.c index bfd0a1f..ace8c8a 100644 --- a/src/rpg/entity/player.c +++ b/src/rpg/entity/player.c @@ -9,6 +9,8 @@ #include "assert/assert.h" #include "input/input.h" #include "display/tileset/tileset_entities.h" +#include "rpg/rpgcamera.h" +#include "util/memory.h" void playerInit(entity_t *entity) { assertNotNull(entity, "Entity pointer cannot be NULL"); @@ -20,13 +22,13 @@ void playerMovement(entity_t *entity) { // Get movement angle as 0-> normalized vector. vec2 dir = { inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT), - inputAxis(INPUT_ACTION_DOWN, INPUT_ACTION_UP) + inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN) }; if(dir[0] == 0 && dir[1] == 0) return; glm_vec2_normalize(dir); - entity->velocity[0] += (worldsubtile_t)(PLAYER_SPEED * dir[0]); - entity->velocity[1] += (worldsubtile_t)(PLAYER_SPEED * dir[1]); + entity->velocity[0] += (worldsubtile_t)((float_t)PLAYER_SPEED * dir[0]); + entity->velocity[1] += (worldsubtile_t)((float_t)PLAYER_SPEED * dir[1]); // Update direction. if(dir[0] > 0) { diff --git a/src/rpg/entity/player.h b/src/rpg/entity/player.h index ff4dcf9..41bd590 100644 --- a/src/rpg/entity/player.h +++ b/src/rpg/entity/player.h @@ -8,7 +8,7 @@ #pragma once #include "dusk.h" -#define PLAYER_SPEED 3 +#define PLAYER_SPEED 4 #define PLAYER_INTERACTION_RANGE 1.0f #define PLAYER_INTERACTION_SIZE 0.5f diff --git a/src/rpg/rpg.c b/src/rpg/rpg.c index 4770329..8f8b440 100644 --- a/src/rpg/rpg.c +++ b/src/rpg/rpg.c @@ -8,14 +8,24 @@ #include "rpg.h" #include "entity/entity.h" #include "time/time.h" +#include "rpgcamera.h" +#include "util/memory.h" errorret_t rpgInit(void) { - // TEST - entityInit(&ENTITIES[0], ENTITY_TYPE_PLAYER); - // ENTITIES[0].position[0].tile = 2; - // ENTITIES[0].position[0].subtile = 8; - // ENTITIES[0].position[1].tile = 3; - // ENTITIES[0].position[1].subtile = 12; + memoryZero(ENTITIES, sizeof(ENTITIES)); + + rpgCameraInit(); + + entity_t *ent; + ent = &ENTITIES[0]; + entityInit(ent, ENTITY_TYPE_PLAYER); + RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY; + RPG_CAMERA.followEntity.followEntityId = ent->id; + ent->position[0].tile = 32, ent->position[1].tile = 32; + + ent = &ENTITIES[1]; + entityInit(ent, ENTITY_TYPE_NPC); + ent->position[0].tile = 40, ent->position[1].tile = 32; errorOk(); } @@ -28,6 +38,8 @@ void rpgUpdate(void) { if(ent->type == ENTITY_TYPE_NULL) continue; entityUpdate(ent); } while(++ent < &ENTITIES[ENTITY_COUNT]); + + rpgCameraUpdate(); } void rpgDispose(void) { diff --git a/src/rpg/rpgcamera.c b/src/rpg/rpgcamera.c new file mode 100644 index 0000000..b10c627 --- /dev/null +++ b/src/rpg/rpgcamera.c @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rpgcamera.h" +#include "util/memory.h" +#include "rpg/entity/entity.h" + +rpgcamera_t RPG_CAMERA = { + .position = { 0 } +}; + +void rpgCameraInit(void) { + memoryZero(&RPG_CAMERA, sizeof(rpgcamera_t)); +} + +void rpgCameraUpdate(void) { + switch(RPG_CAMERA.mode) { + case RPG_CAMERA_MODE_FOLLOW_ENTITY: + if(RPG_CAMERA.followEntity.followEntityId >= ENTITY_COUNT) break; + entity_t *ent = &ENTITIES[RPG_CAMERA.followEntity.followEntityId]; + if(ent->type == ENTITY_TYPE_NULL) break; + memoryCopy( + &RPG_CAMERA.position, ent->position, sizeof(RPG_CAMERA.position) + ); + break; + + default: + break; + } +} \ No newline at end of file diff --git a/src/rpg/rpgcamera.h b/src/rpg/rpgcamera.h new file mode 100644 index 0000000..c76ba3b --- /dev/null +++ b/src/rpg/rpgcamera.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "world/worldunit.h" + +typedef enum { + RPG_CAMERA_MODE_FREE, + RPG_CAMERA_MODE_FOLLOW_ENTITY, +} rpgcameramode_t; + +typedef struct { + worldpos_t position[WORLD_DIMENSIONS]; + rpgcameramode_t mode; + + union { + struct { + uint8_t followEntityId; + } followEntity; + }; +} rpgcamera_t; + +extern rpgcamera_t RPG_CAMERA; + +/** + * Initializes the RPG camera. + */ +void rpgCameraInit(void); + +/** + * Updates the RPG camera. + */ +void rpgCameraUpdate(void); \ No newline at end of file diff --git a/src/rpg/world/worldunit.c b/src/rpg/world/worldunit.c index d69e2e6..6869366 100644 --- a/src/rpg/world/worldunit.c +++ b/src/rpg/world/worldunit.c @@ -8,7 +8,7 @@ #include "worldunit.h" #include "assert/assert.h" -void worldChunkPosAdd(worldchunkpos_t *pos, worldsubtile_t amt) { +void worldChunkPosAdd(worldchunkpos_t *pos, const worldsubtile_t amt) { assertNotNull(pos, "Position pointer cannot be NULL"); /* @@ -29,12 +29,34 @@ void worldChunkPosAdd(worldchunkpos_t *pos, worldsubtile_t amt) { pos->tile = (uint8_t)(pos->tile + (uint8_t)tileCarry); } +void worldPosAddSubtile(worldpos_t *pos, const worldsubtile_t amt) { + assertNotNull(pos, "Position pointer cannot be NULL"); + // Same as worldChunkPosAdd but with chunk handling. + int32_t shiftedTotal = (int32_t)pos->subtile + (int32_t)amt + 128; + int32_t tileCarry = shiftedTotal >> 8; // divide by 256 + int32_t wrappedSubtile = shiftedTotal - (tileCarry << 8); + pos->subtile = (int8_t)(wrappedSubtile - 128); + int32_t newTile = (int32_t)pos->tile + (int32_t)tileCarry; + int32_t chunkCarry = newTile / WORLD_CHUNK_SIZE; + pos->tile = (uint8_t)(newTile % WORLD_CHUNK_SIZE); + pos->chunk = (uint8_t)(pos->chunk + (uint8_t)chunkCarry); +} -float_t worldChunkPosToF32(worldchunkpos_t pos, const uint8_t tileSize) { - const float scaleFactor = (float)tileSize * (1.0f / 256.0f); +float_t worldChunkPosToF32(const worldchunkpos_t pos, const uint8_t tileSize) { + const float_t scaleFactor = (float_t)tileSize * (1.0f / 256.0f); return ( - (float)pos.tile * (float)tileSize + ((float)pos.subtile + 128.0f) * + (float_t)pos.tile * (float_t)tileSize + ((float_t)pos.subtile + 128.0f) * scaleFactor ); +} + +float_t worldPosToF32(const worldpos_t pos, const uint8_t tileSize) { + const float_t scaleFactor = (float_t)tileSize * (1.0f / 256.0f); + const float_t chunkFactor = WORLD_CHUNK_SIZE * (float_t)tileSize; + return ( + (float_t)pos.chunk * chunkFactor + + (float_t)pos.tile * (float_t)tileSize + + ((float_t)pos.subtile + 128.0f) * scaleFactor + ); } \ No newline at end of file diff --git a/src/rpg/world/worldunit.h b/src/rpg/world/worldunit.h index edb63e6..8c91a6e 100644 --- a/src/rpg/world/worldunit.h +++ b/src/rpg/world/worldunit.h @@ -11,6 +11,7 @@ #define WORLD_DIMENSIONS 3 #define WORLD_SUBTITLE_MIN -128 #define WORLD_SUBTITLE_MAX 127 +#define WORLD_CHUNK_SIZE 256 // Tiles per axis per chunk. /** * Position in SUBTILE space in a world, each unit represents a single subtile. @@ -67,10 +68,16 @@ typedef struct worldpos_s { * @param pos Pointer to the world chunk position to modify. * @param amt The amount of subtiles to add (can be negative). */ -void worldChunkPosAdd( - worldchunkpos_t *pos, - const worldsubtile_t amt -); +void worldChunkPosAdd(worldchunkpos_t *pos, const worldsubtile_t amt); + +/** + * Adds a number of subtiles to a world position, rolling over into tiles and + * chunks as necessary. + * + * @param pos Pointer to the world position to modify. + * @param amt The amount of subtiles to add (can be negative). + */ +void worldPosAddSubtile(worldpos_t *pos, const worldsubtile_t amt); /** * Converts a world chunk position to a floating point number, given the tile @@ -80,7 +87,14 @@ void worldChunkPosAdd( * @param tileSize The size of a tile in pixels. * @return The position as a floating point number. */ -float_t worldChunkPosToF32( - const worldchunkpos_t pos, - const uint8_t tileSize -); \ No newline at end of file +float_t worldChunkPosToF32(const worldchunkpos_t pos, const uint8_t tileSize); + +/** + * Converts a world position to a floating point number, given the tile size + * in pixels. + * + * @param pos Pointer to the world position to convert. + * @param tileSize The size of a tile in pixels. + * @return The position as a floating point number. + */ +float_t worldPosToF32(const worldpos_t pos, const uint8_t tileSize); \ No newline at end of file diff --git a/src/scene/scene/scenemap.c b/src/scene/scene/scenemap.c index 9bd0436..f256f1a 100644 --- a/src/scene/scene/scenemap.c +++ b/src/scene/scene/scenemap.c @@ -11,9 +11,9 @@ #include "assert/assert.h" #include "rpg/entity/entity.h" #include "display/screen.h" +#include "rpg/rpgcamera.h" -#define TILE_WIDTH 8 -#define TILE_HEIGHT TILE_WIDTH +#define TILE_SIZE 8 errorret_t sceneMapInit(scenedata_t *data) { cameraInitPerspective(&data->sceneMap.camera); @@ -31,6 +31,12 @@ void sceneMapRender(scenedata_t *data) { data->sceneMap.camera.perspective.fov / 2.0f ) * ((float_t)SCREEN.height / 2.0f); + for(uint8_t i = 0; i < WORLD_DIMENSIONS; i++) { + data->sceneMap.camera.lookat.target[i] = worldPosToF32( + RPG_CAMERA.position[i], TILE_SIZE + ); + } + glm_vec3_copy((vec3){ data->sceneMap.camera.lookat.target[0], data->sceneMap.camera.lookat.target[1] + camOffset, @@ -55,15 +61,15 @@ void sceneMapRenderEntity(const entity_t *entity) { if(entity->type == ENTITY_TYPE_NULL) return; - float_t x = worldChunkPosToF32(entity->position[0], TILE_WIDTH); - float_t y = worldChunkPosToF32(entity->position[1], TILE_HEIGHT); - x -= TILE_WIDTH * 0.5f; - y -= TILE_HEIGHT * 0.5f; + float_t x = worldPosToF32(entity->position[0], TILE_SIZE); + float_t y = worldPosToF32(entity->position[1], TILE_SIZE); + x -= TILE_SIZE * 0.5f; + y -= TILE_SIZE * 0.5f; spriteBatchPush( NULL, x, y, - x + TILE_WIDTH, y + TILE_HEIGHT, + x + TILE_SIZE, y + TILE_SIZE, COLOR_RED, 0.0f, 0.0f, 1.0f, 1.0f