Initial commit.

This commit is contained in:
2025-10-26 15:42:34 -05:00
commit f1db7de87c
29 changed files with 781 additions and 0 deletions

33
.gitignore vendored Executable file
View File

@@ -0,0 +1,33 @@
# C/C++ build artifacts
*.o
*.obj
*.so
*.dll
*.exe
*.out
*.a
*.lib
*.lo
*.dep
# CMake files
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
Makefile
CMakeLists.txt.user
CMakeConfigureLog.yaml
# Editor files
*.swp
*.swo
*~
.vscode/
.idea/
# OS files
.DS_Store
Thumbs.db
# CMake build directories
build/

19
CMakeLists.txt 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
cmake_minimum_required(VERSION 3.13)
project(microrpg
VERSION 0.1.0
DESCRIPTION "A tiny JRPG game written in C"
LANGUAGES C
)
set(CMAKE_C_STANDARD 99)
# Executable
add_executable(microrpg)
# Add sources
add_subdirectory(src)

2
README.md Executable file
View File

@@ -0,0 +1,2 @@
# Micro JRPG
Small JRPG game/engine that is designed to run on "just about" anything.

21
src/CMakeLists.txt Executable file
View File

@@ -0,0 +1,21 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Include directories
target_include_directories(microrpg PRIVATE ${CMAKE_CURRENT_LIST_DIR})
# Sources
target_sources(microrpg PRIVATE
main.c
game.c
input.c
)
# Subdirs
add_subdirectory(entity)
add_subdirectory(scene)
add_subdirectory(world)
add_subdirectory(term)

10
src/entity/CMakeLists.txt Executable file
View 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(microrpg PRIVATE
entity.c
player.c
)

21
src/entity/direction.h Executable file
View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "microrpg.h"
#define DIRECTION_UP 0
#define DIRECTION_RIGHT 1
#define DIRECTION_DOWN 2
#define DIRECTION_LEFT 3
#define DIRECTION_NORTH DIRECTION_UP
#define DIRECTION_EAST DIRECTION_RIGHT
#define DIRECTION_SOUTH DIRECTION_DOWN
#define DIRECTION_WEST DIRECTION_LEFT
typedef uint8_t direction_t;

48
src/entity/entity.c Executable file
View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
void entityInit(entity_t *entity, const entitytype_t type) {
memset(entity, 0, sizeof(entity_t));
entity->type = type;
}
void entityTick(entity_t *entity) {
if(entity->type == ENTITY_TYPE_NULL) return;
// Let entities move (if they do)
if(entity->type == ENTITY_TYPE_PLAYER) {
playerTickInput(entity);
}
}
void entityTurn(entity_t *entity, const direction_t direction) {
// TODO: animation/delay.
entity->direction = direction;
}
void entityWalk(entity_t *entity, const direction_t direction) {
// TODO: Animation, delay, colission, result, etc.
entity->direction = direction;
switch(direction) {
case DIRECTION_NORTH:
entity->position.y--;
break;
case DIRECTION_EAST:
entity->position.x++;
break;
case DIRECTION_SOUTH:
entity->position.y++;
break;
case DIRECTION_WEST:
entity->position.x--;
break;
}
}

52
src/entity/entity.h Executable file
View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "entity/direction.h"
#include "entity/entitytype.h"
#include "player.h"
typedef struct entity_s {
struct {
// Relative to top-left of the map
uint8_t x, y;
} position;
direction_t direction;
entitytype_t type;
} entity_t;
/**
* Initialize an entity.
*
* @param entity Pointer to the entity to initialize.
* @param type The type of the entity.
*/
void entityInit(entity_t *entity, const entitytype_t type);
/**
* Update an entity for one tick.
*
* @param entity Pointer to the entity to update.
*/
void entityTick(entity_t *entity);
/**
* Turn an entity to face a new direction.
*
* @param entity Pointer to the entity to turn.
* @param direction The direction to face.
*/
void entityTurn(entity_t *entity, const direction_t direction);
/**
* Make an entity walk in a direction.
*
* @param entity Pointer to the entity to make walk.
* @param direction The direction to walk in.
*/
void entityWalk(entity_t *entity, const direction_t direction);

