diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt index 81008b3..40d0bc8 100755 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -3,6 +3,6 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -add_subdirectory(raylib) +# add_subdirectory(raylib) add_subdirectory(term) -#add_subdirectory(term) \ No newline at end of file +add_subdirectory(tty) \ No newline at end of file diff --git a/src/platform/raylib/inputraylib.c b/src/platform/raylib/inputraylib.c index 8b5c0fe..3baa713 100644 --- a/src/platform/raylib/inputraylib.c +++ b/src/platform/raylib/inputraylib.c @@ -6,19 +6,20 @@ */ #include "input.h" +#include "platformraylib.h" bool_t inputDown(const uint8_t action) { - return false; + return (PLATFORM_RAYLIB.inputCurrent & action) != 0; } bool_t inputUp(const uint8_t action) { - return true; + return (PLATFORM_RAYLIB.inputCurrent & action) == 0; } bool_t inputWasDown(const uint8_t action) { - return false; + return (PLATFORM_RAYLIB.inputPrevious & action) != 0; } bool_t inputWasUp(const uint8_t action) { - return true; + return (PLATFORM_RAYLIB.inputPrevious & action) == 0; } \ No newline at end of file diff --git a/src/platform/raylib/platform.c b/src/platform/raylib/platform.c index f3a4ed4..e8bd949 100644 --- a/src/platform/raylib/platform.c +++ b/src/platform/raylib/platform.c @@ -6,18 +6,61 @@ */ #include "platform/platform.h" +#include "platform/raylib/platformraylib.h" #include #include "font.h" #include "platform/term/term.h" +#include "input.h" +#include "game.h" +#include "gametime.h" +#include + +typedef struct rlkeyboardmap_t { + uint8_t action; + int32_t raylibKey; +} rlkeyboardmap_t; + +static const rlkeyboardmap_t RL_KEYBOARD_MAP[] = { + { INPUT_ACTION_UP, KEY_W }, + { INPUT_ACTION_UP, KEY_UP }, + { INPUT_ACTION_DOWN, KEY_S }, + { INPUT_ACTION_DOWN, KEY_DOWN }, + { INPUT_ACTION_LEFT, KEY_A }, + { INPUT_ACTION_LEFT, KEY_LEFT }, + { INPUT_ACTION_RIGHT, KEY_D }, + { INPUT_ACTION_RIGHT, KEY_RIGHT }, + { INPUT_ACTION_A, KEY_E }, + { INPUT_ACTION_B, KEY_Q }, + { INPUT_ACTION_START, KEY_ENTER }, + { INPUT_ACTION_START, KEY_SPACE }, + { INPUT_ACTION_SELECT, KEY_BACKSPACE }, + { 0x00, 0x00 } +}; + +platformraylib_t PLATFORM_RAYLIB; uint8_t platformInit(void) { + memset(&PLATFORM_RAYLIB, 0, sizeof(platformraylib_t)); + + // Init raylib. InitWindow(800, 600, "Micro JRPG"); SetTargetFPS(60); + // Load font if(fontInit() != 0) return PLATFORM_ERROR; + // Resize window SetWindowSize(40 * FONT.charWidth, 30 * FONT.charHeight); + // 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; + return PLATFORM_OK; } @@ -25,7 +68,18 @@ uint8_t platformUpdate() { if(WindowShouldClose()) { return PLATFORM_EXIT; } - + + // Update input state. + PLATFORM_RAYLIB.inputPrevious = PLATFORM_RAYLIB.inputCurrent; + PLATFORM_RAYLIB.inputCurrent = 0; + rlkeyboardmap_t *map = RL_KEYBOARD_MAP; + do { + if(IsKeyDown(map->raylibKey)) { + PLATFORM_RAYLIB.inputCurrent |= map->action; + } + map++; + } while(map->action != 0x00); + return PLATFORM_OK; } diff --git a/src/platform/raylib/platformraylib.h b/src/platform/raylib/platformraylib.h new file mode 100644 index 0000000..318b186 --- /dev/null +++ b/src/platform/raylib/platformraylib.h @@ -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 "microrpg.h" + +typedef struct platformraylib_s { + uint8_t inputCurrent; + uint8_t inputPrevious; +} platformraylib_t; + +extern platformraylib_t PLATFORM_RAYLIB; \ No newline at end of file diff --git a/src/platform/term/CMakeLists.txt b/src/platform/term/CMakeLists.txt index 37b8d22..beeeabc 100644 --- a/src/platform/term/CMakeLists.txt +++ b/src/platform/term/CMakeLists.txt @@ -6,6 +6,7 @@ # Sources target_sources(microrpg PRIVATE term.c + termoverworld.c ) # Compiler flags diff --git a/src/platform/term/term.c b/src/platform/term/term.c index 6740acf..285524c 100644 --- a/src/platform/term/term.c +++ b/src/platform/term/term.c @@ -6,49 +6,50 @@ */ #include "term.h" -#include "game.h" - -void termDrawOverworld() { - // Draw map. - - 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 - 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; - } - - termPushChar -} +#include "termoverworld.h" void termDraw() { termClear(); + int32_t width, height, x, y; + width = termGetColumnCount(); + height = termGetRowCount(); + // Create buffers + char_t *chars = malloc(width * height * sizeof(char_t)); + if(chars == NULL) { + printf("termDrawOverworld: Out of memory!\n"); + abort(); + return; + } + memset(chars, ' ', width * height * sizeof(char_t)); + + termcolor_t *colors = malloc(width * height * sizeof(termcolor_t)); + if(colors == NULL) { + printf("termDrawOverworld: Out of memory!\n"); + free(chars); + abort(); + return; + } + memset(colors, TERM_COLOR_WHITE, width * height * sizeof(termcolor_t)); + + // Draw + termDrawOverworld(chars, colors, width, height); + + // Print + size_t i; + termcolor_t curColor = TERM_COLOR_WHITE; + for(size_t i = 0; i < width * height; i++) { + if(colors[i] != curColor) { + termPushColor(colors[i]); + curColor = colors[i]; + } + termPushChar(chars[i]); + } + + // Clean up + free(chars); + free(colors); termFlush(); } \ No newline at end of file diff --git a/src/platform/term/termoverworld.c b/src/platform/term/termoverworld.c new file mode 100644 index 0000000..586d913 --- /dev/null +++ b/src/platform/term/termoverworld.c @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "termoverworld.h" + +void termDrawOverworld( + char_t *chars, + termcolor_t *colors, + const int32_t w, + const int32_t h +) { + // Draw the ground + for(int32_t x = 0; x < w; x++) { + for(int32_t y = 0; y < h; y++) { + size_t index = y * w + x; + chars[index] = '.'; + colors[index] = TERM_COLOR_GREEN; + } + } + + // Draw entities + termDrawEntity(&GAME.player, chars, colors, w, h); +} + +void termDrawEntity( + const entity_t *ent, + char_t *chars, + termcolor_t *colors, + const int32_t w, + const int32_t h +) { + if(ent->type == ENTITY_TYPE_NULL) return; + + if(ent->position.x >= w || ent->position.y >= h) return; + + int32_t index = ent->position.y * w + ent->position.x; + + if(ent->type == ENTITY_TYPE_PLAYER) { + chars[index] = '@'; + colors[index] = TERM_COLOR_YELLOW; + } +} \ No newline at end of file diff --git a/src/platform/term/termoverworld.h b/src/platform/term/termoverworld.h new file mode 100644 index 0000000..e7055ca --- /dev/null +++ b/src/platform/term/termoverworld.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "term.h" +#include "game.h" + +/** + * Draw the overworld SCENE to the terminal. + * + * @param chars Buffer of characters to write to. + * @param colors Buffer of colors to write to. + * @param w Width of the terminal in characters. + * @param h Height of the terminal in characters. + */ +void termDrawOverworld( + char_t *chars, + termcolor_t *colors, + const int32_t w, + const int32_t h +); + +/** + * Draw an entity to the terminal. + * + * @param ent The entity to draw. + * @param chars Buffer of characters to write to. + * @param colors Buffer of colors to write to. + * @param w Width of the terminal in characters. + * @param h Height of the terminal in characters. + */ +void termDrawEntity( + const entity_t *ent, + char_t *chars, + termcolor_t *colors, + const int32_t w, + const int32_t h +); \ No newline at end of file diff --git a/src/platform/tty/CMakeLists.txt b/src/platform/tty/CMakeLists.txt index f06a029..5e7f085 100755 --- a/src/platform/tty/CMakeLists.txt +++ b/src/platform/tty/CMakeLists.txt @@ -18,12 +18,12 @@ target_link_libraries(microrpg PRIVATE # Sources target_sources(microrpg PRIVATE - term.c inputterm.c platform.c + termtty.c ) # Compiler flags target_compile_definitions(microrpg PRIVATE - RPG_TERM=1 + RPG_TTY=1 ) \ No newline at end of file diff --git a/src/platform/tty/inputterm.c b/src/platform/tty/inputterm.c index 62b5223..ba1991f 100755 --- a/src/platform/tty/inputterm.c +++ b/src/platform/tty/inputterm.c @@ -5,20 +5,20 @@ * https://opensource.org/licenses/MIT */ -#include "term.h" +#include "platformtty.h" bool_t inputDown(const uint8_t action) { - return (TERM.inputCurrent & action) != 0; + return (PLATFORM_TTY.inputCurrent & action) != 0; } bool_t inputUp(const uint8_t action) { - return (TERM.inputCurrent & action) == 0; + return (PLATFORM_TTY.inputCurrent & action) == 0; } bool_t inputWasDown(const uint8_t action) { - return (TERM.inputPrevious & action) != 0; + return (PLATFORM_TTY.inputPrevious & action) != 0; } bool_t inputWasUp(const uint8_t action) { - return (TERM.inputPrevious & action) == 0; + return (PLATFORM_TTY.inputPrevious & action) == 0; } \ No newline at end of file diff --git a/src/platform/tty/platform.c b/src/platform/tty/platform.c index b871159..58a75a0 100644 --- a/src/platform/tty/platform.c +++ b/src/platform/tty/platform.c @@ -7,19 +7,109 @@ #include "platform/platform.h" #include "platform/term/term.h" +#include "platform/tty/platformtty.h" +#include "input.h" +#include -void platformInit() { - termInit(); +typedef struct ttyinputmap_s { + int key; + uint8_t action; +} ttyinputmap_t; + +static const ttyinputmap_t TERM_INPUT_MAP[] = { + { KEY_UP, INPUT_ACTION_UP }, + { 'w', INPUT_ACTION_UP }, + { KEY_DOWN, INPUT_ACTION_DOWN }, + { 's', INPUT_ACTION_DOWN }, + { KEY_LEFT, INPUT_ACTION_LEFT }, + { 'a', INPUT_ACTION_LEFT }, + { KEY_RIGHT, INPUT_ACTION_RIGHT }, + { 'd', INPUT_ACTION_RIGHT }, + { 'j', INPUT_ACTION_A }, + { 'e', INPUT_ACTION_A }, + { 'k', INPUT_ACTION_B }, + { 'q', INPUT_ACTION_B }, + { KEY_ENTER, INPUT_ACTION_START }, + { ' ', INPUT_ACTION_SELECT }, + { -1, 0 } +}; + +platformtty_t PLATFORM_TTY; + +uint8_t platformInit() { + memset(&PLATFORM_TTY, 0, sizeof(platformtty_t)); + + initscr(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + nodelay(stdscr, TRUE); + curs_set(0); + start_color(); + use_default_colors(); + + // Color Pairs + init_pair(TERM_COLOR_BLACK, COLOR_BLACK, -1); + init_pair(TERM_COLOR_RED, COLOR_RED, -1); + init_pair(TERM_COLOR_GREEN, COLOR_GREEN, -1); + init_pair(TERM_COLOR_YELLOW, COLOR_YELLOW, -1); + init_pair(TERM_COLOR_BLUE, COLOR_BLUE, -1); + init_pair(TERM_COLOR_MAGENTA, COLOR_MAGENTA, -1); + init_pair(TERM_COLOR_CYAN, COLOR_CYAN, -1); + init_pair(TERM_COLOR_WHITE, COLOR_WHITE, -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; + + return PLATFORM_OK; } -void platformUpdate() { - termUpdate(); +uint8_t platformUpdate() { + PLATFORM_TTY.inputPrevious = PLATFORM_TTY.inputCurrent; + PLATFORM_TTY.inputCurrent = 0; + + int ch = getch(); + if(ch == ERR) { + PLATFORM_TTY.lastch = ERR; + return PLATFORM_OK; + } + + if(ch == PLATFORM_TTY.lastch) { + return PLATFORM_OK; + } + PLATFORM_TTY.lastch = ch; + + const ttyinputmap_t *map = TERM_INPUT_MAP; + while(map->key != -1) { + if(map->key == ch) { + PLATFORM_TTY.inputCurrent |= map->action; + break; + } + map++; + } + + return PLATFORM_OK; } void platformDraw() { + clear(); + termDraw(); + + attroff(COLOR_PAIR(1)); + refresh(); + + // 16ms delay (60FPS) + napms(16); } void platformDispose() { - termDispose(); + endwin(); } \ No newline at end of file diff --git a/src/platform/tty/platformtty.h b/src/platform/tty/platformtty.h new file mode 100755 index 0000000..6d5d4f5 --- /dev/null +++ b/src/platform/tty/platformtty.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "microrpg.h" +#include "game.h" +#include + +typedef struct platformtty_s { + uint8_t inputCurrent; + uint8_t inputPrevious; + int lastch; +} platformtty_t; + +extern platformtty_t PLATFORM_TTY; \ No newline at end of file diff --git a/src/platform/tty/term.c b/src/platform/tty/term.c deleted file mode 100755 index acdedf3..0000000 --- a/src/platform/tty/term.c +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "term.h" -#include "input.h" -#include - -typedef struct terminputmap_s { - int key; - uint8_t action; -} terminputmap_t; - -static const terminputmap_t TERM_INPUT_MAP[] = { - { KEY_UP, INPUT_ACTION_UP }, - { 'w', INPUT_ACTION_UP }, - { KEY_DOWN, INPUT_ACTION_DOWN }, - { 's', INPUT_ACTION_DOWN }, - { KEY_LEFT, INPUT_ACTION_LEFT }, - { 'a', INPUT_ACTION_LEFT }, - { KEY_RIGHT, INPUT_ACTION_RIGHT }, - { 'd', INPUT_ACTION_RIGHT }, - { 'j', INPUT_ACTION_A }, - { 'e', INPUT_ACTION_A }, - { 'k', INPUT_ACTION_B }, - { 'q', INPUT_ACTION_B }, - { KEY_ENTER, INPUT_ACTION_START }, - { ' ', INPUT_ACTION_SELECT }, - { -1, 0 } -}; - -term_t TERM; - -void termInit() { - memset(&TERM, 0, sizeof(TERM)); - - initscr(); - cbreak(); - noecho(); - keypad(stdscr, TRUE); - nodelay(stdscr, TRUE); - 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() { - TERM.inputPrevious = TERM.inputCurrent; - TERM.inputCurrent = 0; - - int ch = getch(); - if(ch == ERR) { - TERM.lastch = ERR; - return; - } - - if(ch == TERM.lastch) { - return; - } - TERM.lastch = ch; - - const terminputmap_t *map = TERM_INPUT_MAP; - while(map->key != -1) { - if(map->key == ch) { - TERM.inputCurrent |= map->action; - break; - } - map++; - } -} - -void termDraw() { - clear(); - - switch(GAME.scene) { - case SCENE_OVERWORLD: - termDrawOverworld(); - break; - - default: - break; - } - - attroff(COLOR_PAIR(1)); - refresh(); - - // 16ms delay (60FPS) - 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 - 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() { - endwin(); -} \ No newline at end of file diff --git a/src/platform/tty/term.h b/src/platform/tty/term.h deleted file mode 100755 index ffbbb31..0000000 --- a/src/platform/tty/term.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "microrpg.h" -#include "game.h" -#include - -typedef struct term_s { - uint8_t inputCurrent; - uint8_t inputPrevious; - int lastch; -} term_t; - -extern term_t TERM; - -/** - * Initialize the terminal subsystem. - */ -void termInit(); - -/** - * Update the terminal prior to game update. - */ -void termUpdate(); - -/** - * Draw the terminal game. - */ -void termDraw(); - -/** - * Draw the overworld scene. - */ -void termDrawOverworld(); - -/** - * Draw an entity to the terminal. - * - * @param ent The entity to draw. - */ -void termDrawEntity(const entity_t *ent); - -/** - * Dispose of the terminal subsystem. - */ -void termDispose(); \ No newline at end of file diff --git a/src/platform/tty/termtty.c b/src/platform/tty/termtty.c new file mode 100644 index 0000000..414d2aa --- /dev/null +++ b/src/platform/tty/termtty.c @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "platform/term/term.h" +#include + +void termClear() { + clear(); + refresh(); +} + +void termFlush() { + // Nothing to do +} + +uint8_t termGetColumnCount() { + int cols = getmaxx(stdscr); + return (uint8_t)cols; +} + +uint8_t termGetRowCount() { + int rows = getmaxy(stdscr); + return (uint8_t)rows; +} + +void termPushColor(termcolor_t color) { + // Map the termcolor_t to the ncurses color pair + attron(COLOR_PAIR((int)color)); +} + +void termPushChar(char_t c) { + addch(c); +} \ No newline at end of file