Moved the platform stuff around

This commit is contained in:
2025-10-28 07:52:05 -05:00
parent f1db7de87c
commit 358fa9a493
25 changed files with 456 additions and 46 deletions

View File

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

27
src/cutscene/cutscene.h Executable file
View File

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

13
src/cutscene/testcutscene.h Executable file
View File

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

View File

@@ -5,6 +5,8 @@
# Sources
target_sources(microrpg PRIVATE
direction.c
entity.c
player.c
sign.c
)

32
src/entity/direction.c Executable file
View File

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

View File

@@ -19,3 +19,12 @@
#define DIRECTION_WEST DIRECTION_LEFT
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);

View File

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

View File

@@ -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;
/**
@@ -50,3 +55,10 @@ void entityTurn(entity_t *entity, const direction_t direction);
* @param direction The direction to walk in.
*/
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);

View File

@@ -10,5 +10,6 @@
#define ENTITY_TYPE_NULL 0
#define ENTITY_TYPE_PLAYER 1
#define ENTITY_TYPE_SIGN 2
typedef uint8_t entitytype_t;

View File

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

13
src/entity/sign.c Executable file
View File

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

22
src/entity/sign.h Executable file
View 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 "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);

View File

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

View File

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

38
src/gametime.c Executable file
View File

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

36
src/gametime.h Executable file
View File

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

View File

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

7
src/platform/CMakeLists.txt Executable file
View File

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

29
src/platform/platform.h Normal file
View File

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

View File

@@ -20,6 +20,7 @@ target_link_libraries(microrpg PRIVATE
target_sources(microrpg PRIVATE
term.c
inputterm.c
platform.c
)
# Compiler flags

View File

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

View File

@@ -7,6 +7,7 @@
#include "term.h"
#include "input.h"
#include <time.h>
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() {

View File

@@ -33,6 +33,11 @@ void termUpdate();
*/
void termDraw();
/**
* Draw the overworld scene.
*/
void termDrawOverworld();
/**
* Draw an entity to the terminal.
*

View File

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