]> git.mcshandy.xyz Git - barrow_crawler/commitdiff
Introduce main menu, menu control system, and get the Quit button working as a proof...
authorRandy McShandy <randy@mcshandy.xyz>
Sun, 15 Jun 2025 03:52:38 +0000 (22:52 -0500)
committerRandy McShandy <randy@mcshandy.xyz>
Sun, 15 Jun 2025 03:52:38 +0000 (22:52 -0500)
bin/posix_BC
src/enums.h [new file with mode: 0644]
src/main.c
src/menufuncs.c [new file with mode: 0644]
src/menufuncs.h [new file with mode: 0644]
src/render_raylib.c
src/structs.c
src/structs.h
src/ui.c [new file with mode: 0644]
src/ui.h [new file with mode: 0644]

index d2ca837bc1eb65e83d19bdaa8c61ee5c610c0a89..0ce3cc5eebdaf79b0ea2b08d02fc2573a1d50853 100755 (executable)
Binary files a/bin/posix_BC and b/bin/posix_BC differ
diff --git a/src/enums.h b/src/enums.h
new file mode 100644 (file)
index 0000000..503f6bf
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef __LOCAL_ENUMS__
+#define __LOCAL_ENUMS__
+
+typedef enum
+{
+  Square = 0,
+  Circle,
+  Barrow
+} Shape;
+
+typedef enum
+{
+  STATUS_TEXT_FADE_TIME = 0,
+  STATUS_TIMERS
+} eTimers;
+
+typedef enum
+{
+  UNKNOWN = 0,
+  GOOD,
+  BAD,
+  WAITING,
+  FINISHED,
+  STATES
+} eState;
+
+typedef enum
+{
+  SAVE_FILE_LOAD = 0,
+  SAVE_FILE_SAVE,
+
+  /* Resource load and generation here eventually too */
+
+  PROCESSES
+} eProcess;
+
+typedef enum
+{
+  STATUS_TEXT_PLACE = 0,
+  TEXT_PLACES,
+} eTextPlaces;
+
+typedef enum
+{
+  RENDER_MAIN_MENU = 0,
+  RENDER_RESOURCE_WAIT,
+  RENDER_RESOURCE_READY,
+  RENDER_GAME,
+  RENDER_MODES
+} eRenderMode;
+
+typedef enum
+{
+  CONTROL_MAIN_MENU = 0,
+  CONTROL_RESOURCE_WAIT,
+  CONTROL_RESOURCE_READY,
+  CONTROL_GAME,
+  CONTROL_MODES
+} eControlMode;
+
+#define MENU_ITEMS  \
+  X(MI_NONE)                     \
+  X(MI_NEW_GAME)         \
+  X(MI_LOAD_GAME)        \
+  X(MI_SAVE_GAME)        \
+  X(MI_QUIT)                     \
+  X(MI_ITEMS)
+
+#define X(item) item,
+typedef enum
+{
+  MENU_ITEMS
+} eMenuItem;
+#undef X
+
+
+#endif /* __LOCAL_ENUMS__ */
+
index 6359d2b9bc056c5b176a28b7b02003ae626b4677..c0949a1125932e4e3b4b768a6432c647876e48f1 100755 (executable)
@@ -39,7 +39,7 @@ int main(int argc, char** argv)
        start_render_loop();
        pthread_join(generator_thread, NULL);
 #else
-       resource_state = 1;
+       resource_state = 0;
        start_render_loop();
 #endif /* ENABLE_BARROWGEN */
 
