Creating generic term renderer.
This commit is contained in:
		| @@ -3,5 +3,6 @@ | ||||
| # This software is released under the MIT License. | ||||
| # https://opensource.org/licenses/MIT | ||||
|  | ||||
| # Change inclusion to platform-specific | ||||
| add_subdirectory(term) | ||||
| add_subdirectory(raylib) | ||||
| add_subdirectory(term) | ||||
| #add_subdirectory(term) | ||||
| @@ -8,15 +8,23 @@ | ||||
| #pragma once | ||||
| #include "microrpg.h" | ||||
|  | ||||
| #define PLATFORM_OK 0 | ||||
| #define PLATFORM_EXIT 1 | ||||
| #define PLATFORM_ERROR 2 | ||||
|  | ||||
| /** | ||||
|  * Initialize the platform-specific subsystem. | ||||
|  *  | ||||
|  * @return 0 on success, non-zero on failure. | ||||
|  */ | ||||
| void platformInit(void); | ||||
| uint8_t platformInit(void); | ||||
|  | ||||
| /** | ||||
|  * Update the platform-specific subsystem. | ||||
|  *  | ||||
|  * @return 0 to continue, 1 to exit, anything else for error. | ||||
|  */ | ||||
| void platformUpdate(void); | ||||
| uint8_t platformUpdate(void); | ||||
|  | ||||
| /** | ||||
|  * Render the platform-specific subsystem. | ||||
|   | ||||
							
								
								
									
										27
									
								
								src/platform/raylib/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/platform/raylib/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| # Copyright (c) 2025 Dominic Masters | ||||
| #  | ||||
| # This software is released under the MIT License. | ||||
| # https://opensource.org/licenses/MIT | ||||
|  | ||||
| # Libraries | ||||
| find_package(raylib REQUIRED) | ||||
|  | ||||
| target_link_libraries(microrpg PRIVATE | ||||
|   raylib::raylib | ||||
| ) | ||||
|  | ||||
| # Sources | ||||
| target_sources(microrpg PRIVATE | ||||
|   inputraylib.c | ||||
|   termraylib.c | ||||
|   platform.c | ||||
|   font.c | ||||
| ) | ||||
|  | ||||
| # Embed resources | ||||
| embed_file(microrpg "${ASSETS_DIR}/8x8ascii-darkrose/font.png" "assets/font.h" FONT) | ||||
|  | ||||
| # Compiler flags | ||||
| target_compile_definitions(microrpg PRIVATE | ||||
|   RPG_RAYLIB=1 | ||||
| ) | ||||
							
								
								
									
										89
									
								
								src/platform/raylib/font.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/platform/raylib/font.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #include "font.h" | ||||
| #include "assets/font.h" | ||||
|  | ||||
| font_t FONT; | ||||
|  | ||||
| int32_t fontInit() { | ||||
|   memset(&FONT, 0, sizeof(FONT)); | ||||
|    | ||||
|   FONT.image = LoadImageFromMemory(".png", FONT_DATA, FONT_SIZE); | ||||
|   if(FONT.image.data == NULL) { | ||||
|     printf("Failed to load font image from memory.\n"); | ||||
|     return -1; // Failed to load image | ||||
|   } | ||||
|  | ||||
|   FONT.texture = LoadTextureFromImage(FONT.image); | ||||
|   if(FONT.texture.id == 0) { | ||||
|     printf("Failed to load font texture from image.\n"); | ||||
|     UnloadImage(FONT.image); | ||||
|     return -1; // Failed to load texture | ||||
|   } | ||||
|  | ||||
|   FONT.charWidthRaw = FONT.texture.width / FONT_COLUMN_COUNT; | ||||
|   FONT.charHeightRaw = FONT.texture.height / FONT_ROW_COUNT; | ||||
|   FONT.charWidth = FONT.charWidthRaw * FONT_SCALE; | ||||
|   FONT.charHeight = FONT.charHeightRaw * FONT_SCALE; | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| void fontDraw( | ||||
|   const char_t c, const float_t x, const float_t y, const Color color | ||||
| ) { | ||||
|   int32_t charAdjusted = (int32_t)c - 1; | ||||
|   if(c >= '`') charAdjusted += 1;// For some reason the font has empty slot? | ||||
|  | ||||
|   int32_t col = (charAdjusted % FONT_COLUMN_COUNT); | ||||
|   int32_t row = (charAdjusted / FONT_COLUMN_COUNT); | ||||
|  | ||||
|   Rectangle sourceRec = { | ||||
|     .x = (float_t)(col * FONT.charWidthRaw), | ||||
|     .y = (float_t)(row * FONT.charHeightRaw), | ||||
|     .width = (float_t)FONT.charWidthRaw, | ||||
|     .height = (float_t)FONT.charHeightRaw | ||||
|   }; | ||||
|   Rectangle destRec = { | ||||
|     .x = x, | ||||
|     .y = y, | ||||
|     .width = (float_t)FONT.charWidth, | ||||
|     .height = (float_t)FONT.charHeight | ||||
|   }; | ||||
|  | ||||
|   DrawTexturePro( | ||||
|     FONT.texture, sourceRec, destRec, (Vector2){0, 0}, 0.0f, color | ||||
|   ); | ||||
| } | ||||
|  | ||||
| void fontDrawString( | ||||
|   const char_t *str, const float_t x, const float_t y, const Color color | ||||
| ) { | ||||
|   float_t cx, cy; | ||||
|   cx = x; | ||||
|   cy = y; | ||||
|  | ||||
|   const char_t *c = (char_t *)str; | ||||
|   while(*c) { | ||||
|     if(*c == '\n') { | ||||
|       cx = x; | ||||
|       cy += (float_t)(FONT.charHeight + FONT_CHAR_SPACING); | ||||
|       c++; | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     fontDraw(*c, cx, cy, color); | ||||
|     cx += (float_t)(FONT.charWidth + FONT_CHAR_SPACING); | ||||
|     c++; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void fontDispose() { | ||||
|   UnloadTexture(FONT.texture); | ||||
|   UnloadImage(FONT.image); | ||||
| } | ||||
							
								
								
									
										60
									
								
								src/platform/raylib/font.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/platform/raylib/font.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include "microrpg.h" | ||||
| #include <raylib.h> | ||||
|  | ||||
| #define FONT_COLUMN_COUNT 16 | ||||
| #define FONT_ROW_COUNT 8 | ||||
| #define FONT_CHAR_SPACING 0// Font has built in spacing | ||||
| #define FONT_SCALE 3.0f | ||||
|  | ||||
| typedef struct font_s { | ||||
|   Texture2D texture; | ||||
|   Image image; | ||||
|   int32_t charWidthRaw; | ||||
|   int32_t charHeightRaw; | ||||
|   int32_t charWidth; | ||||
|   int32_t charHeight; | ||||
| } font_t; | ||||
|  | ||||
| extern font_t FONT; | ||||
|  | ||||
| /** | ||||
|  * Initializes the font. | ||||
|  */ | ||||
| int32_t fontInit(); | ||||
|  | ||||
| /** | ||||
|  * Draws a character at the specified position. | ||||
|  *  | ||||
|  * @param c The character to draw. | ||||
|  * @param x The x position. | ||||
|  * @param y The y position. | ||||
|  * @param color The color to draw the character with. | ||||
|  */ | ||||
| void fontDraw( | ||||
|   const char_t c, const float_t x, const float_t y, const Color color | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * Draws a string at the specified position. No wrapping is performed. | ||||
|  *  | ||||
|  * @param str The string to draw. | ||||
|  * @param x The x position. | ||||
|  * @param y The y position. | ||||
|  * @param color The color to draw the string with. | ||||
|  */ | ||||
| void fontDrawString( | ||||
|   const char_t *str, const float_t x, const float_t y, const Color color | ||||
| ); | ||||
|  | ||||
| /** | ||||
|  * Disposes of the font. | ||||
|  */ | ||||
| void fontDispose(); | ||||
							
								
								
									
										24
									
								
								src/platform/raylib/inputraylib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/platform/raylib/inputraylib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #include "input.h" | ||||
|    | ||||
| bool_t inputDown(const uint8_t action) { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool_t inputUp(const uint8_t action) { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool_t inputWasDown(const uint8_t action) { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool_t inputWasUp(const uint8_t action) { | ||||
|   return true; | ||||
| } | ||||
							
								
								
									
										44
									
								
								src/platform/raylib/platform.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/platform/raylib/platform.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #include "platform/platform.h" | ||||
| #include <raylib.h> | ||||
| #include "font.h" | ||||
| #include "platform/term/term.h" | ||||
|  | ||||
| uint8_t platformInit(void) { | ||||
|   InitWindow(800, 600, "Micro JRPG"); | ||||
|   SetTargetFPS(60); | ||||
|  | ||||
|   if(fontInit() != 0) return PLATFORM_ERROR; | ||||
|  | ||||
|   SetWindowSize(40 * FONT.charWidth, 30 * FONT.charHeight); | ||||
|  | ||||
|   return PLATFORM_OK; | ||||
| } | ||||
|  | ||||
| uint8_t platformUpdate() { | ||||
|   if(WindowShouldClose()) { | ||||
|     return PLATFORM_EXIT; | ||||
|   } | ||||
|    | ||||
|   return PLATFORM_OK; | ||||
| } | ||||
|  | ||||
| void platformDraw() { | ||||
|   BeginDrawing(); | ||||
|   ClearBackground(BLACK); | ||||
|    | ||||
|   termDraw(); | ||||
|  | ||||
|   EndDrawing(); | ||||
| } | ||||
|  | ||||
| void platformDispose() { | ||||
|   fontDispose(); | ||||
|   CloseWindow(); | ||||
| } | ||||
							
								
								
									
										84
									
								
								src/platform/raylib/termraylib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/platform/raylib/termraylib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #include "font.h" | ||||
| #include "platform/term/term.h" | ||||
|  | ||||
| typedef struct termraylibcolormap_t { | ||||
|   termcolor_t termColor; | ||||
|   Color raylibColor; | ||||
| } termraylibcolormap_t; | ||||
|  | ||||
| typedef struct termraylib_s { | ||||
|   Color currentColor; | ||||
|   float_t x, y; | ||||
| } termraylib_t; | ||||
|  | ||||
| termraylibcolormap_t TERM_RAYLIB_COLORMAP[] = { | ||||
|   { TERM_COLOR_BLACK,   BLACK }, | ||||
|   { TERM_COLOR_RED,     RED }, | ||||
|   { TERM_COLOR_GREEN,   GREEN }, | ||||
|   { TERM_COLOR_YELLOW,  YELLOW }, | ||||
|   { TERM_COLOR_BLUE,    BLUE }, | ||||
|   { TERM_COLOR_MAGENTA, MAGENTA }, | ||||
|   { TERM_COLOR_CYAN,    SKYBLUE }, | ||||
|   { TERM_COLOR_WHITE,   WHITE } | ||||
| }; | ||||
|  | ||||
| #define TERM_RAYLIB_INITIAL_COLOR WHITE | ||||
|  | ||||
| termraylib_t TERM_RAYLIB; | ||||
|  | ||||
| void termClear() { | ||||
|   TERM_RAYLIB.currentColor = WHITE; | ||||
|   TERM_RAYLIB.x = 0.0f; | ||||
|   TERM_RAYLIB.y = 0.0f; | ||||
| } | ||||
|  | ||||
| void termFlush() { | ||||
|   // Nothing to do | ||||
| } | ||||
|  | ||||
| uint8_t termGetColumnCount() { | ||||
|   // Get Window width | ||||
|   int32_t windowWidth = GetScreenWidth(); | ||||
|   return (uint8_t)(windowWidth / FONT.charWidth); | ||||
| } | ||||
|  | ||||
| uint8_t termGetRowCount() { | ||||
|   // Get Window height | ||||
|   int32_t windowHeight = GetScreenHeight(); | ||||
|   return (uint8_t)(windowHeight / FONT.charHeight); | ||||
| } | ||||
|  | ||||
| void termPushColor(termcolor_t color) { | ||||
|   // Find the corresponding raylib color | ||||
|   size_t colors = sizeof(TERM_RAYLIB_COLORMAP) / sizeof(termraylibcolormap_t); | ||||
|   for(size_t i = 0; i < colors; i++) { | ||||
|     if(TERM_RAYLIB_COLORMAP[i].termColor == color) { | ||||
|       TERM_RAYLIB.currentColor = TERM_RAYLIB_COLORMAP[i].raylibColor; | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void termPushChar(char_t c) { | ||||
|   if(c == '\n') { | ||||
|     TERM_RAYLIB.x = 0.0f; | ||||
|     TERM_RAYLIB.y += (float_t)(FONT.charHeight + FONT_CHAR_SPACING); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   fontDraw(c, TERM_RAYLIB.x, TERM_RAYLIB.y, TERM_RAYLIB.currentColor); | ||||
|   TERM_RAYLIB.x += (float_t)(FONT.charWidth + FONT_CHAR_SPACING); | ||||
|  | ||||
|   // Wrapping | ||||
|   if(TERM_RAYLIB.x + FONT.charWidth > (float_t)GetScreenWidth()) { | ||||
|     TERM_RAYLIB.x = 0.0f; | ||||
|     TERM_RAYLIB.y += (float_t)(FONT.charHeight + FONT_CHAR_SPACING); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										15
									
								
								src/platform/term/CMakeLists.txt
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										15
									
								
								src/platform/term/CMakeLists.txt
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -3,24 +3,9 @@ | ||||
| # This software is released under the MIT License. | ||||
| # https://opensource.org/licenses/MIT | ||||
|  | ||||
| # Libraries | ||||
| find_package(Curses REQUIRED) | ||||
|  | ||||
| target_link_libraries(microrpg PRIVATE | ||||
|   ${CURSES_LIBRARIES} | ||||
| ) | ||||
| target_include_directories(microrpg PRIVATE | ||||
|   ${CURSES_INCLUDE_DIR} | ||||
| ) | ||||
| target_link_libraries(microrpg PRIVATE | ||||
|   ${CURSES_LIBRARIES} | ||||
| ) | ||||
|  | ||||
| # Sources | ||||
| target_sources(microrpg PRIVATE | ||||
|   term.c | ||||
|   inputterm.c | ||||
|   platform.c | ||||
| ) | ||||
|  | ||||
| # Compiler flags | ||||
|   | ||||
							
								
								
									
										108
									
								
								src/platform/term/term.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										108
									
								
								src/platform/term/term.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -6,107 +6,11 @@ | ||||
|  */ | ||||
|  | ||||
| #include "term.h" | ||||
| #include "input.h" | ||||
| #include <time.h> | ||||
|  | ||||
| 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); | ||||
| } | ||||
| #include "game.h" | ||||
|  | ||||
| void termDrawOverworld() { | ||||
|   // Draw map. | ||||
|  | ||||
|   // Draw entities. | ||||
|   attron(COLOR_PAIR(1)); | ||||
|   termDrawEntity(&GAME.player); | ||||
|  | ||||
|   entity_t *start = GAME.overworld.map.entities; | ||||
| @@ -138,9 +42,13 @@ void termDrawEntity(const entity_t *ent) { | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   mvaddch(ent->position.y, ent->position.x, c); | ||||
|   termPushChar | ||||
| } | ||||
|  | ||||
| void termDispose() { | ||||
|   endwin(); | ||||
| void termDraw() { | ||||
|   termClear(); | ||||
|  | ||||
|  | ||||
|  | ||||
|   termFlush(); | ||||
| } | ||||
							
								
								
									
										70
									
								
								src/platform/term/term.h
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										70
									
								
								src/platform/term/term.h
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -7,45 +7,51 @@ | ||||
|  | ||||
| #pragma once | ||||
| #include "microrpg.h" | ||||
| #include "game.h" | ||||
| #include <ncurses.h> | ||||
| #include "termcolor.h" | ||||
|  | ||||
| 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. | ||||
| /** | ||||
|  * Common implementation to draw the game state to the terminal (whatever the | ||||
|  * terminal implementation by the platform is). | ||||
|  */ | ||||
| void termDraw(); | ||||
|  | ||||
| /**  | ||||
|  * Draw the overworld scene. | ||||
| /** | ||||
|  * Clears the terminal screen and resets the cursor to the top left. Called at | ||||
|  * the start of each "frame". Should also reset any terminal state (e.g. color). | ||||
|  */ | ||||
| void termDrawOverworld(); | ||||
| void termClear(); | ||||
|  | ||||
| /**  | ||||
|  * Draw an entity to the terminal. | ||||
| /** | ||||
|  * Flushes any pending terminal operations. Called at the end of each "frame". | ||||
|  */ | ||||
| void termFlush(); | ||||
|  | ||||
| /** | ||||
|  * Get the number of columns in the terminal. | ||||
|  *  | ||||
|  * @param ent The entity to draw. | ||||
|  * @return The number of columns. | ||||
|  */ | ||||
| void termDrawEntity(const entity_t *ent); | ||||
| uint8_t termGetColumnCount(); | ||||
|  | ||||
| /**  | ||||
|  * Dispose of the terminal subsystem. | ||||
| /** | ||||
|  * Get the number of rows in the terminal. | ||||
|  *  | ||||
|  * @return The number of rows. | ||||
|  */ | ||||
| void termDispose(); | ||||
| uint8_t termGetRowCount(); | ||||
|  | ||||
| /** | ||||
|  * Push a color onto the terminal color stack. | ||||
|  *  | ||||
|  * @param color The color to push. | ||||
|  */ | ||||
| void termPushColor(termcolor_t color); | ||||
|  | ||||
| /** | ||||
|  * Push a character onto the terminal at the current cursor position. This will | ||||
|  * also advance the cursor position by 1 character. Wrapping will be handled by | ||||
|  * the terminal implementation. | ||||
|  *  | ||||
|  * @param c The character to push. | ||||
|  */ | ||||
| void termPushChar(char_t c); | ||||
							
								
								
									
										20
									
								
								src/platform/term/termcolor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/platform/term/termcolor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include "microrpg.h" | ||||
|  | ||||
| #define TERM_COLOR_WHITE   0 | ||||
| #define TERM_COLOR_RED     1 | ||||
| #define TERM_COLOR_GREEN   2 | ||||
| #define TERM_COLOR_YELLOW  3 | ||||
| #define TERM_COLOR_BLUE    4 | ||||
| #define TERM_COLOR_MAGENTA 5 | ||||
| #define TERM_COLOR_CYAN    6 | ||||
| #define TERM_COLOR_BLACK   7 | ||||
|  | ||||
| typedef uint8_t termcolor_t; | ||||
							
								
								
									
										29
									
								
								src/platform/tty/CMakeLists.txt
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										29
									
								
								src/platform/tty/CMakeLists.txt
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| # Copyright (c) 2025 Dominic Masters | ||||
| #  | ||||
| # This software is released under the MIT License. | ||||
| # https://opensource.org/licenses/MIT | ||||
|  | ||||
| # Libraries | ||||
| find_package(Curses REQUIRED) | ||||
|  | ||||
| target_link_libraries(microrpg PRIVATE | ||||
|   ${CURSES_LIBRARIES} | ||||
| ) | ||||
| target_include_directories(microrpg PRIVATE | ||||
|   ${CURSES_INCLUDE_DIR} | ||||
| ) | ||||
| target_link_libraries(microrpg PRIVATE | ||||
|   ${CURSES_LIBRARIES} | ||||
| ) | ||||
|  | ||||
| # Sources | ||||
| target_sources(microrpg PRIVATE | ||||
|   term.c | ||||
|   inputterm.c | ||||
|   platform.c | ||||
| ) | ||||
|  | ||||
| # Compiler flags | ||||
| target_compile_definitions(microrpg PRIVATE | ||||
|   RPG_TERM=1 | ||||
| ) | ||||
							
								
								
									
										146
									
								
								src/platform/tty/term.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										146
									
								
								src/platform/tty/term.c
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| /** | ||||
|  * 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 <time.h> | ||||
|  | ||||
| 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(); | ||||
| } | ||||
							
								
								
									
										51
									
								
								src/platform/tty/term.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										51
									
								
								src/platform/tty/term.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| /** | ||||
|  * 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 <ncurses.h> | ||||
|  | ||||
| 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(); | ||||
		Reference in New Issue
	
	Block a user