A game engine for top-down 2D RPG games.
rpg game-engine raylib c99
1#include "keraforge/world.h" 2#include <keraforge.h> 3#include <raylib.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#include <math.h> 8 9 10struct _kf_tiles kf_tiles = {0}; 11 12 13static inline 14void _kf_updatetilebitmask(struct kf_world *world, u32 x, u32 y); 15 16 17kf_tileid_t kf_addtile(struct kf_tile_opts opts) 18{ 19 KF_SANITY_CHECK(opts.key != NULL, "tile added without key"); 20 KF_SANITY_CHECK(opts.sheet != NULL, "tile added without sheet"); 21 22 kf_tileid_t id = ++kf_tiles.count; 23 24 kf_tiles.key[id] = opts.key; 25 kf_tiles.mapcol[id] = opts.mapcol; 26 kf_tiles.sheet[id] = opts.sheet; 27 kf_tiles.sprite[id] = opts.sprite; 28 kf_tiles.collide[id] = opts.collide; 29 30 return id; 31} 32 33struct kf_world *kf_world_new(u32 width, u32 height, kf_tileid_t fill) 34{ 35 const size_t len = sizeof(struct kf_world) + sizeof(struct kf_tile)*width*height; 36 struct kf_world *world = malloc(len); 37 printf("creating world: %lu bytes: %p\n", len, world); 38 world->revision = 0; 39 world->width = width; 40 world->height = height; 41 memset(world->map, 0, sizeof(struct kf_tile)*width*height); 42 for (size_t i = 0 ; i < width*height ; i++) 43 world->map[i].id = fill; 44 45 u32 x; 46 for (u32 y = 0 ; y < height ; y++) 47 { 48 for (x = 0 ; x < width ; x++) 49 { 50 _kf_updatetilebitmask(world, x, y); 51 } 52 } 53 54 return world; 55} 56 57size_t kf_world_getsize(struct kf_world *world) 58{ 59 return sizeof(struct kf_world) + sizeof(struct kf_tile)*world->width*world->height; 60} 61 62static inline 63void _kf_updatetilebitmask(struct kf_world *world, u32 x, u32 y) 64{ 65 struct kf_tile *t = kf_world_gettile(world, x, y); 66 kf_tileid_t 67 n = y-1 >= world->height ? 0 : kf_world_gettile(world, x, y-1)->id, 68 w = x-1 >= world->width ? 0 : kf_world_gettile(world, x-1, y)->id, 69 e = x+1 >= world->width ? 0 : kf_world_gettile(world, x+1, y)->id, 70 s = y+1 >= world->height ? 0 : kf_world_gettile(world, x, y+1)->id; 71 t->data = 0x0000; 72 if (t->id == n) t->data |= KF_TILEMASK_NORTH; 73 if (t->id == w) t->data |= KF_TILEMASK_WEST; 74 if (t->id == e) t->data |= KF_TILEMASK_EAST; 75 if (t->id == s) t->data |= KF_TILEMASK_SOUTH; 76} 77 78void kf_world_updatetile(struct kf_world *world, u32 x, u32 y, bool update_neighbours) 79{ 80 _kf_updatetilebitmask(world, x, y); 81 if (update_neighbours) 82 { 83 if (y-1 < world->height) kf_world_updatetile(world, x, y-1, false); 84 if (x-1 < world->width) kf_world_updatetile(world, x-1, y, false); 85 if (x+1 < world->width) kf_world_updatetile(world, x+1, y, false); 86 if (y+1 < world->height) kf_world_updatetile(world, x, y+1, false); 87 } 88} 89 90inline 91struct kf_vec2(u32) kf_getspritefortilebitmask(kf_tiledatum_t t) 92{ 93 switch (t) 94 { 95 case 0x0000: return (struct kf_vec2(u32)){0, 3}; /* 0x0000: */ 96 case 0x0001: return (struct kf_vec2(u32)){0, 2}; /* 0x0001: N */ 97 case 0x0010: return (struct kf_vec2(u32)){3, 3}; /* 0x0010: W */ 98 case 0x0011: return (struct kf_vec2(u32)){3, 2}; /* 0x0011: NW */ 99 case 0x0100: return (struct kf_vec2(u32)){1, 3}; /* 0x0100: E */ 100 case 0x0101: return (struct kf_vec2(u32)){1, 2}; /* 0x0101: EN */ 101 case 0x0110: return (struct kf_vec2(u32)){2, 3}; /* 0x0110: EW */ 102 case 0x0111: return (struct kf_vec2(u32)){2, 2}; /* 0x0111: EWN */ 103 case 0x1000: return (struct kf_vec2(u32)){0, 0}; /* 0x1000: S */ 104 case 0x1001: return (struct kf_vec2(u32)){0, 1}; /* 0x1001: SN */ 105 case 0x1010: return (struct kf_vec2(u32)){3, 0}; /* 0x1010: SW */ 106 case 0x1011: return (struct kf_vec2(u32)){3, 1}; /* 0x1011: SWN */ 107 case 0x1100: return (struct kf_vec2(u32)){1, 0}; /* 0x1100: SE */ 108 case 0x1101: return (struct kf_vec2(u32)){1, 1}; /* 0x1101: SEN */ 109 case 0x1110: return (struct kf_vec2(u32)){2, 0}; /* 0x1110: SEW */ 110 case 0x1111: return (struct kf_vec2(u32)){2, 1}; /* 0x1111: NESW */ 111 } 112 KF_UNREACHABLE("invalid bitmask: 0x%x", t); 113 return (struct kf_vec2(u32)){-1, -1}; 114} 115 116struct kf_tile *kf_world_gettile(struct kf_world *world, u32 x, u32 y) 117{ 118 return &world->map[y*world->width + x]; 119} 120 121void kf_world_drawcolliders(struct kf_world *world, struct kf_actor *player, Camera2D camera) 122{ 123 Rectangle r = { 124 player->pos.x + (player->size.x / 2) + player->sizeoffset.x, 125 player->pos.y + (player->size.y / 2) + player->sizeoffset.y, 126 player->size.x, 127 player->size.y 128 }; 129 130 DrawRectangleLinesEx(r, 1, BLUE); 131 132 const Vector2 start = GetScreenToWorld2D((Vector2){0, 0}, camera); 133 const Vector2 end = GetScreenToWorld2D((Vector2){GetScreenWidth(), GetScreenHeight()}, camera); 134 const u32 sx = fmax(0, floorf(start.x / KF_TILE_SIZE_PX)); 135 const u32 sy = fmax(0, floorf(start.y / KF_TILE_SIZE_PX)); 136 const u32 ex = fmin(world->width, ceilf(end.x / KF_TILE_SIZE_PX)); 137 const u32 ey = fmin(world->height, ceilf(end.y / KF_TILE_SIZE_PX)); 138 const size_t down = world->width - ex + sx - 1; /* number of indexes to add to reach the next tile down */ 139 struct kf_tile *tile = kf_world_gettile(world, sx, sy); 140 141 /* check if any tiles will collide with the actor's rect */ 142 const f32 trx = sx * KF_TILE_SIZE_PX; 143 Rectangle tr = { 144 trx, 145 sy * KF_TILE_SIZE_PX, 146 KF_TILE_SIZE_PX, 147 KF_TILE_SIZE_PX, 148 }; /* tile rect */ 149 u32 x; 150 151 for (u32 y = sy ; y <= ey ; y++) 152 { 153 for (x = sx ; x <= ex ; x++) 154 { 155 if (kf_tiles.collide[tile->id]) 156 DrawRectangleLinesEx(tr, 1, CheckCollisionRecs(r, tr) ? RED : BLACK); 157 158 tile++; /* shift tile pointer to the right */ 159 tr.x += KF_TILE_SIZE_PX; 160 } 161 tile += down; /* shift tile pointer down */ 162 tr.x = trx; 163 tr.y += KF_TILE_SIZE_PX; 164 } 165} 166 167void kf_world_draw(struct kf_world *world, Camera2D camera) 168{ 169 const Vector2 start = GetScreenToWorld2D((Vector2){0, 0}, camera); 170 const Vector2 end = GetScreenToWorld2D((Vector2){GetScreenWidth(), GetScreenHeight()}, camera); 171 const u32 sx = fmax(0, floorf(start.x / KF_TILE_SIZE_PX)); 172 const u32 sy = fmax(0, floorf(start.y / KF_TILE_SIZE_PX)); 173 const u32 ex = fmin(world->width, ceilf(end.x / KF_TILE_SIZE_PX)); 174 const u32 ey = fmin(world->height, ceilf(end.y / KF_TILE_SIZE_PX)); 175 const size_t down = world->width - ex + sx; /* number of indexes to add to reach the next tile down */ 176 struct kf_tile *tile = kf_world_gettile(world, sx, sy); 177 u32 x; 178 for (u32 y = sy ; y < ey ; y++) 179 { 180 for (x = sx ; x < ex ; x++) 181 { 182 KF_SANITY_CHECK(tile->id <= kf_tiles.count, "erroneous tile on map at %u,%u: %u (count=%u)", x, y, tile->id, kf_tiles.count); 183 if (tile->id) 184 { 185 struct kf_vec2(u32) s = kf_getspritefortilebitmask(tile->data); 186 kf_drawsprite_wh( 187 kf_tiles.sheet[tile->id], 188 x*KF_TILE_SIZE_PX, 189 y*KF_TILE_SIZE_PX, 190 KF_TILE_SIZE_PX, 191 KF_TILE_SIZE_PX, 192 kf_tiles.sprite[tile->id].x + s.x, 193 kf_tiles.sprite[tile->id].y + s.y 194 ); 195 } 196 tile++; /* shift tile pointer to the right */ 197 } 198 tile += down; /* shift tile pointer down */ 199 } 200}