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->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;
|
||||
}
|
||||
|
||||
@ -1561,6 +1573,8 @@ void options_main_t::build()
|
||||
button("Graphics", 6);
|
||||
button("Audio", 2);
|
||||
button("System", 5);
|
||||
if(prefs->global_leaderboard)
|
||||
button("Leaderboard Board Name", 7);
|
||||
space();
|
||||
|
||||
button("DONE!", 0);
|
||||
@ -1595,6 +1609,9 @@ void st_options_main_t::select(int tag)
|
||||
case 6:
|
||||
gsm.push(&st_options_graphics);
|
||||
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;
|
||||
|
||||
|
||||
/*----------------------------------------------------------
|
||||
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
|
||||
----------------------------------------------------------*/
|
||||
@ -550,6 +581,7 @@ extern st_options_graphics_t st_options_graphics;
|
||||
extern st_options_audio_t st_options_audio;
|
||||
extern st_options_control_t st_options_control;
|
||||
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_error_t st_error;
|
||||
#ifdef PROFILE_AUDIO
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user