14
src/entity/entitytype.h Executable file
View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "microrpg.h"
#define ENTITY_TYPE_NULL 0
#define ENTITY_TYPE_PLAYER 1
typedef uint8_t entitytype_t;

21
src/entity/player.c Executable file
View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
#include "input.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);
}
}

18
src/entity/player.h Executable file
View File

@@ -0,0 +1,18 @@
/**
* 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;
/**
* Ticks player input logic.
*
* @param entity Pointer to the player entity.
*/
void playerTickInput(entity_t *entity);

22
src/game.c 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
*/
#include "game.h"
game_t GAME;
void gameInit() {
memset(&GAME, 0, sizeof(GAME));
entityInit(&GAME.player, ENTITY_TYPE_PLAYER);
GAME.scene = SCENE_OVERWORLD;
}
void gameTick() {
sceneTick();
}

37
src/game.h Executable file
View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "entity/entity.h"
#include "world/map.h"
#include "scene/scene.h"
#define GAME_SCENE_INITIAL 0
#define GAME_SCENE_OVERWORLD 1
typedef struct game_s {
scene_t scene;
entity_t player;
union {
struct {
map_t map;
} overworld;
};
} game_t;
extern game_t GAME;
/**
* Initialize the game state.
*/
void gameInit();
/**
* Advance the game state by one tick. Should be called at 60Hz.
*/
void gameTick();

16
src/input.c Executable file
View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
bool_t inputPressed(const uint8_t action) {
return inputDown(action) && inputWasUp(action);
}
bool_t inputReleased(const uint8_t action) {
return inputUp(action) && inputWasDown(action);
}

66
src/input.h Executable file
View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "microrpg.h"
#define INPUT_ACTION_UP (1 << 0)
#define INPUT_ACTION_DOWN (1 << 1)
#define INPUT_ACTION_LEFT (1 << 2)
#define INPUT_ACTION_RIGHT (1 << 3)
#define INPUT_ACTION_A (1 << 4)
#define INPUT_ACTION_B (1 << 5)
#define INPUT_ACTION_START (1 << 6)
#define INPUT_ACTION_SELECT (1 << 7)
/**
* Check if an input action is currently held down.
*
* @param action The action to check.
* @return true if the action is held down, false otherwise.
*/
bool_t inputDown(const uint8_t action);
/**
* Check if an input action is not currently held down.
*
* @param action The action to check.
* @return true if the action is not held down, false otherwise.
*/
bool_t inputUp(const uint8_t action);
/**
* Check if an input action was held down in the previous tick.
*
* @param action The action to check.
* @return true if the action was down last tick, false otherwise.
*/
bool_t inputWasDown(const uint8_t action);
/**
* Check if an input action was not held down in the previous tick.
*
* @param action The action to check.
* @return true if the action was up last tick, false otherwise.
*/
bool_t inputWasUp(const uint8_t action);
/**
* Check if an input action was just pressed this tick.
*
* @param action The action to check.
* @return true if the action was just pressed, false otherwise.
*/
bool_t inputPressed(const uint8_t action);
/**
* Check if an input action was just released this tick.
*
* @param action The action to check.
* @return true if the action was just released, false otherwise.
*/
bool_t inputReleased(const uint8_t action);

37
src/main.c Executable file
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 "game.h"
#if RPG_TERM == 1
#include "term/term.h"
#endif
int main(int argc, char** argv) {
gameInit();
#if RPG_TERM == 1
termInit();
#endif
while(1) {
#if RPG_TERM == 1
termUpdate();
#endif
gameTick();
#if RPG_TERM == 1
termDraw();
#endif
}
#if RPG_TERM == 1
termDispose();
#endif
return 0;
}