diff --git a/src/menufuncs.c b/src/menufuncs.c
new file mode 100644 (file)
index 0000000..76462db
--- /dev/null
@@ -0,0 +1,52 @@
+#include <assert.h>
+#include <raylib.h>
+#include <stdio.h>
+
+#include "structs.h"
+
+bool menu_action_MI_NONE()
+{ return true; }
+
+bool menu_action_MI_NEW_GAME()
+{
+  bool response = false;
+
+
+
+  return response;
+}
+
+bool menu_action_MI_LOAD_GAME()
+{
+  bool response = false;
+
+
+
+  return response;
+}
+
+bool menu_action_MI_SAVE_GAME()
+{
+  bool response = false;
+
+
+
+  return response;
+}
+
+bool menu_action_MI_QUIT()
+{
+  bool response = false;
+
+  printf("shutting down!\n");
+  playtime.should_quit = 1;
+
+  return response;
+}
+
+bool menu_action_MI_ITEMS()
+{
+  /* Unreachable! */
+  assert(0);
+}
+
diff --git a/src/menufuncs.h b/src/menufuncs.h
new file mode 100644 (file)
index 0000000..c82e776
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __MENUFUNCS__
+#define __MENUFUNCS__
+
+#include <stdbool.h>
+#include "enums.h"
+
+typedef bool(*menufunc)();
+
+bool menu_action_MI_NONE();
+bool menu_action_MI_NEW_GAME();
+bool menu_action_MI_LOAD_GAME();
+bool menu_action_MI_SAVE_GAME();
+bool menu_action_MI_QUIT();
+bool menu_action_MI_ITEMS();
+
+extern menufunc menufuncs[MI_ITEMS + 1];
+
+#endif /* __MENU_FUNCS__ */
+
index 533f49b370a99bfd843697816e8ca43838db88a7..979171f11db9842f5546bba55a1e67e678e07f20 100644 (file)
@@ -10,6 +10,7 @@
 #include <raymath.h>
 
 #include "structs.h"
+#include "ui.h"
 
 #define str(x) #x
 #define xstr(x) str(x)
@@ -119,9 +120,8 @@ int draw_collision_mesh = 0;
 
 typedef void(*renderfunc)();
 typedef void(*controlfunc)();
-#define MAX_RENDERFUNCS 3U
-renderfunc renderfuncs[MAX_RENDERFUNCS];
-controlfunc controlfuncs[MAX_RENDERFUNCS];
+renderfunc renderfuncs[RENDER_MODES];
+controlfunc controlfuncs[CONTROL_MODES];
 
 Color ColorLerp(Color c1, Color c2, float amount)
 {
@@ -135,6 +135,34 @@ Color ColorLerp(Color c1, Color c2, float amount)
        return new_color;
 }
 
+void drawing_main_menu_mode()
+{
+  ClearBackground(LIGHTGRAY);
+
+  const size_t title_font_size = 64;
+  const size_t sub_font_size = 48;
+  const char title_text[] = "Randy's Barrow Adventure";
+
+       DrawText(title_text,
+                       (fscreen_dims.x/2.0f) - (MeasureText(title_text, title_font_size)/2.0f), (fscreen_dims.y/4.0f) * 1.0f, title_font_size, BLACK);
+
+  for (size_t n = 0; n < 3; n++)
+  {
+    const MenuButton button = main_menu_items[n];
+    const char* text = menu_items[button.item_index].text;
+    const Vector2 text_v2 = button.text_v2;
+    const Rectangle button_bound = button.button_bound;
+
+    const bool mouse_in_container = menu_info.hover_item == button.item_index;
+    const Color fg = (mouse_in_container) ? button.fg : button.bg;
+    const Color bg = (mouse_in_container) ? button.bg : button.fg;
+
+    DrawRectangle(button_bound.x, button_bound.y, button_bound.width, button_bound.height, bg);
+    DrawText(text,
+        text_v2.x, text_v2.y, sub_font_size, fg);
+  }
+}
+
 /* Render the regular game mode.
 NOTE: Only call inside a Raylib BeginDrawing() block!
 */
@@ -188,9 +216,9 @@ void drawing_game_mode()
        Rectangle minimap_dest = {.width = 64.0f*img_export_scale.x, .height = 64.0f*img_export_scale.y, .x = 0.0f, .y = 0.0f};
        Rectangle minimap_src = {.width = barrow_texture.width, .height = barrow_texture.height, .x = 0.0f, .y = 0.0f};
 
-  if (timers[E_STATUS_TEXT_FADE_TIME].time > 0.0)
+  if (timers[STATUS_TEXT_FADE_TIME].time > 0.0)
   {
-    const char* text = text_places[E_STATUS_TEXT_PLACE];
+    const char* text = text_places[STATUS_TEXT_PLACE];
                DrawText(TextFormat("%s", text), 0, screen_dims.y - 32, 32, GREEN);
   }
 
