Compare commits

...

9 Commits

Author SHA1 Message Date
En Yi 8207558be7 Add title to level select scene 2024-07-08 21:08:55 +08:00
En Yi 9b6c364269 Add mouse support for scroll area
Internal Changelog:
- Refactor scroll area refocus function
    - This only triggers when selection is made
- Add mouse function process in test scene
- Add action for mouse selection
2024-07-08 19:16:10 +08:00
En Yi eff3d090df Add proper level selection actions
Internal Changelog:
- Scroll area will auto scroll to make selection completely visible
2024-07-08 18:42:29 +08:00
En Yi 41f3656ba1 Integrate Level Selection scene transition
Internal Changelog:
- Changing scene now return the scene to change into
- Set the level pack and selected level
2024-07-08 18:18:48 +08:00
En Yi 98b957a8ff Encapsulate scroll area as UI component 2024-07-08 18:02:56 +08:00
En Yi 6dd185b6cd Integrate scroll bar to level select 2024-07-08 12:40:27 +08:00
En Yi 33de816841 Replace hardcoded scene values to enum 2024-07-07 16:48:55 +08:00
En Yi 0c461d3167 Integrate level select screen to main
Not complete tho
2024-07-07 15:45:17 +08:00
En Yi 893fc1c73f Add level select scene
Just a render texture with simple scroll
2024-07-02 21:54:54 +08:00
15 changed files with 679 additions and 23 deletions

View File

@ -167,6 +167,18 @@ target_link_options(scene_man_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_libraries(scene_man_test
${GAME_LIBS}
)
add_executable(level_select_test
level_select_test.c
)
target_include_directories(level_select_test
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_options(level_select_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_options(level_select_test PRIVATE -fsanitize=address -gdwarf-4)
target_link_libraries(level_select_test
${GAME_LIBS}
)
if (BUILD_TESTING)
find_package(cmocka 1.1.0 REQUIRED)

View File

@ -89,10 +89,10 @@ void process_inputs(GameEngine_t* engine, Scene_t* scene)
}
}
void change_scene(GameEngine_t* engine, unsigned int idx)
Scene_t* change_scene(GameEngine_t* engine, unsigned int idx)
{
// Backwards compat
change_active_scene(engine, idx);
return change_active_scene(engine, idx);
}
bool load_sfx(GameEngine_t* engine, const char* snd_name, uint32_t tag_idx)
@ -361,11 +361,13 @@ void remove_child_scene(GameEngine_t* engine, unsigned int idx)
child->parent_scene = NULL;
}
void change_active_scene(GameEngine_t* engine, unsigned int idx)
Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx)
{
engine->scenes[engine->curr_scene]->state = 0;
engine->curr_scene = idx;
engine->scenes[engine->curr_scene]->state = SCENE_COMPLETE_ACTIVE;
sc_queue_clear(&engine->key_buffer);
return engine->scenes[engine->curr_scene];
}
void change_focused_scene(GameEngine_t* engine, unsigned int idx)

View File

@ -86,8 +86,8 @@ void process_active_scene_inputs(GameEngine_t* engine);
void update_curr_scene(GameEngine_t* engine);
void render_curr_scene(GameEngine_t* engine);
void change_scene(GameEngine_t* engine, unsigned int idx);
void change_active_scene(GameEngine_t* engine, unsigned int idx);
Scene_t* change_scene(GameEngine_t* engine, unsigned int idx);
Scene_t* change_active_scene(GameEngine_t* engine, unsigned int idx);
void change_focused_scene(GameEngine_t* engine, unsigned int idx);
bool load_sfx(GameEngine_t* engine, const char* snd_name, uint32_t tag_idx);
void play_sfx(GameEngine_t* engine, unsigned int tag_idx);

View File

