Compare commits
9 Commits
b158bee1e8
...
8207558be7
Author | SHA1 | Date |
---|---|---|
|
8207558be7 | |
|
9b6c364269 | |
|
eff3d090df | |
|
41f3656ba1 | |
|
98b957a8ff | |
|
6dd185b6cd | |
|
33de816841 | |
|
0c461d3167 | |
|
893fc1c73f |
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
366
engine/gui.c
366
engine/gui.c
|
@ -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();
|
||||
|
|
34
engine/gui.h
34
engine/gui.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
18
main.c
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue