A game engine for top-down 2D RPG games.
rpg game-engine raylib c99

initial commit (again)

EmmaTheMartian b08e6c71

+2
.gitignore
··· 1 + /build/ 2 + /data/
+5
compile_flags.txt
··· 1 + -Wall 2 + -Wextra 3 + -Werror 4 + -std=c99 5 + -Iinclude/
+10
include/keraforge.h
··· 1 + #ifndef __kf__ 2 + #define __kf__ 3 + 4 + #include <keraforge/_header.h> 5 + #include <keraforge/actor.h> 6 + #include <keraforge/fs.h> 7 + #include <keraforge/world.h> 8 + #include <keraforge/math.h> 9 + 10 + #endif
+21
include/keraforge/_header.h
··· 1 + #ifndef __kf__header__ 2 + #define __kf__header__ 3 + 4 + #include <stdint.h> 5 + #include <stddef.h> 6 + #include <stdbool.h> 7 + 8 + typedef int8_t i8; 9 + typedef int16_t i16; 10 + typedef int32_t i32; 11 + typedef int64_t i64; 12 + 13 + typedef uint8_t u8; 14 + typedef uint16_t u16; 15 + typedef uint32_t u32; 16 + typedef uint64_t u64; 17 + 18 + typedef float f32; 19 + typedef double f64; 20 + 21 + #endif
+26
include/keraforge/actor.h
··· 1 + #ifndef __kf_actor__ 2 + #define __kf_actor__ 3 + 4 + #include <keraforge/_header.h> 5 + #include <keraforge/math.h> 6 + #include <keraforge/world.h> 7 + #include <raylib.h> 8 + 9 + struct kf_actor 10 + { 11 + struct kf_vec2(f32) pos, vel, size; 12 + f32 speed, speedmod, friction; 13 + bool collide; 14 + void (*tick)(struct kf_world *world, struct kf_actor *self); 15 + void (*draw)(struct kf_world *world, struct kf_actor *self); 16 + }; 17 + 18 + struct kf_actor *kf_actor_new(void); 19 + 20 + /* Check if the actor can move in the given direction. */ 21 + int kf_actor_canmovetowards(struct kf_world *world, struct kf_actor *actor, struct kf_vec2(f32) dir); 22 + void kf_actor_addforce(struct kf_actor *self, struct kf_vec2(f32) force); 23 + /* Move the actor according to their velocity. */ 24 + void kf_actor_move(struct kf_world *world, struct kf_actor *actor, f32 deltatime); 25 + 26 + #endif
+15
include/keraforge/fs.h
··· 1 + #ifndef __kf_fs__ 2 + #define __kf_fs__ 3 + 4 + #include <keraforge/_header.h> 5 + 6 + /* Check if a file exists. */ 7 + int kf_exists(char *filename); 8 + 9 + /* Read binary file contents. */ 10 + u8 *kf_readbin(char *filename, size_t *plen); 11 + 12 + /* Write binary file contents. */ 13 + int kf_writebin(char *filename, u8 *data, size_t len); 14 + 15 + #endif
+54
include/keraforge/math.h
··· 1 + #ifndef __kf_math__ 2 + #define __kf_math__ 3 + 4 + #include <keraforge/_header.h> 5 + #include <raylib.h> 6 + #include <raymath.h> 7 + 8 + #define _kf_mathdef(x) \ 9 + x(i8); \ 10 + x(i16); \ 11 + x(i32); \ 12 + x(i64); \ 13 + x(u8); \ 14 + x(u16); \ 15 + x(u32); \ 16 + x(u64); \ 17 + x(f32); \ 18 + x(f64); \ 19 + 20 + #define kf_vec2(T) kf_vec2##T 21 + #define x(T) struct kf_vec2(T) { T x, y; } 22 + _kf_mathdef(x) 23 + #undef x 24 + 25 + #define kf_normalize_vec2(T) kf_normalize_vec2##T 26 + #define x(T) struct kf_vec2(T) kf_normalize_vec2##T(struct kf_vec2(T) v); 27 + _kf_mathdef(x) 28 + #undef x 29 + 30 + #define kf_add_vec2(T) kf_add_vec2##T 31 + #define kf_sub_vec2(T) kf_sub_vec2##T 32 + #define kf_mul_vec2(T) kf_mul_vec2##T 33 + #define kf_div_vec2(T) kf_div_vec2##T 34 + #define kf_addval_vec2(T) kf_addval_vec2##T 35 + #define kf_subval_vec2(T) kf_subval_vec2##T 36 + #define kf_mulval_vec2(T) kf_mulval_vec2##T 37 + #define kf_divval_vec2(T) kf_divval_vec2##T 38 + #define kf_vec2_x(T) kf_vec2##T##_x 39 + #define kf_vec2_y(T) kf_vec2##T##_y 40 + #define x(T) \ 41 + struct kf_vec2(T) kf_add_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b); \ 42 + struct kf_vec2(T) kf_sub_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b); \ 43 + struct kf_vec2(T) kf_mul_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b); \ 44 + struct kf_vec2(T) kf_div_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b); \ 45 + struct kf_vec2(T) kf_addval_vec2##T(struct kf_vec2(T) a, T val); \ 46 + struct kf_vec2(T) kf_subval_vec2##T(struct kf_vec2(T) a, T val); \ 47 + struct kf_vec2(T) kf_mulval_vec2##T(struct kf_vec2(T) a, T val); \ 48 + struct kf_vec2(T) kf_divval_vec2##T(struct kf_vec2(T) a, T val); \ 49 + struct kf_vec2(T) kf_vec2##T##_x(struct kf_vec2(T) a); \ 50 + struct kf_vec2(T) kf_vec2##T##_y(struct kf_vec2(T) a); 51 + _kf_mathdef(x) 52 + #undef x 53 + 54 + #endif
+45
include/keraforge/world.h
··· 1 + #ifndef __kf_world__ 2 + #define __kf_world__ 3 + 4 + #include <keraforge/_header.h> 5 + #include <raylib.h> 6 + 7 + #define KF_TILEID_MAX UINT16_MAX 8 + typedef u16 kf_tileid_t; 9 + #define KF_TILE_SIZE_PX 16 10 + 11 + struct kf_world 12 + { 13 + /* Never ever reorder `revision` to be after anything. 14 + If you add something before it or move it then the 15 + version checker will compare the first u32 in the 16 + map's binary to the expected revision, which will 17 + almost always be wrong. */ 18 + u32 revision; 19 + u32 width; 20 + u32 height; 21 + kf_tileid_t map[]; 22 + }; 23 + 24 + struct _kf_tiles 25 + { 26 + char *key[KF_TILEID_MAX]; 27 + Color color[KF_TILEID_MAX]; 28 + bool collide[KF_TILEID_MAX]; 29 + }; 30 + extern struct _kf_tiles kf_tiles; 31 + 32 + /* Create a world using the given width and height. 33 + Fills the map with the given `fill` tile. */ 34 + struct kf_world *kf_world_new(u32 width, u32 height, kf_tileid_t fill); 35 + 36 + /* Get the size of the world in bytes. */ 37 + size_t kf_world_getsize(struct kf_world *world); 38 + 39 + /* Get a pointer to the tile ID at a given position. */ 40 + kf_tileid_t *kf_world_gettile(struct kf_world *world, u32 x, u32 y); 41 + 42 + /* Draw the part of the world visible to the given camera. */ 43 + void kf_world_draw(struct kf_world *world, Camera2D camera); 44 + 45 + #endif
scripts/_config.sh