@@ -314,44 +342,44 @@ void control_game_mode()
                        playtime.cam.position = cam_reset_position;
                }
 
-    if (IsKeyReleased(KEY_F4) && timers[E_STATUS_TEXT_FADE_TIME].time == 0)
+    if (IsKeyReleased(KEY_F4) && timers[STATUS_TEXT_FADE_TIME].time == 0)
     {
-      ProcessInfo* process = &processes[E_SAVE_FILE_SAVE];
-      processes[E_SAVE_FILE_SAVE].state     = E_WAITING;
+      ProcessInfo* process = &processes[SAVE_FILE_SAVE];
+      processes[SAVE_FILE_SAVE].state     = WAITING;
 
       const int success = save_game(playtime);
 
-      if ((success == 0) && timers[E_STATUS_TEXT_FADE_TIME].time == 0)
+      if ((success == 0) && timers[STATUS_TEXT_FADE_TIME].time == 0)
       {
-        processes[E_SAVE_FILE_SAVE].state = E_FINISHED;
+        processes[SAVE_FILE_SAVE].state = FINISHED;
       }
       else if (success != 0)
       {
-        processes[E_SAVE_FILE_SAVE].state = E_BAD;
+        processes[SAVE_FILE_SAVE].state = BAD;
       }
 
-      memcpy(text_places[E_STATUS_TEXT_PLACE], process->info_text[process->state], 64);
-      timers[E_STATUS_TEXT_FADE_TIME].time = timers[E_STATUS_TEXT_FADE_TIME].max;
+      memcpy(text_places[STATUS_TEXT_PLACE], process->info_text[process->state], 64);
+      timers[STATUS_TEXT_FADE_TIME].time = timers[STATUS_TEXT_FADE_TIME].max;
     }
 
-    if (IsKeyReleased(KEY_F5) && timers[E_STATUS_TEXT_FADE_TIME].time == 0)
+    if (IsKeyReleased(KEY_F5) && timers[STATUS_TEXT_FADE_TIME].time == 0)
     {
-      ProcessInfo* process = &processes[E_SAVE_FILE_LOAD];
-      process->state     = E_WAITING;
+      ProcessInfo* process = &processes[SAVE_FILE_LOAD];
+      process->state     = WAITING;
 
       const int success = load_game();
 
-      if ((success == 0 ) && timers[E_STATUS_TEXT_FADE_TIME].time == 0.0)
+      if ((success == 0 ) && timers[STATUS_TEXT_FADE_TIME].time == 0.0)
       {
-        process->state = E_FINISHED;
+        process->state = FINISHED;
       }
       else if (success != 0)
       {
-        process->state = E_BAD;
+        process->state = BAD;
       }
 
-      memcpy(text_places[E_STATUS_TEXT_PLACE], process->info_text[process->state], 64);
-      timers[E_STATUS_TEXT_FADE_TIME].time = timers[E_STATUS_TEXT_FADE_TIME].max;
+      memcpy(text_places[STATUS_TEXT_PLACE], process->info_text[process->state], 64);
+      timers[STATUS_TEXT_FADE_TIME].time = timers[STATUS_TEXT_FADE_TIME].max;
     }
 
                if (IsKeyReleased(KEY_ONE))
@@ -393,6 +421,7 @@ void control_resource_wait_mode()
 {
 }
 
