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}