This is a binary file and will not be displayed.

+22
scripts/build.sh
··· 1 + #!/usr/bin/env sh 2 + set -e 3 + . scripts/_config.sh 4 + 5 + mkdir -p build/ 6 + 7 + if [ -e "build/keraforge" ] 8 + then 9 + rm build/keraforge 10 + fi 11 + 12 + echo ": building" 13 + for f in `find src/ -name '*.c'` 14 + do 15 + ff=$(echo "$f" | tr '/' '_') 16 + gcc -Wall -Wextra -Werror -std=c99 -Iinclude/ -c $f -o build/${ff%.c}.o 17 + done 18 + 19 + echo ": linking" 20 + gcc -lraylib -lm -lGL -lpthread -ldl -lrt -lX11 build/*.o -o build/keraforge 21 + 22 + rm build/*.o
+90
src/actor.c
··· 1 + #include "keraforge/math.h" 2 + #include "keraforge/world.h" 3 + #include <keraforge.h> 4 + #include <stdlib.h> 5 + #include <raymath.h> 6 + 7 + struct kf_actor *kf_actor_new(void) 8 + { 9 + struct kf_actor *actor = calloc(1, sizeof(struct kf_actor)); 10 + return actor; 11 + } 12 + 13 + int kf_actor_canmovetowards(struct kf_world *world, struct kf_actor *actor, struct kf_vec2(f32) dir) 14 + { 15 + Rectangle r = {actor->pos.x - actor->size.x / 2, actor->pos.y - actor->size.y / 2, actor->size.x, actor->size.y}; 16 + r.x += dir.x; 17 + r.y += dir.y; 18 + 19 + /* get a range of tiles to check */ 20 + const u32 sx = fmax(0, floorf(r.x / KF_TILE_SIZE_PX) - 1); 21 + const u32 sy = fmax(0, floorf(r.y / KF_TILE_SIZE_PX) - 1); 22 + const u32 ex = fmin(world->width, ceilf(r.width / KF_TILE_SIZE_PX) + sx + 2); 23 + const u32 ey = fmin(world->height, ceilf(r.height / KF_TILE_SIZE_PX) + sy + 2); 24 + const size_t down = world->width - ex + sx - 1; /* number of indexes to add to reach the next tile down */ 25 + 26 + /* check if any tiles will collide with the actor's rect */ 27 + const f32 trx = sx * KF_TILE_SIZE_PX; 28 + Rectangle tr = { 29 + trx, 30 + sy * KF_TILE_SIZE_PX, 31 + KF_TILE_SIZE_PX + 1, 32 + KF_TILE_SIZE_PX + 1, 33 + }; /* tile rect */ 34 + u32 x; 35 + kf_tileid_t *tile = kf_world_gettile(world, sx, sy); 36 + 37 + for (u32 y = sy ; y <= ey ; y++) 38 + { 39 + for (x = sx ; x <= ex ; x++) 40 + { 41 + if (kf_tiles.collide[*tile] && CheckCollisionRecs(r, tr)) 42 + return 0; 43 + tile++; /* shift tile pointer to the right */ 44 + tr.x += KF_TILE_SIZE_PX; 45 + } 46 + tile += down; /* shift tile pointer down */ 47 + tr.x = trx; 48 + tr.y += KF_TILE_SIZE_PX; 49 + } 50 + 51 + return 1; 52 + } 53 + 54 + void kf_actor_addforce(struct kf_actor *self, struct kf_vec2(f32) force) 55 + { 56 + self->vel.x += force.x; 57 + self->vel.y += force.y; 58 + } 59 + 60 + void kf_actor_move(struct kf_world *world, struct kf_actor *self, f32 dt) 61 + { 62 + struct kf_vec2(f32) delta = kf_mulval_vec2(f32)(self->vel, (self->speed * self->speedmod) * dt); 63 + 64 + if (self->collide) 65 + { 66 + if (delta.x && !kf_actor_canmovetowards(world, self, kf_vec2_x(f32)(delta))) 67 + delta.x = 0; 68 + if (delta.y && !kf_actor_canmovetowards(world, self, kf_vec2_y(f32)(delta))) 69 + delta.y = 0; 70 + } 71 + 72 + self->pos = kf_add_vec2(f32)(self->pos, delta); 73 + 74 + static const f32 speed_deadzone = 0.1f; 75 + 76 + if (self->vel.x > -speed_deadzone && self->vel.x < speed_deadzone) 77 + self->vel.x = 0; 78 + else if (self->vel.x) 79 + self->vel.x /= self->friction; 80 + 81 + if (self->vel.y > -speed_deadzone && self->vel.y < speed_deadzone) 82 + self->vel.y = 0; 83 + else if (self->vel.y) 84 + self->vel.y /= self->friction; 85 + 86 + if (self->speedmod > -(1+speed_deadzone) && self->speedmod < 1+speed_deadzone) 87 + self->speedmod = 1; 88 + else if (self->speedmod) 89 + self->speedmod /= self->friction; 90 + }
+50
src/fs.c
··· 1 + #include <keraforge/fs.h> 2 + #include <stdio.h> 3 + #include <stdlib.h> 4 + 5 + int kf_exists(char *filename) 6 + { 7 + FILE *fp = fopen(filename, "r"); 8 + bool opened = fp != NULL; 9 + if (opened) 10 + fclose(fp); 11 + return opened; 12 + } 13 + 14 + u8 *kf_readbin(char *filename, size_t *plen) 15 + { 16 + FILE *fp = fopen(filename, "rb"); 17 + if (!fp) 18 + return NULL; 19 + 20 + *plen = 0; 21 + fseek(fp, 0, SEEK_END); 22 + *plen = ftell(fp); 23 + fseek(fp, 0, SEEK_SET); 24 + if (*plen == 0) 25 + { 26 + fclose(fp); 27 + return NULL; 28 + } 29 + 30 + u8 *data = malloc(*plen); 31 + (void)fread(data, 1, *plen, fp); 32 + fclose(fp); 33 + 34 + return data; 35 + } 36 + 37 + int kf_writebin(char *filename, u8 *data, size_t len) 38 + { 39 + FILE *fp = fopen(filename, "wb"); 40 + if (!fp) 41 + return 0; 42 + 43 + size_t n = fwrite(data, 1, len, fp); 44 + fclose(fp); 45 + 46 + if (n != len) 47 + return 0; 48 + 49 + return 1; 50 + }
+142
src/main.c
··· 1 + #include "keraforge/actor.h" 2 + #include "keraforge/math.h" 3 + #include "keraforge/world.h" 4 + #include <raylib.h> 5 + #include <raymath.h> 6 + #include <keraforge.h> 7 + #include <stdio.h> 8 + #include <stdlib.h> 9 + 10 + static Camera2D cam; 11 + static f32 dt = 0; 12 + 13 + static void _player_tick(struct kf_world *world, struct kf_actor *self) 14 + { 15 + bool w = IsKeyDown(KEY_W), s = IsKeyDown(KEY_S); 16 + bool a = IsKeyDown(KEY_A), d = IsKeyDown(KEY_D); 17 + struct kf_vec2(f32) v = {0, 0}; 18 + 19 + if (w && s) 20 + v.y = 0; 21 + else if (w) 22 + v.y = -1; 23 + else if (s) 24 + v.y = 1; 25 + 26 + if (a && d) 27 + v.x = 0; 28 + else if (a) 29 + v.x = -1; 30 + else if (d) 31 + v.x = 1; 32 + 33 + if (v.x || v.y) 34 + kf_actor_addforce(self, kf_normalize_vec2(f32)(v)); 35 + 36 + if (IsKeyPressed(KEY_SPACE) && self->speedmod <= 1.0f) 37 + self->speedmod = 3.0f; 38 + 39 + kf_actor_move(world, self, dt); 40 + } 41 + 42 + static void _player_draw(struct kf_world *world, struct kf_actor *self) 43 + { 44 + (void)world; 45 + DrawCircle(self->pos.x, self->pos.y, 4, RED); 46 + 47 + cam.target.x = self->pos.x; 48 + cam.target.y = self->pos.y; 49 + } 50 + 51 + int main(int argc, const char *argv[]) 52 + { 53 + (void)argc; 54 + (void)argv; 55 + 56 + SetTraceLogLevel(LOG_WARNING); 57 + InitWindow(800, 600, "Keraforge"); 58 + SetTargetFPS(60); 59 + 60 + kf_tiles.color[0] = GREEN; 61 + kf_tiles.color[1] = BROWN; 62 + kf_tiles.color[2] = GRAY; 63 + kf_tiles.collide[2] = true; 64 + 65 + if (!DirectoryExists("data")) 66 + MakeDirectory("data"); 67 + 68 + struct kf_world *world = NULL; 69 + if (!kf_exists("data/map.bin")) 70 + { 71 + printf("-> creating world\n"); 72 + world = kf_world_new(128, 128, 0); 73 + for (size_t i = 0 ; i < world->width*world->height ; i += (size_t)((float)(rand())/RAND_MAX*10)) 74 + world->map[i] = (rand()/(float)RAND_MAX*3); 75 + printf("-> saving world\n"); 76 + size_t len = kf_world_getsize(world); 77 + printf("-> writing of %lu bytes\n", len); 78 + if (!kf_writebin("data/map.bin", (u8 *)world, len)) 79 + { 80 + fprintf(stderr, "error creating map: failed to save map.bin\n"); 81 + free(world); 82 + exit(1); 83 + } 84 + } 85 + else 86 + { 87 + printf("-> loading world\n"); 88 + size_t len = 0; 89 + world = (struct kf_world *)kf_readbin("data/map.bin", &len); 90 + printf("-> world is %lu bytes\n", len); 91 + } 92 + if (!world) 93 + { 94 + fprintf(stderr, "error: failed to load world\n"); 95 + exit(1); 96 + } 97 + 98 + struct kf_actor *player = kf_actor_new(); 99 + player->pos.x = world->width / 4.0f * KF_TILE_SIZE_PX; 100 + player->pos.y = world->height / 4.0f * KF_TILE_SIZE_PX; 101 + player->vel.x = 0; 102 + player->vel.y = 0; 103 + player->size.x = 6; 104 + player->size.y = 6; 105 + player->speed = 25; 106 + player->speedmod = 1; 107 + player->friction = 1.25f; 108 + player->collide = true; 109 + player->tick = _player_tick; 110 + player->draw = _player_draw; 111 + 112 + cam = (Camera2D){{GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f}, {0, 0}, 0, 2}; 113 + while (!WindowShouldClose()) 114 + { 115 + player->tick(world, player); 116 + 117 + BeginDrawing(); 118 + ClearBackground(WHITE); 119 + 120 + BeginMode2D(cam); 121 + kf_world_draw(world, cam); 122 + player->draw(world, player); 123 + EndMode2D(); 124 + 125 + DrawFPS(0, 0); 126 + DrawText(TextFormat("%f", dt), 0, 20, 20, RED); 127 + 128 + EndDrawing(); 129 + 130 + dt = GetFrameTime(); 131 + } 132 + 133 + if (world) 134 + { 135 + if (!kf_writebin("data/map.bin", (u8 *)world, kf_world_getsize(world))) 136 + fprintf(stderr, "error: failed to save map.bin\n"); 137 + free(world); 138 + } 139 + CloseWindow(); 140 + 141 + return 0; 142 + }
+26
src/math.c
··· 1 + #include <keraforge.h> 2 + #include <raylib.h> 3 + #include <raymath.h> 4 + 5 + #define x(T) \ 6 + struct kf_vec2(T) kf_normalize_vec2##T(struct kf_vec2(T) v) \ 7 + { \ 8 + Vector2 v2 = Vector2Normalize((Vector2){v.x, v.y}); \ 9 + return (struct kf_vec2(T)){v2.x, v2.y}; \ 10 + } 11 + _kf_mathdef(x) 12 + #undef x 13 + 14 + #define x(T) \ 15 + struct kf_vec2(T) kf_add_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b) { return (struct kf_vec2(T)){a.x+b.x, a.y+b.y}; } \ 16 + struct kf_vec2(T) kf_sub_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b) { return (struct kf_vec2(T)){a.x-b.x, a.y-b.y}; } \ 17 + struct kf_vec2(T) kf_mul_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b) { return (struct kf_vec2(T)){a.x*b.x, a.y*b.y}; } \ 18 + struct kf_vec2(T) kf_div_vec2##T(struct kf_vec2(T) a, struct kf_vec2(T) b) { return (struct kf_vec2(T)){a.x/b.x, a.y/b.y}; } \ 19 + struct kf_vec2(T) kf_addval_vec2##T(struct kf_vec2(T) a, T val) { return (struct kf_vec2(T)){a.x+val, a.y+val}; } \ 20 + struct kf_vec2(T) kf_subval_vec2##T(struct kf_vec2(T) a, T val) { return (struct kf_vec2(T)){a.x-val, a.y-val}; } \ 21 + struct kf_vec2(T) kf_mulval_vec2##T(struct kf_vec2(T) a, T val) { return (struct kf_vec2(T)){a.x*val, a.y*val}; } \ 22 + struct kf_vec2(T) kf_divval_vec2##T(struct kf_vec2(T) a, T val) { return (struct kf_vec2(T)){a.x/val, a.y/val}; } \ 23 + struct kf_vec2(T) kf_vec2##T##_x(struct kf_vec2(T) a) { return (struct kf_vec2(T)){a.x, 0}; } \ 24 + struct kf_vec2(T) kf_vec2##T##_y(struct kf_vec2(T) a) { return (struct kf_vec2(T)){0, a.y}; } 25 + _kf_mathdef(x) 26 + #undef x
+57
src/world.c
··· 1 + #include <keraforge/world.h> 2 + #include <keraforge/actor.h> 3 + #include <raylib.h> 4 + #include <stdlib.h> 5 + #include <string.h> 6 + #include <math.h> 7 + 8 + struct _kf_tiles kf_tiles; 9 + 10 + struct kf_world *kf_world_new(u32 width, u32 height, kf_tileid_t fill) 11 + { 12 + const size_t len = sizeof(kf_tileid_t) * width * height; 13 + struct kf_world *world = malloc(sizeof(struct kf_world) + len); 14 + world->revision = 0; 15 + world->width = width; 16 + world->height = height; 17 + memset(world->map, fill, len); 18 + return world; 19 + } 20 + 21 + size_t kf_world_getsize(struct kf_world *world) 22 + { 23 + return sizeof(struct kf_world) + sizeof(kf_tileid_t)*world->width*world->height; 24 + } 25 + 26 + kf_tileid_t *kf_world_gettile(struct kf_world *world, u32 x, u32 y) 27 + { 28 + return &world->map[y*world->width + x]; 29 + } 30 + 31 + void kf_world_draw(struct kf_world *world, Camera2D camera) 32 + { 33 + const Vector2 start = GetScreenToWorld2D((Vector2){0, 0}, camera); 34 + const Vector2 end = GetScreenToWorld2D((Vector2){GetScreenWidth(), GetScreenHeight()}, camera); 35 + const u32 sx = fmax(0, floorf(start.x / KF_TILE_SIZE_PX)); 36 + const u32 sy = fmax(0, floorf(start.y / KF_TILE_SIZE_PX)); 37 + const u32 ex = fmin(world->width, ceilf(end.x / KF_TILE_SIZE_PX)); 38 + const u32 ey = fmin(world->height, ceilf(end.y / KF_TILE_SIZE_PX)); 39 + const size_t down = world->width - ex + sx; /* number of indexes to add to reach the next tile down */ 40 + kf_tileid_t *tile = kf_world_gettile(world, sx, sy); 41 + u32 x; 42 + for (u32 y = sy ; y < ey ; y++) 43 + { 44 + for (x = sx ; x < ex ; x++) 45 + { 46 + DrawRectangle( 47 + (int)x * KF_TILE_SIZE_PX, 48 + (int)y * KF_TILE_SIZE_PX, 49 + KF_TILE_SIZE_PX, 50 + KF_TILE_SIZE_PX, 51 + kf_tiles.color[*tile] 52 + ); 53 + tile++; /* shift tile pointer to the right */ 54 + } 55 + tile += down; /* shift tile pointer down */ 56 + } 57 + }