712 lines
14 KiB
C++
712 lines
14 KiB
C++
|
|
#include <nds.h>
|
|
#include <bitset>
|
|
#include <stdio.h>
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <array>
|
|
#include <consts.h>
|
|
#include <maxmod9.h>
|
|
#include <nds/ndstypes.h>
|
|
|
|
#include "soundbank.h"
|
|
#include "soundbank_bin.h"
|
|
|
|
#include "tilemap.h"
|
|
|
|
volatile uint frame = 0;
|
|
volatile uint ticks = 0;
|
|
volatile bool internals = false;
|
|
|
|
std::deque<std::array<int, 3>> snake;
|
|
|
|
std::vector<std::array<int, 2>> apples;
|
|
std::vector<std::array<int, 2>> 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<int, 3> headpos = snake.front();
|
|
|
|
std::deque<std::array<int, 3>>::iterator testit = snake.begin();
|
|
testit++; //skip head, no collision here
|
|
for(testit; testit != snake.end(); testit++)
|
|
{
|
|
std::array<int, 3> dot = *testit;
|
|
if ((dot[0] == headpos[0] && dot[1] == headpos[1] ) && !gameOver) {
|
|
gameOver = true;
|
|
mmStop();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void check_collision_apples() {
|
|
|
|
std::array<int, 3> headpos = snake.front();
|
|
|
|
std::vector<std::array<int, 2>>::iterator testit = apples.begin();
|
|
for(testit; testit <= apples.end(); testit++)
|
|
{
|
|
std::array<int, 2> 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<int, 3> headpos = snake.front();
|
|
|
|
std::vector<std::array<int, 2>>::iterator testit = bad_apples.begin();
|
|
for(testit; testit <= bad_apples.end(); testit++)
|
|
{
|
|
std::array<int, 2> 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<int, 3> 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<int, 3> 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<int, 2> 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<int, 2> 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<int, 3> headpos = snake.front();
|
|
std::array<int, 3> 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<int,2> 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<int,2> 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;
|
|
}
|