Add a main menu scene with Start/Hard Mode/Music options
The game now boots into a menu (Start Game, Hard Mode toggle, Music toggle) navigated with Up/Down and A/Start, instead of dropping straight into a match. B returns to the menu from a paused or game-over state.
This commit is contained in:
1 parent
0e7477031f
commit
8bf999c33b
5 files changed
+85
-7
No files matched your search
@@ -20,6 +20,7 @@ struct RenderAssets {
|
|||||||
|
|
||||||
void draw_game(const GameState &state, const RenderAssets &assets);
|
void draw_game(const GameState &state, const RenderAssets &assets);
|
||||||
void draw_stats(const GameState &state, bool internals, bool musicEnabled, uint8_t frame);
|
void draw_stats(const GameState &state, bool internals, bool musicEnabled, uint8_t frame);
|
||||||
|
void draw_menu(const GameState &state, const RenderAssets &assets, bool musicEnabled);
|
||||||
void blink_apples(const GameState &state, u16 *gfx_apple);
|
void blink_apples(const GameState &state, u16 *gfx_apple);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -6,6 +6,13 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <nds/ndstypes.h>
|
#include <nds/ndstypes.h>
|
||||||
|
|
||||||
|
enum class Scene {
|
||||||
|
Menu,
|
||||||
|
Playing,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int MENU_ITEM_COUNT = 3;
|
||||||
|
|
||||||
struct GameState {
|
struct GameState {
|
||||||
volatile uint16_t ticks = 0;
|
volatile uint16_t ticks = 0;
|
||||||
volatile uint16_t snakelength = 0;
|
volatile uint16_t snakelength = 0;
|
||||||
@@ -16,6 +23,9 @@ struct GameState {
|
|||||||
bool paused = false;
|
bool paused = false;
|
||||||
bool hardMode = false;
|
bool hardMode = false;
|
||||||
|
|
||||||
|
Scene scene = Scene::Menu;
|
||||||
|
int menuSelection = 0;
|
||||||
|
|
||||||
uint16_t curseTicksRemaining = 0;
|
uint16_t curseTicksRemaining = 0;
|
||||||
|
|
||||||
std::deque<std::array<int, 3>> snake;
|
std::deque<std::array<int, 3>> snake;
|
||||||
|
|||||||
@@ -29,6 +29,43 @@ bool handle_input_frame(InputContext &ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.state->scene == Scene::Menu) {
|
||||||
|
if ((keys != 0U) && ((keys & (KEY_UP | KEY_DOWN)) != 0U)) {
|
||||||
|
const int delta = ((keys & KEY_UP) != 0U) ? -1 : 1;
|
||||||
|
ctx.state->menuSelection = (ctx.state->menuSelection + delta + MENU_ITEM_COUNT) % MENU_ITEM_COUNT;
|
||||||
|
draw_menu(*ctx.state, *ctx.renderAssets, audio_is_music_enabled(*ctx.audio));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((keys != 0U) && (((keys & KEY_A) != 0U) || ((keys & KEY_START) != 0U))) {
|
||||||
|
switch (ctx.state->menuSelection) {
|
||||||
|
case 0:
|
||||||
|
ctx.state->scene = Scene::Playing;
|
||||||
|
ctx.init_game();
|
||||||
|
draw_game(*ctx.state, *ctx.renderAssets);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ctx.state->hardMode = !ctx.state->hardMode;
|
||||||
|
draw_menu(*ctx.state, *ctx.renderAssets, audio_is_music_enabled(*ctx.audio));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
audio_toggle_music(*ctx.audio);
|
||||||
|
draw_menu(*ctx.state, *ctx.renderAssets, audio_is_music_enabled(*ctx.audio));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ctx.state->paused || ctx.state->gameOver) && (keys != 0U) && ((keys & KEY_B) != 0U)) {
|
||||||
|
ctx.state->scene = Scene::Menu;
|
||||||
|
ctx.state->paused = false;
|
||||||
|
draw_menu(*ctx.state, *ctx.renderAssets, audio_is_music_enabled(*ctx.audio));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ctx.state->paused && !ctx.state->gameOver) {
|
if (!ctx.state->paused && !ctx.state->gameOver) {
|
||||||
const bool cursed = ctx.state->curseTicksRemaining > 0;
|
const bool cursed = ctx.state->curseTicksRemaining > 0;
|
||||||
|
|
||||||
|
|||||||
@@ -143,9 +143,14 @@ void draw_stats(const GameState &state, bool internals, bool musicEnabled, uint8
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!state.paused) {
|
if (!state.paused) {
|
||||||
|
if (state.gameOver) {
|
||||||
|
iprintf("\x1b[23;0HB = Main Menu");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iprintf("\x1b[21;0HB = Main Menu");
|
||||||
|
|
||||||
if (musicEnabled) {
|
if (musicEnabled) {
|
||||||
iprintf("\x1b[23;0HX = Disable Music");
|
iprintf("\x1b[23;0HX = Disable Music");
|
||||||
} else {
|
} else {
|
||||||
@@ -159,6 +164,30 @@ void draw_stats(const GameState &state, bool internals, bool musicEnabled, uint8
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw_menu(const GameState &state, const RenderAssets &assets, bool musicEnabled) {
|
||||||
|
oamClear(&oamSub, 0, 0);
|
||||||
|
oamUpdate(&oamSub);
|
||||||
|
|
||||||
|
consoleSelect(assets.bottomScreen);
|
||||||
|
consoleClear();
|
||||||
|
|
||||||
|
consoleSelect(assets.topScreen);
|
||||||
|
consoleClear();
|
||||||
|
|
||||||
|
iprintf("\x1b[3;10HSNAKE PLUS");
|
||||||
|
iprintf("\x1b[5;5H---------------------");
|
||||||
|
|
||||||
|
const char *hardModeLabel = state.hardMode ? "ON " : "OFF";
|
||||||
|
const char *musicLabel = musicEnabled ? "ON " : "OFF";
|
||||||
|
|
||||||
|
iprintf("\x1b[8;7H%c Start Game", state.menuSelection == 0 ? '>' : ' ');
|
||||||
|
iprintf("\x1b[10;7H%c Hard Mode: %s", state.menuSelection == 1 ? '>' : ' ', hardModeLabel);
|
||||||
|
iprintf("\x1b[12;7H%c Music: %s", state.menuSelection == 2 ? '>' : ' ', musicLabel);
|
||||||
|
|
||||||
|
iprintf("\x1b[19;3HUP/DOWN: Navigate");
|
||||||
|
iprintf("\x1b[20;3HA/START: Select");
|
||||||
|
}
|
||||||
|
|
||||||
void blink_apples(const GameState &state, u16 *gfx_apple) {
|
void blink_apples(const GameState &state, u16 *gfx_apple) {
|
||||||
if (state.ticks % 4) {
|
if (state.ticks % 4) {
|
||||||
dmaCopy(get_sprite_pointer(5), gfx_apple, 64);
|
dmaCopy(get_sprite_pointer(5), gfx_apple, 64);
|
||||||
|
|||||||
+8
-7
@@ -57,6 +57,7 @@ void setup() {
|
|||||||
videoSetMode(MODE_0_2D);
|
videoSetMode(MODE_0_2D);
|
||||||
videoSetModeSub(MODE_0_2D);
|
videoSetModeSub(MODE_0_2D);
|
||||||
|
|
||||||
|
vramSetBankA(VRAM_A_MAIN_BG);
|
||||||
vramSetBankC(VRAM_C_SUB_BG);
|
vramSetBankC(VRAM_C_SUB_BG);
|
||||||
vramSetBankD(VRAM_D_SUB_SPRITE);
|
vramSetBankD(VRAM_D_SUB_SPRITE);
|
||||||
consoleInit(&topScreen, 3,BgType_Text4bpp, BgSize_T_256x256, X_MAX, 0, true, true);
|
consoleInit(&topScreen, 3,BgType_Text4bpp, BgSize_T_256x256, X_MAX, 0, true, true);
|
||||||
@@ -115,14 +116,16 @@ void setup() {
|
|||||||
}
|
}
|
||||||
void handle_vblank() {
|
void handle_vblank() {
|
||||||
frame++;
|
frame++;
|
||||||
consoleSelect(&topScreen);
|
if (gameState.scene == Scene::Playing) {
|
||||||
draw_stats(gameState, internals, audio_is_music_enabled(audioContext), frame);
|
consoleSelect(&topScreen);
|
||||||
|
draw_stats(gameState, internals, audio_is_music_enabled(audioContext), frame);
|
||||||
|
}
|
||||||
oamUpdate(&oamSub);
|
oamUpdate(&oamSub);
|
||||||
//flashy_colors();
|
//flashy_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_tick() {
|
void do_tick() {
|
||||||
if (!paused) {
|
if (gameState.scene == Scene::Playing && !paused) {
|
||||||
ticks++;
|
ticks++;
|
||||||
if (!gameOver){
|
if (!gameOver){
|
||||||
const bool wasGameOver = gameOver;
|
const bool wasGameOver = gameOver;
|
||||||
@@ -211,9 +214,7 @@ int main(void) {
|
|||||||
irqSet(IRQ_VBLANK, handle_vblank);
|
irqSet(IRQ_VBLANK, handle_vblank);
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
init_game();
|
draw_menu(gameState, renderAssets, audio_is_music_enabled(audioContext));
|
||||||
|
|
||||||
// Start timer for game ticks
|
|
||||||
|
|
||||||
InputContext inputContext;
|
InputContext inputContext;
|
||||||
inputContext.state = &gameState;
|
inputContext.state = &gameState;
|
||||||
@@ -223,7 +224,7 @@ int main(void) {
|
|||||||
inputContext.topScreen = &topScreen;
|
inputContext.topScreen = &topScreen;
|
||||||
inputContext.init_game = init_game;
|
inputContext.init_game = init_game;
|
||||||
|
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
if (!handle_input_frame(inputContext)) {
|
if (!handle_input_frame(inputContext)) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in new issue
Block a user