From 358fa9a4930076c651e88f165a47430caaa2769b Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 28 Oct 2025 07:52:05 -0500 Subject: [PATCH] Moved the platform stuff around --- src/CMakeLists.txt | 4 +- src/cutscene/cutscene.h | 27 +++++++++++ src/cutscene/testcutscene.h | 13 +++++ src/entity/CMakeLists.txt | 2 + src/entity/direction.c | 32 +++++++++++++ src/entity/direction.h | 11 ++++- src/entity/entity.c | 66 +++++++++++++++++++++----- src/entity/entity.h | 16 ++++++- src/entity/entitytype.h | 1 + src/entity/player.c | 66 ++++++++++++++++++++++---- src/entity/sign.c | 13 +++++ src/entity/sign.h | 22 +++++++++ src/game.c | 5 ++ src/game.h | 2 + src/gametime.c | 38 +++++++++++++++ src/gametime.h | 36 ++++++++++++++ src/main.c | 22 ++------- src/platform/CMakeLists.txt | 7 +++ src/platform/platform.h | 29 +++++++++++ src/{ => platform}/term/CMakeLists.txt | 1 + src/{ => platform}/term/inputterm.c | 0 src/platform/term/platform.c | 25 ++++++++++ src/{ => platform}/term/term.c | 57 ++++++++++++++++++++-- src/{ => platform}/term/term.h | 5 ++ src/world/map.h | 2 +- 25 files changed, 456 insertions(+), 46 deletions(-) create mode 100755 src/cutscene/cutscene.h create mode 100755 src/cutscene/testcutscene.h create mode 100755 src/entity/direction.c create mode 100755 src/entity/sign.c create mode 100755 src/entity/sign.h create mode 100755 src/gametime.c create mode 100755 src/gametime.h create mode 100755 src/platform/CMakeLists.txt create mode 100644 src/platform/platform.h rename src/{ => platform}/term/CMakeLists.txt (97%) rename src/{ => platform}/term/inputterm.c (100%) create mode 100644 src/platform/term/platform.c rename src/{ => platform}/term/term.c (63%) rename src/{ => platform}/term/term.h (91%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7c16a88..142560e 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,11 +11,11 @@ target_sources(microrpg PRIVATE main.c game.c input.c + gametime.c ) # Subdirs add_subdirectory(entity) add_subdirectory(scene) +add_subdirectory(platform) add_subdirectory(world) - -add_subdirectory(term) \ No newline at end of file diff --git a/src/cutscene/cutscene.h b/src/cutscene/cutscene.h new file mode 100755 index 0000000..07bb59d --- /dev/null +++ b/src/cutscene/cutscene.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "microrpg.h" + +#define CUTSCENE_ITEM_NULL 0 +#define CUTSCENE_ITEM_TEXTBOX 1 +#define CUTSCENE_ITEM_CALLBACK 2 + +typedef struct { + uint8_t type; + + union { + const char *textbox; + void (*callback)(void);// TODO: Bring entity data across. + }; +} cutsceneitem_t; + +typedef struct { + cutsceneitem_t *items; + uint8_t itemCount; +} cutscene_t; \ No newline at end of file diff --git a/src/cutscene/testcutscene.h b/src/cutscene/testcutscene.h new file mode 100755 index 0000000..d24a131 --- /dev/null +++ b/src/cutscene/testcutscene.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "cutscene.h" + +cutsceneitem_t TEST_CUTSCENE_ITEMS[] = { + { .type = CUTSCENE_ITEM_TEXTBOX, .textbox = "Hello World" } +}; \ No newline at end of file diff --git a/src/entity/CMakeLists.txt b/src/entity/CMakeLists.txt index b61983e..51d94d2 100755 --- a/src/entity/CMakeLists.txt +++ b/src/entity/CMakeLists.txt @@ -5,6 +5,8 @@ # Sources target_sources(microrpg PRIVATE + direction.c entity.c player.c + sign.c ) \ No newline at end of file diff --git a/src/entity/direction.c b/src/entity/direction.c new file mode 100755 index 0000000..2c4de25 --- /dev/null +++ b/src/entity/direction.c @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "direction.h" + +void directionGetRelative(const direction_t from, int8_t *outX, int8_t *outY) { + switch(from) { + case DIRECTION_NORTH: + *outX = 0; + *outY = -1; + break; + + case DIRECTION_EAST: + *outX = 1; + *outY = 0; + break; + + case DIRECTION_SOUTH: + *outX = 0; + *outY = 1; + break; + + case DIRECTION_WEST: + *outX = -1; + *outY = 0; + break; + } +} \ No newline at end of file diff --git a/src/entity/direction.h b/src/entity/direction.h index e5585d6..285072a 100755 --- a/src/entity/direction.h +++ b/src/entity/direction.h @@ -18,4 +18,13 @@ #define DIRECTION_SOUTH DIRECTION_DOWN #define DIRECTION_WEST DIRECTION_LEFT -typedef uint8_t direction_t; \ No newline at end of file +typedef uint8_t direction_t; + +/** + * Get the relative x and y offsets for a given direction. + * + * @param from The direction to get offsets for. + * @param outX Pointer to store the x offset. + * @param outY Pointer to store the y offset. + */ +void directionGetRelative(const direction_t from, int8_t *outX, int8_t *outY); \ No newline at end of file diff --git a/src/entity/entity.c b/src/entity/entity.c index 3c69706..ceeaa2b 100755 --- a/src/entity/entity.c +++ b/src/entity/entity.c @@ -6,6 +6,7 @@ */ #include "entity.h" +#include "game.h" void entityInit(entity_t *entity, const entitytype_t type) { memset(entity, 0, sizeof(entity_t)); @@ -28,21 +29,62 @@ void entityTurn(entity_t *entity, const direction_t direction) { } void entityWalk(entity_t *entity, const direction_t direction) { - // TODO: Animation, delay, colission, result, etc. + // TODO: Animation, delay, etc. entity->direction = direction; - switch(direction) { - case DIRECTION_NORTH: - entity->position.y--; + // Where are we moving? + uint8_t newX, newY; + newX = entity->position.x; + newY = entity->position.y; + { + int8_t relX, relY; + directionGetRelative(direction, &relX, &relY); + newX += relX; + newY += relY; + } + + // TODO: Tile in way? + // TODO: Map bounds in way? + + // Entity in way? + entity_t *start = GAME.overworld.map.entities; + entity_t *end = start + MAP_ENTITY_COUNT; + while(start < end) { + if( + start == entity || + entity->type == ENTITY_TYPE_NULL || + start->position.x != newX || + start->position.y != newY + ) { + start++; + continue; + } + + return;// Blocked + } + + // Player in way? + entity_t *player = &GAME.player; + if( + entity != player && + player->position.x == newX && + player->position.y == newY + ) { + return;// Blocked + } + + // Move. + entity->position.x = newX; + entity->position.y = newY; +} + +void entityInteract(entity_t *entity) { + switch(entity->type) { + case ENTITY_TYPE_SIGN: + signInteract(entity); break; - case DIRECTION_EAST: - entity->position.x++; - break; - case DIRECTION_SOUTH: - entity->position.y++; - break; - case DIRECTION_WEST: - entity->position.x--; + + default: break; } } \ No newline at end of file diff --git a/src/entity/entity.h b/src/entity/entity.h index fb46903..eabf3d1 100755 --- a/src/entity/entity.h +++ b/src/entity/entity.h @@ -9,15 +9,20 @@ #include "entity/direction.h" #include "entity/entitytype.h" #include "player.h" +#include "sign.h" typedef struct entity_s { struct { - // Relative to top-left of the map + // Relative to top-left position of the top-left chunk of the map. uint8_t x, y; } position; direction_t direction; entitytype_t type; + + union { + sign_t sign; + }; } entity_t; /** @@ -49,4 +54,11 @@ void entityTurn(entity_t *entity, const direction_t direction); * @param entity Pointer to the entity to make walk. * @param direction The direction to walk in. */ -void entityWalk(entity_t *entity, const direction_t direction); \ No newline at end of file +void entityWalk(entity_t *entity, const direction_t direction); + +/** + * Receive interaction from player. + * + * @param entity Pointer to the entity being interacted with. + */ +void entityInteract(entity_t *entity); \ No newline at end of file diff --git a/src/entity/entitytype.h b/src/entity/entitytype.h index 2e7f11e..a1e4795 100755 --- a/src/entity/entitytype.h +++ b/src/entity/entitytype.h @@ -10,5 +10,6 @@ #define ENTITY_TYPE_NULL 0 #define ENTITY_TYPE_PLAYER 1 +#define ENTITY_TYPE_SIGN 2 typedef uint8_t entitytype_t; \ No newline at end of file diff --git a/src/entity/player.c b/src/entity/player.c index 338ca47..3c1d847 100755 --- a/src/entity/player.c +++ b/src/entity/player.c @@ -7,15 +7,65 @@ #include "entity.h" #include "input.h" +#include "game.h" void playerTickInput(entity_t *entity) { - if(inputPressed(INPUT_ACTION_UP)) { - entityWalk(entity, DIRECTION_NORTH); - } else if(inputPressed(INPUT_ACTION_DOWN)) { - entityWalk(entity, DIRECTION_SOUTH); - } else if(inputPressed(INPUT_ACTION_LEFT)) { - entityWalk(entity, DIRECTION_WEST); - } else if(inputPressed(INPUT_ACTION_RIGHT)) { - entityWalk(entity, DIRECTION_EAST); + // Turn + if(inputPressed(INPUT_ACTION_UP) && entity->direction != DIRECTION_NORTH) { + return entityTurn(entity, DIRECTION_NORTH); + } + if(inputPressed(INPUT_ACTION_DOWN) && entity->direction != DIRECTION_SOUTH) { + return entityTurn(entity, DIRECTION_SOUTH); + } + if(inputPressed(INPUT_ACTION_LEFT) && entity->direction != DIRECTION_WEST) { + return entityTurn(entity, DIRECTION_WEST); + } + if(inputPressed(INPUT_ACTION_RIGHT) && entity->direction != DIRECTION_EAST) { + return entityTurn(entity, DIRECTION_EAST); + } + + // Walk + if(inputDown(INPUT_ACTION_UP)) { + return entityWalk(entity, DIRECTION_NORTH); + } + if(inputDown(INPUT_ACTION_DOWN)) { + return entityWalk(entity, DIRECTION_SOUTH); + } + if(inputDown(INPUT_ACTION_LEFT)) { + return entityWalk(entity, DIRECTION_WEST); + } + if(inputDown(INPUT_ACTION_RIGHT)) { + return entityWalk(entity, DIRECTION_EAST); + } + + // Interaction + if(inputPressed(INPUT_ACTION_A)) { + // Facing direction relative + uint8_t targetX, targetY; + { + int8_t faceX, faceY; + directionGetRelative(entity->direction, &faceX, &faceY); + targetX = entity->position.x + faceX; + targetY = entity->position.y + faceY; + } + + // For each entity + entity_t *start = GAME.overworld.map.entities; + entity_t *end = start + MAP_ENTITY_COUNT; + while(start < end) { + if( + start == entity || + start->type == ENTITY_TYPE_NULL || + start->position.x != targetX || + start->position.y != targetY + ) { + start++; + continue; + } + + // Interact + entityInteract(start); + return; + } } } \ No newline at end of file diff --git a/src/entity/sign.c b/src/entity/sign.c new file mode 100755 index 0000000..9fd6504 --- /dev/null +++ b/src/entity/sign.c @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "sign.h" +#include "entity.h" + +void signInteract(entity_t *entity) { + abort(); +} \ No newline at end of file diff --git a/src/entity/sign.h b/src/entity/sign.h new file mode 100755 index 0000000..18e4d00 --- /dev/null +++ b/src/entity/sign.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "microrpg.h" + +typedef struct entity_s entity_t; + +typedef struct sign_s { + void *nothing; +} sign_t; + +/** + * Receive interaction from player. + * + * @param entity Pointer to the entity being interacted with. + */ +void signInteract(entity_t *entity); \ No newline at end of file diff --git a/src/game.c b/src/game.c index 8ba674a..743ce41 100755 --- a/src/game.c +++ b/src/game.c @@ -14,9 +14,14 @@ void gameInit() { entityInit(&GAME.player, ENTITY_TYPE_PLAYER); + entityInit(&GAME.overworld.map.entities[0], ENTITY_TYPE_SIGN); + GAME.overworld.map.entities[0].position.x = 5; + GAME.overworld.map.entities[0].position.y = 5; + GAME.scene = SCENE_OVERWORLD; } void gameTick() { + gameTimeTick(&GAME.time); sceneTick(); } \ No newline at end of file diff --git a/src/game.h b/src/game.h index 149df76..f8de64d 100755 --- a/src/game.h +++ b/src/game.h @@ -9,6 +9,7 @@ #include "entity/entity.h" #include "world/map.h" #include "scene/scene.h" +#include "gametime.h" #define GAME_SCENE_INITIAL 0 #define GAME_SCENE_OVERWORLD 1 @@ -16,6 +17,7 @@ typedef struct game_s { scene_t scene; entity_t player; + gametime_t time; union { struct { diff --git a/src/gametime.c b/src/gametime.c new file mode 100755 index 0000000..1ba8f2e --- /dev/null +++ b/src/gametime.c @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "time.h" +#include "game.h" + +void gameTimeTick() { + GAME.time.ticks++; + + if(GAME.time.ticks >= GAME_TIME_TICKS_PER_SECOND) { + GAME.time.ticks = 0; + GAME.time.seconds++; + + if(GAME.time.seconds >= GAME_TIME_SECONDS_PER_MINUTE) { + GAME.time.seconds = 0; + GAME.time.minutes++; + + if(GAME.time.minutes >= GAME_TIME_MINUTES_PER_HOUR) { + GAME.time.minutes = 0; + GAME.time.hours++; + + if(GAME.time.hours >= GAME_TIME_HOURS_PER_DAY) { + GAME.time.hours = 0; + GAME.time.days++; + + if(GAME.time.days >= GAME_TIME_DAYS_MAX) { + GAME.time.days = 0; + // Roll over occured. + } + } + } + } + } +} \ No newline at end of file diff --git a/src/gametime.h b/src/gametime.h new file mode 100755 index 0000000..21fe23a --- /dev/null +++ b/src/gametime.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "microrpg.h" + +#define GAME_TIME_TICKS_PER_SECOND 60 +#define GAME_TIME_SECONDS_PER_MINUTE 60 +#define GAME_TIME_MINUTES_PER_HOUR 60 +#define GAME_TIME_HOURS_PER_DAY 24 +#define GAME_TIME_DAYS_MAX 7 + +#define GAME_TIME_SUNDAY 0 +#define GAME_TIME_MONDAY 1 +#define GAME_TIME_TUESDAY 2 +#define GAME_TIME_WEDNESDAY 3 +#define GAME_TIME_THURSDAY 4 +#define GAME_TIME_FRIDAY 5 +#define GAME_TIME_SATURDAY 6 + +typedef struct gametime_s { + uint8_t ticks; + uint8_t seconds; + uint8_t minutes; + uint8_t hours; + uint8_t days; +} gametime_t; + +/** + * Advance the game time by one tick. + */ +void gameTimeTick(); \ No newline at end of file diff --git a/src/main.c b/src/main.c index 92d8626..0891af0 100755 --- a/src/main.c +++ b/src/main.c @@ -6,32 +6,20 @@ */ #include "game.h" -#if RPG_TERM == 1 -#include "term/term.h" -#endif +#include "platform/platform.h" int main(int argc, char** argv) { gameInit(); - - #if RPG_TERM == 1 - termInit(); - #endif + platformInit(); while(1) { - #if RPG_TERM == 1 - termUpdate(); - #endif + platformUpdate(); gameTick(); - #if RPG_TERM == 1 - termDraw(); - #endif + platformDraw(); } - #if RPG_TERM == 1 - termDispose(); - #endif - + platformDispose(); return 0; } \ No newline at end of file diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt new file mode 100755 index 0000000..74eecee --- /dev/null +++ b/src/platform/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Change inclusion to platform-specific +add_subdirectory(term) \ No newline at end of file diff --git a/src/platform/platform.h b/src/platform/platform.h new file mode 100644 index 0000000..7be1d87 --- /dev/null +++ b/src/platform/platform.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "microrpg.h" + +/** + * Initialize the platform-specific subsystem. + */ +void platformInit(void); + +/** + * Update the platform-specific subsystem. + */ +void platformUpdate(void); + +/** + * Render the platform-specific subsystem. + */ +void platformDraw(void); + +/** + * Dispose of the platform-specific subsystem. + */ +void platformDispose(void); \ No newline at end of file diff --git a/src/term/CMakeLists.txt b/src/platform/term/CMakeLists.txt similarity index 97% rename from src/term/CMakeLists.txt rename to src/platform/term/CMakeLists.txt index 8ea11da..f06a029 100755 --- a/src/term/CMakeLists.txt +++ b/src/platform/term/CMakeLists.txt @@ -20,6 +20,7 @@ target_link_libraries(microrpg PRIVATE target_sources(microrpg PRIVATE term.c inputterm.c + platform.c ) # Compiler flags diff --git a/src/term/inputterm.c b/src/platform/term/inputterm.c similarity index 100% rename from src/term/inputterm.c rename to src/platform/term/inputterm.c diff --git a/src/platform/term/platform.c b/src/platform/term/platform.c new file mode 100644 index 0000000..b871159 --- /dev/null +++ b/src/platform/term/platform.c @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "platform/platform.h" +#include "platform/term/term.h" + +void platformInit() { + termInit(); +} + +void platformUpdate() { + termUpdate(); +} + +void platformDraw() { + termDraw(); +} + +void platformDispose() { + termDispose(); +} \ No newline at end of file diff --git a/src/term/term.c b/src/platform/term/term.c similarity index 63% rename from src/term/term.c rename to src/platform/term/term.c index 6923c6c..acdedf3 100755 --- a/src/term/term.c +++ b/src/platform/term/term.c @@ -7,6 +7,7 @@ #include "term.h" #include "input.h" +#include typedef struct terminputmap_s { int key; @@ -44,7 +45,17 @@ void termInit() { curs_set(0); start_color(); use_default_colors(); + init_pair(1, COLOR_GREEN, -1); + + // Set in-game time to real world time. + time_t now = time(NULL); + struct tm *t = localtime(&now); + + GAME.time.days = t->tm_wday; + GAME.time.hours = t->tm_hour; + GAME.time.minutes = t->tm_min; + GAME.time.seconds = t->tm_sec; } void termUpdate() { @@ -74,9 +85,15 @@ void termUpdate() { void termDraw() { clear(); - attron(COLOR_PAIR(1)); - termDrawEntity(&GAME.player); + switch(GAME.scene) { + case SCENE_OVERWORLD: + termDrawOverworld(); + break; + + default: + break; + } attroff(COLOR_PAIR(1)); refresh(); @@ -85,9 +102,43 @@ void termDraw() { napms(16); } +void termDrawOverworld() { + // Draw map. + + // Draw entities. + attron(COLOR_PAIR(1)); + termDrawEntity(&GAME.player); + + entity_t *start = GAME.overworld.map.entities; + entity_t *end = start + MAP_ENTITY_COUNT; + while(start < end) { + if(start->type != ENTITY_TYPE_NULL) termDrawEntity(start); + start++; + } +} + void termDrawEntity(const entity_t *ent) { // Placeholder: Draw entity at its position - mvaddch(ent->position.y, ent->position.x, '@'); + char c; + switch(ent->direction) { + case DIRECTION_NORTH: + c = '^'; + break; + case DIRECTION_EAST: + c = '>'; + break; + case DIRECTION_SOUTH: + c = 'v'; + break; + case DIRECTION_WEST: + c = '<'; + break; + default: + c = '@'; + break; + } + + mvaddch(ent->position.y, ent->position.x, c); } void termDispose() { diff --git a/src/term/term.h b/src/platform/term/term.h similarity index 91% rename from src/term/term.h rename to src/platform/term/term.h index 9b29696..ffbbb31 100755 --- a/src/term/term.h +++ b/src/platform/term/term.h @@ -33,6 +33,11 @@ void termUpdate(); */ void termDraw(); +/** + * Draw the overworld scene. + */ +void termDrawOverworld(); + /** * Draw an entity to the terminal. * diff --git a/src/world/map.h b/src/world/map.h index b7b4eaf..7c293e8 100755 --- a/src/world/map.h +++ b/src/world/map.h @@ -18,7 +18,7 @@ typedef struct map_s { // Loaded chunks. chunk_t chunks[MAP_CHUNK_COUNT]; chunk_t *order[MAP_CHUNK_COUNT]; - int8_t firstChunk; + int8_t topLeftX, topLeftY; // Map entities entity_t entities[MAP_ENTITY_COUNT];