A game engine for top-down 2D RPG games.
rpg
game-engine
raylib
c99
1#include "keraforge/input.h"
2#include "keraforge/ui.h"
3#include <raylib.h>
4#include <raymath.h>
5#include <keraforge.h>
6#include <stdio.h>
7#include <stdlib.h>
8
9static Camera2D cam;
10static f32 dt = 0;
11static struct kf_vec2(u32) select = { 0, 0 };
12static int selected_tile = 0;
13
14static struct
15{
16 enum { menu_none, menu_palette, menu_escape } menu;
17} menu;
18
19static kf_inputbind_t
20 inputbind_move_up,
21 inputbind_move_down,
22 inputbind_move_left,
23 inputbind_move_right,
24 inputbind_select,
25 inputbind_cancel,
26 inputbind_palette
27;
28
29static void loadbinds()
30{
31 inputbind_move_up = kf_addinput("move_up", KEY_UP, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y);
32 inputbind_move_down = kf_addinput("move_down", KEY_DOWN, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_Y);
33 inputbind_move_left = kf_addinput("move_left", KEY_LEFT, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X);
34 inputbind_move_right = kf_addinput("move_right", KEY_RIGHT, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_UNKNOWN, GAMEPAD_AXIS_LEFT_X);
35 inputbind_select = kf_addinput("select", KEY_Z, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_DOWN, GAMEPAD_AXIS_UNKNOWN);
36 inputbind_cancel = kf_addinput("cancel", KEY_X, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, GAMEPAD_AXIS_UNKNOWN);
37 inputbind_palette = kf_addinput("palette", KEY_TAB, MOUSE_BUTTON_UNKNOWN, GAMEPAD_BUTTON_RIGHT_FACE_UP, GAMEPAD_AXIS_UNKNOWN);
38}
39
40static void _player_tick_move(struct kf_actor *self)
41{
42 bool w = kf_checkinputdown(inputbind_move_up);
43 bool s = kf_checkinputdown(inputbind_move_down);
44 bool a = kf_checkinputdown(inputbind_move_left);
45 bool d = kf_checkinputdown(inputbind_move_right);
46
47 struct kf_vec2(f32) v = {0, 0};
48
49 if (w && s)
50 v.y = 0;
51 else if (w)
52 v.y = -1;
53 else if (s)
54 v.y = 1;
55
56 if (a && d)
57 v.x = 0;
58 else if (a)
59 v.x = -1;
60 else if (d)
61 v.x = 1;
62
63 if (v.x || v.y)
64 kf_actor_addforce(self, kf_normalize_vec2(f32)(v));
65
66 if (IsKeyPressed(KEY_SPACE) && self->speedmod <= 1.0f)
67 self->speedmod = 2.0f;
68}
69
70static void _player_tick(struct kf_world *world, struct kf_actor *self)
71{
72 if (menu.menu == menu_none)
73 _player_tick_move(self);
74
75 kf_actor_move(world, self, dt);
76}
77
78static void _player_draw(struct kf_world *world, struct kf_actor *self)
79{
80 (void)world;
81 DrawCircle(self->pos.x, self->pos.y, 4, RED);
82
83 cam.target.x = self->pos.x;
84 cam.target.y = self->pos.y;
85}
86
87int main(int argc, const char *argv[])
88{
89 (void)argc;
90 (void)argv;
91
92 loadbinds();
93
94 SetTraceLogLevel(LOG_WARNING);
95 InitWindow(800, 600, "Keraforge");
96 SetTargetFPS(60);
97 SetExitKey(KEY_NULL);
98
99 kf_tiles.key[0] = "grass";
100 kf_tiles.color[0] = GREEN;
101 kf_tiles.key[1] = "dirt";
102 kf_tiles.color[1] = BROWN;
103 kf_tiles.key[2] = "stone";
104 kf_tiles.color[2] = GRAY;
105 kf_tiles.collide[2] = true;
106 kf_tiles.count = 3;
107
108 struct kf_uiconfig *uiconfig = kf_ui_getconfig();
109 uiconfig->select = inputbind_select;
110 uiconfig->cancel = inputbind_cancel;
111 uiconfig->up = inputbind_move_up;
112 uiconfig->down = inputbind_move_down;
113
114 if (!DirectoryExists("data"))
115 MakeDirectory("data");
116
117 struct kf_world *world = NULL;
118 if (!kf_exists("data/map.bin"))
119 {
120 printf("-> creating world\n");
121 world = kf_world_new(128, 128, 0);
122 for (size_t i = 0 ; i < world->width*world->height ; i += (size_t)((float)(rand())/RAND_MAX*10))
123 world->map[i] = (rand()/(float)RAND_MAX*3);
124 printf("-> saving world\n");
125 size_t len = kf_world_getsize(world);
126 printf("-> writing of %lu bytes\n", len);
127 if (!kf_writebin("data/map.bin", (u8 *)world, len))
128 {
129 fprintf(stderr, "error creating map: failed to save map.bin\n");
130 free(world);
131 exit(1);
132 }
133 }
134 else
135 {
136 printf("-> loading world\n");
137 size_t len = 0;
138 world = (struct kf_world *)kf_readbin("data/map.bin", &len);
139 printf("-> world is %lu bytes\n", len);
140 }
141 if (!world)
142 {
143 fprintf(stderr, "error: failed to load world\n");
144 exit(1);
145 }
146
147 struct kf_actor *player = kf_actor_new();
148 player->pos.x = world->width / 4.0f * KF_TILE_SIZE_PX;
149 player->pos.y = world->height / 4.0f * KF_TILE_SIZE_PX;
150 player->vel.x = 0;
151 player->vel.y = 0;
152 player->size.x = 6;
153 player->size.y = 6;
154 player->speed = 25;
155 player->speedmod = 1;
156 player->friction = 1.25f;
157 player->collide = true;
158 player->tick = _player_tick;
159 player->draw = _player_draw;
160
161 cam = (Camera2D){{GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f}, {0, 0}, 0, 2};
162 int running = 1;
163 while (!WindowShouldClose() && running)
164 {
165 if (IsWindowResized())
166 {
167 cam.offset.x = GetScreenWidth() / 2.0f;
168 cam.offset.y = GetScreenHeight() / 2.0f;
169 }
170
171 player->tick(world, player);
172
173 Vector2 v = GetScreenToWorld2D(GetMousePosition(), cam);
174 select.x = v.x / KF_TILE_SIZE_PX;
175 select.y = v.y / KF_TILE_SIZE_PX;
176
177 if (kf_checkinputpress(inputbind_palette))
178 menu.menu = menu_palette;
179 else if (IsKeyPressed(KEY_ESCAPE) && menu.menu == menu_none)
180 menu.menu = menu_escape;
181
182 BeginDrawing();
183 ClearBackground(WHITE);
184
185 BeginMode2D(cam);
186 kf_world_draw(world, cam);
187 if (select.x < world->width && select.y < world->height)
188 {
189 if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
190 {
191 kf_tileid_t *t = kf_world_gettile(world, select.x, select.y);
192 *t = (kf_tileid_t)selected_tile;
193 }
194 DrawRectangleLines(select.x * KF_TILE_SIZE_PX, select.y * KF_TILE_SIZE_PX, KF_TILE_SIZE_PX, KF_TILE_SIZE_PX, WHITE);
195 }
196 player->draw(world, player);
197 EndMode2D();
198
199 switch (menu.menu)
200 {
201 case menu_none:
202 break;
203 case menu_palette:
204 if (kf_ui_choice("Select tile", &kf_tiles.key[0], kf_tiles.count, &selected_tile))
205 menu.menu = menu_none;
206 break;
207 case menu_escape:
208 {
209 static int result = 0;
210 if (kf_ui_yesno("Exit game?", &result))
211 {
212 menu.menu = menu_none;
213 if (result == 0)
214 running = 0;
215 result = 0;
216 }
217 break;
218 }
219 }
220
221 DrawFPS(0, 0);
222 DrawText(TextFormat("%f", dt), 0, 20, 20, RED);
223
224 EndDrawing();
225
226 dt = GetFrameTime();
227 }
228
229 if (world)
230 {
231 if (!kf_writebin("data/map.bin", (u8 *)world, kf_world_getsize(world)))
232 fprintf(stderr, "error: failed to save map.bin\n");
233 free(world);
234 }
235 CloseWindow();
236
237 return 0;
238}