+
 void wait_initialize_shaders()
 {
        shader = LoadShader("./src/shaders/lighting.vs", "./src/shaders/lighting.fs");
@@ -570,18 +599,20 @@ void initialize_prerenderer()
 
        /* Mode handler setup */
        {
-               renderfuncs[0U] = drawing_resource_wait_mode;
-               renderfuncs[1U] = drawing_resource_ready_mode;
-               renderfuncs[2U] = drawing_game_mode;
-               controlfuncs[0U] = control_resource_wait_mode;
-               controlfuncs[1U] = control_resource_ready_mode;
-               controlfuncs[2U] = control_game_mode;
+               renderfuncs[RENDER_MAIN_MENU]       = drawing_main_menu_mode;
+               renderfuncs[RENDER_RESOURCE_WAIT]   = drawing_resource_wait_mode;
+               renderfuncs[RENDER_RESOURCE_READY]  = drawing_resource_ready_mode;
+               renderfuncs[RENDER_GAME]            = drawing_game_mode;
+               controlfuncs[CONTROL_MAIN_MENU]     = control_main_menu_mode;
+               controlfuncs[CONTROL_RESOURCE_WAIT]   = control_resource_wait_mode;
+               controlfuncs[CONTROL_RESOURCE_READY]  = control_resource_ready_mode;
+               controlfuncs[CONTROL_GAME]            = control_game_mode;
        }
 }
 
 void update_timers()
 {
-  for (int t = 0; t < E_STATUS_TIMERS; t++)
+  for (int t = 0; t < STATUS_TIMERS; t++)
   {
     if (timers[t].time > 0)
     {
@@ -592,9 +623,14 @@ void update_timers()
 
 void start_render_loop()
 {
+  /* Things that need to happen before Raylib init */
        initialize_prerenderer();
+
        SetTraceLogLevel(LOG_ALL);
        InitWindow(fscreen_dims.x, fscreen_dims.y, screen_title);
+
+  /* Things that need to happen after Raylib init */
+  init_menus(fscreen_dims);
        wait_initialize_shaders();
 
        SetTargetFPS(target_fps);
@@ -605,7 +641,6 @@ void start_render_loop()
                int func_idx = resource_state;
 
                player_collide_point = playtime.cam.position;
-               //player_collide_point.y -= 1.0f;
                controlfuncs[func_idx]();
 
                SetShaderValue(shader, cam_position_shader_loc, &playtime.cam.position, SHADER_UNIFORM_VEC3);
@@ -624,9 +659,9 @@ void start_render_loop()
                /* Decay forward velocity and rotation */
                playtime.player_velocity.x = Lerp(playtime.player_velocity.x, 0.0f, forward_speed_decay * frame_time);
                playtime.player_rotation.x = Lerp(playtime.player_rotation.x, 0.0f, rotate_speed_decay * frame_time);
-       }
 
-       playtime.should_quit = 1;
+    if (playtime.should_quit) break;
+       }
 
        UnloadImage(barrow_image);
        UnloadTexture(barrow_texture);
index 10e86f3caf78e5ec10beb4d8c7f930c857664704..72e729fc108a77095e651a3aca6a75f6883604f9 100644 (file)
@@ -14,7 +14,7 @@ RD_Opts puffer =
        .delta_t = 0.6f,
 
        .name = "puffer",
-       .shape = eCircle
+       .shape = Circle
 };
 
 // diff values influence tightness, delta between them shouldn't get wider than ~0.5 or narrower than ~0.4
@@ -28,7 +28,7 @@ RD_Opts barrow =
        .delta_t = 0.6f,
 
        .name = "barrow",
-       .shape = eBarrow
+       .shape = Barrow
 };
 
 RD_Opts worms =
@@ -41,7 +41,7 @@ RD_Opts worms =
        .delta_t = 0.6f,
 
        .name = "worms",
-       .shape = eSquare
+       .shape = Square
 };
 
 RD_Opts meiosis =
@@ -54,7 +54,7 @@ RD_Opts meiosis =
        .delta_t = 1.0f,
 
        .name = "meiosis",
-       .shape = eSquare
+       .shape = Square
 };
 
 const Mat3 laplacian_kernel =
@@ -72,20 +72,21 @@ const Mat3 laplacian_kernel =
 
 PlaytimeData playtime;
 
-Timer timers[E_STATUS_TIMERS] =
+Timer timers[STATUS_TIMERS] =
 {
   /* timer                            time    max   */
   /* E_STATUS_TEXT_FADE_TIME */     { 0,      4000  }
 };
 
