+10
-1
.gitignore
+10
-1
.gitignore
+1
compile_flags.txt
+1
compile_flags.txt
+56
etc/style.c
+56
etc/style.c
···
···
1
+
#ifndef __main__
2
+
#define __main__
3
+
4
+
5
+
/*
6
+
Include order doesn't really matter to me, however I do like to keep them reasonably organized:
7
+
1. "Header" includes (i.e, keraforge/_header.h).
8
+
2. Macro/common headers (i.e, keraforge/log.h).
9
+
3. Internal library includes (i.e, keraforge/*.h).
10
+
4. External library includes (i.e, raylib.h, raymath.h, etc).
11
+
5. C standard library includes.
12
+
*/
13
+
#include <keraforge.h>
14
+
#include <stdio.h>
15
+
16
+
17
+
/*
18
+
Please follow good practice when defining macros:
19
+
- #undef after usage.
20
+
- Use do/while blocks for parameterized macros.
21
+
- Wrap constant macros in parenthesis, unless its a string that can be safely concatenated.
22
+
*/
23
+
24
+
25
+
/* Additional keywords (excl. const) should be on their own line. */
26
+
static
27
+
/* All public items should be prefixed with kf_. All static items should use _kf_. */
28
+
int _kf_parse(int argc, /* Attach pointers with the identifier, not the type. */ char *argv[])
29
+
/* Allman braces */
30
+
{
31
+
/* Tabs for indentation, spaces for alignment. */
32
+
33
+
/* Space out your semicolons in for loops. */
34
+
for (int i = 0 ; i < argc ; i++)
35
+
fprintf(stderr, "%s", argv[i]);
36
+
37
+
if (true)
38
+
{
39
+
/* ... */
40
+
}
41
+
else
42
+
{
43
+
/* ... */
44
+
}
45
+
46
+
return 1;
47
+
}
48
+
49
+
int main(void)
50
+
{
51
+
(void)_kf_parse(0, NULL);
52
+
return 0;
53
+
}
54
+
55
+
56
+
#endif
+73
etc/style.txt
+73
etc/style.txt
···
···
1
+
2
+
Style Guidelines
3
+
================
4
+
5
+
As a preface: I break these guidelines occasionally. It is
6
+
important to note that I often find myself up into the
7
+
early morning hours (sometimes even as early as 03:00)
8
+
developing Keraforge. Late-night coding sessions tend to
9
+
lead to mistakes that I accidentally push to prod. Feel
10
+
free to point these mistakes out to me!
11
+
12
+
Zen
13
+
---
14
+
15
+
1. Keep types simple.
16
+
- If your type is complex enough to be unreadable, maybe
17
+
you should change your type.
18
+
- Function pointers may be disgraceful to write, but if
19
+
you write your code in such a way that avoids writing
20
+
the type more than once then you'll be fine.
21
+
- See: `struct kf_actorregistry`. I use some quirky
22
+
types there, but nothing too unreadable. Notice how I
23
+
never rewrite those types again and notice that I am not
24
+
using a typedef either.
25
+
- Additionally, if the user is never going to use that
26
+
type then it almost definitely does not need to be
27
+
typedef'd.
28
+
2. Documentation is important.
29
+
- "Self-documenting code" is not sufficient. If the item
30
+
is public then it should have an explanation.
31
+
- The ONLY exceptions to this rule are blatantly obvious
32
+
struct fields, such as `position`, `velocity`, etc.
33
+
- A few lines of documentation will save you a massive
34
+
headache in the future.
35
+
3. Macro magic is garbage, but unmaintainable code is worse.
36
+
- Using macro magic sparingly can help prevent you from
37
+
being smitten by the DRY deities. Save yourself today!
38
+
- See: math.h, log.h, and bini.h for cases where macro
39
+
magic is acceptable.
40
+
4. Keep names terse but readable.
41
+
- I don't want to see stdlib-style function names, but I
42
+
would much rather that over a name ripped from an
43
+
object-oriented C.
44
+
- If you must use a stdlib-style function name to remain
45
+
terse, then please provide a sentence in its docs to
46
+
explain the name.
47
+
5. Most importantly: Be simple.
48
+
- This often means sitting at your screen pondering the
49
+
best implementation for something rather than simply
50
+
using the first one that comes to mind.
51
+
- It's okay to sacrifice a bit of performance for more
52
+
maintainable code.
53
+
- If you must write complex code, please explain why via
54
+
a brief comment.
55
+
56
+
Basics
57
+
------
58
+
59
+
See <etc/style.c> for a sample of the style with comments
60
+
explaining each main point.
61
+
62
+
For more specifics, please give a skim throughout the
63
+
codebase itself.
64
+
65
+
- Prefer [iuf](8|16|32|64) over (float|double|(u?int(8|16|32|64)_t)).
66
+
- Do not typedef function pointers (see Zen #1), structs, enums, or unions.
67
+
- Use global variables instead of requiring the user to maintain state.
68
+
See: kf_uiconfig, kf_actors, kf_actorregistry, kf_state, etc.
69
+
- Use `struct kf_vec2(type)` instead of `struct kf_vec2f32`.
70
+
- Do not modify libraries included in the source.
71
+
Those are: bini.h.
72
+
- I'm not afraid to postfix _t to typedef'd types. The standard is useless here since we prefix kf_ anyway.
73
+
(I think this standard is useless *to begin with*, but I digress)
+24
-13
include/keraforge/actor.h
+24
-13
include/keraforge/actor.h
···
7
#include <keraforge/world.h>
8
#include <keraforge/math.h>
9
#include <keraforge/sprites.h>
10
#include <raylib.h>
11
12
···
18
TODO: This will probably be better as a hash table (especially for kf_actor_getregistryid). */
19
struct kf_actorregistry
20
{
21
int count;
22
-
char *id[KF_MAX_ACTOR_TYPES];
23
-
u8 *(*serialize[KF_MAX_ACTOR_TYPES])(struct kf_actor *self, size_t *plen);
24
-
void (*deserialize[KF_MAX_ACTOR_TYPES])(struct kf_actor *self, u8 *data, size_t len);
25
};
26
/* Global actor registry instance. */
27
extern struct kf_actorregistry kf_actorregistry;
···
29
/* Represents any kinematic body in the world (players, NPCs, etc). */
30
struct kf_actor
31
{
32
-
/* Unique string identifier for this actor. */
33
-
char *id;
34
-
/* Spritesheet for the actor. */
35
-
struct kf_spritesheet sprites;
36
/* The actor's position. */
37
struct kf_vec2(f32) pos;
38
/* The actor's velocity. */
···
55
enum kf_direction pointing;
56
/* If the actor is running. This will not increase their speed, you are expected to yourself. */
57
bool running;
58
-
/* Called every frame to update the actor. Don't forget about deltatime (kf_dts, kf_dtms)! */
59
-
void (*tick)(struct kf_actor *self);
60
-
/* Called every frame to render the actor. */
61
-
void (*draw)(struct kf_actor *self);
62
/* Doubly-linked list of actors. */
63
struct kf_actor *prev, *next;
64
};
65
/* Linked list of actors. */
66
extern struct kf_actor *kf_actors, *kf_actors_last;
67
extern u32 kf_actor_count;
68
69
70
/* Create a new actor. */
71
-
struct kf_actor *kf_actor_new(char *id, struct kf_spritesheet sprites, f32 width, f32 height, bool collides);
72
73
/* Load a spritesheet, filling in the details for loading character spritesheets. */
74
struct kf_spritesheet kf_actor_loadspritesheet(char *filename);
···
84
85
/* Get the registry ID of a given actor ID.
86
This will perform a lot of string comparisons, use sparingly! */
87
-
int kf_actor_getregistryid(char *id);
88
89
/* Save actor data. */
90
int kf_saveactors(void);
91
92
93
#endif
···
7
#include <keraforge/world.h>
8
#include <keraforge/math.h>
9
#include <keraforge/sprites.h>
10
+
#include <keraforge/bini.h>
11
#include <raylib.h>
12
13
···
19
TODO: This will probably be better as a hash table (especially for kf_actor_getregistryid). */
20
struct kf_actorregistry
21
{
22
+
/* Amount of actor types currently registered. */
23
int count;
24
+
/* Keys for each actor. */
25
+
char *key[KF_MAX_ACTOR_TYPES];
26
+
/* Save actor to a binary stream. */
27
+
void (*serialize[KF_MAX_ACTOR_TYPES])(struct kf_actor *self, struct bini_stream *bs);
28
+
/* Load actor from a binary stream. */
29
+
void (*deserialize[KF_MAX_ACTOR_TYPES])(struct kf_actor *self, struct bini_stream *bs);
30
+
/* Called every frame to update actor. Don't forget about deltatime (kf_dts, kf_dtms)! */
31
+
void (*tick[KF_MAX_ACTOR_TYPES])(struct kf_actor *self);
32
+
/* Called every frame to render actor. */
33
+
void (*draw[KF_MAX_ACTOR_TYPES])(struct kf_actor *self);
34
+
/* Spritesheet for actor. */
35
+
struct kf_spritesheet sprite[KF_MAX_ACTOR_TYPES];
36
};
37
/* Global actor registry instance. */
38
extern struct kf_actorregistry kf_actorregistry;
···
40
/* Represents any kinematic body in the world (players, NPCs, etc). */
41
struct kf_actor
42
{
43
+
/* Integer identifier for this actor, comes from kf_actorregistry.
44
+
This is unique per-actor-type, not per-actor. */
45
+
int id;
46
/* The actor's position. */
47
struct kf_vec2(f32) pos;
48
/* The actor's velocity. */
···
65
enum kf_direction pointing;
66
/* If the actor is running. This will not increase their speed, you are expected to yourself. */
67
bool running;
68
/* Doubly-linked list of actors. */
69
struct kf_actor *prev, *next;
70
};
71
/* Linked list of actors. */
72
extern struct kf_actor *kf_actors, *kf_actors_last;
73
+
/* Number of actors currently in the world. */
74
extern u32 kf_actor_count;
75
76
77
/* Create a new actor. */
78
+
struct kf_actor *kf_actor_new(char *key);
79
+
/* Register a new actor and return its integer ID. */
80
+
int kf_actor_register(char *key);
81
82
/* Load a spritesheet, filling in the details for loading character spritesheets. */
83
struct kf_spritesheet kf_actor_loadspritesheet(char *filename);
···
93
94
/* Get the registry ID of a given actor ID.
95
This will perform a lot of string comparisons, use sparingly! */
96
+
int kf_actor_getregistryid(char *key);
97
98
/* Save actor data. */
99
int kf_saveactors(void);
100
+
/* Load actor data. */
101
+
int kf_loadactors(void);
102
103
104
#endif
+2
include/keraforge/error.h
+2
include/keraforge/error.h
+11
include/keraforge/graphics.h
+11
include/keraforge/graphics.h
···
10
/* Represents either a model or a menu. */
11
struct kf_modal
12
{
13
char *name;
14
void (*exit)(void);
15
void (*init)(void);
16
void (*update)(void);
17
void (*render_world)(void);
18
void (*render_ui)(void);
19
void *data;
20
};
21
···
63
extern f32 kf_dts;
64
65
66
int kf_measuretext(int size, char *text);
67
void kf_drawtext(Color c, int x, int y, int size, char *text);
68
void kf_drawtextshadowed(Color c, int x, int y, int size, char *text);
69
70
···
10
/* Represents either a model or a menu. */
11
struct kf_modal
12
{
13
+
/* The title of this modal. */
14
char *name;
15
+
/* Called when this modal is closed. */
16
void (*exit)(void);
17
+
/* Called when this modal is opened/ */
18
void (*init)(void);
19
+
/* Called every frame before rendering anything. */
20
void (*update)(void);
21
+
/* Called every frame to render the world. */
22
void (*render_world)(void);
23
+
/* Called every frame to render the UI. */
24
void (*render_ui)(void);
25
+
/* Any extra data this needed.
26
+
You are expected to allocate and free this yourself in init/exit respectively. */
27
void *data;
28
};
29
···
71
extern f32 kf_dts;
72
73
74
+
/* Measure text using the default font. */
75
int kf_measuretext(int size, char *text);
76
+
/* Draw text using the default font. */
77
void kf_drawtext(Color c, int x, int y, int size, char *text);
78
+
/* Draw text with a shadow using the default font. */
79
void kf_drawtextshadowed(Color c, int x, int y, int size, char *text);
80
81
+6
-1
include/keraforge/input.h
+6
-1
include/keraforge/input.h
···
6
#include <raylib.h>
7
8
9
#define MOUSE_BUTTON_UNKNOWN ((MouseButton)-1)
10
#define GAMEPAD_AXIS_UNKNOWN ((GamepadAxis)-1)
11
12
#define KF_INPUTBIND_MAX UINT8_MAX
···
17
/* Struct-of-Arrays for keybindings. */
18
struct _kf_inputbinds
19
{
20
-
kf_inputbind_t count; /* must start at 1. 0 is the `none` keybind. */
21
char *id[KF_INPUTBIND_MAX];
22
KeyboardKey key[KF_INPUTBIND_MAX];
23
KeyboardKey alt[KF_INPUTBIND_MAX];
···
6
#include <raylib.h>
7
8
9
+
/* Represents an unknown mouse button. Provided by Keraforge, not by Raylib. */
10
#define MOUSE_BUTTON_UNKNOWN ((MouseButton)-1)
11
+
/* Represents an unknown gamepad axis. Provided by Keraforge, not by Raylib. */
12
#define GAMEPAD_AXIS_UNKNOWN ((GamepadAxis)-1)
13
14
#define KF_INPUTBIND_MAX UINT8_MAX
···
19
/* Struct-of-Arrays for keybindings. */
20
struct _kf_inputbinds
21
{
22
+
/* Amount of input bindings registered.
23
+
Must start at 1. 0 is the `none` keybind. */
24
+
kf_inputbind_t count;
25
+
/* String IDs of each binding. */
26
char *id[KF_INPUTBIND_MAX];
27
KeyboardKey key[KF_INPUTBIND_MAX];
28
KeyboardKey alt[KF_INPUTBIND_MAX];
+1
include/keraforge/inputbinds.h
+1
include/keraforge/inputbinds.h
+8
-2
include/keraforge/log.h
+8
-2
include/keraforge/log.h
···
1
#ifndef __kf_log__
2
#define __kf_log__
3
4
#include <stdio.h> /* fprintf, stderr */
5
#include <stdlib.h> /* exit */
6
#include <stdarg.h>
7
8
void kf_vlog(char *level, char *fmt, va_list va);
9
void kf_log(char *level, char *fmt, ...);
10
void kf_logdbg(char *fmt, ...);
11
void kf_loginfo(char *fmt, ...);
12
void kf_logerr(char *fmt, ...);
13
14
-
/* Errors */
15
16
/* Throw a formatted error message without printing a traceback or exiting. */
17
#define KF_THROWSOFTER(MSG, ...) \
···
36
} \
37
while (0)
38
39
-
/* Sanity Checking */
40
41
#ifdef KF_SANITY_CHECKS
42
/* Indicate that the given expression should never resolve to false and throw an error if it manages to. */
···
61
# define KF_SANITY_CHECK(EXPR, MSG, ...) do { ; } while (0)
62
# define KF_UNREACHABLE(MSG, ...) do { ; } while (0)
63
#endif
64
65
#endif
···
1
#ifndef __kf_log__
2
#define __kf_log__
3
4
+
5
#include <stdio.h> /* fprintf, stderr */
6
#include <stdlib.h> /* exit */
7
#include <stdarg.h>
8
9
+
10
+
/* Log a message. */
11
void kf_vlog(char *level, char *fmt, va_list va);
12
+
/* Log a message. */
13
void kf_log(char *level, char *fmt, ...);
14
+
/* Log a debug message. */
15
void kf_logdbg(char *fmt, ...);
16
+
/* Log an info message. */
17
void kf_loginfo(char *fmt, ...);
18
+
/* Log a error message. */
19
void kf_logerr(char *fmt, ...);
20
21
22
/* Throw a formatted error message without printing a traceback or exiting. */
23
#define KF_THROWSOFTER(MSG, ...) \
···
42
} \
43
while (0)
44
45
46
#ifdef KF_SANITY_CHECKS
47
/* Indicate that the given expression should never resolve to false and throw an error if it manages to. */
···
66
# define KF_SANITY_CHECK(EXPR, MSG, ...) do { ; } while (0)
67
# define KF_UNREACHABLE(MSG, ...) do { ; } while (0)
68
#endif
69
+
70
71
#endif
+2
-2
include/keraforge/player.h
+2
-2
include/keraforge/player.h
+3
include/keraforge/sprites.h
+3
include/keraforge/sprites.h
···
10
Can be used for animations or texture atlases. */
11
struct kf_spritesheet
12
{
13
+
/* The texture to pull sprites from. */
14
Texture2D texture;
15
+
/* Width and height of each sprite in this sheet. */
16
u16 spritewidth, spriteheight;
17
+
/* Number of sprites in this sheet. */
18
u32 nsprites;
19
};
20
+1
-7
include/keraforge/state.h
+1
-7
include/keraforge/state.h
···
8
#define KF_NPCPOOL_SIZE 1024
9
10
11
-
/* Stores global variables to be saved alongside the world. Use this for save data.
12
-
Do not use pointers here! This gets transmuted into a u8 array for serialization. */
13
struct kf_state
14
{
15
/* Do not modify this and do not relocate it. See kf_world for an explanation. */
16
u16 revision;
17
-
/* Player data */
18
-
struct {
19
-
struct kf_vec2(f32) pos;
20
-
} player;
21
-
// struct kf_actor npc[KF_NPCPOOL_SIZE];
22
};
23
24
+12
include/keraforge/string.h
+12
include/keraforge/string.h
+20
include/keraforge/time.h
+20
include/keraforge/time.h
···
6
#include <time.h>
7
8
9
+
/* Execute the given statement(s) and time them, printing
10
+
the amount of time it took with loginfo using this format:
11
+
"took [<duration>] to <_msg>" */
12
+
#define kf_timeit(_msg, ...) \
13
+
do \
14
+
{ \
15
+
struct kf_timer __timer = { \
16
+
.mode = kf_timermode_stopwatch, \
17
+
}; \
18
+
kf_timer_start(&__timer); \
19
+
__VA_ARGS__; \
20
+
kf_timer_stop(&__timer); \
21
+
char __timerstr[BUFSIZ] = {0}; \
22
+
kf_timer_snprint(&__timer, __timerstr, sizeof(__timerstr)); \
23
+
kf_loginfo("took [%s] to %s", __timerstr, _msg); \
24
+
} \
25
+
while (0)
26
+
27
+
28
enum kf_timermode
29
{
30
/* Manually stopped timer. Upon stopping, age is
···
72
void kf_timer_start(struct kf_timer *timer);
73
void kf_timer_tick(struct kf_timer *timer);
74
void kf_timer_stop(struct kf_timer *timer);
75
+
size_t kf_timer_snprint(struct kf_timer *timer, char *buf, size_t bufsiz);
76
77
78
#endif
+15
-15
include/keraforge/world.h
+15
-15
include/keraforge/world.h
···
8
#include <raylib.h>
9
10
11
#define KF_TILE_SIZE_PX 16
12
13
#define KF_TILE_IDWIDTH 10
14
#define KF_TILE_DATAWIDTH 4
15
16
-
#define KF_TILEID_MAX (1023) /* 2^10-1 */
17
18
/* Used to store tile IDs. Stored in 10 bits; max value is 1023! */
19
typedef u16 kf_tileid_t;
···
31
32
33
/* Represents a singular tile in the world. */
34
-
// struct kf_tile
35
-
// {
36
-
// kf_tileid_t subid : KF_TILE_IDWIDTH;
37
-
// kf_tileid_t id : KF_TILE_IDWIDTH;
38
-
// kf_tiledatum_t data : KF_TILE_DATAWIDTH;
39
-
// } __attribute__((packed));
40
struct kf_tile
41
{
42
-
kf_tileid_t subid ;//: KF_TILE_IDWIDTH;
43
-
kf_tileid_t id ;//: KF_TILE_IDWIDTH;
44
-
kf_tiledatum_t data ;//: KF_TILE_DATAWIDTH;
45
-
};// __attribute__((packed));
46
47
/* Represents a world (or a subworld, often called "rooms"). */
48
struct kf_world
···
93
bool transparent;
94
};
95
96
kf_tileid_t kf_addtile(struct kf_tile_opts opts);
97
#define KF_ADDTILE(...) (kf_addtile((struct kf_tile_opts){ __VA_ARGS__ }))
98
99
/* Create a world using the given width and height.
···
118
/* Draw the part of the world visible to the given camera. */
119
void kf_world_draw(struct kf_world *world, Camera2D camera);
120
121
-
/* Save a world to map.bin(.xz). */
122
-
int kf_world_save(struct kf_world *world, bool compress);
123
-
/* Load a world from a map.bin(.xz). */
124
-
int kf_world_load(struct kf_world **world, bool compressed);
125
126
#endif
···
8
#include <raylib.h>
9
10
11
+
/* Size of tiles in pixels. */
12
#define KF_TILE_SIZE_PX 16
13
14
+
/* Number of bits to use for storing tile IDs. See: struct kf_tile. */
15
#define KF_TILE_IDWIDTH 10
16
+
/* Number of bits to use for storing tile datum. See: struct kf_tile. */
17
#define KF_TILE_DATAWIDTH 4
18
19
+
/* Number of max tiles. This should be (2^KF_TILE_IDWIDTH-1). */
20
+
#define KF_TILEID_MAX (1023)
21
22
/* Used to store tile IDs. Stored in 10 bits; max value is 1023! */
23
typedef u16 kf_tileid_t;
···
35
36
37
/* Represents a singular tile in the world. */
38
struct kf_tile
39
{
40
+
kf_tileid_t subid : KF_TILE_IDWIDTH;
41
+
kf_tileid_t id : KF_TILE_IDWIDTH;
42
+
kf_tiledatum_t data : KF_TILE_DATAWIDTH;
43
+
} __attribute__((packed));
44
45
/* Represents a world (or a subworld, often called "rooms"). */
46
struct kf_world
···
91
bool transparent;
92
};
93
94
+
/* Register a tile. */
95
kf_tileid_t kf_addtile(struct kf_tile_opts opts);
96
+
/* Register a tile. */
97
#define KF_ADDTILE(...) (kf_addtile((struct kf_tile_opts){ __VA_ARGS__ }))
98
99
/* Create a world using the given width and height.
···
118
/* Draw the part of the world visible to the given camera. */
119
void kf_world_draw(struct kf_world *world, Camera2D camera);
120
121
+
/* Save a world to map.bin(.xz). Set outfile to NULL to use the default. */
122
+
int kf_world_save(struct kf_world *world, bool compress, char *outfile);
123
+
/* Load a world from a map.bin(.xz). Set infile to NULL to use the default. */
124
+
int kf_world_load(struct kf_world **world, bool compressed, char *infile);
125
126
#endif
+1
include/keraforge.h
+1
include/keraforge.h
+23
readme
+23
readme
···
54
Develop
55
-------
56
57
+
Libraries (Debian):
58
+
Raylib:
59
+
libasound2-dev libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev libxcursor-dev libxinerama-dev libwayland-dev libxkbcommon-dev
60
+
Keraforge:
61
+
liblzma-dev
62
+
63
+
Initialise a development environment:
64
+
`sh run.sh init`
65
+
66
Build "system" is contained in a folder of shell scripts,
67
`scripts`. You can run these with the `run.sh` script to
68
run multiple tasks in order. Generally:
69
`sh run.sh build run`
70
is all you'll need to run while developing.
71
+
72
+
Please note that I will be hesitant to accept PRs to this
73
+
codebase. I am very nitpicky with my code style and if your
74
+
PR does not match it (or if it goes out-of-scope, adds
75
+
useless/redundant functions, further pollute global scope,
76
+
etc), then I will likely decline the PR or request a number
77
+
of changes. Ideally, please communicate with me before
78
+
submitting a PR just for it to get closed. You can find my
79
+
contact information on my website. I don't want you wasting
80
+
your time or effort!
81
+
82
+
Additionally, please read through <etc/style.txt> before
83
+
attempting to contribute. It'll make your life much easier
84
+
if you understand my style *before* attempting a PR.
85
86
License
87
-------
+2
-2
scripts/_config.sh
+2
-2
scripts/_config.sh
···
10
export KF_DEBUG_CFLAGS="-g -DKF_SANITY_CHECKS"
11
export KF_DEBUG_LFLAGS="-g -rdynamic"
12
13
+
export CFLAGS="-Wall -Wextra -Werror -std=c99 -Iinclude/ -Iraylib/src/ -c -DKF_GNU $KF_DEBUG_CFLAGS"
14
+
export LFLAGS="raylib/src/libraylib.a -lm -lGL -lpthread -ldl -lrt -lX11 -llzma $KF_DEBUG_LFLAGS"
+20
scripts/init.sh
+20
scripts/init.sh
···
···
1
+
#!/usr/bin/env sh
2
+
3
+
set -e
4
+
5
+
RAYLIB_VERSION=5.5
6
+
7
+
if [ ! -e "raylib.tar.gz" ]
8
+
then
9
+
curl -Lo raylib.tar.gz https://github.com/raysan5/raylib/archive/refs/tags/$RAYLIB_VERSION.tar.gz
10
+
fi
11
+
12
+
if [ ! -e "raylib/" ]
13
+
then
14
+
tar -xkf raylib.tar.gz && mv -v raylib-$RAYLIB_VERSION raylib
15
+
fi
16
+
17
+
cd raylib/src
18
+
make
19
+
cd ../..
20
+
+84
-45
src/actor.c
+84
-45
src/actor.c
···
1
#include "keraforge/fs.h"
2
#include <keraforge.h>
3
#include <stdlib.h>
···
10
u32 kf_actor_count = 0;
11
12
13
-
struct kf_actor *kf_actor_new(char *id, struct kf_spritesheet sprites, f32 width, f32 height, bool collides)
14
{
15
struct kf_actor *actor = calloc(1, sizeof(struct kf_actor));
16
kf_actor_count++;
···
26
kf_actors_last = actor;
27
}
28
29
-
actor->id = id;
30
-
actor->sprites = sprites;
31
-
actor->size.x = width;
32
-
actor->size.y = height;
33
-
actor->collide = collides;
34
35
actor->speed = 25;
36
actor->speedmod = 1;
···
38
actor->pointing = kf_north;
39
40
return actor;
41
}
42
43
struct kf_spritesheet kf_actor_loadspritesheet(char *filename)
···
174
175
x += (int)(kf_s * (frames+1)) % frames;
176
177
-
kf_drawsprite(&actor->sprites, actor->pos.x, actor->pos.y, x, y);
178
}
179
180
-
int kf_actor_getregistryid(char *id)
181
{
182
-
size_t l = strlen(id);
183
for (int i = 0 ; i < kf_actorregistry.count ; i++)
184
{
185
-
char *c = kf_actorregistry.id[i];
186
size_t cl = strlen(c);
187
-
if (strncmp(c, id, l>cl?l:cl) == 0)
188
{
189
return i;
190
}
···
198
199
int kf_saveactors(void)
200
{
201
// char *outfile = compress ? _KF_ACTORFILE_TMP : _KF_ACTORFILE;
202
char *outfile = _KF_ACTORFILE;
203
204
-
FILE *fp = fopen(outfile, "wb");
205
-
if (!fp)
206
-
KF_THROW("failed to open %s", outfile);
207
208
-
size_t nactors = 0;
209
-
size_t total_bytes = 0;
210
211
-
for (struct kf_actor *actor = kf_actors ; actor != NULL ; actor = actor->next)
212
{
213
-
if (!actor->id)
214
-
continue;
215
-
int id = kf_actor_getregistryid(actor->id);
216
if (id == -1)
217
continue;
218
-
size_t n = strlen(actor->id);
219
-
if (fwrite(actor->id, 1, n, fp) != n)
220
-
{
221
-
KF_THROWSOFTER("failed to write actor ID: %s", actor->id);
222
-
fclose(fp);
223
-
return 0;
224
-
}
225
-
n = 0;
226
-
u8 *data = kf_actorregistry.serialize[id](actor, &n);
227
-
if (!data)
228
-
{
229
-
KF_THROWSOFTER("failed to serialize actor of type %s", actor->id);
230
-
fclose(fp);
231
-
return 0;
232
-
}
233
-
if (fwrite(data, 1, n, fp) != n)
234
-
{
235
-
KF_THROWSOFTER("failed to write serialized actor of type %s", actor->id);
236
-
fclose(fp);
237
-
return 0;
238
-
}
239
-
nactors++;
240
-
total_bytes += n;
241
}
242
243
-
kf_logdbg("serialized %d actors (%lu bytes)", nactors, total_bytes);
244
-
fclose(fp);
245
246
return 1;
247
}
···
1
+
#include "keraforge/actor.h"
2
#include "keraforge/fs.h"
3
#include <keraforge.h>
4
#include <stdlib.h>
···
11
u32 kf_actor_count = 0;
12
13
14
+
struct kf_actor *kf_actor_new(char *key)
15
{
16
struct kf_actor *actor = calloc(1, sizeof(struct kf_actor));
17
kf_actor_count++;
···
27
kf_actors_last = actor;
28
}
29
30
+
actor->id = kf_actor_getregistryid(key);
31
+
actor->size.x = 10;
32
+
actor->size.y = 10;
33
+
actor->collide = true;
34
35
actor->speed = 25;
36
actor->speedmod = 1;
···
38
actor->pointing = kf_north;
39
40
return actor;
41
+
}
42
+
43
+
int kf_actor_register(char *key)
44
+
{
45
+
int id = kf_actorregistry.count++;
46
+
kf_actorregistry.key[id] = key;
47
+
return id;
48
}
49
50
struct kf_spritesheet kf_actor_loadspritesheet(char *filename)
···
181
182
x += (int)(kf_s * (frames+1)) % frames;
183
184
+
kf_drawsprite(&kf_actorregistry.sprite[actor->id], actor->pos.x, actor->pos.y, x, y);
185
}
186
187
+
int kf_actor_getregistryid(char *key)
188
{
189
+
size_t l = strlen(key);
190
for (int i = 0 ; i < kf_actorregistry.count ; i++)
191
{
192
+
char *c = kf_actorregistry.key[i];
193
size_t cl = strlen(c);
194
+
if (strncmp(c, key, l>cl?l:cl) == 0)
195
{
196
return i;
197
}
···
205
206
int kf_saveactors(void)
207
{
208
+
struct bini_stream *bs = bini_new();
209
+
210
+
/* kf_actor_count includes unserialized actors, so we have to manually count. */
211
+
size_t nactors = 0;
212
+
for (struct kf_actor *actor = kf_actors ; actor != NULL ; actor = actor->next)
213
+
{
214
+
if (actor->id == -1)
215
+
continue;
216
+
nactors++;
217
+
}
218
+
bini_wlu(bs, nactors);
219
+
kf_logdbg("expecting to save %lu actors", nactors);
220
+
221
+
for (struct kf_actor *actor = kf_actors ; actor != NULL ; actor = actor->next)
222
+
{
223
+
if (actor->id == -1)
224
+
continue;
225
+
bini_wstr(bs, kf_actorregistry.key[actor->id]);
226
+
kf_actorregistry.serialize[actor->id](actor, bs);
227
+
}
228
+
229
+
kf_logdbg("serialized %d actors (%lu bytes)", nactors, bs->len);
230
+
231
// char *outfile = compress ? _KF_ACTORFILE_TMP : _KF_ACTORFILE;
232
char *outfile = _KF_ACTORFILE;
233
+
if (!kf_writebin(outfile, bs->buffer, bs->len))
234
+
KF_THROW("failed to write actors to %s", outfile);
235
236
+
return 1;
237
+
}
238
+
239
+
int kf_loadactors(void)
240
+
{
241
+
// char *infile = compress ? _KF_ACTORFILE_TMP : _KF_ACTORFILE;
242
+
char *infile = _KF_ACTORFILE;
243
+
244
+
if (!kf_exists(infile))
245
+
{
246
+
kf_logdbg("actors.bin nonexistent, creating...");
247
+
kf_saveactors();
248
+
return 1;
249
+
}
250
251
+
size_t len = 0;
252
+
struct bini_stream bs = {
253
+
.mode = BINI_STREAM,
254
+
.buffer = kf_readbin(infile, &len),
255
+
};
256
+
if (!bs.buffer)
257
+
KF_THROW("failed to read/open %s", infile);
258
+
bs.cap = len;
259
+
bs.len = len;
260
+
kf_logdbg("loaded actors into binary stream: len=%lu", len);
261
262
+
const size_t nactors = bini_rlu(&bs);
263
+
kf_logdbg("expecting to load %lu actors", nactors);
264
+
265
+
for (size_t i = 0 ; i < nactors ; i++)
266
{
267
+
kf_logdbg("loading actor #%lu", i);
268
+
char key[4096];
269
+
size_t n = bini_rstr(&bs, key);
270
+
key[n] = '\0';
271
+
char *keycpy = kf_strdup(key);
272
+
kf_logdbg(" key: %s", keycpy);
273
+
int id = kf_actor_getregistryid(keycpy);
274
+
kf_logdbg(" id: %d", id);
275
if (id == -1)
276
continue;
277
+
struct kf_actor *actor = kf_actor_new(keycpy);
278
+
kf_actorregistry.deserialize[id](actor, &bs);
279
}
280
281
+
kf_logdbg("loaded %d actors", nactors);
282
+
283
+
free(bs.buffer);
284
285
return 1;
286
}
+2
src/bini.c
+2
src/bini.c
+1
-1
src/editor.c
+1
-1
src/editor.c
+35
-27
src/graphics.c
+35
-27
src/graphics.c
···
44
45
struct kf_state *state = NULL;
46
int is_new_state = kf_state_load(&state);
47
kf_window.state = state;
48
49
struct kf_world *world = NULL;
50
-
kf_world_load(&world, false);
51
kf_window.room = world;
52
53
-
kf_actorregistry.id[0] = "player";
54
-
kf_actorregistry.serialize[0] = kf_player_serialize;
55
-
kf_actorregistry.deserialize[0] = kf_player_deserialize;
56
-
kf_actorregistry.count++;
57
58
-
struct kf_actor *player = kf_actor_new("player", kf_actor_loadspritesheet("data/res/img/char/template.png"), 10, 10, true);
59
-
player->sizeoffset.y = 6;
60
-
player->tick = kf_player_tick;
61
-
player->draw = kf_player_draw;
62
-
player->controlled = true;
63
-
if (is_new_state) /* place the player in the centre of the room */
64
{
65
-
state->player.pos.x = world->width * KF_TILE_SIZE_PX / 2.0f;
66
-
state->player.pos.y = world->width * KF_TILE_SIZE_PX / 2.0f;
67
}
68
-
player->pos.x = state->player.pos.x;
69
-
player->pos.y = state->player.pos.y;
70
-
kf_window.player = player;
71
72
-
struct kf_actor *player2 = kf_actor_new("player", kf_actor_loadspritesheet("data/res/img/char/whom.png"), 10, 10, true);
73
-
player2->sizeoffset.y = 6;
74
-
player2->tick = kf_player_tick; /* when controlled is false, player_tick won't process keybinds */
75
-
player2->draw = kf_player_draw;
76
-
player2->pos.x = player->pos.x;
77
-
player2->pos.y = player->pos.y - 24;
78
79
-
kf_window.cam.target.x = player->pos.x + (player->size.x / 2);
80
-
kf_window.cam.target.y = player->pos.y + (player->size.y / 2);
81
kf_window.cam.zoom = 2;
82
83
kf_setmodal(&kf_modal_play);
···
132
133
kf_saveactors();
134
135
-
kf_window.state->player.pos = kf_window.player->pos;
136
kf_state_save(kf_window.state);
137
138
free(kf_window.player);
···
181
{
182
// kf_window.player->tick(kf_window.player);
183
for (struct kf_actor *actor = kf_actors ; actor != NULL ; actor = actor->next)
184
-
actor->tick(actor);
185
186
Vector2 v = GetScreenToWorld2D(GetMousePosition(), kf_window.cam);
187
kf_window.select.x = v.x / KF_TILE_SIZE_PX;
···
213
// kf_world_drawcolliders(world, player, cam);
214
// kf_window.player->draw(kf_window.player);
215
for (struct kf_actor *actor = kf_actors ; actor != NULL ; actor = actor->next)
216
-
actor->draw(actor);
217
}
218
219
static
···
44
45
struct kf_state *state = NULL;
46
int is_new_state = kf_state_load(&state);
47
+
(void)is_new_state;
48
kf_window.state = state;
49
50
struct kf_world *world = NULL;
51
+
kf_timeit("load world", kf_world_load(&world, true, NULL));
52
kf_window.room = world;
53
54
+
int idplayer = kf_actor_register("player");
55
+
kf_actorregistry.serialize[idplayer] = kf_player_serialize;
56
+
kf_actorregistry.deserialize[idplayer] = kf_player_deserialize;
57
+
kf_actorregistry.sprite[idplayer] = kf_actor_loadspritesheet("data/res/img/char/template.png");
58
+
kf_actorregistry.tick[idplayer] = kf_player_tick;
59
+
kf_actorregistry.draw[idplayer] = kf_player_draw;
60
+
61
+
int idplayer2 = kf_actor_register("player2");
62
+
kf_actorregistry.serialize[idplayer2] = kf_player_serialize;
63
+
kf_actorregistry.deserialize[idplayer2] = kf_player_deserialize;
64
+
kf_actorregistry.sprite[idplayer2] = kf_actor_loadspritesheet("data/res/img/char/whom.png");
65
+
kf_actorregistry.tick[idplayer2] = kf_player_tick;
66
+
kf_actorregistry.draw[idplayer2] = kf_player_draw;
67
68
+
if (!kf_exists("data/actors.bin"))
69
{
70
+
struct kf_actor *player = kf_actor_new("player");
71
+
player->controlled = true;
72
+
player->pos.x = world->width * KF_TILE_SIZE_PX / 2.0f;
73
+
player->pos.y = world->width * KF_TILE_SIZE_PX / 2.0f;
74
+
75
+
struct kf_actor *player2 = kf_actor_new("player2");
76
+
player2->pos.x = world->width * KF_TILE_SIZE_PX / 2.0f;
77
+
player2->pos.y = (world->width * KF_TILE_SIZE_PX / 2.0f) - 32;
78
+
79
+
kf_timeit("save actors", kf_saveactors());
80
}
81
+
else
82
+
{
83
+
kf_timeit("load actors", kf_loadactors());
84
+
}
85
86
+
kf_window.player = kf_actors; /* player should always be the first actor. */
87
88
+
kf_window.cam.target.x = kf_window.player->pos.x + (kf_window.player->size.x / 2);
89
+
kf_window.cam.target.y = kf_window.player->pos.y + (kf_window.player->size.y / 2);
90
kf_window.cam.zoom = 2;
91
92
kf_setmodal(&kf_modal_play);
···
141
142
kf_saveactors();
143
144
kf_state_save(kf_window.state);
145
146
free(kf_window.player);
···
189
{
190
// kf_window.player->tick(kf_window.player);
191
for (struct kf_actor *actor = kf_actors ; actor != NULL ; actor = actor->next)
192
+
kf_actorregistry.tick[actor->id](actor);
193
194
Vector2 v = GetScreenToWorld2D(GetMousePosition(), kf_window.cam);
195
kf_window.select.x = v.x / KF_TILE_SIZE_PX;
···
221
// kf_world_drawcolliders(world, player, cam);
222
// kf_window.player->draw(kf_window.player);
223
for (struct kf_actor *actor = kf_actors ; actor != NULL ; actor = actor->next)
224
+
kf_actorregistry.draw[actor->id](actor);
225
}
226
227
static
+9
-15
src/player.c
+9
-15
src/player.c
···
1
#include <keraforge.h>
2
3
···
81
}
82
}
83
84
-
85
-
struct _kf_serialized_player
86
-
{
87
-
struct kf_vec2(f32) pos;
88
-
};
89
-
90
-
u8* kf_player_serialize(struct kf_actor *self, size_t *plen)
91
{
92
-
*plen = sizeof(struct _kf_serialized_player);
93
-
struct _kf_serialized_player *s = malloc(*plen);
94
-
s->pos = self->pos;
95
-
return (u8 *)s;
96
}
97
98
-
void kf_player_deserialize(struct kf_actor *self, u8 *data, size_t len)
99
{
100
-
(void)len;
101
-
struct _kf_serialized_player *s = (struct _kf_serialized_player *)data;
102
-
self->pos = s->pos;
103
}
···
1
+
#include "keraforge/bini.h"
2
#include <keraforge.h>
3
4
···
82
}
83
}
84
85
+
void kf_player_serialize(struct kf_actor *self, struct bini_stream *bs)
86
{
87
+
bini_wf(bs, self->pos.x);
88
+
bini_wf(bs, self->pos.y);
89
+
bini_wb(bs, self->controlled);
90
}
91
92
+
void kf_player_deserialize(struct kf_actor *self, struct bini_stream *bs)
93
{
94
+
self->pos.x = bini_rf(bs);
95
+
self->pos.y = bini_rf(bs);
96
+
self->controlled = bini_rb(bs);
97
}
+13
src/string.c
+13
src/string.c
+46
-1
src/time.c
+46
-1
src/time.c
···
1
#include <keraforge.h>
2
3
4
void kf_timer_start(struct kf_timer *timer)
···
13
14
void kf_timer_tick(struct kf_timer *timer)
15
{
16
time_t now = time(NULL);
17
time_t dif = now - timer->now;
18
timer->age += dif;
···
23
case kf_timermode_stopwatch:
24
break;
25
case kf_timermode_repeat:
26
case kf_timermode_oneshot:
27
break;
28
}
29
30
}
31
32
-
void kf_timer_stop(struct kf_timer *timer);
···
1
+
#include "keraforge/time.h"
2
#include <keraforge.h>
3
+
#include <time.h>
4
5
6
void kf_timer_start(struct kf_timer *timer)
···
15
16
void kf_timer_tick(struct kf_timer *timer)
17
{
18
+
if (timer->stop)
19
+
return;
20
+
21
time_t now = time(NULL);
22
time_t dif = now - timer->now;
23
timer->age += dif;
···
28
case kf_timermode_stopwatch:
29
break;
30
case kf_timermode_repeat:
31
+
if (timer->age >= timer->length)
32
+
{
33
+
timer->age = 0;
34
+
if (timer->callback)
35
+
timer->callback(timer);
36
+
}
37
+
break;
38
case kf_timermode_oneshot:
39
+
if (timer->age >= timer->length)
40
+
{
41
+
timer->stop = now;
42
+
timer->age = 0;
43
+
if (timer->callback)
44
+
timer->callback(timer);
45
+
}
46
break;
47
}
48
49
}
50
51
+
void kf_timer_stop(struct kf_timer *timer)
52
+
{
53
+
time(&timer->stop);
54
+
if (timer->mode == kf_timermode_stopwatch)
55
+
{
56
+
timer->length = timer->stop - timer->start;
57
+
if (timer->callback)
58
+
timer->callback(timer);
59
+
}
60
+
}
61
+
62
+
size_t kf_timer_snprint(struct kf_timer *timer, char *buf, size_t bufsiz)
63
+
{
64
+
size_t n = 0;
65
+
switch (timer->mode)
66
+
{
67
+
case kf_timermode_stopwatch:
68
+
n += strftime(buf, bufsiz, "%Hh:%Mm:%Ss", gmtime(&timer->length));
69
+
break;
70
+
case kf_timermode_repeat:
71
+
case kf_timermode_oneshot:
72
+
n += strftime(buf, bufsiz, "%Hh:%Mm:%Ss/", gmtime(&timer->age));
73
+
n += strftime(buf + n, bufsiz - n, "%Hh:%Mm:%Ss", gmtime(&timer->length));
74
+
break;
75
+
}
76
+
return n;
77
+
}
+4
-4
src/world.c
+4
-4
src/world.c
···
240
}
241
}
242
243
-
int kf_world_save(struct kf_world *world, bool compress)
244
{
245
-
char *outfile = compress ? _KF_MAPFILE_TMP : _KF_MAPFILE;
246
struct bini_stream *bs = bini_new();
247
_kf_world_save_bs(world, bs);
248
if (!kf_writebin(outfile, bs->buffer, bs->len))
···
284
*pworld = world;
285
}
286
287
-
int kf_world_load(struct kf_world **pworld, bool compressed)
288
{
289
if (compressed) /* decompress before loading */
290
{
···
302
}
303
}
304
305
-
char *infile = compressed ? _KF_MAPFILE_TMP : _KF_MAPFILE;
306
kf_logdbg("loading world: %s", infile);
307
308
size_t len = 0;
···
240
}
241
}
242
243
+
int kf_world_save(struct kf_world *world, bool compress, char *outfile)
244
{
245
+
outfile = outfile ? outfile : (compress ? _KF_MAPFILE_TMP : _KF_MAPFILE);
246
struct bini_stream *bs = bini_new();
247
_kf_world_save_bs(world, bs);
248
if (!kf_writebin(outfile, bs->buffer, bs->len))
···
284
*pworld = world;
285
}
286
287
+
int kf_world_load(struct kf_world **pworld, bool compressed, char *infile)
288
{
289
if (compressed) /* decompress before loading */
290
{
···
302
}
303
}
304
305
+
infile = infile ? infile : (compressed ? _KF_MAPFILE_TMP : _KF_MAPFILE);
306
kf_logdbg("loading world: %s", infile);
307
308
size_t len = 0;
+13
-2
todo
+13
-2
todo
···
10
. Core
11
. World
12
x Tiles
13
-
. Actors
14
x Compression
15
. Compress without saving the world binary as an intermediate step.
16
-
All I need to do for this is adapt the compression functions to have an in-memory (`u8 *`) compression equivalent instead of just `FILE *` compression.
17
. Dialogue
18
. Quests
19
. Combat
20
. Character Stats
21
. (De)Buffs
···
10
. Core
11
. World
12
x Tiles
13
+
/ Actors
14
+
x Rendering
15
+
x Serialization
16
+
. NPC paths (i.e, walking to/from locations. Stardew Valley style)
17
x Compression
18
. Compress without saving the world binary as an intermediate step.
19
+
20
+
All I need to do for this is adapt the compression functions to have an
21
+
in-memory (`u8 *`) compression equivalent instead of just `FILE *`
22
+
compression.
23
+
24
+
Another good option would be to implement compression support in Bini,
25
+
i.e, a function that compresses a binary stream's buffer. I would
26
+
also need to mark the stream as read-only until a flush occurs, though.
27
. Dialogue
28
. Quests
29
+
. UI+layout library
30
. Combat
31
. Character Stats
32
. (De)Buffs
+5
-15
tools/newgame.c
+5
-15
tools/newgame.c
···
73
struct kf_world *world = NULL;
74
75
kf_loginfo("creating world");
76
-
world = kf_world_new(width, height, 2);
77
78
/* path for our map.bin */
79
char worldpath[4096] = {0};
···
82
MakeDirectory(GetDirectoryPath(worldpath));
83
84
size_t len = kf_world_getsize(world);
85
-
kf_loginfo("saving world (%lu bytes uncompressed)", len);
86
-
kf_world_save(world, compress);
87
-
// if (!kf_writebin(worldpath, (u8 *)world, len))
88
-
// KF_THROW("failed to save %s", worldpath);
89
-
90
-
if (compress)
91
-
{
92
-
char worldxzpath[4096] = {0};
93
-
strcpy(worldxzpath, path);
94
-
strcpy(&worldxzpath[0] + strlen(path), "/map.bin.xz");
95
-
if (!kf_compress(worldpath, worldxzpath))
96
-
KF_THROW("failed to compress %s", worldpath);
97
-
remove(worldpath); /* no longer needed */
98
-
}
99
100
free(world);
101
}
···
73
struct kf_world *world = NULL;
74
75
kf_loginfo("creating world");
76
+
kf_timeit("create world", {
77
+
world = kf_world_new(width, height, 2);
78
+
});
79
80
/* path for our map.bin */
81
char worldpath[4096] = {0};
···
84
MakeDirectory(GetDirectoryPath(worldpath));
85
86
size_t len = kf_world_getsize(world);
87
+
kf_loginfo("saving world to %s (%lu bytes uncompressed)", worldpath, len);
88
+
kf_timeit("save world", kf_world_save(world, compress, worldpath));
89
90
free(world);
91
}