+1
include/keraforge.h
+1
include/keraforge.h
+7
-2
include/keraforge/actor.h
+7
-2
include/keraforge/actor.h
···
11
11
struct kf_actor
12
12
{
13
13
struct kf_spritesheet sprites;
14
-
struct kf_vec2(f32) pos, vel, size;
14
+
struct kf_vec2(f32) pos, vel, size, sizeoffset;
15
15
f32 speed, speedmod, friction;
16
-
bool collide;
16
+
bool collide, controlled;
17
17
enum kf_direction pointing;
18
18
void (*tick)(struct kf_world *world, struct kf_actor *self);
19
19
void (*draw)(struct kf_world *world, struct kf_actor *self);
···
21
21
22
22
struct kf_actor *kf_actor_new(struct kf_spritesheet sprites, f32 width, f32 height, bool collides);
23
23
24
+
/* Load a spritesheet, filling in the details for loading character spritesheets. */
25
+
struct kf_spritesheet kf_actor_loadspritesheet(char *filename);
26
+
24
27
/* Check if the actor can move in the given direction. */
25
28
int kf_actor_canmovetowards(struct kf_world *world, struct kf_actor *actor, struct kf_vec2(f32) dir);
26
29
/* Add the given force to the actor's velocity. */
27
30
void kf_actor_addforce(struct kf_actor *self, struct kf_vec2(f32) force);
28
31
/* Move the actor according to their velocity. */
29
32
void kf_actor_move(struct kf_world *world, struct kf_actor *actor, f32 deltatime);
33
+
/* Draw the actor. */
34
+
void kf_actor_draw(struct kf_actor *actor);
30
35
31
36
#endif
+15
include/keraforge/graphics.h
+15
include/keraforge/graphics.h
···
1
+
#ifndef __kf_graphics__
2
+
#define __kf_graphics__
3
+
4
+
#include <keraforge/_header.h>
5
+
6
+
/* Number of frames since the game opened.
7
+
You should only use this for animations!
8
+
It's not consistent enough for precise timing use deltatime (kf_dts/kf_dtms) for that. */
9
+
extern u64 kf_frame;
10
+
/* Deltatime in milliseconds */
11
+
extern f32 kf_dtms;
12
+
/* Deltatime in seconds. */
13
+
extern f32 kf_dts;
14
+
15
+
#endif
+3
include/keraforge/input.h
+3
include/keraforge/input.h
+1
include/keraforge/sprites.h
+1
include/keraforge/sprites.h
+4
include/keraforge/world.h
+4
include/keraforge/world.h
···
40
40
/* Get a pointer to the tile ID at a given position. */
41
41
kf_tileid_t *kf_world_gettile(struct kf_world *world, u32 x, u32 y);
42
42
43
+
/* Draw visible collision rectangles. */
44
+
struct kf_actor;
45
+
void kf_world_drawcolliders(struct kf_world *world, struct kf_actor *player, Camera2D camera);
46
+
43
47
/* Draw the part of the world visible to the given camera. */
44
48
void kf_world_draw(struct kf_world *world, Camera2D camera);
45
49
+51
-6
src/actor.c
+51
-6
src/actor.c
···
13
13
14
14
actor->speed = 25;
15
15
actor->speedmod = 1;
16
-
actor->friction = 1.25f;
16
+
actor->friction = 1.5f;
17
17
actor->pointing = kf_north;
18
18
19
19
return actor;
20
20
}
21
21
22
+
struct kf_spritesheet kf_actor_loadspritesheet(char *filename)
23
+
{
24
+
return kf_loadspritesheet(filename, 20, 20);
25
+
}
26
+
22
27
int kf_actor_canmovetowards(struct kf_world *world, struct kf_actor *actor, struct kf_vec2(f32) dir)
23
28
{
24
-
Rectangle r = {actor->pos.x - actor->size.x / 2, actor->pos.y - actor->size.y / 2, actor->size.x, actor->size.y};
29
+
Rectangle r = {
30
+
actor->pos.x + (actor->size.x / 2) + actor->sizeoffset.x,
31
+
actor->pos.y + (actor->size.y / 2) + actor->sizeoffset.y,
32
+
actor->size.x,
33
+
actor->size.y
34
+
};
25
35
r.x += dir.x;
26
36
r.y += dir.y;
27
37
···
37
47
Rectangle tr = {
38
48
trx,
39
49
sy * KF_TILE_SIZE_PX,
40
-
KF_TILE_SIZE_PX + 1,
41
-
KF_TILE_SIZE_PX + 1,
50
+
/* TODO:
51
+
Subtracting 1 as a bandaid fix to high velocities causing the player to be stopped early in collisions.
52
+
This is a very notorious problem in 3D collision and fwik there are plenty of 2D solutions.
53
+
I'll research and implement one eventually:tm: */
54
+
KF_TILE_SIZE_PX - 1,
55
+
KF_TILE_SIZE_PX - 1,
42
56
}; /* tile rect */
43
57
u32 x;
44
58
kf_tileid_t *tile = kf_world_gettile(world, sx, sy);
···
78
92
delta.y = 0;
79
93
}
80
94
81
-
if (delta.y > 0)
82
-
self->pointing = kf_north;
95
+
if (!self->controlled)
96
+
{
97
+
if (delta.y > 0)
98
+
self->pointing = kf_south;
99
+
else if (delta.y < 0)
100
+
self->pointing = kf_north;
101
+
else if (delta.x < 0)
102
+
self->pointing = kf_west;
103
+
else if (delta.x > 0)
104
+
self->pointing = kf_east;
105
+
}
83
106
84
107
self->pos = kf_add_vec2(f32)(self->pos, delta);
85
108
···
100
123
else if (self->speedmod)
101
124
self->speedmod -= self->friction * self->friction * dt;
102
125
}
126
+
127
+
void kf_actor_draw(struct kf_actor *actor)
128
+
{
129
+
int x = 0, y = 0;
130
+
131
+
switch (actor->pointing)
132
+
{
133
+
case kf_south: y = 0; break;
134
+
case kf_east: y = 1; break;
135
+
case kf_west: y = 2; break;
136
+
case kf_north: y = 3; break;
137
+
}
138
+
139
+
if (actor->vel.x != 0 || actor->vel.y != 0)
140
+
x += 7; /* walk sprites */
141
+
142
+
x += (kf_frame / 15) % 4;
143
+
144
+
/* todo: run and jump */
145
+
146
+
kf_drawsprite(&actor->sprites, actor->pos.x, actor->pos.y, x, y);
147
+
}
+5
src/graphics.c
+5
src/graphics.c
+2
-1
src/input.c
+2
-1
src/input.c
+94
-42
src/main.c
+94
-42
src/main.c
···
1
1
#include "keraforge/input.h"
2
-
#include "keraforge/sprites.h"
3
-
#include "keraforge/ui.h"
2
+
#include <keraforge.h>
4
3
#include <raylib.h>
5
4
#include <raymath.h>
6
-
#include <keraforge.h>
7
5
#include <stdio.h>
8
6
#include <stdlib.h>
9
7
10
8
static Camera2D cam;
11
-
static f32 dt = 0;
12
9
static struct kf_vec2(u32) select = { 0, 0 };
13
10
static int selected_tile = 0;
14
11
···
23
20
inputbind_move_down,
24
21
inputbind_move_left,
25
22
inputbind_move_right,
23
+
inputbind_ui_up,
24
+
inputbind_ui_down,
25
+
inputbind_ui_left,
26
+
inputbind_ui_right,
26
27
inputbind_select,
27
28
inputbind_cancel,
28
-
inputbind_palette
29
+
inputbind_pause,
30
+
inputbind_palette,
31
+
inputbind_zoom_reset,
32
+
inputbind_zoom_in,
33
+
inputbind_zoom_out
29
34
;
30
35
31
36
static void loadbinds()
32
37
{
33
-
inputbind_move_up = kf_addinput("move_up", KEY_UP, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y);
34
-
inputbind_move_down = kf_addinput("move_down", KEY_DOWN, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y);
35
-
inputbind_move_left = kf_addinput("move_left", KEY_LEFT, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X);
36
-
inputbind_move_right = kf_addinput("move_right", KEY_RIGHT, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X);
37
-
inputbind_select = kf_addinput("select", KEY_Z, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_DOWN, GAMEPAD_AXIS_UNKNOWN);
38
-
inputbind_cancel = kf_addinput("cancel", KEY_X, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, GAMEPAD_AXIS_UNKNOWN);
39
-
inputbind_palette = kf_addinput("palette", KEY_TAB, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_UP, GAMEPAD_AXIS_UNKNOWN);
38
+
inputbind_move_up = kf_addinput("move_up", KEY_W, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y);
39
+
inputbind_move_down = kf_addinput("move_down", KEY_S, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y);
40
+
inputbind_move_left = kf_addinput("move_left", KEY_A, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X);
41
+
inputbind_move_right = kf_addinput("move_right", KEY_D, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X);
42
+
43
+
inputbind_ui_up = kf_addinput("ui_up", KEY_W, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_UP, GAMEPAD_AXIS_UNKNOWN);
44
+
inputbind_ui_down = kf_addinput("ui_down", KEY_S, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_DOWN, GAMEPAD_AXIS_UNKNOWN);
45
+
inputbind_ui_left = kf_addinput("ui_left", KEY_A, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_LEFT, GAMEPAD_AXIS_UNKNOWN);
46
+
inputbind_ui_right = kf_addinput("ui_right", KEY_D, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_LEFT_FACE_RIGHT, GAMEPAD_AXIS_UNKNOWN);
47
+
48
+
inputbind_select = kf_addinput("select", KEY_F, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_DOWN, GAMEPAD_AXIS_UNKNOWN);
49
+
inputbind_cancel = kf_addinput("cancel", KEY_Q, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, GAMEPAD_AXIS_UNKNOWN);
50
+
51
+
inputbind_pause = kf_addinput("pause", KEY_ESCAPE, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_MIDDLE_RIGHT, GAMEPAD_AXIS_UNKNOWN);
52
+
inputbind_palette = kf_addinput("palette", KEY_TAB, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_UP, GAMEPAD_AXIS_UNKNOWN);
53
+
54
+
inputbind_zoom_reset = kf_addinput("zoom_reset", KEY_ZERO, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_UNKNOWN);
55
+
inputbind_zoom_in = kf_addinput("zoom_in", KEY_EQUAL, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_UNKNOWN);
56
+
inputbind_zoom_out = kf_addinput("zoom_out", KEY_MINUS, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_UNKNOWN);
40
57
}
41
58
42
59
static void _player_tick_move(struct kf_actor *self)
43
60
{
61
+
struct kf_vec2(f32) v = {0, 0};
62
+
63
+
/* gamepad axis movement */
64
+
f32 gpx = kf_getgamepadaxis(inputbind_move_left);
65
+
f32 gpy = kf_getgamepadaxis(inputbind_move_up);
66
+
// f32 gpx = GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X);
67
+
// f32 gpy = GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y);
68
+
if (gpx > kf_deadzone || gpx < -kf_deadzone || gpy > kf_deadzone || gpy < -kf_deadzone)
69
+
{
70
+
v.y = gpy;
71
+
v.x = gpx;
72
+
73
+
f32 angle = Vector2LineAngle(Vector2Zero(), (Vector2){gpx, gpy}) * RAD2DEG;
74
+
angle /= 90;
75
+
switch ((int)roundf(angle))
76
+
{
77
+
case 0: self->pointing = kf_east; break;
78
+
case 1: self->pointing = kf_north; break;
79
+
case -2:
80
+
case 2: self->pointing = kf_west; break;
81
+
case -1: self->pointing = kf_south; break;
82
+
}
83
+
84
+
goto done;
85
+
}
86
+
87
+
/* non-axis movement */
44
88
bool w = kf_checkinputdown(inputbind_move_up);
45
89
bool s = kf_checkinputdown(inputbind_move_down);
46
90
bool a = kf_checkinputdown(inputbind_move_left);
47
91
bool d = kf_checkinputdown(inputbind_move_right);
48
92
49
-
struct kf_vec2(f32) v = {0, 0};
93
+
if (a && d) { v.x = 0; }
94
+
else if (a) { v.x = -1; self->pointing = kf_west; }
95
+
else if (d) { v.x = 1; self->pointing = kf_east; }
50
96
51
-
if (w && s)
52
-
v.y = 0;
53
-
else if (w)
54
-
v.y = -1;
55
-
else if (s)
56
-
v.y = 1;
97
+
if (w && s) { v.y = 0; }
98
+
else if (w) { v.y = -1; self->pointing = kf_north; }
99
+
else if (s) { v.y = 1; self->pointing = kf_south; }
57
100
58
-
if (a && d)
59
-
v.x = 0;
60
-
else if (a)
61
-
v.x = -1;
62
-
else if (d)
63
-
v.x = 1;
101
+
v = kf_normalize_vec2(f32)(v);
64
102
103
+
done:
65
104
if (v.x || v.y)
66
-
kf_actor_addforce(self, kf_normalize_vec2(f32)(v));
67
-
68
-
if (IsKeyPressed(KEY_SPACE) && self->speedmod <= 1.0f)
69
-
self->speedmod = 2.0f;
105
+
kf_actor_addforce(self, v);
70
106
}
71
107
72
108
static void _player_tick(struct kf_world *world, struct kf_actor *self)
···
74
110
if (menu == menu_none)
75
111
_player_tick_move(self);
76
112
77
-
kf_actor_move(world, self, dt);
113
+
kf_actor_move(world, self, kf_dts);
78
114
}
79
115
80
116
static void _player_draw(struct kf_world *world, struct kf_actor *self)
81
117
{
82
118
(void)world;
83
-
DrawCircle(self->pos.x, self->pos.y, 4, RED);
119
+
// DrawRectangleLines(self->pos.x + self->size.x/2 + self->sizeoffset.x, self->pos.y + self->size.y/2 + self->sizeoffset.y, self->size.x, self->size.y, RED);
120
+
kf_actor_draw(self);
84
121
85
-
cam.target.x = self->pos.x;
86
-
cam.target.y = self->pos.y;
122
+
cam.target.x = self->pos.x + (self->size.x / 2);
123
+
cam.target.y = self->pos.y + (self->size.y / 2);
87
124
}
88
125
89
126
int main(int argc, const char *argv[])
···
91
128
(void)argc;
92
129
(void)argv;
93
130
94
-
loadbinds();
95
-
96
131
SetTraceLogLevel(LOG_WARNING);
97
132
InitWindow(800, 600, "Keraforge");
98
133
SetTargetFPS(60);
99
134
SetExitKey(KEY_NULL);
135
+
136
+
loadbinds();
100
137
101
138
kf_tiles.key[0] = "grass";
102
139
kf_tiles.color[0] = GREEN;
···
110
147
struct kf_uiconfig *uiconfig = kf_ui_getconfig();
111
148
uiconfig->select = inputbind_select;
112
149
uiconfig->cancel = inputbind_cancel;
113
-
uiconfig->up = inputbind_move_up;
114
-
uiconfig->down = inputbind_move_down;
150
+
uiconfig->up = inputbind_ui_up;
151
+
uiconfig->down = inputbind_ui_down;
115
152
116
153
if (!DirectoryExists("data"))
117
154
MakeDirectory("data");
···
146
183
exit(1);
147
184
}
148
185
149
-
struct kf_actor *player = kf_actor_new(kf_loadspritesheet("data/res/player.png", 16, 16), 6, 6, true);
186
+
struct kf_actor *player = kf_actor_new(kf_actor_loadspritesheet("data/res/img/char/whom.png"), 10, 10, true);
187
+
player->sizeoffset.y = 6;
150
188
player->pos.x = world->width / 4.0f * KF_TILE_SIZE_PX;
151
189
player->pos.y = world->height / 4.0f * KF_TILE_SIZE_PX;
152
190
player->tick = _player_tick;
153
191
player->draw = _player_draw;
192
+
player->controlled = true;
154
193
155
-
cam = (Camera2D){{GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f}, {0, 0}, 0, 2};
194
+
cam = (Camera2D){0};
195
+
cam.offset.x = GetScreenWidth() / 2.0f;
196
+
cam.offset.y = GetScreenHeight() / 2.0f;
197
+
cam.zoom = 2;
198
+
156
199
int running = 1;
157
200
while (!WindowShouldClose() && running)
158
201
{
···
170
213
171
214
if (kf_checkinputpress(inputbind_palette))
172
215
menu = menu_palette;
173
-
else if (IsKeyPressed(KEY_ESCAPE) && menu == menu_none)
216
+
else if (kf_checkinputpress(inputbind_pause) && menu == menu_none)
174
217
menu = menu_escape;
218
+
else if (kf_checkinputpress(inputbind_zoom_reset))
219
+
cam.zoom = 2;
220
+
else if (kf_checkinputpress(inputbind_zoom_in) && cam.zoom < 3.50f)
221
+
cam.zoom += 0.25f;
222
+
else if (kf_checkinputpress(inputbind_zoom_out) && cam.zoom > 1.00f)
223
+
cam.zoom -= 0.25f;
175
224
176
225
BeginDrawing();
177
226
ClearBackground(WHITE);
178
227
179
228
BeginMode2D(cam);
180
229
kf_world_draw(world, cam);
230
+
kf_world_drawcolliders(world, player, cam);
181
231
if (select.x < world->width && select.y < world->height)
182
232
{
183
233
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
···
213
263
}
214
264
215
265
DrawFPS(0, 0);
216
-
DrawText(TextFormat("%f", dt), 0, 20, 20, RED);
266
+
DrawText(TextFormat("%f", kf_dts), 0, 20, 20, RED);
217
267
218
268
EndDrawing();
219
269
220
-
dt = GetFrameTime();
270
+
kf_frame++;
271
+
kf_dts = GetFrameTime();
272
+
kf_dtms = kf_dts * 1000;
221
273
}
222
274
223
275
if (world)
+22
src/sprites.c
+22
src/sprites.c
···
1
1
#include <keraforge.h>
2
+
#include <raylib.h>
2
3
3
4
struct kf_spritesheet kf_loadspritesheet(char *filename, int spritewidth, int spriteheight)
4
5
{
···
10
11
.nsprites = (tex.width / spritewidth) * (tex.height / spriteheight),
11
12
};
12
13
}
14
+
15
+
inline
16
+
void kf_drawsprite(struct kf_spritesheet *sheet, f32 x, f32 y, int spritex, int spritey)
17
+
{
18
+
DrawTexturePro(
19
+
sheet->texture,
20
+
(Rectangle){
21
+
spritex * sheet->spritewidth,
22
+
spritey * sheet->spriteheight,
23
+
sheet->spritewidth,
24
+
sheet->spriteheight },
25
+
(Rectangle){
26
+
x,
27
+
y,
28
+
sheet->spritewidth,
29
+
sheet->spriteheight },
30
+
(Vector2){0, 0},
31
+
0,
32
+
WHITE
33
+
);
34
+
}
+3
-3
src/ui.c
+3
-3
src/ui.c
···
62
62
63
63
skip_text:
64
64
65
-
if (kf_checkkeypress(_kf_uiconfig.select) || kf_checkkeypress(_kf_uiconfig.cancel))
65
+
if (kf_checkinputpress(_kf_uiconfig.select) || kf_checkinputpress(_kf_uiconfig.cancel))
66
66
return 1;
67
-
else if (kf_checkkeypress(_kf_uiconfig.down) && *choice < nchoices)
67
+
else if (kf_checkinputpress(_kf_uiconfig.down) && *choice < nchoices)
68
68
(*choice)++;
69
-
else if (kf_checkkeypress(_kf_uiconfig.up) && *choice > 0)
69
+
else if (kf_checkinputpress(_kf_uiconfig.up) && *choice > 0)
70
70
(*choice)--;
71
71
72
72
return 0;
+46
src/world.c
+46
src/world.c
···
27
27
return &world->map[y*world->width + x];
28
28
}
29
29
30
+
void kf_world_drawcolliders(struct kf_world *world, struct kf_actor *player, Camera2D camera)
31
+
{
32
+
Rectangle r = {
33
+
player->pos.x + (player->size.x / 2) + player->sizeoffset.x,
34
+
player->pos.y + (player->size.y / 2) + player->sizeoffset.y,
35
+
player->size.x,
36
+
player->size.y
37
+
};
38
+
39
+
DrawRectangleLinesEx(r, 1, BLUE);
40
+
41
+
const Vector2 start = GetScreenToWorld2D((Vector2){0, 0}, camera);
42
+
const Vector2 end = GetScreenToWorld2D((Vector2){GetScreenWidth(), GetScreenHeight()}, camera);
43
+
const u32 sx = fmax(0, floorf(start.x / KF_TILE_SIZE_PX));
44
+
const u32 sy = fmax(0, floorf(start.y / KF_TILE_SIZE_PX));
45
+
const u32 ex = fmin(world->width, ceilf(end.x / KF_TILE_SIZE_PX));
46
+
const u32 ey = fmin(world->height, ceilf(end.y / KF_TILE_SIZE_PX));
47
+
const size_t down = world->width - ex + sx - 1; /* number of indexes to add to reach the next tile down */
48
+
kf_tileid_t *tile = kf_world_gettile(world, sx, sy);
49
+
50
+
/* check if any tiles will collide with the actor's rect */
51
+
const f32 trx = sx * KF_TILE_SIZE_PX;
52
+
Rectangle tr = {
53
+
trx,
54
+
sy * KF_TILE_SIZE_PX,
55
+
KF_TILE_SIZE_PX,
56
+
KF_TILE_SIZE_PX,
57
+
}; /* tile rect */
58
+
u32 x;
59
+
60
+
for (u32 y = sy ; y <= ey ; y++)
61
+
{
62
+
for (x = sx ; x <= ex ; x++)
63
+
{
64
+
if (kf_tiles.collide[*tile])
65
+
DrawRectangleLinesEx(tr, 1, CheckCollisionRecs(r, tr) ? RED : BLACK);
66
+
67
+
tile++; /* shift tile pointer to the right */
68
+
tr.x += KF_TILE_SIZE_PX;
69
+
}
70
+
tile += down; /* shift tile pointer down */
71
+
tr.x = trx;
72
+
tr.y += KF_TILE_SIZE_PX;
73
+
}
74
+
}
75
+
30
76
void kf_world_draw(struct kf_world *world, Camera2D camera)
31
77
{
32
78
const Vector2 start = GetScreenToWorld2D((Vector2){0, 0}, camera);