A game engine for top-down 2D RPG games.
rpg game-engine raylib c99
at main 6.9 kB view raw
1#include "keraforge/actor.h" 2#include "keraforge/fs.h" 3#include <keraforge.h> 4#include <stdlib.h> 5#include <raymath.h> 6#include <string.h> 7 8 9struct kf_actorregistry kf_actorregistry = {0}; 10struct kf_actor *kf_actors = NULL, *kf_actors_last = NULL; 11u32 kf_actor_count = 0; 12 13 14struct kf_actor *kf_actor_new(char *key) 15{ 16 struct kf_actor *actor = calloc(1, sizeof(struct kf_actor)); 17 kf_actor_count++; 18 19 if (!kf_actors) 20 { 21 kf_actors = kf_actors_last = actor; 22 } 23 else 24 { 25 actor->prev = kf_actors_last; 26 kf_actors_last->next = actor; 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; 37 actor->friction = 1.5f; 38 actor->pointing = kf_north; 39 40 return actor; 41} 42 43int kf_actor_register(char *key) 44{ 45 int id = kf_actorregistry.count++; 46 kf_actorregistry.key[id] = key; 47 return id; 48} 49 50struct kf_spritesheet kf_actor_loadspritesheet(char *filename) 51{ 52 return kf_loadspritesheet(filename, 20, 20); 53} 54 55int kf_actor_canmovetowards(struct kf_world *world, struct kf_actor *actor, struct kf_vec2(f32) dir) 56{ 57 Rectangle r = { 58 actor->pos.x + (actor->size.x / 2) + actor->sizeoffset.x, 59 actor->pos.y + (actor->size.y / 2) + actor->sizeoffset.y, 60 actor->size.x, 61 actor->size.y 62 }; 63 r.x += dir.x; 64 r.y += dir.y; 65 66 /* get a range of tiles to check */ 67 const u32 sx = fmax(0, floorf(r.x / KF_TILE_SIZE_PX) - 1); 68 const u32 sy = fmax(0, floorf(r.y / KF_TILE_SIZE_PX) - 1); 69 const u32 ex = fmin(world->width, ceilf(r.width / KF_TILE_SIZE_PX) + sx + 2); 70 const u32 ey = fmin(world->height, ceilf(r.height / KF_TILE_SIZE_PX) + sy + 2); 71 const size_t down = world->width - ex + sx - 1; /* number of indexes to add to reach the next tile down */ 72 73 /* check if any tiles will collide with the actor's rect */ 74 const f32 trx = sx * KF_TILE_SIZE_PX; 75 Rectangle tr = { 76 trx, 77 sy * KF_TILE_SIZE_PX, 78 /* TODO: 79 Subtracting 1 as a bandaid fix to high velocities causing the player to be stopped early in collisions. 80 This is a very notorious problem in 3D collision and fwik there are plenty of 2D solutions. 81 I'll research and implement one eventually:tm: */ 82 KF_TILE_SIZE_PX - 1, 83 KF_TILE_SIZE_PX - 1, 84 }; /* tile rect */ 85 u32 x; 86 struct kf_tile *tile = kf_world_gettile(world, sx, sy); 87 88 for (u32 y = sy ; y <= ey ; y++) 89 { 90 for (x = sx ; x <= ex ; x++) 91 { 92 if (kf_tiles.collide[tile->id] && CheckCollisionRecs(r, tr)) 93 return 0; 94 tile++; /* shift tile pointer to the right */ 95 tr.x += KF_TILE_SIZE_PX; 96 } 97 tile += down; /* shift tile pointer down */ 98 tr.x = trx; 99 tr.y += KF_TILE_SIZE_PX; 100 } 101 102 return 1; 103} 104 105void kf_actor_addforce(struct kf_actor *self, struct kf_vec2(f32) force) 106{ 107 self->vel.x += force.x; 108 self->vel.y += force.y; 109} 110 111void kf_actor_move(struct kf_world *world, struct kf_actor *self, f32 dt) 112{ 113 struct kf_vec2(f32) delta = kf_mulval_vec2(f32)(self->vel, (self->speed * self->speedmod) * dt); 114 115 if (self->collide) 116 { 117 if (delta.x && !kf_actor_canmovetowards(world, self, kf_vec2_x(f32)(delta))) 118 delta.x = 0; 119 if (delta.y && !kf_actor_canmovetowards(world, self, kf_vec2_y(f32)(delta))) 120 delta.y = 0; 121 } 122 123 if (!self->controlled) 124 { 125 if (delta.y > 0) 126 self->pointing = kf_south; 127 else if (delta.y < 0) 128 self->pointing = kf_north; 129 else if (delta.x < 0) 130 self->pointing = kf_west; 131 else if (delta.x > 0) 132 self->pointing = kf_east; 133 } 134 135 self->pos = kf_add_vec2(f32)(self->pos, delta); 136 137 static const f32 speed_deadzone = 0.1f; 138 139 if (self->vel.x > -speed_deadzone && self->vel.x < speed_deadzone) 140 self->vel.x = 0; 141 else if (self->vel.x) 142 self->vel.x /= self->friction; 143 144 if (self->vel.y > -speed_deadzone && self->vel.y < speed_deadzone) 145 self->vel.y = 0; 146 else if (self->vel.y) 147 self->vel.y /= self->friction; 148 149 // if (self->speedmod > -(1+speed_deadzone) && self->speedmod < 1+speed_deadzone) 150 // self->speedmod = 1; 151 // else if (self->speedmod) 152 // self->speedmod -= self->friction * self->friction * dt; 153} 154 155void kf_actor_draw(struct kf_actor *actor) 156{ 157 int frames = 4; 158 int x = 0, y = 0; 159 160 switch (actor->pointing) 161 { 162 case kf_south: y = 0; break; 163 case kf_east: y = 1; break; 164 case kf_west: y = 2; break; 165 case kf_north: y = 3; break; 166 } 167 168 bool moving = actor->vel.x != 0 || actor->vel.y != 0; 169 170 if (actor->running && moving) 171 { 172 y += 5; /* run sprites */ 173 frames = 6; 174 } 175 else if (moving) 176 { 177 x += 7; /* walk sprites */ 178 } 179 180 /* todo: jump */ 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 187int 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 } 198 } 199 return -1; 200} 201 202#define _KF_ACTORFILE_TMP "data/tmp/actors.bin" 203#define _KF_ACTORFILE_XZ "data/actors.bin.xz" 204#define _KF_ACTORFILE "data/actors.bin" 205 206int 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 239int 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}