15
src/microrpg.h Executable file
View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
typedef bool bool_t;

9
src/scene/CMakeLists.txt Executable file
View File

@@ -0,0 +1,9 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(microrpg PRIVATE
scene.c
)

23
src/scene/scene.c Executable file
View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scene.h"
#include "game.h"
void sceneTick() {
switch(GAME.scene) {
case SCENE_INITIAL: {
break;
}
case SCENE_OVERWORLD: {
mapTick(&GAME.overworld.map);
entityTick(&GAME.player);
break;
}
}
}

16
src/scene/scene.h Executable file
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"
#define SCENE_INITIAL 0
#define SCENE_OVERWORLD 1
typedef uint8_t scene_t;
void sceneTick();

28
src/term/CMakeLists.txt Executable file
View File

@@ -0,0 +1,28 @@
# 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
)
# Compiler flags
target_compile_definitions(microrpg PRIVATE
RPG_TERM=1
)

24
src/term/inputterm.c Executable file
View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "term.h"
bool_t inputDown(const uint8_t action) {
return (TERM.inputCurrent & action) != 0;
}
bool_t inputUp(const uint8_t action) {
return (TERM.inputCurrent & action) == 0;
}
bool_t inputWasDown(const uint8_t action) {
return (TERM.inputPrevious & action) != 0;
}
bool_t inputWasUp(const uint8_t action) {
return (TERM.inputPrevious & action) == 0;
}

95
src/term/term.c Executable file
View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "term.h"
#include "input.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);
}
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();
attron(COLOR_PAIR(1));
termDrawEntity(&GAME.player);
attroff(COLOR_PAIR(1));
refresh();
// 16ms delay (60FPS)
napms(16);
}
void termDrawEntity(const entity_t *ent) {
// Placeholder: Draw entity at its position
mvaddch(ent->position.y, ent->position.x, '@');
}
void termDispose() {
endwin();
}

46
src/term/term.h Executable file
View File

@@ -0,0 +1,46 @@
/**
* 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 an entity to the terminal.
*
* @param ent The entity to draw.
*/
void termDrawEntity(const entity_t *ent);
/**
* Dispose of the terminal subsystem.
*/
void termDispose();

9
src/world/CMakeLists.txt Executable file
View File

@@ -0,0 +1,9 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(microrpg PRIVATE
map.c
)

17
src/world/chunk.h Executable file
View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "world/tile.h"
#define CHUNK_WIDTH_IN_TILES 16
#define CHUNK_HEIGHT_IN_TILES 16
#define CHUNK_TILE_COUNT (CHUNK_WIDTH_IN_TILES * CHUNK_HEIGHT_IN_TILES)
typedef struct chunk_s {
tile_t tiles[CHUNK_TILE_COUNT];
} chunk_t;

14
src/world/map.c Executable file
View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "map.h"
void mapTick(map_t *map) {
for(int i = 0; i < MAP_ENTITY_COUNT; i++) {
entityTick(&map->entities[i]);
}
}

35
src/world/map.h Executable file
View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "world/chunk.h"
#include "entity/entity.h"
#define MAP_ENTITY_COUNT 8
#define MAP_WIDTH_IN_CHUNKS 3
#define MAP_HEIGHT_IN_CHUNKS 3
#define MAP_CHUNK_COUNT (MAP_WIDTH_IN_CHUNKS * MAP_HEIGHT_IN_CHUNKS)
typedef struct map_s {
// Loaded chunks.
chunk_t chunks[MAP_CHUNK_COUNT];
chunk_t *order[MAP_CHUNK_COUNT];
int8_t firstChunk;
// Map entities
entity_t entities[MAP_ENTITY_COUNT];
// Size of map (in chunks)
uint8_t width, height;
} map_t;
/**
* Update the map for one tick.
*
* @param map Pointer to the map to update.
*/
void mapTick(map_t *map);

13
src/world/tile.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 "microrpg.h"
#define TILE_NULL 0x00
typedef uint8_t tile_t;