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

Compare changes

Choose any two refs to compare.

+10 -1
.gitignore
··· 1 /build/ 2 3 - /data.bak/
··· 1 + # Data 2 + /data/ 3 + 4 + # Built files 5 /build/ 6 7 + # Backups 8 + *.bak 9 + 10 + # Raylib 11 + /raylib.tar.gz 12 + /raylib/
+1
compile_flags.txt
··· 4 5 -std=c99 6 -Iinclude/ 7 -xc-header 8 9 -DKF_SANITY_CHECKS
··· 4 5 -std=c99 6 -Iinclude/ 7 + -Iraylib/src/ 8 -xc-header 9 10 -DKF_SANITY_CHECKS
+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
···
··· 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)
+11 -7
include/keraforge/actor.h
··· 2 #define __kf_actor__ 3 4 5 - #include "keraforge/bini.h" 6 #include <keraforge/_header.h> 7 #include <keraforge/math.h> 8 #include <keraforge/world.h> 9 #include <keraforge/math.h> 10 #include <keraforge/sprites.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 int count; 23 char *key[KF_MAX_ACTOR_TYPES]; 24 - /* Save the actor to a binary stream. */ 25 void (*serialize[KF_MAX_ACTOR_TYPES])(struct kf_actor *self, struct bini_stream *bs); 26 - /* Load the actor from a binary stream. */ 27 void (*deserialize[KF_MAX_ACTOR_TYPES])(struct kf_actor *self, struct bini_stream *bs); 28 - /* Called every frame to update the actor. Don't forget about deltatime (kf_dts, kf_dtms)! */ 29 void (*tick[KF_MAX_ACTOR_TYPES])(struct kf_actor *self); 30 - /* Called every frame to render the actor. */ 31 void (*draw[KF_MAX_ACTOR_TYPES])(struct kf_actor *self); 32 - /* Spritesheet for the actor. */ 33 struct kf_spritesheet sprite[KF_MAX_ACTOR_TYPES]; 34 }; 35 /* Global actor registry instance. */ ··· 38 /* Represents any kinematic body in the world (players, NPCs, etc). */ 39 struct kf_actor 40 { 41 - /* Integer identifier for this actor, comes from kf_actorregistry. */ 42 int id; 43 /* The actor's position. */ 44 struct kf_vec2(f32) pos; ··· 67 }; 68 /* Linked list of actors. */ 69 extern struct kf_actor *kf_actors, *kf_actors_last; 70 extern u32 kf_actor_count; 71 72
··· 2 #define __kf_actor__ 3 4 5 #include <keraforge/_header.h> 6 #include <keraforge/math.h> 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. */ ··· 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; ··· 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
+2
include/keraforge/error.h
··· 3 4 5 #include <keraforge/_header.h> 6 7 8 void kf_printbacktrace(FILE *file); 9 10
··· 3 4 5 #include <keraforge/_header.h> 6 + #include <stdio.h> 7 8 9 + /* Print a traceback. Requires GNU extensions to work properly. */ 10 void kf_printbacktrace(FILE *file); 11 12
+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 #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
··· 33 ; 34 35 36 void kf_loaddefaultbinds(void); 37 38
··· 33 ; 34 35 36 + /* Load default input bindings. */ 37 void kf_loaddefaultbinds(void); 38 39
+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
+3
include/keraforge/sprites.h
··· 10 Can be used for animations or texture atlases. */ 11 struct kf_spritesheet 12 { 13 Texture2D texture; 14 u16 spritewidth, spriteheight; 15 u32 nsprites; 16 }; 17
··· 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
include/keraforge/string.h
··· 5 #include <keraforge/_header.h> 6 7 8 char *kf_strdup(char *str); 9 10
··· 5 #include <keraforge/_header.h> 6 7 8 + /* Duplicate a string. Owner is expected to free this. */ 9 char *kf_strdup(char *str); 10 11
+3
include/keraforge/time.h
··· 6 #include <time.h> 7 8 9 #define kf_timeit(_msg, ...) \ 10 do \ 11 { \
··· 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 { \
+11 -11
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.
··· 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.
+23
readme
··· 54 Develop 55 ------- 56 57 Build "system" is contained in a folder of shell scripts, 58 `scripts`. You can run these with the `run.sh` script to 59 run multiple tasks in order. Generally: 60 `sh run.sh build run` 61 is all you'll need to run while developing. 62 63 License 64 -------
··· 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
··· 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/ -c -DKF_GNU $KF_DEBUG_CFLAGS" 14 - export LFLAGS="-lraylib -lm -lGL -lpthread -ldl -lrt -lX11 -llzma $KF_DEBUG_LFLAGS"
··· 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
···
··· 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 +