-ProcessInfo processes[E_PROCESSES] =
+ProcessInfo processes[PROCESSES] =
 {
   /* Process              { STATE       Progress, { E_UNKNOWN,  E_GOOD, E_BAD,                    E_WAITING,    E_FINISHED  } } */
-  /* E_SAVE_FILE_LOAD */  { E_UNKNOWN,  0.0f,     { "",         "",     "Failed to load save.",   "Loading...", "Loaded"    } },
-  /* E_SAVE_FILE_SAVE */  { E_UNKNOWN,  0.0f,     { "",         "",     "Failed to create save.", "Saving...",  "Saved"     } }
+  /* E_SAVE_FILE_LOAD */  { UNKNOWN,    0.0f,     { "",         "",     "Failed to load save.",   "Loading...", "Loaded"    } },
+  /* E_SAVE_FILE_SAVE */  { UNKNOWN,    0.0f,     { "",         "",     "Failed to create save.", "Saving...",  "Saved"     } }
 };
 
-char text_places[E_TEXT_PLACES][64];
+
+char text_places[TEXT_PLACES][64];
 
 const char chars[6] = {' ', ' ', ' ', '+', '#', '@'};
 const int max_chars = sizeof(chars) / sizeof(chars[0]) - 1;
@@ -96,5 +97,8 @@ const int target_fps = 60;
 
 const IVec2 img_export_scale = {.x = 2, .y = 2};
 float resource_generation_progress = 0.0f;
+
+// Move this into menuinfo
+// Actually merge all of them into a display manager
 int resource_state = 0;
 
index eb4ff20d0884918a3117c264784abacb27f08a27..803c5411325e18377ac5d373790f72cec2f95184 100644 (file)
@@ -8,12 +8,8 @@
 #include <stdint.h>
 #include <raylib.h>
 
-typedef enum
-{
-       eSquare = 0,
-       eCircle,
-       eBarrow
-} Shape;
+#include "enums.h"
+#include "ui.h"
 
 typedef struct
 {
@@ -100,50 +96,19 @@ typedef struct
   float max;
 } Timer;
 
-typedef enum
-{
-  E_STATUS_TEXT_FADE_TIME = 0,
-  E_STATUS_TIMERS
-} eTimers;
-
-typedef enum
-{
-  E_UNKNOWN = 0,
-  E_GOOD,
-  E_BAD,
-  E_WAITING,
-  E_FINISHED,
-  E_STATES
-} eState;
-
-typedef enum
-{
-  E_SAVE_FILE_LOAD = 0,
-  E_SAVE_FILE_SAVE,
-
-  /* Resource load and generation here eventually too */
-
-  E_PROCESSES
-} eProcess;
-
 typedef struct
 {
   eState state;
   float progress;
-  char info_text[E_STATES][64];
+  char info_text[STATES][64];
 } ProcessInfo;
 
-typedef enum
-{
-  E_STATUS_TEXT_PLACE = 0,
-  E_TEXT_PLACES,
-} eTextPlaces;
-
 extern PlaytimeData playtime;
+extern MenuInfo menu_info;
 
-extern ProcessInfo processes[E_PROCESSES];
-extern Timer timers[E_STATUS_TIMERS];
-extern char text_places[E_TEXT_PLACES][64];
+extern ProcessInfo processes[PROCESSES];
+extern Timer timers[STATUS_TIMERS];
+extern char text_places[TEXT_PLACES][64];
 
 extern RD_Opts barrow;
 extern RD_Opts puffer;
