+10
include/keraforge.h
+10
include/keraforge.h
+21
include/keraforge/_header.h
+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
+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
+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
+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
+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
scripts/_config.sh
This is a binary file and will not be displayed.
+22
scripts/build.sh
+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
+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
+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
+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
+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
+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
+
}