#define CAMERA_RATIO_X 16.0f
#define CAMERA_RATIO_Y 9.0f
+#define METADATA_TITLE_MAX_SIZE 256
+#define METADATA_VERSION_MAX_SIZE 256
+#define METADATA_FILEPATH_MAX_SIZE 512
+
+#define VERSION "1"
+
#endif // CONFIG_H_
#define DYNARRAY_INIT_CAPACITY 8
-void *dynarray_pointer_at(Dynarray *dynarray, size_t index)
+void *dynarray_pointer_at(const Dynarray *dynarray, size_t index)
{
trace_assert(index < dynarray->count);
return (uint8_t *)dynarray->data + index * dynarray->element_size;
return result;
}
-void *dynarray_pointer_at(Dynarray *dynarray, size_t index);
+void *dynarray_pointer_at(const Dynarray *dynarray, size_t index);
void dynarray_replace_at(Dynarray *dynarray, size_t index, void *element);
void dynarray_copy_to(Dynarray *dynarray, void *dest, size_t index);
void dynarray_clear(Dynarray *dynarray);
RETURN_LT(lt, NULL);
}
- if (metadata_load_from_line_stream(&level_editor->metadata, level_stream) < 0) {
+ if (metadata_load_from_line_stream(&level_editor->metadata, level_stream, level_editor->file_name) < 0) {
RETURN_LT(lt, NULL);
}
#include "./level_folder.h"
-#define LEVEL_FOLDER_MAX_LENGTH 512
-
-struct LevelFolder
-{
- Lt *lt;
- Dynarray filenames;
- Dynarray titles;
-};
-
-LevelFolder *create_level_folder(const char *dirpath)
+void level_folder_read(const char *dirpath, LevelFolder *folder)
{
- trace_assert(dirpath);
-
- Lt *lt = create_lt();
-
- LevelFolder *level_folder = PUSH_LT(
- lt,
- nth_calloc(1, sizeof(LevelFolder)),
- free);
- if (level_folder == NULL) {
- RETURN_LT(lt, NULL);
+ DIR *level_dir = opendir(dirpath);
+ if (level_dir == NULL) {
+ log_fail("Can't open asset folder: %s\n", dirpath);
+ abort();
}
- level_folder->lt = lt;
-
- level_folder->filenames = create_dynarray(sizeof(const char*));
- level_folder->titles = create_dynarray(sizeof(const char*));
- char path[LEVEL_FOLDER_MAX_LENGTH];
- DIR *level_dir = PUSH_LT(lt, opendir(dirpath), closedir_lt);
+ dynarray_clear(&folder->metadatas);
- LevelMetadata level_metadata;
+ char filepath[METADATA_FILEPATH_MAX_SIZE];
+ LevelMetadata metadata;
for (struct dirent *d = readdir(level_dir);
d != NULL;
d = readdir(level_dir)) {
- if (*d->d_name == '.') {
- continue;
- }
+ if (*d->d_name == '.') continue;
- snprintf(path, LEVEL_FOLDER_MAX_LENGTH, "%s/%s", dirpath, d->d_name);
- const char *filepath = PUSH_LT(lt, string_duplicate(trim_endline(path), NULL), free);
- if (filepath == NULL) {
- RETURN_LT(lt, NULL);
- }
+ snprintf(filepath, METADATA_FILEPATH_MAX_SIZE,
+ "%s/%s", dirpath, d->d_name);
- if (metadata_load_from_file(&level_metadata, filepath) < 0) {
- RETURN_LT(lt, NULL);
+ if (metadata_load_from_file(&metadata, filepath) < 0) {
+ log_warn("Can't read level: %s\n", filepath);
+ continue;
}
- const char *version = PUSH_LT(
- lt,
- string_duplicate(level_metadata.version, NULL),
- free);
- const char *title = PUSH_LT(
- lt,
- string_duplicate(level_metadata.title, NULL),
- free);
-
- if(strcmp(version, VERSION) == 0) {
- dynarray_push(&level_folder->titles, &title);
- dynarray_push(&level_folder->filenames, &filepath);
- } else {
- log_info(
+ if(strcmp(metadata.version, VERSION) != 0) {
+ log_warn(
"Unsupported version for level [%s]: Expected `%s`, got `%s`\n",
- d->d_name,
- VERSION,
- version);
+ filepath, VERSION, metadata.version);
+ continue;
}
- }
- closedir(RELEASE_LT(lt, level_dir));
-
- return level_folder;
-}
-
-void destroy_level_folder(LevelFolder *level_folder)
-{
- trace_assert(level_folder);
- free(level_folder->filenames.data);
- free(level_folder->titles.data);
- RETURN_LT0(level_folder->lt);
-}
-
-const char **level_folder_filenames(const LevelFolder *level_folder)
-{
- trace_assert(level_folder);
- return (const char **)level_folder->filenames.data;
-}
-
-const char **level_folder_titles(const LevelFolder *level_folder)
-{
- trace_assert(level_folder);
- return (const char **)level_folder->titles.data;
-}
+ dynarray_push(&folder->metadatas, &metadata);
+ }
-size_t level_folder_count(const LevelFolder *level_folder)
-{
- trace_assert(level_folder);
- return level_folder->filenames.count;
+ closedir(level_dir);
}
#ifndef LEVEL_FOLDER_H_
#define LEVEL_FOLDER_H_
-typedef struct LevelFolder LevelFolder;
+typedef struct {
+ Dynarray metadatas;
+} LevelFolder;
-LevelFolder *create_level_folder(const char *dirpath);
-void destroy_level_folder(LevelFolder *level_folder);
+static inline
+LevelFolder create_level_folder(void)
+{
+ LevelFolder result = {
+ .metadatas = create_dynarray(sizeof(LevelMetadata)),
+ };
+ return result;
+}
-const char **level_folder_filenames(const LevelFolder *level_folder);
-const char **level_folder_titles(const LevelFolder *level_folder);
-size_t level_folder_count(const LevelFolder *level_folder);
+static inline
+void destroy_level_folder(LevelFolder level_folder)
+{
+ free(level_folder.metadatas.data);
+}
+
+void level_folder_read(const char *dirpath, LevelFolder *folder);
#endif // LEVEL_FOLDER_H_
#include "level_metadata.h"
#include "math/extrema.h"
-struct LevelMetadata
-{
- Lt *lt;
- const char *version;
- const char *title;
-};
-
int metadata_load_from_line_stream(LevelMetadata *metadata,
- LineStream *line_stream)
+ LineStream *line_stream,
+ const char *filepath)
{
trace_assert(metadata);
trace_assert(line_stream);
memset(metadata->version, 0, METADATA_VERSION_MAX_SIZE);
memset(metadata->title, 0, METADATA_TITLE_MAX_SIZE);
+ memset(metadata->filepath, 0, METADATA_FILEPATH_MAX_SIZE);
const char *line = line_stream_next(line_stream);
if (line == NULL) return -1;
min_size_t(strlen(line), METADATA_VERSION_MAX_SIZE - 1));
trim_endline(metadata->title);
+ memcpy(metadata->filepath,
+ filepath,
+ min_size_t(strlen(filepath), METADATA_FILEPATH_MAX_SIZE - 1));
+ trim_endline(metadata->filepath);
+
return 0;
}
-int metadata_load_from_file(LevelMetadata *metadata, const char *filename)
+int metadata_load_from_file(LevelMetadata *metadata, const char *filepath)
{
trace_assert(metadata);
- trace_assert(filename);
+ trace_assert(filepath);
- LineStream *line_stream = create_line_stream(filename, "r", 256);
+ LineStream *line_stream = create_line_stream(filepath, "r", 256);
if (line_stream == NULL) return -1;
- int err = metadata_load_from_line_stream(metadata, line_stream);
+ int err = metadata_load_from_line_stream(metadata, line_stream, filepath);
destroy_line_stream(line_stream);
return err;
}
#ifndef LEVEL_METADATA_H_
#define LEVEL_METADATA_H_
-typedef struct LineStream LineStream;
-
-#define VERSION "1"
+#include "config.h"
-#define METADATA_TITLE_MAX_SIZE 256
-#define METADATA_VERSION_MAX_SIZE 256
+typedef struct LineStream LineStream;
typedef struct {
+ char filepath[METADATA_FILEPATH_MAX_SIZE];
char version[METADATA_VERSION_MAX_SIZE];
char title[METADATA_TITLE_MAX_SIZE];
} LevelMetadata;
-int metadata_load_from_file(LevelMetadata *metadata, const char *file);
-int metadata_load_from_line_stream(LevelMetadata *metadata, LineStream *line_stream);
+int metadata_load_from_file(LevelMetadata *metadata, const char *filepath);
+int metadata_load_from_line_stream(LevelMetadata *metadata, LineStream *line_stream, const char *filepath);
#endif // LEVEL_METADATA_H_
Lt *lt;
Background background;
Vec2f camera_position;
- LevelFolder *level_folder;
+ LevelFolder level_folder;
WigglyText wiggly_text;
- ListSelector *list_selector;
+ ListSelector list_selector;
};
+static inline
+const char *list_item_text(void *element)
+{
+ trace_assert(element);
+ return ((LevelMetadata *)element)->title;
+}
+
LevelPicker *create_level_picker(const char *dirpath)
{
trace_assert(dirpath);
level_picker->camera_position = vec(0.0f, 0.0f);
- level_picker->level_folder = PUSH_LT(
- lt,
- create_level_folder(dirpath),
- destroy_level_folder);
- if (level_picker->level_folder == NULL) {
- RETURN_LT(lt, NULL);
- }
+ level_picker->level_folder = create_level_folder();
+ level_folder_read("./assets/levels", &level_picker->level_folder);
level_picker->wiggly_text = (WigglyText) {
.text = "Select Level",
.color = COLOR_WHITE,
};
- level_picker->list_selector = PUSH_LT(
- lt,
- create_list_selector(
- level_folder_titles(level_picker->level_folder),
- level_folder_count(level_picker->level_folder),
- vec(5.0f, 5.0f),
- 50.0f),
- destroy_list_selector);
- if (level_picker->list_selector == NULL) {
- RETURN_LT(lt, NULL);
- }
+ level_picker->list_selector.items = level_picker->level_folder.metadatas;
+ level_picker->list_selector.font_scale = vec(5.0f, 5.0f);
+ level_picker->list_selector.padding_bottom = 50.0f;
+ level_picker->list_selector.list_item_text = list_item_text;
return level_picker;
}
void destroy_level_picker(LevelPicker *level_picker)
{
trace_assert(level_picker);
+ destroy_level_folder(level_picker->level_folder);
RETURN_LT0(level_picker->lt);
}
camera,
vec(viewport.w * 0.5f - title_size.x * 0.5f, TITLE_MARGIN_TOP));
- if (list_selector_render(camera, level_picker->list_selector) < 0) {
- return -1;
- }
+ list_selector_render(camera, &level_picker->list_selector);
{
/* CSS */
const Vec2f title_size = wiggly_text_size(&level_picker->wiggly_text);
const Vec2f selector_size = list_selector_size(
- level_picker->list_selector,
+ &level_picker->list_selector,
font_scale,
padding_bottom);
- list_selector_move(
- level_picker->list_selector,
+ level_picker->list_selector.position =
vec((float)width * 0.5f - selector_size.x * 0.5f,
- TITLE_MARGIN_TOP + title_size.y + TITLE_MARGIN_BOTTOM));
+ TITLE_MARGIN_TOP + title_size.y + TITLE_MARGIN_BOTTOM);
} break;
}
} break;
}
- return list_selector_event(level_picker->list_selector, event);
+ return list_selector_event(&level_picker->list_selector, event);
}
int level_picker_input(LevelPicker *level_picker,
{
trace_assert(level_picker);
- const int selected_index = list_selector_selected(level_picker->list_selector);
- if (selected_index < 0) {
+ if (level_picker->list_selector.selected_item < 0) {
return NULL;
}
- const char **filenames = level_folder_filenames(level_picker->level_folder);
+ LevelMetadata *metadata = dynarray_pointer_at(
+ &level_picker->level_folder.metadatas,
+ (size_t)level_picker->list_selector.selected_item);
- return filenames[selected_index];
+ return metadata->filepath;
}
void level_picker_clean_selection(LevelPicker *level_picker)
{
trace_assert(level_picker);
- list_selector_clean_selection(level_picker->list_selector);
+ level_picker->list_selector.selected_item = -1;
}
int level_picker_enter_camera_event(LevelPicker *level_picker,
#include "./list_selector.h"
-struct ListSelector
-{
- Lt *lt;
- const char **items;
- size_t count;
- size_t cursor;
- int selected_item;
- Vec2f position;
- Vec2f font_scale;
- float padding_bottom;
-};
-
-ListSelector *create_list_selector(const char *items[],
- size_t count,
- Vec2f font_scale,
- float padding_bottom)
-{
- trace_assert(items);
-
- Lt *lt = create_lt();
-
- ListSelector *list_selector = PUSH_LT(lt, nth_calloc(1, sizeof(ListSelector)), free);
- if (list_selector == NULL) {
- RETURN_LT(lt, NULL);
- }
- list_selector->lt = lt;
-
- list_selector->items = items;
- list_selector->count = count;
- list_selector->cursor = 0;
- list_selector->selected_item = -1;
- list_selector->position = vec(0.0f, 0.0f);
- list_selector->font_scale = font_scale;
- list_selector->padding_bottom = padding_bottom;
-
- return list_selector;
-}
-
-void destroy_list_selector(ListSelector *list_selector)
-{
- trace_assert(list_selector);
- RETURN_LT0(list_selector->lt);
-}
int list_selector_render(const Camera *camera,
const ListSelector *list_selector)
trace_assert(camera);
trace_assert(list_selector);
- for (size_t i = 0; i < list_selector->count; ++i) {
+ for (size_t i = 0; i < list_selector->items.count; ++i) {
const Vec2f current_position = vec_sum(
list_selector->position,
vec(0.0f, (float) i * ((float) FONT_CHAR_HEIGHT * list_selector->font_scale.y + list_selector->padding_bottom)));
+ const char *item_text =
+ list_selector->list_item_text(
+ dynarray_pointer_at(
+ &list_selector->items,
+ i));
+
sprite_font_render_text(
&camera->font,
camera->renderer,
current_position,
list_selector->font_scale,
rgba(1.0f, 1.0f, 1.0f, 1.0f),
- list_selector->items[i]);
+ item_text);
if (i == list_selector->cursor) {
SDL_Rect boundary_box = rect_for_sdl(
sprite_font_boundary_box(
current_position,
list_selector->font_scale,
- strlen(list_selector->items[i])));
+ strlen(item_text)));
if (SDL_SetRenderDrawColor(camera->renderer, 255, 255, 255, 255) < 0) {
return -1;
}
}
Vec2f list_selector_size(const ListSelector *list_selector,
- Vec2f font_scale,
- float padding_bottom)
+ Vec2f font_scale,
+ float padding_bottom)
{
trace_assert(list_selector);
Vec2f result = vec(0.0f, 0.0f);
- for (size_t i = 0; i < list_selector->count; ++i) {
+ for (size_t i = 0; i < list_selector->items.count; ++i) {
+ const char *item_text =
+ list_selector->list_item_text(
+ dynarray_pointer_at(
+ &list_selector->items,
+ i));
+
Rect boundary_box = sprite_font_boundary_box(
vec(0.0f, 0.0f),
font_scale,
- strlen(list_selector->items[i]));
+ strlen(item_text));
result.x = fmaxf(result.x, boundary_box.w);
result.y += boundary_box.y + padding_bottom;
return result;
}
-int list_selector_update(ListSelector *list_selector, float delta_time)
-{
- trace_assert(list_selector);
- (void) delta_time;
-
- return 0;
-}
-
int list_selector_event(ListSelector *list_selector, const SDL_Event *event)
{
trace_assert(list_selector);
}
break;
case SDLK_DOWN:
- if (list_selector->cursor < list_selector->count - 1) {
+ if (list_selector->cursor < list_selector->items.count - 1) {
list_selector->cursor++;
}
break;
const Vec2f mouse_pos = vec((float) event->motion.x, (float) event->motion.y);
Vec2f position = list_selector->position;
- for (size_t i = 0; i < list_selector->count; ++i) {
+ for (size_t i = 0; i < list_selector->items.count; ++i) {
+ const char *item_text =
+ list_selector->list_item_text(
+ dynarray_pointer_at(
+ &list_selector->items,
+ i));
+
Rect boundary_box = sprite_font_boundary_box(
position,
list_selector->font_scale,
- strlen(list_selector->items[i]));
+ strlen(item_text));
if (rect_contains_point(boundary_box, mouse_pos)) {
list_selector->cursor = i;
// check if the click position was actually inside...
// note: make sure there's actually stuff in the list! tsoding likes
// to remove all levels and change title to "SMOL BREAK"...
- if (list_selector->count == 0)
+ if (list_selector->items.count == 0)
break;
// note: this assumes that all list items are the same height!
// this is probably a valid assumption as long as we use a sprite font.
- float single_item_height = sprite_font_boundary_box(
- list_selector->position,
- list_selector->font_scale,
- strlen(list_selector->items[0])).h + list_selector->padding_bottom;
+ float single_item_height =
+ FONT_CHAR_HEIGHT * list_selector->font_scale.y + list_selector->padding_bottom;
Vec2f position = list_selector->position;
vec_add(&position, vec(0.0f, (float) list_selector->cursor * single_item_height));
+ const char *item_text =
+ list_selector->list_item_text(
+ dynarray_pointer_at(
+ &list_selector->items,
+ list_selector->cursor));
+
Rect boundary_box = sprite_font_boundary_box(
position,
list_selector->font_scale,
- strlen(list_selector->items[list_selector->cursor]));
+ strlen(item_text));
const Vec2f mouse_pos = vec((float) event->motion.x, (float) event->motion.y);
if (rect_contains_point(boundary_box, mouse_pos)) {
return 0;
}
-
-int list_selector_selected(const ListSelector *list_selector)
-{
- trace_assert(list_selector);
- return list_selector->selected_item;
-}
-
-void list_selector_clean_selection(ListSelector *list_selector)
-{
- trace_assert(list_selector);
- list_selector->selected_item = -1;
-}
-
-void list_selector_move(ListSelector *list_selector, Vec2f position)
-{
- list_selector->position = position;
-}
#define LIST_SELECTOR_H_
#include "game/camera.h"
+#include "dynarray.h"
-typedef struct ListSelector ListSelector;
+typedef const char *(*ListItemText)(void *element);
-ListSelector *create_list_selector(const char *items[],
- size_t count,
- Vec2f font_scale,
- float padding_bottom);
-void destroy_list_selector(ListSelector *list_selector);
+typedef struct {
+ Dynarray items;
+ size_t cursor;
+ int selected_item;
+ Vec2f position;
+ Vec2f font_scale;
+ float padding_bottom;
+ ListItemText list_item_text;
+} ListSelector;
int list_selector_render(const Camera *camera,
const ListSelector *list_selector);
Vec2f list_selector_size(const ListSelector *list_selector,
Vec2f font_scale,
float padding_bottom);
-
-int list_selector_update(ListSelector *list_selector, float delta_time);
int list_selector_event(ListSelector *list_selector, const SDL_Event *event);
-int list_selector_selected(const ListSelector *list_selector);
-void list_selector_clean_selection(ListSelector *list_selector);
-
-void list_selector_move(ListSelector *list_selector, Vec2f position);
+static inline
+void list_selector_clean_selection(ListSelector *list_selector)
+{
+ trace_assert(list_selector);
+ list_selector->selected_item = -1;
+}
#endif // LIST_SELECTOR_H_