Input test.
This commit is contained in:
@ -19,6 +19,7 @@ target_include_directories(${DUSK_TARGET_NAME}
|
|||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
main.c
|
main.c
|
||||||
|
input.c
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
|
22
src/display/color.h
Normal file
22
src/display/color.h
Normal file
@ -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 "dusk.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
COLOR_BLACK,
|
||||||
|
COLOR_RED,
|
||||||
|
COLOR_GREEN,
|
||||||
|
COLOR_YELLOW,
|
||||||
|
COLOR_BLUE,
|
||||||
|
COLOR_MAGENTA,
|
||||||
|
COLOR_CYAN,
|
||||||
|
COLOR_WHITE,
|
||||||
|
} color_t;
|
||||||
|
|
||||||
|
#define COLOR_COUNT (COLOR_WHITE + 1)
|
@ -5,20 +5,64 @@
|
|||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "assert/assert.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "term.h"
|
#include "term.h"
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
void renderInit() {
|
void renderInit() {
|
||||||
termInit();
|
termInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderUpdate() {
|
void renderUpdate() {
|
||||||
|
char_t c;
|
||||||
|
color_t color, colorCurrent;
|
||||||
|
entity_t *ent;
|
||||||
|
|
||||||
termUpdate();
|
termUpdate();
|
||||||
termClear();
|
termClear();
|
||||||
|
|
||||||
|
colorCurrent = COLOR_WHITE;
|
||||||
|
termPushColor(colorCurrent);
|
||||||
|
|
||||||
for(int32_t y = 0; y < TERM.height; y++) {
|
for(int32_t y = 0; y < TERM.height; y++) {
|
||||||
for(int32_t x = 0; x < TERM.width; x++) {
|
for(int32_t x = 0; x < TERM.width; x++) {
|
||||||
char_t c = ' '; // Replace with actual rendering logic
|
color = COLOR_WHITE;
|
||||||
|
c = ' ';
|
||||||
|
|
||||||
|
ent = entityGetAt(x, y);
|
||||||
|
if(ent) {
|
||||||
|
color = COLOR_RED;
|
||||||
|
|
||||||
|
switch(ent->dir) {
|
||||||
|
case ENTITY_DIR_UP:
|
||||||
|
c = '^';
|
||||||
|
color = COLOR_GREEN;
|
||||||
|
break;
|
||||||
|
case ENTITY_DIR_DOWN:
|
||||||
|
c = 'v';
|
||||||
|
color = COLOR_RED;
|
||||||
|
break;
|
||||||
|
case ENTITY_DIR_LEFT:
|
||||||
|
c = '<';
|
||||||
|
color = COLOR_YELLOW;
|
||||||
|
break;
|
||||||
|
case ENTITY_DIR_RIGHT:
|
||||||
|
c = '>';
|
||||||
|
color = COLOR_BLUE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assertUnreachable("Invalid entity direction");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == ' ') {
|
||||||
|
termPushChar(' ');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(color != colorCurrent) termPushColor((colorCurrent = color));
|
||||||
termPushChar(c);
|
termPushChar(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
|
||||||
extern int32_t renderColumnCount;
|
|
||||||
extern int32_t renderRowCount;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init the render system.
|
* Init the render system.
|
||||||
*/
|
*/
|
||||||
|
@ -5,12 +5,24 @@
|
|||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "assert/assert.h"
|
||||||
#include "term.h"
|
#include "term.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
term_t TERM;
|
term_t TERM;
|
||||||
|
|
||||||
|
const char_t* TERM_COLORS[COLOR_COUNT] = {
|
||||||
|
[COLOR_BLACK] = "\033[30m",
|
||||||
|
[COLOR_RED] = "\033[31m",
|
||||||
|
[COLOR_GREEN] = "\033[32m",
|
||||||
|
[COLOR_YELLOW] = "\033[33m",
|
||||||
|
[COLOR_BLUE] = "\033[34m",
|
||||||
|
[COLOR_MAGENTA] = "\033[35m",
|
||||||
|
[COLOR_CYAN] = "\033[36m",
|
||||||
|
[COLOR_WHITE] = "\033[37m"
|
||||||
|
};
|
||||||
|
|
||||||
void termInit() {
|
void termInit() {
|
||||||
memoryZero(&TERM, sizeof(term_t));
|
memoryZero(&TERM, sizeof(term_t));
|
||||||
|
|
||||||
@ -33,6 +45,11 @@ void termClear() {
|
|||||||
printf("\033[2J\033[H");
|
printf("\033[2J\033[H");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void termPushColor(const color_t color) {
|
||||||
|
assertTrue(color < COLOR_COUNT, "Invalid color index");
|
||||||
|
printf("%s", TERM_COLORS[color]);
|
||||||
|
}
|
||||||
|
|
||||||
void termPushChar(const char_t c) {
|
void termPushChar(const char_t c) {
|
||||||
putchar(c);
|
putchar(c);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t width;
|
int32_t width;
|
||||||
@ -15,6 +16,8 @@ typedef struct {
|
|||||||
|
|
||||||
extern term_t TERM;
|
extern term_t TERM;
|
||||||
|
|
||||||
|
extern const char_t* TERM_COLORS[COLOR_COUNT];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the terminal system.
|
* Initialize the terminal system.
|
||||||
*/
|
*/
|
||||||
@ -30,6 +33,8 @@ void termUpdate();
|
|||||||
*/
|
*/
|
||||||
void termClear();
|
void termClear();
|
||||||
|
|
||||||
|
void termPushColor(const color_t color);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push a character to the terminal output buffer.
|
* Push a character to the terminal output buffer.
|
||||||
*
|
*
|
||||||
|
92
src/input.c
92
src/input.c
@ -8,14 +8,96 @@
|
|||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
|
#include <fcntl.h>
|
||||||
#define INPUT_BIND_MAP_COUNT (sizeof(INPUT_BIND_MAPS) / sizeof(inputbindmap_t))
|
|
||||||
|
|
||||||
input_t INPUT;
|
input_t INPUT;
|
||||||
|
|
||||||
void inputInit(void) {
|
inputmap_t INPUT_MAP[] = {
|
||||||
memoryZero(&INPUT, sizeof(input_t));
|
{"w", INPUT_UP},
|
||||||
|
{"s", INPUT_DOWN},
|
||||||
|
{"a", INPUT_LEFT},
|
||||||
|
{"d", INPUT_RIGHT},
|
||||||
|
{" ", INPUT_ACTION},
|
||||||
|
{"e", INPUT_ACTION},
|
||||||
|
{"\n", INPUT_ACTION}, // Enter key
|
||||||
|
{"\x1B", INPUT_CANCEL}, // Escape key
|
||||||
|
{"\033[A", INPUT_UP}, // Up arrow
|
||||||
|
{"\033[B", INPUT_DOWN}, // Down arrow
|
||||||
|
{"\033[C", INPUT_RIGHT}, // Right arrow
|
||||||
|
{"\033[D", INPUT_LEFT}, // Left arrow
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
void inputEnableRawMode() {
|
||||||
|
if (INPUT.isRawMode) return;
|
||||||
|
tcgetattr(STDIN_FILENO, &INPUT.origin);
|
||||||
|
struct termios raw = INPUT.origin;
|
||||||
|
raw.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &raw);
|
||||||
|
INPUT.isRawMode = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void inputUpdate(void) {
|
void inputDisableRawMode() {
|
||||||
|
if(!INPUT.isRawMode) return;
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &INPUT.origin);
|
||||||
|
INPUT.isRawMode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inputSetNonBlocking() {
|
||||||
|
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
|
||||||
|
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inputInit() {
|
||||||
|
memoryZero(&INPUT, sizeof(input_t));
|
||||||
|
|
||||||
|
inputEnableRawMode();
|
||||||
|
inputSetNonBlocking();
|
||||||
|
}
|
||||||
|
|
||||||
|
void inputUpdate() {
|
||||||
|
char_t buffer[INPUT_BUFFER_SIZE];
|
||||||
|
ssize_t n = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
|
||||||
|
|
||||||
|
if(n < 0) {
|
||||||
|
INPUT.previous = INPUT.current;
|
||||||
|
INPUT.current = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(n < INPUT_BUFFER_SIZE, "Input buffer overflow");
|
||||||
|
|
||||||
|
uint8_t val = 0;
|
||||||
|
inputmap_t *map;
|
||||||
|
uint8_t len;
|
||||||
|
char_t *c;
|
||||||
|
for(uint8_t i = 0; i < n; i++) {
|
||||||
|
map = INPUT_MAP;
|
||||||
|
|
||||||
|
do {
|
||||||
|
len = strlen(map->key);
|
||||||
|
|
||||||
|
if(i + len > n) {
|
||||||
|
map++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = buffer + i;
|
||||||
|
if(memoryCompare(c, map->key, len) != 0) {
|
||||||
|
map++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button was pressed
|
||||||
|
val |= map->value;
|
||||||
|
i += len - 1;
|
||||||
|
} while(map->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
INPUT.previous = INPUT.current;
|
||||||
|
INPUT.current = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void inputDispose() {
|
||||||
|
inputDisableRawMode();
|
||||||
}
|
}
|
26
src/input.h
26
src/input.h
@ -7,19 +7,39 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#define INPUT_UP (1 << 0)
|
||||||
|
#define INPUT_DOWN (1 << 1)
|
||||||
|
#define INPUT_LEFT (1 << 2)
|
||||||
|
#define INPUT_RIGHT (1 << 3)
|
||||||
|
#define INPUT_ACTION (1 << 4)
|
||||||
|
#define INPUT_CANCEL (1 << 5)
|
||||||
|
|
||||||
|
#define INPUT_BUFFER_SIZE 128
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char_t input;
|
uint8_t current;
|
||||||
|
uint8_t previous;
|
||||||
|
bool_t isRawMode;
|
||||||
|
struct termios origin;
|
||||||
} input_t;
|
} input_t;
|
||||||
|
|
||||||
extern input_t INPUT;
|
extern input_t INPUT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_t *key;
|
||||||
|
uint8_t value;
|
||||||
|
} inputmap_t;
|
||||||
|
|
||||||
|
extern inputmap_t INPUT_MAP[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the input system.
|
* Initializes the input system.
|
||||||
*/
|
*/
|
||||||
void inputInit(void);
|
void inputInit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the input system.
|
* Updates the input system.
|
||||||
*/
|
*/
|
||||||
void inputUpdate(void);
|
void inputUpdate();
|
20
src/main.c
20
src/main.c
@ -5,14 +5,34 @@
|
|||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "input.h"
|
||||||
#include "util/random.h"
|
#include "util/random.h"
|
||||||
#include "display/render.h"
|
#include "display/render.h"
|
||||||
|
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
int32_t main(const int32_t argc, const char **argv) {
|
int32_t main(const int32_t argc, const char **argv) {
|
||||||
|
inputInit();
|
||||||
randomInit();
|
randomInit();
|
||||||
renderInit();
|
renderInit();
|
||||||
|
|
||||||
|
entity_t *ent;
|
||||||
|
|
||||||
|
ent = &ENTITIES[0];
|
||||||
|
entityInit(ent, ENTITY_TYPE_PLAYER);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
|
inputUpdate();
|
||||||
|
|
||||||
|
ent = ENTITIES;
|
||||||
|
do {
|
||||||
|
if(ent->type == ENTITY_TYPE_NULL) {
|
||||||
|
ent++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entityUpdate(ent++);
|
||||||
|
} while(ent < (ENTITIES + ENTITY_COUNT));
|
||||||
|
|
||||||
renderUpdate();
|
renderUpdate();
|
||||||
usleep(100 * 1000); // Sleep for 16 milliseconds (60 FPS)
|
usleep(100 * 1000); // Sleep for 16 milliseconds (60 FPS)
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,5 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(item)
|
add_subdirectory(item)
|
||||||
add_subdirectory(quest)
|
add_subdirectory(quest)
|
||||||
|
add_subdirectory(world)
|
@ -12,12 +12,11 @@
|
|||||||
entity_t ENTITIES[ENTITY_COUNT];
|
entity_t ENTITIES[ENTITY_COUNT];
|
||||||
|
|
||||||
entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
||||||
[ENTITY_TYPE_NULL] = {
|
[ENTITY_TYPE_NULL] = { 0 },
|
||||||
.init = NULL
|
|
||||||
},
|
|
||||||
|
|
||||||
[ENTITY_TYPE_PLAYER] = {
|
[ENTITY_TYPE_PLAYER] = {
|
||||||
.init = playerInit
|
.init = playerInit,
|
||||||
|
.update = playerUpdate
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,4 +30,23 @@ void entityInit(entity_t *entity, const entitytype_t type) {
|
|||||||
memoryZero(entity, sizeof(entity_t));
|
memoryZero(entity, sizeof(entity_t));
|
||||||
entity->type = type;
|
entity->type = type;
|
||||||
ENTITY_CALLBACKS[type].init(entity);
|
ENTITY_CALLBACKS[type].init(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void entityUpdate(entity_t *entity) {
|
||||||
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
|
assertNotNull(
|
||||||
|
ENTITY_CALLBACKS[entity->type].update,
|
||||||
|
"Entity type has no update"
|
||||||
|
);
|
||||||
|
|
||||||
|
ENTITY_CALLBACKS[entity->type].update(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity_t * entityGetAt(const uint8_t x, const uint8_t y) {
|
||||||
|
entity_t *e = ENTITIES;
|
||||||
|
do {
|
||||||
|
if(e->type && e->x == x && e->y == y) return e;
|
||||||
|
e++;
|
||||||
|
} while(e < (ENTITIES + ENTITY_COUNT));
|
||||||
|
return NULL;
|
||||||
}
|
}
|
@ -43,6 +43,7 @@ extern entity_t ENTITIES[ENTITY_COUNT];
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (*init)(entity_t *entity);
|
void (*init)(entity_t *entity);
|
||||||
|
void (*update)(entity_t *entity);
|
||||||
} entitycallbacks_t;
|
} entitycallbacks_t;
|
||||||
extern entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
extern entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
||||||
|
|
||||||
@ -52,4 +53,20 @@ extern entitycallbacks_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
|
|||||||
* @param entity Pointer to the entity to initialize.
|
* @param entity Pointer to the entity to initialize.
|
||||||
* @param type The type of the entity to initialize.
|
* @param type The type of the entity to initialize.
|
||||||
*/
|
*/
|
||||||
void entityInit(entity_t *entity, const entitytype_t type);
|
void entityInit(entity_t *entity, const entitytype_t type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the entity's state based on its type.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void entityUpdate(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the entity at a given position.
|
||||||
|
*
|
||||||
|
* @param x The x-coordinate of the entity.
|
||||||
|
* @param y The y-coordinate of the entity.
|
||||||
|
* @return Pointer to the entity at the specified position, or NULL.
|
||||||
|
*/
|
||||||
|
entity_t * entityGetAt(const uint8_t x, const uint8_t y);
|
@ -11,4 +11,9 @@
|
|||||||
void playerInit(entity_t *player) {
|
void playerInit(entity_t *player) {
|
||||||
assertNotNull(player, "Player entity is NULL");
|
assertNotNull(player, "Player entity is NULL");
|
||||||
assertTrue(player->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
|
assertTrue(player->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
|
||||||
|
}
|
||||||
|
|
||||||
|
void playerUpdate(entity_t *entity) {
|
||||||
|
assertNotNull(entity, "Entity is NULL");
|
||||||
|
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity is not a player");
|
||||||
}
|
}
|
@ -19,4 +19,11 @@ typedef struct {
|
|||||||
*
|
*
|
||||||
* @param ent Pointer to the player entity to initialize.
|
* @param ent Pointer to the player entity to initialize.
|
||||||
*/
|
*/
|
||||||
void playerInit(entity_t *ent);
|
void playerInit(entity_t *ent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a player entity.
|
||||||
|
*
|
||||||
|
* @param ent Entity to update.
|
||||||
|
*/
|
||||||
|
void playerUpdate(entity_t *ent);
|
10
src/rpg/world/CMakeLists.txt
Normal file
10
src/rpg/world/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
|
PRIVATE
|
||||||
|
map.c
|
||||||
|
)
|
0
src/rpg/world/map.c
Normal file
0
src/rpg/world/map.c
Normal file
16
src/rpg/world/map.h
Normal file
16
src/rpg/world/map.h
Normal 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 "dusk.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t width;
|
||||||
|
uint8_t height;
|
||||||
|
} map_t;
|
||||||
|
|
||||||
|
extern map_t MAP;
|
Reference in New Issue
Block a user