#include #include #include #include #include #include #include #include #include #include "soundbank.h" #include "soundbank_bin.h" #include "tilemap.h" volatile uint frame = 0; volatile uint ticks = 0; volatile bool internals = false; std::deque> snake; std::vector> apples; std::vector> bad_apples; volatile uint snakelength; /*** * 0 = up * 1 = right * 2 = down * 3 = left ***/ volatile uint snakeDirection; volatile uint score; bool gameOver = false; bool paused = false; bool musicEnabled = true; bool hardMode = false; // 31x23 PrintConsole topScreen; PrintConsole bottomScreen; int sound; mm_sound_effect sfx_pickup; mm_sound_effect sfx_boom; mm_sound_effect sfx_pause; mm_sound_effect sfx_unpause; mm_sound_effect sfx_plop; mm_sound_effect sfx_fail; //Sprites u16* gfx_segment; u16* gfx_segment_small; u16* gfx_head; u16* gfx_apple; u16* gfx_tail; u16* gfx_apple_rotten; const void * get_sprite_pointer(uint spriteIndex) { return &tilemapTiles[spriteIndex*16]; } void setup() { // Set up screens videoSetMode(MODE_0_2D); videoSetModeSub(MODE_0_2D); vramSetBankC(VRAM_C_SUB_BG); vramSetBankD(VRAM_D_SUB_SPRITE); consoleInit(&topScreen, 3,BgType_Text4bpp, BgSize_T_256x256, X_MAX, 0, true, true); consoleInit(&bottomScreen, 3,BgType_Text4bpp, BgSize_T_256x256, X_MAX, 0, false, true); oamInit(&oamSub, SpriteMapping_1D_32, false); gfx_segment = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color); gfx_segment_small = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color); gfx_head = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color); gfx_apple = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color); gfx_apple_rotten = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color); gfx_tail = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color); dmaCopy(get_sprite_pointer(1),gfx_segment,64); dmaCopy(get_sprite_pointer(2),gfx_segment_small,64); dmaCopy(get_sprite_pointer(3),gfx_head,64); dmaCopy(get_sprite_pointer(5),gfx_apple,64); dmaCopy(get_sprite_pointer(11),gfx_apple_rotten,64); consoleSelect(&topScreen); dmaCopy(tilemapPal,SPRITE_PALETTE_SUB,tilemapPalLen); mmInitDefaultMem((mm_addr)soundbank_bin); // Background Music mmLoad( MOD_0RIGIN ); // Load SFX mmLoadEffect( SFX_332629__TREASURESOUNDS__ITEM_PICKUP ); mmLoadEffect( SFX_BOOM ); mmLoadEffect( SFX_167127__CRISSTANZA__PAUSE ); mmLoadEffect( SFX_167126__CRISSTANZA__UNPAUSE ); mmLoadEffect( SFX_273792__PACOMAV__PLOP ); mmLoadEffect( SFX_73750__TIMBRE__REMIX_OF_BENBONCAN_SAD_TROMBONE_MORE_WAH_BRIGHT_DE_CLICKED ); sfx_pickup = { { SFX_332629__TREASURESOUNDS__ITEM_PICKUP } , // id (int)(1.0f * (1<<10)), // rate 0, // handle 255, // volume 128, // panning }; sfx_boom = { { SFX_BOOM } , (int)(1.0f * (1<<10)), 1, 255, 128, }; sfx_pause = { { SFX_167127__CRISSTANZA__PAUSE } , (int)(1.0f * (1<<10)), 2, 255, 128, }; sfx_unpause = { { SFX_167126__CRISSTANZA__UNPAUSE } , (int)(1.0f * (1<<10)), 3, 255, 128, }; sfx_plop = { { SFX_273792__PACOMAV__PLOP } , (int)(1.0f * (1<<10)), 4, 128, 128, }; sfx_fail = { { SFX_73750__TIMBRE__REMIX_OF_BENBONCAN_SAD_TROMBONE_MORE_WAH_BRIGHT_DE_CLICKED } , (int)(1.0f * (1<<10)), 4, 255, 128, }; } void check_collision_self() { std::array headpos = snake.front(); std::deque>::iterator testit = snake.begin(); testit++; //skip head, no collision here for(testit; testit != snake.end(); testit++) { std::array dot = *testit; if ((dot[0] == headpos[0] && dot[1] == headpos[1] ) && !gameOver) { gameOver = true; mmStop(); } } } void check_collision_apples() { std::array headpos = snake.front(); std::vector>::iterator testit = apples.begin(); for(testit; testit <= apples.end(); testit++) { std::array apple = *testit; if ((apple[0] == headpos[0] && apple[1] == headpos[1] )) { score += 10; snakelength++; apples.erase(testit); mmEffectEx(&sfx_pickup); } } } void check_collision_apples_rotten() { std::array headpos = snake.front(); std::vector>::iterator testit = bad_apples.begin(); for(testit; testit <= bad_apples.end(); testit++) { std::array apple = *testit; if ((apple[0] == headpos[0] && apple[1] == headpos[1] )) { snakelength--; bad_apples.erase(testit); mmEffectEx(&sfx_boom); if (snakelength < 2) { gameOver = true; } } } } void draw_snake_head(uint &oamId) { std::array dot = snake.front(); bool hflip = false; bool vflip = false; switch (snakeDirection) { case 0: //going up dmaCopy(&tilemapTiles[64],gfx_head,64); break; case 1: //going right dmaCopy(&tilemapTiles[48],gfx_head,64); break; case 2: //going down dmaCopy(&tilemapTiles[64],gfx_head,64); vflip = true; break; case 3: //going left /*for(int i = 0; i < 8 * 8; i=i+2) { gfx_head[i/2] = ((BITMAP_HEAD_RIGHT[i+1]<<8) | BITMAP_HEAD_RIGHT[i]); }*/ dmaCopy(&tilemapTiles[48],gfx_head,64); hflip = true; break; default: break; } oamSet(&oamSub, // oam oamId, //id dot[0]*8, // x dot[1]*8, // y 0, // priority 0, // palette_alpha SpriteSize_8x8, // size SpriteColorFormat_256Color, // color format gfx_head, // gfxoffset -1, // affineIndex false, // sizeDouble false, // hide hflip, // hflip vflip, // vflip false // mosaic ); oamId++; } void draw_snake_tail(uint &oamId) { std::array dot = snake.back(); bool hflip = false; bool vflip = false; switch (snake.at(snake.size()-2).at(2)) { case 0: //going up dmaCopy(get_sprite_pointer(9+((snake.size()-1)%2)),gfx_tail,64); vflip = true; break; case 1: //going right dmaCopy(get_sprite_pointer(7+((snake.size()-1)%2)),gfx_tail,64); hflip = true; break; case 2: //going down dmaCopy(get_sprite_pointer(9+((snake.size()-1)%2)),gfx_tail,64); break; case 3: //going left /*for(int i = 0; i < 8 * 8; i=i+2) { gfx_tail[i/2] = ((BITMAP_tail_RIGHT[i+1]<<8) | BITMAP_tail_RIGHT[i]); }*/ dmaCopy(get_sprite_pointer(7+((snake.size()-1)%2)),gfx_tail,64); break; default: break; } oamSet(&oamSub, // oam oamId, //id dot[0]*8, // x dot[1]*8, // y 0, // priority 0, // palette_alpha SpriteSize_8x8, // size SpriteColorFormat_256Color, // color format gfx_tail, // gfxoffset -1, // affineIndex false, // sizeDouble false, // hide hflip, // hflip vflip, // vflip false // mosaic ); oamId++; } void draw_snake(uint &oamId) { draw_snake_head(oamId); for(size_t i = 1; i < snake.size()-1; i++) { if(i%2) { oamSet(&oamSub, // oam oamId, //id snake[i][0]*8, // x snake[i][1]*8, // y 0, // priority 0, // palette_alpha SpriteSize_8x8, // size SpriteColorFormat_256Color, // color format gfx_segment_small, // gfxoffset -1, // affineIndex false, // sizeDouble false, // hide false, // hflip false, // vflip false // mosaic ); } else { oamSet(&oamSub, // oam oamId, //id snake[i][0]*8, // x snake[i][1]*8, // y 0, // priority 0, // palette_alpha SpriteSize_8x8, // size SpriteColorFormat_256Color, // color format gfx_segment, // gfxoffset -1, // affineIndex false, // sizeDouble false, // hide false, // hflip false, // vflip false // mosaic ); } oamId++; } draw_snake_tail(oamId); } void draw_apples(uint &oamId) { for(std::array apple : apples) { oamSet(&oamSub, // oam oamId, //id apple[0]*8, // x apple[1]*8, // y 0, // priority 0, // palette_alpha SpriteSize_8x8, // size SpriteColorFormat_256Color, // color format gfx_apple, // gfxoffset -1, // affineIndex false, // sizeDouble false, // hide false, // hflip false, // vflip false // mosaic ); oamId++; } } void draw_apples_rotten(uint &oamId) { for(std::array apple : bad_apples) { oamSet(&oamSub, // oam oamId, //id apple[0]*8, // x apple[1]*8, // y 0, // priority 0, // palette_alpha SpriteSize_8x8, // size SpriteColorFormat_256Color, // color format gfx_apple_rotten, // gfxoffset -1, // affineIndex false, // sizeDouble false, // hide false, // hflip false, // vflip false // mosaic ); oamId++; } } void draw_game() { uint oamid = 0; if (!gameOver) { draw_apples(oamid); draw_apples_rotten(oamid); draw_snake(oamid); } else { iprintf("\x1b[11;12HGAME OVER!"); mmStop(); mmEffectEx(&sfx_fail); consoleSelect(&topScreen); iprintf("\x1b[10;13HGAME OVER!"); } /*iprintf("\x1b[7;0HoamIDmax=%i",oamid);*/ } void draw_stats() { if ( internals ) { iprintf("\x1b[0;0HFrame = %i\nTick = %i\ndir = %i", frame, ticks, snakeDirection); iprintf("\x1b[3;0HLength: %i", snakelength); iprintf("\x1b[4;0Hx=%i\ny=%i",snake.front()[0], snake.front()[1]); } iprintf("\x1b[10;12HScore: %i", score); if (paused) { if (musicEnabled) { iprintf("\x1b[23;0HX = Disable Music"); } else { iprintf("\x1b[23;0HX = Enable Music "); } if (hardMode) { iprintf("\x1b[22;0HY = Restart in normal mode"); } else { iprintf("\x1b[22;0HY = Restart in Hard mode"); } } } void handle_vblank() { frame++; consoleSelect(&topScreen); draw_stats(); oamUpdate(&oamSub); //flashy_colors(); } void update_snake() { std::array headpos = snake.front(); std::array newHead; if (snakeDirection == 0) { newHead = { headpos[0], headpos[1]-1, snakeDirection }; } else if (snakeDirection == 1) { newHead = { headpos[0]+1, headpos[1], snakeDirection }; } else if (snakeDirection == 2) { newHead = { headpos[0], headpos[1]+1, snakeDirection }; } else if (snakeDirection == 3) { newHead = { headpos[0]-1, headpos[1], snakeDirection }; } if (newHead[0] > X_MAX) { newHead[0] = 0; } if (newHead[1] > Y_MAX) { newHead[1] = 0; } if (newHead[0] < 0) { newHead[0] = X_MAX; } if (newHead[1] < 0) { newHead[1] = Y_MAX; } snake.push_front(newHead); snake.resize(snakelength); } void spawn_apples() { if (apples.size() < 1 ) { std::array apple; apple[0] = rand() % (X_MAX+1); apple[1] = rand() % (Y_MAX+1); apples.push_back(apple); } } void spawn_apples_rotten() { if ( (ticks%10==0) && hardMode && ((rand() % 10)<10) && (bad_apples.size()<20)) { std::array badApple; badApple[0] = rand() % (X_MAX+1); badApple[1] = rand() % (Y_MAX+1); bad_apples.push_back(badApple); } } void blink_apples() { if (ticks % 4) { dmaCopy(get_sprite_pointer(5),gfx_apple,64); } else { dmaCopy(get_sprite_pointer(6),gfx_apple,64); } } void do_tick() { if (!paused) { ticks++; if (!gameOver){ /*if (hardMode) { timerStop(0); timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(ticks+5), do_tick); }*/ update_snake(); mmEffectEx(&sfx_plop); check_collision_self(); check_collision_apples(); check_collision_apples_rotten(); spawn_apples(); spawn_apples_rotten(); blink_apples(); //consoleSelect(&bottomScreen); draw_game(); } } } void init_game() { if (hardMode) { timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(9), do_tick); setBackdropColorSub(RGB15(10,5,5)); score = 0; snakelength = START_LENGTH_HARD; snake = { {2,1,1}, {1,1,1} }; apples.clear(); bad_apples.clear(); snakeDirection = 1; ticks = 0; gameOver=false; consoleSelect(&topScreen); consoleClear(); consoleSelect(&bottomScreen); consoleClear(); oamClear(&oamSub,0,0); mmStart( MOD_0RIGIN, MM_PLAY_LOOP ); if (!musicEnabled) { mmPause(); } } else { timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(5), do_tick); setBackdropColorSub(RGB15(5,5,5)); score = 0; snakelength = START_LENGTH; snake = { {4,1,1}, {3,1,1}, {2,1,1}, {1,1,1} }; snakeDirection = 1; ticks = 0; gameOver=false; consoleSelect(&topScreen); consoleClear(); consoleSelect(&bottomScreen); consoleClear(); oamClear(&oamSub,0,0); mmStart( MOD_0RIGIN, MM_PLAY_LOOP ); if (!musicEnabled) { mmPause(); } } } int main(void) { // Set up VBlank handler irqSet(IRQ_VBLANK, handle_vblank); setup(); init_game(); // Start timer for game ticks while(1) { scanKeys(); int keys = keysDown(); if (keys && internals) { consoleSelect(&topScreen); iprintf("\x1b[6;0Hkeys = %s", std::bitset<16>(keys).to_string().c_str()); } if ((keys & KEY_START) && (keys & KEY_L) ) break; if (keys & KEY_SELECT) { internals = !internals; if (internals) { setBackdropColor(RGB15(0,0,7)); } else { setBackdropColor(RGB15(0,0,0)); consoleSelect(&topScreen); consoleClear(); } } if (!paused && !gameOver) { if (keys & KEY_UP) { snakeDirection = 0; draw_game(); } if (keys & KEY_RIGHT) { snakeDirection = 1; draw_game(); } if (keys & KEY_DOWN) { snakeDirection = 2; draw_game(); } if (keys & KEY_LEFT) { snakeDirection = 3; draw_game(); } } if (paused) { if (keys & KEY_X) { musicEnabled = !musicEnabled; } if (keys & KEY_Y) { hardMode = !hardMode; init_game(); draw_game(); } } if (keys & KEY_R) { lcdSwap(); } if (keys & (KEY_START)) { if (!gameOver) { paused = !paused; if (paused) { consoleSelect(&topScreen); mmEffectEx(&sfx_pause); if ( musicEnabled ) { mmPause(); } iprintf("\x1b[11;12HPaused"); } else { if (musicEnabled) { mmResume(); } mmEffectEx(&sfx_unpause); consoleSelect(&topScreen); consoleClear(); } } else { init_game(); } } } return 0; }