rockbox/apps/plugins/snake2.c
Thomas Martitz 249bba03f1 Initial commit of the Samsung YP-R0 port.
This port is a hybrid native/RaaA port. It runs on a embedded linux system,
but is the only application. It therefore can implement lots of stuff that
native targets also implement, while leveraging the underlying linux kernel.

The port is quite advanced. User interface, audio playback, plugins work
mostly fine. Missing is e.g. power mangement and USB (see SamsungYPR0 wiki page).

Included in utils/ypr0tools are scripts and programs required to generate
a patched firmware. The patched firmware has the rootfs modified to load
Rockbox. It includes a early/safe USB mode.

This port needs a new toolchain, one that includes glibc headers and libraries.
rockboxdev.sh can generate it, but e.g. codesourcey and distro packages may
also work.

Most of the initial effort is done by Lorenzo Miori and others (on ABI),
including reverse engineering and patching of the original firmware,
initial drivers, and more. Big thanks to you.

Flyspray: FS#12348
Author: Lorenzo Miori, myself

Merry christmas to ypr0 owners! :)

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31415 a1c6a512-1295-4272-9138-f99709370657
2011-12-24 11:56:46 +00:00

1550 lines
38 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2003 Mat Holton
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
Snake2!
Board consists of a WIDTHxHEIGHT grid. If board element is 0 then nothing is
there otherwise it is part of the snake or a wall.
Head and Tail are stored
*/
#include "plugin.h"
#include "lib/highscore.h"
#include "lib/playback_control.h"
#define WIDTH 28
#define HEIGHT 16
#if (LCD_WIDTH >= 160) && (LCD_HEIGHT >= 128)
#include "pluginbitmaps/snake2_header1.h"
#include "pluginbitmaps/snake2_header2.h"
#include "pluginbitmaps/snake2_left.h"
#include "pluginbitmaps/snake2_right.h"
#include "pluginbitmaps/snake2_bottom.h"
#define BMPHEIGHT_snake2_header BMPHEIGHT_snake2_header1
#define BMPWIDTH_snake2_header BMPWIDTH_snake2_header1
#endif
#if (LCD_WIDTH >= 640) && (LCD_HEIGHT >= 480)
#define MULTIPLIER 20 /*Modifier for porting on other screens*/
#define MODIFIER_1 20
#define MODIFIER_2 16
#define CENTER_X 40
#define CENTER_Y 110
#define TOP_X1 68 /* x-coord of the upperleft item (game type) */
#define TOP_X2 562 /* x-coord of the upperright item (maze type) */
#define TOP_X3 84 /* x-coord of the lowerleft item (speed) */
#define TOP_X4 548 /* x-coord of the lowerright item (hi-score) */
#define TOP_Y1 8 /* y-coord of the top row of items */
#define TOP_Y2 50 /* y-coord of the bottom row of items */
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240)
#define MULTIPLIER 10 /*Modifier for porting on other screens*/
#define MODIFIER_1 10
#define MODIFIER_2 8
#define CENTER_X 20
#define CENTER_Y 55
#define TOP_X1 34 /* x-coord of the upperleft item (game type) */
#define TOP_X2 281 /* x-coord of the upperright item (maze type) */
#define TOP_X3 42 /* x-coord of the lowerleft item (speed) */
#define TOP_X4 274 /* x-coord of the lowerright item (hi-score) */
#define TOP_Y1 4 /* y-coord of the top row of items */
#define TOP_Y2 25 /* y-coord of the bottom row of items */
#elif (LCD_WIDTH >= 240) && (LCD_HEIGHT >= 168)
#define MULTIPLIER 8
#define MODIFIER_1 8
#define MODIFIER_2 6
#define CENTER_X 8
#define CENTER_Y 34
#define TOP_X1 34
#define TOP_X2 201
#define TOP_X3 42
#define TOP_X4 194
#define TOP_Y1 4
#define TOP_Y2 25
#elif (LCD_WIDTH >= 220) && (LCD_HEIGHT >= 176)
#define MULTIPLIER 7
#define MODIFIER_1 7
#define MODIFIER_2 5
#define CENTER_X 12
#define CENTER_Y 46
#define TOP_X1 34
#define TOP_X2 181
#define TOP_X3 42
#define TOP_X4 174
#define TOP_Y1 4
#define TOP_Y2 25
#elif (LCD_WIDTH >= 176) && (LCD_HEIGHT >= 132)
#define MULTIPLIER 5
#define MODIFIER_1 5
#define MODIFIER_2 3
#define CENTER_X 18
#define CENTER_Y 40
#define TOP_X1 34
#define TOP_X2 137
#define TOP_X3 42
#define TOP_X4 130
#define TOP_Y1 4
#define TOP_Y2 25
#elif (LCD_WIDTH >= 160) && (LCD_HEIGHT >= 128)
#define MULTIPLIER 5
#define MODIFIER_1 5
#define MODIFIER_2 3
#define CENTER_X 10
#define CENTER_Y 38
#define TOP_X1 34
#define TOP_X2 121
#define TOP_X3 42
#define TOP_X4 114
#define TOP_Y1 4
#define TOP_Y2 25
#else
#define MULTIPLIER 4
#define MODIFIER_1 4
#define MODIFIER_2 2
#define CENTER_X 0
#define CENTER_Y 0
#endif
/* variable button definitions */
#if CONFIG_KEYPAD == RECORDER_PAD
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_OFF
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_OFF
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif CONFIG_KEYPAD == ONDIO_PAD
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_OFF
#define SNAKE2_PLAYPAUSE BUTTON_MENU
#define SNAKE2_PLAYPAUSE_TEXT "Menu"
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_OFF
#define SNAKE2_PLAYPAUSE BUTTON_ON
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#define SNAKE2_RC_QUIT BUTTON_RC_STOP
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_MENU
#define SNAKE2_DOWN BUTTON_PLAY
#define SNAKE2_QUIT (BUTTON_SELECT | BUTTON_MENU)
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
(CONFIG_KEYPAD == SANSA_C200_PAD) || \
(CONFIG_KEYPAD == SANSA_CONNECT_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
(CONFIG_KEYPAD == SANSA_M200_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT (BUTTON_HOME|BUTTON_REPEAT)
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_SCROLL_UP
#define SNAKE2_DOWN BUTTON_SCROLL_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_FF
#define SNAKE2_PLAYPAUSE_TEXT "FF"
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
(CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_BACK
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif (CONFIG_KEYPAD == MROBE100_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_SELECT
#define SNAKE2_PLAYPAUSE_TEXT "Select"
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
#define SNAKE2_LEFT BUTTON_RC_REW
#define SNAKE2_RIGHT BUTTON_RC_FF
#define SNAKE2_UP BUTTON_RC_VOL_UP
#define SNAKE2_DOWN BUTTON_RC_VOL_DOWN
#define SNAKE2_QUIT BUTTON_RC_REC
#define SNAKE2_PLAYPAUSE BUTTON_RC_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#elif (CONFIG_KEYPAD == COWON_D2_PAD)
#define SNAKE2_QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_BACK
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#elif (CONFIG_KEYPAD == PHILIPS_HDD1630_PAD) || \
(CONFIG_KEYPAD == PHILIPS_HDD6330_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_VIEW
#define SNAKE2_PLAYPAUSE_TEXT "View"
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
#define SNAKE2_LEFT BUTTON_PREV
#define SNAKE2_RIGHT BUTTON_NEXT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_RIGHT
#define SNAKE2_PLAYPAUSE_TEXT "Right"
#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
(CONFIG_KEYPAD == ONDAVX777_PAD) || \
CONFIG_KEYPAD == MROBE500_PAD
#define SNAKE2_QUIT BUTTON_POWER
#elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_REC
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
#define SNAKE2_LEFT BUTTON_PREV
#define SNAKE2_RIGHT BUTTON_NEXT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_REC
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#elif CONFIG_KEYPAD == MPIO_HD200_PAD
#define SNAKE2_LEFT BUTTON_VOL_DOWN
#define SNAKE2_RIGHT BUTTON_VOL_UP
#define SNAKE2_UP BUTTON_REW
#define SNAKE2_DOWN BUTTON_FF
#define SNAKE2_QUIT (BUTTON_REC | BUTTON_PLAY)
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#elif CONFIG_KEYPAD == MPIO_HD300_PAD
#define SNAKE2_LEFT BUTTON_MENU
#define SNAKE2_RIGHT BUTTON_ENTER
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT (BUTTON_REC|BUTTON_REPEAT)
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "Play"
#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_PLAYPAUSE
#define SNAKE2_PLAYPAUSE_TEXT "Play/Pause"
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef SNAKE2_LEFT
#define SNAKE2_LEFT BUTTON_MIDLEFT
#endif
#ifndef SNAKE2_RIGHT
#define SNAKE2_RIGHT BUTTON_MIDRIGHT
#endif
#ifndef SNAKE2_UP
#define SNAKE2_UP BUTTON_TOPMIDDLE
#endif
#ifndef SNAKE2_DOWN
#define SNAKE2_DOWN BUTTON_BOTTOMMIDDLE
#endif
#ifndef SNAKE2_QUIT
#define SNAKE2_QUIT BUTTON_TOPLEFT
#endif
#ifndef SNAKE2_PLAYPAUSE
#define SNAKE2_PLAYPAUSE BUTTON_CENTER
#endif
#ifndef SNAKE2_PLAYPAUSE_TEXT
#define SNAKE2_PLAYPAUSE_TEXT "CENTER"
#endif
#endif
static int max_levels = 0;
static char (*level_cache)[HEIGHT][WIDTH];
/*Board itself - 2D int array*/
static int board[WIDTH][HEIGHT];
/*
Buffer for sorting movement (in case user presses two movements during a
single frame
*/
static int ardirectionbuffer[2];
static int score;
static int applex;
static int appley;
static int dir;
static int frames;
static int apple;
static int level = 4, speed = 5,dead = 0, quit = 0;
static int sillydir = 0, num_levels = 0;
static int level_from_file = 0;
static int headx, heady, tailx, taily, applecountdown = 5;
static int game_type = 0;
static int num_apples_to_get=1;
static int num_apples_to_got=0;
static int game_b_level=0;
static int applecount=0;
/* used for string width, height for orientation purposes */
static int strwdt, strhgt;
static char strbuf[32];
#define NUM_SCORES 5
static struct highscore highscores[NUM_SCORES];
#define NORTH 1
#define EAST 2
#define SOUTH 4
#define WEST 8
#define HEAD 16
#define EAST_NORTH 32
#define EAST_SOUTH 64
#define WEST_NORTH 128
#define WEST_SOUTH 256
#define NORTH_EAST 512
#define NORTH_WEST 1024
#define SOUTH_EAST 2048
#define SOUTH_WEST 4096
#define LEVELS_FILE PLUGIN_GAMES_DIR "/snake2.levels"
#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/snake2.score"
static int load_all_levels(void)
{
int linecnt = 0;
int fd;
size_t size;
char buf[64]; /* Larger than WIDTH, to allow for whitespace after the
lines */
/* Init the level_cache pointer and
calculate how many levels that will fit */
level_cache = rb->plugin_get_buffer(&size);
max_levels = size / (HEIGHT*WIDTH);
num_levels = 0;
/* open file */
if ((fd = rb->open(LEVELS_FILE, O_RDONLY)) < 0)
{
return -1;
}
while(rb->read_line(fd, buf, 64) > 0)
{
if(rb->strlen(buf) == 0) /* Separator? */
{
num_levels++;
if(num_levels > max_levels)
{
rb->splash(HZ, "Too many levels in file");
break;
}
continue;
}
rb->memcpy(level_cache[num_levels][linecnt], buf, WIDTH);
linecnt++;
if(linecnt == HEIGHT)
{
linecnt = 0;
}
}
rb->close(fd);
return 0;
}
/*
** Completely clear the board of walls and/or snake
*/
static void clear_board( void)
{
int x,y;
for (x = 0; x < WIDTH; x++)
{
for (y = 0; y < HEIGHT; y++)
{
board[x][y] = 0;
}
}
}
static int load_level( int level_number )
{
int x,y;
clear_board();
for(y = 0;y < HEIGHT;y++)
{
for(x = 0;x < WIDTH;x++)
{
switch(level_cache[level_number][y][x])
{
case '|':
board[x][y] = NORTH;
break;
case '-':
board[x][y] = EAST;
break;
case '+':
board[x][y] = HEAD;
break;
}
}
}
return 1;
}
/*
** Gets the currently chosen direction from the first place
** in the direction buffer. If there is something in the
** next part of the buffer then that is moved to the first place
*/
static void get_direction( void )
{
/*if 1st place is empty*/
if(ardirectionbuffer[0] != -1)
{
/*return this direction*/
dir = ardirectionbuffer[0];
ardirectionbuffer[0]=-1;
/*now see if one needs moving:*/
if(ardirectionbuffer[1] != -1)
{
/*there's a move waiting to be done
so move it into the space:*/
ardirectionbuffer[0] = ardirectionbuffer[1];
ardirectionbuffer[1] = -1;
}
}
}
/*
** Sets the direction
*/
static void set_direction(int newdir)
{
if(ardirectionbuffer[0] != newdir)
{
/*if 1st place is empty*/
if(ardirectionbuffer[0] == -1)
{
/*use 1st space:*/
ardirectionbuffer[0] = newdir;
}
else
{
/*use 2nd space:*/
if(ardirectionbuffer[0] != newdir) ardirectionbuffer[1] = newdir;
}
if(frames < 0) ardirectionbuffer[0] = newdir;
}
}
static void new_level(int level)
{
load_level(level);
ardirectionbuffer[0] = -1;
ardirectionbuffer[1] = -1;
dir = EAST;
headx = WIDTH/2;
heady = HEIGHT/2;
tailx = headx - 4;
taily = heady;
applecountdown = 0;
/*Create a small snake to start off with*/
board[headx][heady] = dir;
board[headx-1][heady] = dir;
board[headx-2][heady] = dir;
board[headx-3][heady] = dir;
board[headx-4][heady] = dir;
num_apples_to_got=0;
}
static void init_snake(void)
{
num_apples_to_get=1;
if(game_type == 1)
level_from_file = 1;
game_b_level=1;
new_level(level_from_file);
}
#if (LCD_WIDTH >= 160) && (LCD_HEIGHT >= 128)
static void draw_frame_bitmap(int header_type)
{
rb->lcd_bitmap(header_type==1? snake2_header1: snake2_header2, 0, 0,
BMPWIDTH_snake2_header, BMPHEIGHT_snake2_header);
rb->lcd_bitmap(snake2_left, 0, BMPHEIGHT_snake2_header,
BMPWIDTH_snake2_left, BMPHEIGHT_snake2_left);
rb->lcd_bitmap(snake2_right,
LCD_WIDTH - BMPWIDTH_snake2_right, BMPHEIGHT_snake2_header,
BMPWIDTH_snake2_right, BMPHEIGHT_snake2_right);
rb->lcd_bitmap(snake2_bottom,
0, BMPHEIGHT_snake2_header + BMPHEIGHT_snake2_left,
BMPWIDTH_snake2_bottom, BMPHEIGHT_snake2_bottom);
}
#endif
/*
** Draws the apple. If it doesn't exist then
** a new one get's created.
*/
static void draw_apple_bit(int x, int y)
{
rb->lcd_fillrect((CENTER_X+x*MULTIPLIER)+1, CENTER_Y+y*MULTIPLIER,
MODIFIER_2, MODIFIER_1);
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER, (CENTER_Y+y*MULTIPLIER)+1,
MODIFIER_1, MODIFIER_2);
}
static void draw_apple( void )
{
int x,y;
#if LCD_WIDTH >= 160 && LCD_HEIGHT >= 128
draw_frame_bitmap(2);
rb->snprintf(strbuf, sizeof(strbuf), "%d", applecount);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X3 - strwdt/2, TOP_Y2, strbuf);
rb->snprintf(strbuf, sizeof(strbuf), "%d", score);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X4 - strwdt/2, TOP_Y2, strbuf);
#endif
if (!apple)
{
do
{
x = (rb->rand() % (WIDTH-1))+1;
y = (rb->rand() % (HEIGHT-1))+1;
} while (board[x][y]);
apple = 1;
board[x][y] = -1;
applex = x;appley = y;
}
draw_apple_bit(applex, appley);
}
/*
* x x *
* x x *
* x x *
* x x *
*/
static void draw_vertical_bit(int x, int y)
{
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER+1, CENTER_Y+y*MULTIPLIER,
MODIFIER_2, MODIFIER_1);
}
/*
* * * *
X X X X
X X X X
* * * *
*/
static void draw_horizontal_bit(int x, int y)
{
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER, CENTER_Y+y*MULTIPLIER+1,
MODIFIER_1, MODIFIER_2);
}
/*
* * * *
* * X X
* X X X
* X X *
*/
static void draw_n_to_e_bit(int x, int y)
{
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER+1, CENTER_Y+y*MULTIPLIER+2,
MODIFIER_2, MODIFIER_2);
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER+2, CENTER_Y+y*MULTIPLIER+1,
MODIFIER_2, MODIFIER_2);
}
/*
* * * *
* * X X
* X X X
* X X *
*/
static void draw_w_to_s_bit(int x, int y)
{
draw_n_to_e_bit(x,y);
}
/*
* * * *
X X * *
X X X *
* X X *
*/
static void draw_n_to_w_bit(int x, int y)
{
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER, CENTER_Y+y*MULTIPLIER+1,
MODIFIER_2, MODIFIER_2);
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER+1, CENTER_Y+y*MULTIPLIER+2,
MODIFIER_2, MODIFIER_2);
}
/*
* * * *
X X * *
X X X *
* X X *
*/
static void draw_e_to_s_bit(int x, int y)
{
draw_n_to_w_bit(x, y);
}
/*
* X X *
* X X X
* * X X
* * * *
*/
static void draw_s_to_e_bit(int x, int y)
{
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER+1, CENTER_Y+y*MULTIPLIER,
MODIFIER_2, MODIFIER_2);
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER+2, CENTER_Y+y*MULTIPLIER+1,
MODIFIER_2, MODIFIER_2);
}
/*
* X X *
* X X X
* * X X
* * * *
*/
static void draw_w_to_n_bit(int x, int y)
{
draw_s_to_e_bit(x,y);
}
/*
* X X *
X X X *
X X * *
* * * *
*/
static void draw_e_to_n_bit(int x, int y)
{
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER+1, CENTER_Y+y*MULTIPLIER,
MODIFIER_2, MODIFIER_2);
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER, CENTER_Y+y*MULTIPLIER+1,
MODIFIER_2, MODIFIER_2);
}
/*
* X X *
X X X *
X X * *
* * * *
*/
static void draw_s_to_w_bit(int x, int y)
{
draw_e_to_n_bit(x, y);
}
static void draw_head_bit(int x, int y)
{
rb->lcd_fillrect(CENTER_X+x*MULTIPLIER, CENTER_Y+y*MULTIPLIER,
MODIFIER_1, MODIFIER_1);
}
#if 0 /* unused */
/*
** Draws a wall/obsticals
*/
static void draw_boundary ( void )
{
int x, y;
/*TODO: Load levels from file!*/
/*top and bottom line*/
for(x=0; x < WIDTH; x++)
{
board[x][0] = EAST;
board[x][HEIGHT-1] = WEST;
}
/*left and right lines*/
for(y=0; y < HEIGHT; y++)
{
board[0][y] = NORTH;
board[WIDTH-1][y] = SOUTH;
}
/*corners:*/
board[0][0] = NORTH_EAST;
board[WIDTH-1][0] = EAST_SOUTH;
board[0][HEIGHT-1] = SOUTH_EAST;
board[WIDTH-1][HEIGHT-1] = EAST_NORTH;
}
#endif
/*
** Redraw the entire board
*/
static void redraw (void)
{
int x,y;
#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(LCD_BLACK);
rb->lcd_set_background(LCD_WHITE);
#endif
rb->lcd_clear_display();
for (x = 0; x < WIDTH; x++)
{
for (y = 0; y < HEIGHT; y++)
{
switch (board[x][y])
{
case -1:
draw_apple_bit(x, y);
break;
case 0:
break;
case NORTH:
case SOUTH:
draw_vertical_bit(x,y);
break;
case EAST:
case WEST:
draw_horizontal_bit(x,y);
break;
default:
draw_head_bit(x, y);
break;
}
}
}
#if LCD_WIDTH >= 160 && LCD_HEIGHT >= 128
draw_frame_bitmap(2);
rb->snprintf(strbuf, sizeof(strbuf), "%d", applecount);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X3 - strwdt/2, TOP_Y2, strbuf);
rb->snprintf(strbuf, sizeof(strbuf), "%d", score);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X4 - strwdt/2, TOP_Y2, strbuf);
#endif
}
/*
** Draws the snake bit described by nCurrentBit at position x/y
** deciding whether it's a corner bit by examing the nPrevious bit
*/
static void draw_snake_bit(int currentbit, int previousbit, int x, int y)
{
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
draw_head_bit(x, y);
rb->lcd_set_drawmode(DRMODE_SOLID);
switch(currentbit)
{
case(NORTH):
switch(previousbit)
{
case(SOUTH):
case(NORTH):
draw_vertical_bit(x,y);
break;
case(EAST):
draw_e_to_n_bit(x,y);
break;
case(WEST):
draw_w_to_n_bit(x,y);
break;
}
break;
case(EAST):
switch(previousbit)
{
case(WEST):
case(EAST):
draw_horizontal_bit(x,y);
break;
case(NORTH):
draw_n_to_e_bit(x,y);
break;
case(SOUTH):
draw_s_to_e_bit(x,y);
break;
}
break;
case(SOUTH):
switch(previousbit)
{
case(SOUTH):
case(NORTH):
draw_vertical_bit(x,y);
break;
case(EAST):
draw_e_to_s_bit(x,y);
break;
case(WEST):
draw_w_to_s_bit(x,y);
break;
}
break;
case(WEST):
switch(previousbit)
{
case(EAST):
case(WEST):
draw_horizontal_bit(x,y);
break;
case(SOUTH):
draw_s_to_w_bit(x,y);
break;
case(NORTH):
draw_n_to_w_bit(x,y);
break;
}
break;
}
}
static void redraw_snake(void)
{
int x = tailx, y = taily;
int olddir, newdir = board[x][y];
while (x != headx || y != heady)
{
olddir = newdir;
switch (olddir)
{
case(NORTH):
y--;
break;
case(EAST):
x++;
break;
case(SOUTH):
y++;
break;
case(WEST):
x--;
break;
}
if(x == WIDTH)
x = 0;
else if(x < 0)
x = WIDTH-1;
if(y == HEIGHT)
y = 0;
else if(y < 0)
y = HEIGHT-1;
newdir = board[x][y];
if(olddir != newdir)
draw_snake_bit(newdir, olddir, x, y);
}
}
/*
** Death 'sequence' and end game stuff.
*/
static void die (void)
{
int button;
bool done=false;
rb->splash(HZ*2, "Oops!");
rb->lcd_clear_display();
applecount=0;
rb->lcd_getstringsize("You died!",&strwdt,&strhgt);
rb->lcd_putsxy((LCD_WIDTH - strwdt)/2,strhgt,"You died!");
rb->snprintf(strbuf, sizeof(strbuf), "Your score: %d", score);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy((LCD_WIDTH - strwdt)/2, strhgt * 2 + 2, strbuf);
if (highscore_update(score, level_from_file, game_type==0?"Type A":"Type B",
highscores, NUM_SCORES) == 0)
{
rb->lcd_getstringsize("New high score!",&strwdt,&strhgt);
rb->lcd_putsxy((LCD_WIDTH - strwdt)/2,strhgt * 4 + 2,"New high score!");
}
else
{
rb->snprintf(strbuf, sizeof(strbuf), "High score: %d", highscores[0].score);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy((LCD_WIDTH - strwdt)/2, strhgt * 5, strbuf);
}
rb->snprintf(strbuf, sizeof(strbuf), "Press %s...", SNAKE2_PLAYPAUSE_TEXT);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy((LCD_WIDTH - strwdt)/2, strhgt * 7, strbuf);
rb->lcd_update();
while(!done)
{
button=rb->button_get(true);
switch(button)
{
case SNAKE2_PLAYPAUSE:
done = true;
break;
}
}
dead=1;
}
/*
** Check for collision. TODO: Currently this
** sets of the death sequence. What we want is it to only return a true/false
** depending on whether a collision occured.
*/
static void collision ( int x, int y )
{
int bdeath=0;
switch (board[x][y])
{
case 0:
break;
case -1:
score = score + (1 * level);
apple=0;
applecountdown=2;
applecount++;
if(game_type==1)
{
if(num_apples_to_get == num_apples_to_got)
{
level_from_file++;
if(level_from_file >= num_levels)
{
level_from_file = 1;
/*and increase the number of apples to pick up
before level changes*/
num_apples_to_get+=2;
game_b_level++;
}
rb->splash(HZ, "Level Completed!");
new_level(level_from_file);
redraw();
rb->lcd_update();
}
else
num_apples_to_got++;
}
break;
default:
bdeath=1;
break;
}
if(bdeath==1)
{
die();
sillydir = dir;
frames = -110;
}
}
static void move( void )
{
int taildir;
/*this actually sets the dir variable.*/
get_direction();
/*draw head*/
switch (dir)
{
case (NORTH):
board[headx][heady]=NORTH;
heady--;
break;
case (EAST):
board[headx][heady]=EAST;
headx++;
break;
case (SOUTH):
board[headx][heady]=SOUTH;
heady++;
break;
case (WEST):
board[headx][heady]=WEST;
headx--;
break;
}
if(headx == WIDTH)
headx = 0;
else if(headx < 0)
headx = WIDTH-1;
if(heady == HEIGHT)
heady = 0;
else if(heady < 0)
heady = HEIGHT-1;
draw_head_bit(headx, heady);
/*clear tail*/
if(applecountdown <= 0)
{
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
draw_head_bit(tailx, taily);
rb->lcd_set_drawmode(DRMODE_SOLID);
taildir = board[tailx][taily];
board[tailx][taily] = 0;
switch (taildir)
{
case(NORTH):
taily--;
break;
case(EAST):
tailx++;
break;
case(SOUTH):
taily++;
break;
case(WEST):
tailx--;
break;
}
if(tailx == WIDTH)
tailx = 0;
else if(tailx < 0)
tailx = WIDTH-1;
if(taily == HEIGHT)
taily = 0;
else if(taily < 0)
taily = HEIGHT-1;
}
else
applecountdown--;
}
static void frame (void)
{
int olddir, noldx, noldy, temp;
noldx = headx;
noldy = heady;
olddir = 0;
switch(dir)
{
case(NORTH):
if(heady == HEIGHT-1)
temp = 0;
else
temp = heady + 1;
olddir = board[headx][temp];
break;
case(EAST):
if(headx == 0)
temp = WIDTH-1;
else
temp = headx - 1;
olddir = board[temp][heady];
break;
case(SOUTH):
if(heady == 0)
temp = HEIGHT-1;
else
temp = heady - 1;
olddir = board[headx][temp];
break;
case(WEST):
if(headx == WIDTH-1)
temp = 0;
else
temp = headx + 1;
olddir = board[temp][heady];
break;
}
move();
/*
now redraw the bit that was
the tail, to something snake-like:
*/
draw_snake_bit(dir, olddir, noldx, noldy);
collision(headx, heady);
rb->lcd_update();
}
static void game_pause (void)
{
int button;
rb->lcd_clear_display();
rb->lcd_getstringsize("Paused",&strwdt,&strhgt);
rb->lcd_putsxy((LCD_WIDTH - strwdt)/2,LCD_HEIGHT/2,"Paused");
rb->lcd_update();
while (1)
{
button = rb->button_get(true);
switch (button)
{
case SNAKE2_PLAYPAUSE:
redraw();
redraw_snake();
draw_head_bit(headx, heady);
rb->lcd_update();
rb->sleep(HZ/2);
return;
#ifdef SNAKE2_RC_QUIT
case SNAKE2_RC_QUIT:
#endif
case SNAKE2_QUIT:
dead = 1;
quit = 1;
return;
default:
if (rb->default_event_handler(button)==SYS_USB_CONNECTED) {
dead = 1;
quit = 2;
return;
}
break;
}
}
}
static void game (void)
{
int button;
redraw();
rb->lcd_update();
/*main loop:*/
while (1)
{
if(frames==5)
{
frame();
if(frames > 0) frames = 0;
}
frames++;
if(frames == 0)
{
die();
}
else
{
if(frames < 0)
{
if(sillydir != dir)
{
/*it has, great set frames to a positive value again:*/
frames = 1;
}
}
}
if (dead) return;
draw_apple();
rb->sleep(HZ/speed);
button = rb->button_get(false);
#ifdef HAS_BUTTON_HOLD
if (rb->button_hold())
button = SNAKE2_PLAYPAUSE;
#endif
switch (button)
{
case SNAKE2_UP:
case SNAKE2_UP | BUTTON_REPEAT:
if (dir != SOUTH) set_direction(NORTH);
break;
case SNAKE2_RIGHT:
case SNAKE2_RIGHT | BUTTON_REPEAT:
if (dir != WEST) set_direction(EAST);
break;
case SNAKE2_DOWN:
case SNAKE2_DOWN | BUTTON_REPEAT:
if (dir != NORTH) set_direction(SOUTH);
break;
case SNAKE2_LEFT:
case SNAKE2_LEFT | BUTTON_REPEAT:
if (dir != EAST) set_direction(WEST);
break;
#ifdef SNAKE2_RC_QUIT
case SNAKE2_RC_QUIT:
#endif
case SNAKE2_QUIT:
quit = 1;
return;
case SNAKE2_PLAYPAUSE:
game_pause();
break;
default:
if (rb->default_event_handler(button)==SYS_USB_CONNECTED) {
quit = 2;
return;
}
break;
}
}
}
static void select_maze(void)
{
int button;
clear_board();
load_level( level_from_file );
redraw();
rb->lcd_update();
while (1)
{
#if LCD_WIDTH >= 160 && LCD_HEIGHT >= 128
draw_frame_bitmap(1);
rb->snprintf(strbuf, sizeof(strbuf), "%d", level);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X3 - strwdt/2, TOP_Y2, strbuf);
rb->snprintf(strbuf, sizeof(strbuf), "%d", level_from_file);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X2 - strwdt/2, TOP_Y1, strbuf);
rb->strcpy(strbuf, game_type==0? "A": "B");
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X1, TOP_Y1, strbuf);
rb->snprintf(strbuf, sizeof(strbuf), "%d", highscores[0].score);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy(TOP_X4 - strwdt/2, TOP_Y2, strbuf);
#else
rb->snprintf(strbuf, sizeof(strbuf), "Maze: %d", level_from_file);
rb->lcd_getstringsize(strbuf, &strwdt, &strhgt);
rb->lcd_putsxy((WIDTH*MULTIPLIER - strwdt)/2,
(HEIGHT*MULTIPLIER - strhgt)/2, strbuf);
#endif
rb->lcd_update();
button = rb->button_get(true);
switch (button)
{
case SNAKE2_QUIT:
case SNAKE2_PLAYPAUSE:
return;
break;
case SNAKE2_UP:
case SNAKE2_RIGHT:
if(level_from_file < num_levels)
level_from_file++;
else
level_from_file = 0;
load_level( level_from_file );
redraw();
break;
case SNAKE2_DOWN:
case SNAKE2_LEFT:
if(level_from_file > 0)
level_from_file--;
else
level_from_file = num_levels;
load_level( level_from_file );
redraw();
break;
default:
if (rb->default_event_handler(button)==SYS_USB_CONNECTED) {
quit = 2;
return;
}
break;
}
}
}
static void game_init(void)
{
int selection = 0;
static const struct opt_items type_options[] = {
{ "Type A", -1 },
{ "Type B", -1 },
};
MENUITEM_STRINGLIST(menu, "Snake2 Menu", NULL,
"Start New Game",
"Game Type", "Select Maze", "Speed",
"High Scores",
"Playback Control", "Quit");
rb->button_clear_queue();
dead = 0;
apple = 0;
score = 0;
applecount = 0;
while (1) {
switch (rb->do_menu(&menu, &selection, NULL, false)) {
case 0:
speed = level*20;
return;
case 1:
rb->set_option("Game Type", &game_type, INT,
type_options, 2, NULL);
break;
case 2:
select_maze();
if(quit) return;
break;
case 3:
rb->set_int("Speed", "", UNIT_INT, &level,
NULL, 1, 1, 10, NULL);
break;
case 4:
highscore_show(-1, highscores, NUM_SCORES, true);
break;
case 5:
playback_control(NULL);
break;
case 6:
quit = 1;
return;
case MENU_ATTACHED_USB:
quit = 2;
return;
default:
break;
}
}
}
enum plugin_status plugin_start(const void* parameter)
{
(void)(parameter);
/* Lets use the default font */
rb->lcd_setfont(FONT_SYSFIXED);
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
load_all_levels();
if (num_levels == 0) {
rb->splash(HZ*2, "Failed loading levels!");
return PLUGIN_OK;
}
highscore_load(SCORE_FILE, highscores, NUM_SCORES);
while(quit==0)
{
game_init();
if(quit)
break;
rb->lcd_clear_display();
frames=1;
init_snake();
/*Start Game:*/
game();
}
highscore_save(SCORE_FILE, highscores, NUM_SCORES);
return (quit==1) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
}