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

Start on actor (de)serialization TODO: Add a binary IO library similar to Minecraft's ByteBuf API.

Changed files
+173 -8
include
keraforge
src
+31 -1
include/keraforge/actor.h
··· 10 10 #include <raylib.h> 11 11 12 12 13 + /* The maximum number of registered actors. */ 14 + #define KF_MAX_ACTOR_TYPES 1024 15 + 16 + 17 + /* Stores serializers and deserializers for actors. Any actor you plan to save MUST be registered here! 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; 28 + 13 29 /* Represents any kinematic body in the world (players, NPCs, etc). */ 14 30 struct kf_actor 15 31 { 32 + /* Unique string identifier for this actor. */ 33 + char *id; 16 34 /* Spritesheet for the actor. */ 17 35 struct kf_spritesheet sprites; 18 36 /* The actor's position. */ ··· 41 59 void (*tick)(struct kf_actor *self); 42 60 /* Called every frame to render the actor. */ 43 61 void (*draw)(struct kf_actor *self); 62 + /* Doubly-linked list of actors. */ 63 + struct kf_actor *prev, *next; 44 64 }; 65 + /* Linked list of actors. */ 66 + extern struct kf_actor *kf_actors, *kf_actors_last; 67 + extern u32 kf_actor_count; 45 68 46 69 47 70 /* Create a new actor. */ 48 - struct kf_actor *kf_actor_new(struct kf_spritesheet sprites, f32 width, f32 height, bool collides); 71 + struct kf_actor *kf_actor_new(char *id, struct kf_spritesheet sprites, f32 width, f32 height, bool collides); 49 72 50 73 /* Load a spritesheet, filling in the details for loading character spritesheets. */ 51 74 struct kf_spritesheet kf_actor_loadspritesheet(char *filename); ··· 58 81 void kf_actor_move(struct kf_world *world, struct kf_actor *actor, f32 deltatime); 59 82 /* Draw the actor. */ 60 83 void kf_actor_draw(struct kf_actor *actor); 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); 61 91 62 92 63 93 #endif
+2
include/keraforge/player.h
··· 9 9 void kf_player_tick(struct kf_actor *self); 10 10 void kf_player_draw(struct kf_actor *self); 11 11 12 + u8* kf_player_serialize(struct kf_actor *self, size_t *plen); 13 + void kf_player_deserialize(struct kf_actor *self, u8 *data, size_t len); 12 14 13 15 #endif
+90 -1
src/actor.c
··· 1 + #include "keraforge/fs.h" 1 2 #include <keraforge.h> 2 3 #include <stdlib.h> 3 4 #include <raymath.h> 5 + #include <string.h> 6 + 4 7 8 + struct kf_actorregistry kf_actorregistry = {0}; 9 + struct kf_actor *kf_actors = NULL, *kf_actors_last = NULL; 10 + u32 kf_actor_count = 0; 5 11 6 - struct kf_actor *kf_actor_new(struct kf_spritesheet sprites, f32 width, f32 height, bool collides) 12 + 13 + struct kf_actor *kf_actor_new(char *id, struct kf_spritesheet sprites, f32 width, f32 height, bool collides) 7 14 { 8 15 struct kf_actor *actor = calloc(1, sizeof(struct kf_actor)); 16 + kf_actor_count++; 9 17 18 + if (!kf_actors) 19 + { 20 + kf_actors = kf_actors_last = actor; 21 + } 22 + else 23 + { 24 + actor->prev = kf_actors_last; 25 + kf_actors_last->next = actor; 26 + kf_actors_last = actor; 27 + } 28 + 29 + actor->id = id; 10 30 actor->sprites = sprites; 11 31 actor->size.x = width; 12 32 actor->size.y = height; ··· 156 176 157 177 kf_drawsprite(&actor->sprites, actor->pos.x, actor->pos.y, x, y); 158 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 + } 191 + } 192 + return -1; 193 + } 194 + 195 + #define _KF_ACTORFILE_TMP "data/tmp/actors.bin" 196 + #define _KF_ACTORFILE_XZ "data/actors.bin.xz" 197 + #define _KF_ACTORFILE "data/actors.bin" 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 + }
+23 -3
src/graphics.c
··· 1 + #include "keraforge/actor.h" 1 2 #include <keraforge.h> 2 3 #include <raylib.h> 3 4 ··· 49 50 kf_world_load(&world, true); 50 51 kf_window.room = world; 51 52 52 - struct kf_actor *player = kf_actor_new(kf_actor_loadspritesheet("data/res/img/char/template.png"), 10, 10, true); 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); 53 59 player->sizeoffset.y = 6; 54 60 player->tick = kf_player_tick; 55 61 player->draw = kf_player_draw; ··· 63 69 player->pos.y = state->player.pos.y; 64 70 kf_window.player = player; 65 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 + 66 79 kf_window.cam.target.x = player->pos.x + (player->size.x / 2); 67 80 kf_window.cam.target.y = player->pos.y + (player->size.y / 2); 68 81 kf_window.cam.zoom = 2; ··· 75 88 kf_window.running = 1; 76 89 kf_window.cam.offset.x = GetScreenWidth() / 2.0f; 77 90 kf_window.cam.offset.y = GetScreenHeight() / 2.0f; 91 + 78 92 while (!WindowShouldClose() && kf_window.running) 79 93 { 80 94 if (IsWindowResized()) ··· 116 130 kf_setmenu(NULL); 117 131 kf_setmodal(NULL); 118 132 133 + kf_saveactors(); 134 + 119 135 kf_window.state->player.pos = kf_window.player->pos; 120 136 kf_state_save(kf_window.state); 121 137 ··· 163 179 static 164 180 void _kf_modal_play_update(void) 165 181 { 166 - kf_window.player->tick(kf_window.player); 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); 167 185 168 186 Vector2 v = GetScreenToWorld2D(GetMousePosition(), kf_window.cam); 169 187 kf_window.select.x = v.x / KF_TILE_SIZE_PX; ··· 193 211 { 194 212 kf_world_draw(kf_window.room, kf_window.cam); 195 213 // kf_world_drawcolliders(world, player, cam); 196 - kf_window.player->draw(kf_window.player); 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); 197 217 } 198 218 199 219 static
+27 -3
src/player.c
··· 51 51 52 52 void kf_player_tick(struct kf_actor *self) 53 53 { 54 - if (!kf_window.menu) 54 + if (!kf_window.menu && self->controlled) 55 55 { 56 56 _player_tick_move(self); 57 57 ··· 74 74 { 75 75 kf_actor_draw(self); 76 76 77 - kf_window.cam.target.x = self->pos.x + (self->size.x / 2); 78 - kf_window.cam.target.y = self->pos.y + (self->size.y / 2); 77 + if (self->controlled) 78 + { 79 + kf_window.cam.target.x = self->pos.x + (self->size.x / 2); 80 + kf_window.cam.target.y = self->pos.y + (self->size.y / 2); 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; 79 103 }