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}