a52dec-0.7.4 Sat Jul 27 20:44:00 PDT 2002
The library is now fully reentrant.
Added win32 output module, al file output, gain control.
A few additional portability enhancements.
a52dec-0.7.3 Wed Feb 20 23:38:22 PST 2002
Performance enhancements, from 40% to 80% depending on streams.
Fixed a few embarassing bugs in liba52: one memory corruption issue
and a few minor portability problems.
Several new output modules, and fixes in the existing .wav file output.
a52dec-0.7.2 Sun Dec 16 14:39:56 PST 2001
Minor bugfixes, performance and portability enhancements.
Also added wav format output, and reduced the demux buffer size which
makes it easier to use a52dec as a pipe.
a52dec-0.7.1 Thu Aug 30 02:13:23 PDT 2001
Minor release for bugfixes. Looks like 0.7.0 was a bit rushed out.
Now compiles with gcc 3.0, made sure mlib implementation works, and
fixed a small bug in the a52dec test program.
a52dec-0.7.0 Thu Aug 23 23:18:00 PDT 2001
First release since more than one year !
The most user-noticeable additions are the downmix to arbitrary
speaker configurations, and the implementation of dynamic range
compression.
The speed has been improved by a factor of 2 to 3, the conformance and
precision should be higher, and we also fixed a small bug when playing
stereo rematrixed streams.
/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 Philipp Pertermann * * 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. * ****************************************************************************/#include"plugin.h"#include"lib/configfile.h"#include"lib/helper.h"#include"lib/playback_control.h"#ifdef DEBUG_WORMLETstatic long max_cycle;#endif/* size of the field the worm lives in */#define FIELD_RECT_X 1#define FIELD_RECT_Y 1#define FIELD_RECT_WIDTH (LCD_WIDTH - 45)#define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)/* when the game starts */#define INITIAL_WORM_LENGTH 10/* num of pixel the worm grows per eaten food */#define WORM_PER_FOOD 7/* num of worms creeping in the FIELD */#define MAX_WORMS 3/* minimal distance between a worm and an argh when a new argh is made */#define MIN_ARGH_DIST 5#if (CONFIG_KEYPAD == RECORDER_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_PLAYER2_DIR1 BUTTON_F2#define BTN_PLAYER2_DIR2 BUTTON_F3#define BTN_STARTPAUSE BUTTON_PLAY#define BTN_QUIT BUTTON_OFF#define BTN_STOPRESET BUTTON_ON#define BTN_TOGGLE_KEYS BUTTON_F1#if BUTTON_REMOTE != 0#define BTN_RC_UP BUTTON_RC_VOL_UP#define BTN_RC_DOWN BUTTON_RC_VOL_DOWN#define REMOTE#define MULTIPLAYER#endif#elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_PLAYER2_DIR1 BUTTON_F2#define BTN_PLAYER2_DIR2 BUTTON_F3#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_OFF#define BTN_STOPRESET BUTTON_ON#define BTN_TOGGLE_KEYS BUTTON_F1#elif (CONFIG_KEYPAD == ONDIO_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)#define BTN_QUIT (BUTTON_OFF|BUTTON_REL)#define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_1G2G_PAD)#define BTN_DIR_UP BUTTON_MENU#define BTN_DIR_DOWN BUTTON_PLAY#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)#define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)#define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)#elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)#define BTN_QUIT BUTTON_OFF#define BTN_STOPRESET BUTTON_ON#define BTN_RC_QUIT BUTTON_RC_STOP#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_PLAY#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_REC#elif (CONFIG_KEYPAD == GIGABEAT_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_A#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \(CONFIG_KEYPAD == SANSA_C200_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_REC#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_HOME#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT (BUTTON_HOME|BUTTON_REPEAT)#define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)#elif (CONFIG_KEYPAD == SANSA_M200_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE (BUTTON_SELECT | BUTTON_REL)#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)#define BTN_DIR_UP BUTTON_SCROLL_UP#define BTN_DIR_DOWN BUTTON_SCROLL_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_PLAY#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_REW#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \ (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_BACK#define BTN_STOPRESET BUTTON_MENU#elif (CONFIG_KEYPAD == MROBE100_PAD)#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_DISPLAY#elif CONFIG_KEYPAD == IAUDIO_M3_PAD#define BTN_DIR_UP BUTTON_RC_VOL_UP#define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN#define BTN_DIR_LEFT BUTTON_RC_REW#define BTN_DIR_RIGHT BUTTON_RC_FF#define BTN_STARTPAUSE BUTTON_RC_PLAY#define BTN_QUIT BUTTON_RC_REC#define BTN_STOPRESET BUTTON_RC_MODE#elif (CONFIG_KEYPAD == COWON_D2_PAD)#define BTN_QUIT BUTTON_POWER#elif CONFIG_KEYPAD == CREATIVEZVM_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_PLAY#define BTN_QUIT BUTTON_BACK#define BTN_STOPRESET BUTTON_MENU#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_MENU#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_VIEW#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_PREV#define BTN_DIR_RIGHT BUTTON_NEXT#define BTN_STARTPAUSE BUTTON_MENU#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_RIGHT#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_PREV#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_MENU#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_RIGHT#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \(CONFIG_KEYPAD == ONDAVX777_PAD) || \CONFIG_KEYPAD == MROBE500_PAD#define BTN_QUIT BUTTON_POWER#elif CONFIG_KEYPAD == SAMSUNG_YH_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_PLAY#define BTN_QUIT BUTTON_FFWD#define BTN_STOPRESET BUTTON_REW#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_PREV#define BTN_DIR_RIGHT BUTTON_NEXT#define BTN_STARTPAUSE BUTTON_PLAY#define BTN_QUIT BUTTON_REC#define BTN_STOPRESET BUTTON_CANCEL#elif CONFIG_KEYPAD == MPIO_HD200_PAD#define BTN_DIR_UP BUTTON_REW#define BTN_DIR_DOWN BUTTON_FF#define BTN_DIR_LEFT BUTTON_VOL_DOWN#define BTN_DIR_RIGHT BUTTON_VOL_UP#define BTN_STARTPAUSE BUTTON_FUNC#define BTN_QUIT (BUTTON_REC|BUTTON_PLAY)#define BTN_STOPRESET (BUTTON_FUNC|BUTTON_REPEAT)#elif CONFIG_KEYPAD == MPIO_HD300_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_REW#define BTN_DIR_RIGHT BUTTON_FF#define BTN_STARTPAUSE BUTTON_PLAY#define BTN_QUIT (BUTTON_MENU | BUTTON_REPEAT)#define BTN_STOPRESET (BUTTON_PLAY | BUTTON_REPEAT)#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_PLAYPAUSE#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_BACK#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_VOL_DOWN#elif CONFIG_KEYPAD == HM60X_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET (BUTTON_POWER|BUTTON_SELECT)#elif CONFIG_KEYPAD == HM801_PAD#define BTN_DIR_UP BUTTON_UP#define BTN_DIR_DOWN BUTTON_DOWN#define BTN_DIR_LEFT BUTTON_LEFT#define BTN_DIR_RIGHT BUTTON_RIGHT#define BTN_STARTPAUSE BUTTON_SELECT#define BTN_QUIT BUTTON_POWER#define BTN_STOPRESET BUTTON_PLAY#else#error No keymap defined!#endif#ifdef HAVE_TOUCHSCREEN#ifndef BTN_DIR_UP#define BTN_DIR_UP BUTTON_TOPMIDDLE#endif#ifndef BTN_DIR_DOWN#define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE#endif#ifndef BTN_DIR_LEFT#define BTN_DIR_LEFT BUTTON_MIDLEFT#endif#ifndef BTN_DIR_RIGHT#define BTN_DIR_RIGHT BUTTON_MIDRIGHT#endif#ifndef BTN_STARTPAUSE#define BTN_STARTPAUSE BUTTON_CENTER#endif#ifndef BTN_QUIT#define BTN_QUIT BUTTON_TOPLEFT#endif#ifndef BTN_STOPRESET#define BTN_STOPRESET BUTTON_TOPRIGHT#endif#endif#if (LCD_WIDTH == 96) && (LCD_HEIGHT == 96)#define FOOD_SIZE 3#define ARGH_SIZE 4#define SPEED 14#define MAX_WORM_SEGMENTS 128#elif (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)#define FOOD_SIZE 3#define ARGH_SIZE 4#define SPEED 14#define MAX_WORM_SEGMENTS 128#elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)#define FOOD_SIZE 3#define ARGH_SIZE 4#define SPEED 14#define MAX_WORM_SEGMENTS 128#elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)#define FOOD_SIZE 3#define ARGH_SIZE 4#define SPEED 14#define MAX_WORM_SEGMENTS 128#elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)#define FOOD_SIZE 3#define ARGH_SIZE 4#define SPEED 12#define MAX_WORM_SEGMENTS 128#elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)#define FOOD_SIZE 4#define ARGH_SIZE 5#define SPEED 10#define MAX_WORM_SEGMENTS 128#elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)#define FOOD_SIZE 4#define ARGH_SIZE 5#define SPEED 9#define MAX_WORM_SEGMENTS 128#elif ((LCD_WIDTH == 160) && (LCD_HEIGHT == 128)) || \ ((LCD_WIDTH == 128) && (LCD_HEIGHT == 160))#define FOOD_SIZE 4#define ARGH_SIZE 5#define SPEED 8#define MAX_WORM_SEGMENTS 256#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)#define FOOD_SIZE 4#define ARGH_SIZE 5#define SPEED 6#define MAX_WORM_SEGMENTS 256#elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)#define FOOD_SIZE 5#define ARGH_SIZE 6#define SPEED 4#define MAX_WORM_SEGMENTS 512#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)#define FOOD_SIZE 5#define ARGH_SIZE 6#define SPEED 4#define MAX_WORM_SEGMENTS 512#elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \ ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))#define FOOD_SIZE 7#define ARGH_SIZE 8#define SPEED 4#define MAX_WORM_SEGMENTS 512#elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \ ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))#define FOOD_SIZE 14#define ARGH_SIZE 16#define SPEED 4#define MAX_WORM_SEGMENTS 512#endif#ifdef HAVE_LCD_COLOR#define COLOR_WORM LCD_RGBPACK(80, 40, 0)#define COLOR_ARGH LCD_RGBPACK(175, 0, 0)#define COLOR_FOOD LCD_RGBPACK(0, 150, 0)#define COLOR_FG LCD_RGBPACK(0, 0, 0)#define COLOR_BG LCD_RGBPACK(181, 199, 231)#endif#define CHECK_SQUARE_COLLISION(x1,y1,s1,x2,y2,s2) (x1+s1>x2)&&(x2+s2>x1)&&(y1+s1>y2)&&(y2+s2>y1)/** * All the properties that a worm has. */static struct worm {/* The worm is stored in a ring of xy coordinates */int x[MAX_WORM_SEGMENTS];int y[MAX_WORM_SEGMENTS];int head;/* index of the head within the buffer */int tail;/* index of the tail within the buffer */int growing;/* number of cyles the worm still keeps growing */bool alive;/* the worms living state *//* direction vector in which the worm moves */int dirx;/* only values -1 0 1 allowed */int diry;/* only values -1 0 1 allowed *//* this method is used to fetch the direction the user has selected. It can be one of the values human_player1, human_player2, remote_player, virtual_player. All these values are fuctions, that can change the direction of the worm */void(*fetch_worm_direction)(struct worm *w);} worms[MAX_WORMS];/* stores the highscore - besides it was scored by a virtual player */static int highscore;#define MAX_FOOD 5/* maximal number of food items *//* The arrays store the food coordinates */static int foodx[MAX_FOOD];static int foody[MAX_FOOD];#define MAX_ARGH 100/* maximal number of argh items */#define ARGHS_PER_FOOD 2/* number of arghs produced per eaten food *//* The arrays store the argh coordinates */static int arghx[MAX_ARGH];static int arghy[MAX_ARGH];/* the number of arghs that are currently in use */static int argh_count;/* the number of arghs per food, settable by user */static int arghs_per_food = ARGHS_PER_FOOD;/* the size of the argh, settable by user */static int argh_size = ARGH_SIZE;/* the size of the food, settable by user */static int food_size = FOOD_SIZE;/* the speed of the worm, settable by user */static int speed = SPEED;/* the amount a worm grows by eating a food, settable by user */static int worm_food = WORM_PER_FOOD;/* End additional variables *//* the number of active worms (dead or alive) */static int worm_count = MAX_WORMS;/* in multiplayer mode: en- / disables the remote worm control in singleplayer mode: toggles 4 / 2 button worm control */static bool use_remote =false;/* return values of check_collision */#define COLLISION_NONE 0#define COLLISION_WORM 1#define COLLISION_FOOD 2#define COLLISION_ARGH 3#define COLLISION_FIELD 4static const char*const state_desc[] = {[COLLISION_NONE] = NULL,[COLLISION_WORM] ="Wormed",[COLLISION_FOOD] ="Growing",[COLLISION_ARGH] ="Argh",[COLLISION_FIELD] ="Crashed",};/* constants for use as directions. Note that the values are ordered clockwise. Thus increasing / decreasing the values is equivalent to right / left turns. */#define WEST 0#define NORTH 1#define EAST 2#define SOUTH 3/* direction of human player 1 */static int player1_dir = EAST;/* direction of human player 2 */static int player2_dir = EAST;/* direction of human player 3 */static int player3_dir = EAST;/* the number of (human) players that currently control a worm */static int players =1;#define SETTINGS_VERSION 1#define SETTINGS_MIN_VERSION 1#define SETTINGS_FILENAME"wormlet.cfg"static struct configdata config[] ={{TYPE_INT,0,1024, { .int_p = &highscore },"highscore", NULL},{TYPE_INT,0,15, { .int_p = &arghs_per_food },"arghs per food", NULL},{TYPE_INT,0,15, { .int_p = &argh_size },"argh size", NULL},{TYPE_INT,0,15, { .int_p = &food_size },"food size", NULL},{TYPE_INT,0,3, { .int_p = &players },"players", NULL},{TYPE_INT,0,3, { .int_p = &worm_count },"worms", NULL},{TYPE_INT,0,20, { .int_p = &speed },"speed", NULL},{TYPE_INT,0,15, { .int_p = &worm_food },"Worm Growth Per Food", NULL}};/** * Returns the direction id in which the worm * currently is creeping. * @param struct worm *w The worm that is to be investigated. * w Must not be null. * @return int A value 0 <= value < 4 * Note the predefined constants NORTH, SOUTH, EAST, WEST */static intget_worm_dir(struct worm *w){int retVal ;if(w->dirx ==0) {if(w->diry ==1) {
retVal = SOUTH;}else{
retVal = NORTH;}}else{if(w->dirx ==1) {
retVal = EAST;}else{
retVal = WEST;}}return retVal;}/** * Set the direction of the specified worm with a direction id. * Increasing the value by 1 means to turn the worm direction * to right by 90 degree. * @param struct worm *w The worm that is to be altered. w Must not be null. * @param int dir The new direction in which the worm is to creep. * dir must be 0 <= dir < 4. Use predefined constants * NORTH, SOUTH, EAST, WEST */static voidset_worm_dir(struct worm *w,int dir){switch(dir) {case WEST:
w->dirx = -1;
w->diry =0;break;case NORTH:
w->dirx =0;
w->diry = -1;break;case EAST:
w->dirx =1;
w->diry =0;break;case SOUTH:
w->dirx =0;
w->diry =1;break;}}/** * Returns the current length of the worm array. This * is also a value for the number of bends that are in the worm. * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS */static intget_worm_array_length(struct worm *w){/* initial simple calculation will be overwritten if wrong. */int retVal = w->head - w->tail;/* if the worm 'crosses' the boundaries of the ringbuffer */if(retVal <0) {
retVal = w->head + MAX_WORM_SEGMENTS - w->tail;}return retVal;}/** * Returns the score the specified worm. The score is the length * of the worm. * @param struct worm *w The worm that is to be investigated. * w must not be null. * @return int The length of the worm (>= 0). */static intget_score(struct worm *w){int retval =0;int length =get_worm_array_length(w);int i;for(i =0; i < length; i++) {/* The iteration iterates the length of the worm. Here's the conversion to the true indices within the worm arrays. */int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;int lineend = (linestart +1) % MAX_WORM_SEGMENTS;int startx = w->x[linestart];int starty = w->y[linestart];int endx = w->x[lineend];int endy = w->y[lineend];int minimum, maximum;if(startx == endx) {
minimum =MIN(starty, endy);
maximum =MAX(starty, endy);}else{
minimum =MIN(startx, endx);
maximum =MAX(startx, endx);}
retval +=abs(maximum - minimum);}return retval;}/** * Determines wether the line specified by startx, starty, endx, endy intersects * the rectangle specified by x, y, width, height. Note that the line must be exactly * horizontal or vertical (startx == endx or starty == endy). * @param int startx The x coordinate of the start point of the line. * @param int starty The y coordinate of the start point of the line. * @param int endx The x coordinate of the end point of the line. * @param int endy The y coordinate of the end point of the line. * @param int x The x coordinate of the top left corner of the rectangle. * @param int y The y coordinate of the top left corner of the rectangle. * @param int width The width of the rectangle. * @param int height The height of the rectangle. * @return bool Returns true if the specified line intersects with the recangle. */static boolline_in_rect(int startx,int starty,int endx,int endy,int x,int y,int width,int height){bool retval =false;int simple, simplemin, simplemax;int compa, compb, compmin, compmax;int temp;if(startx == endx) {
simple = startx;
simplemin = x;
simplemax = x + width;
compa = starty;
compb = endy;
compmin = y;
compmax = y + height;}else{
simple = starty;
simplemin = y;
simplemax = y + height;
compa = startx;
compb = endx;
compmin = x;
compmax = x + width;};
temp = compa;
compa =MIN(compa, compb);
compb =MAX(temp, compb);if(simplemin <= simple && simple <= simplemax) {if((compmin <= compa && compa <= compmax) ||(compmin <= compb && compb <= compmax) ||(compa <= compmin && compb >= compmax)) {
retval =true;}}return retval;}/** * Tests wether the specified worm intersects with the rect. * @param struct worm *w The worm to be investigated * @param int x The x coordinate of the top left corner of the rect * @param int y The y coordinate of the top left corner of the rect * @param int widht The width of the rect * @param int height The height of the rect * @return bool Returns true if the worm intersects with the rect */static boolworm_in_rect(struct worm *w,int x,int y,int width,int height){bool retval =false;/* get_worm_array_length is expensive -> buffer the value */int wormLength =get_worm_array_length(w);int i;/* test each entry that is part of the worm */for(i =0; i < wormLength && retval ==false; i++) {/* The iteration iterates the length of the worm. Here's the conversion to the true indices within the worm arrays. */int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;int lineend = (linestart +1) % MAX_WORM_SEGMENTS;int startx = w->x[linestart];int starty = w->y[linestart];int endx = w->x[lineend];int endy = w->y[lineend];
retval =line_in_rect(startx, starty, endx, endy, x, y, width, height);}return retval;}/** * Checks wether a specific food in the food arrays is at the * specified coordinates. * @param int foodIndex The index of the food in the food arrays * @param int x the x coordinate. * @param int y the y coordinate. * @return Returns true if the coordinate hits the food specified by * foodIndex. */static boolspecific_food_collision(int foodIndex,int x,int y){bool retVal =false;if(x >= foodx[foodIndex] &&
x < foodx[foodIndex] + food_size &&
y >= foody[foodIndex] &&
y < foody[foodIndex] + food_size) {
retVal =true;}return retVal;}/** * Returns the index of the food that is at the * given coordinates. If no food is at the coordinates * -1 is returned. * @return int -1 <= value < MAX_FOOD */static intfood_collision(int x,int y){int i =0;int retVal = -1;for(i =0; i < MAX_FOOD; i++) {if(specific_food_collision(i, x, y)) {
retVal = i;break;}}return retVal;}/** * Checks wether a specific argh in the argh arrays is at the * specified coordinates. * @param int arghIndex The index of the argh in the argh arrays * @param int x the x coordinate. * @param int y the y coordinate. * @return Returns true if the coordinate hits the argh specified by * arghIndex. */static boolspecific_argh_collision(int arghIndex,int x,int y){if( x >= arghx[arghIndex] &&
y >= arghy[arghIndex] &&
x < arghx[arghIndex] + argh_size &&
y < arghy[arghIndex] + argh_size ){return true;}return false;}/** * Returns the index of the argh that is at the * given coordinates. If no argh is at the coordinates * -1 is returned. * @param int x The x coordinate. * @param int y The y coordinate. * @return int -1 <= value < argh_count <= MAX_ARGH */static intargh_collision(int x,int y){int i =0;int retVal = -1;/* search for the argh that has the specified coords */for(i =0; i < argh_count; i++) {if(specific_argh_collision(i, x, y)) {
retVal = i;break;}}return retVal;}/** * Checks wether the worm collides with the food at the specfied food-arrays. * @param int foodIndex The index of the food in the arrays. Ensure the value is * 0 <= foodIndex <= MAX_FOOD * @return Returns true if the worm collides with the specified food. */static boolworm_food_collision(struct worm *w,int foodIndex){bool retVal =false;
retVal =worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
food_size -1, food_size -1);return retVal;}/** * Returns true if the worm hits the argh within the next moves (unless * the worm changes it's direction). * @param struct worm *w - The worm to investigate * @param int argh_idx - The index of the argh * @param int moves - The number of moves that are considered. * @return Returns false if the specified argh is not hit within the next * moves. */static boolworm_argh_collision_in_moves(struct worm *w,int argh_idx,int moves){bool retVal =false;int x1, y1, x2, y2;
x1 = w->x[w->head];
y1 = w->y[w->head];
x2 = w->x[w->head] + moves * w->dirx;
y2 = w->y[w->head] + moves * w->diry;
retVal =line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
argh_size, argh_size);return retVal;}/** * Checks wether the worm collides with the argh at the specfied argh-arrays. * @param int arghIndex The index of the argh in the arrays. * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH * @return Returns true if the worm collides with the specified argh. */static boolworm_argh_collision(struct worm *w,int arghIndex){bool retVal =false;
retVal =worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
argh_size -1, argh_size -1);return retVal;}/** * Find new coordinates for the food stored in foodx[index], foody[index] * that don't collide with any other food or argh * @param int index * Ensure that 0 <= index < MAX_FOOD. */static voidmake_food(int index){int x =0;int y =0;bool collisionDetected =false;int i;do{/* make coordinates for a new food so that the entire food lies within the FIELD */
x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
collisionDetected =false;/* Ensure that the new food doesn't collide with any existing foods or arghs. If the new food hit any existing argh or food a collision is detected. */for(i=0; i<MAX_FOOD && !collisionDetected; i++) {
collisionDetected =CHECK_SQUARE_COLLISION(x,y,food_size,foodx[i],foody[i],food_size);}for(i=0; i<argh_count && !collisionDetected; i++) {
collisionDetected =CHECK_SQUARE_COLLISION(x,y,food_size,arghx[i],arghy[i],argh_size);}/* use coordinates for further testing */
foodx[index] = x;
foody[index] = y;/* now test wether we accidently hit the worm with food ;) */
i =0;for(i =0; i < worm_count && !collisionDetected; i++) {
collisionDetected =worm_food_collision(&worms[i], index);}}while(collisionDetected);return;}/** * Clears a food from the lcd buffer. * @param int index The index of the food arrays under which * the coordinates of the desired food can be found. Ensure * that the value is 0 <= index <= MAX_FOOD. */static voidclear_food(int index){/* remove the old food from the screen */
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
foody[index] + FIELD_RECT_Y,
food_size, food_size);
rb->lcd_set_drawmode(DRMODE_SOLID);}/** * Draws a food in the lcd buffer. * @param int index The index of the food arrays under which * the coordinates of the desired food can be found. Ensure * that the value is 0 <= index <= MAX_FOOD. */static voiddraw_food(int index){/* draw the food object */#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(COLOR_FOOD);#endif
rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
foody[index] + FIELD_RECT_Y,
food_size, food_size);
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
rb->lcd_fillrect(foodx[index] + FIELD_RECT_X +1,
foody[index] + FIELD_RECT_Y +1,
food_size -2, food_size -2);
rb->lcd_set_drawmode(DRMODE_SOLID);#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(COLOR_FG);#endif}/** * Find new coordinates for the argh stored in arghx[index], arghy[index] * that don't collide with any other food or argh. * @param int index * Ensure that 0 <= index < argh_count < MAX_ARGH. */static voidmake_argh(int index){int x = -1;int y = -1;bool collisionDetected =false;int i;do{/* make coordinates for a new argh so that the entire food lies within the FIELD */
x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
collisionDetected =false;/* Ensure that the new argh doesn't intersect with any existing foods or arghs. If the new argh hit any existing argh or food an intersection is detected. */for(i=0; i<MAX_FOOD && !collisionDetected; i++) {
collisionDetected =CHECK_SQUARE_COLLISION(x,y,argh_size,foodx[i],foody[i],food_size);}for(i=0; i<argh_count && !collisionDetected; i++) {
collisionDetected =CHECK_SQUARE_COLLISION(x,y,argh_size,arghx[i],arghy[i],argh_size);}/* use the candidate coordinates to make a real argh */
arghx[index] = x;
arghy[index] = y;/* now test wether we accidently hit the worm with argh ;) */for(i =0; i < worm_count && !collisionDetected; i++) {
collisionDetected |=worm_argh_collision(&worms[i], index);
collisionDetected |=worm_argh_collision_in_moves(&worms[i], index,
MIN_ARGH_DIST);}}while(collisionDetected);return;}/** * Draws an argh in the lcd buffer. * @param int index The index of the argh arrays under which * the coordinates of the desired argh can be found. Ensure * that the value is 0 <= index < argh_count <= MAX_ARGH. */static voiddraw_argh(int index){/* draw the new argh */#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(COLOR_ARGH);#endif
rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
arghy[index] + FIELD_RECT_Y,
argh_size, argh_size);#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(COLOR_FG);#endif}static voidvirtual_player(struct worm *w);/** * Initialzes the specified worm with INITIAL_WORM_LENGTH * and the tail at the specified position. The worm will * be initialized alive and creeping EAST. * @param struct worm *w The worm that is to be initialized * @param int x The x coordinate at which the tail of the worm starts. * x must be 0 <= x < FIELD_RECT_WIDTH. * @param int y The y coordinate at which the tail of the worm starts * y must be 0 <= y < FIELD_RECT_WIDTH. */static voidinit_worm(struct worm *w,int x,int y){/* initialize the worm size */
w->head =1;
w->tail =0;
w->x[w->head] = x +1;
w->y[w->head] = y;
w->x[w->tail] = x;
w->y[w->tail] = y;/* set the initial direction the worm creeps to */
w->dirx =1;
w->diry =0;
w->growing = INITIAL_WORM_LENGTH -1;
w->alive =true;
w->fetch_worm_direction = virtual_player;}/** * Writes the direction that was stored for * human player 1 into the specified worm. This function * may be used to be stored in worm.fetch_worm_direction. * The value of * the direction is read from player1_dir. * @param struct worm *w - The worm of which the direction * is altered. */static voidhuman_player1(struct worm *w) {set_worm_dir(w, player1_dir);}/** * Writes the direction that was stored for * human player 2 into the specified worm. This function * may be used to be stored in worm.fetch_worm_direction. * The value of * the direction is read from player2_dir. * @param struct worm *w - The worm of which the direction * is altered. */static voidhuman_player2(struct worm *w) {set_worm_dir(w, player2_dir);}/** * Writes the direction that was stored for * human player using a remote control * into the specified worm. This function * may be used to be stored in worm.fetch_worm_direction. * The value of * the direction is read from player3_dir. * @param struct worm *w - The worm of which the direction * is altered. */static voidremote_player(struct worm *w) {set_worm_dir(w, player3_dir);}/** * Initializes the worm-, food- and argh-arrays, draws a frame, * makes some food and argh and display all that stuff. */static voidinit_wormlet(void){int i;for(i =0; i< worm_count; i++) {/* Initialize all the worm coordinates to center. */int x = (int)(FIELD_RECT_WIDTH /2);int y = (int)((FIELD_RECT_HEIGHT -20)/2) + i *10;init_worm(&worms[i], x, y);}
player1_dir = EAST;
player2_dir = EAST;
player3_dir = EAST;if(players >0) {
worms[0].fetch_worm_direction = human_player1;}if(players >1) {if(use_remote) {
worms[1].fetch_worm_direction = remote_player;}else{
worms[1].fetch_worm_direction = human_player2;}}if(players >2) {
worms[2].fetch_worm_direction = human_player2;}/* Needed when the game is restarted using BTN_STOPRESET */
rb->lcd_clear_display();/* make and display some food and argh */
argh_count = MAX_FOOD;for(i =0; i < MAX_FOOD; i++) {make_food(i);draw_food(i);make_argh(i);draw_argh(i);}/* draw the game field */
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_fillrect(0,0, FIELD_RECT_WIDTH +2, FIELD_RECT_HEIGHT +2);
rb->lcd_fillrect(1,1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
rb->lcd_set_drawmode(DRMODE_SOLID);/* make everything visible */
rb->lcd_update();}/** * Move the worm one step further if it is alive. * The direction in which the worm moves is taken from dirx and diry. * move_worm decreases growing if > 0. While the worm is growing the tail * is left untouched. * @param struct worm *w The worm to move. w must not be NULL. */static voidmove_worm(struct worm *w){if(w->alive) {/* determine the head point and its precessor */int headx = w->x[w->head];int heady = w->y[w->head];int prehead = (w->head + MAX_WORM_SEGMENTS -1) % MAX_WORM_SEGMENTS;int preheadx = w->x[prehead];int preheady = w->y[prehead];/* determine the old direction */int olddirx;int olddiry;if(headx == preheadx) {
olddirx =0;
olddiry = (heady > preheady) ? 1: -1;}else{
olddiry =0;
olddirx = (headx > preheadx) ? 1: -1;}/* olddir == dir? a change of direction means a new segment has been opened */if(olddirx != w->dirx ||
olddiry != w->diry) {
w->head = (w->head +1) % MAX_WORM_SEGMENTS;}/* new head position */
w->x[w->head] = headx + w->dirx;
w->y[w->head] = heady + w->diry;/* while the worm is growing no tail procession is necessary */if(w->growing >0) {/* update the worms grow state */
w->growing--;}/* if the worm isn't growing the tail has to be dragged */else{/* index of the end of the tail segment */int tail_segment_end = (w->tail +1) % MAX_WORM_SEGMENTS;/* drag the end of the tail *//* only one coordinate has to be altered. Here it is determined which one */int dir =0;/* specifies wether the coord has to be in- or decreased */if(w->x[w->tail] == w->x[tail_segment_end]) {
dir = (w->y[w->tail] - w->y[tail_segment_end] <0) ? 1: -1;
w->y[w->tail] += dir;}else{
dir = (w->x[w->tail] - w->x[tail_segment_end] <0) ? 1: -1;
w->x[w->tail] += dir;}/* when the tail has been dragged so far that it meets the next segment start the tail segment is obsolete and must be freed */if(w->x[w->tail] == w->x[tail_segment_end] &&
w->y[w->tail] == w->y[tail_segment_end]){/* drop the last tail point */
w->tail = tail_segment_end;}}}}/** * Draws the head and clears the tail of the worm in * the display buffer. lcd_update() is NOT called thus * the caller has to take care that the buffer is displayed. */static voiddraw_worm(struct worm *w){/* draw the new head */int x = w->x[w->head];int y = w->y[w->head];#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(COLOR_WORM);#endifif(x >=0&& x < FIELD_RECT_WIDTH && y >=0&& y < FIELD_RECT_HEIGHT) {
rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);}
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);/* clear the space behind the worm */
x = w->x[w->tail] ;
y = w->y[w->tail] ;if(x >=0&& x < FIELD_RECT_WIDTH && y >=0&& y < FIELD_RECT_HEIGHT) {
rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);}
rb->lcd_set_drawmode(DRMODE_SOLID);#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(COLOR_FG);#endif}/** * Checks wether the coordinate is part of the worm. Returns * true if any part of the worm was hit - including the head. * @param x int The x coordinate * @param y int The y coordinate * @return int The index of the worm arrays that contain x, y. * Returns -1 if the coordinates are not part of the worm. */static intspecific_worm_collision(struct worm *w,int x,int y){int retVal = -1;/* get_worm_array_length is expensive -> buffer the value */int wormLength =get_worm_array_length(w);int i;/* test each entry that is part of the worm */for(i =0; i < wormLength && retVal == -1; i++) {/* The iteration iterates the length of the worm. Here's the conversion to the true indices within the worm arrays. */int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;int lineend = (linestart +1) % MAX_WORM_SEGMENTS;bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);if(samex || samey){int test, min, max, tmp;if(samey) {
min = w->x[linestart];
max = w->x[lineend];
test = x;}else{
min = w->y[linestart];
max = w->y[lineend];
test = y;}
tmp = min;
min =MIN(min, max);
max =MAX(tmp, max);if(min <= test && test <= max) {
retVal = lineend;}}}return retVal;}/** * Increases the length of the specified worm by marking * that it may grow by len pixels. Note that the worm has * to move to make the growing happen. * @param worm *w The worm that is to be altered. * @param int len A positive value specifying the amount of * pixels the worm may grow. */static voidadd_growing(struct worm *w,int len) {
w->growing += len;}/** * Determins the worm that is at the coordinates x, y. The parameter * w is a switch parameter that changes the functionality of worm_collision. * If w is specified and x,y hits the head of w NULL is returned. * This is a useful way to determine wether the head of w hits * any worm but including itself but excluding its own head. * (It hits always its own head ;)) * If w is set to NULL worm_collision returns any worm including all heads * that is at position of x,y. * @param struct worm *w The worm of which the head should be excluded in * the test. w may be set to NULL. * @param int x The x coordinate that is checked * @param int y The y coordinate that is checkec * @return struct worm* The worm that has been hit by x,y. If no worm * was at the position NULL is returned. */static struct worm*worm_collision(struct worm *w,int x,int y){struct worm *retVal = NULL;int i;for(i =0; (i < worm_count) && (retVal == NULL); i++) {int collision_at =specific_worm_collision(&worms[i], x, y);if(collision_at != -1) {if(!(w == &worms[i] && collision_at == w->head)){
retVal = &worms[i];}}}return retVal;}/** * Returns true if the head of the worm just has * crossed the field boundaries. * @return bool true if the worm just has wrapped. */static boolfield_collision(struct worm *w){bool retVal =false;if((w->x[w->head] >= FIELD_RECT_WIDTH) ||(w->y[w->head] >= FIELD_RECT_HEIGHT) ||(w->x[w->head] <0) ||(w->y[w->head] <0)){
retVal =true;}return retVal;}/** * Returns true if the specified coordinates are within the * field specified by the FIELD_RECT_XXX constants. * @param int x The x coordinate of the point that is investigated * @param int y The y coordinate of the point that is investigated * @return bool Returns false if x,y specifies a point outside the * field of worms. */static boolis_in_field_rect(int x,int y){bool retVal =false;
retVal = (x >=0&& x < FIELD_RECT_WIDTH &&
y >=0&& y < FIELD_RECT_HEIGHT);return retVal;}/** * Checks and returns wether the head of the w * is colliding with something currently. * @return int One of the values: * COLLISION_NONE * COLLISION_w * COLLISION_FOOD * COLLISION_ARGH * COLLISION_FIELD */static intcheck_collision(struct worm *w){int retVal = COLLISION_NONE;if(worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
retVal = COLLISION_WORM;if(food_collision(w->x[w->head], w->y[w->head]) >=0)
retVal = COLLISION_FOOD;if(argh_collision(w->x[w->head], w->y[w->head]) >=0)
retVal = COLLISION_ARGH;if(field_collision(w))
retVal = COLLISION_FIELD;return retVal;}/** * Returns the index of the food that is closest to the point * specified by x, y. This index may be used in the foodx and * foody arrays. * @param int x The x coordinate of the point * @param int y The y coordinate of the point * @return int A value usable as index in foodx and foody. */static intget_nearest_food(int x,int y){int nearestfood =0;int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;int deltax =0;int deltay =0;int foodindex;for(foodindex =0; foodindex < MAX_FOOD; foodindex++) {int distance;
deltax = foodx[foodindex] - x;
deltay = foody[foodindex] - y;
deltax = deltax >0 ? deltax : deltax * (-1);
deltay = deltay >0 ? deltay : deltay * (-1);
distance = deltax + deltay;if(distance < olddistance) {
olddistance = distance;
nearestfood = foodindex;}}return nearestfood;}/** * Returns wether the specified position is next to the worm * and in the direction the worm looks. Use this method to * test wether this position would be hit with the next move of * the worm unless the worm changes its direction. * @param struct worm *w - The worm to be investigated * @param int x - The x coordinate of the position to test. * @param int y - The y coordinate of the position to test. * @return Returns true if the worm will hit the position unless * it change its direction before the next move. */static boolis_in_front_of_worm(struct worm *w,int x,int y){bool infront =false;int deltax = x - w->x[w->head];int deltay = y - w->y[w->head];if(w->dirx ==0) {
infront = (w->diry * deltay) >0;}else{
infront = (w->dirx * deltax) >0;}return infront;}/** * Returns true if the worm will collide with the next move unless * it changes its direction. * @param struct worm *w - The worm to be investigated. * @return Returns true if the worm will collide with the next move * unless it changes its direction. */static boolwill_worm_collide(struct worm *w){int x = w->x[w->head] + w->dirx;int y = w->y[w->head] + w->diry;bool retVal = !is_in_field_rect(x, y);if(!retVal) {
retVal = (argh_collision(x, y) != -1);}if(!retVal) {
retVal = (worm_collision(w, x, y) != NULL);}return retVal;}/** * This function * may be used to be stored in worm.fetch_worm_direction for * worms that are not controlled by humans but by artificial stupidity. * A direction is searched that doesn't lead to collision but to the nearest * food - but not very intelligent. The direction is written to the specified * worm. * @param struct worm *w - The worm of which the direction * is altered. */static voidvirtual_player(struct worm *w){bool isright;int plana, planb, planc;/* find the next lunch */int nearestfood =get_nearest_food(w->x[w->head], w->y[w->head]);/* determine in which direction it is *//* in front of me? */bool infront =is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);/* left right of me? */int olddir =get_worm_dir(w);set_worm_dir(w, (olddir +1) %4);
isright =is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);set_worm_dir(w, olddir);/* detect situation, set strategy */if(infront) {if(isright) {
plana = olddir;
planb = (olddir +1) %4;
planc = (olddir +3) %4;}else{
plana = olddir;
planb = (olddir +3) %4;
planc = (olddir +1) %4;}}else{if(isright) {
plana = (olddir +1) %4;
planb = olddir;
planc = (olddir +3) %4;}else{
plana = (olddir +3) %4;
planb = olddir;
planc = (olddir +1) %4;}}/* test for collision */set_worm_dir(w, plana);if(will_worm_collide(w)){/* plan b */set_worm_dir(w, planb);/* test for collision */if(will_worm_collide(w)) {/* plan c */set_worm_dir(w, planc);}}}/** * prints out the score board with all the status information * about the game. */static voidscore_board(void){int i;int y =0;
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
rb->lcd_fillrect(FIELD_RECT_WIDTH +2,0,
LCD_WIDTH - FIELD_RECT_WIDTH -2, LCD_HEIGHT);
rb->lcd_set_drawmode(DRMODE_SOLID);for(i =0; i < worm_count; i++) {int score =get_score(&worms[i]);int collision =check_collision(&worms[i]);const char*state_str;/* high score */if(worms[i].fetch_worm_direction != virtual_player){if(highscore < score) {
highscore = score;}}/* worm state */if(collision == COLLISION_NONE) {if(worms[i].growing >0)
state_str ="Growing";else{
state_str = worms[i].alive ? "Hungry":"Wormed";}}else{
state_str = state_desc[collision];}/* length */
rb->lcd_putsxyf(FIELD_RECT_WIDTH +3, y ,"Len:%d", score);
rb->lcd_putsxyf(FIELD_RECT_WIDTH +3, y+8, state_str);if(!worms[i].alive){
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_fillrect(FIELD_RECT_WIDTH +2, y,
LCD_WIDTH - FIELD_RECT_WIDTH -2,17);
rb->lcd_set_drawmode(DRMODE_SOLID);}
y +=19;}#ifdef DEBUG_WORMLET
rb->lcd_putsxyf(FIELD_RECT_WIDTH +3, LCD_HEIGHT -8,"ticks %d", max_cycle);#else
rb->lcd_putsxyf(FIELD_RECT_WIDTH +3, LCD_HEIGHT -8,"Hs: %d", highscore);#endif}/** * Checks for collisions of the worm and its environment and * takes appropriate actions like growing the worm or killing it. * @return bool Returns true if the worm is dead. Returns * false if the worm is healthy, up and creeping. */static boolprocess_collisions(struct worm *w){int index = -1;
w->alive &= !field_collision(w);if(w->alive) {/* check if food was eaten */
index =food_collision(w->x[w->head], w->y[w->head]);if(index != -1){int i;clear_food(index);make_food(index);draw_food(index);for(i =0; i < arghs_per_food; i++) {
argh_count++;if(argh_count > MAX_ARGH)
argh_count = MAX_ARGH;make_argh(argh_count -1);draw_argh(argh_count -1);}add_growing(w, worm_food);draw_worm(w);}/* check if argh was eaten */else{
index =argh_collision(w->x[w->head], w->y[w->head]);if(index != -1) {
w->alive =false;}else{if(worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
w->alive =false;}}}}return!w->alive;}/** * The main loop of the game. * @return bool Returns true if the game ended * with a dead worm. Returns false if the user * aborted the game manually. */static intrun(void){int button =0;int wormDead =false;bool paused =false;/* ticks are counted to compensate speed variations */long cycle_start =0, cycle_end =0;#ifdef DEBUG_WORMLETint ticks_to_max_cycle_reset =20;
max_cycle =0;#endif/* initialize the board and so on */init_wormlet();
cycle_start = *rb->current_tick;/* change the direction of the worm */while(!wormDead){int i;long cycle_duration=0;#ifdef HAS_BUTTON_HOLDif(rb->button_hold())
paused =true;#endifswitch(button) {case BTN_STARTPAUSE:
paused = !paused;break;case BTN_STOPRESET:if(paused)return1;/* restart game */else
paused =true;break;#ifdef BTN_RC_QUITcase BTN_RC_QUIT:#endifcase BTN_QUIT:return2;/* back to menu */break;}if(!paused){switch(button) {case BTN_DIR_UP:if(players ==1&& !use_remote) {
player1_dir = NORTH;}break;case BTN_DIR_DOWN:if(players ==1&& !use_remote) {
player1_dir = SOUTH;}break;case BTN_DIR_LEFT:if(players !=1|| use_remote) {
player1_dir = (player1_dir +3) %4;}else{
player1_dir = WEST;}break;case BTN_DIR_RIGHT:if(players !=1|| use_remote) {
player1_dir = (player1_dir +1) %4;}else{
player1_dir = EAST;}break;#ifdef MULTIPLAYERcase BTN_PLAYER2_DIR1:
player2_dir = (player2_dir +3) %4;break;case BTN_PLAYER2_DIR2:
player2_dir = (player2_dir +1) %4;break;#endif#ifdef REMOTEcase BTN_RC_UP:
player3_dir = (player3_dir +1) %4;break;case BTN_RC_DOWN:
player3_dir = (player3_dir +3) %4;break;#endif}for(i =0; i < worm_count; i++) {
worms[i].fetch_worm_direction(&worms[i]);}
wormDead =true;for(i =0; i < worm_count; i++){struct worm *w = &worms[i];move_worm(w);
wormDead &=process_collisions(w);draw_worm(w);}score_board();
rb->lcd_update();if(button == BTN_STOPRESET) {
wormDead =true;}/* here the wormlet game cycle ends thus the current tick is stored as end time */
cycle_end = *rb->current_tick;/* The duration of the game cycle */
cycle_duration = cycle_end - cycle_start;
cycle_duration =MAX(0, cycle_duration);
cycle_duration =MIN(speed -1, cycle_duration);#ifdef DEBUG_WORMLET
ticks_to_max_cycle_reset--;if(ticks_to_max_cycle_reset <=0) {
max_cycle =0;}if(max_cycle < cycle_duration) {
max_cycle = cycle_duration;
ticks_to_max_cycle_reset =20;}#endif}/* adjust the number of ticks to wait for a button. This ensures that a complete game cycle including user input runs in constant time */
button = rb->button_get_w_tmo(speed - cycle_duration);
cycle_start = *rb->current_tick;}
rb->splash(HZ*2,"Game Over!");return2;/* back to menu */}#ifdef DEBUG_WORMLET/** * Just a test routine that checks that worm_food_collision works * in some typical situations. */static voidtest_worm_food_collision(void){int collision_count =0;int i;
rb->lcd_clear_display();init_worm(&worms[0],10,10);add_growing(&worms[0],10);set_worm_dir(&worms[0], EAST);for(i =0; i <10; i++) {move_worm(&worms[0]);draw_worm(&worms[0]);}set_worm_dir(&worms[0], SOUTH);for(i =0; i <10; i++) {move_worm(&worms[0]);draw_worm(&worms[0]);}
foodx[0] =15;
foody[0] =12;for(foody[0] =20; foody[0] >0; foody[0] --) {bool collision;draw_worm(&worms[0]);draw_food(0);
collision =worm_food_collision(&worms[0],0);if(collision) {
collision_count++;}
rb->lcd_putsxyf(0, LCD_HEIGHT -8,"collisions: %d", collision_count);
rb->lcd_update();}if(collision_count != food_size) {
rb->button_get(true);}
foody[0] =15;for(foodx[0] =30; foodx[0] >0; foodx[0] --) {bool collision;draw_worm(&worms[0]);draw_food(0);
collision =worm_food_collision(&worms[0],0);if(collision) {
collision_count ++;}
rb->lcd_putsxyf(0, LCD_HEIGHT -8,"collisions: %d", collision_count);
rb->lcd_update();}if(collision_count != food_size *2) {
rb->button_get(true);}}static boolexpensive_worm_in_rect(struct worm *w,int rx,int ry,int rw,int rh){int x, y;bool retVal =false;for(x = rx; x < rx + rw; x++){for(y = ry; y < ry + rh; y++) {if(specific_worm_collision(w, x, y) != -1) {
retVal =true;}}}return retVal;}static voidtest_worm_argh_collision(void){int i;int dir;int collision_count =0;
rb->lcd_clear_display();init_worm(&worms[0],10,10);add_growing(&worms[0],40);for(dir =0; dir <4; dir++) {set_worm_dir(&worms[0], (EAST + dir) %4);for(i =0; i <10; i++) {move_worm(&worms[0]);draw_worm(&worms[0]);}}
arghx[0] =12;for(arghy[0] =0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){bool collision;draw_argh(0);
collision =worm_argh_collision(&worms[0],0);if(collision) {
collision_count ++;}
rb->lcd_putsxyf(0, LCD_HEIGHT -8,"collisions: %d", collision_count);
rb->lcd_update();}if(collision_count != argh_size *2) {
rb->button_get(true);}
arghy[0] =12;for(arghx[0] =0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){bool collision;draw_argh(0);
collision =worm_argh_collision(&worms[0],0);if(collision) {
collision_count ++;}
rb->lcd_putsxyf(0, LCD_HEIGHT -8,"collisions: %d", collision_count);
rb->lcd_update();}if(collision_count != argh_size *4) {
rb->button_get(true);}}static inttestline_in_rect(void){int testfailed = -1;int rx =10;int ry =15;int rw =20;int rh =25;/* Test 1 */int x1 =12;int y1 =8;int x2 =12;int y2 =42;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_update();
rb->lcd_putsxy(0,0,"failed 1");
rb->button_get(true);
testfailed =1;}/* test 2 */
y2 =20;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 2");
rb->lcd_update();
rb->button_get(true);
testfailed =2;}/* test 3 */
y1 =30;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 3");
rb->lcd_update();
rb->button_get(true);
testfailed =3;}/* test 4 */
y2 =45;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 4");
rb->lcd_update();
rb->button_get(true);
testfailed =4;}/* test 5 */
y1 =50;if(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 5");
rb->lcd_update();
rb->button_get(true);
testfailed =5;}/* test 6 */
y1 =5;
y2 =7;if(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 6");
rb->lcd_update();
rb->button_get(true);
testfailed =6;}/* test 7 */
x1 =8;
y1 =20;
x2 =35;
y2 =20;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 7");
rb->lcd_update();
rb->button_get(true);
testfailed =7;}/* test 8 */
x2 =12;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 8");
rb->lcd_update();
rb->button_get(true);
testfailed =8;}/* test 9 */
x1 =25;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 9");
rb->lcd_update();
rb->button_get(true);
testfailed =9;}/* test 10 */
x2 =37;if(!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&!line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 10");
rb->lcd_update();
rb->button_get(true);
testfailed =10;}/* test 11 */
x1 =42;if(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 11");
rb->lcd_update();
rb->button_get(true);
testfailed =11;}/* test 12 */
x1 =5;
x2 =7;if(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 12");
rb->lcd_update();
rb->button_get(true);
testfailed =12;}/* test 13 */
rx =9;
ry =15;
rw = food_size;
rh = food_size;
x1 =10;
y1 =10;
x2 =10;
y2 =20;if(!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
rb->lcd_drawrect(rx, ry, rw, rh);
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_putsxy(0,0,"failed 13");
rb->lcd_update();
rb->button_get(true);
testfailed =13;}/* test 14 */
rx =9;
ry =15;
rw =4;
rh =4;
x1 =10;
y1 =10;
x2 =10;
y2 =19;if(!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
rb->lcd_drawline(x1, y1, x2, y2);
rb->lcd_invertrect(rx, ry, rw, rh);
rb->lcd_putsxy(0,0,"failed 14");
rb->lcd_update();
rb->button_get(true);
testfailed =14;}
rb->lcd_clear_display();return testfailed;}/** * Just a test routine to test wether specific_worm_collision might work properly */static inttest_specific_worm_collision(void){int collisions =0;int dir;int x =0;int y =0;
rb->lcd_clear_display();init_worm(&worms[0],10,20);add_growing(&worms[0],20- INITIAL_WORM_LENGTH);for(dir = EAST; dir < EAST +4; dir++) {int i;set_worm_dir(&worms[0], dir %4);for(i =0; i <5; i++) {if(!(dir %4== NORTH && i ==9)) {move_worm(&worms[0]);draw_worm(&worms[0]);}}}for(y =15; y <30; y ++){for(x =5; x <20; x++) {if(specific_worm_collision(&worms[0], x, y) != -1) {
collisions ++;}
rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
rb->lcd_putsxyf(0, LCD_HEIGHT -8,"collisions %d", collisions);
rb->lcd_update();}}if(collisions !=21) {
rb->button_get(true);}return collisions;}static voidtest_make_argh(void){int dir;int seed =0;int hit =0;int failures =0;int last_failures =0;int i, worm_idx;
rb->lcd_clear_display();
worm_count =3;for(worm_idx =0; worm_idx < worm_count; worm_idx++) {init_worm(&worms[worm_idx],10+ worm_idx *20,20);add_growing(&worms[worm_idx],40- INITIAL_WORM_LENGTH);}for(dir = EAST; dir < EAST +4; dir++) {for(worm_idx =0; worm_idx < worm_count; worm_idx++) {set_worm_dir(&worms[worm_idx], dir %4);for(i =0; i <10; i++) {if(!(dir %4== NORTH && i ==9)) {move_worm(&worms[worm_idx]);draw_worm(&worms[worm_idx]);}}}}
rb->lcd_update();for(seed =0; hit <20; seed +=2) {int x, y;
rb->srand(seed);
x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);for(worm_idx =0; worm_idx < worm_count; worm_idx++){if(expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {int tries =0;
rb->srand(seed);
tries =make_argh(0);if((x == arghx[0] && y == arghy[0]) || tries <2) {
failures ++;}
rb->lcd_putsxyf(0, LCD_HEIGHT -8,"(%d;%d) fail%d try%d",
x, y, failures, tries);
rb->lcd_update();
rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y,
argh_size, argh_size);
rb->lcd_update();draw_argh(0);
rb->lcd_update();
rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y,
argh_size, argh_size);
rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y,
argh_size, argh_size);if(failures > last_failures) {
rb->button_get(true);}
last_failures = failures;
hit ++;}}}}static voidtest_worm_argh_collision_in_moves(void) {int hit_count =0;int i;
rb->lcd_clear_display();init_worm(&worms[0],10,20);
arghx[0] =20;
arghy[0] =18;draw_argh(0);set_worm_dir(&worms[0], EAST);for(i =0; i <20; i++) {move_worm(&worms[0]);draw_worm(&worms[0]);if(worm_argh_collision_in_moves(&worms[0],0,5)){
hit_count ++;}
rb->lcd_putsxyf(0, LCD_HEIGHT -8,"in 5 moves hits: %d", hit_count);
rb->lcd_update();}if(hit_count != argh_size +5) {
rb->button_get(true);}}#endif/* DEBUG_WORMLET *//* * Reverts default settings */static voiddefault_settings(void){
arghs_per_food = ARGHS_PER_FOOD;
argh_size = ARGH_SIZE;
food_size = FOOD_SIZE;
speed = SPEED;
worm_food = WORM_PER_FOOD;
players =1;
worm_count = MAX_WORMS;
use_remote =false;return;}/* * Launches the wormlet game */static boollaunch_wormlet(void){int game_result =1;
rb->lcd_clear_display();/* Turn off backlight timeout */backlight_ignore_timeout();/* start the game */while(game_result ==1)
game_result =run();switch(game_result){case2:/* Turn on backlight timeout (revert to settings) */backlight_use_settings();return false;break;}return false;}/** * Main entry point */enum plugin_status plugin_start(const void* parameter){int result;int menu_quit =0;int new_setting;(void)(parameter);default_settings();if(configfile_load(SETTINGS_FILENAME, config,sizeof(config)/sizeof(*config),
SETTINGS_MIN_VERSION ) <0){/* If the loading failed, save a new config file (as the disk is already spinning) */configfile_save(SETTINGS_FILENAME, config,sizeof(config)/sizeof(*config),
SETTINGS_VERSION);}#ifdef HAVE_LCD_COLOR
rb->lcd_set_foreground(COLOR_FG);
rb->lcd_set_background(COLOR_BG);#endif#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);#endif#ifdef DEBUG_WORMLETtestline_in_rect();test_worm_argh_collision_in_moves();test_make_argh();test_worm_food_collision();test_worm_argh_collision();test_specific_worm_collision();#endif/* Setup screen */static const struct opt_items noyes[2] = {{"No", -1},{"Yes", -1},};static const struct opt_items remoteonly_option[1] = {{"Remote Control", -1}};static const struct opt_items key24_option[2] = {{"4 Key Control", -1},{"2 Key Control", -1}};#ifdef REMOTEstatic const struct opt_items remote_option[2] = {{"Remote Control", -1},{"No Rem. Control", -1}};#elsestatic const struct opt_items key2_option[1] = {{"2 Key Control", -1}};#endifstatic const struct opt_items nokey_option[1] = {{"Out of Control", -1}};MENUITEM_STRINGLIST(menu,"Wormlet Menu", NULL,"Play Wormlet!","Number of Worms","Number of Players","Control Style","Worm Growth Per Food","Worm Speed","Arghs Per Food","Argh Size","Food Size","Revert to Default Settings","Playback Control","Quit");
rb->button_clear_queue();while(!menu_quit) {switch(rb->do_menu(&menu, &result, NULL,false)){case0:
rb->lcd_setfont(FONT_SYSFIXED);launch_wormlet();break;case1:
rb->set_int("Number of Worms","", UNIT_INT, &worm_count, NULL,1,1,3, NULL);if(worm_count < players) {
worm_count = players;}break;case2:#ifdef MULTIPLAYER
rb->set_int("Number of Players","", UNIT_INT, &players, NULL,1,0,4, NULL);#else
rb->set_int("Number of Players","", UNIT_INT, &players, NULL,1,0,2, NULL);#endifif(players > worm_count) {
worm_count = players;}if(players >2) {
use_remote =true;}break;case3:switch(players) {case0:
rb->set_option("Control Style",&use_remote,INT,
nokey_option,1, NULL);break;case1:
rb->set_option("Control Style",&use_remote,INT,
key24_option,2, NULL);break;case2:#ifdef REMOTE
rb->set_option("Control Style",&use_remote,INT,
remote_option,2, NULL);#else
rb->set_option("Control Style",&use_remote,INT,
key2_option,1, NULL);#endifbreak;case3:
rb->set_option("Control Style",&use_remote,INT,
remoteonly_option,1, NULL);break;}break;case4:
rb->set_int("Worm Growth Per Food","", UNIT_INT, &worm_food,
NULL,1,0,15, NULL);break;case5:
new_setting =20- speed;
rb->set_int("Worm Speed","", UNIT_INT, &new_setting,
NULL,1,0,20, NULL);
speed =20- new_setting;break;case6:
rb->set_int("Arghs Per Food","", UNIT_INT, &arghs_per_food,
NULL,1,0,8, NULL);break;case7:
rb->set_int("Argh Size","", UNIT_INT, &argh_size,
NULL,1,2,10, NULL);break;case8:
rb->set_int("Food Size","", UNIT_INT, &food_size,
NULL,1,2,10, NULL);break;case9:
new_setting =0;
rb->set_option("Reset Settings?", &new_setting, INT, noyes ,2, NULL);if(new_setting ==1)default_settings();break;case10:playback_control(NULL);break;default:
menu_quit=1;break;}}configfile_save(SETTINGS_FILENAME, config,sizeof(config)/sizeof(*config),
SETTINGS_VERSION);return PLUGIN_OK;}