@ -9,6 +9,11 @@
static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 };
static bool guiStyleLoaded = false; // Style loaded flag for lazy style initialization
static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib)
//
// Check if two rectangles are equal, used to validate a slider bounds as an id
#ifndef CHECK_BOUNDS_ID
#define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
#endif
typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
@ -369,6 +374,367 @@ void UI_button(const UIComp_t* comp, const char* text)
}
// Slider control with pro parameters
// NOTE: Other GuiSlider*() controls use this one
int GuiSliderPro(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
{
Rectangle bounds = comp->bbox;
int result = 0;
GuiState state = comp->state;
float temp = (maxValue - minValue)/2.0f;
if (value == NULL) value = &temp;
int sliderValue = (int)(((*value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
if (sliderWidth > 0) // Slider
{
slider.x += (sliderValue - (sliderWidth >> 1));
slider.width = (float)sliderWidth;
}
else if (sliderWidth == 0) // SliderBar
{
slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
slider.width = (float)sliderValue;
}
// Update control
//--------------------------------------------------------------------
//if ((state != STATE_DISABLED) && !guiLocked)
if (state != STATE_DISABLED)
{
Vector2 mousePoint = GetMousePosition();
if (comp->pressed) // Keep dragging outside of bounds
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
//if (CHECK_BOUNDS_ID(bounds, guiSliderActive))
{
state = STATE_PRESSED;
// Get equivalent value and slider position from mousePosition.x
*value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue;
}
}
//else
//{
// guiSliderDragging = false;
// guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
//}
}
else if (CheckCollisionPointRec(mousePoint, bounds))
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
//guiSliderDragging = true;
//guiSliderActive = bounds; // Store bounds as an identifier when dragging starts
if (!CheckCollisionPointRec(mousePoint, slider))
{
// Get equivalent value and slider position from mousePosition.x
*value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + (sliderWidth >> 1) )))/(float)(bounds.width - sliderWidth) + minValue;
if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider
else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar
}
}
else state = STATE_FOCUSED;
}
if (*value > maxValue) *value = maxValue;
else if (*value < minValue) *value = minValue;
}
// Bar limits check
if (sliderWidth > 0) // Slider
{
if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
}
else if (sliderWidth == 0) // SliderBar
{
if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
}
//--------------------------------------------------------------------
// Draw control
//--------------------------------------------------------------------
GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
// Draw slider internal bar (depends on state)
if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
// Draw left/right text if provided
if (textLeft != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textLeft);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - (GuiGetStyle(DEFAULT, TEXT_SIZE) >> 1);
GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
}
if (textRight != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textRight);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
textBounds.y = bounds.y + bounds.height/2 - (GuiGetStyle(DEFAULT, TEXT_SIZE) >> 1);
GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
}
//--------------------------------------------------------------------
return result;
}
//------------------------------------------------------------------------------------
// Controls Functions Definitions (local)
//------------------------------------------------------------------------------------
float GuiVerticalSliderPro(const UIComp_t* comp, const char *textTop, const char *textBottom, float* in_value, float minValue, float maxValue, int sliderHeight)
{
float value = *in_value;
GuiState state = comp->state;
Rectangle bounds = comp->bbox;
int sliderValue = (int)(((value - minValue)/(maxValue - minValue)) * (bounds.height - 2 * GuiGetStyle(SLIDER, BORDER_WIDTH)));
Rectangle slider = {
bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
bounds.y + bounds.height - sliderValue,
bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING),
0.0f,
};
if (sliderHeight > 0) // Slider
{
slider.y -= sliderHeight >> 1;
slider.height = (float)sliderHeight;
}
else if (sliderHeight == 0) // SliderBar
{
slider.y -= GuiGetStyle(SLIDER, BORDER_WIDTH);
slider.height = (float)sliderValue;
}
// Update control
//--------------------------------------------------------------------
//if ((state != STATE_DISABLED) && !guiLocked)
if (state != STATE_DISABLED)
{
Vector2 mousePoint = GetMousePosition();
if (CheckCollisionPointRec(mousePoint, bounds))
{
if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
{
state = STATE_PRESSED;
// Get equivalent value and slider position from mousePoint.x
float normalizedValue = (bounds.y + bounds.height - mousePoint.y - (float)(sliderHeight >> 1)) / (bounds.height - (float)sliderHeight);
value = (maxValue - minValue) * normalizedValue + minValue;
if (sliderHeight > 0) slider.y = mousePoint.y - slider.height / 2; // Slider
else if (sliderHeight == 0) // SliderBar
{
slider.y = mousePoint.y;
slider.height = bounds.y + bounds.height - slider.y - GuiGetStyle(SLIDER, BORDER_WIDTH);
}
}
else state = STATE_FOCUSED;
}
if (value > maxValue) value = maxValue;
else if (value < minValue) value = minValue;
}
// Bar limits check
if (sliderHeight > 0) // Slider
{
if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH);
else if ((slider.y + slider.height) >= (bounds.y + bounds.height)) slider.y = bounds.y + bounds.height - slider.height - GuiGetStyle(SLIDER, BORDER_WIDTH);
}
else if (sliderHeight == 0) // SliderBar
{
if (slider.y < (bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH)))
{
slider.y = bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH);
slider.height = bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
}
}
//--------------------------------------------------------------------
// Draw control
//--------------------------------------------------------------------
GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), comp->alpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), comp->alpha));
// Draw slider internal bar (depends on state)
if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), comp->alpha));
else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), comp->alpha));
// Draw top/bottom text if provided
if (textTop != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textTop);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2;
textBounds.y = bounds.y - textBounds.height - GuiGetStyle(SLIDER, TEXT_PADDING);
GuiDrawText(textTop, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), comp->alpha));
}
if (textBottom != NULL)
{
Rectangle textBounds = { 0 };
textBounds.width = (float)GetTextWidth(textBottom);
textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
textBounds.x = bounds.x + bounds.width/2 - textBounds.width/2;
textBounds.y = bounds.y + bounds.height + GuiGetStyle(SLIDER, TEXT_PADDING);
GuiDrawText(textBottom, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), comp->alpha));
}
//--------------------------------------------------------------------
*in_value = value;
return value;
}
float UI_vert_slider(const UIComp_t* comp, const char *textTop, const char *textBottom, float* value, float minValue, float maxValue)
{
return GuiVerticalSliderPro(comp, textTop, textBottom, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
}
// Slider control extended, returns selected value and has text
float UI_slider(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
{
return GuiSliderPro(comp, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
}
void vert_scrollarea_init(VertScrollArea_t* scroll_area, Rectangle display_area, Vector2 canvas_dims)
{
scroll_area->canvas = LoadRenderTexture(canvas_dims.x, canvas_dims.y);
scroll_area->scroll_pos = canvas_dims.y - display_area.height;
scroll_area->scroll_bounds = (Vector2){
0, scroll_area->scroll_pos
};
vert_scrollarea_set_item_dims(scroll_area, 12, 3);
scroll_area->curr_selection = 0;
scroll_area->display_area = display_area;
scroll_area->scroll_bar.alpha = 1;
scroll_area->scroll_bar.bbox = (Rectangle){
display_area.width + display_area.x, display_area.y,
0.1f * display_area.width, display_area.height,
};
scroll_area->scroll_bar.state = STATE_NORMAL;
scroll_area->comp.alpha = 1;
scroll_area->comp.bbox = display_area;
scroll_area->comp.bbox.width *= 1.1f;
scroll_area->scroll_bar.state = STATE_NORMAL;
}
void vert_scrollarea_set_item_dims(VertScrollArea_t* scroll_area, unsigned int item_height, unsigned int item_padding)
{
scroll_area->item_height = item_height;
scroll_area->item_padding = item_padding;
scroll_area->n_items = (scroll_area->canvas.texture.height - scroll_area->item_padding) / (scroll_area->item_height + scroll_area->item_padding);
}
void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx)
{
if (item_idx >= scroll_area->n_items) return;
DrawText(str, 0, scroll_area->item_padding + (scroll_area->item_height + scroll_area->item_padding) * item_idx, scroll_area->item_height, BLACK);
}
unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos)
{
float x = pos.x - scroll_area->display_area.x;
float y = pos.y - scroll_area->display_area.y;
if (x >= scroll_area->display_area.width || x < 0) return scroll_area->n_items;
if (y >= scroll_area->display_area.height || y < 0) return scroll_area->n_items;
scroll_area->curr_selection = (y - scroll_area->item_padding + (scroll_area->scroll_bounds.y - scroll_area->scroll_pos)) / (scroll_area->item_height + scroll_area->item_padding);
return scroll_area->curr_selection;
}
void vert_scrollarea_refocus(VertScrollArea_t* scroll_area)
{
float selection_y = scroll_area->curr_selection * (scroll_area->item_height + scroll_area->item_padding) + scroll_area->item_padding - (scroll_area->scroll_bounds.y - scroll_area->scroll_pos);
// Auto adjust scroll based on selection
if (selection_y < 0)
{
scroll_area->scroll_pos -= selection_y;
}
else if (selection_y + scroll_area->item_height + scroll_area->item_padding > scroll_area->display_area.height)
{
scroll_area->scroll_pos -= selection_y + scroll_area->item_height + scroll_area->item_padding - scroll_area->display_area.height;
}
}
void vert_scrollarea_render(VertScrollArea_t* scroll_area)
{
BeginScissorMode(scroll_area->comp.bbox.x, scroll_area->comp.bbox.y, scroll_area->comp.bbox.width, scroll_area->comp.bbox.height);
UI_vert_slider(&scroll_area->scroll_bar,
NULL,
NULL,
&scroll_area->scroll_pos,
scroll_area->scroll_bounds.x,
scroll_area->scroll_bounds.y
);
Rectangle draw_rec = (Rectangle){
0, scroll_area->scroll_pos,
scroll_area->display_area.width,
scroll_area->display_area.height
};
float selection_y = scroll_area->curr_selection * (scroll_area->item_height + scroll_area->item_padding) + scroll_area->item_padding - (scroll_area->scroll_bounds.y - scroll_area->scroll_pos);
DrawRectangle(
scroll_area->display_area.x,
scroll_area->display_area.y + selection_y,
scroll_area->canvas.texture.width, scroll_area->item_height,
Fade(BLUE, 0.7)
);
Vector2 draw_pos = {scroll_area->display_area.x, scroll_area->display_area.y};
draw_rec.height *= -1;
DrawTextureRec(
scroll_area->canvas.texture,
draw_rec,
draw_pos,
WHITE
);
EndScissorMode();
}
void vert_scrollarea_free(VertScrollArea_t* scroll_area)
{
UnloadRenderTexture(scroll_area->canvas);
}
void init_UI(void)
{
GuiLoadStyleDefault();

View File

@ -3,6 +3,7 @@
#include "raylib.h"
#include "raygui.h"
// Generic Component
typedef struct UIComp {
Rectangle bbox;
GuiState state;
@ -10,6 +11,39 @@ typedef struct UIComp {
bool pressed;
} UIComp_t;
typedef struct VertScrollArea {
UIComp_t comp; // Display area + scrollbar
Rectangle display_area; // Display dimension
RenderTexture2D canvas; // Complete canvas
unsigned int n_items;
unsigned int curr_selection;
unsigned int item_height;
unsigned int item_padding;
float scroll_pos;
Vector2 scroll_bounds;
UIComp_t scroll_bar;
} VertScrollArea_t;
void init_UI(void);
void vert_scrollarea_init(VertScrollArea_t* scroll_area, Rectangle display_area, Vector2 canvas_dims);
void vert_scrollarea_set_item_dims(VertScrollArea_t* scroll_area, unsigned int item_height, unsigned int item_padding);
void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx);
unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos);
void vert_scrollarea_refocus(VertScrollArea_t* scroll_area);
void vert_scrollarea_render(VertScrollArea_t* scroll_area);
void vert_scrollarea_free(VertScrollArea_t* scroll_area);
static inline void ScrollAreaRenderBegin(VertScrollArea_t* scroll)
{
BeginTextureMode(scroll->canvas);
}
#define ScrollAreaRenderEnd() EndTextureMode()
void UI_button(const UIComp_t* bbox, const char* text);
float UI_slider(const UIComp_t* comp, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue);
float UI_vert_slider(const UIComp_t* comp, const char *textTop, const char *textBottom, float* value, float minValue, float maxValue);
#endif

View File

@ -539,7 +539,7 @@ RAYGUIAPI bool GuiSpinner(Rectangle bounds, const char *text, int *value, int mi
RAYGUIAPI bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
RAYGUIAPI bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text
RAYGUIAPI bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines
RAYGUIAPI float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider control, returns selected value
RAYGUIAPI float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue);
RAYGUIAPI float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider Bar control, returns selected value
RAYGUIAPI float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Progress Bar control, shows current progress value
RAYGUIAPI void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text

View File

@ -0,0 +1,80 @@
#include "mempool.h"
#include "scene_impl.h"
#include "gui.h"
#include <stdio.h>
#include <unistd.h>
#include <math.h>
// Maintain own queue to handle key presses
struct sc_queue_32 key_buffer;
const float DT = 1.0f/60.0f;
int main(void)
{
sc_queue_init(&key_buffer);
InitWindow(1280, 640, "raylib");
SetTargetFPS(60);
init_memory_pools();
init_UI();
LevelSelectScene_t scene;
init_level_select_scene(&scene);
scene.scene.bg_colour = RAYWHITE;
while(true)
{
Vector2 raw_mouse_pos = GetMousePosition();
scene.scene.mouse_pos = raw_mouse_pos;
// This entire key processing relies on the assumption that a pressed key will
// appear in the polling of raylib
unsigned int sz = sc_queue_size(&key_buffer);
// Process any existing pressed key
for (size_t i = 0; i < sz; i++)
{
int button = sc_queue_del_first(&key_buffer);
ActionType_t action = sc_map_get_64(&scene.scene.action_map, button);
if (IsKeyReleased(button))
{
do_action(&scene.scene, action, false);
}
else
{
do_action(&scene.scene, action, true);
sc_queue_add_last(&key_buffer, button);
}
}
// Detect new key presses
while(true)
{
int button = GetKeyPressed();
if (button == 0) break;
ActionType_t action = sc_map_get_64(&scene.scene.action_map, button);
if (!sc_map_found(&scene.scene.action_map)) continue;
do_action(&scene.scene, action, true);
sc_queue_add_last(&key_buffer, button);
}
ActionType_t action = sc_map_get_64(&scene.scene.action_map, MOUSE_BUTTON_LEFT);
if (sc_map_found(&scene.scene.action_map))
{
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
{
do_action(&scene.scene, action, true);
}
else if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
{
do_action(&scene.scene, action, false);
}
}
float frame_time = GetFrameTime();
float delta_time = fminf(frame_time, DT);
update_scene(&scene.scene, delta_time);
// This is needed to advance time delta
render_scene(&scene.scene);
if (WindowShouldClose()) break;
}
free_level_select_scene(&scene);
sc_queue_term(&key_buffer);
CloseWindow();
}

18
main.c
View File

@ -10,7 +10,7 @@
Scene_t *scenes[N_SCENES];
static GameEngine_t engine = {
.scenes = scenes,
.max_scenes = 3,
.max_scenes = 4,
.curr_scene = 0,
.assets = {0}
};
@ -68,16 +68,22 @@ int main(void)
{
level_scene.data.level_pack = pack;
level_scene.data.current_level = 0;
load_level_tilemap(&level_scene, 0);
}
MenuScene_t menu_scene;
menu_scene.scene.engine = &engine;
init_menu_scene(&menu_scene);
scenes[0] = &menu_scene.scene;
scenes[1] = &level_scene.scene;
scenes[2] = &sandbox_scene.scene;
change_scene(&engine, 0);
LevelSelectScene_t level_sel_scene;
level_sel_scene.scene.engine = &engine;
level_sel_scene.data.level_pack = pack;
init_level_select_scene(&level_sel_scene);
scenes[MAIN_MENU_SCENE] = &menu_scene.scene;
scenes[LEVEL_SELECT_SCENE] = &level_sel_scene.scene;
scenes[GAME_SCENE] = &level_scene.scene;
scenes[SANDBOX_SCENE] = &sandbox_scene.scene;
change_scene(&engine, MAIN_MENU_SCENE);
const float DT = 1.0f/60.0f;
while (!WindowShouldClose())

View File

@ -16,6 +16,7 @@ int main(void)
init_memory_pools();
MenuScene_t scene;
init_menu_scene(&scene);
scene.scene.bg_colour = RAYWHITE;
while(true)
{
@ -55,10 +56,7 @@ int main(void)
update_scene(&scene.scene, delta_time);
update_entity_manager(&scene.scene.ent_manager);
// This is needed to advance time delta
BeginDrawing();
render_scene(&scene.scene);
ClearBackground(RAYWHITE);
EndDrawing();
render_scene(&scene.scene);
if (WindowShouldClose()) break;
}
free_menu_scene(&scene);

View File

@ -5,6 +5,7 @@ add_library(lib_scenes STATIC
water_flow.c
editor_scene.c
menu_scene.c
level_select_scene.c
game_scene.c
game_systems.c
scene_systems.c

View File

@ -35,9 +35,11 @@ typedef enum SFXTag {
COIN_SFX,
} SFXTag_t;
//typedef enum SceneType {
// LEVEL_SCENE = 0,
// MENU_SCENE,
//}SceneType_t;
typedef enum SceneType {
MAIN_MENU_SCENE = 0,
LEVEL_SELECT_SCENE,
GAME_SCENE,
SANDBOX_SCENE,
}SceneType_t;
#endif

View File

@ -89,7 +89,7 @@ static void level_do_action(Scene_t* scene, ActionType_t action, bool pressed)
case ACTION_EXIT:
if(scene->engine != NULL)
{
change_scene(scene->engine, 0);
change_scene(scene->engine, MAIN_MENU_SCENE);
}
break;
default:

View File

@ -0,0 +1,143 @@
#include "scene_impl.h"
#include "assets_tag.h"
#include "gui.h"
#include "raymath.h"
#include <stdio.h>
static void level_select_render_func(Scene_t* scene)
{
LevelSelectSceneData_t* data = &(CONTAINER_OF(scene, LevelSelectScene_t, scene)->data);
BeginTextureMode(scene->layers.render_layers[0].layer_tex);
ClearBackground(BLANK);
DrawText("Level Select", 10, 10, 40, BLACK);
vert_scrollarea_render(&data->scroll_area);
EndTextureMode();
}
static void level_select_do_action(Scene_t* scene, ActionType_t action, bool pressed)
{
LevelSelectSceneData_t* data = &(CONTAINER_OF(scene, LevelSelectScene_t, scene)->data);
switch(action)
{
case ACTION_UP:
if (!pressed)
{
if (data->scroll_area.curr_selection > 0)
{
data->scroll_area.curr_selection--;
vert_scrollarea_refocus(&data->scroll_area);
}
}
break;
case ACTION_DOWN:
if (!pressed)
{
if (data->scroll_area.curr_selection < data->scroll_area.n_items - 1)
{
data->scroll_area.curr_selection++;
vert_scrollarea_refocus(&data->scroll_area);
}
}
break;
case ACTION_EXIT:
if (!pressed)
{
if(scene->engine != NULL)
{
change_scene(scene->engine, MAIN_MENU_SCENE);
}
}
break;
case ACTION_NEXT_SPAWN:
if (!pressed)
{
unsigned int prev_sel = data->scroll_area.curr_selection;
if (vert_scrollarea_set_pos(&data->scroll_area, scene->mouse_pos) != data->scroll_area.n_items)
{
vert_scrollarea_refocus(&data->scroll_area);
if (prev_sel == data->scroll_area.curr_selection)
{
if (data->level_pack != NULL && data->scroll_area.curr_selection < data->level_pack->n_levels)
{
// TODO: Need to load the current level
LevelScene_t* level_scene = (LevelScene_t*)change_scene(scene->engine, GAME_SCENE);
level_scene->data.level_pack = data->level_pack;
level_scene->data.current_level = data->scroll_area.curr_selection;
reload_level_tilemap(level_scene);
}
}
}
}
break;
case ACTION_CONFIRM:
if (!pressed)
{
if (data->level_pack != NULL && data->scroll_area.curr_selection < data->level_pack->n_levels)
{
// TODO: Need to load the current level
LevelScene_t* level_scene = (LevelScene_t*)change_scene(scene->engine, GAME_SCENE);
level_scene->data.level_pack = data->level_pack;
level_scene->data.current_level = data->scroll_area.curr_selection;
reload_level_tilemap(level_scene);
}
}
break;
default:
break;
}
}
#define FONT_SIZE 30
#define TEXT_PADDING 3
#define DISPLAY_AREA_HEIGHT 400
#define SCROLL_TOTAL_HEIGHT 800
void init_level_select_scene(LevelSelectScene_t* scene)
{
init_scene(&scene->scene, &level_select_do_action);
add_scene_layer(
&scene->scene, 400, 800,
(Rectangle){0, 0, 400, 800}
);
vert_scrollarea_init(&scene->data.scroll_area, (Rectangle){50, 100, 150, DISPLAY_AREA_HEIGHT - 100}, (Vector2){150, SCROLL_TOTAL_HEIGHT});
vert_scrollarea_set_item_dims(&scene->data.scroll_area, FONT_SIZE, TEXT_PADDING);
char buf[32];
ScrollAreaRenderBegin(&scene->data.scroll_area);
ClearBackground(BLANK);
if (scene->data.level_pack != NULL)
{
scene->data.scroll_area.n_items = scene->data.level_pack->n_levels;
for (unsigned int i = 0; i < scene->data.level_pack->n_levels; ++i)
{
vert_scrollarea_insert_item(&scene->data.scroll_area, scene->data.level_pack->levels[i].level_name, i);
}
for (unsigned int i = scene->data.level_pack->n_levels; i < scene->data.scroll_area.n_items; ++i)
{
vert_scrollarea_insert_item(&scene->data.scroll_area, "---", i);
}
}
else
{
for (unsigned int i = 0; i < scene->data.scroll_area.n_items; ++i)
{
sprintf(buf, "Level %u", i);
vert_scrollarea_insert_item(&scene->data.scroll_area, buf, i);
}
}
ScrollAreaRenderEnd();
sc_array_add(&scene->scene.systems, &level_select_render_func);
sc_map_put_64(&scene->scene.action_map, KEY_UP, ACTION_UP);
sc_map_put_64(&scene->scene.action_map, KEY_DOWN, ACTION_DOWN);
sc_map_put_64(&scene->scene.action_map, KEY_Q, ACTION_EXIT);
sc_map_put_64(&scene->scene.action_map, KEY_ENTER, ACTION_CONFIRM);
sc_map_put_64(&scene->scene.action_map, MOUSE_LEFT_BUTTON, ACTION_NEXT_SPAWN); // Abuse an unused action
}
void free_level_select_scene(LevelSelectScene_t* scene)
{
vert_scrollarea_free(&scene->data.scroll_area);
free_scene(&scene->scene);
}

View File

@ -1,4 +1,5 @@
#include "scene_impl.h"
#include "assets_tag.h"
#include "raymath.h"
#include <stdio.h>
@ -21,10 +22,10 @@ static void exec_component_function(Scene_t* scene, int sel)
switch(sel)
{
case 0:
change_scene(scene->engine, 1);
change_scene(scene->engine, LEVEL_SELECT_SCENE);
break;
case 1:
change_scene(scene->engine, 2);
change_scene(scene->engine, SANDBOX_SCENE);
break;
case 3:
scene->state = 0;

View File

@ -86,7 +86,18 @@ typedef struct MenuScene {
MenuSceneData_t data;
} MenuScene_t;
typedef struct LevelSelectSceneData {
VertScrollArea_t scroll_area;
LevelPack_t* level_pack;
} LevelSelectSceneData_t;
typedef struct LevelSelectScene {
Scene_t scene;
LevelSelectSceneData_t data;
} LevelSelectScene_t;
void init_menu_scene(MenuScene_t* scene);
void free_menu_scene(MenuScene_t* scene);
void init_level_select_scene(LevelSelectScene_t* scene);
void free_level_select_scene(LevelSelectScene_t* scene);
#endif // __SCENE_IMPL_H