From 4083b2581edcf16724b956288abf55809ea683fd Mon Sep 17 00:00:00 2001 From: roolku Date: Thu, 10 Apr 2008 23:28:19 +0000 Subject: [PATCH] FS#8861 - More Rockbox friendly MazezaM (by Malcolm Tyrrell) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17068 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/mazezam.c | 2204 ++++++++++------------- manual/plugins/images/ss-mazezam-112x64x1.png | Bin 0 -> 401 bytes manual/plugins/images/ss-mazezam-128x128x16.png | Bin 0 -> 541 bytes manual/plugins/images/ss-mazezam-132x80x16.png | Bin 0 -> 451 bytes manual/plugins/images/ss-mazezam-138x110x2.png | Bin 0 -> 327 bytes manual/plugins/images/ss-mazezam-160x128x1.png | Bin 323 -> 287 bytes manual/plugins/images/ss-mazezam-160x128x16.png | Bin 0 -> 562 bytes manual/plugins/images/ss-mazezam-160x128x2.png | Bin 0 -> 341 bytes manual/plugins/images/ss-mazezam-176x132x16.png | Bin 0 -> 565 bytes manual/plugins/images/ss-mazezam-176x220x16.png | Bin 0 -> 809 bytes manual/plugins/images/ss-mazezam-220x176x16.png | Bin 0 -> 752 bytes manual/plugins/images/ss-mazezam-240x320x16.png | Bin 0 -> 1198 bytes manual/plugins/images/ss-mazezam-320x240x16.png | Bin 0 -> 991 bytes manual/plugins/main.tex | 2 + manual/plugins/mazezam.tex | 29 + 15 files changed, 989 insertions(+), 1246 deletions(-) rewrite apps/plugins/mazezam.c (70%) create mode 100644 manual/plugins/images/ss-mazezam-112x64x1.png create mode 100644 manual/plugins/images/ss-mazezam-128x128x16.png create mode 100644 manual/plugins/images/ss-mazezam-132x80x16.png create mode 100644 manual/plugins/images/ss-mazezam-138x110x2.png create mode 100644 manual/plugins/images/ss-mazezam-160x128x16.png create mode 100644 manual/plugins/images/ss-mazezam-160x128x2.png create mode 100644 manual/plugins/images/ss-mazezam-176x132x16.png create mode 100644 manual/plugins/images/ss-mazezam-176x220x16.png create mode 100644 manual/plugins/images/ss-mazezam-220x176x16.png create mode 100644 manual/plugins/images/ss-mazezam-240x320x16.png create mode 100644 manual/plugins/images/ss-mazezam-320x240x16.png create mode 100644 manual/plugins/mazezam.tex diff --git a/apps/plugins/mazezam.c b/apps/plugins/mazezam.c dissimilarity index 70% index dac73473c..43d6cd7e3 100644 --- a/apps/plugins/mazezam.c +++ b/apps/plugins/mazezam.c @@ -1,1246 +1,958 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * ### line of auto-generated stuff I don't understand ### - * - * Copyright (C) 2006 Malcolm Tyrrell - * - * MazezaM - a Rockbox version of my ZX Spectrum game from 2002 - * - * All files in this archive are subject to the GNU General Public License. - * See the file COPYING in the source tree root for full license agreement. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "plugin.h" -#include "configfile.h" -#include "helper.h" - -/* Include standard plugin macro */ -PLUGIN_HEADER - -static struct plugin_api* rb; - -MEM_FUNCTION_WRAPPERS(rb); - -#if CONFIG_KEYPAD == RECORDER_PAD -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_PLAY - -#define MAZEZAM_RETRY BUTTON_F1 -#define MAZEZAM_RETRY_KEYNAME "[F1]" -#define MAZEZAM_QUIT BUTTON_OFF -#define MAZEZAM_QUIT_KEYNAME "[OFF]" - -#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_F1 -#define MAZEZAM_RETRY_KEYNAME "[F1]" -#define MAZEZAM_QUIT BUTTON_OFF -#define MAZEZAM_QUIT_KEYNAME "[OFF]" - -#elif CONFIG_KEYPAD == ONDIO_PAD -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_RIGHT - -#define MAZEZAM_RETRY BUTTON_MENU -#define MAZEZAM_RETRY_KEYNAME "[MENU]" -#define MAZEZAM_QUIT BUTTON_OFF -#define MAZEZAM_QUIT_KEYNAME "[OFF]" - -#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_REC -#define MAZEZAM_RETRY_KEYNAME "[REC]" -#define MAZEZAM_QUIT BUTTON_POWER -#define MAZEZAM_QUIT_KEYNAME "[POWER]" - -#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ - (CONFIG_KEYPAD == IPOD_3G_PAD) || \ - (CONFIG_KEYPAD == IPOD_1G2G_PAD) -#define MAZEZAM_UP BUTTON_MENU -#define MAZEZAM_DOWN BUTTON_PLAY -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_SELECT -#define MAZEZAM_RETRY_KEYNAME "[SELECT]" -#define MAZEZAM_QUIT (BUTTON_SELECT | BUTTON_REPEAT) -#define MAZEZAM_QUIT_KEYNAME "[SELECT] (held)" - -#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ - (CONFIG_KEYPAD == IRIVER_H300_PAD) -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_ON -#define MAZEZAM_RETRY_KEYNAME "[ON]" -#define MAZEZAM_QUIT BUTTON_OFF -#define MAZEZAM_QUIT_KEYNAME "[OFF]" - -#elif (CONFIG_KEYPAD == GIGABEAT_PAD) -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_A -#define MAZEZAM_RETRY_KEYNAME "[A]" -#define MAZEZAM_QUIT BUTTON_POWER -#define MAZEZAM_QUIT_KEYNAME "[POWER]" - -#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \ -(CONFIG_KEYPAD == SANSA_C200_PAD) -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_REC -#define MAZEZAM_RETRY_KEYNAME "[REC]" -#define MAZEZAM_QUIT BUTTON_POWER -#define MAZEZAM_QUIT_KEYNAME "[POWER]" - -#elif (CONFIG_KEYPAD == IRIVER_H10_PAD) -#define MAZEZAM_UP BUTTON_SCROLL_UP -#define MAZEZAM_DOWN BUTTON_SCROLL_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_PLAY - -#define MAZEZAM_RETRY BUTTON_PLAY -#define MAZEZAM_RETRY_KEYNAME "[PLAY]" -#define MAZEZAM_QUIT BUTTON_POWER -#define MAZEZAM_QUIT_KEYNAME "[POWER]" - -#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_PLAY -#define MAZEZAM_RETRY_KEYNAME "[PLAY]" -#define MAZEZAM_QUIT BUTTON_BACK -#define MAZEZAM_QUIT_KEYNAME "[BACK]" - -#elif (CONFIG_KEYPAD == MROBE100_PAD) -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_DISPLAY -#define MAZEZAM_RETRY_KEYNAME "[DISPLAY]" -#define MAZEZAM_QUIT BUTTON_POWER -#define MAZEZAM_QUIT_KEYNAME "[POWER]" - -#elif CONFIG_KEYPAD == IAUDIO_M3_PAD -#define MAZEZAM_UP BUTTON_RC_VOL_UP -#define MAZEZAM_DOWN BUTTON_RC_VOL_DOWN -#define MAZEZAM_LEFT BUTTON_RC_REW -#define MAZEZAM_RIGHT BUTTON_RC_FF -#define MAZEZAM_SELECT BUTTON_RC_PLAY - -#define MAZEZAM_RETRY BUTTON_RC_MODE -#define MAZEZAM_RETRY_KEYNAME "[MODE]" -#define MAZEZAM_QUIT BUTTON_RC_REC -#define MAZEZAM_QUIT_KEYNAME "[REC]" - -#elif (CONFIG_KEYPAD == COWOND2_PAD) -#define MAZEZAM_UP BUTTON_UP -#define MAZEZAM_DOWN BUTTON_DOWN -#define MAZEZAM_LEFT BUTTON_LEFT -#define MAZEZAM_RIGHT BUTTON_RIGHT -#define MAZEZAM_SELECT BUTTON_SELECT - -#define MAZEZAM_RETRY BUTTON_SELECT -#define MAZEZAM_RETRY_KEYNAME "[PLAY]" -#define MAZEZAM_QUIT BUTTON_POWER -#define MAZEZAM_QUIT_KEYNAME "[POWER]" - -#else -#error No keymap defined! -#endif - -/* The gap for the border around the heading in text pages. In fact, 2 is - * really the only acceptable value. - */ -#define MAZEZAM_MENU_BORDER 2 -#define MAZEZAM_EXTRA_LIFE 2 /* get an extra life every _ levels */ -#define MAZEZAM_START_LIVES 3 /* how many lives at game start */ - -#ifdef HAVE_LCD_COLOR -#define MAZEZAM_HEADING_COLOR LCD_RGBPACK(255,255, 0) /* Yellow */ -#define MAZEZAM_BORDER_COLOR LCD_RGBPACK( 0, 0,255) /* Blue */ -#define MAZEZAM_TEXT_COLOR LCD_RGBPACK(255,255,255) /* White */ -#define MAZEZAM_BG_COLOR LCD_RGBPACK( 0, 0, 0) /* Black */ -#define MAZEZAM_WALL_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */ -#define MAZEZAM_PLAYER_COLOR LCD_RGBPACK(255,255,255) /* White */ -#define MAZEZAM_GATE_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */ - -/* the rows are coloured sequentially */ -#define MAZEZAM_NUM_CHUNK_COLORS 8 -static const unsigned chunk_colors[MAZEZAM_NUM_CHUNK_COLORS] = { - LCD_RGBPACK(255,192, 32), /* Orange */ - LCD_RGBPACK(255, 0, 0), /* Red */ - LCD_RGBPACK( 0,255, 0), /* Green */ - LCD_RGBPACK( 0,255,255), /* Cyan */ - LCD_RGBPACK(255,175,175), /* Pink */ - LCD_RGBPACK(255,255, 0), /* Yellow */ - LCD_RGBPACK( 0, 0,255), /* Blue */ - LCD_RGBPACK(255, 0,255), /* Magenta */ -}; - -#elif LCD_DEPTH > 1 - -#define MAZEZAM_HEADING_GRAY LCD_BLACK -#define MAZEZAM_BORDER_GRAY LCD_DARKGRAY -#define MAZEZAM_TEXT_GRAY LCD_BLACK -#define MAZEZAM_BG_GRAY LCD_WHITE -#define MAZEZAM_WALL_GRAY LCD_DARKGRAY -#define MAZEZAM_PLAYER_GRAY LCD_BLACK -#define MAZEZAM_GATE_GRAY LCD_BLACK -#define MAZEZAM_CHUNK_EDGE_GRAY LCD_BLACK - -#define MAZEZAM_NUM_CHUNK_GRAYS 2 -static const unsigned chunk_gray[MAZEZAM_NUM_CHUNK_GRAYS] = { - LCD_LIGHTGRAY, - LCD_DARKGRAY, -}; -/* darker version of the above */ -static const unsigned chunk_gray_shade[MAZEZAM_NUM_CHUNK_GRAYS] = { - LCD_DARKGRAY, - LCD_BLACK, -}; -#endif - -#define MAZEZAM_GAMEOVER_TEXT "Game Over" -#define MAZEZAM_GAMEOVER_DELAY (3 * HZ) / 2 -#define MAZEZAM_LEVEL_LIVES_TEXT "Level %d, Lives %d" -#define MAZEZAM_LEVEL_LIVES_DELAY HZ -#define MAZEZAM_WELLDONE_DELAY 4 * HZ - -/* The maximum number of lines that a text page can display. - * This must be 4 or less if the Archos recorder is to be - * supported. - */ -#define MAZEZAM_TEXT_MAXLINES 4 - -/* A structure for holding text pages */ -struct textpage { - /* Ensure 1 < num_lines <= MAZEZAM_TEXT_MAXLINES */ - short num_lines; - char *line[MAZEZAM_TEXT_MAXLINES]; /* text of lines */ -}; - -/* The text page for the welcome screen */ -static const struct textpage title_page = { - 4, - {"MazezaM", "play game", "instructions", "quit"} -}; - -/* The number of help screens */ -#define MAZEZAM_NUM_HELP_PAGES 4 - -/* The instruction screens */ -static const struct textpage help_page[] = { - {4,{"Instructions","10 mazezams","bar your way","to freedom"}}, - {4,{"Instructions","Push the rows","left and right","to escape"}}, - {4,{"Instructions","Press " MAZEZAM_RETRY_KEYNAME " to","retry a level", - "(lose 1 life)"}}, - {4,{"Instructions","Press " MAZEZAM_QUIT_KEYNAME,"to quit","the game"}} -}; - -/* the text of the screen that asks for a quit confirmation */ -static const struct textpage confirm_page = { - 4, - {"Quit","Are you sure?","yes","no"} -}; - -/* the text of the screen at the end of the game */ -static const struct textpage welldone_page = { - 3, - {"Well Done","You have","escaped",""} -}; - -/* the text of the screen asking if the user wants to - * resume or start a new game. - */ -static const struct textpage resume_page = { - 3, - {"Checkpoint", "continue", "new game"} -}; - -/* maximum height of a level */ -#define MAZEZAM_MAX_LINES 11 -/* maximum number of chunks on a line */ -#define MAZEZAM_MAX_CHUNKS 5 - -/* A structure for holding levels */ -struct mazezam_level { - short height; /* the number of lines */ - short width; /* the width */ - short entrance; /* the line on which the entrance lies */ - short exit; /* the line on which the exit lies */ - char *line[MAZEZAM_MAX_LINES]; /* the chunk data in string form */ -}; - -/* The number of levels. Note that the instruction screens reference this - * number - */ -#define MAZEZAM_NUM_LEVELS 10 - -/* The levels. In theory, they could be stored in a file so this data - * structure should not be accessed outside parse_level() - * - * These levels are copyright (C) 2002 Malcolm Tyrrell. They're - * probably covered by the GPL as they constitute part of the source - * code of this plugin, but you may distibute them seperately with - * other Free Software if you want. You can download them from: - * http://webpages.dcu.ie/~tyrrelma/MazezaM. - */ -static const struct mazezam_level level_data[MAZEZAM_NUM_LEVELS] = { - {2,7,0,0,{" $ $"," $ $$",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, - NULL}}, - {3,8,2,1,{" $ $$$"," $ $ $"," $ $ $",NULL,NULL,NULL,NULL,NULL,NULL, - NULL,NULL}}, - {4,14,1,3,{" $$$$$ $$ $$"," $$ $$ $$","$$ $ $$ $$$", - " $$$$$$$$ $",NULL,NULL,NULL,NULL,NULL,NULL,NULL}}, - {6,7,4,2,{" $"," $$$$"," $$$ $$"," $ $ $"," $ $$","$ $$", - NULL,NULL,NULL,NULL,NULL}}, - {6,13,0,0,{" $$$$$","$ $$$$$ $$$"," $ $$$ $$$$", - "$ $ $$$$$$$"," $$$ $ $$","$ $ $ $$ $",NULL,NULL, - NULL,NULL,NULL}}, - {11,5,10,0,{" $"," $ $$"," $$","$ $"," $ $"," $$$","$ $", - " $ $"," $ $","$ $$"," $"}}, - {7,16,0,6,{" $$$$$$$"," $$$$ $$$$ $ $","$$ $$ $$$$$$ $ $", - "$ $ $"," $$$$$$$$$$$$$$"," $ $$ $ $$$", - " $ $$$ $$",NULL,NULL,NULL,NULL}}, - {4,15,2,0,{" $$$$ $$$$ $$"," $ $$ $$ $ $$"," $ $$ $$$$ $$", - " $ $$ $$$$ $",NULL,NULL,NULL,NULL,NULL,NULL,NULL}}, - {7,9,6,2,{" $ $$$$"," $ $ $$"," $ $$$$ $","$ $$ $"," $ $$$", - " $$$$$$"," $",NULL,NULL,NULL,NULL}}, - {10,14,8,0,{" $"," $$$$$$$$$$ $"," $$$ $$", - " $ $$$$$$$$ $"," $$$ $$$ $$$"," $$$ $ $$$", - " $ $$$$$$$ $$"," $ $ $ $$$"," $$$$$$$$$$$$", - "",NULL}} -}; - -/* This is the data structure the game uses for managing levels */ -struct chunk_data { - /* the number of chunks on a line */ - short l_num[MAZEZAM_MAX_LINES]; - /* the width of a chunk */ - short c_width[MAZEZAM_MAX_LINES][MAZEZAM_MAX_CHUNKS]; - /* the inset of a chunk */ - short c_inset[MAZEZAM_MAX_LINES][MAZEZAM_MAX_CHUNKS]; -}; - -/* The state and exit code of the level loop */ -enum level_state { - LEVEL_STATE_LOOPING, - LEVEL_STATE_COMPLETED, - LEVEL_STATE_FAILED, - LEVEL_STATE_QUIT, - LEVEL_STATE_PARSE_ERROR, - LEVEL_STATE_USB_CONNECTED, -}; - -/* The state and exit code of the text screens. I use the - * same enum for all of them, even though there are some - * differences. - */ -enum text_state { - TEXT_STATE_LOOPING, - TEXT_STATE_QUIT, - TEXT_STATE_OKAY, - TEXT_STATE_USB_CONNECTED, - TEXT_STATE_PARSE_ERROR, - TEXT_STATE_BACK, -}; - -/* The state and exit code of the game loop */ -enum game_state { - GAME_STATE_LOOPING, - GAME_STATE_QUIT, - GAME_STATE_OKAY, - GAME_STATE_USB_CONNECTED, - GAME_STATE_OVER, - GAME_STATE_COMPLETED, - GAME_STATE_PARSE_ERROR, -}; - -/* The various constants needed for configuration files. - * See apps/plugins/lib/configfile.* - */ -#define MAZEZAM_CONFIG_FILENAME "mazezam.data" -#define MAZEZAM_CONFIG_NUM_ITEMS 1 -#define MAZEZAM_CONFIG_VERSION 0 -#define MAZEZAM_CONFIG_MINVERSION 0 -#define MAZEZAM_CONFIG_LEVELS_NAME "restart_level" - -/* A structure containing the data that is written to - * the configuration file - */ -struct resume_data { - int level; /* level at which to restart the game */ -}; - -/* Display a screen of text. line[0] is the heading. - * line[highlight] will be highlighted, unless highlight == 0 - */ -static void display_text_page(struct textpage text, int highlight) -{ - int w[text.num_lines], h[text.num_lines]; - int hsum,i,vgap,vnext; - - rb->lcd_clear_display(); - - /* find out how big the text is so we can determine the positioning */ - hsum = 0; - for(i = 0; i < text.num_lines; i++) { - rb->lcd_getstringsize(text.line[i], w+i, h+i); - hsum += h[i]; - } - - vgap = (LCD_HEIGHT-hsum)/(text.num_lines+1); - - /* The Heading */ - -#ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(MAZEZAM_BORDER_COLOR); -#elif LCD_DEPTH > 1 - rb->lcd_set_foreground(MAZEZAM_BORDER_GRAY); -#endif - rb->lcd_drawrect((LCD_WIDTH-w[0])/2-MAZEZAM_MENU_BORDER, - vgap-MAZEZAM_MENU_BORDER, w[0] + 2*MAZEZAM_MENU_BORDER, - h[0] + 2*MAZEZAM_MENU_BORDER); - rb->lcd_drawrect((LCD_WIDTH-w[0])/2-MAZEZAM_MENU_BORDER*2, - vgap-MAZEZAM_MENU_BORDER*2, w[0] + 4*MAZEZAM_MENU_BORDER, - h[0] + 4*MAZEZAM_MENU_BORDER); - rb->lcd_drawline(0,vgap + h[0]/2,(LCD_WIDTH-w[0])/2-MAZEZAM_MENU_BORDER*2, - vgap + h[0]/2); - rb->lcd_drawline((LCD_WIDTH-w[0])/2+w[0]+MAZEZAM_MENU_BORDER*2, - vgap + h[0]/2,LCD_WIDTH-1,vgap + h[0]/2); -#ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(MAZEZAM_HEADING_COLOR); -#elif LCD_DEPTH > 1 - rb->lcd_set_foreground(MAZEZAM_HEADING_GRAY); -#endif - rb->lcd_putsxy((LCD_WIDTH-w[0])/2,vgap,text.line[0]); - - vnext = vgap*2 + h[0]; - - /* The other lines */ - -#ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(MAZEZAM_TEXT_COLOR); -#elif LCD_DEPTH > 1 - rb->lcd_set_foreground(MAZEZAM_TEXT_GRAY); -#endif - for (i = 1; ilcd_putsxy((LCD_WIDTH-w[i])/2,vnext,text.line[i]); - - /* add underlining if i is the highlighted line */ - if (i == highlight) { - rb->lcd_drawline((LCD_WIDTH-w[i])/2, vnext + h[i] + 1, - (LCD_WIDTH-w[i])/2 + w[i], vnext + h[i] + 1); - } - - vnext += vgap + h[i]; - } - - rb->lcd_update(); -} - - -/* Parse the level data from the level_data structure. This could be - * replaced by a file read. Returns true if the level parsed correctly. - */ -static bool parse_level(short level, struct chunk_data *cd, - short *width, short *height, short *entrance, short *exit) -{ - int i,j; - char c,clast; - - *width = level_data[level].width; - *height = level_data[level].height; - *entrance = level_data[level].entrance; - *exit = level_data[level].exit; - - /* for each line in the level */ - for (i = 0; il_num[i] = 0; - clast = ' '; /* the character we last considered */ - while ((c = level_data[level].line[i][j]) != '\0') { - if (c != ' ') { - if (clast == ' ') { - cd->l_num[i] += 1; - if (cd->l_num[i] > MAZEZAM_MAX_CHUNKS) - return false; - cd->c_inset[i][cd->l_num[i] - 1] = j; - cd->c_width[i][cd->l_num[i] - 1] = 1; - } - else - cd->c_width[i][cd->l_num[i] - 1] += 1; - } - clast = c; - j++; - } - } - } - return true; -} - -/* Draw the level */ -static void draw_level( - struct chunk_data *cd, /* the data about the chunks */ - short *shift, /* an array of the horizontal offset of the lines */ - short width, - short height, - short entrance, - short exit, - short x, /* player's x and y coords */ - short y) -{ - /* The number of pixels the side of a square should be */ - short size = (LCD_WIDTH/(width+2)) < (LCD_HEIGHT/height) ? - (LCD_WIDTH/(width+2)) : (LCD_HEIGHT/height); - /* The x and y position (in pixels) of the top left corner of the - * level - */ - short xOff = (LCD_WIDTH - (size*width))/2; - short yOff = (LCD_HEIGHT - (size*height))/2; - /* For drawing the player, taken from the sokoban plugin */ - short max = size - 1; - short middle = max / 2; - short ldelta = (middle + 1) / 2; - short i,j; - short third = size / 3; - short twothirds = (2 * size) / 3; -#ifndef HAVE_LCD_COLOR - /* We #def these out to supress a compiler warning */ - short k; -#if LCD_DEPTH <= 1 - short l; -#endif -#endif - - rb->lcd_clear_display(); - -#ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(MAZEZAM_WALL_COLOR); -#elif LCD_DEPTH > 1 - rb->lcd_set_foreground(MAZEZAM_WALL_GRAY); -#endif - /* draw the upper wall */ - rb->lcd_fillrect(0,0,xOff,yOff+(size*entrance)); - rb->lcd_fillrect(xOff,0,size*width,yOff); - rb->lcd_fillrect(xOff+(size*width),0,LCD_WIDTH-xOff-(size*width), - yOff+(size*exit)); - - /* draw the lower wall */ - rb->lcd_fillrect(0,yOff+(size*entrance)+size,xOff, - LCD_HEIGHT-yOff-(size*entrance)-size); - rb->lcd_fillrect(xOff,yOff+(size*height),size*width, - LCD_HEIGHT-yOff-(size*height)); - /* Note: the exit is made one pixel thinner than necessary as a visual - * clue that chunks cannot be pushed into it - */ - rb->lcd_fillrect(xOff+(size*width),yOff+(size*exit)+size-1, - LCD_WIDTH-xOff+(size*width), - LCD_HEIGHT-yOff-(size*exit)-size+1); - - /* draw the chunks */ - for (i = 0; ilcd_set_foreground(chunk_colors[(i+width) % - MAZEZAM_NUM_CHUNK_COLORS]); -#endif - for (j = 0; jl_num[i]; j++) { -#ifdef HAVE_LCD_COLOR - rb->lcd_fillrect(xOff+size*shift[i]+size*cd->c_inset[i][j], - yOff+size*i, cd->c_width[i][j]*size,size); -#elif LCD_DEPTH > 1 - rb->lcd_set_foreground(MAZEZAM_CHUNK_EDGE_GRAY); - rb->lcd_drawrect(xOff+size*shift[i]+size*cd->c_inset[i][j], - yOff+size*i, cd->c_width[i][j]*size,size); - - /* draw shade */ - rb->lcd_set_foreground(chunk_gray_shade[(i+width) % - MAZEZAM_NUM_CHUNK_GRAYS]); - rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+1, - yOff+size*i+size-2, - xOff+size*shift[i]+size*cd->c_inset[i][j]+ - cd->c_width[i][j]*size-3, - yOff+size*i+size-2); - rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+ - cd->c_width[i][j]*size-2, - yOff+size*i, - xOff+size*shift[i]+size*cd->c_inset[i][j]+ - cd->c_width[i][j]*size-2, - yOff+size*i+size-2); - - /* draw fill */ - rb->lcd_set_foreground(chunk_gray[(i+width) % - MAZEZAM_NUM_CHUNK_GRAYS]); - for (k = yOff+size*i+2; k < yOff+size*i+size-2; k += 2) - rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+2,k, - xOff+size*shift[i]+size*cd->c_inset[i][j]+ - cd->c_width[i][j]*size-3,k); -#else - rb->lcd_drawrect(xOff+size*shift[i]+size*cd->c_inset[i][j], - yOff+size*i, cd->c_width[i][j]*size,size); - for (k = xOff+size*shift[i]+size*cd->c_inset[i][j]+2; - k < xOff+size*shift[i]+size*cd->c_inset[i][j]+ - cd->c_width[i][j]*size; - k += 2 + (i & 1)) - for (l = yOff+size*i+2; l < yOff+size*i+size; l += 2 + (i & 1)) - rb->lcd_drawpixel(k, l); -#endif - } - } - - /* draw the player (mostly copied from the sokoban plugin) */ -#ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(MAZEZAM_PLAYER_COLOR); -#elif LCD_DEPTH > 1 - rb->lcd_set_foreground(MAZEZAM_PLAYER_GRAY); -#endif - rb->lcd_drawline(xOff+size*x, yOff+size*y+middle, - xOff+size*x+max, yOff+size*y+middle); - rb->lcd_drawline(xOff+size*x+middle, yOff+size*y, - xOff+size*x+middle, yOff+size*y+max-ldelta); - rb->lcd_drawline(xOff+size*x+middle, yOff+size*y+max-ldelta, - xOff+size*x+middle-ldelta, yOff+size*y+max); - rb->lcd_drawline(xOff+size*x+middle, yOff+size*y+max-ldelta, - xOff+size*x+middle+ldelta, yOff+size*y+max); - - /* draw the gate, if the player has moved into the level */ - if (x >= 0) { -#ifdef HAVE_LCD_COLOR - rb->lcd_set_foreground(MAZEZAM_GATE_COLOR); -#elif LCD_DEPTH > 1 - rb->lcd_set_foreground(MAZEZAM_GATE_GRAY); -#endif - rb->lcd_drawline(xOff-size,yOff+entrance*size+third, - xOff-1,yOff+entrance*size+third); - rb->lcd_drawline(xOff-size,yOff+entrance*size+twothirds, - xOff-1,yOff+entrance*size+twothirds); - rb->lcd_drawline(xOff-size+third,yOff+entrance*size, - xOff-size+third,yOff+entrance*size+size-1); - rb->lcd_drawline(xOff-size+twothirds,yOff+entrance*size, - xOff-size+twothirds,yOff+entrance*size+size-1); - } -} - -/* Manage the congratulations screen */ -static enum text_state welldone_screen(void) -{ - int button = BUTTON_NONE; - enum text_state state = TEXT_STATE_LOOPING; - - display_text_page(welldone_page, 0); - - while (state == TEXT_STATE_LOOPING) { - button = rb->button_get(true); - - switch (button) { - case MAZEZAM_QUIT: - state = TEXT_STATE_QUIT; - break; - - case MAZEZAM_SELECT: -#if CONFIG_KEYPAD != ONDIO_PAD - case MAZEZAM_RIGHT: -#endif - state = TEXT_STATE_OKAY; - break; - - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - state = TEXT_STATE_USB_CONNECTED; - break; - } - } - - return state; -} - -/* Manage the quit confimation screen */ -static enum text_state quitconfirm_loop(void) -{ - int button = BUTTON_NONE; - enum text_state state = TEXT_STATE_LOOPING; - short select = 2; - - display_text_page(confirm_page, select + 1); - - /* Wait for a button release. This is useful when a repeated button - * press is used for quit. - */ - while ((rb->button_get(true) & BUTTON_REL) != BUTTON_REL); - - while (state == TEXT_STATE_LOOPING) { - display_text_page(confirm_page, select + 1); - - button = rb->button_get(true); - - switch (button) { - case MAZEZAM_QUIT: - state = TEXT_STATE_QUIT; - break; - - case MAZEZAM_UP: - case MAZEZAM_DOWN: - select = (2 - select) + 1; - break; - - case MAZEZAM_SELECT: -#if CONFIG_KEYPAD != ONDIO_PAD - case MAZEZAM_RIGHT: -#endif - if (select == 1) - state = TEXT_STATE_QUIT; - else - state = TEXT_STATE_OKAY; - break; - - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - state = TEXT_STATE_USB_CONNECTED; - break; - } - } - - return state; -} - -/* Manage the playing of a level */ -static enum level_state level_loop(short level, short lives) -{ - struct chunk_data cd; - short shift[MAZEZAM_MAX_LINES]; /* amount each line has been shifted */ - short width; - short height; - short entrance; - short exit; - short i; - short x,y; - int button; - enum level_state state = LEVEL_STATE_LOOPING; - bool blocked; /* is there a chunk in the way of the player? */ - - if (!(parse_level(level,&cd,&width,&height,&entrance,&exit))) - return LEVEL_STATE_PARSE_ERROR; - - for (i = 0; i < height; i++) - shift[i] = 0; - - x = -1; - y = entrance; - - draw_level(&cd, shift, width, height, entrance, exit, x, y); - -#ifdef HAVE_REMOTE_LCD - /* Splash text seems to use the remote display by - * default. I suppose I better keep it tidy! - */ - rb->lcd_remote_clear_display(); -#endif - rb->splash(MAZEZAM_LEVEL_LIVES_DELAY, MAZEZAM_LEVEL_LIVES_TEXT, - level+1, lives); - - /* ensure keys pressed during the splash screen are ignored */ - rb->button_clear_queue(); - - while (state == LEVEL_STATE_LOOPING) { - draw_level(&cd, shift, width, height, entrance, exit, x, y); - rb->lcd_update(); - button = rb->button_get(true); - blocked = false; - - switch (button) { - case MAZEZAM_UP: - case MAZEZAM_UP | BUTTON_REPEAT: - if ((y > 0) && (x >= 0) && (x < width)) { - for (i = 0; i < cd.l_num[y-1]; i++) - blocked = blocked || - ((x>=shift[y-1]+cd.c_inset[y-1][i]) && - (x= 0) && (x < width)) { - for (i = 0; i < cd.l_num[y+1]; i++) - blocked = blocked || - ((x>=shift[y+1]+cd.c_inset[y+1][i]) && - (x 0) { - for (i = 0; i < cd.l_num[y]; i++) - blocked = blocked || - (x == shift[y]+cd.c_inset[y][i]+ - cd.c_width[y][i]); - if (!blocked) x -= 1; - else if (shift[y] + cd.c_inset[y][0] > 0) { - x -= 1; - shift[y] -= 1; - } - } - break; - - case MAZEZAM_RIGHT: - case MAZEZAM_RIGHT | BUTTON_REPEAT: - if (x < width-1) { - for (i = 0; i < cd.l_num[y]; i++) - blocked = blocked || (x+1 == shift[y]+cd.c_inset[y][i]); - if (!blocked) x += 1; - else if (shift[y] + cd.c_inset[y][cd.l_num[y]-1] + - cd.c_width[y][cd.l_num[y]-1] < width) { - x += 1; - shift[y] += 1; - } - } - else if (x == width) state = LEVEL_STATE_COMPLETED; - else if (y == exit) x += 1; - break; - - case MAZEZAM_RETRY: - state = LEVEL_STATE_FAILED; - break; - - case MAZEZAM_QUIT: - switch (quitconfirm_loop()) { - case TEXT_STATE_QUIT: - state = LEVEL_STATE_QUIT; - break; - - case TEXT_STATE_USB_CONNECTED: - state = LEVEL_STATE_USB_CONNECTED; - break; - - default: - break; - } - break; - - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - state = LEVEL_STATE_USB_CONNECTED; - break; - } - } - - return state; -} - -/* The loop which manages a full game of MazezaM */ -static enum game_state game_loop(struct resume_data *r) -{ - enum game_state state = GAME_STATE_LOOPING; - int level = r->level; - int lives = MAZEZAM_START_LIVES; - - rb->lcd_clear_display(); - - while (state == GAME_STATE_LOOPING) - { - switch (level_loop(level,lives)) { - case LEVEL_STATE_COMPLETED: - level += 1; - if (!((level - r->level) % MAZEZAM_EXTRA_LIFE)) - lives += 1; - break; - - case LEVEL_STATE_QUIT: - state = GAME_STATE_QUIT; - break; - - case LEVEL_STATE_FAILED: - lives -= 1; - break; - - case LEVEL_STATE_PARSE_ERROR: - state = GAME_STATE_PARSE_ERROR; - break; - - case LEVEL_STATE_USB_CONNECTED: - state = GAME_STATE_USB_CONNECTED; - break; - - default: - break; - } - if (lives == 0) - state = GAME_STATE_OVER; - else if (level == MAZEZAM_NUM_LEVELS) - state = GAME_STATE_COMPLETED; - } - - switch (state) { - case GAME_STATE_OVER: -#ifdef HAVE_REMOTE_LCD - /* Splash text seems to use the remote display by - * default. I suppose I better keep it tidy! - */ - rb->lcd_remote_clear_display(); -#endif - rb->splash(MAZEZAM_GAMEOVER_DELAY, MAZEZAM_GAMEOVER_TEXT); - break; - - case GAME_STATE_COMPLETED: - switch (welldone_screen()) { - case TEXT_STATE_QUIT: - state = GAME_STATE_QUIT; - break; - - case TEXT_STATE_USB_CONNECTED: - state = GAME_STATE_USB_CONNECTED; - break; - - default: - state = GAME_STATE_OKAY; - break; - } - break; - - default: - break; - } - - /* This particular resume game logic is designed to make - * players prove they can solve a level more than once - */ - if (level > r->level + 1) - r->level += 1; - - return state; -} - -/* Manage the instruction screen */ -static enum text_state instruction_loop(void) -{ - int button; - enum text_state state = TEXT_STATE_LOOPING; - int page = 0; - - while (state == TEXT_STATE_LOOPING) { - display_text_page(help_page[page], 0); - button = rb->button_get(true); - - switch (button) { - case MAZEZAM_LEFT: - page -= 1; - break; - - case MAZEZAM_SELECT: -#if CONFIG_KEYPAD != ONDIO_PAD - case MAZEZAM_RIGHT: -#endif - page += 1; - break; - - case MAZEZAM_QUIT: - state = TEXT_STATE_QUIT; - break; - - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - state = TEXT_STATE_USB_CONNECTED; - break; - - } - - if ((page < 0) || (page >= MAZEZAM_NUM_HELP_PAGES)) - state = TEXT_STATE_OKAY; - } - - return state; -} - -/* Manage the text screen that offers the user the option of - * resuming or starting a new game - */ -static enum text_state resume_game_loop (struct resume_data *r) -{ - int button = BUTTON_NONE; - enum text_state state = TEXT_STATE_LOOPING; - short select = 0; - - /* if the resume level is 0, don't bother asking */ - if (r->level == 0) return TEXT_STATE_OKAY; - - display_text_page(resume_page, select + 1); - - while (state == TEXT_STATE_LOOPING) { - display_text_page(resume_page, select + 1); - - button = rb->button_get(true); - - switch (button) { - case MAZEZAM_QUIT: - state = TEXT_STATE_QUIT; - break; - - case MAZEZAM_LEFT: - state = TEXT_STATE_BACK; - break; - - case MAZEZAM_UP: - case MAZEZAM_DOWN: - select = 1 - select; - break; - - case MAZEZAM_SELECT: -#if CONFIG_KEYPAD != ONDIO_PAD - case MAZEZAM_RIGHT: -#endif - if (select == 1) { - /* The player wants to play a new game. I could ask - * for confirmation here, but the only penalty is - * playing through some already completed levels, - * so I don't think it's necessary - */ - r->level = 0; - } - state = TEXT_STATE_OKAY; - break; - - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - state = TEXT_STATE_USB_CONNECTED; - break; - } - } - - return state; -} - -/* Load the resume data from the config file. The data is - * stored in both r and old. - */ -static void resume_load_data (struct resume_data *r, struct resume_data *old) -{ - struct configdata config[] = { - {TYPE_INT,0,MAZEZAM_NUM_LEVELS-1,&(r->level), - MAZEZAM_CONFIG_LEVELS_NAME,NULL,NULL} - }; - - if (configfile_load(MAZEZAM_CONFIG_FILENAME,config,MAZEZAM_CONFIG_NUM_ITEMS, - MAZEZAM_CONFIG_VERSION) < 0) - r->level = 0; - /* an extra precaution */ - else if ((r->level < 0) || (MAZEZAM_NUM_LEVELS <= r->level)) - r->level = 0; - - old->level = r->level; -} - -/* Save the resume data in the config file, but only if necessary */ -static void resume_save_data (struct resume_data *r, struct resume_data *old) -{ - struct configdata config[] = { - {TYPE_INT,0,MAZEZAM_NUM_LEVELS-1,&(r->level), - MAZEZAM_CONFIG_LEVELS_NAME,NULL,NULL} - }; - - /* To reduce disk usage, only write the file if the resume data has - * changed. - */ - if (old->level != r->level) - configfile_save(MAZEZAM_CONFIG_FILENAME,config,MAZEZAM_CONFIG_NUM_ITEMS, - MAZEZAM_CONFIG_MINVERSION); -} - -/* The loop which manages the welcome screen and menu */ -static enum text_state welcome_loop(void) -{ - int button; - short select = 0; - enum text_state state = TEXT_STATE_LOOPING; - struct resume_data r_data, old_data; - - /* Load data */ - resume_load_data(&r_data, &old_data); - - while (state == TEXT_STATE_LOOPING) { - display_text_page(title_page, select + 1); - button = rb->button_get(true); - - switch (button) { - case MAZEZAM_QUIT: - state = TEXT_STATE_QUIT; - break; - - case MAZEZAM_UP: - select = (select + (title_page.num_lines - 2)) % - (title_page.num_lines - 1); - break; - - case MAZEZAM_DOWN: - select = (select + 1) % (title_page.num_lines - 1); - break; - - case MAZEZAM_SELECT: -#if CONFIG_KEYPAD != ONDIO_PAD - case MAZEZAM_RIGHT: -#endif - if (select == 0) { /* play game */ - switch (resume_game_loop(&r_data)) { - case TEXT_STATE_QUIT: - state = TEXT_STATE_QUIT; - break; - - case TEXT_STATE_USB_CONNECTED: - state = TEXT_STATE_USB_CONNECTED; - break; - - case TEXT_STATE_BACK: - break; - - default: { /* Ouch! This nesting is too deep! */ - switch (game_loop(&r_data)) { - case GAME_STATE_QUIT: - state = TEXT_STATE_QUIT; - break; - - case GAME_STATE_USB_CONNECTED: - state = TEXT_STATE_USB_CONNECTED; - break; - - case GAME_STATE_PARSE_ERROR: - state = TEXT_STATE_PARSE_ERROR; - break; - - default: - break; - } - break; - } - } - } - else if (select == 1) { /* Instructions */ - switch (instruction_loop()) { - case TEXT_STATE_QUIT: - state = TEXT_STATE_QUIT; - break; - - case TEXT_STATE_USB_CONNECTED: - state = TEXT_STATE_USB_CONNECTED; - break; - - default: - break; - } - } - else /* Quit */ - state = TEXT_STATE_QUIT; - - break; - - default: - if (rb->default_event_handler(button) == SYS_USB_CONNECTED) - state = TEXT_STATE_USB_CONNECTED; - break; - } - } - - /* I'm not sure if it's appropriate to write to disk on USB events. - * Currently, I do so. - */ - resume_save_data(&r_data, &old_data); - - return state; -} - -/* Plugin entry point */ -enum plugin_status plugin_start(struct plugin_api* api, void* parameter) -{ - enum plugin_status state; - - /* Usual plugin stuff */ - (void)parameter; - rb = api; - - /* Turn off backlight timeout */ - backlight_force_on(rb); /* backlight control in lib/helper.c */ - -#ifdef HAVE_LCD_COLOR - rb->lcd_set_background(MAZEZAM_BG_COLOR); - rb->lcd_set_backdrop(NULL); -#elif LCD_DEPTH > 1 - rb->lcd_set_background(MAZEZAM_BG_GRAY); -#endif - rb->lcd_setfont(FONT_SYSFIXED); - - /* initialise the config file module */ - configfile_init(rb); - - switch (welcome_loop()) { - case TEXT_STATE_USB_CONNECTED: - state = PLUGIN_USB_CONNECTED; - break; - - case TEXT_STATE_PARSE_ERROR: - state = PLUGIN_ERROR; - break; - - default: - state = PLUGIN_OK; - break; - } - - /* Turn on backlight timeout (revert to settings) */ - backlight_use_settings(rb); /* backlight control in lib/helper.c */ - - return state; -} +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2006, 2008 Malcolm Tyrrell + * + * MazezaM - a Rockbox version of my ZX Spectrum game from 2002 + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "plugin.h" +#include "configfile.h" +#include "helper.h" +#include "pluginlib_actions.h" +#include "playback_control.h" + +/* Include standard plugin macro */ +PLUGIN_HEADER + +static struct plugin_api* rb; + +/* The plugin actions of interest. */ +const struct button_mapping *plugin_contexts[] += {generic_directions, generic_actions}; + +MEM_FUNCTION_WRAPPERS(rb); + +/* Use the standard plugin buttons rather than a hard-to-maintain list of + * MazezaM specific buttons. */ +#define MAZEZAM_UP PLA_UP +#define MAZEZAM_UP_REPEAT PLA_UP_REPEAT +#define MAZEZAM_DOWN PLA_DOWN +#define MAZEZAM_DOWN_REPEAT PLA_DOWN_REPEAT +#define MAZEZAM_LEFT PLA_LEFT +#define MAZEZAM_LEFT_REPEAT PLA_LEFT_REPEAT +#define MAZEZAM_RIGHT PLA_RIGHT +#define MAZEZAM_RIGHT_REPEAT PLA_RIGHT_REPEAT +#define MAZEZAM_MENU PLA_QUIT + +/* All the text is here */ +#define MAZEZAM_TEXT_GAME_OVER "Game Over" +#define MAZEZAM_TEXT_LIVES "Level %d, Lives %d" +#define MAZEZAM_TEXT_CHECKPOINT "Checkpoint reached" +#define MAZEZAM_TEXT_WELLDONE_TITLE "You have escaped!" +#define MAZEZAM_TEXT_WELLDONE_OPTION "Goodbye" +#define MAZEZAM_TEXT_MAZEZAM_MENU "MazezaM Menu" +#define MAZEZAM_TEXT_RETRY_LEVEL "Retry level" +#define MAZEZAM_TEXT_AUDIO_PLAYBACK "Audio playback" +#define MAZEZAM_TEXT_QUIT "Quit" +#define MAZEZAM_TEXT_BACK "Return" +#define MAZEZAM_TEXT_MAIN_MENU "MazezaM" +#define MAZEZAM_TEXT_CONTINUE "Play from checkpoint" +#define MAZEZAM_TEXT_PLAY_GAME "Play game" +#define MAZEZAM_TEXT_PLAY_NEW_GAME "Play new game" + +#define MAZEZAM_START_LIVES 3 /* how many lives at game start */ +#define MAZEZAM_FIRST_CHECKPOINT 3 /* The level at the first checkpoint */ +#define MAZEZAM_CHECKPOINT_INTERVAL 4 /* A checkpoint every _ levels */ + +#ifdef HAVE_LCD_COLOR +#define MAZEZAM_HEADING_COLOR LCD_RGBPACK(255,255, 0) /* Yellow */ +#define MAZEZAM_BORDER_COLOR LCD_RGBPACK( 0, 0,255) /* Blue */ +#define MAZEZAM_COLOR LCD_RGBPACK(255,255,255) /* White */ +#define MAZEZAM_BG_COLOR LCD_RGBPACK( 0, 0, 0) /* Black */ +#define MAZEZAM_WALL_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */ +#define MAZEZAM_PLAYER_COLOR LCD_RGBPACK(255,255,255) /* White */ +#define MAZEZAM_GATE_COLOR LCD_RGBPACK(100,100,100) /* Dark gray */ + +/* the rows are coloured sequentially */ +#define MAZEZAM_NUM_CHUNK_COLORS 8 +static const unsigned chunk_colors[MAZEZAM_NUM_CHUNK_COLORS] = { + LCD_RGBPACK(255,192, 32), /* Orange */ + LCD_RGBPACK(255, 0, 0), /* Red */ + LCD_RGBPACK( 0,255, 0), /* Green */ + LCD_RGBPACK( 0,255,255), /* Cyan */ + LCD_RGBPACK(255,175,175), /* Pink */ + LCD_RGBPACK(255,255, 0), /* Yellow */ + LCD_RGBPACK( 0, 0,255), /* Blue */ + LCD_RGBPACK(255, 0,255), /* Magenta */ +}; + +#elif LCD_DEPTH > 1 + +#define MAZEZAM_HEADING_GRAY LCD_BLACK +#define MAZEZAM_BORDER_GRAY LCD_DARKGRAY +#define MAZEZAM_GRAY LCD_BLACK +#define MAZEZAM_BG_GRAY LCD_WHITE +#define MAZEZAM_WALL_GRAY LCD_DARKGRAY +#define MAZEZAM_PLAYER_GRAY LCD_BLACK +#define MAZEZAM_GATE_GRAY LCD_BLACK +#define MAZEZAM_CHUNK_EDGE_GRAY LCD_BLACK + +#define MAZEZAM_NUM_CHUNK_GRAYS 2 +static const unsigned chunk_gray[MAZEZAM_NUM_CHUNK_GRAYS] = { + LCD_LIGHTGRAY, + LCD_DARKGRAY, +}; +/* darker version of the above */ +static const unsigned chunk_gray_shade[MAZEZAM_NUM_CHUNK_GRAYS] = { + LCD_DARKGRAY, + LCD_BLACK, +}; +#endif + +#define MAZEZAM_DELAY_CHECKPOINT HZ +#define MAZEZAM_DELAY_LIVES HZ +#define MAZEZAM_DELAY_GAME_OVER (3 * HZ) / 2 + +/* maximum height of a level */ +#define MAZEZAM_MAX_LINES 11 +/* maximum number of chunks on a line */ +#define MAZEZAM_MAX_CHUNKS 5 + +/* A structure for storing level data in unparsed form */ +struct mazezam_level { + short height; /* the number of lines */ + short width; /* the width */ + short entrance; /* the line on which the entrance lies */ + short exit; /* the line on which the exit lies */ + char *line[MAZEZAM_MAX_LINES]; /* the chunk data in string form */ +}; + +/* The number of levels. */ +#define MAZEZAM_NUM_LEVELS 10 + +/* The levels. In theory, they could be stored in a file so this data + * structure should not be accessed outside parse_level() + * + * These levels are copyright (C) 2002 Malcolm Tyrrell. They're + * probably covered by the GPL as they constitute part of the source + * code of this plugin, but you may distibute them seperately with + * other Free Software if you want. You can download them from: + * http://webpages.dcu.ie/~tyrrelma/MazezaM. + */ +static const struct mazezam_level level_data[MAZEZAM_NUM_LEVELS] = { + {2,7,0,0,{" $ $"," $ $$"}}, + {3,8,2,1,{" $ $$$"," $ $ $"," $ $ $"}}, + {4,14,1,3,{" $$$$$ $$ $$"," $$ $$ $$","$$ $ $$ $$$", + " $$$$$$$$ $"}}, + {6,7,4,2,{" $"," $$$$"," $$$ $$"," $ $ $"," $ $$","$ $$"}}, + {6,13,0,0,{" $$$$$","$ $$$$$ $$$"," $ $$$ $$$$", + "$ $ $$$$$$$"," $$$ $ $$","$ $ $ $$ $"}}, + {11,5,10,0,{" $"," $ $$"," $$","$ $"," $ $"," $$$","$ $", + " $ $"," $ $","$ $$"," $"}}, + {7,16,0,6,{" $$$$$$$"," $$$$ $$$$ $ $","$$ $$ $$$$$$ $ $", + "$ $ $"," $$$$$$$$$$$$$$"," $ $$ $ $$$", + " $ $$$ $$"}}, + {4,15,2,0,{" $$$$ $$$$ $$"," $ $$ $$ $ $$"," $ $$ $$$$ $$", + " $ $$ $$$$ $"}}, + {7,9,6,2,{" $ $$$$"," $ $ $$"," $ $$$$ $","$ $$ $"," $ $$$", + " $$$$$$"," $"}}, + {10,14,8,0,{" $"," $$$$$$$$$$ $"," $$$ $$", + " $ $$$$$$$$ $"," $$$ $$$ $$$"," $$$ $ $$$", + " $ $$$$$$$ $$"," $ $ $ $$$"," $$$$$$$$$$$$", + ""}} +}; + +/* This data structure which holds information about the rows */ +struct chunk_data { + /* the number of chunks on a line */ + short l_num[MAZEZAM_MAX_LINES]; + /* the width of a chunk */ + short c_width[MAZEZAM_MAX_LINES][MAZEZAM_MAX_CHUNKS]; + /* the inset of a chunk */ + short c_inset[MAZEZAM_MAX_LINES][MAZEZAM_MAX_CHUNKS]; +}; + +/* Parsed level data */ +struct level_info { + short width; + short height; + short entrance; + short exit; + struct chunk_data cd; +}; + +/* The state variable used to hold the state of the plugin */ +static enum { + STATE_QUIT, /* The player wants to quit */ + STATE_USB_CONNECTED, /* A USB cable has been inserted */ + STATE_PARSE_ERROR, /* There's a parse error in the levels */ + STATE_WELLDONE, /* The player has finished the game */ + + STATE_IN_APPLICATION, + + STATE_MAIN_MENU /* The player is at the main menu */ + = STATE_IN_APPLICATION, + STATE_GAME_OVER, /* The player is out of lives */ + + STATE_IN_GAME, + + STATE_COMPLETED /* A level has been completed */ + = STATE_IN_GAME, + + STATE_FAILED, /* The player wants to retry the level */ + STATE_GAME_MENU, /* The player wan't to access the in-game menu */ + + STATE_IN_LEVEL, +} state; + +/* The various constants needed for configuration files. + * See apps/plugins/lib/configfile.* + */ +#define MAZEZAM_CONFIG_FILENAME "mazezam.data" +#define MAZEZAM_CONFIG_NUM_ITEMS 1 +#define MAZEZAM_CONFIG_VERSION 0 +#define MAZEZAM_CONFIG_MINVERSION 0 +#define MAZEZAM_CONFIG_LEVELS_NAME "restart_level" + +/* A structure containing the data that is written to + * the configuration file + */ +struct resume_data { + int level; /* level at which to restart the game */ +}; + +#if LCD_DEPTH > 1 +/* Store the display settings so they are reintroduced during menus */ +static struct { + fb_data* backdrop; + unsigned foreground; + unsigned background; +} lcd_settings; +#endif + +/***************************************************************************** +* Store the LCD settings +******************************************************************************/ +static void store_lcd_settings(void) +{ + /* Store the old settings */ +#if LCD_DEPTH > 1 + lcd_settings.backdrop = rb->lcd_get_backdrop(); + lcd_settings.foreground = rb->lcd_get_foreground(); + lcd_settings.background = rb->lcd_get_background(); +#endif +} + +/***************************************************************************** +* Restore the LCD settings to their defaults +******************************************************************************/ +static void restore_lcd_settings(void) { + /* Turn on backlight timeout (revert to settings) */ + backlight_use_settings(rb); /* backlight control in lib/helper.c */ + + /* Restore the old settings */ +#if LCD_DEPTH > 1 + rb->lcd_set_foreground(lcd_settings.foreground); + rb->lcd_set_background(lcd_settings.background); + rb->lcd_set_backdrop(lcd_settings.backdrop); +#endif +} + +/***************************************************************************** +* Adjust the LCD settings to suit MazezaM levels +******************************************************************************/ +static void plugin_lcd_settings(void) { + /* Turn off backlight timeout */ + backlight_force_on(rb); /* backlight control in lib/helper.c */ + + /* Set the new settings */ +#ifdef HAVE_LCD_COLOR + rb->lcd_set_background(MAZEZAM_BG_COLOR); + rb->lcd_set_backdrop(NULL); +#elif LCD_DEPTH > 1 + rb->lcd_set_background(MAZEZAM_BG_GRAY); + rb->lcd_set_backdrop(NULL); +#endif +} + +/***************************************************************************** +* Parse the level data from the level_data structure. This could be +* replaced by a file read. Returns true if the level parsed correctly. +******************************************************************************/ +static bool parse_level(short level, struct level_info* li) +{ + int i,j; + char c,clast; + + li->width = level_data[level].width; + li->height = level_data[level].height; + li->entrance = level_data[level].entrance; + li->exit = level_data[level].exit; + + /* for each line in the level */ + for (i = 0; icd.l_num[i] = 0; + clast = ' '; /* the character we last considered */ + while ((c = level_data[level].line[i][j]) != '\0') { + if (c != ' ') { + if (clast == ' ') { + li->cd.l_num[i] += 1; + if (li->cd.l_num[i] > MAZEZAM_MAX_CHUNKS) + return false; + li->cd.c_inset[i][li->cd.l_num[i] - 1] = j; + li->cd.c_width[i][li->cd.l_num[i] - 1] = 1; + } + else + li->cd.c_width[i][li->cd.l_num[i] - 1] += 1; + } + clast = c; + j++; + } + } + } + return true; +} + +/***************************************************************************** +* Draw the walls of a level +******************************************************************************/ +static void draw_walls( + short size, + short xOff, + short yOff, + short width, + short height, + short entrance, + short exit) +{ +#ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(MAZEZAM_WALL_COLOR); +#elif LCD_DEPTH > 1 + rb->lcd_set_foreground(MAZEZAM_WALL_GRAY); +#endif + /* draw the upper wall */ + rb->lcd_fillrect(0,0,xOff,yOff+(size*entrance)); + rb->lcd_fillrect(xOff,0,size*width,yOff); + rb->lcd_fillrect(xOff+(size*width),0,LCD_WIDTH-xOff-(size*width), + yOff+(size*exit)); + + /* draw the lower wall */ + rb->lcd_fillrect(0,yOff+(size*entrance)+size,xOff, + LCD_HEIGHT-yOff-(size*entrance)-size); + rb->lcd_fillrect(xOff,yOff+(size*height),size*width, + LCD_HEIGHT-yOff-(size*height)); + /* Note: the exit is made one pixel thinner than necessary as a visual + * clue that chunks cannot be pushed into it + */ + rb->lcd_fillrect(xOff+(size*width),yOff+(size*exit)+size-1, + LCD_WIDTH-xOff+(size*width), + LCD_HEIGHT-yOff-(size*exit)-size+1); +} + +/***************************************************************************** +* Draw chunk row i +******************************************************************************/ +static void draw_row( + short size, + short xOff, + short yOff, + short width, + short i, /* the row number */ + struct chunk_data *cd, /* the data about the chunks */ + short *shift /* an array of the horizontal offset of the lines */ +) +{ + /* The assignment below is just a hack to make supress a warning on + * non color targets */ + short j = width; +#ifndef HAVE_LCD_COLOR + /* We #def these out to supress a compiler warning */ + short k; +#if LCD_DEPTH <= 1 + short l; +#endif +#endif +#ifdef HAVE_LCD_COLOR + /* adding width to i should have a fixed, but randomising effect on + * the choice of the colours of the top line of chunks + */ + rb->lcd_set_foreground(chunk_colors[(i+width) % + MAZEZAM_NUM_CHUNK_COLORS]); +#endif + for (j = 0; jl_num[i]; j++) { +#ifdef HAVE_LCD_COLOR + rb->lcd_fillrect(xOff+size*shift[i]+size*cd->c_inset[i][j], + yOff+size*i, cd->c_width[i][j]*size,size); +#elif LCD_DEPTH > 1 + rb->lcd_set_foreground(MAZEZAM_CHUNK_EDGE_GRAY); + rb->lcd_drawrect(xOff+size*shift[i]+size*cd->c_inset[i][j], + yOff+size*i, cd->c_width[i][j]*size,size); + + /* draw shade */ + rb->lcd_set_foreground(chunk_gray_shade[(i+width) % + MAZEZAM_NUM_CHUNK_GRAYS]); + rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+1, + yOff+size*i+size-2, + xOff+size*shift[i]+size*cd->c_inset[i][j]+ + cd->c_width[i][j]*size-3, + yOff+size*i+size-2); + rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+ + cd->c_width[i][j]*size-2, + yOff+size*i, + xOff+size*shift[i]+size*cd->c_inset[i][j]+ + cd->c_width[i][j]*size-2, + yOff+size*i+size-2); + + /* draw fill */ + rb->lcd_set_foreground(chunk_gray[(i+width) % + MAZEZAM_NUM_CHUNK_GRAYS]); + for (k = yOff+size*i+2; k < yOff+size*i+size-2; k += 2) + rb->lcd_drawline(xOff+size*shift[i]+size*cd->c_inset[i][j]+2,k, + xOff+size*shift[i]+size*cd->c_inset[i][j]+ + cd->c_width[i][j]*size-3,k); +#else + rb->lcd_drawrect(xOff+size*shift[i]+size*cd->c_inset[i][j], + yOff+size*i, cd->c_width[i][j]*size,size); + for (k = xOff+size*shift[i]+size*cd->c_inset[i][j]+2; + k < xOff+size*shift[i]+size*cd->c_inset[i][j]+ + cd->c_width[i][j]*size; + k += 2 + (i & 1)) + for (l = yOff+size*i+2; l < yOff+size*i+size; l += 2 + (i & 1)) + rb->lcd_drawpixel(k, l); +#endif + } +} + +/***************************************************************************** +* Draw the player +******************************************************************************/ +static void draw_player( + short size, + short xOff, + short yOff, + short x, + short y) +{ + /* For drawing the player, taken from the sokoban plugin */ + short max = size - 1; + short middle = max / 2; + short ldelta = (middle + 1) / 2; + + /* draw the player (mostly copied from the sokoban plugin) */ +#ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(MAZEZAM_PLAYER_COLOR); +#elif LCD_DEPTH > 1 + rb->lcd_set_foreground(MAZEZAM_PLAYER_GRAY); +#endif + rb->lcd_drawline(xOff+size*x, yOff+size*y+middle, + xOff+size*x+max, yOff+size*y+middle); + rb->lcd_drawline(xOff+size*x+middle, yOff+size*y, + xOff+size*x+middle, yOff+size*y+max-ldelta); + rb->lcd_drawline(xOff+size*x+middle, yOff+size*y+max-ldelta, + xOff+size*x+middle-ldelta, yOff+size*y+max); + rb->lcd_drawline(xOff+size*x+middle, yOff+size*y+max-ldelta, + xOff+size*x+middle+ldelta, yOff+size*y+max); +} + +/***************************************************************************** +* Draw the gate +******************************************************************************/ +static void draw_gate( + short size, + short xOff, + short yOff, + short entrance) +{ + short third = size / 3; + short twothirds = (2 * size) / 3; +#ifdef HAVE_LCD_COLOR + rb->lcd_set_foreground(MAZEZAM_GATE_COLOR); +#elif LCD_DEPTH > 1 + rb->lcd_set_foreground(MAZEZAM_GATE_GRAY); +#endif + rb->lcd_drawline(xOff-size,yOff+entrance*size+third, + xOff-1,yOff+entrance*size+third); + rb->lcd_drawline(xOff-size,yOff+entrance*size+twothirds, + xOff-1,yOff+entrance*size+twothirds); + rb->lcd_drawline(xOff-size+third,yOff+entrance*size, + xOff-size+third,yOff+entrance*size+size-1); + rb->lcd_drawline(xOff-size+twothirds,yOff+entrance*size, + xOff-size+twothirds,yOff+entrance*size+size-1); +} + +/***************************************************************************** +* Draw the level +******************************************************************************/ +static void draw_level( + struct level_info* li, + short *shift, /* an array of the horizontal offset of the lines */ + short x, /* player's x and y coords */ + short y) +{ + /* First we calculate the draw info */ + /* The number of pixels the side of a square should be */ + short size = (LCD_WIDTH/(li->width+2)) < (LCD_HEIGHT/li->height) ? + (LCD_WIDTH/(li->width+2)) : (LCD_HEIGHT/li->height); + /* The x and y position (in pixels) of the top left corner of the + * level + */ + short xOff = (LCD_WIDTH - (size*li->width))/2; + short yOff = (LCD_HEIGHT - (size*li->height))/2; + short i; + + rb->lcd_clear_display(); + + draw_walls(size,xOff,yOff,li->width, li->height, li->entrance, li->exit); + + /* draw the chunks */ + for (i = 0; iheight; i++) { + draw_row(size,xOff,yOff,li->width,i,&(li->cd),shift); + } + + draw_player(size,xOff,yOff,x,y); + + /* if the player has moved into the level, draw the gate */ + if (x >= 0) + draw_gate(size,xOff,yOff,li->entrance); +} + +/***************************************************************************** +* Manage the congratulations screen +******************************************************************************/ +static void welldone_screen(void) +{ + int start_selection = 0; + + MENUITEM_STRINGLIST(menu,MAZEZAM_TEXT_WELLDONE_TITLE,NULL, + MAZEZAM_TEXT_WELLDONE_OPTION); + + switch(rb->do_menu(&menu, &start_selection, NULL, true)){ + case MENU_ATTACHED_USB: + state = STATE_USB_CONNECTED; + break; + } +} + +/***************************************************************************** +* Manage the playing of a level +******************************************************************************/ +static void level_loop(struct level_info* li, short* shift, short *x, short *y) +{ + int i; + int button; + bool blocked; /* is there a chunk in the way of the player? */ + + while (state >= STATE_IN_LEVEL) { + draw_level(li, shift, *x, *y); + rb->lcd_update(); + button = pluginlib_getaction(rb, TIMEOUT_BLOCK, plugin_contexts, 2); + blocked = false; + + switch (button) { + case MAZEZAM_UP: + case MAZEZAM_UP_REPEAT: + if ((*y > 0) && (*x >= 0) && (*x < li->width)) { + for (i = 0; i < li->cd.l_num[*y-1]; i++) + blocked = blocked || + ((*x>=shift[*y-1]+li->cd.c_inset[*y-1][i]) && + (*xcd.c_inset[*y-1][i]+ + li->cd.c_width[*y-1][i])); + if (!blocked) *y -= 1; + } + break; + + + + case MAZEZAM_DOWN: + case MAZEZAM_DOWN_REPEAT: + if ((*y < li->height-1) && (*x >= 0) && (*x < li->width)) { + for (i = 0; i < li->cd.l_num[*y+1]; i++) + blocked = blocked || + ((*x>=shift[*y+1]+li->cd.c_inset[*y+1][i]) && + (*xcd.c_inset[*y+1][i]+ + li->cd.c_width[*y+1][i])); + if (!blocked) *y += 1; + } + break; + + case MAZEZAM_LEFT: + case MAZEZAM_LEFT_REPEAT: + if (*x > 0) { + for (i = 0; i < li->cd.l_num[*y]; i++) + blocked = blocked || + (*x == shift[*y]+li->cd.c_inset[*y][i]+ + li->cd.c_width[*y][i]); + if (!blocked) *x -= 1; + else if (shift[*y] + li->cd.c_inset[*y][0] > 0) { + *x -= 1; + shift[*y] -= 1; + } + } + break; + + case MAZEZAM_RIGHT: + case MAZEZAM_RIGHT_REPEAT: + if (*x < li->width-1) { + for (i = 0; i < li->cd.l_num[*y]; i++) + blocked = blocked || + (*x+1 == shift[*y]+li->cd.c_inset[*y][i]); + if (!blocked) *x += 1; + else if (shift[*y] + + li->cd.c_inset[*y][li->cd.l_num[*y]-1] + + li->cd.c_width[*y][li->cd.l_num[*y]-1] + < li->width) { + *x += 1; + shift[*y] += 1; + } + } + else if (*x == li->width) state = STATE_COMPLETED; + else if (*y == li->exit) *x += 1; + break; + + case MAZEZAM_MENU: + state = STATE_GAME_MENU; + break; + + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + state = STATE_USB_CONNECTED; + break; + } + } +} + +/***************************************************************************** +* Manage the in game menu +******************************************************************************/ +static void in_game_menu(void) +{ + /* The initial option is retry level */ + int start_selection = 1; + + MENUITEM_STRINGLIST(menu,MAZEZAM_TEXT_MAZEZAM_MENU, NULL, + MAZEZAM_TEXT_BACK, + MAZEZAM_TEXT_RETRY_LEVEL, + MAZEZAM_TEXT_AUDIO_PLAYBACK, + MAZEZAM_TEXT_QUIT); + + /* Don't show the status bar */ + switch(rb->do_menu(&menu, &start_selection, NULL, false)){ + case 1: /* retry */ + state = STATE_FAILED; + break; + + case 2: /* Audio playback */ + playback_control(rb); + state = STATE_IN_LEVEL; + break; + + case 3: /* quit */ + state = STATE_QUIT; + break; + + case MENU_ATTACHED_USB: + state = STATE_USB_CONNECTED; + break; + + default: /* Back */ + state = STATE_IN_LEVEL; + break; + } +} + +/***************************************************************************** +* Is the level a checkpoint +******************************************************************************/ +static bool at_checkpoint(int level) +{ + if (level <= MAZEZAM_FIRST_CHECKPOINT) + return level == MAZEZAM_FIRST_CHECKPOINT; + else { + level = level - MAZEZAM_FIRST_CHECKPOINT; + return level % MAZEZAM_CHECKPOINT_INTERVAL == 0; + } +} + +/***************************************************************************** +* Set up and play a level +* new_level should be true if this is the first time we've encountered +* this level +******************************************************************************/ +static void play_level(short level, short lives, bool new_level) +{ + struct level_info li; + short shift[MAZEZAM_MAX_LINES]; /* amount each line has been shifted */ + short x,y; + int i; + + state = STATE_IN_LEVEL; + + if (!(parse_level(level,&li))) + state = STATE_PARSE_ERROR; + + for (i = 0; i < li.height; i++) + shift[i] = 0; + + x = -1; + y = li.entrance; + + plugin_lcd_settings(); + rb->lcd_clear_display(); + + draw_level(&li, shift, x, y); + + /* If we've just reached a checkpoint, then alert the player */ + if (new_level && at_checkpoint(level)) { + rb->splash(MAZEZAM_DELAY_CHECKPOINT, MAZEZAM_TEXT_CHECKPOINT); + /* Clear the splash */ + draw_level(&li, shift, x, y); + } + +#ifdef HAVE_REMOTE_LCD + /* Splash text seems to use the remote display by + * default. I suppose I better keep it tidy! + */ + rb->lcd_remote_clear_display(); +#endif + rb->splash(MAZEZAM_DELAY_LIVES, MAZEZAM_TEXT_LIVES, + level+1, lives); + + /* ensure keys pressed during the splash screen are ignored */ + rb->button_clear_queue(); + + /* this little loop just ensures we return to the game if the player + * doesn't perform an interesting action during the in game menu */ + while (state >= STATE_IN_LEVEL) { + level_loop(&li, shift, &x, &y); + + if (state == STATE_GAME_MENU) { + restore_lcd_settings(); + in_game_menu(); + plugin_lcd_settings(); + } + } + restore_lcd_settings(); +} + +/***************************************************************************** +* Update the resume data based on the level reached +******************************************************************************/ +static void update_resume_data(struct resume_data *r, int level) +{ + if (at_checkpoint(level)) + r->level = level; +} + +/***************************************************************************** +* The loop which manages a full game of MazezaM. +******************************************************************************/ +static void game_loop(struct resume_data *r) +{ + int level = r->level; + int lives = MAZEZAM_START_LIVES; + /* We want to know when a player reaches a level for the first time, + * so we keep a second copy of the level. */ + int old_level = level; + + state = STATE_IN_GAME; + + while (state >= STATE_IN_GAME) + { + play_level(level, lives, old_level < level); + old_level = level; + + switch (state) { + case STATE_COMPLETED: + level += 1; + if (level == MAZEZAM_NUM_LEVELS) + state = STATE_WELLDONE; + break; + + case STATE_FAILED: + lives -= 1; + if (lives == 0) + state = STATE_GAME_OVER; + break; + + default: + break; + } + + update_resume_data(r,level); + } + + switch (state) { + case STATE_GAME_OVER: +#ifdef HAVE_REMOTE_LCD + /* Splash text seems to use the remote display by + * default. I suppose I better keep it tidy! + */ + rb->lcd_remote_clear_display(); +#endif + rb->splash(MAZEZAM_DELAY_GAME_OVER, MAZEZAM_TEXT_GAME_OVER); + break; + + case STATE_WELLDONE: + welldone_screen(); + break; + + default: + break; + } +} + +/***************************************************************************** +* Load the resume data from the config file. The data is +* stored in both r and old. +******************************************************************************/ +static void resume_load_data (struct resume_data *r, struct resume_data *old) +{ + struct configdata config[] = { + {TYPE_INT,0,MAZEZAM_NUM_LEVELS-1,&(r->level), + MAZEZAM_CONFIG_LEVELS_NAME,NULL,NULL} + }; + + if (configfile_load(MAZEZAM_CONFIG_FILENAME,config, + MAZEZAM_CONFIG_NUM_ITEMS, MAZEZAM_CONFIG_VERSION) < 0) + r->level = 0; + /* an extra precaution */ + else if ((r->level < 0) || (MAZEZAM_NUM_LEVELS <= r->level)) + r->level = 0; + + old->level = r->level; +} + +/***************************************************************************** +* Save the resume data in the config file, but only if necessary +******************************************************************************/ +static void resume_save_data (struct resume_data *r, struct resume_data *old) +{ + struct configdata config[] = { + {TYPE_INT,0,MAZEZAM_NUM_LEVELS-1,&(r->level), + MAZEZAM_CONFIG_LEVELS_NAME,NULL,NULL} + }; + + /* To reduce disk usage, only write the file if the resume data has + * changed. + */ + if (old->level != r->level) + configfile_save(MAZEZAM_CONFIG_FILENAME,config, + MAZEZAM_CONFIG_NUM_ITEMS, MAZEZAM_CONFIG_MINVERSION); +} + +/***************************************************************************** +* Offer a main menu with no continue option +******************************************************************************/ +static int main_menu_without_continue(int* start_selection) +{ + MENUITEM_STRINGLIST(menu,MAZEZAM_TEXT_MAIN_MENU,NULL, + MAZEZAM_TEXT_PLAY_GAME, + MAZEZAM_TEXT_QUIT); + return rb->do_menu(&menu, start_selection, NULL, false); +} + +/***************************************************************************** +* Offer a main menu with a continue option +******************************************************************************/ +static int main_menu_with_continue(int* start_selection) +{ + MENUITEM_STRINGLIST(menu,MAZEZAM_TEXT_MAIN_MENU,NULL, + MAZEZAM_TEXT_CONTINUE, + MAZEZAM_TEXT_PLAY_NEW_GAME, + MAZEZAM_TEXT_QUIT); + return rb->do_menu(&menu, start_selection, NULL, false); +} + +/***************************************************************************** +* Manages the main menu +******************************************************************************/ +static void main_menu(void) +{ + /* The initial option is "play game" */ + int start_selection = 0; + int choice = 0; + struct resume_data r_data, old_data; + + /* Load data */ + resume_load_data(&r_data, &old_data); + + while (state >= STATE_IN_APPLICATION) { + if (r_data.level == 0) + choice = main_menu_without_continue(&start_selection); + else + choice = main_menu_with_continue(&start_selection); + + switch(choice) { + case 0: /* Continue */ + state = STATE_IN_GAME; + game_loop(&r_data); + break; + + case 1: /* Quit or Play new game */ + if (r_data.level == 0) + state = STATE_QUIT; + else { /* Play new game */ + r_data.level = 0; + state = STATE_IN_GAME; + game_loop(&r_data); + } + break; + + case MENU_ATTACHED_USB: + state = STATE_USB_CONNECTED; + break; + + default: /* Quit */ + state = STATE_QUIT; + break; + } + } + + /* I'm not sure if it's appropriate to write to disk on USB events. + * Currently, I do so. + */ + resume_save_data(&r_data, &old_data); +} + +/***************************************************************************** +* Plugin entry point +******************************************************************************/ +enum plugin_status plugin_start(struct plugin_api* api, void* parameter) +{ + enum plugin_status plugin_state; + + /* Usual plugin stuff */ + (void)parameter; + rb = api; + + + /* initialise the config file module */ + configfile_init(rb); + + store_lcd_settings(); + + state = STATE_MAIN_MENU; + main_menu(); + + switch (state) { + case STATE_USB_CONNECTED: + plugin_state = PLUGIN_USB_CONNECTED; + break; + + case STATE_PARSE_ERROR: + plugin_state = PLUGIN_ERROR; + break; + + default: + plugin_state = PLUGIN_OK; + break; + } + + return plugin_state; +} diff --git a/manual/plugins/images/ss-mazezam-112x64x1.png b/manual/plugins/images/ss-mazezam-112x64x1.png new file mode 100644 index 0000000000000000000000000000000000000000..876aaa30428689d4e3cd45fbdb8d4195f93fd98b GIT binary patch literal 401 zcwXxa@N?(olHy`uVBq!ia0vp^1wib;!2~3If23>#Qk(@Ik;OpT1B~5HX4^3^Fgkm> zIEGZ*dVAxnV6%ciTjI$GN3E?Q+P=J&Gd?mYov70}_T4hYs7Gf_;|F731}HcX?=fxN zd{e!cd*1WyOt-CDF>gwb<@V)S+LJ3iU+;R=8h2gjTJ5&&>sH4t$~*O4a-r_p?p5-( zGU7%~udNEY1gmV_n_XAlc3PjY*Z=w9f6Wqn2lA>#?$2OT-ZCetzo>i0)$X93d*x^9 z{}q(lo-FKgh_NsywJK^Ac knu{e?|A|j!fdz(H{PaIlKK!bPQ3UZkUHx3vIVCg!08><^I{*Lx literal 0 HcwPel00001 diff --git a/manual/plugins/images/ss-mazezam-128x128x16.png b/manual/plugins/images/ss-mazezam-128x128x16.png new file mode 100644 index 0000000000000000000000000000000000000000..db76d24438c27f0fdaca1564cda076c9357fac9b GIT binary patch literal 541 zcwXxa@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwroCO|{#XwpWjFmnl1u!r$p7(Td z45_&F_O7GfAqNJBz^ok|>>suAdX@*Re{`(5V#=-y8l8QGNe`22@?D*bx71kqa5g@D z_ASl*`J#J`|NRl>F&$y);1m#6P;>C1w!rQFcNIITCGKo?+r2Aii~VuA=h=(z zrxvlHsNUFr^v$kyr;WC6``cMsRJ+$_-TMEheyn3YH-%y2-(7WclG?ZT8_IsX`R{M1 z-KA=V`^KzC?=kJ!_lJMEg5Tq$a)X~41y!drYOCu1T9{wB`uWPY*O#AOu~_^z{d;=Z zoV9(|OApKxLUvf&eePMY&--}Rzjk~5<>cG#Rd*hK-f;1wUi14%OJ*dWg*}p!u=Dl&)VF`uv^PP*{=^g&}+H#rBRGZ{2>b1+> z^YlJ6Vf`fZxG?g|a?QWN;)~}lc(ZYe$(~QP@_HK|e%pTG-_Kp#y}`Hat86Y7F8j6T zUiz9gRL8~7o87f;v3J+^2}^%BJop#9H~*uJ&zH*JkCt0jeLCfU?3tW;d8fy`|6At$ kiI3)6u1VO(ScKkvyyB?z?4LseFeVs0UHx3vIVCg!0I$oe_y7O^ literal 0 HcwPel00001 diff --git a/manual/plugins/images/ss-mazezam-138x110x2.png b/manual/plugins/images/ss-mazezam-138x110x2.png new file mode 100644 index 0000000000000000000000000000000000000000..9b79029b9304d49d87515a77a87c06fc0d02dbe0 GIT binary patch literal 327 zcwXxa@N?(olHy`uVBq!ia0vp^T|k`2#0(@`ZIY9L6ib)>ZpHh6{JY))R--ucjOF0+Uhe$1SA zDgA66*LIumNIj#&lHZ<)>fbf~c)oW_VbI#nxbjOoCav->`JXnQy>e;byNmx`{&ZPV z-BQ2k_$SFzC-Qj@@>E;7URra(XYKz7OKP{r&o?cuO`B(5B`=i>blxB9&ukZ2R%`9c S_HYAwmci52&t;ucLK6TEs)qOg literal 0 HcwPel00001 diff --git a/manual/plugins/images/ss-mazezam-160x128x1.png b/manual/plugins/images/ss-mazezam-160x128x1.png index 2ef425706f48d7f941b35a33eb6e3eea19ce7668..1e3814bdfa35892046c4d2183ba08897ac4061b8 100644 GIT binary patch delta 272 zcwRfqG@ogLL_H%j0|UdhwI3pY6kC8#h-(}Z(?>RDi~1!MKoQOYkH}&mtqR6UACdxq zjQyT2jv*e$lYjKH8uW1W0U_U>2aI(FkNE?BF@OI5pIu^xW0MSz!hu5<4k#Qra^L}j z;FBm;rfi9@wYLsz$-25Ap!4X*gtHEIO%0s&Oufzm1_xCqGYPQhO?8;8d9g1vfMs@> zYEB>f?+FLx-Z)QaSFEdN;89>>EnsLo)82APJAuJ4qmh|IB(B*}HSD-NL$&ZcF3BH_ zSqmC@SbKPQWOM?6=GlZE@R-`zc$$5_*RD+W!aoO|KMFhugpO~T7}OnJXYH(BBmwdu NgQu&X%Q~loCII@>V>bW* literal 323 zcwXxa@N?(olHy`uVBq!ia0vp^3xK$Rg&9cJ`PORyDT4r?5Z5>+rjKkOK_bE9Xysra z#aZAHSOQLy0w8TFYCY#*_}|l#g>MX=ZSIrx+WNh^{o`-TI{8;_rOD;}*>7G~%D>^evTc=m z*#QX^UFIn(P1f!&J#nJv%!%_7JMUiL$=&8SpMeP(%;T?P_^2Rj5@UF6I>?8fu6{1- HoD!M<5Lsv? diff --git a/manual/plugins/images/ss-mazezam-160x128x16.png b/manual/plugins/images/ss-mazezam-160x128x16.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb932e8d3f477739dca926c24724a630295a1df GIT binary patch literal 562 zcwXxa@N?(olHy`uVBq!ia0vp^3xK$Rg9%77Gk)~}Qk(@Ik;On-6^xZWBn2=qFh22g zaSW-r_4dv|UnWP1hQR9=I$CdZi6@0-X~*dt3A)zo);dGci1E?$YICQu8Be%(KWR_B zc`twci(UJ^uvzFfK4$3?w(xskK@-O9p80iE6;gKl9h_gFOS zn9F!-*Sgb1e>|RsU+dg?-pKI#%f0J7ukBYWm~)_TcIn-nW^Zh5ZmZ4|zFFg6agIN7 zPJ3_(P$s_d{`vFO*AxEj>OU|)Jia%1+DDluAK(4_u{1xv->$57r)AaCj~k7v=S5aN zD?RSB>7Oa4)y(DM=X@fL@%1<#7hc+#?7w~W#+mtv$9G4yl{ZweALo=&2f2X>#+dz6 tb;p{I{$+fB)VlYd)3hQS5EL=)F|1W`Y^pey9t(_k22WQ%mvv4FO#noG^z8ru literal 0 HcwPel00001 diff --git a/manual/plugins/images/ss-mazezam-160x128x2.png b/manual/plugins/images/ss-mazezam-160x128x2.png new file mode 100644 index 0000000000000000000000000000000000000000..9c405e4a63ef0add47c7b8042aee9cd44c5d9a5f GIT binary patch literal 341 zcwXxa@N?(olHy`uVBq!ia0vp^3xK$Ri5W0A#%Pba4!cIQ(|1A>Sbb9@qUx*aE9X^$s6-I)l-%@4`~= z{!1&3c`j)6y>)193RxMH;{Ws1|%}+j6giF9Xic585ED zzU%z_f3G|q>)j~)DQW)ujIGsl&OYM;ldQQSodGQCbA*=LUfwWMBIUN_+e6ddMy4E> zc3*tS^QEQJ$wTcM3Z{y5PT-opXI=lI?oP1@xhkT%Q=0C6zivEh(}TX7)qfc6uKhbI jnX#aeO)BHTuLshn>=f@LnTD%Wq&N#aB8!2vDi|w$ND5$JV0`ZB z;uunK>+PM>xwiuZ94;CJDC?=LO-Wv|>c-lX$4)_9n@{jhkMwC_k8Y|wcS+!c`;Vq2 zZY-Wn)*xtWAj^VqjK#Ky0qd=1aiz7#q0E;4r6O{xyPXFk9zW)4W_ixeiNB`f5 z`>*jXQM~!%)o16hs(!3da{qg7^xXcq?D%;v^G+MzoxJAYi&?KBN*--`e_VRz;rM(1 z+b(?p;==>=;16)78&qol`;+ E0E(QyuK)l5 literal 0 HcwPel00001 diff --git a/manual/plugins/images/ss-mazezam-176x220x16.png b/manual/plugins/images/ss-mazezam-176x220x16.png new file mode 100644 index 0000000000000000000000000000000000000000..2fafc13f5af1836c76b006b73408092542b549d4 GIT binary patch literal 809 zcwXxa@N?(olHy`uVBq!ia0vp^8-Vx@2NRHtf4oNnNO2Z;L>2>SRWMfikQBhcz;xTw z#WAGf*4w*|d6xr394@k?HXjgjx+!u>NW;%LklwxhE_SpW3?bviA0M^WS{_ z`m(BN{=K(*8TwoWS`;`O1)3CC966c0ya%M@2perbYxovL*mbx!?zyq<^BF1 zuO%JkJN}-Xw`{$;1p7wYdfD81xqJQd-~IpcwsPA%e);9?drJO3K5pytTz!vzvps%K z_dHh&J1;!v-uZ9#viuQwJN{km-~QuE<+i=X1VT&ZU9DvM?0kt?>+9b##?Mc?ZS9Od zSdaXj{qgoT-}}WMx942`*?IIc%l}<3xkKhCBB?998CR)yFFXGJ>!K;!3wQozzW4FJ zz?;+SdBDE5cKq{$IVt+DKpNl6RDu2f=3F_F>?NSDr)To9p^m-qdr+`&|1+Dt@#p@Z i4P_%Kt|=F|&p27tyJ$gOB?mCQGkCiCxvX$ zKErVPVMRs5q~p=@40D83Ef=Y{PkGWYNhWZTvR9FdXP?GN!9gN8XZhXrHGjWaeLqt8 z+VJ20w_?h=?@LZSthn*r={*6HmZ?2BE^&KX(W<)b8%zEk+*b0pG1)rNk?p%PLe04v z*`yxB&k0ZJ=KL;JJ5`_kx<=(l`GYUMK=XFWJ^j7&_nTI=nl|mgx)|SAlk|%KFxfJAy85}Sb4q9e0H--W3jhEB literal 0 HcwPel00001 diff --git a/manual/plugins/images/ss-mazezam-240x320x16.png b/manual/plugins/images/ss-mazezam-240x320x16.png new file mode 100644 index 0000000000000000000000000000000000000000..288d204afbd0991b0ae91b782c2b35a061e9f8d6 GIT binary patch literal 1198 zcwXxa@N?(olHy`uVBq!ia0vp^9~c-I9XOakB3)@LK#H@#BeEDstAer8hok@o29^X* z7srr_TW{~ib_qL5I9znOvtU>4}ohOV8g< zwRSw(ewCfUm~)nh<7B0bAQs=Q1)hQ?noVl17nX1g55ZH%J8xJ0{Acv(V{Oj+=bh=F z?(_d!$Q$?b?IvFv32oxcKoOiW(|J?#xqchf-9H{&KlftYT>pJ(t)vew*Jq9ckIh!mb1OpW+%aDBp+w9yZX;>`)2pGbQyoyyX^QyzrWr-vo>-e zA-z}xWnNu9;k$iE<95F_D$WigMkl)9e*@W_?|=V1FnPuA2R&~uIbKZP z7Q3x8db1Ob5dT`d>v!Gr#M^J}Ej|^y%sUc#VSBgDkHz^GKTkRKKovjT>-hgy|A%+E z0uirc9slp!zc7>UUEPKHJ8LhSxRf8gQu&X%Q~loCIGG-(n0_L literal 0 HcwPel00001 diff --git a/manual/plugins/images/ss-mazezam-320x240x16.png b/manual/plugins/images/ss-mazezam-320x240x16.png new file mode 100644 index 0000000000000000000000000000000000000000..8d48d725c15073cecc6786832c7985dd6d80cacf GIT binary patch literal 991 zcwXxa@N?(olHy`uVBq!ia0y~yU~~YoKX5Ps$$$P@Hb9EAz$3C4NUMUe(ubq~1_tJd zo-U3d6}R5rb8jZMjK25N^BFAs4hX0va0nZ;a7r9v;Ynm_GhlR{(V*xvT!c5AKJn=G>D+{` zX20(^Pix$|dEfK&+n)RCQ6=(dlemSZ__=eVxzE z&nF7qMZVj-u5q8y$?ES#q+gJQclYSbGj+~5av@>gM9IL(eb$S1Jdgap%<@yLuKxDJ z*|Cj8hY(!Y%p{0iVtd^6!`ag(9z76K=S0vELtZ#vGA<-(XI*CvFqblTy85}Sb4q9e E0FG-)>;M1& literal 0 HcwPel00001 diff --git a/manual/plugins/main.tex b/manual/plugins/main.tex index f21c46c0c..1e049f8d4 100644 --- a/manual/plugins/main.tex +++ b/manual/plugins/main.tex @@ -34,6 +34,8 @@ text files, chip8 games), or from the \setting{Open with} option on the \opt{lcd_bitmap}{\input{plugins/jewels.tex}} +\opt{lcd_bitmap}{\input{plugins/mazezam.tex}} + \opt{lcd_bitmap}{\input{plugins/minesweeper.tex}} \opt{player}{\input{plugins/nim.tex}} diff --git a/manual/plugins/mazezam.tex b/manual/plugins/mazezam.tex new file mode 100644 index 000000000..2e37a0d9e --- /dev/null +++ b/manual/plugins/mazezam.tex @@ -0,0 +1,29 @@ +\subsection{MazezaM} +\screenshot{plugins/images/ss-mazezam}{MazezaM}{fig:mazezam} + +The goal of this puzzle game is to escape a dungeon consisting of ten +``mazezams''. +These are rooms containing rows of blocks which can be shifted left or +right. +You can move the rows only by pushing them and if you move the rows +carelessly, you will get stuck. +You can have another go by selecting ``retry level'' from the menu, +but this will cost you a life. +You start the game with three lives. +Luckily, there are checkpoints at levels four and eight. + +\begin{table} +\begin{btnmap}{}{} + \opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu, \ButtonPlay,} + \opt{SANSA_E200_PAD}{\ButtonScrollFwd, \ButtonScrollBack,} + \opt{IRIVER_H100_PAD,IRIVER_H300_PAD,IAUDIO_X5_PAD,SANSA_C200_PAD,RECORDER_PAD,ONDIO_PAD,GIGABEAT_PAD,MROBE100_PAD} + {\ButtonUp, \ButtonDown,}% + \opt{IRIVER_H10_PAD}{\ButtonScrollUp, \ButtonScrollDown,} + \ButtonLeft, \ButtonRight + & Move Character\\ + \opt{IRIVER_H100_PAD,IRIVER_H300_PAD,RECORDER_PAD,ONDIO_PAD}{\ButtonOff} + \opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonSelect+\ButtonMenu} + \opt{IAUDIO_X5_PAD,IRIVER_H10_PAD,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD,MROBE100_PAD}{\ButtonPower} + & Menu\\ +\end{btnmap} +\end{table} -- 2.11.4.GIT