diff --git a/src/ui.c b/src/ui.c
new file mode 100644 (file)
index 0000000..22e2ff6
--- /dev/null
+++ b/src/ui.c
@@ -0,0 +1,97 @@
+#include <stddef.h>
+#include <stdio.h>
+
+#include "menufuncs.h"
+#include "ui.h"
+
+const MenuItem menu_items[MI_ITEMS] =
+{
+  { MI_NONE,      ""          , false},
+  { MI_NEW_GAME,  "New Game"  , false},
+  { MI_LOAD_GAME, "Load Game" , false},
+  { MI_SAVE_GAME, "Save Game" , false},
+  { MI_QUIT,      "Quit"      , false},
+};
+
+MenuButton main_menu_items[3] =
+{
+  { MI_NEW_GAME , (Rectangle){}, (Vector2){}, DARKGRAY, LIGHTGRAY, 48 },
+  { MI_LOAD_GAME, (Rectangle){}, (Vector2){}, DARKGRAY, LIGHTGRAY, 48 },
+  { MI_QUIT     , (Rectangle){}, (Vector2){}, DARKGRAY, LIGHTGRAY, 48 }
+};
+
+MenuInfo menu_info =
+{
+  RENDER_MAIN_MENU,
+  CONTROL_MAIN_MENU,
+  MI_NONE
+};
+
+menufunc menufuncs[MI_ITEMS+1];
+
+void init_menus(Vector2 screen_dims)
+{
+  for (size_t n = 0; n < sizeof(main_menu_items)/sizeof(main_menu_items[0]); n++)
+  {
+    MenuButton* button = &main_menu_items[n];
+    const MenuItem* menu_item = &menu_items[button->item_index];
+    const int text_width = MeasureText(menu_item->text, button->font_size);
+    const Vector2 text_size = {.x = text_width, .y = button->font_size};
+
+    Vector2 text_v2 =
+    {
+      (screen_dims.x/2.0f) - (text_size.x / 2.0f),
+      (screen_dims.y/13.0f) * (n+5),
+    };
+
+    const uint32_t margin = 8U;
+    Rectangle button_bound =
+    (Rectangle){
+      .x       = text_v2.x - margin,
+      .y       = text_v2.y - margin,
+      .width   = text_size.x + margin * 2U,
+      .height  = text_size.y + margin,
+    };
+
+    button->button_bound = button_bound;
+    button->text_v2 = text_v2;
+
+  }
+
+#define X(emenu_item) menufuncs[emenu_item] = menu_action_ ## emenu_item;
+  MENU_ITEMS
+#undef X
+
+}
+
+void control_main_menu_mode()
+{
+  const Vector2 mouse = GetMousePosition();
+  const bool left_click = IsMouseButtonReleased(MOUSE_LEFT_BUTTON);
+
+  /* Figure out a click in menu item bounds
+     Set globals
+     Let renderfunc act on it
+   */
+
+  menu_info.hover_item = MI_NONE;
+  for (size_t n = 0; n < sizeof(main_menu_items)/sizeof(main_menu_items[0]); n++)
+  {
+    const MenuButton button = main_menu_items[n];
+    const Rectangle button_bound = button.button_bound;
+    const bool mouse_in_container = CheckCollisionPointRec(mouse, button_bound);
+
+    if (mouse_in_container)
+    {
+      menu_info.hover_item = button.item_index;
+      menu_info.hover_item_picked = menu_info.hover_item && left_click;
+    }
+
+    if ((menu_info.hover_item == button.item_index) && (menu_info.hover_item_picked))
+    {
+      menufuncs[menu_info.hover_item]();
+    }
+  }
+
+}
+
diff --git a/src/ui.h b/src/ui.h
new file mode 100644 (file)
index 0000000..446f865
--- /dev/null
+++ b/src/ui.h
@@ -0,0 +1,45 @@
+#ifndef __UI__
+#define __UI__
+
+#include <raylib.h>
+#include <stdint.h>
+
+#include "enums.h"
+
+typedef struct
+{
+  eRenderMode render_mode;
+  eControlMode control_mode;
+  eMenuItem hover_item;
+  bool hover_item_picked;
+} MenuInfo;
+
+typedef struct
+{
+  eMenuItem type;
+  char text[64];
+  bool selected;
+} MenuItem;
+
+typedef struct
+{
+  eMenuItem item_index;
+  Rectangle button_bound;
+  Vector2   text_v2;
+  Color bg;
+  Color fg;
+  int font_size;
+} MenuButton;
+
+extern MenuInfo menu_info;
+extern const MenuItem menu_items[MI_ITEMS];
+extern MenuButton main_menu_items[3];
+
+void init_menus(Vector2 screen_dims);
+void control_main_menu_mode();
+
+#define stringify2(x) #x
+#define stringify(x) stringify2(x)
+
+#endif /* __UI__ */
+