Add board-name editor + auto-prompt for global leaderboard
When the global leaderboard is enabled but no board name is configured, prompt the user for one on first main-menu entry of the session. The editor itself is also reachable any time via Options > Leaderboard Board Name. - edit_board_t / st_edit_board_t: clones the new_player_t arrow-key text editor. Bound to prefs->leaderboard_board; allows [A-Za-z0-9_-]; OK trims trailing arrow-key noise, writes back, and marks prefs dirty. - st_main_menu_t::open: on first main-menu entry of the session with global_leaderboard on and board empty, push st_edit_board. Static guard prevents re-prompting after cancel (user can always reach the editor via the options menu). - options_main_t: adds "Leaderboard Board Name" entry, gated on global_leaderboard being on so it doesn't clutter the menu otherwise. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9238ce61c6
commit
8b3e21b5f3
252
states.cpp
252
states.cpp
@ -1357,6 +1357,18 @@ kobo_form_t *st_main_menu_t::open()
|
|||||||
|
|
||||||
menu = new main_menu_t;
|
menu = new main_menu_t;
|
||||||
menu->open();
|
menu->open();
|
||||||
|
|
||||||
|
// First time we reach the main menu with the leaderboard on but no
|
||||||
|
// board name configured, prompt for one. Only once per session — if
|
||||||
|
// the user cancels, they can edit it from Options later.
|
||||||
|
static bool prompted_for_board = false;
|
||||||
|
if(!prompted_for_board && prefs->global_leaderboard
|
||||||
|
&& prefs->leaderboard_board[0] == '\0')
|
||||||
|
{
|
||||||
|
prompted_for_board = true;
|
||||||
|
gsm.push(&st_edit_board);
|
||||||
|
}
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1561,6 +1573,8 @@ void options_main_t::build()
|
|||||||
button("Graphics", 6);
|
button("Graphics", 6);
|
||||||
button("Audio", 2);
|
button("Audio", 2);
|
||||||
button("System", 5);
|
button("System", 5);
|
||||||
|
if(prefs->global_leaderboard)
|
||||||
|
button("Leaderboard Board Name", 7);
|
||||||
space();
|
space();
|
||||||
|
|
||||||
button("DONE!", 0);
|
button("DONE!", 0);
|
||||||
@ -1595,6 +1609,9 @@ void st_options_main_t::select(int tag)
|
|||||||
case 6:
|
case 6:
|
||||||
gsm.push(&st_options_graphics);
|
gsm.push(&st_options_graphics);
|
||||||
break;
|
break;
|
||||||
|
case 7:
|
||||||
|
gsm.push(&st_edit_board);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2142,3 +2159,238 @@ void st_global_leaderboard_t::post_render()
|
|||||||
|
|
||||||
st_global_leaderboard_t st_global_leaderboard;
|
st_global_leaderboard_t st_global_leaderboard;
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------
|
||||||
|
Edit leaderboard board name
|
||||||
|
----------------------------------------------------------*/
|
||||||
|
//
|
||||||
|
// Cloned from new_player_t. Differences: bound to prefs->leaderboard_board,
|
||||||
|
// allows alphanumerics + '-' / '_' (room-code shape), no profile creation —
|
||||||
|
// OK writes the value back into prefs and pops.
|
||||||
|
|
||||||
|
void edit_board_t::open()
|
||||||
|
{
|
||||||
|
init(gengine);
|
||||||
|
place(wmain->x(), wmain->y(), wmain->width(), wmain->height());
|
||||||
|
font(B_NORMAL_FONT);
|
||||||
|
foreground(wmain->map_rgb(255, 255, 255));
|
||||||
|
background(wmain->map_rgb(0, 0, 0));
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
strncpy(buf, prefs->leaderboard_board, sizeof(buf) - 1);
|
||||||
|
currentIndex = strlen(buf);
|
||||||
|
if(currentIndex == 0)
|
||||||
|
{
|
||||||
|
buf[0] = 'A';
|
||||||
|
currentIndex = 0;
|
||||||
|
}
|
||||||
|
editing = 1;
|
||||||
|
build_all();
|
||||||
|
if(gengine->window_handle())
|
||||||
|
SDL_StartTextInput(gengine->window_handle());
|
||||||
|
}
|
||||||
|
|
||||||
|
void edit_board_t::close()
|
||||||
|
{
|
||||||
|
if(gengine->window_handle())
|
||||||
|
SDL_StopTextInput(gengine->window_handle());
|
||||||
|
clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
void edit_board_t::change(int delta)
|
||||||
|
{
|
||||||
|
kobo_form_t::change(delta);
|
||||||
|
if(!selected()) return;
|
||||||
|
selection = selected()->tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void edit_board_t::build()
|
||||||
|
{
|
||||||
|
medium();
|
||||||
|
space(2);
|
||||||
|
label("Global Leaderboard");
|
||||||
|
label("Use arrows or keyboard to enter board name");
|
||||||
|
label("(share it with friends to compete)");
|
||||||
|
|
||||||
|
big();
|
||||||
|
space();
|
||||||
|
button(buf, 1);
|
||||||
|
space();
|
||||||
|
|
||||||
|
button("Ok", MENU_TAG_OK);
|
||||||
|
button("Cancel", MENU_TAG_CANCEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void edit_board_t::rebuild()
|
||||||
|
{
|
||||||
|
int sel = selected_index();
|
||||||
|
build_all();
|
||||||
|
select(sel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool board_char_valid(int c)
|
||||||
|
{
|
||||||
|
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
|
||||||
|
|| (c >= '0' && c <= '9') || c == '-' || c == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
st_edit_board_t::st_edit_board_t()
|
||||||
|
{
|
||||||
|
name = "edit_board";
|
||||||
|
}
|
||||||
|
|
||||||
|
void st_edit_board_t::frame() { manage.run_intro(); }
|
||||||
|
void st_edit_board_t::enter() { menu.open(); run_intro = 0; sound.ui_ok(); }
|
||||||
|
void st_edit_board_t::leave() { menu.close(); }
|
||||||
|
void st_edit_board_t::post_render() { kobo_basestate_t::post_render(); menu.render(); }
|
||||||
|
|
||||||
|
void st_edit_board_t::press(int button)
|
||||||
|
{
|
||||||
|
if(menu.editing)
|
||||||
|
{
|
||||||
|
switch(button)
|
||||||
|
{
|
||||||
|
case BTN_EXIT:
|
||||||
|
sound.ui_ok();
|
||||||
|
menu.editing = 0;
|
||||||
|
menu.next(); // Select the CANCEL option.
|
||||||
|
menu.next();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BTN_FIRE:
|
||||||
|
if(!prefs->use_joystick) break;
|
||||||
|
// fallthrough
|
||||||
|
case BTN_START:
|
||||||
|
case BTN_SELECT:
|
||||||
|
sound.ui_ok();
|
||||||
|
menu.editing = 0;
|
||||||
|
menu.next(); // Select the OK option.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BTN_UP:
|
||||||
|
case BTN_INC:
|
||||||
|
{
|
||||||
|
char &c = menu.buf[menu.currentIndex];
|
||||||
|
if(!c) c = 'A';
|
||||||
|
else if(c == 'Z') c = 'a';
|
||||||
|
else if(c == 'z') c = '0';
|
||||||
|
else if(c == '9') c = '-';
|
||||||
|
else if(c == '-') c = '_';
|
||||||
|
else if(c == '_') c = 'A';
|
||||||
|
else c++;
|
||||||
|
sound.ui_tick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BTN_DOWN:
|
||||||
|
case BTN_DEC:
|
||||||
|
{
|
||||||
|
char &c = menu.buf[menu.currentIndex];
|
||||||
|
if(!c) c = 'A';
|
||||||
|
else if(c == 'A') c = '_';
|
||||||
|
else if(c == '_') c = '-';
|
||||||
|
else if(c == '-') c = '9';
|
||||||
|
else if(c == '0') c = 'z';
|
||||||
|
else if(c == 'a') c = 'Z';
|
||||||
|
else c--;
|
||||||
|
sound.ui_tick();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BTN_RIGHT:
|
||||||
|
if(menu.currentIndex < sizeof(menu.buf) - 2)
|
||||||
|
{
|
||||||
|
menu.currentIndex++;
|
||||||
|
sound.ui_tick();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sound.ui_error();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(menu.buf[menu.currentIndex] == '\0')
|
||||||
|
menu.buf[menu.currentIndex] = 'A';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BTN_LEFT:
|
||||||
|
case BTN_BACK:
|
||||||
|
if(menu.currentIndex > 0)
|
||||||
|
{
|
||||||
|
menu.buf[menu.currentIndex] = '\0';
|
||||||
|
menu.currentIndex--;
|
||||||
|
sound.ui_tick();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sound.ui_error();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(board_char_valid(unicode))
|
||||||
|
{
|
||||||
|
menu.buf[menu.currentIndex] = (char)unicode;
|
||||||
|
if(menu.currentIndex < sizeof(menu.buf) - 2)
|
||||||
|
{
|
||||||
|
menu.currentIndex++;
|
||||||
|
sound.ui_tick();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sound.ui_error();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sound.ui_error();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
menu.rebuild();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menu.selection = -1;
|
||||||
|
switch(button)
|
||||||
|
{
|
||||||
|
case BTN_EXIT: menu.selection = MENU_TAG_CANCEL; break;
|
||||||
|
case BTN_CLOSE: menu.selection = MENU_TAG_OK; break;
|
||||||
|
case BTN_FIRE:
|
||||||
|
case BTN_START:
|
||||||
|
case BTN_SELECT: menu.change(0); break;
|
||||||
|
case BTN_INC:
|
||||||
|
case BTN_UP: menu.prev(); break;
|
||||||
|
case BTN_DEC:
|
||||||
|
case BTN_DOWN: menu.next(); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(menu.selection)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
if(button == BTN_START || button == BTN_SELECT
|
||||||
|
|| button == BTN_FIRE)
|
||||||
|
{
|
||||||
|
sound.ui_ok();
|
||||||
|
menu.editing = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MENU_TAG_OK:
|
||||||
|
{
|
||||||
|
// Trim trailing garbage from arrow-key wandering.
|
||||||
|
size_t n = strlen(menu.buf);
|
||||||
|
while(n > 0 && !board_char_valid(menu.buf[n - 1]))
|
||||||
|
menu.buf[--n] = '\0';
|
||||||
|
strncpy(prefs->leaderboard_board, menu.buf,
|
||||||
|
sizeof(prefs->leaderboard_board) - 1);
|
||||||
|
prefs->leaderboard_board[
|
||||||
|
sizeof(prefs->leaderboard_board) - 1] = '\0';
|
||||||
|
prefs->changed = 1;
|
||||||
|
sound.ui_ok();
|
||||||
|
pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MENU_TAG_CANCEL:
|
||||||
|
sound.ui_cancel();
|
||||||
|
pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
st_edit_board_t st_edit_board;
|
||||||
|
|
||||||
|
|||||||
32
states.h
32
states.h
@ -473,6 +473,37 @@ class st_ask_abort_game_t : public st_yesno_base_t
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------------------------------------
|
||||||
|
Edit leaderboard board name
|
||||||
|
----------------------------------------------------------*/
|
||||||
|
|
||||||
|
class edit_board_t : public kobo_form_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int editing;
|
||||||
|
char buf[32];
|
||||||
|
unsigned currentIndex;
|
||||||
|
int selection;
|
||||||
|
virtual void change(int delta);
|
||||||
|
virtual void build();
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
void rebuild();
|
||||||
|
};
|
||||||
|
|
||||||
|
class st_edit_board_t : public kobo_basestate_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
edit_board_t menu;
|
||||||
|
st_edit_board_t();
|
||||||
|
void enter();
|
||||||
|
void leave();
|
||||||
|
void press(int button);
|
||||||
|
void frame();
|
||||||
|
void post_render();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*----------------------------------------------------------
|
/*----------------------------------------------------------
|
||||||
Global Leaderboard browser
|
Global Leaderboard browser
|
||||||
----------------------------------------------------------*/
|
----------------------------------------------------------*/
|
||||||
@ -550,6 +581,7 @@ extern st_options_graphics_t st_options_graphics;
|
|||||||
extern st_options_audio_t st_options_audio;
|
extern st_options_audio_t st_options_audio;
|
||||||
extern st_options_control_t st_options_control;
|
extern st_options_control_t st_options_control;
|
||||||
extern st_options_game_t st_options_game;
|
extern st_options_game_t st_options_game;
|
||||||
|
extern st_edit_board_t st_edit_board;
|
||||||
extern st_global_leaderboard_t st_global_leaderboard;
|
extern st_global_leaderboard_t st_global_leaderboard;
|
||||||
extern st_error_t st_error;
|
extern st_error_t st_error;
|
||||||
#ifdef PROFILE_AUDIO
|
#ifdef PROFILE_AUDIO
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user