Add level completion flags

Changelog:
- Completing a level will mark it so
- Level selects reflects the status of the level completion with text
  colour
- This also prepare to persist these flags
main
En Yi 2025-09-03 20:49:42 +08:00
parent e85dc42f3e
commit 9ebacbc572
9 changed files with 127 additions and 30 deletions

View File

@ -684,15 +684,18 @@ bool vert_scrollarea_n_items(VertScrollArea_t* scroll_area, unsigned int n_items
return true; return true;
} }
void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx) void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx, Color colour)
{ {
if (item_idx >= scroll_area->max_items) return; if (item_idx >= scroll_area->max_items) return;
float y_pos = scroll_area->item_padding + (scroll_area->item_height + scroll_area->item_padding) * item_idx;
//DrawRectangle(0, y_pos, scroll_area->display_area.width, scroll_area->item_height, BLANK);
DrawTextEx( DrawTextEx(
scroll_area->comp.font, scroll_area->comp.font,
str, str,
(Vector2){0, scroll_area->item_padding + (scroll_area->item_height + scroll_area->item_padding) * item_idx} (Vector2){0, y_pos}
, scroll_area->item_height, 0, BLACK); , scroll_area->item_height, 0, colour);
} }
unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos) unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos)

View File

@ -35,7 +35,7 @@ void init_UI(void);
void vert_scrollarea_init(VertScrollArea_t* scroll_area, Rectangle display_area, Vector2 canvas_dims); 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_set_item_dims(VertScrollArea_t* scroll_area, unsigned int item_height, unsigned int item_padding);
bool vert_scrollarea_n_items(VertScrollArea_t* scroll_area, unsigned int n_items); bool vert_scrollarea_n_items(VertScrollArea_t* scroll_area, unsigned int n_items);
void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx); void vert_scrollarea_insert_item(VertScrollArea_t* scroll_area, char* str, unsigned int item_idx, Color colour);
unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos); unsigned int vert_scrollarea_set_pos(VertScrollArea_t* scroll_area, Vector2 pos);
void vert_scrollarea_refocus(VertScrollArea_t* scroll_area); void vert_scrollarea_refocus(VertScrollArea_t* scroll_area);
void vert_scrollarea_render(VertScrollArea_t* scroll_area); void vert_scrollarea_render(VertScrollArea_t* scroll_area);

View File

