Make TTY and Raylib identical

This commit is contained in:
2025-10-28 21:07:56 -05:00
parent b6aab03370
commit 300cd7b811
15 changed files with 364 additions and 254 deletions

View File

@@ -3,6 +3,6 @@
# 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
add_subdirectory(raylib) # add_subdirectory(raylib)
add_subdirectory(term) add_subdirectory(term)
#add_subdirectory(term) add_subdirectory(tty)

View File

@@ -6,19 +6,20 @@
*/ */
#include "input.h" #include "input.h"
#include "platformraylib.h"
bool_t inputDown(const uint8_t action) { bool_t inputDown(const uint8_t action) {
return false; return (PLATFORM_RAYLIB.inputCurrent & action) != 0;
} }
bool_t inputUp(const uint8_t action) { bool_t inputUp(const uint8_t action) {
return true; return (PLATFORM_RAYLIB.inputCurrent & action) == 0;
} }
bool_t inputWasDown(const uint8_t action) { bool_t inputWasDown(const uint8_t action) {
return false; return (PLATFORM_RAYLIB.inputPrevious & action) != 0;
} }
bool_t inputWasUp(const uint8_t action) { bool_t inputWasUp(const uint8_t action) {
return true; return (PLATFORM_RAYLIB.inputPrevious & action) == 0;
} }

View File

@@ -6,18 +6,61 @@
*/ */
#include "platform/platform.h" #include "platform/platform.h"
#include "platform/raylib/platformraylib.h"
#include <raylib.h> #include <raylib.h>
#include "font.h" #include "font.h"
#include "platform/term/term.h" #include "platform/term/term.h"
#include "input.h"
#include "game.h"
#include "gametime.h"
#include <time.h>
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) { uint8_t platformInit(void) {
memset(&PLATFORM_RAYLIB, 0, sizeof(platformraylib_t));
// Init raylib.
InitWindow(800, 600, "Micro JRPG"); InitWindow(800, 600, "Micro JRPG");
SetTargetFPS(60); SetTargetFPS(60);
// Load font
if(fontInit() != 0) return PLATFORM_ERROR; if(fontInit() != 0) return PLATFORM_ERROR;
// Resize window
SetWindowSize(40 * FONT.charWidth, 30 * FONT.charHeight); 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; return PLATFORM_OK;
} }
@@ -25,7 +68,18 @@ uint8_t platformUpdate() {
if(WindowShouldClose()) { if(WindowShouldClose()) {
return PLATFORM_EXIT; 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; return PLATFORM_OK;
} }

View File

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

View File

@@ -6,6 +6,7 @@
# Sources # Sources
target_sources(microrpg PRIVATE target_sources(microrpg PRIVATE
term.c term.c
termoverworld.c
) )
# Compiler flags # Compiler flags

View File

@@ -6,49 +6,50 @@
*/ */
#include "term.h" #include "term.h"
#include "game.h" #include "termoverworld.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
}
void termDraw() { void termDraw() {
termClear(); 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(); termFlush();
} }

View File

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

View File

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

View File

@@ -18,12 +18,12 @@ target_link_libraries(microrpg PRIVATE
# Sources # Sources
target_sources(microrpg PRIVATE target_sources(microrpg PRIVATE
term.c
inputterm.c inputterm.c
platform.c platform.c
termtty.c
) )
# Compiler flags # Compiler flags
target_compile_definitions(microrpg PRIVATE target_compile_definitions(microrpg PRIVATE
RPG_TERM=1 RPG_TTY=1
) )

View File

@@ -5,20 +5,20 @@
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#include "term.h" #include "platformtty.h"
bool_t inputDown(const uint8_t action) { 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) { 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) { 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) { bool_t inputWasUp(const uint8_t action) {
return (TERM.inputPrevious & action) == 0; return (PLATFORM_TTY.inputPrevious & action) == 0;
} }

View File

@@ -7,19 +7,109 @@
#include "platform/platform.h" #include "platform/platform.h"
#include "platform/term/term.h" #include "platform/term/term.h"
#include "platform/tty/platformtty.h"
#include "input.h"
#include <time.h>
void platformInit() { typedef struct ttyinputmap_s {
termInit(); 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() { uint8_t platformUpdate() {
termUpdate(); 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() { void platformDraw() {
clear();
termDraw(); termDraw();
attroff(COLOR_PAIR(1));
refresh();
// 16ms delay (60FPS)
napms(16);
} }
void platformDispose() { void platformDispose() {
termDispose(); endwin();
} }

19
src/platform/tty/platformtty.h Executable file
View File

@@ -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 <ncurses.h>
typedef struct platformtty_s {
uint8_t inputCurrent;
uint8_t inputPrevious;
int lastch;
} platformtty_t;
extern platformtty_t PLATFORM_TTY;

View File

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

View File

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

View File

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