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>
2397 lines
45 KiB
C++
2397 lines
45 KiB
C++
/*
|
|
------------------------------------------------------------
|
|
Kobo Deluxe - An enhanced SDL port of XKobo
|
|
------------------------------------------------------------
|
|
* Copyright (C) 2001-2003 David Olofson
|
|
* Copyright (C) 2002 Jeremy Sheeley
|
|
* Copyright (C) 2005-2007 David Olofson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "sdl_compat.h"
|
|
#include "gfxengine.h"
|
|
#include <math.h>
|
|
#ifndef M_PI
|
|
# define M_PI 3.14159265358979323846 /* pi */
|
|
#endif
|
|
|
|
#include "kobolog.h"
|
|
#include "config.h"
|
|
#include "states.h"
|
|
#include "kobo.h"
|
|
#include "screen.h"
|
|
#include "manage.h"
|
|
#include "options.h"
|
|
#include "sound.h"
|
|
#include "radar.h"
|
|
#include "random.h"
|
|
#include "leaderboard.h"
|
|
#include "prefs.h"
|
|
#include <stdio.h>
|
|
|
|
|
|
gamestatemanager_t gsm;
|
|
int run_intro = 0;
|
|
int last_level = -1;
|
|
|
|
|
|
kobo_basestate_t::kobo_basestate_t()
|
|
{
|
|
name = "<unnamed>";
|
|
}
|
|
|
|
|
|
void kobo_basestate_t::frame()
|
|
{
|
|
}
|
|
|
|
|
|
void kobo_basestate_t::pre_render()
|
|
{
|
|
screen.render_background(wmain);
|
|
}
|
|
|
|
|
|
void kobo_basestate_t::post_render()
|
|
{
|
|
screen.render_fx(wmain);
|
|
DBG(if(prefs->cmd_debug)
|
|
{
|
|
wmain->font(B_NORMAL_FONT);
|
|
wmain->string(30, 5, name);
|
|
})
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_introbase
|
|
----------------------------------------------------------*/
|
|
|
|
st_introbase_t::st_introbase_t()
|
|
{
|
|
name = "intro";
|
|
inext = NULL;
|
|
duration = 0;
|
|
timer = 0;
|
|
}
|
|
|
|
|
|
void st_introbase_t::enter()
|
|
{
|
|
if(!run_intro)
|
|
{
|
|
manage.init_resources_title();
|
|
if(prefs->use_music)
|
|
sound.ui_music(SOUND_TITLE);
|
|
run_intro = 1;
|
|
}
|
|
start_time = (int)SDL_GetTicks() + INTRO_BLANK_TIME;
|
|
timer = 0;
|
|
}
|
|
|
|
|
|
void st_introbase_t::reenter()
|
|
{
|
|
if(!run_intro)
|
|
{
|
|
manage.init_resources_title();
|
|
if(prefs->use_music)
|
|
sound.ui_music(SOUND_TITLE);
|
|
run_intro = 1;
|
|
}
|
|
gsm.change(&st_intro_title);
|
|
}
|
|
|
|
|
|
void st_introbase_t::press(int button)
|
|
{
|
|
switch (button)
|
|
{
|
|
case BTN_EXIT:
|
|
case BTN_CLOSE:
|
|
gsm.push(&st_main_menu);
|
|
break;
|
|
case BTN_START:
|
|
case BTN_FIRE:
|
|
case BTN_SELECT:
|
|
run_intro = 0;
|
|
gsm.push(&st_main_menu);
|
|
if(scorefile.numProfiles <= 0)
|
|
gsm.push(&st_new_player);
|
|
break;
|
|
case BTN_BACK:
|
|
case BTN_UP:
|
|
case BTN_LEFT:
|
|
gsm.change(&st_intro_title);
|
|
break;
|
|
case BTN_DOWN:
|
|
case BTN_RIGHT:
|
|
if(inext)
|
|
gsm.change(inext);
|
|
break;
|
|
#ifdef PROFILE_AUDIO
|
|
case BTN_F9:
|
|
run_intro = 0;
|
|
gsm.push(&st_profile_audio);
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void st_introbase_t::frame()
|
|
{
|
|
manage.run_intro();
|
|
if((timer > duration) && inext)
|
|
gsm.change(inext);
|
|
}
|
|
|
|
|
|
void st_introbase_t::pre_render()
|
|
{
|
|
kobo_basestate_t::pre_render();
|
|
timer = (int)SDL_GetTicks() - start_time;
|
|
}
|
|
|
|
|
|
void st_introbase_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
screen.scroller();
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_intro_title
|
|
----------------------------------------------------------*/
|
|
|
|
st_intro_title_t::st_intro_title_t()
|
|
{
|
|
name = "intro_title";
|
|
}
|
|
|
|
void st_intro_title_t::enter()
|
|
{
|
|
st_introbase_t::enter();
|
|
if(!duration)
|
|
duration = INTRO_TITLE_TIME + 2000 - INTRO_BLANK_TIME;
|
|
if(!inext)
|
|
inext = &st_intro_instructions;
|
|
screen.set_highlight(0, 0);
|
|
}
|
|
|
|
void st_intro_title_t::post_render()
|
|
{
|
|
if(exit_game)
|
|
return;
|
|
st_introbase_t::post_render();
|
|
if((timer >= 0) && (timer < duration))
|
|
{
|
|
float nt = (float)timer / duration;
|
|
float snt = 1.0f - sin(nt * M_PI);
|
|
snt = 1.0f - snt * snt * snt;
|
|
screen.title(timer, snt, mode);
|
|
}
|
|
}
|
|
|
|
st_intro_title_t st_intro_title;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_intro_instructions
|
|
----------------------------------------------------------*/
|
|
|
|
st_intro_instructions_t::st_intro_instructions_t()
|
|
{
|
|
name = "intro_instructions";
|
|
}
|
|
|
|
void st_intro_instructions_t::enter()
|
|
{
|
|
st_introbase_t::enter();
|
|
duration = INTRO_INSTRUCTIONS_TIME;
|
|
inext = &st_intro_title;
|
|
st_intro_title.inext = &st_intro_highscores;
|
|
st_intro_title.duration = INTRO_TITLE2_TIME - INTRO_BLANK_TIME;
|
|
st_intro_title.mode = pubrand.get(1) + 1;
|
|
}
|
|
|
|
void st_intro_instructions_t::post_render()
|
|
{
|
|
if(exit_game)
|
|
return;
|
|
st_introbase_t::post_render();
|
|
if((timer >= 0) && (timer < duration))
|
|
screen.help(timer);
|
|
else
|
|
screen.set_highlight(0, 0);
|
|
}
|
|
|
|
st_intro_instructions_t st_intro_instructions;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_intro_highshores
|
|
----------------------------------------------------------*/
|
|
|
|
st_intro_highscores_t::st_intro_highscores_t()
|
|
{
|
|
name = "intro_highscores";
|
|
}
|
|
|
|
void st_intro_highscores_t::enter()
|
|
{
|
|
scorefile.gather_high_scores(1);
|
|
screen.init_highscores();
|
|
st_introbase_t::enter();
|
|
duration = INTRO_HIGHSCORE_TIME;
|
|
inext = &st_intro_title;
|
|
st_intro_title.inext = &st_intro_credits;
|
|
st_intro_title.duration = INTRO_TITLE2_TIME - INTRO_BLANK_TIME;
|
|
st_intro_title.mode = pubrand.get(1) + 1;
|
|
screen.set_highlight(0, 0);
|
|
}
|
|
|
|
void st_intro_highscores_t::post_render()
|
|
{
|
|
if(exit_game)
|
|
return;
|
|
st_introbase_t::post_render();
|
|
if((timer >= 0) && (timer < duration))
|
|
{
|
|
float nt = (float)timer / duration;
|
|
float snt = 1.0f - sin(nt * M_PI);
|
|
snt = 1.0f - snt * snt * snt;
|
|
screen.highscores(timer, snt);
|
|
}
|
|
}
|
|
|
|
st_intro_highscores_t st_intro_highscores;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_intro_credits
|
|
----------------------------------------------------------*/
|
|
|
|
st_intro_credits_t::st_intro_credits_t()
|
|
{
|
|
name = "intro_credits";
|
|
}
|
|
|
|
void st_intro_credits_t::enter()
|
|
{
|
|
st_introbase_t::enter();
|
|
duration = INTRO_CREDITS_TIME;
|
|
inext = &st_intro_title;
|
|
st_intro_title.inext = &st_intro_instructions;
|
|
st_intro_title.duration = INTRO_TITLE_TIME - INTRO_BLANK_TIME;
|
|
st_intro_title.mode = 0;
|
|
}
|
|
|
|
void st_intro_credits_t::post_render()
|
|
{
|
|
if(exit_game)
|
|
return;
|
|
st_introbase_t::post_render();
|
|
if((timer >= 0) && (timer < duration))
|
|
screen.credits(timer);
|
|
else
|
|
screen.set_highlight(0, 0);
|
|
}
|
|
|
|
st_intro_credits_t st_intro_credits;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_game
|
|
----------------------------------------------------------*/
|
|
|
|
st_game_t::st_game_t()
|
|
{
|
|
name = "game";
|
|
}
|
|
|
|
|
|
void st_game_t::enter()
|
|
{
|
|
audio_channel_stop(0, -1); //Stop any music
|
|
run_intro = 0;
|
|
manage.game_start();
|
|
if(exit_game || manage.game_stopped())
|
|
{
|
|
st_error.message("Could not start game!",
|
|
"Please, check your installation.");
|
|
gsm.change(&st_error);
|
|
}
|
|
if(prefs->mousecapture && gengine->window_handle())
|
|
SDL_SetWindowMouseGrab(gengine->window_handle(), true);
|
|
}
|
|
|
|
|
|
void st_game_t::leave()
|
|
{
|
|
if(gengine->window_handle())
|
|
SDL_SetWindowMouseGrab(gengine->window_handle(), false);
|
|
st_intro_title.inext = &st_intro_instructions;
|
|
st_intro_title.duration = INTRO_TITLE_TIME + 2000;
|
|
st_intro_title.mode = 0;
|
|
}
|
|
|
|
|
|
void st_game_t::yield()
|
|
{
|
|
if(gengine->window_handle())
|
|
SDL_SetWindowMouseGrab(gengine->window_handle(), false);
|
|
}
|
|
|
|
|
|
void st_game_t::reenter()
|
|
{
|
|
if(prefs->mousecapture && gengine->window_handle())
|
|
SDL_SetWindowMouseGrab(gengine->window_handle(), true);
|
|
}
|
|
|
|
|
|
void st_game_t::press(int button)
|
|
{
|
|
switch (button)
|
|
{
|
|
case BTN_EXIT:
|
|
gsm.push(&st_main_menu);
|
|
break;
|
|
case BTN_CLOSE:
|
|
gsm.push(&st_ask_exit);
|
|
break;
|
|
case BTN_SELECT:
|
|
case BTN_START:
|
|
case BTN_PAUSE:
|
|
gsm.push(&st_pause_game);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void st_game_t::frame()
|
|
{
|
|
if(manage.get_ready())
|
|
{
|
|
gsm.push(&st_get_ready);
|
|
return;
|
|
}
|
|
manage.run_game();
|
|
last_level = manage.scene();
|
|
if(manage.game_over())
|
|
gsm.change(&st_game_over);
|
|
else if(exit_game || manage.game_stopped())
|
|
pop();
|
|
}
|
|
|
|
|
|
void st_game_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
wradar->frame();
|
|
}
|
|
|
|
|
|
st_game_t st_game;
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_pause_game
|
|
----------------------------------------------------------*/
|
|
|
|
st_pause_game_t::st_pause_game_t()
|
|
{
|
|
name = "pause_game";
|
|
}
|
|
|
|
|
|
void st_pause_game_t::enter()
|
|
{
|
|
sound.ui_pause();
|
|
}
|
|
|
|
|
|
void st_pause_game_t::press(int button)
|
|
{
|
|
switch (button)
|
|
{
|
|
case BTN_EXIT:
|
|
gsm.change(&st_main_menu);
|
|
break;
|
|
default:
|
|
sound.ui_play();
|
|
pop();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void st_pause_game_t::frame()
|
|
{
|
|
manage.run_pause();
|
|
}
|
|
|
|
|
|
void st_pause_game_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
|
|
float ft = SDL_GetTicks() * 0.001;
|
|
wmain->font(B_BIG_FONT);
|
|
int y = PIXEL2CS(75) + (int)floor(PIXEL2CS(15)*sin(ft * 6));
|
|
wmain->center_fxp(y, "PAUSED");
|
|
|
|
wradar->frame();
|
|
}
|
|
|
|
st_pause_game_t st_pause_game;
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_get_ready
|
|
----------------------------------------------------------*/
|
|
|
|
st_get_ready_t::st_get_ready_t()
|
|
{
|
|
name = "get_ready";
|
|
}
|
|
|
|
|
|
void st_get_ready_t::enter()
|
|
{
|
|
manage.update();
|
|
sound.ui_ready();
|
|
start_time = (int)SDL_GetTicks();
|
|
frame_time = 0;
|
|
countdown = prefs->countdown;
|
|
}
|
|
|
|
|
|
void st_get_ready_t::press(int button)
|
|
{
|
|
if(frame_time < 500)
|
|
return;
|
|
|
|
switch (button)
|
|
{
|
|
case BTN_EXIT:
|
|
case BTN_NO:
|
|
gsm.change(&st_main_menu);
|
|
break;
|
|
case BTN_LEFT:
|
|
case BTN_RIGHT:
|
|
case BTN_UP:
|
|
case BTN_DOWN:
|
|
case BTN_UL:
|
|
case BTN_UR:
|
|
case BTN_DL:
|
|
case BTN_DR:
|
|
case BTN_FIRE:
|
|
case BTN_YES:
|
|
sound.ui_play();
|
|
pop();
|
|
break;
|
|
case BTN_SELECT:
|
|
case BTN_START:
|
|
case BTN_PAUSE:
|
|
gsm.change(&st_pause_game);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void st_get_ready_t::frame()
|
|
{
|
|
manage.run_pause();
|
|
|
|
if(exit_game || manage.game_stopped())
|
|
{
|
|
pop();
|
|
return;
|
|
}
|
|
|
|
frame_time = (int)SDL_GetTicks() - start_time;
|
|
if(0 == prefs->countdown)
|
|
{
|
|
if(frame_time > 700)
|
|
{
|
|
sound.ui_play();
|
|
pop();
|
|
}
|
|
}
|
|
else if(prefs->countdown <= 9)
|
|
{
|
|
int prevcount = countdown;
|
|
countdown = prefs->countdown - frame_time/1000;
|
|
if(prevcount != countdown)
|
|
sound.ui_countdown(countdown);
|
|
|
|
if(countdown < 1)
|
|
{
|
|
sound.ui_play();
|
|
pop();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void st_get_ready_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
|
|
float ft = SDL_GetTicks() * 0.001;
|
|
char counter[2] = "0";
|
|
wmain->font(B_BIG_FONT);
|
|
int y = PIXEL2CS(70) + (int)floor(PIXEL2CS(15)*sin(ft * 6));
|
|
wmain->center_fxp(y, "GET READY!");
|
|
|
|
float z = (float)((int)SDL_GetTicks() - start_time);
|
|
if(10 == prefs->countdown)
|
|
z = -1;
|
|
else if(prefs->countdown)
|
|
z = prefs->countdown - z * 0.001;
|
|
else
|
|
z = 1.0 - z / 700.0;
|
|
if((z > 0.0) && (z < 1.0))
|
|
{
|
|
float x = wmain->width() / 2;
|
|
wmain->foreground(wmain->map_rgb(
|
|
255 - (int)(z * 255.0),
|
|
(int)(z * 255.0),
|
|
0));
|
|
wmain->fillrect_fxp(PIXEL2CS((int)(x - z * 50.0)),
|
|
y + PIXEL2CS(76),
|
|
PIXEL2CS((int)(z * 100.0)),
|
|
PIXEL2CS(10));
|
|
}
|
|
|
|
wmain->font(B_MEDIUM_FONT);
|
|
if(10 == prefs->countdown)
|
|
wmain->center_fxp(y + PIXEL2CS(70), "(Press FIRE)");
|
|
else if(prefs->countdown)
|
|
{
|
|
wmain->center_fxp(y + PIXEL2CS(100), "(Press FIRE)");
|
|
counter[0] = countdown + '0';
|
|
wmain->font(B_COUNTER_FONT);
|
|
wmain->center_fxp(y + PIXEL2CS(60), counter);
|
|
}
|
|
|
|
wradar->frame();
|
|
}
|
|
|
|
st_get_ready_t st_get_ready;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_game_over
|
|
----------------------------------------------------------*/
|
|
|
|
st_game_over_t::st_game_over_t()
|
|
{
|
|
name = "game_over";
|
|
}
|
|
|
|
|
|
void st_game_over_t::enter()
|
|
{
|
|
sound.ui_gameover();
|
|
manage.update();
|
|
start_time = (int)SDL_GetTicks();
|
|
}
|
|
|
|
|
|
void st_game_over_t::press(int button)
|
|
{
|
|
if(frame_time < 500)
|
|
return;
|
|
|
|
switch (button)
|
|
{
|
|
case BTN_EXIT:
|
|
case BTN_NO:
|
|
case BTN_LEFT:
|
|
case BTN_RIGHT:
|
|
case BTN_UP:
|
|
case BTN_DOWN:
|
|
case BTN_UL:
|
|
case BTN_UR:
|
|
case BTN_DL:
|
|
case BTN_DR:
|
|
case BTN_FIRE:
|
|
case BTN_START:
|
|
case BTN_SELECT:
|
|
case BTN_YES:
|
|
sound.ui_ok();
|
|
pop();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void st_game_over_t::frame()
|
|
{
|
|
manage.run_game();
|
|
|
|
frame_time = (int)SDL_GetTicks() - start_time;
|
|
if(frame_time > 5000)
|
|
pop();
|
|
}
|
|
|
|
|
|
void st_game_over_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
|
|
float ft = SDL_GetTicks() * 0.001;
|
|
wmain->font(B_BIG_FONT);
|
|
int y = PIXEL2CS(100) + (int)floor(PIXEL2CS(15)*sin(ft * 6));
|
|
wmain->center_fxp(y, "GAME OVER");
|
|
|
|
const leaderboard_submission_t *sub = leaderboard::submission();
|
|
if(sub->status != LB_IDLE)
|
|
{
|
|
char buf[64];
|
|
const char *msg = NULL;
|
|
switch(sub->status)
|
|
{
|
|
case LB_INFLIGHT:
|
|
msg = "Submitting score…";
|
|
break;
|
|
case LB_DONE:
|
|
if(sub->rank > 0 && sub->total > 0)
|
|
{
|
|
snprintf(buf, sizeof(buf), "Rank #%d of %d",
|
|
sub->rank, sub->total);
|
|
msg = buf;
|
|
}
|
|
else
|
|
msg = "Score submitted";
|
|
break;
|
|
case LB_OFFLINE:
|
|
msg = "(offline)";
|
|
break;
|
|
case LB_FAILED:
|
|
msg = "(submission failed)";
|
|
break;
|
|
default: break;
|
|
}
|
|
if(msg)
|
|
{
|
|
wmain->font(B_NORMAL_FONT);
|
|
wmain->center_fxp(PIXEL2CS(140), msg);
|
|
}
|
|
}
|
|
|
|
wradar->frame();
|
|
}
|
|
|
|
st_game_over_t st_game_over;
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Menu Base
|
|
----------------------------------------------------------*/
|
|
|
|
/*
|
|
* menu_base_t
|
|
*/
|
|
void menu_base_t::open()
|
|
{
|
|
init(gengine);
|
|
place(wmain->x(), wmain->y(), wmain->width(), wmain->height());
|
|
font(B_NORMAL_FONT);
|
|
foreground(wmain->map_rgb(0xffffff));
|
|
background(wmain->map_rgb(0x000000));
|
|
build_all();
|
|
}
|
|
|
|
void menu_base_t::close()
|
|
{
|
|
clean();
|
|
}
|
|
|
|
|
|
/*
|
|
* st_menu_base_t
|
|
*/
|
|
|
|
st_menu_base_t::st_menu_base_t()
|
|
{
|
|
name = "(menu_base derivate)";
|
|
sounds = 1;
|
|
form = NULL;
|
|
}
|
|
|
|
|
|
st_menu_base_t::~st_menu_base_t()
|
|
{
|
|
delete form;
|
|
}
|
|
|
|
|
|
void st_menu_base_t::enter()
|
|
{
|
|
form = open();
|
|
if(manage.game_stopped())
|
|
run_intro = 1;
|
|
if(sounds)
|
|
sound.ui_ok();
|
|
}
|
|
|
|
// Because we may get back here after changing the configuration!
|
|
void st_menu_base_t::reenter()
|
|
{
|
|
if(global_status & OS_RESTART_VIDEO)
|
|
pop();
|
|
}
|
|
|
|
void st_menu_base_t::leave()
|
|
{
|
|
screen.set_highlight(0, 0);
|
|
close();
|
|
delete form;
|
|
form = NULL;
|
|
if(manage.game_stopped())
|
|
{
|
|
manage.init_resources_title();
|
|
st_intro_title.inext = &st_intro_instructions;
|
|
st_intro_title.duration = INTRO_TITLE_TIME + 2000;
|
|
st_intro_title.mode = 0;
|
|
}
|
|
}
|
|
|
|
void st_menu_base_t::frame()
|
|
{
|
|
if(manage.game_stopped())
|
|
manage.run_intro();
|
|
//(Game is paused when a menu is active.)
|
|
}
|
|
|
|
void st_menu_base_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
if(form)
|
|
form->render();
|
|
if(!manage.game_stopped())
|
|
wradar->frame();
|
|
}
|
|
|
|
int st_menu_base_t::translate(int tag, int button)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTN_INC:
|
|
case BTN_RIGHT:
|
|
case BTN_DEC:
|
|
case BTN_LEFT:
|
|
return -1;
|
|
default:
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
void st_menu_base_t::press(int button)
|
|
{
|
|
int selection;
|
|
if(!form)
|
|
return;
|
|
|
|
do_default_action = 1;
|
|
|
|
// Translate
|
|
switch(button)
|
|
{
|
|
case BTN_EXIT:
|
|
case BTN_CLOSE:
|
|
selection = 0;
|
|
break;
|
|
case BTN_UP:
|
|
case BTN_DOWN:
|
|
selection = -1;
|
|
break;
|
|
case BTN_INC:
|
|
case BTN_RIGHT:
|
|
case BTN_DEC:
|
|
case BTN_LEFT:
|
|
case BTN_FIRE:
|
|
case BTN_START:
|
|
case BTN_SELECT:
|
|
if(form->selected())
|
|
selection = translate(form->selected()->tag,
|
|
button);
|
|
else
|
|
selection = -2;
|
|
break;
|
|
default:
|
|
selection = -1;
|
|
break;
|
|
}
|
|
|
|
// Default action
|
|
if(do_default_action)
|
|
switch(button)
|
|
{
|
|
case BTN_EXIT:
|
|
escape();
|
|
break;
|
|
case BTN_INC:
|
|
case BTN_RIGHT:
|
|
form->change(1);
|
|
break;
|
|
case BTN_DEC:
|
|
case BTN_LEFT:
|
|
form->change(-1);
|
|
break;
|
|
case BTN_FIRE:
|
|
case BTN_START:
|
|
case BTN_SELECT:
|
|
form->change(0);
|
|
break;
|
|
case BTN_UP:
|
|
form->prev();
|
|
break;
|
|
case BTN_DOWN:
|
|
form->next();
|
|
break;
|
|
#ifdef PROFILE_AUDIO
|
|
case BTN_F9:
|
|
gsm.push(&st_profile_audio);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
switch(selection)
|
|
{
|
|
case -1:
|
|
break;
|
|
case 0:
|
|
if(sounds)
|
|
sound.ui_cancel();
|
|
select(0);
|
|
pop();
|
|
break;
|
|
default:
|
|
select(selection);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_new_player
|
|
----------------------------------------------------------*/
|
|
|
|
void new_player_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(name, 0, sizeof(name));
|
|
name[0] = 'A';
|
|
currentIndex = 0;
|
|
editing = 1;
|
|
build_all();
|
|
if(gengine->window_handle())
|
|
SDL_StartTextInput(gengine->window_handle());
|
|
}
|
|
|
|
void new_player_t::close()
|
|
{
|
|
if(gengine->window_handle())
|
|
SDL_StopTextInput(gengine->window_handle());
|
|
clean();
|
|
}
|
|
|
|
void new_player_t::change(int delta)
|
|
{
|
|
kobo_form_t::change(delta);
|
|
|
|
if(!selected())
|
|
return;
|
|
|
|
selection = selected()->tag;
|
|
}
|
|
|
|
void new_player_t::build()
|
|
{
|
|
medium();
|
|
space(2);
|
|
label("Use arrows, joystick or keyboard");
|
|
label("to enter name");
|
|
|
|
big();
|
|
space();
|
|
button(name, 1);
|
|
space();
|
|
|
|
button("Ok", MENU_TAG_OK);
|
|
button("Cancel", MENU_TAG_CANCEL);
|
|
}
|
|
|
|
void new_player_t::rebuild()
|
|
{
|
|
int sel = selected_index();
|
|
build_all();
|
|
select(sel);
|
|
}
|
|
|
|
|
|
st_new_player_t::st_new_player_t()
|
|
{
|
|
name = "new_player";
|
|
}
|
|
|
|
void st_new_player_t::frame()
|
|
{
|
|
manage.run_intro();
|
|
}
|
|
|
|
void st_new_player_t::enter()
|
|
{
|
|
menu.open();
|
|
run_intro = 0;
|
|
sound.ui_ok();
|
|
}
|
|
|
|
void st_new_player_t::leave()
|
|
{
|
|
menu.close();
|
|
}
|
|
|
|
void st_new_player_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
menu.render();
|
|
}
|
|
|
|
void st_new_player_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;
|
|
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:
|
|
if(!menu.name[menu.currentIndex])
|
|
menu.name[menu.currentIndex] = 'A';
|
|
else if(menu.name[menu.currentIndex] == 'Z')
|
|
menu.name[menu.currentIndex] = 'a';
|
|
else if(menu.name[menu.currentIndex] == 'z')
|
|
menu.name[menu.currentIndex] = 'A';
|
|
else
|
|
menu.name[menu.currentIndex]++;
|
|
sound.ui_tick();
|
|
break;
|
|
|
|
case BTN_DEC:
|
|
case BTN_DOWN:
|
|
if(!menu.name[menu.currentIndex])
|
|
menu.name[menu.currentIndex] = 'A';
|
|
else if(menu.name[menu.currentIndex] == 'A')
|
|
menu.name[menu.currentIndex] = 'z';
|
|
else if(menu.name[menu.currentIndex] == 'a')
|
|
menu.name[menu.currentIndex] = 'Z';
|
|
else
|
|
menu.name[menu.currentIndex]--;
|
|
sound.ui_tick();
|
|
break;
|
|
|
|
case BTN_RIGHT:
|
|
if(menu.currentIndex < sizeof(menu.name) - 2)
|
|
{
|
|
menu.currentIndex++;
|
|
sound.ui_tick();
|
|
}
|
|
else
|
|
{
|
|
sound.ui_error();
|
|
break;
|
|
}
|
|
if(menu.name[menu.currentIndex] == '\0')
|
|
menu.name[menu.currentIndex] = 'A';
|
|
break;
|
|
|
|
case BTN_LEFT:
|
|
case BTN_BACK:
|
|
if(menu.currentIndex > 0)
|
|
{
|
|
menu.name[menu.currentIndex] = '\0';
|
|
menu.currentIndex--;
|
|
sound.ui_tick();
|
|
}
|
|
else
|
|
sound.ui_error();
|
|
break;
|
|
|
|
default:
|
|
if((unicode >= 'a') && (unicode <= 'z') ||
|
|
(unicode >= 'A') && (unicode <= 'Z'))
|
|
{
|
|
menu.name[menu.currentIndex] = (char)unicode;
|
|
if(menu.currentIndex < sizeof(menu.name) - 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:
|
|
switch(scorefile.addPlayer(menu.name))
|
|
{
|
|
case 0:
|
|
sound.ui_ok();
|
|
prefs->last_profile = scorefile.current_profile();
|
|
prefs->changed = 1;
|
|
pop();
|
|
break;
|
|
case -1:
|
|
sound.ui_error();
|
|
st_error.message("Cannot create Player Profile!",
|
|
"Too many profiles!");
|
|
gsm.change(&st_error);
|
|
break;
|
|
case -2:
|
|
case -3:
|
|
prefs->last_profile = scorefile.current_profile();
|
|
sound.ui_error();
|
|
st_error.message("Cannot save Player Profile!",
|
|
"Please, check your installation.");
|
|
gsm.change(&st_error);
|
|
break;
|
|
default:
|
|
sound.ui_error();
|
|
st_error.message("Cannot create Player Profile!",
|
|
"Bug or internal error.");
|
|
gsm.change(&st_error);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MENU_TAG_CANCEL:
|
|
sound.ui_cancel();
|
|
strcpy(menu.name, "A");
|
|
pop();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
st_new_player_t st_new_player;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_error
|
|
----------------------------------------------------------*/
|
|
|
|
void st_error_t::message(const char *error, const char *hint)
|
|
{
|
|
msg[0] = error;
|
|
msg[1] = hint;
|
|
}
|
|
|
|
|
|
st_error_t::st_error_t()
|
|
{
|
|
name = "error";
|
|
msg[0] = "No error!";
|
|
msg[1] = "(Why am I here...?)";
|
|
}
|
|
|
|
|
|
void st_error_t::enter()
|
|
{
|
|
sound.ui_error();
|
|
manage.update();
|
|
start_time = (int)SDL_GetTicks();
|
|
}
|
|
|
|
|
|
void st_error_t::press(int button)
|
|
{
|
|
if(frame_time < 500)
|
|
return;
|
|
|
|
switch (button)
|
|
{
|
|
case BTN_EXIT:
|
|
case BTN_NO:
|
|
case BTN_LEFT:
|
|
case BTN_RIGHT:
|
|
case BTN_UP:
|
|
case BTN_DOWN:
|
|
case BTN_UL:
|
|
case BTN_UR:
|
|
case BTN_DL:
|
|
case BTN_DR:
|
|
case BTN_FIRE:
|
|
case BTN_START:
|
|
case BTN_SELECT:
|
|
case BTN_YES:
|
|
sound.ui_ok();
|
|
pop();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void st_error_t::frame()
|
|
{
|
|
manage.run_intro();
|
|
|
|
frame_time = (int)SDL_GetTicks() - start_time;
|
|
|
|
if(frame_time % 1000 < 500)
|
|
sound.ui_error();
|
|
}
|
|
|
|
|
|
void st_error_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
|
|
wmain->font(B_MEDIUM_FONT);
|
|
wmain->center(95, msg[0]);
|
|
|
|
wmain->font(B_NORMAL_FONT);
|
|
wmain->center(123, msg[1]);
|
|
|
|
if(frame_time % 1000 < 500)
|
|
{
|
|
int w = wmain->width();
|
|
int h = wmain->height();
|
|
wmain->foreground(wmain->map_rgb(0xff0000));
|
|
wmain->rectangle(20, 80, w - 40, h - 160);
|
|
wmain->rectangle(22, 82, w - 44, h - 164);
|
|
wmain->rectangle(24, 84, w - 48, h - 168);
|
|
}
|
|
}
|
|
|
|
st_error_t st_error;
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_main_menu
|
|
----------------------------------------------------------*/
|
|
|
|
void main_menu_t::buildStartLevel(int profNum)
|
|
{
|
|
char buf[50];
|
|
int MaxStartLevel = scorefile.last_scene(profNum);
|
|
start_level = manage.scene();
|
|
if(start_level > MaxStartLevel)
|
|
start_level = MaxStartLevel;
|
|
list("Start at Stage", &start_level, 5);
|
|
for(int i = 0; i <= MaxStartLevel; ++i)
|
|
{
|
|
snprintf(buf, sizeof(buf), "%d", i + 1);
|
|
item(buf, i);
|
|
}
|
|
}
|
|
|
|
void main_menu_t::build()
|
|
{
|
|
if(manage.game_stopped())
|
|
{
|
|
prefs->last_profile = scorefile.current_profile();
|
|
if(last_level < 0)
|
|
manage.select_scene(scorefile.last_scene());
|
|
else
|
|
manage.select_scene(last_level);
|
|
}
|
|
|
|
halign = ALIGN_CENTER;
|
|
xoffs = 0.5;
|
|
big();
|
|
if(manage.game_stopped())
|
|
{
|
|
space();
|
|
if(scorefile.numProfiles > 0)
|
|
{
|
|
button("Start Game!", 1);
|
|
space();
|
|
list("Player", &prefs->last_profile, 4);
|
|
for(int i = 0; i < scorefile.numProfiles; ++i)
|
|
item(scorefile.name(i), i);
|
|
small();
|
|
buildStartLevel(prefs->last_profile);
|
|
big();
|
|
}
|
|
else
|
|
space(2);
|
|
button("New Player...", 3);
|
|
}
|
|
else
|
|
{
|
|
space(2);
|
|
button("Return to Game", 0);
|
|
}
|
|
space();
|
|
button("Options", 2);
|
|
if(prefs->global_leaderboard && prefs->leaderboard_board[0])
|
|
button("Global Leaderboard", 200);
|
|
space();
|
|
if(manage.game_stopped())
|
|
button("Return to Intro", 0);
|
|
else
|
|
button("Abort Current Game", 101);
|
|
#ifndef __EMSCRIPTEN__
|
|
// In the browser, exit() leaves the page on a dead canvas. Drop the
|
|
// option entirely; reloading the tab is the user's exit.
|
|
button("Quit Kobo Deluxe", MENU_TAG_CANCEL);
|
|
#endif
|
|
}
|
|
|
|
void main_menu_t::rebuild()
|
|
{
|
|
int sel = selected_index();
|
|
build_all();
|
|
select(sel);
|
|
}
|
|
|
|
|
|
kobo_form_t *st_main_menu_t::open()
|
|
{
|
|
if(manage.game_stopped())
|
|
{
|
|
// Moved here, as we want to do it as late as
|
|
// possible, but *not* as a result of rebuild().
|
|
if(prefs->last_profile >= scorefile.numProfiles)
|
|
{
|
|
prefs->last_profile = 0;
|
|
prefs->changed = 1;
|
|
}
|
|
scorefile.select_profile(prefs->last_profile);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
void st_main_menu_t::reenter()
|
|
{
|
|
menu->rebuild();
|
|
st_menu_base_t::reenter();
|
|
}
|
|
|
|
|
|
// Custom translator to map inc/dec on certain widgets
|
|
int st_main_menu_t::translate(int tag, int button)
|
|
{
|
|
switch(tag)
|
|
{
|
|
case 4:
|
|
// The default translate() filters out the
|
|
// inc/dec events, and performs the default
|
|
// action for fire/start/select...
|
|
switch(button)
|
|
{
|
|
case BTN_FIRE:
|
|
case BTN_START:
|
|
case BTN_SELECT:
|
|
do_default_action = 0;
|
|
return tag + 10;
|
|
default:
|
|
return tag;
|
|
}
|
|
case 5:
|
|
return tag;
|
|
default:
|
|
return st_menu_base_t::translate(tag, button);
|
|
}
|
|
}
|
|
|
|
void st_main_menu_t::select(int tag)
|
|
{
|
|
switch(tag)
|
|
{
|
|
case 1:
|
|
gsm.change(&st_skill_menu);
|
|
break;
|
|
case 2:
|
|
gsm.push(&st_options_main);
|
|
break;
|
|
case 3:
|
|
gsm.push(&st_new_player);
|
|
break;
|
|
case 200:
|
|
gsm.push(&st_global_leaderboard);
|
|
break;
|
|
case 4: // Player: Inc/Dec
|
|
sound.ui_tick();
|
|
prefs->changed = 1;
|
|
scorefile.select_profile(prefs->last_profile);
|
|
menu->rebuild();
|
|
break;
|
|
case 14: // Player: Select
|
|
// Edit player profile!
|
|
// menu->rebuild();
|
|
break;
|
|
case 5: // Start level: Inc/Dec
|
|
sound.ui_tick();
|
|
manage.select_scene(menu->start_level);
|
|
break;
|
|
case MENU_TAG_CANCEL:
|
|
gsm.change(&st_ask_exit);
|
|
break;
|
|
case 101:
|
|
gsm.change(&st_ask_abort_game);
|
|
break;
|
|
case 0:
|
|
if(!manage.game_stopped())
|
|
gsm.change(&st_pause_game);
|
|
break;
|
|
}
|
|
}
|
|
|
|
st_main_menu_t st_main_menu;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_skill_menu
|
|
----------------------------------------------------------*/
|
|
|
|
void skill_menu_t::build()
|
|
{
|
|
halign = ALIGN_CENTER;
|
|
xoffs = 0.5;
|
|
medium();
|
|
label("Select Skill Level");
|
|
space(1);
|
|
|
|
big();
|
|
button("Classic", SKILL_CLASSIC + 10);
|
|
small();
|
|
space();
|
|
big();
|
|
button("Newbie", SKILL_NEWBIE + 10);
|
|
button("Gamer", SKILL_GAMER + 10);
|
|
button("Elite", SKILL_ELITE + 10);
|
|
button("God", SKILL_GOD + 10);
|
|
|
|
space();
|
|
small();
|
|
switch(skill)
|
|
{
|
|
case SKILL_CLASSIC:
|
|
label("\"I want the original XKobo, dammit!\"");
|
|
break;
|
|
case SKILL_NEWBIE:
|
|
label("\"Damn, this is hard...!\"");
|
|
break;
|
|
case SKILL_GAMER:
|
|
label("\"Classic is too retro for me!\"");
|
|
break;
|
|
case SKILL_ELITE:
|
|
label("\"Bah! Gimme some resistance here.\"");
|
|
break;
|
|
case SKILL_GOD:
|
|
label("\"The dark is afraid of me.\"");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void skill_menu_t::rebuild()
|
|
{
|
|
int sel = selected_index();
|
|
build_all();
|
|
select(sel);
|
|
}
|
|
|
|
|
|
kobo_form_t *st_skill_menu_t::open()
|
|
{
|
|
menu = new skill_menu_t;
|
|
menu->set_skill(scorefile.profile()->skill);
|
|
menu->open();
|
|
switch(scorefile.profile()->skill)
|
|
{
|
|
case SKILL_CLASSIC:
|
|
menu->select(1);
|
|
break;
|
|
case SKILL_NEWBIE:
|
|
menu->select(2);
|
|
break;
|
|
case SKILL_GAMER:
|
|
menu->select(3);
|
|
break;
|
|
case SKILL_ELITE:
|
|
menu->select(4);
|
|
break;
|
|
case SKILL_GOD:
|
|
menu->select(5);
|
|
break;
|
|
}
|
|
return menu;
|
|
}
|
|
|
|
|
|
void st_skill_menu_t::press(int button)
|
|
{
|
|
st_menu_base_t::press(button);
|
|
switch (button)
|
|
{
|
|
case BTN_UP:
|
|
case BTN_DOWN:
|
|
menu->set_skill(menu->selected()->tag - 10);
|
|
menu->rebuild();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void st_skill_menu_t::select(int tag)
|
|
{
|
|
if((tag >= 10) && (tag <= 20))
|
|
{
|
|
scorefile.profile()->skill = menu->selected()->tag - 10;
|
|
gsm.change(&st_game);
|
|
}
|
|
}
|
|
|
|
st_skill_menu_t st_skill_menu;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_options_main
|
|
----------------------------------------------------------*/
|
|
|
|
void options_main_t::build()
|
|
{
|
|
medium();
|
|
label("Options");
|
|
space();
|
|
|
|
big();
|
|
button("Game", 4);
|
|
button("Controls", 3);
|
|
button("Video", 1);
|
|
button("Graphics", 6);
|
|
button("Audio", 2);
|
|
button("System", 5);
|
|
if(prefs->global_leaderboard)
|
|
button("Leaderboard Board Name", 7);
|
|
space();
|
|
|
|
button("DONE!", 0);
|
|
}
|
|
|
|
kobo_form_t *st_options_main_t::open()
|
|
{
|
|
options_main_t *m = new options_main_t;
|
|
m->open();
|
|
return m;
|
|
}
|
|
|
|
void st_options_main_t::select(int tag)
|
|
{
|
|
switch(tag)
|
|
{
|
|
case 1:
|
|
gsm.push(&st_options_video);
|
|
break;
|
|
case 2:
|
|
gsm.push(&st_options_audio);
|
|
break;
|
|
case 3:
|
|
gsm.push(&st_options_control);
|
|
break;
|
|
case 4:
|
|
gsm.push(&st_options_game);
|
|
break;
|
|
case 5:
|
|
gsm.push(&st_options_system);
|
|
break;
|
|
case 6:
|
|
gsm.push(&st_options_graphics);
|
|
break;
|
|
case 7:
|
|
gsm.push(&st_edit_board);
|
|
break;
|
|
}
|
|
}
|
|
|
|
st_options_main_t st_options_main;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_options_base
|
|
----------------------------------------------------------*/
|
|
|
|
kobo_form_t *st_options_base_t::open()
|
|
{
|
|
sounds = 0;
|
|
cfg_form = oopen();
|
|
cfg_form->open(prefs);
|
|
return cfg_form;
|
|
}
|
|
|
|
void st_options_base_t::close()
|
|
{
|
|
cfg_form->close();
|
|
}
|
|
|
|
void st_options_base_t::enter()
|
|
{
|
|
sound.ui_ok();
|
|
st_menu_base_t::enter();
|
|
}
|
|
|
|
void st_options_base_t::select(int tag)
|
|
{
|
|
if(cfg_form->status() & OS_CANCEL)
|
|
cfg_form->undo();
|
|
else if(cfg_form->status() & OS_CLOSE)
|
|
{
|
|
if(cfg_form->status() & (OS_RESTART | OS_RELOAD))
|
|
{
|
|
exit_game = 0;
|
|
manage.freeze_abort();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle changes that require only an update...
|
|
*/
|
|
if(cfg_form->status() & OS_UPDATE_AUDIO)
|
|
sound.prefschange();
|
|
if(cfg_form->status() & OS_UPDATE_ENGINE)
|
|
{
|
|
gengine->timefilter(prefs->timefilter * 0.01f);
|
|
gengine->interpolation(prefs->filter);
|
|
gengine->vsync(prefs->vsync);
|
|
}
|
|
cfg_form->clearstatus(OS_UPDATE);
|
|
|
|
if(cfg_form->status() & (OS_CANCEL | OS_CLOSE))
|
|
pop();
|
|
}
|
|
|
|
void st_options_base_t::press(int button)
|
|
{
|
|
st_menu_base_t::press(button);
|
|
gengine->timefilter(prefs->timefilter * 0.01f);
|
|
gengine->interpolation(prefs->filter);
|
|
}
|
|
|
|
void st_options_base_t::escape()
|
|
{
|
|
sound.ui_cancel();
|
|
cfg_form->undo();
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Options...
|
|
----------------------------------------------------------*/
|
|
st_options_system_t st_options_system;
|
|
st_options_video_t st_options_video;
|
|
st_options_graphics_t st_options_graphics;
|
|
st_options_audio_t st_options_audio;
|
|
st_options_control_t st_options_control;
|
|
st_options_game_t st_options_game;
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Requesters
|
|
----------------------------------------------------------*/
|
|
void yesno_menu_t::build()
|
|
{
|
|
halign = ALIGN_CENTER;
|
|
xoffs = 0.5;
|
|
big();
|
|
space(8);
|
|
button("YES", MENU_TAG_OK);
|
|
button("NO", MENU_TAG_CANCEL);
|
|
}
|
|
|
|
void yesno_menu_t::rebuild()
|
|
{
|
|
int sel = selected_index();
|
|
build_all();
|
|
select(sel);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_yesno_base_t
|
|
----------------------------------------------------------*/
|
|
|
|
kobo_form_t *st_yesno_base_t::open()
|
|
{
|
|
menu = new yesno_menu_t;
|
|
menu->open();
|
|
menu->select(1);
|
|
return menu;
|
|
}
|
|
|
|
void st_yesno_base_t::reenter()
|
|
{
|
|
menu->rebuild();
|
|
menu->select(1);
|
|
st_menu_base_t::reenter();
|
|
}
|
|
|
|
void st_yesno_base_t::press(int button)
|
|
{
|
|
switch (button)
|
|
{
|
|
case BTN_NO:
|
|
select(MENU_TAG_CANCEL);
|
|
break;
|
|
case BTN_YES:
|
|
select(MENU_TAG_OK);
|
|
break;
|
|
default:
|
|
st_menu_base_t::press(button);
|
|
}
|
|
}
|
|
|
|
void st_yesno_base_t::frame()
|
|
{
|
|
if(manage.game_stopped())
|
|
manage.run_intro();
|
|
else
|
|
manage.run_pause();
|
|
}
|
|
|
|
void st_yesno_base_t::post_render()
|
|
{
|
|
st_menu_base_t::post_render();
|
|
|
|
float ft = SDL_GetTicks() * 0.001;
|
|
wmain->font(B_BIG_FONT);
|
|
int y = PIXEL2CS(100) + (int)floor(PIXEL2CS(15)*sin(ft * 6));
|
|
wmain->center_fxp(y, msg);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_ask_exit
|
|
----------------------------------------------------------*/
|
|
|
|
st_ask_exit_t::st_ask_exit_t()
|
|
{
|
|
name = "ask_exit";
|
|
msg = "Quit Kobo Deluxe?";
|
|
}
|
|
|
|
void st_ask_exit_t::select(int tag)
|
|
{
|
|
switch(tag)
|
|
{
|
|
case MENU_TAG_OK:
|
|
audio_channel_stop(0, -1); //Stop any music
|
|
sound.ui_ok();
|
|
exit_game = 1;
|
|
pop();
|
|
break;
|
|
case MENU_TAG_CANCEL:
|
|
sound.ui_cancel();
|
|
if(manage.game_stopped())
|
|
pop();
|
|
else
|
|
gsm.change(&st_pause_game);
|
|
break;
|
|
}
|
|
}
|
|
|
|
st_ask_exit_t st_ask_exit;
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
st_ask_abort_game
|
|
----------------------------------------------------------*/
|
|
|
|
st_ask_abort_game_t::st_ask_abort_game_t()
|
|
{
|
|
name = "ask_abort_game";
|
|
msg = "Abort Game?";
|
|
}
|
|
|
|
void st_ask_abort_game_t::select(int tag)
|
|
{
|
|
switch(tag)
|
|
{
|
|
case MENU_TAG_OK:
|
|
sound.ui_ok();
|
|
manage.abort();
|
|
pop();
|
|
break;
|
|
case MENU_TAG_CANCEL:
|
|
gsm.change(&st_pause_game);
|
|
break;
|
|
}
|
|
}
|
|
|
|
st_ask_abort_game_t st_ask_abort_game;
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Debug: Audio Engine Profiling
|
|
----------------------------------------------------------*/
|
|
#ifdef PROFILE_AUDIO
|
|
|
|
#include "sound.h"
|
|
#include "a_midicon.h"
|
|
|
|
st_profile_audio_t::st_profile_audio_t()
|
|
{
|
|
name = "profile_audio";
|
|
pan = 0;
|
|
pitch = 60;
|
|
shift = 0;
|
|
}
|
|
|
|
|
|
void st_profile_audio_t::enter()
|
|
{
|
|
audio_group_control(SOUND_GROUP_SFX, ACC_PAN, pan);
|
|
audio_group_control(SOUND_GROUP_SFX, ACC_PITCH, pitch << 16);
|
|
screen.set_highlight(0, 0);
|
|
screen.noise(0);
|
|
sound.sfx_volume(1.0f);
|
|
}
|
|
|
|
|
|
void st_profile_audio_t::press(int button)
|
|
{
|
|
switch (button)
|
|
{
|
|
case BTN_EXIT:
|
|
pop();
|
|
break;
|
|
case BTN_LEFT:
|
|
pan -= 8192;
|
|
if(pan < -65536)
|
|
pan = -65536;
|
|
audio_group_control(SOUND_GROUP_SFX, ACC_PAN, pan);
|
|
break;
|
|
case BTN_RIGHT:
|
|
pan += 8192;
|
|
if(pan > 65536)
|
|
pan = 65536;
|
|
audio_group_control(SOUND_GROUP_SFX, ACC_PAN, pan);
|
|
break;
|
|
case BTN_UP:
|
|
++pitch;
|
|
if(pitch > 127)
|
|
pitch = 127;
|
|
audio_group_control(SOUND_GROUP_SFX, ACC_PITCH, pitch << 16);
|
|
break;
|
|
case BTN_DOWN:
|
|
--pitch;
|
|
if(pitch < 0)
|
|
pitch = 0;
|
|
audio_group_control(SOUND_GROUP_SFX, ACC_PITCH, pitch << 16);
|
|
break;
|
|
case BTN_NO:
|
|
case BTN_UL:
|
|
case BTN_UR:
|
|
case BTN_DL:
|
|
case BTN_DR:
|
|
break;
|
|
case BTN_INC:
|
|
shift += 8;
|
|
if(shift > AUDIO_MAX_WAVES-8)
|
|
shift = 0;
|
|
break;
|
|
case BTN_DEC:
|
|
shift -= 8;
|
|
if(shift < 0)
|
|
shift = AUDIO_MAX_WAVES-8;
|
|
break;
|
|
case BTN_FIRE:
|
|
{
|
|
audio_channel_stop(-1, -1);
|
|
int startt = SDL_GetTicks();
|
|
audio_wave_load(0, "sfx.agw", 0);
|
|
log_printf(VLOG, "(Loading + processing time: %d ms)\n",
|
|
SDL_GetTicks() - startt);
|
|
break;
|
|
}
|
|
case BTN_START:
|
|
case BTN_SELECT:
|
|
audio_channel_stop(-1, -1);
|
|
break;
|
|
case BTN_YES:
|
|
break;
|
|
case BTN_F1:
|
|
sound.g_play0(0 + shift);
|
|
midicon_midisock.program_change(0, 0 + shift);
|
|
break;
|
|
case BTN_F2:
|
|
sound.g_play0(1 + shift);
|
|
midicon_midisock.program_change(0, 1 + shift);
|
|
break;
|
|
case BTN_F3:
|
|
sound.g_play0(2 + shift);
|
|
midicon_midisock.program_change(0, 2 + shift);
|
|
break;
|
|
case BTN_F4:
|
|
sound.g_play0(3 + shift);
|
|
midicon_midisock.program_change(0, 3 + shift);
|
|
break;
|
|
case BTN_F5:
|
|
sound.g_play0(4 + shift);
|
|
midicon_midisock.program_change(0, 4 + shift);
|
|
break;
|
|
case BTN_F6:
|
|
sound.g_play0(5 + shift);
|
|
midicon_midisock.program_change(0, 5 + shift);
|
|
break;
|
|
case BTN_F7:
|
|
sound.g_play0(6 + shift);
|
|
midicon_midisock.program_change(0, 6 + shift);
|
|
break;
|
|
case BTN_F8:
|
|
sound.g_play0(7 + shift);
|
|
midicon_midisock.program_change(0, 7 + shift);
|
|
break;
|
|
case BTN_F11:
|
|
switch(audio_cpu_ticks)
|
|
{
|
|
case 50:
|
|
audio_cpu_ticks = 100;
|
|
break;
|
|
case 100:
|
|
audio_cpu_ticks = 250;
|
|
break;
|
|
case 250:
|
|
audio_cpu_ticks = 500;
|
|
break;
|
|
case 500:
|
|
audio_cpu_ticks = 1000;
|
|
break;
|
|
default:
|
|
audio_cpu_ticks = 50;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void st_profile_audio_t::pre_render()
|
|
{
|
|
/*
|
|
* Heeelp! I just *can't* stay away from chances
|
|
* like this to play around... :-D
|
|
*/
|
|
static int dither = 0;
|
|
int y = 0;
|
|
int y2 = wmain->height();
|
|
float t = SDL_GetTicks()/1000.0;
|
|
while(y < y2)
|
|
{
|
|
float c1 = sin(y*0.11 + t*1.5)*30.0 + 30;
|
|
float c2 = sin(y*0.07 + t*2.5)*25.0 + 25;
|
|
float c3 = sin(y*0.03 - t)*40.0 + 40;
|
|
//Wideband color dither - improves 15/16 bit modes.
|
|
float c4 = (dither + y) & 1 ? 3.0 : 0.0;
|
|
int r = (int)(c1 + c2 + c4);
|
|
int g = (int)(c2 + 3.0 - c4);
|
|
int b = (int)(c1 + c2 + c3 + c4);
|
|
wmain->foreground(wmain->map_rgb(r, g, b));
|
|
wmain->fillrect(0, y, wmain->width(), 1);
|
|
++y;
|
|
}
|
|
dither = 1 - dither;
|
|
}
|
|
|
|
void st_profile_audio_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
|
|
wmain->font(B_BIG_FONT);
|
|
wmain->center(20, "Audio CPU Load");
|
|
|
|
wmain->font(B_NORMAL_FONT);
|
|
Uint32 fgc = wmain->map_rgb(0xffcc00);
|
|
Uint32 bgc = wmain->map_rgb(0x006600);
|
|
char buf[40];
|
|
for(int i = 0; i < AUDIO_CPU_FUNCTIONS; ++i)
|
|
{
|
|
int perc = (int)(audio_cpu_function[i] / audio_cpu_total * 100.0);
|
|
wmain->foreground(fgc);
|
|
wmain->fillrect(103, 50+i*12+9, (int)audio_cpu_function[i]/2, 2);
|
|
wmain->fillrect(128+32, 50+i*12+9, (int)perc/2, 2);
|
|
wmain->foreground(bgc);
|
|
wmain->fillrect(103 + (int)audio_cpu_function[i]/2, 50+i*12+9,
|
|
50 - (int)audio_cpu_function[i]/2, 2);
|
|
wmain->fillrect(128+32 + (int)perc/2, 50+i*12+9, 50 - (int)perc/2, 2);
|
|
snprintf(buf, sizeof(buf), "%s:%5.2f%% (%1.0f%%)",
|
|
audio_cpu_funcname[i],
|
|
audio_cpu_function[i],
|
|
audio_cpu_function[i] / audio_cpu_total * 100.0);
|
|
wmain->center_token(120, 50+i*12, buf, ':');
|
|
}
|
|
|
|
wmain->foreground(fgc);
|
|
wmain->fillrect(80, 178, (int)audio_cpu_total, 4);
|
|
wmain->foreground(bgc);
|
|
wmain->fillrect(80 + (int)audio_cpu_total, 178, 100 - (int)audio_cpu_total, 4);
|
|
wmain->font(B_BIG_FONT);
|
|
snprintf(buf, sizeof(buf), "Total:%5.2f%%", audio_cpu_total);
|
|
wmain->center_token(120, 180, buf, ':');
|
|
|
|
wmain->font(B_NORMAL_FONT);
|
|
snprintf(buf, sizeof(buf), "Pan [L/R]:%5.2f "
|
|
"Pitch [U/D]: %d ",
|
|
(float)pan/65536.0,
|
|
pitch);
|
|
wmain->center(200, buf);
|
|
snprintf(buf, sizeof(buf), "F1..F8 [+/-]: %d..%d",
|
|
shift, shift + 7);
|
|
wmain->center(215, buf);
|
|
}
|
|
|
|
st_profile_audio_t st_profile_audio;
|
|
|
|
#endif /*PROFILE_AUDIO*/
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Global Leaderboard browser
|
|
----------------------------------------------------------*/
|
|
|
|
st_global_leaderboard_t::st_global_leaderboard_t()
|
|
{
|
|
name = "global_leaderboard";
|
|
}
|
|
|
|
void st_global_leaderboard_t::enter()
|
|
{
|
|
start_time = (int)SDL_GetTicks();
|
|
leaderboard::fetch_board();
|
|
}
|
|
|
|
void st_global_leaderboard_t::press(int button)
|
|
{
|
|
switch(button)
|
|
{
|
|
case BTN_EXIT:
|
|
case BTN_NO:
|
|
case BTN_FIRE:
|
|
case BTN_START:
|
|
case BTN_SELECT:
|
|
case BTN_YES:
|
|
sound.ui_ok();
|
|
pop();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void st_global_leaderboard_t::frame()
|
|
{
|
|
manage.run_intro();
|
|
}
|
|
|
|
void st_global_leaderboard_t::post_render()
|
|
{
|
|
kobo_basestate_t::post_render();
|
|
|
|
const leaderboard_browse_t *b = leaderboard::browse();
|
|
|
|
wmain->font(B_BIG_FONT);
|
|
wmain->center(40, "GLOBAL LEADERBOARD");
|
|
|
|
char buf[96];
|
|
wmain->font(B_NORMAL_FONT);
|
|
if(b->board[0])
|
|
{
|
|
snprintf(buf, sizeof(buf), "Board: %s", b->board);
|
|
wmain->center(70, buf);
|
|
}
|
|
|
|
switch(b->status)
|
|
{
|
|
case LB_INFLIGHT:
|
|
wmain->center(120, "Loading…");
|
|
break;
|
|
case LB_OFFLINE:
|
|
wmain->center(120, "(offline)");
|
|
break;
|
|
case LB_FAILED:
|
|
wmain->center(120, "(server error)");
|
|
break;
|
|
case LB_IDLE:
|
|
wmain->center(120, "Leaderboard disabled.");
|
|
wmain->center(140, "Set leaderboard_board in config.");
|
|
break;
|
|
case LB_DONE:
|
|
{
|
|
if(b->count == 0)
|
|
{
|
|
wmain->center(120, "No scores yet.");
|
|
break;
|
|
}
|
|
// Two-column layout: rank + name on the left, score + stage
|
|
// on the right. 18px line height matches the existing intro
|
|
// high-score screen feel.
|
|
int y = 100;
|
|
int rows = b->count > 16 ? 16 : b->count;
|
|
for(int i = 0; i < rows; ++i)
|
|
{
|
|
const leaderboard_entry_t &e = b->entries[i];
|
|
snprintf(buf, sizeof(buf), "%2d. %-10s",
|
|
i + 1, e.name);
|
|
wmain->string(40, y, buf);
|
|
snprintf(buf, sizeof(buf), "%9u st.%2d",
|
|
e.score, e.end_scene + 1);
|
|
wmain->string(160, y, buf);
|
|
y += 12;
|
|
}
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
wmain->center(WSIZE - 30, "(Press any key to return)");
|
|
}
|
|
|
|
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;
|
|
|