@ -12,6 +12,7 @@ add_library(lib_scenes STATIC
scene_systems.c scene_systems.c
camera_systems.c camera_systems.c
engine_impl.c engine_impl.c
save_data.c
) )
target_include_directories(lib_scenes target_include_directories(lib_scenes
PUBLIC PUBLIC

View File

@ -58,4 +58,5 @@ static const uint8_t CONNECTIVITY_TILE_MAPPING[16] = {
}; };
#define ANIM_FRAME_RATE (1.0f/24) #define ANIM_FRAME_RATE (1.0f/24)
#define SAVE_FILE_NAME "save.dat"
#endif // __CONSTANTS_H #endif // __CONSTANTS_H

View File

@ -2,6 +2,7 @@
#include "keymaps.h" #include "keymaps.h"
#include "scene_impl.h" #include "scene_impl.h"
#include "save_data.h"
#include "game_systems.h" #include "game_systems.h"
#include "water_flow.h" #include "water_flow.h"
#include "constants.h" #include "constants.h"
@ -530,6 +531,7 @@ static void at_level_start(Scene_t* scene)
static void at_level_complete(Scene_t* scene) static void at_level_complete(Scene_t* scene)
{ {
LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data); LevelSceneData_t* data = &(CONTAINER_OF(scene, LevelScene_t, scene)->data);
set_level_completed(data->current_level, true);
data->sm.fractional += scene->delta_time; data->sm.fractional += scene->delta_time;
if (data->sm.fractional > 1.0f) if (data->sm.fractional > 1.0f)
{ {

View File

@ -3,6 +3,7 @@
#include "gui.h" #include "gui.h"
#include "raymath.h" #include "raymath.h"
#include <stdio.h> #include <stdio.h>
#include "save_data.h"
#define LEVEL_PREVIEW_SIZE 430 #define LEVEL_PREVIEW_SIZE 430
static void level_select_render_func(Scene_t* scene) static void level_select_render_func(Scene_t* scene)
@ -257,15 +258,55 @@ static void level_select_action_remap_func(Scene_t* scene)
sc_map_put_64(&scene->action_map, KEY_ENTER, ACTION_CONFIRM); sc_map_put_64(&scene->action_map, KEY_ENTER, ACTION_CONFIRM);
} }
static void render_scroll_canvas(LevelSelectSceneData_t* data)
{
// Hardcoded buffer size
char buf[48];
ScrollAreaRenderBegin(&data->scroll_area);
ClearBackground(BLANK);
if (data->level_pack != NULL)
{
vert_scrollarea_n_items(&data->scroll_area, data->level_pack->n_levels);
for (unsigned int i = 0; i < data->level_pack->n_levels; ++i)
{
vert_scrollarea_insert_item(&data->scroll_area, data->level_pack->levels[i].level_name, i, (is_level_completed(i)) ? GREEN : BLACK);
}
for (unsigned int i = data->level_pack->n_levels; i < data->scroll_area.max_items; ++i)
{
vert_scrollarea_insert_item(&data->scroll_area, "---", i, BLACK);
}
}
else
{
for (unsigned int i = 0; i < data->scroll_area.max_items; ++i)
{
sprintf(buf, "Level %u", i);
vert_scrollarea_insert_item(&data->scroll_area, buf, i, BLACK);
}
}
ScrollAreaRenderEnd();
}
static void reset_level_selection_scene(Scene_t* scene)
{
LevelSelectSceneData_t* data = &(CONTAINER_OF(scene, LevelSelectScene_t, scene)->data);
// Re-draw the canvas, in case of completion update
render_scroll_canvas(data);
data->scroll_area.curr_selection = 0;
data->update_preview = true;
}
#define FONT_SIZE 22 #define FONT_SIZE 22
#define TEXT_PADDING 3 #define TEXT_PADDING 3
#define SCROLL_TOTAL_HEIGHT 800 #define SCROLL_TOTAL_HEIGHT 800
void init_level_select_scene(LevelSelectScene_t* scene) void init_level_select_scene(LevelSelectScene_t* scene)
{ {
init_scene(&scene->scene, &level_select_do_action, 0); init_scene(&scene->scene, &level_select_do_action, 0);
scene->scene.reset_function = &reset_level_selection_scene;
scene->data.preview = LoadRenderTexture(LEVEL_PREVIEW_SIZE, LEVEL_PREVIEW_SIZE); scene->data.preview = LoadRenderTexture(LEVEL_PREVIEW_SIZE, LEVEL_PREVIEW_SIZE);
scene->data.update_preview = true; scene->data.update_preview = true;
scene->scene.action_remap_function = level_select_action_remap_func; scene->scene.action_remap_function = &level_select_action_remap_func;
add_scene_layer( add_scene_layer(
&scene->scene, scene->scene.engine->intended_window_size.x, &scene->scene, scene->scene.engine->intended_window_size.x,
scene->scene.engine->intended_window_size.y, scene->scene.engine->intended_window_size.y,
@ -283,30 +324,8 @@ void init_level_select_scene(LevelSelectScene_t* scene)
if (menu_font != NULL) { if (menu_font != NULL) {
scene->data.scroll_area.comp.font = *menu_font; scene->data.scroll_area.comp.font = *menu_font;
} }
char buf[32];
ScrollAreaRenderBegin(&scene->data.scroll_area); render_scroll_canvas(&scene->data);
ClearBackground(BLANK);
if (scene->data.level_pack != NULL)
{
vert_scrollarea_n_items(&scene->data.scroll_area, 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.max_items; ++i)
{
vert_scrollarea_insert_item(&scene->data.scroll_area, "---", i);
}
}
else
{
for (unsigned int i = 0; i < scene->data.scroll_area.max_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_preview_render_func); sc_array_add(&scene->scene.systems, &level_preview_render_func);
sc_array_add(&scene->scene.systems, &level_select_render_func); sc_array_add(&scene->scene.systems, &level_select_render_func);

View File

@ -53,7 +53,7 @@ static void exec_component_function(Scene_t* scene, int sel)
switch(sel) switch(sel)
{ {
case 0: case 0:
change_scene(scene->engine, LEVEL_SELECT_SCENE); change_scene_ext(scene->engine, LEVEL_SELECT_SCENE, true);
break; break;
case 1: case 1:
change_scene_ext(scene->engine, OPTIONS_SCENE, true); change_scene_ext(scene->engine, OPTIONS_SCENE, true);

47
scenes/save_data.c 100644
View File

@ -0,0 +1,47 @@
#include "save_data.h"
#include "constants.h"
// Bit flags for level completion.
// Expect 30 levels.
// Could use a 32bit integer though.
// Using array to allow arbitrary length of flags.
#define NUMBER_OF_BYTES 4
static uint8_t completion_flags[NUMBER_OF_BYTES] = {0};
int load_savedata(uint8_t* bytes, size_t len)
{
return 0;
}
int writeout_savedata(uint8_t* bytes, size_t len)
{
return 0;
}
bool is_level_completed(uint32_t level_num)
{
// 0-indexed
uint32_t byte_num = level_num >> 3;
if (byte_num >= NUMBER_OF_BYTES) return false;
uint32_t bit_num = level_num % 8;
return ((completion_flags[byte_num] >> bit_num) & 1) > 0;
}
void set_level_completed(uint32_t level_num, bool complete)
{
uint32_t byte_num = level_num >> 3;
if (byte_num >= NUMBER_OF_BYTES) return;
uint32_t bit_num = level_num % 8;
if (complete)
{
completion_flags[byte_num] |= (1 <<bit_num);
}
else
{
completion_flags[byte_num] &= ~(1 << bit_num);
}
}

24
scenes/save_data.h 100644
View File

@ -0,0 +1,24 @@
#ifndef SAVEDATA_SCENES_H
#define SAVEDATA_SCENES_H
/**
* These are data that should persist and accessible by all scenes.
* It is expected to be modified over the course of the program lifetime by any scene.
*
* It is expected that only one save data is loaded and modified at any given time.
* Only used to keep track of the completed levels.
*
* General engine data belongs in the engine layer.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
int load_savedata(uint8_t* bytes, size_t len);
int writeout_savedata(uint8_t* bytes, size_t len);
bool is_level_completed(uint32_t level_num);
void set_level_completed(uint32_t level_num, bool complete);
#endif // SAVEDATA_SCENES_H