Fully working leaderboards
This commit is contained in:
parent
d0beff76fe
commit
c39adb2a46
5
app.c
5
app.c
@ -4,6 +4,7 @@
|
||||
#include "entity.h"
|
||||
#include "gametime.h"
|
||||
#include "input.h"
|
||||
#include "leaderboards.h"
|
||||
#include "particle.h"
|
||||
#include "physics.h"
|
||||
#include "player.h"
|
||||
@ -34,9 +35,12 @@ App *app_NewApp() {
|
||||
app->particle = particle_NewSystem(app);
|
||||
app->time = gametime_NewSystem(app);
|
||||
app->ui = ui_NewSystem(app);
|
||||
app->lboard = lboard_NewSystem(app);
|
||||
ui_RebuildUI(app->ui);
|
||||
ui_PushState(app->ui, ui_TitleMenu);
|
||||
|
||||
lboard_LoadFromFile(app->lboard, "leaderboards.txt");
|
||||
|
||||
app->current_level = NULL;
|
||||
app->switch_level = NULL;
|
||||
app->timescale = 1.0;
|
||||
@ -58,6 +62,7 @@ void app_DeleteApp(App *app) {
|
||||
particle_DeleteSystem(app->particle);
|
||||
gametime_DeleteSystem(app->time);
|
||||
ui_DeleteSystem(app->ui);
|
||||
lboard_DeleteSystem(app->lboard);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
18
app.h
18
app.h
@ -2,6 +2,7 @@
|
||||
|
||||
#include "entity.h"
|
||||
#include "gametime.h"
|
||||
#include "leaderboards.h"
|
||||
#include "physics.h"
|
||||
#include "player.h"
|
||||
#include "input.h"
|
||||
@ -19,14 +20,15 @@ extern "C" {
|
||||
typedef struct _App {
|
||||
void *window; // HWND pointer; window handle set by main()
|
||||
|
||||
System_Physics *physics;
|
||||
System_Player *player;
|
||||
System_Input *input;
|
||||
System_Entity *entity;
|
||||
System_Camera *camera;
|
||||
System_Particle *particle;
|
||||
System_GameTime *time;
|
||||
System_UI *ui;
|
||||
System_Physics *physics;
|
||||
System_Player *player;
|
||||
System_Input *input;
|
||||
System_Entity *entity;
|
||||
System_Camera *camera;
|
||||
System_Particle *particle;
|
||||
System_GameTime *time;
|
||||
System_UI *ui;
|
||||
System_Leaderboards *lboard;
|
||||
|
||||
char *current_level;
|
||||
char *switch_level;
|
||||
|
@ -25,7 +25,7 @@ void lboard_DeleteSystem(System_Leaderboards *sys) {
|
||||
|
||||
// Free records
|
||||
for (int i = 0; i < vector_Size(sys->records); i++) {
|
||||
tree_Tree *tree = *(tree_Tree **)vector_At(sys->levelnames, i);
|
||||
tree_Tree *tree = *(tree_Tree **)vector_At(sys->records, i);
|
||||
for (tree_Node *node = tree_FirstNode(tree); node != NULL; node = tree_Node_Next(node)) {
|
||||
vector_Vector *names = *(vector_Vector **)node->data;
|
||||
if (names) {
|
||||
@ -57,7 +57,7 @@ void lboard_Insert(System_Leaderboards *sys, const char *level, uintptr_t time_m
|
||||
// Append a new one
|
||||
char *level_copy = copy_malloc(level);
|
||||
tree_Tree *empty_tree = tree_Create(sizeof(vector_Vector *));
|
||||
vector_Push(sys->levelnames, level_copy);
|
||||
vector_Push(sys->levelnames, &level_copy);
|
||||
vector_Push(sys->records, &empty_tree);
|
||||
ASSERT(vector_Size(sys->levelnames) == vector_Size(sys->records));
|
||||
indice = vector_Size(sys->levelnames) - 1;
|
||||
@ -67,7 +67,7 @@ void lboard_Insert(System_Leaderboards *sys, const char *level, uintptr_t time_m
|
||||
|
||||
// Insert or find this node
|
||||
bool added;
|
||||
tree_Node *node = tree_Insert(tree, time_millisec, &added);
|
||||
tree_Node *node = tree_InsertNode(tree, time_millisec, &added);
|
||||
if (added)
|
||||
// Brand new node, create a new vector of names
|
||||
*((vector_Vector **)node->data) = vector_Create(sizeof(char *));
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
#include "leaderboards.h"
|
||||
#include "types.h"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
@ -10,6 +11,9 @@ void lboard_LoadFromFile(System_Leaderboards *sys, const char *filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
int indice = -1;
|
||||
char *current_level = NULL;
|
||||
|
||||
|
||||
// Read every line
|
||||
char linebuf[512];
|
||||
@ -21,7 +25,56 @@ void lboard_LoadFromFile(System_Leaderboards *sys, const char *filename) {
|
||||
// Skip empty lines
|
||||
if (strlen(linebuf) == 0)
|
||||
continue;
|
||||
|
||||
// Look at the first char
|
||||
// if '!', begin a new indice
|
||||
if (linebuf[0] == '!') {
|
||||
indice++;
|
||||
|
||||
if (current_level != NULL)
|
||||
free(current_level);
|
||||
current_level = copy_malloc(&linebuf[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Else, find the first space
|
||||
// Should have a number in front, and name in the back
|
||||
char *space_pos = strchr(linebuf, ' ');
|
||||
if (!space_pos)
|
||||
// Not found? continue
|
||||
continue;
|
||||
*space_pos = 0;
|
||||
|
||||
uintptr_t time_millisec = atoll(linebuf);
|
||||
lboard_Insert(sys, current_level, time_millisec, space_pos + 1);
|
||||
INFO("Inserted @%s %d.%03ds [%s]", current_level, time_millisec / 1000, time_millisec % 1000, space_pos + 1);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
void lboard_SaveToFile(System_Leaderboards *sys, const char *filename) {
|
||||
FILE *f = fopen(filename, "w");
|
||||
if (!f) {
|
||||
WARN("failed to open file\"%s\"", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dump!
|
||||
for (int i = 0; i < vector_Size(sys->records); i++) {
|
||||
fprintf(f, "\n!%s\n", *(char **)vector_At(sys->levelnames, i));
|
||||
tree_Tree *tree = *(tree_Tree **)vector_At(sys->records, i);
|
||||
INFO("Tree %s has %d nodes", *(char **)vector_At(sys->levelnames, i), tree_Count(tree));
|
||||
for (tree_Node *node = tree_FirstNode(tree); node != NULL; node = tree_Node_Next(node)) {
|
||||
vector_Vector *names = *(vector_Vector **)node->data;
|
||||
uintptr_t time_millisec = node->key;
|
||||
if (names) {
|
||||
INFO("Vector %d has %d names", time_millisec, vector_Size(names));
|
||||
for (int j = 0; j < vector_Size(names); j++) {
|
||||
char *name = *(char **)vector_At(names, j);
|
||||
fprintf(f, "%llu %s\n", (unsigned long long)time_millisec, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
7
types.h
7
types.h
@ -37,6 +37,13 @@ static inline void *copy_malloc_size(void *src, size_t size) {
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline char *copy_realloc(char *original, const char *src) {
|
||||
size_t len = strlen(src);
|
||||
char *p = (char *)realloc(original, len + 1);
|
||||
memcpy(p, src, len + 1);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
// A 2d vector of double.
|
||||
typedef struct {
|
||||
|
5
ui.h
5
ui.h
@ -104,8 +104,9 @@ ui_Part ui_Button_NewLeftAligned(Box2 box, const char *label, ui_ActionCallback
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *label; // Allocated & copied, NULL means no label
|
||||
int align; // <0: left aligned; ==0: center aligned; >0: right aligned
|
||||
char *label; // Allocated & copied, NULL means no label
|
||||
int align; // <0: left aligned; ==0: center aligned; >0: right aligned
|
||||
uint32_t color;
|
||||
} ui_Label;
|
||||
ui_Part ui_Label_New(Box2 box, const char *label, int alignment);
|
||||
void ui_Label_SetLabel(ui_Part *part, const char *label);
|
||||
|
127
ui_build.c
127
ui_build.c
@ -1,15 +1,25 @@
|
||||
|
||||
#include "leaderboards.h"
|
||||
#include "render_util.h"
|
||||
#include "types.h"
|
||||
#include "ui.h"
|
||||
#include "app.h"
|
||||
#include "util/tree.h"
|
||||
#include "util/vector.h"
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
#ifndef RGB
|
||||
#define RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
|
||||
#endif
|
||||
|
||||
#define UI_LEADERBOARDS_ITEM_COUNT (8)
|
||||
|
||||
|
||||
// Defined in ui_label.c
|
||||
void _ui_Label_UpdateShiny(System_UI *sys, ui_Part *part, uintptr_t user, Duration deltatime);
|
||||
|
||||
|
||||
static void _ui_Action_ResumeGame(System_UI *sys, ui_Part *part, uintptr_t data) {
|
||||
ui_Action_PopState(sys, part, data);
|
||||
@ -63,6 +73,48 @@ static void _ui_Action_EndIntermission(System_UI *sys, ui_Part *part, uintptr_t
|
||||
}
|
||||
}
|
||||
|
||||
static void _ui_Action_EnterLeaderboards(System_UI *sys, ui_Part *part, uintptr_t data) {
|
||||
vector_Vector *parts = sys->parts[ui_Leaderboards];
|
||||
#define PART(i) ((ui_Part *)vector_At(parts, i))
|
||||
|
||||
int collected = 0;
|
||||
int indice = lboard_FindLevel(sys->super->lboard, sys->super->current_level);
|
||||
if (indice != -1) {
|
||||
tree_Tree *tree = *(tree_Tree **)vector_At(sys->super->lboard->records, indice);
|
||||
for (tree_Node *node = tree_FirstNode(tree); node != NULL; node = tree_Node_Next(node)) {
|
||||
uintptr_t time_millisec = node->key;
|
||||
vector_Vector *names = *(vector_Vector **)node->data;
|
||||
if (names)
|
||||
for (int i = 0; i < vector_Size(names); i++) {
|
||||
char *name = *(char **)vector_At(names, i);
|
||||
if (collected < UI_LEADERBOARDS_ITEM_COUNT) {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%2d %s", collected + 1, name);
|
||||
((ui_Label *)PART(collected * 2)->user)->label = copy_realloc(((ui_Label *)PART(collected * 2)->user)->label, buf);
|
||||
snprintf(
|
||||
buf, sizeof(buf),
|
||||
"%02d:%02d.%02d",
|
||||
time_millisec / 1000 / 60,
|
||||
time_millisec / 1000 % 60,
|
||||
time_millisec / 10 % 100);
|
||||
((ui_Label *)PART(collected * 2 + 1)->user)->label = copy_realloc(((ui_Label *)PART(collected * 2 + 1)->user)->label, buf);
|
||||
collected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (collected < UI_LEADERBOARDS_ITEM_COUNT) {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%2d", collected + 1);
|
||||
((ui_Label *)PART(collected * 2)->user)->label = copy_realloc(((ui_Label *)PART(collected * 2)->user)->label, buf);
|
||||
((ui_Label *)PART(collected * 2 + 1)->user)->label = copy_realloc(((ui_Label *)PART(collected * 2 + 1)->user)->label, "-- N/A --");
|
||||
collected++;
|
||||
}
|
||||
|
||||
ui_Action_PushState(sys, part, (uintptr_t)ui_Leaderboards);
|
||||
}
|
||||
|
||||
|
||||
void ui_EnterIntermission(System_UI *sys, const char *next_level) {
|
||||
// Let's malloc & copy this next_level string
|
||||
@ -71,10 +123,13 @@ void ui_EnterIntermission(System_UI *sys, const char *next_level) {
|
||||
ui_Part *last_part = vector_Back(sys->parts[ui_InterMission]);
|
||||
if (((ui_Button *)last_part->user)->callback_data)
|
||||
free((void *)((ui_Button *)last_part->user)->callback_data);
|
||||
if (!next_level || strlen(next_level) == 0)
|
||||
if (!next_level || strlen(next_level) == 0) {
|
||||
((ui_Button *)last_part->user)->callback_data = 0;
|
||||
else
|
||||
((ui_Button *)last_part->user)->label = copy_realloc(((ui_Button *)last_part->user)->label, "Return to Title");
|
||||
} else {
|
||||
((ui_Button *)last_part->user)->callback_data = (uintptr_t)copy_malloc(next_level);
|
||||
((ui_Button *)last_part->user)->label = copy_realloc(((ui_Button *)last_part->user)->label, "Proceed to Next Level");
|
||||
}
|
||||
ui_PushState(sys, ui_InterMission);
|
||||
sys->super->paused = true;
|
||||
|
||||
@ -82,7 +137,7 @@ void ui_EnterIntermission(System_UI *sys, const char *next_level) {
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
// "Level Time" <level_time> "Record Time" <record_time> <seperator> <replay_level> <return_to_title> <leaderboards> <sep> <next_level>
|
||||
vector_Vector *parts = sys->parts[ui_InterMission];
|
||||
#define PART(i) ((ui_Part *)vector_At(parts, i))
|
||||
// #define PART(i) ((ui_Part *)vector_At(parts, i))
|
||||
char buf[64];
|
||||
|
||||
// <level_time>
|
||||
@ -93,6 +148,42 @@ void ui_EnterIntermission(System_UI *sys, const char *next_level) {
|
||||
sys->super->level_playtime.microseconds / 1000 / 1000 % 60,
|
||||
sys->super->level_playtime.microseconds / 10000 % 100);
|
||||
ui_Label_SetLabel(PART(1), buf);
|
||||
|
||||
// Get the record time
|
||||
uintptr_t record = 0;
|
||||
int indice = lboard_FindLevel(sys->super->lboard, sys->super->current_level);
|
||||
if (indice != -1) {
|
||||
tree_Tree *tree = *(tree_Tree **)vector_At(sys->super->lboard->records, indice);
|
||||
tree_Node *first = tree_FirstNode(tree);
|
||||
if (first)
|
||||
record = first->key;
|
||||
}
|
||||
|
||||
// <record_time>
|
||||
if (record)
|
||||
snprintf(
|
||||
buf, sizeof(buf),
|
||||
"%02d:%02d.%02d",
|
||||
record / 1000 / 60,
|
||||
record / 1000 % 60,
|
||||
record / 10 % 100);
|
||||
else
|
||||
strcpy(buf, "--:--.--");
|
||||
ui_Label_SetLabel(PART(3), buf);
|
||||
|
||||
// Shiny if lower than record
|
||||
if (record == 0 || sys->super->level_playtime.microseconds / 1000 < record)
|
||||
PART(1)->update = &_ui_Label_UpdateShiny;
|
||||
else {
|
||||
PART(1)->update = NULL;
|
||||
((ui_Label *)PART(1)->user)->color = RGB(255, 255, 255);
|
||||
}
|
||||
|
||||
// Get user name
|
||||
DWORD bufsize = sizeof(buf) - 1;
|
||||
GetUserNameA(buf, &bufsize);
|
||||
lboard_Insert(sys->super->lboard, sys->super->current_level, sys->super->level_playtime.microseconds / 1000, buf);
|
||||
lboard_SaveToFile(sys->super->lboard, "leaderboards.txt");
|
||||
}
|
||||
|
||||
|
||||
@ -164,7 +255,7 @@ void ui_RebuildUI(System_UI *sys) {
|
||||
PUSH_UI(ui_Button_New(
|
||||
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250 + 2 * (40 + UI_PADDING)), vec2(400, 40)),
|
||||
"Leaderboard for This Level",
|
||||
&ui_Action_PushState, (uintptr_t)ui_Leaderboards))
|
||||
&_ui_Action_EnterLeaderboards, 0))
|
||||
PUSH_UI(ui_Button_New(
|
||||
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250 + 3 * (40 + UI_PADDING)), vec2(400, 40)),
|
||||
"Options",
|
||||
@ -228,7 +319,7 @@ void ui_RebuildUI(System_UI *sys) {
|
||||
PUSH_UI(ui_Button_New(
|
||||
ui_BoxBuilder_At(builder, 5, 1),
|
||||
"View Leaderboards for Level",
|
||||
&ui_Action_PushState, (uintptr_t)ui_Leaderboards))
|
||||
&_ui_Action_EnterLeaderboards, 0))
|
||||
PUSH_UI(ui_Fill_New(
|
||||
box2v(
|
||||
vec2_Add(
|
||||
@ -240,8 +331,34 @@ void ui_RebuildUI(System_UI *sys) {
|
||||
ui_BoxBuilder_At(builder, 7, 1),
|
||||
"Proceed to Next Level",
|
||||
&_ui_Action_EndIntermission, 0))
|
||||
ui_BoxBuilder_Delete(builder);
|
||||
|
||||
|
||||
// Leaderboards
|
||||
p = sys->parts[ui_Leaderboards];
|
||||
vector_Clear(p);
|
||||
|
||||
builder = ui_BoxBuilder_New();
|
||||
builder->line_height = 28;
|
||||
builder->pivot = vec2(0.5, 0.5625);
|
||||
builder->position = vec2(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0);
|
||||
for (int i = 1; i <= UI_LEADERBOARDS_ITEM_COUNT; i++)
|
||||
ui_BoxBuilder_SetCount(builder, i, 1);
|
||||
ui_BoxBuilder_SetCount(builder, UI_LEADERBOARDS_ITEM_COUNT + 1, 1);
|
||||
ui_BoxBuilder_Assemble(builder);
|
||||
|
||||
for (int i = 1; i <= UI_LEADERBOARDS_ITEM_COUNT; i++) {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%2d", i);
|
||||
PUSH_UI(ui_Label_New(
|
||||
ui_BoxBuilder_At(builder, i, 1),
|
||||
buf, -1))
|
||||
PUSH_UI(ui_Label_New(
|
||||
ui_BoxBuilder_At(builder, i, 1),
|
||||
"-- N/A --", 1))
|
||||
}
|
||||
PUSH_UI(ui_Button_New(
|
||||
ui_BoxBuilder_At(builder, UI_LEADERBOARDS_ITEM_COUNT + 1, 1),
|
||||
"Return",
|
||||
&ui_Action_PopState, 0))
|
||||
}
|
||||
|
27
ui_label.c
27
ui_label.c
@ -3,9 +3,35 @@
|
||||
#include "ui.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifndef RGB
|
||||
#define RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
|
||||
#endif
|
||||
|
||||
|
||||
void _ui_Fill_Draw(System_UI *sys, ui_Part *part, uintptr_t user); // Defined in ui_render.cpp
|
||||
void _ui_Label_Draw(System_UI *sys, ui_Part *part, uintptr_t user); // Defined in ui_render.cpp
|
||||
void _ui_Label_UpdateShiny(System_UI *sys, ui_Part *part, uintptr_t user, Duration deltatime) {
|
||||
ui_Label *label = (ui_Label *)part->user;
|
||||
// Compute the new color
|
||||
int r, g, b;
|
||||
double now = time_Now().microseconds / 1e6;
|
||||
double cycle = fmod(now, 3.0);
|
||||
double prog = fmod(now, 1.0);
|
||||
if (cycle < 1.0) {
|
||||
r = 255;
|
||||
g = 255 * prog;
|
||||
b = 255 * (1 - prog);
|
||||
} else if (cycle < 2.0) {
|
||||
r = 255 * (1 - prog);
|
||||
g = 255;
|
||||
b = 255 * prog;
|
||||
} else {
|
||||
r = 255 * prog;
|
||||
g = 255 * (1 - prog);
|
||||
b = 255;
|
||||
}
|
||||
label->color = RGB(r, g, b);
|
||||
}
|
||||
void _ui_Label_Free(System_UI *sys, ui_Part *part, uintptr_t user) {
|
||||
ui_Label *label = (ui_Label *)part->user;
|
||||
if (label->label)
|
||||
@ -17,6 +43,7 @@ ui_Part ui_Label_New(Box2 box, const char *label_text, int alignment) {
|
||||
if (label_text && strlen(label_text) > 0)
|
||||
label->label = copy_malloc(label_text);
|
||||
label->align = alignment;
|
||||
label->color = RGB(255, 255, 255);
|
||||
|
||||
ui_Part part = {
|
||||
.box = box,
|
||||
|
@ -92,7 +92,7 @@ extern "C" void _ui_Label_Draw(System_UI *sys, ui_Part *part, uintptr_t user) {
|
||||
padded_box.size.x -= 2 * UI_PADDING;
|
||||
|
||||
setbkcolor(0);
|
||||
settextcolor(RGB(255, 255, 255));
|
||||
settextcolor(label->color);
|
||||
|
||||
if (label->align < 0)
|
||||
render_DrawTextEx(label->label, padded_box, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
||||
|
Loading…
Reference in New Issue
Block a user