1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 Philipp Pertermann
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "configfile.h"
25 /* size of the field the worm lives in */
26 #define FIELD_RECT_X 1
27 #define FIELD_RECT_Y 1
28 #define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
29 #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
31 /* when the game starts */
32 #define INITIAL_WORM_LENGTH 10
34 /* num of pixel the worm grows per eaten food */
35 #define WORM_PER_FOOD 7
37 /* num of worms creeping in the FIELD */
40 /* minimal distance between a worm and an argh
41 when a new argh is made */
42 #define MIN_ARGH_DIST 5
44 #if (CONFIG_KEYPAD == RECORDER_PAD)
45 #define BTN_DIR_UP BUTTON_UP
46 #define BTN_DIR_DOWN BUTTON_DOWN
47 #define BTN_DIR_LEFT BUTTON_LEFT
48 #define BTN_DIR_RIGHT BUTTON_RIGHT
49 #define BTN_PLAYER2_DIR1 BUTTON_F2
50 #define BTN_PLAYER2_DIR2 BUTTON_F3
51 #define BTN_STARTPAUSE BUTTON_PLAY
52 #define BTN_QUIT BUTTON_OFF
53 #define BTN_STOPRESET BUTTON_ON
54 #define BTN_TOGGLE_KEYS BUTTON_F1
56 #if BUTTON_REMOTE != 0
57 #define BTN_RC_UP BUTTON_RC_VOL_UP
58 #define BTN_RC_DOWN BUTTON_RC_VOL_DOWN
63 #define PLAYERS_TEXT "UP/DN"
64 #define WORMS_TEXT "L/R"
65 #define KEY_CONTROL_TEXT "F1"
67 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
68 #define BTN_DIR_UP BUTTON_UP
69 #define BTN_DIR_DOWN BUTTON_DOWN
70 #define BTN_DIR_LEFT BUTTON_LEFT
71 #define BTN_DIR_RIGHT BUTTON_RIGHT
72 #define BTN_PLAYER2_DIR1 BUTTON_F2
73 #define BTN_PLAYER2_DIR2 BUTTON_F3
74 #define BTN_STARTPAUSE BUTTON_SELECT
75 #define BTN_QUIT BUTTON_OFF
76 #define BTN_STOPRESET BUTTON_ON
77 #define BTN_TOGGLE_KEYS BUTTON_F1
79 #define PLAYERS_TEXT "UP/DN"
80 #define WORMS_TEXT "L/R"
81 #define KEY_CONTROL_TEXT "F1"
83 #elif (CONFIG_KEYPAD == ONDIO_PAD)
84 #define BTN_DIR_UP BUTTON_UP
85 #define BTN_DIR_DOWN BUTTON_DOWN
86 #define BTN_DIR_LEFT BUTTON_LEFT
87 #define BTN_DIR_RIGHT BUTTON_RIGHT
88 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
89 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
90 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
92 #define PLAYERS_TEXT "UP/DN"
93 #define WORMS_TEXT "L/R"
95 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
96 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
98 #define BTN_DIR_UP BUTTON_MENU
99 #define BTN_DIR_DOWN BUTTON_PLAY
100 #define BTN_DIR_LEFT BUTTON_LEFT
101 #define BTN_DIR_RIGHT BUTTON_RIGHT
102 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
103 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
104 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
106 #define PLAYERS_TEXT "Menu/Play"
107 #define WORMS_TEXT "Left/Right"
109 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
111 #define BTN_DIR_UP BUTTON_UP
112 #define BTN_DIR_DOWN BUTTON_DOWN
113 #define BTN_DIR_LEFT BUTTON_LEFT
114 #define BTN_DIR_RIGHT BUTTON_RIGHT
115 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
116 #define BTN_QUIT BUTTON_OFF
117 #define BTN_STOPRESET BUTTON_ON
119 #define BTN_RC_QUIT BUTTON_RC_STOP
121 #define PLAYERS_TEXT "Up/Down"
122 #define WORMS_TEXT "Left/Right"
124 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
126 #define BTN_DIR_UP BUTTON_UP
127 #define BTN_DIR_DOWN BUTTON_DOWN
128 #define BTN_DIR_LEFT BUTTON_LEFT
129 #define BTN_DIR_RIGHT BUTTON_RIGHT
130 #define BTN_STARTPAUSE BUTTON_PLAY
131 #define BTN_QUIT BUTTON_POWER
132 #define BTN_STOPRESET BUTTON_REC
134 #define PLAYERS_TEXT "Up/Down"
135 #define WORMS_TEXT "Left/Right"
137 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
139 #define BTN_DIR_UP BUTTON_UP
140 #define BTN_DIR_DOWN BUTTON_DOWN
141 #define BTN_DIR_LEFT BUTTON_LEFT
142 #define BTN_DIR_RIGHT BUTTON_RIGHT
143 #define BTN_STARTPAUSE BUTTON_SELECT
144 #define BTN_QUIT BUTTON_POWER
145 #define BTN_STOPRESET BUTTON_A
147 #define PLAYERS_TEXT "Up/Down"
148 #define WORMS_TEXT "Left/Right"
151 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
152 (CONFIG_KEYPAD == SANSA_C200_PAD)
154 #define BTN_DIR_UP BUTTON_UP
155 #define BTN_DIR_DOWN BUTTON_DOWN
156 #define BTN_DIR_LEFT BUTTON_LEFT
157 #define BTN_DIR_RIGHT BUTTON_RIGHT
158 #define BTN_STARTPAUSE BUTTON_SELECT
159 #define BTN_QUIT BUTTON_POWER
160 #define BTN_STOPRESET BUTTON_REC
162 #define PLAYERS_TEXT "Up/Down"
163 #define WORMS_TEXT "Left/Right"
166 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
168 #define BTN_DIR_UP BUTTON_SCROLL_UP
169 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
170 #define BTN_DIR_LEFT BUTTON_LEFT
171 #define BTN_DIR_RIGHT BUTTON_RIGHT
172 #define BTN_STARTPAUSE BUTTON_PLAY
173 #define BTN_QUIT BUTTON_POWER
174 #define BTN_STOPRESET BUTTON_REW
176 #define PLAYERS_TEXT "Up/Down"
177 #define WORMS_TEXT "Left/Right"
179 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
181 #define BTN_DIR_UP BUTTON_UP
182 #define BTN_DIR_DOWN BUTTON_DOWN
183 #define BTN_DIR_LEFT BUTTON_LEFT
184 #define BTN_DIR_RIGHT BUTTON_RIGHT
185 #define BTN_STARTPAUSE BUTTON_SELECT
186 #define BTN_QUIT BUTTON_BACK
187 #define BTN_STOPRESET BUTTON_MENU
189 #define PLAYERS_TEXT "Up/Down"
190 #define WORMS_TEXT "Left/Right"
192 #elif (CONFIG_KEYPAD == MROBE100_PAD)
194 #define BTN_DIR_UP BUTTON_UP
195 #define BTN_DIR_DOWN BUTTON_DOWN
196 #define BTN_DIR_LEFT BUTTON_LEFT
197 #define BTN_DIR_RIGHT BUTTON_RIGHT
198 #define BTN_STARTPAUSE BUTTON_SELECT
199 #define BTN_QUIT BUTTON_POWER
200 #define BTN_STOPRESET BUTTON_DISPLAY
202 #define PLAYERS_TEXT "Up/Down"
203 #define WORMS_TEXT "Left/Right"
205 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
207 #define BTN_DIR_UP BUTTON_RC_VOL_UP
208 #define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN
209 #define BTN_DIR_LEFT BUTTON_RC_REW
210 #define BTN_DIR_RIGHT BUTTON_RC_FF
211 #define BTN_STARTPAUSE BUTTON_RC_PLAY
212 #define BTN_QUIT BUTTON_RC_REC
213 #define BTN_STOPRESET BUTTON_RC_MODE
215 #define PLAYERS_TEXT "VOL UP/DN"
216 #define WORMS_TEXT "REW/FF"
218 #elif (CONFIG_KEYPAD == COWOND2_PAD)
220 #define BTN_DIR_UP BUTTON_UP
221 #define BTN_DIR_DOWN BUTTON_DOWN
222 #define BTN_DIR_LEFT BUTTON_LEFT
223 #define BTN_DIR_RIGHT BUTTON_RIGHT
224 #define BTN_STARTPAUSE BUTTON_SELECT
225 #define BTN_QUIT BUTTON_POWER
226 #define BTN_STOPRESET BUTTON_PLUS
228 #define PLAYERS_TEXT "Up/Down"
229 #define WORMS_TEXT "Left/Right"
232 #error No keymap defined!
235 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
239 #define MAX_WORM_SEGMENTS 128
240 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
244 #define MAX_WORM_SEGMENTS 128
245 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
249 #define MAX_WORM_SEGMENTS 128
250 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
254 #define MAX_WORM_SEGMENTS 128
255 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
259 #define MAX_WORM_SEGMENTS 128
260 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
264 #define MAX_WORM_SEGMENTS 256
265 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
269 #define MAX_WORM_SEGMENTS 256
270 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
274 #define MAX_WORM_SEGMENTS 512
275 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
279 #define MAX_WORM_SEGMENTS 512
280 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
284 #define MAX_WORM_SEGMENTS 512
285 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
289 #define MAX_WORM_SEGMENTS 512
292 #ifdef HAVE_LCD_COLOR
293 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
294 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
295 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
296 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
297 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
301 * All the properties that a worm has.
304 /* The worm is stored in a ring of xy coordinates */
305 int x
[MAX_WORM_SEGMENTS
];
306 int y
[MAX_WORM_SEGMENTS
];
308 int head
; /* index of the head within the buffer */
309 int tail
; /* index of the tail within the buffer */
310 int growing
; /* number of cyles the worm still keeps growing */
311 bool alive
; /* the worms living state */
313 /* direction vector in which the worm moves */
314 int dirx
; /* only values -1 0 1 allowed */
315 int diry
; /* only values -1 0 1 allowed */
317 /* this method is used to fetch the direction the user
318 has selected. It can be one of the values
319 human_player1, human_player2, remote_player, virtual_player.
320 All these values are fuctions, that can change the direction
322 void (*fetch_worm_direction
)(struct worm
*w
);
325 /* stores the highscore - besides it was scored by a virtual player */
326 static int highscore
;
328 #define MAX_FOOD 5 /* maximal number of food items */
330 /* The arrays store the food coordinates */
331 static int foodx
[MAX_FOOD
];
332 static int foody
[MAX_FOOD
];
334 #define MAX_ARGH 100 /* maximal number of argh items */
335 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
337 /* The arrays store the argh coordinates */
338 static int arghx
[MAX_ARGH
];
339 static int arghy
[MAX_ARGH
];
341 /* the number of arghs that are currently in use */
342 static int argh_count
;
344 /* the number of arghs per food, settable by user */
345 static int arghs_per_food
= ARGHS_PER_FOOD
;
346 /* the size of the argh, settable by user */
347 static int argh_size
= ARGH_SIZE
;
348 /* the size of the food, settable by user */
349 static int food_size
= FOOD_SIZE
;
350 /* the speed of the worm, settable by user */
351 static int speed
= SPEED
;
352 /* the amount a worm grows by eating a food, settable by user */
353 static int worm_food
= WORM_PER_FOOD
;
355 /* End additional variables */
358 /* just a buffer used for debug output */
359 static char debugout
[15];
362 /* the number of active worms (dead or alive) */
363 static int worm_count
= MAX_WORMS
;
365 /* in multiplayer mode: en- / disables the remote worm control
366 in singleplayer mode: toggles 4 / 2 button worm control */
367 static bool use_remote
= false;
369 /* return values of check_collision */
370 #define COLLISION_NONE 0
371 #define COLLISION_WORM 1
372 #define COLLISION_FOOD 2
373 #define COLLISION_ARGH 3
374 #define COLLISION_FIELD 4
376 /* constants for use as directions.
377 Note that the values are ordered clockwise.
378 Thus increasing / decreasing the values
379 is equivalent to right / left turns. */
385 /* direction of human player 1 */
386 static int player1_dir
= EAST
;
387 /* direction of human player 2 */
388 static int player2_dir
= EAST
;
389 /* direction of human player 3 */
390 static int player3_dir
= EAST
;
392 /* the number of (human) players that currently
394 static int players
= 1;
396 /* the rockbox plugin api */
397 static struct plugin_api
* rb
;
399 #define SETTINGS_VERSION 1
400 #define SETTINGS_MIN_VERSION 1
401 #define SETTINGS_FILENAME "wormlet.cfg"
403 static struct configdata config
[] =
405 {TYPE_INT
, 0, 1024, &highscore
, "highscore", NULL
, NULL
},
406 {TYPE_INT
, 0, 15, &arghs_per_food
, "arghs per food", NULL
, NULL
},
407 {TYPE_INT
, 0, 15, &argh_size
, "argh size", NULL
, NULL
},
408 {TYPE_INT
, 0, 15, &food_size
, "food size", NULL
, NULL
},
409 {TYPE_INT
, 0, 3, &players
, "players", NULL
, NULL
},
410 {TYPE_INT
, 0, 3, &worm_count
, "worms", NULL
, NULL
},
411 {TYPE_INT
, 0, 20, &speed
, "speed", NULL
, NULL
},
412 {TYPE_INT
, 0, 15, &worm_food
, "Worm Growth Per Food", NULL
, NULL
}//,
413 //{TYPE_INT, 0, 3, &use_remote, "use remote", NULL, NULL}
417 static void set_debug_out(char *str
){
418 strcpy(debugout
, str
);
423 * Returns the direction id in which the worm
424 * currently is creeping.
425 * @param struct worm *w The worm that is to be investigated.
426 * w Must not be null.
427 * @return int A value 0 <= value < 4
428 * Note the predefined constants NORTH, SOUTH, EAST, WEST
430 static int get_worm_dir(struct worm
*w
) {
449 * Set the direction of the specified worm with a direction id.
450 * Increasing the value by 1 means to turn the worm direction
451 * to right by 90 degree.
452 * @param struct worm *w The worm that is to be altered. w Must not be null.
453 * @param int dir The new direction in which the worm is to creep.
454 * dir must be 0 <= dir < 4. Use predefined constants
455 * NORTH, SOUTH, EAST, WEST
457 static void set_worm_dir(struct worm
*w
, int dir
) {
479 * Returns the current length of the worm array. This
480 * is also a value for the number of bends that are in the worm.
481 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
483 static int get_worm_array_length(struct worm
*w
) {
484 /* initial simple calculation will be overwritten if wrong. */
485 int retVal
= w
->head
- w
->tail
;
487 /* if the worm 'crosses' the boundaries of the ringbuffer */
489 retVal
= w
->head
+ MAX_WORM_SEGMENTS
- w
->tail
;
496 * Returns the score the specified worm. The score is the length
498 * @param struct worm *w The worm that is to be investigated.
499 * w must not be null.
500 * @return int The length of the worm (>= 0).
502 static int get_score(struct worm
*w
) {
504 int length
= get_worm_array_length(w
);
506 for (i
= 0; i
< length
; i
++) {
508 /* The iteration iterates the length of the worm.
509 Here's the conversion to the true indices within the worm arrays. */
510 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
511 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
512 int startx
= w
->x
[linestart
];
513 int starty
= w
->y
[linestart
];
514 int endx
= w
->x
[lineend
];
515 int endy
= w
->y
[lineend
];
517 int minimum
, maximum
;
519 if (startx
== endx
) {
520 minimum
= MIN(starty
, endy
);
521 maximum
= MAX(starty
, endy
);
523 minimum
= MIN(startx
, endx
);
524 maximum
= MAX(startx
, endx
);
526 retval
+= abs(maximum
- minimum
);
532 * Determines wether the line specified by startx, starty, endx, endy intersects
533 * the rectangle specified by x, y, width, height. Note that the line must be exactly
534 * horizontal or vertical (startx == endx or starty == endy).
535 * @param int startx The x coordinate of the start point of the line.
536 * @param int starty The y coordinate of the start point of the line.
537 * @param int endx The x coordinate of the end point of the line.
538 * @param int endy The y coordinate of the end point of the line.
539 * @param int x The x coordinate of the top left corner of the rectangle.
540 * @param int y The y coordinate of the top left corner of the rectangle.
541 * @param int width The width of the rectangle.
542 * @param int height The height of the rectangle.
543 * @return bool Returns true if the specified line intersects with the recangle.
545 static bool line_in_rect(int startx
, int starty
, int endx
, int endy
, int x
, int y
, int width
, int height
) {
547 int simple
, simplemin
, simplemax
;
548 int compa
, compb
, compmin
, compmax
;
550 if (startx
== endx
) {
553 simplemax
= x
+ width
;
558 compmax
= y
+ height
;
562 simplemax
= y
+ height
;
571 compa
= MIN(compa
, compb
);
572 compb
= MAX(temp
, compb
);
574 if (simplemin
<= simple
&& simple
<= simplemax
) {
575 if ((compmin
<= compa
&& compa
<= compmax
) ||
576 (compmin
<= compb
&& compb
<= compmax
) ||
577 (compa
<= compmin
&& compb
>= compmax
)) {
585 * Tests wether the specified worm intersects with the rect.
586 * @param struct worm *w The worm to be investigated
587 * @param int x The x coordinate of the top left corner of the rect
588 * @param int y The y coordinate of the top left corner of the rect
589 * @param int widht The width of the rect
590 * @param int height The height of the rect
591 * @return bool Returns true if the worm intersects with the rect
593 static bool worm_in_rect(struct worm
*w
, int x
, int y
, int width
, int height
) {
597 /* get_worm_array_length is expensive -> buffer the value */
598 int wormLength
= get_worm_array_length(w
);
601 /* test each entry that is part of the worm */
602 for (i
= 0; i
< wormLength
&& retval
== false; i
++) {
604 /* The iteration iterates the length of the worm.
605 Here's the conversion to the true indices within the worm arrays. */
606 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
607 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
608 int startx
= w
->x
[linestart
];
609 int starty
= w
->y
[linestart
];
610 int endx
= w
->x
[lineend
];
611 int endy
= w
->y
[lineend
];
613 retval
= line_in_rect(startx
, starty
, endx
, endy
, x
, y
, width
, height
);
620 * Checks wether a specific food in the food arrays is at the
621 * specified coordinates.
622 * @param int foodIndex The index of the food in the food arrays
623 * @param int x the x coordinate.
624 * @param int y the y coordinate.
625 * @return Returns true if the coordinate hits the food specified by
628 static bool specific_food_collision(int foodIndex
, int x
, int y
) {
630 if (x
>= foodx
[foodIndex
] &&
631 x
< foodx
[foodIndex
] + food_size
&&
632 y
>= foody
[foodIndex
] &&
633 y
< foody
[foodIndex
] + food_size
) {
641 * Returns the index of the food that is at the
642 * given coordinates. If no food is at the coordinates
644 * @return int -1 <= value < MAX_FOOD
646 static int food_collision(int x
, int y
) {
649 for (i
= 0; i
< MAX_FOOD
; i
++) {
650 if (specific_food_collision(i
, x
, y
)) {
659 * Checks wether a specific argh in the argh arrays is at the
660 * specified coordinates.
661 * @param int arghIndex The index of the argh in the argh arrays
662 * @param int x the x coordinate.
663 * @param int y the y coordinate.
664 * @return Returns true if the coordinate hits the argh specified by
667 static bool specific_argh_collision(int arghIndex
, int x
, int y
) {
669 if ( x
>= arghx
[arghIndex
] &&
670 y
>= arghy
[arghIndex
] &&
671 x
< arghx
[arghIndex
] + argh_size
&&
672 y
< arghy
[arghIndex
] + argh_size
)
681 * Returns the index of the argh that is at the
682 * given coordinates. If no argh is at the coordinates
684 * @param int x The x coordinate.
685 * @param int y The y coordinate.
686 * @return int -1 <= value < argh_count <= MAX_ARGH
688 static int argh_collision(int x
, int y
) {
692 /* search for the argh that has the specified coords */
693 for (i
= 0; i
< argh_count
; i
++) {
694 if (specific_argh_collision(i
, x
, y
)) {
703 * Checks wether the worm collides with the food at the specfied food-arrays.
704 * @param int foodIndex The index of the food in the arrays. Ensure the value is
705 * 0 <= foodIndex <= MAX_FOOD
706 * @return Returns true if the worm collides with the specified food.
708 static bool worm_food_collision(struct worm
*w
, int foodIndex
)
712 retVal
= worm_in_rect(w
, foodx
[foodIndex
], foody
[foodIndex
],
713 food_size
- 1, food_size
- 1);
719 * Returns true if the worm hits the argh within the next moves (unless
720 * the worm changes it's direction).
721 * @param struct worm *w - The worm to investigate
722 * @param int argh_idx - The index of the argh
723 * @param int moves - The number of moves that are considered.
724 * @return Returns false if the specified argh is not hit within the next
727 static bool worm_argh_collision_in_moves(struct worm
*w
, int argh_idx
, int moves
){
733 x2
= w
->x
[w
->head
] + moves
* w
->dirx
;
734 y2
= w
->y
[w
->head
] + moves
* w
->diry
;
736 retVal
= line_in_rect(x1
, y1
, x2
, y2
, arghx
[argh_idx
], arghy
[argh_idx
],
737 argh_size
, argh_size
);
742 * Checks wether the worm collides with the argh at the specfied argh-arrays.
743 * @param int arghIndex The index of the argh in the arrays.
744 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
745 * @return Returns true if the worm collides with the specified argh.
747 static bool worm_argh_collision(struct worm
*w
, int arghIndex
)
751 retVal
= worm_in_rect(w
, arghx
[arghIndex
], arghy
[arghIndex
],
752 argh_size
- 1, argh_size
- 1);
758 * Find new coordinates for the food stored in foodx[index], foody[index]
759 * that don't collide with any other food or argh
761 * Ensure that 0 <= index < MAX_FOOD.
763 static void make_food(int index
) {
767 bool collisionDetected
= false;
771 /* make coordinates for a new food so that
772 the entire food lies within the FIELD */
773 x
= rb
->rand() % (FIELD_RECT_WIDTH
- food_size
);
774 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- food_size
);
776 /* Ensure that the new food doesn't collide with any
777 existing foods or arghs.
778 If one or more corners of the new food hit any existing
779 argh or food a collision is detected.
782 food_collision(x
, y
) >= 0 ||
783 food_collision(x
, y
+ food_size
- 1) >= 0 ||
784 food_collision(x
+ food_size
- 1, y
) >= 0 ||
785 food_collision(x
+ food_size
- 1, y
+ food_size
- 1) >= 0 ||
786 argh_collision(x
, y
) >= 0 ||
787 argh_collision(x
, y
+ food_size
- 1) >= 0 ||
788 argh_collision(x
+ food_size
- 1, y
) >= 0 ||
789 argh_collision(x
+ food_size
- 1, y
+ food_size
- 1) >= 0;
791 /* use coordinates for further testing */
795 /* now test wether we accidently hit the worm with food ;) */
797 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
798 collisionDetected
|= worm_food_collision(&worms
[i
], index
);
801 while (collisionDetected
);
806 * Clears a food from the lcd buffer.
807 * @param int index The index of the food arrays under which
808 * the coordinates of the desired food can be found. Ensure
809 * that the value is 0 <= index <= MAX_FOOD.
811 static void clear_food(int index
)
813 /* remove the old food from the screen */
814 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
815 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
816 foody
[index
] + FIELD_RECT_Y
,
817 food_size
, food_size
);
818 rb
->lcd_set_drawmode(DRMODE_SOLID
);
822 * Draws a food in the lcd buffer.
823 * @param int index The index of the food arrays under which
824 * the coordinates of the desired food can be found. Ensure
825 * that the value is 0 <= index <= MAX_FOOD.
827 static void draw_food(int index
)
829 /* draw the food object */
830 #ifdef HAVE_LCD_COLOR
831 rb
->lcd_set_foreground(COLOR_FOOD
);
833 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
834 foody
[index
] + FIELD_RECT_Y
,
835 food_size
, food_size
);
836 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
837 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
+ 1,
838 foody
[index
] + FIELD_RECT_Y
+ 1,
839 food_size
- 2, food_size
- 2);
840 rb
->lcd_set_drawmode(DRMODE_SOLID
);
841 #ifdef HAVE_LCD_COLOR
842 rb
->lcd_set_foreground(COLOR_FG
);
847 * Find new coordinates for the argh stored in arghx[index], arghy[index]
848 * that don't collide with any other food or argh.
850 * Ensure that 0 <= index < argh_count < MAX_ARGH.
852 static void make_argh(int index
)
856 bool collisionDetected
= false;
860 /* make coordinates for a new argh so that
861 the entire food lies within the FIELD */
862 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
863 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
865 /* Ensure that the new argh doesn't intersect with any
866 existing foods or arghs.
867 If one or more corners of the new argh hit any existing
868 argh or food an intersection is detected.
871 food_collision(x
, y
) >= 0 ||
872 food_collision(x
, y
+ argh_size
- 1) >= 0 ||
873 food_collision(x
+ argh_size
- 1, y
) >= 0 ||
874 food_collision(x
+ argh_size
- 1, y
+ argh_size
- 1) >= 0 ||
875 argh_collision(x
, y
) >= 0 ||
876 argh_collision(x
, y
+ argh_size
- 1) >= 0 ||
877 argh_collision(x
+ argh_size
- 1, y
) >= 0 ||
878 argh_collision(x
+ argh_size
- 1, y
+ argh_size
- 1) >= 0;
880 /* use the candidate coordinates to make a real argh */
884 /* now test wether we accidently hit the worm with argh ;) */
885 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
886 collisionDetected
|= worm_argh_collision(&worms
[i
], index
);
887 collisionDetected
|= worm_argh_collision_in_moves(&worms
[i
], index
,
891 while (collisionDetected
);
896 * Draws an argh in the lcd buffer.
897 * @param int index The index of the argh arrays under which
898 * the coordinates of the desired argh can be found. Ensure
899 * that the value is 0 <= index < argh_count <= MAX_ARGH.
901 static void draw_argh(int index
)
903 /* draw the new argh */
904 #ifdef HAVE_LCD_COLOR
905 rb
->lcd_set_foreground(COLOR_ARGH
);
907 rb
->lcd_fillrect(arghx
[index
] + FIELD_RECT_X
,
908 arghy
[index
] + FIELD_RECT_Y
,
909 argh_size
, argh_size
);
910 #ifdef HAVE_LCD_COLOR
911 rb
->lcd_set_foreground(COLOR_FG
);
915 static void virtual_player(struct worm
*w
);
917 * Initialzes the specified worm with INITIAL_WORM_LENGTH
918 * and the tail at the specified position. The worm will
919 * be initialized alive and creeping EAST.
920 * @param struct worm *w The worm that is to be initialized
921 * @param int x The x coordinate at which the tail of the worm starts.
922 * x must be 0 <= x < FIELD_RECT_WIDTH.
923 * @param int y The y coordinate at which the tail of the worm starts
924 * y must be 0 <= y < FIELD_RECT_WIDTH.
926 static void init_worm(struct worm
*w
, int x
, int y
){
927 /* initialize the worm size */
931 w
->x
[w
->head
] = x
+ 1;
937 /* set the initial direction the worm creeps to */
941 w
->growing
= INITIAL_WORM_LENGTH
- 1;
943 w
->fetch_worm_direction
= virtual_player
;
947 * Writes the direction that was stored for
948 * human player 1 into the specified worm. This function
949 * may be used to be stored in worm.fetch_worm_direction.
951 * the direction is read from player1_dir.
952 * @param struct worm *w - The worm of which the direction
955 static void human_player1(struct worm
*w
) {
956 set_worm_dir(w
, player1_dir
);
960 * Writes the direction that was stored for
961 * human player 2 into the specified worm. This function
962 * may be used to be stored in worm.fetch_worm_direction.
964 * the direction is read from player2_dir.
965 * @param struct worm *w - The worm of which the direction
968 static void human_player2(struct worm
*w
) {
969 set_worm_dir(w
, player2_dir
);
973 * Writes the direction that was stored for
974 * human player using a remote control
975 * into the specified worm. This function
976 * may be used to be stored in worm.fetch_worm_direction.
978 * the direction is read from player3_dir.
979 * @param struct worm *w - The worm of which the direction
982 static void remote_player(struct worm
*w
) {
983 set_worm_dir(w
, player3_dir
);
987 * Initializes the worm-, food- and argh-arrays, draws a frame,
988 * makes some food and argh and display all that stuff.
990 static void init_wormlet(void)
994 for (i
= 0; i
< worm_count
; i
++) {
995 /* Initialize all the worm coordinates to center. */
996 int x
= (int)(FIELD_RECT_WIDTH
/ 2);
997 int y
= (int)((FIELD_RECT_HEIGHT
- 20)/ 2) + i
* 10;
999 init_worm(&worms
[i
], x
, y
);
1007 worms
[0].fetch_worm_direction
= human_player1
;
1012 worms
[1].fetch_worm_direction
= remote_player
;
1014 worms
[1].fetch_worm_direction
= human_player2
;
1019 worms
[2].fetch_worm_direction
= human_player2
;
1022 /* Needed when the game is restarted using BTN_STOPRESET */
1023 rb
->lcd_clear_display();
1025 /* make and display some food and argh */
1026 argh_count
= MAX_FOOD
;
1027 for (i
= 0; i
< MAX_FOOD
; i
++) {
1034 /* draw the game field */
1035 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1036 rb
->lcd_fillrect(0, 0, FIELD_RECT_WIDTH
+ 2, FIELD_RECT_HEIGHT
+ 2);
1037 rb
->lcd_fillrect(1, 1, FIELD_RECT_WIDTH
, FIELD_RECT_HEIGHT
);
1038 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1040 /* make everything visible */
1046 * Move the worm one step further if it is alive.
1047 * The direction in which the worm moves is taken from dirx and diry.
1048 * move_worm decreases growing if > 0. While the worm is growing the tail
1049 * is left untouched.
1050 * @param struct worm *w The worm to move. w must not be NULL.
1052 static void move_worm(struct worm
*w
)
1055 /* determine the head point and its precessor */
1056 int headx
= w
->x
[w
->head
];
1057 int heady
= w
->y
[w
->head
];
1058 int prehead
= (w
->head
+ MAX_WORM_SEGMENTS
- 1) % MAX_WORM_SEGMENTS
;
1059 int preheadx
= w
->x
[prehead
];
1060 int preheady
= w
->y
[prehead
];
1062 /* determine the old direction */
1065 if (headx
== preheadx
) {
1067 olddiry
= (heady
> preheady
) ? 1 : -1;
1070 olddirx
= (headx
> preheadx
) ? 1 : -1;
1074 a change of direction means a new segment
1076 if (olddirx
!= w
->dirx
||
1077 olddiry
!= w
->diry
) {
1078 w
->head
= (w
->head
+ 1) % MAX_WORM_SEGMENTS
;
1081 /* new head position */
1082 w
->x
[w
->head
] = headx
+ w
->dirx
;
1083 w
->y
[w
->head
] = heady
+ w
->diry
;
1086 /* while the worm is growing no tail procession is necessary */
1087 if (w
->growing
> 0) {
1088 /* update the worms grow state */
1092 /* if the worm isn't growing the tail has to be dragged */
1094 /* index of the end of the tail segment */
1095 int tail_segment_end
= (w
->tail
+ 1) % MAX_WORM_SEGMENTS
;
1097 /* drag the end of the tail */
1098 /* only one coordinate has to be altered. Here it is
1099 determined which one */
1100 int dir
= 0; /* specifies wether the coord has to be in- or decreased */
1101 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
]) {
1102 dir
= (w
->y
[w
->tail
] - w
->y
[tail_segment_end
] < 0) ? 1 : -1;
1103 w
->y
[w
->tail
] += dir
;
1105 dir
= (w
->x
[w
->tail
] - w
->x
[tail_segment_end
] < 0) ? 1 : -1;
1106 w
->x
[w
->tail
] += dir
;
1109 /* when the tail has been dragged so far that it meets
1110 the next segment start the tail segment is obsolete and
1112 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
] &&
1113 w
->y
[w
->tail
] == w
->y
[tail_segment_end
]){
1115 /* drop the last tail point */
1116 w
->tail
= tail_segment_end
;
1123 * Draws the head and clears the tail of the worm in
1124 * the display buffer. lcd_update() is NOT called thus
1125 * the caller has to take care that the buffer is displayed.
1127 static void draw_worm(struct worm
*w
)
1129 #ifdef HAVE_LCD_COLOR
1130 rb
->lcd_set_foreground(COLOR_WORM
);
1132 /* draw the new head */
1133 int x
= w
->x
[w
->head
];
1134 int y
= w
->y
[w
->head
];
1135 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1136 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1139 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1141 /* clear the space behind the worm */
1144 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1145 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1147 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1148 #ifdef HAVE_LCD_COLOR
1149 rb
->lcd_set_foreground(COLOR_FG
);
1154 * Checks wether the coordinate is part of the worm. Returns
1155 * true if any part of the worm was hit - including the head.
1156 * @param x int The x coordinate
1157 * @param y int The y coordinate
1158 * @return int The index of the worm arrays that contain x, y.
1159 * Returns -1 if the coordinates are not part of the worm.
1161 static int specific_worm_collision(struct worm
*w
, int x
, int y
)
1165 /* get_worm_array_length is expensive -> buffer the value */
1166 int wormLength
= get_worm_array_length(w
);
1169 /* test each entry that is part of the worm */
1170 for (i
= 0; i
< wormLength
&& retVal
== -1; i
++) {
1172 /* The iteration iterates the length of the worm.
1173 Here's the conversion to the true indices within the worm arrays. */
1174 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
1175 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
1176 bool samex
= (w
->x
[linestart
] == x
) && (w
->x
[lineend
] == x
);
1177 bool samey
= (w
->y
[linestart
] == y
) && (w
->y
[lineend
] == y
);
1178 if (samex
|| samey
){
1179 int test
, min
, max
, tmp
;
1182 min
= w
->x
[linestart
];
1183 max
= w
->x
[lineend
];
1186 min
= w
->y
[linestart
];
1187 max
= w
->y
[lineend
];
1192 min
= MIN(min
, max
);
1193 max
= MAX(tmp
, max
);
1195 if (min
<= test
&& test
<= max
) {
1204 * Increases the length of the specified worm by marking
1205 * that it may grow by len pixels. Note that the worm has
1206 * to move to make the growing happen.
1207 * @param worm *w The worm that is to be altered.
1208 * @param int len A positive value specifying the amount of
1209 * pixels the worm may grow.
1211 static void add_growing(struct worm
*w
, int len
) {
1216 * Determins the worm that is at the coordinates x, y. The parameter
1217 * w is a switch parameter that changes the functionality of worm_collision.
1218 * If w is specified and x,y hits the head of w NULL is returned.
1219 * This is a useful way to determine wether the head of w hits
1220 * any worm but including itself but excluding its own head.
1221 * (It hits always its own head ;))
1222 * If w is set to NULL worm_collision returns any worm including all heads
1223 * that is at position of x,y.
1224 * @param struct worm *w The worm of which the head should be excluded in
1225 * the test. w may be set to NULL.
1226 * @param int x The x coordinate that is checked
1227 * @param int y The y coordinate that is checkec
1228 * @return struct worm* The worm that has been hit by x,y. If no worm
1229 * was at the position NULL is returned.
1231 static struct worm
* worm_collision(struct worm
*w
, int x
, int y
){
1232 struct worm
*retVal
= NULL
;
1234 for (i
= 0; (i
< worm_count
) && (retVal
== NULL
); i
++) {
1235 int collision_at
= specific_worm_collision(&worms
[i
], x
, y
);
1236 if (collision_at
!= -1) {
1237 if (!(w
== &worms
[i
] && collision_at
== w
->head
)){
1246 * Returns true if the head of the worm just has
1247 * crossed the field boundaries.
1248 * @return bool true if the worm just has wrapped.
1250 static bool field_collision(struct worm
*w
)
1252 bool retVal
= false;
1253 if ((w
->x
[w
->head
] >= FIELD_RECT_WIDTH
) ||
1254 (w
->y
[w
->head
] >= FIELD_RECT_HEIGHT
) ||
1255 (w
->x
[w
->head
] < 0) ||
1256 (w
->y
[w
->head
] < 0))
1265 * Returns true if the specified coordinates are within the
1266 * field specified by the FIELD_RECT_XXX constants.
1267 * @param int x The x coordinate of the point that is investigated
1268 * @param int y The y coordinate of the point that is investigated
1269 * @return bool Returns false if x,y specifies a point outside the
1272 static bool is_in_field_rect(int x
, int y
) {
1273 bool retVal
= false;
1274 retVal
= (x
>= 0 && x
< FIELD_RECT_WIDTH
&&
1275 y
>= 0 && y
< FIELD_RECT_HEIGHT
);
1280 * Checks and returns wether the head of the w
1281 * is colliding with something currently.
1282 * @return int One of the values:
1289 static int check_collision(struct worm
*w
)
1291 int retVal
= COLLISION_NONE
;
1293 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
)
1294 retVal
= COLLISION_WORM
;
1296 if (food_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1297 retVal
= COLLISION_FOOD
;
1299 if (argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1300 retVal
= COLLISION_ARGH
;
1302 if (field_collision(w
))
1303 retVal
= COLLISION_FIELD
;
1309 * Returns the index of the food that is closest to the point
1310 * specified by x, y. This index may be used in the foodx and
1312 * @param int x The x coordinate of the point
1313 * @param int y The y coordinate of the point
1314 * @return int A value usable as index in foodx and foody.
1316 static int get_nearest_food(int x
, int y
){
1317 int nearestfood
= 0;
1318 int olddistance
= FIELD_RECT_WIDTH
+ FIELD_RECT_HEIGHT
;
1322 for (foodindex
= 0; foodindex
< MAX_FOOD
; foodindex
++) {
1324 deltax
= foodx
[foodindex
] - x
;
1325 deltay
= foody
[foodindex
] - y
;
1326 deltax
= deltax
> 0 ? deltax
: deltax
* (-1);
1327 deltay
= deltay
> 0 ? deltay
: deltay
* (-1);
1328 distance
= deltax
+ deltay
;
1330 if (distance
< olddistance
) {
1331 olddistance
= distance
;
1332 nearestfood
= foodindex
;
1339 * Returns wether the specified position is next to the worm
1340 * and in the direction the worm looks. Use this method to
1341 * test wether this position would be hit with the next move of
1342 * the worm unless the worm changes its direction.
1343 * @param struct worm *w - The worm to be investigated
1344 * @param int x - The x coordinate of the position to test.
1345 * @param int y - The y coordinate of the position to test.
1346 * @return Returns true if the worm will hit the position unless
1347 * it change its direction before the next move.
1349 static bool is_in_front_of_worm(struct worm
*w
, int x
, int y
) {
1350 bool infront
= false;
1351 int deltax
= x
- w
->x
[w
->head
];
1352 int deltay
= y
- w
->y
[w
->head
];
1355 infront
= (w
->diry
* deltay
) > 0;
1357 infront
= (w
->dirx
* deltax
) > 0;
1363 * Returns true if the worm will collide with the next move unless
1364 * it changes its direction.
1365 * @param struct worm *w - The worm to be investigated.
1366 * @return Returns true if the worm will collide with the next move
1367 * unless it changes its direction.
1369 static bool will_worm_collide(struct worm
*w
) {
1370 int x
= w
->x
[w
->head
] + w
->dirx
;
1371 int y
= w
->y
[w
->head
] + w
->diry
;
1372 bool retVal
= !is_in_field_rect(x
, y
);
1374 retVal
= (argh_collision(x
, y
) != -1);
1378 retVal
= (worm_collision(w
, x
, y
) != NULL
);
1385 * may be used to be stored in worm.fetch_worm_direction for
1386 * worms that are not controlled by humans but by artificial stupidity.
1387 * A direction is searched that doesn't lead to collision but to the nearest
1388 * food - but not very intelligent. The direction is written to the specified
1390 * @param struct worm *w - The worm of which the direction
1393 static void virtual_player(struct worm
*w
) {
1395 int plana
, planb
, planc
;
1396 /* find the next lunch */
1397 int nearestfood
= get_nearest_food(w
->x
[w
->head
], w
->y
[w
->head
]);
1399 /* determine in which direction it is */
1401 /* in front of me? */
1402 bool infront
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1404 /* left right of me? */
1405 int olddir
= get_worm_dir(w
);
1406 set_worm_dir(w
, (olddir
+ 1) % 4);
1407 isright
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1408 set_worm_dir(w
, olddir
);
1410 /* detect situation, set strategy */
1414 planb
= (olddir
+ 1) % 4;
1415 planc
= (olddir
+ 3) % 4;
1418 planb
= (olddir
+ 3) % 4;
1419 planc
= (olddir
+ 1) % 4;
1423 plana
= (olddir
+ 1) % 4;
1425 planc
= (olddir
+ 3) % 4;
1427 plana
= (olddir
+ 3) % 4;
1429 planc
= (olddir
+ 1) % 4;
1433 /* test for collision */
1434 set_worm_dir(w
, plana
);
1435 if (will_worm_collide(w
)){
1438 set_worm_dir(w
, planb
);
1440 /* test for collision */
1441 if (will_worm_collide(w
)) {
1444 set_worm_dir(w
, planc
);
1450 * prints out the score board with all the status information
1453 static void score_board(void)
1459 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1460 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, 0, LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, LCD_HEIGHT
);
1461 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1462 for (i
= 0; i
< worm_count
; i
++) {
1463 int score
= get_score(&worms
[i
]);
1466 if (worms
[i
].fetch_worm_direction
!= virtual_player
){
1467 if (highscore
< score
) {
1473 rb
->snprintf(buf
, sizeof (buf
),"Len:%d", score
);
1476 switch (check_collision(&worms
[i
])) {
1477 case COLLISION_NONE
:
1478 if (worms
[i
].growing
> 0)
1488 case COLLISION_WORM
:
1492 case COLLISION_FOOD
:
1496 case COLLISION_ARGH
:
1500 case COLLISION_FIELD
:
1504 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, y
, buf
);
1505 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, y
+8, buf2
);
1507 if (!worms
[i
].alive
){
1508 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1509 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, y
,
1510 LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, 17);
1511 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1515 rb
->snprintf(buf
, sizeof(buf
), "Hs: %d", highscore
);
1516 #ifndef DEBUG_WORMLET
1517 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, buf
);
1519 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, debugout
);
1524 * Checks for collisions of the worm and its environment and
1525 * takes appropriate actions like growing the worm or killing it.
1526 * @return bool Returns true if the worm is dead. Returns
1527 * false if the worm is healthy, up and creeping.
1529 static bool process_collisions(struct worm
*w
)
1533 w
->alive
&= !field_collision(w
);
1537 /* check if food was eaten */
1538 index
= food_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1546 for (i
= 0; i
< arghs_per_food
; i
++) {
1548 if (argh_count
> MAX_ARGH
)
1549 argh_count
= MAX_ARGH
;
1550 make_argh(argh_count
- 1);
1551 draw_argh(argh_count
- 1);
1554 add_growing(w
, worm_food
);
1559 /* check if argh was eaten */
1561 index
= argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1566 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
) {
1576 * The main loop of the game.
1577 * @return bool Returns true if the game ended
1578 * with a dead worm. Returns false if the user
1579 * aborted the game manually.
1581 static int run(void)
1584 int wormDead
= false;
1585 bool paused
= false;
1587 /* ticks are counted to compensate speed variations */
1588 long cycle_start
= 0, cycle_end
= 0;
1589 #ifdef DEBUG_WORMLET
1590 int ticks_to_max_cycle_reset
= 20;
1595 /* initialize the board and so on */
1598 cycle_start
= *rb
->current_tick
;
1599 /* change the direction of the worm */
1603 long cycle_duration
=0;
1605 #ifdef HAS_BUTTON_HOLD
1606 if (rb
->button_hold())
1611 case BTN_STARTPAUSE
:
1616 return 1; /* restart game */
1624 return 2; /* back to menu */
1631 if (players
== 1 && !use_remote
) {
1632 player1_dir
= NORTH
;
1637 if (players
== 1 && !use_remote
) {
1638 player1_dir
= SOUTH
;
1643 if (players
!= 1 || use_remote
) {
1644 player1_dir
= (player1_dir
+ 3) % 4;
1651 if (players
!= 1 || use_remote
) {
1652 player1_dir
= (player1_dir
+ 1) % 4;
1659 case BTN_PLAYER2_DIR1
:
1660 player2_dir
= (player2_dir
+ 3) % 4;
1663 case BTN_PLAYER2_DIR2
:
1664 player2_dir
= (player2_dir
+ 1) % 4;
1670 player3_dir
= (player3_dir
+ 1) % 4;
1674 player3_dir
= (player3_dir
+ 3) % 4;
1680 for (i
= 0; i
< worm_count
; i
++) {
1681 worms
[i
].fetch_worm_direction(&worms
[i
]);
1685 for (i
= 0; i
< worm_count
; i
++){
1686 struct worm
*w
= &worms
[i
];
1688 wormDead
&= process_collisions(w
);
1693 if (button
== BTN_STOPRESET
) {
1697 /* here the wormlet game cycle ends
1698 thus the current tick is stored
1700 cycle_end
= *rb
->current_tick
;
1702 /* The duration of the game cycle */
1703 cycle_duration
= cycle_end
- cycle_start
;
1704 cycle_duration
= MAX(0, cycle_duration
);
1705 cycle_duration
= MIN(speed
-1, cycle_duration
);
1708 #ifdef DEBUG_WORMLET
1709 ticks_to_max_cycle_reset
--;
1710 if (ticks_to_max_cycle_reset
<= 0) {
1714 if (max_cycle
< cycle_duration
) {
1715 max_cycle
= cycle_duration
;
1716 ticks_to_max_cycle_reset
= 20;
1718 rb
->snprintf(buf
, sizeof buf
, "ticks %d", max_cycle
);
1722 /* adjust the number of ticks to wait for a button.
1723 This ensures that a complete game cycle including
1724 user input runs in constant time */
1725 button
= rb
->button_get_w_tmo(speed
- cycle_duration
);
1726 cycle_start
= *rb
->current_tick
;
1729 rb
->splash(HZ
*2, "Game Over!");
1731 return 2; /* back to menu */
1734 #ifdef DEBUG_WORMLET
1737 * Just a test routine that checks that worm_food_collision works
1738 * in some typical situations.
1740 static void test_worm_food_collision(void) {
1741 int collision_count
= 0;
1743 rb
->lcd_clear_display();
1744 init_worm(&worms
[0], 10, 10);
1745 add_growing(&worms
[0], 10);
1746 set_worm_dir(&worms
[0], EAST
);
1747 for (i
= 0; i
< 10; i
++) {
1748 move_worm(&worms
[0]);
1749 draw_worm(&worms
[0]);
1752 set_worm_dir(&worms
[0], SOUTH
);
1753 for (i
= 0; i
< 10; i
++) {
1754 move_worm(&worms
[0]);
1755 draw_worm(&worms
[0]);
1760 for (foody
[0] = 20; foody
[0] > 0; foody
[0] --) {
1763 draw_worm(&worms
[0]);
1765 collision
= worm_food_collision(&worms
[0], 0);
1769 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1770 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1773 if (collision_count
!= food_size
) {
1774 rb
->button_get(true);
1779 for (foodx
[0] = 30; foodx
[0] > 0; foodx
[0] --) {
1782 draw_worm(&worms
[0]);
1784 collision
= worm_food_collision(&worms
[0], 0);
1788 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1789 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1792 if (collision_count
!= food_size
* 2) {
1793 rb
->button_get(true);
1798 static bool expensive_worm_in_rect(struct worm
*w
, int rx
, int ry
, int rw
, int rh
){
1800 bool retVal
= false;
1801 for (x
= rx
; x
< rx
+ rw
; x
++){
1802 for (y
= ry
; y
< ry
+ rh
; y
++) {
1803 if (specific_worm_collision(w
, x
, y
) != -1) {
1811 static void test_worm_argh_collision(void) {
1814 int collision_count
= 0;
1815 rb
->lcd_clear_display();
1816 init_worm(&worms
[0], 10, 10);
1817 add_growing(&worms
[0], 40);
1818 for (dir
= 0; dir
< 4; dir
++) {
1819 set_worm_dir(&worms
[0], (EAST
+ dir
) % 4);
1820 for (i
= 0; i
< 10; i
++) {
1821 move_worm(&worms
[0]);
1822 draw_worm(&worms
[0]);
1827 for (arghy
[0] = 0; arghy
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghy
[0]++){
1831 collision
= worm_argh_collision(&worms
[0], 0);
1835 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1836 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1839 if (collision_count
!= argh_size
* 2) {
1840 rb
->button_get(true);
1844 for (arghx
[0] = 0; arghx
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghx
[0]++){
1848 collision
= worm_argh_collision(&worms
[0], 0);
1852 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1853 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1856 if (collision_count
!= argh_size
* 4) {
1857 rb
->button_get(true);
1861 static int testline_in_rect(void) {
1862 int testfailed
= -1;
1875 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1876 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1877 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1878 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1880 rb
->lcd_putsxy(0, 0, "failed 1");
1881 rb
->button_get(true);
1887 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1888 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1889 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1890 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1891 rb
->lcd_putsxy(0, 0, "failed 2");
1893 rb
->button_get(true);
1899 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1900 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1901 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1902 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1903 rb
->lcd_putsxy(0, 0, "failed 3");
1905 rb
->button_get(true);
1911 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1912 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1913 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1914 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1915 rb
->lcd_putsxy(0, 0, "failed 4");
1917 rb
->button_get(true);
1923 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
1924 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1925 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1926 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1927 rb
->lcd_putsxy(0, 0, "failed 5");
1929 rb
->button_get(true);
1936 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
1937 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1938 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1939 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1940 rb
->lcd_putsxy(0, 0, "failed 6");
1942 rb
->button_get(true);
1951 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1952 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1953 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1954 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1955 rb
->lcd_putsxy(0, 0, "failed 7");
1957 rb
->button_get(true);
1963 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1964 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1965 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1966 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1967 rb
->lcd_putsxy(0, 0, "failed 8");
1969 rb
->button_get(true);
1975 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1976 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1977 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1978 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1979 rb
->lcd_putsxy(0, 0, "failed 9");
1981 rb
->button_get(true);
1987 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1988 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1989 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1990 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1991 rb
->lcd_putsxy(0, 0, "failed 10");
1993 rb
->button_get(true);
1999 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2000 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2001 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2002 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2003 rb
->lcd_putsxy(0, 0, "failed 11");
2005 rb
->button_get(true);
2012 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2013 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2014 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2015 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2016 rb
->lcd_putsxy(0, 0, "failed 12");
2018 rb
->button_get(true);
2032 if (!(line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2033 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
))) {
2034 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2035 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2036 rb
->lcd_putsxy(0, 0, "failed 13");
2038 rb
->button_get(true);
2052 if (!(line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2053 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
))) {
2054 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2055 rb
->lcd_invertrect(rx
, ry
, rw
, rh
);
2056 rb
->lcd_putsxy(0, 0, "failed 14");
2058 rb
->button_get(true);
2062 rb
->lcd_clear_display();
2068 * Just a test routine to test wether specific_worm_collision might work properly
2070 static int test_specific_worm_collision(void) {
2076 rb
->lcd_clear_display();
2077 init_worm(&worms
[0], 10, 20);
2078 add_growing(&worms
[0], 20 - INITIAL_WORM_LENGTH
);
2080 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2082 set_worm_dir(&worms
[0], dir
% 4);
2083 for (i
= 0; i
< 5; i
++) {
2084 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2085 move_worm(&worms
[0]);
2086 draw_worm(&worms
[0]);
2091 for (y
= 15; y
< 30; y
++){
2092 for (x
= 5; x
< 20; x
++) {
2093 if (specific_worm_collision(&worms
[0], x
, y
) != -1) {
2096 rb
->lcd_invertpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
2097 rb
->snprintf(buf
, sizeof buf
, "collisions %d", collisions
);
2098 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2102 if (collisions
!= 21) {
2103 rb
->button_get(true);
2108 static void test_make_argh(void){
2113 int last_failures
= 0;
2115 rb
->lcd_clear_display();
2118 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2119 init_worm(&worms
[worm_idx
], 10 + worm_idx
* 20, 20);
2120 add_growing(&worms
[worm_idx
], 40 - INITIAL_WORM_LENGTH
);
2123 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2124 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2125 set_worm_dir(&worms
[worm_idx
], dir
% 4);
2126 for (i
= 0; i
< 10; i
++) {
2127 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2128 move_worm(&worms
[worm_idx
]);
2129 draw_worm(&worms
[worm_idx
]);
2137 for (seed
= 0; hit
< 20; seed
+= 2) {
2141 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
2142 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
2144 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++){
2145 if (expensive_worm_in_rect(&worms
[worm_idx
], x
, y
, argh_size
, argh_size
)) {
2149 tries
= make_argh(0);
2150 if ((x
== arghx
[0] && y
== arghy
[0]) || tries
< 2) {
2154 rb
->snprintf(buf
, sizeof buf
, "(%d;%d) fail%d try%d", x
, y
, failures
, tries
);
2155 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2157 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
, argh_size
, argh_size
);
2161 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
, argh_size
, argh_size
);
2162 rb
->lcd_clearrect(arghx
[0] + FIELD_RECT_X
, arghy
[0] + FIELD_RECT_Y
, argh_size
, argh_size
);
2164 if (failures
> last_failures
) {
2165 rb
->button_get(true);
2167 last_failures
= failures
;
2174 static void test_worm_argh_collision_in_moves(void) {
2177 rb
->lcd_clear_display();
2178 init_worm(&worms
[0], 10, 20);
2184 set_worm_dir(&worms
[0], EAST
);
2185 for (i
= 0; i
< 20; i
++) {
2187 move_worm(&worms
[0]);
2188 draw_worm(&worms
[0]);
2189 if (worm_argh_collision_in_moves(&worms
[0], 0, 5)){
2192 rb
->snprintf(buf
, sizeof buf
, "in 5 moves hits: %d", hit_count
);
2193 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2196 if (hit_count
!= argh_size
+ 5) {
2197 rb
->button_get(true);
2200 #endif /* DEBUG_WORMLET */
2202 extern bool use_old_rect
;
2205 * These are additional functions required to set various wormlet settings
2206 * and to enable saving of settings and high score
2210 Sets the total number of worms, both human and machine
2212 bool set_worm_num_worms(void)
2215 static const struct opt_items num_worms_option
[3] = {
2221 ret
= rb
->set_option("Number Of Worms", &worm_count
,INT
, num_worms_option
, 3, NULL
);
2222 if (worm_count
< players
) {
2229 Sets the number of human players
2231 bool set_worm_num_players(void)
2234 static const struct opt_items num_players_option
[4] = {
2241 ret
= rb
->set_option("Number of Players", &players
, INT
,num_players_option
, 4, NULL
);
2242 if (players
> worm_count
) {
2243 worm_count
= players
;
2252 Sets the size of each argh block created
2254 bool set_worm_argh_size(void)
2256 static const struct opt_items argh_size_option
[11] = {
2270 return rb
->set_option("Argh Size", &argh_size
,INT
,argh_size_option
, 11, NULL
);
2274 Sets the amount a worm grows per food
2276 bool set_worm_food(void)
2278 static const struct opt_items worm_food_option
[11] = {
2291 return rb
->set_option("Worm Growth Per Food", &worm_food
,INT
,worm_food_option
, 11, NULL
);
2295 Sets the number of arghs created per food
2297 bool set_argh_per_food(void)
2299 static const struct opt_items argh_food_option
[5] = {
2306 return rb
->set_option("Arghs Per Food", &arghs_per_food
,INT
,argh_food_option
, 5, NULL
);
2310 Sets the number of ticks per move
2312 bool set_worm_speed(void)
2315 static const struct opt_items speed_option
[19] = {
2339 ret
= rb
->set_option("Worm Speed", &speed
,INT
,speed_option
, 19, NULL
);
2345 Sets the control style, which depends on the number of players,
2346 remote control existing, etc
2347 bool set_bool_options(char* string, bool* variable,
2348 char* yes_str, char* no_str, void (*function)(bool))
2350 bool set_worm_control_style(void)
2352 static const struct opt_items key1_option
[2] = {
2353 { "Remote Control", -1 },
2354 { "No Rem. Control", -1 },
2357 static const struct opt_items key2_option
[2] = {
2358 { "2 Key Control", -1 },
2359 { "4 Key COntrol", -1 },
2362 static const struct opt_items key3_option
[1] = {
2363 { "Out of Control", -1 },
2367 rb
->set_option("Control Style",&use_remote
,INT
, key1_option
, 2, NULL
);
2370 rb
->set_option("Control Style",&use_remote
,INT
, key2_option
, 2, NULL
);
2373 rb
->set_option("Control Style",&use_remote
,INT
, key3_option
, 1, NULL
);
2379 void default_settings(void)
2381 arghs_per_food
= ARGHS_PER_FOOD
;
2382 argh_size
= ARGH_SIZE
;
2383 food_size
= FOOD_SIZE
;
2385 worm_food
= WORM_PER_FOOD
;
2387 worm_count
= MAX_WORMS
;
2393 Launches the wormlet game
2395 bool launch_wormlet(void)
2397 int game_result
= 1;
2399 rb
->lcd_clear_display();
2401 /* Turn off backlight timeout */
2402 backlight_force_on(rb
); /* backlight control in lib/helper.c */
2404 /* start the game */
2405 while (game_result
== 1)
2406 game_result
= run();
2408 switch (game_result
)
2411 /* Turn on backlight timeout (revert to settings) */
2412 backlight_use_settings(rb
); /* backlight control in lib/helper.c */
2419 /* End of settings/changes etc */
2424 enum plugin_status
plugin_start(struct plugin_api
* api
, void* parameter
)
2434 configfile_init(rb
);
2435 if (configfile_load(SETTINGS_FILENAME
, config
,
2436 sizeof(config
)/sizeof(*config
),
2437 SETTINGS_MIN_VERSION
) < 0)
2439 /* If the loading failed, save a new config file (as the disk is
2440 already spinning) */
2441 configfile_save(SETTINGS_FILENAME
, config
,
2442 sizeof(config
)/sizeof(*config
),
2446 #ifdef HAVE_LCD_COLOR
2447 rb
->lcd_set_foreground(COLOR_FG
);
2448 rb
->lcd_set_background(COLOR_BG
);
2452 rb
->lcd_set_backdrop(NULL
);
2455 #ifdef DEBUG_WORMLET
2457 test_worm_argh_collision_in_moves();
2459 test_worm_food_collision();
2460 test_worm_argh_collision();
2461 test_specific_worm_collision();
2466 static const struct opt_items noyes
[2] = {
2471 static const struct opt_items num_worms_option
[3] = {
2478 static const struct opt_items num_players_option
[4] = {
2480 static const struct opt_items num_players_option
[2] = {
2490 static const struct opt_items argh_size_option
[9] = {
2502 static const struct opt_items food_size_option
[9] = {
2514 static const struct opt_items worm_food_option
[16] = {
2533 static const struct opt_items argh_food_option
[9] = {
2545 static const struct opt_items speed_option
[21] = {
2569 static const struct opt_items remoteonly_option
[1] = {
2570 { "Remote Control", -1 }
2573 static const struct opt_items key24_option
[2] = {
2574 { "4 Key Control", -1 },
2575 { "2 Key Control", -1 }
2579 static const struct opt_items remote_option
[2] = {
2580 { "Remote Control", -1 },
2581 { "No Rem. Control", -1 }
2584 static const struct opt_items key2_option
[1] = {
2585 { "2 Key Control", -1 }
2589 static const struct opt_items nokey_option
[1] = {
2590 { "Out of Control", -1 }
2593 MENUITEM_STRINGLIST(menu
, "Wormlet Menu", NULL
, "Play Wormlet!",
2594 "Number of Worms", "Number of Players", "Control Style",
2595 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2596 "Argh Size","Food Size","Revert to Default Settings",
2599 rb
->button_clear_queue();
2601 while (!menu_quit
) {
2602 switch(rb
->do_menu(&menu
, &result
, NULL
, false))
2605 rb
->lcd_setfont(FONT_SYSFIXED
);
2609 new_setting
= worm_count
- 1;
2610 rb
->set_option("Number of Worms", &new_setting
, INT
, num_worms_option
, 3, NULL
);
2611 if (new_setting
!= worm_count
)
2612 worm_count
= new_setting
+ 1;
2613 if (worm_count
< players
) {
2614 worm_count
= players
;
2618 new_setting
= players
;
2620 rb
->set_option("Number of Players", &new_setting
, INT
, num_players_option
, 4, NULL
);
2622 rb
->set_option("Number of Players", &new_setting
, INT
, num_players_option
, 2, NULL
);
2624 if (new_setting
!= players
)
2625 players
= new_setting
;
2626 if (players
> worm_count
) {
2627 worm_count
= players
;
2634 new_setting
= use_remote
;
2637 rb
->set_option("Control Style",&new_setting
,INT
, nokey_option
, 1, NULL
);
2640 rb
->set_option("Control Style",&new_setting
,INT
, key24_option
, 2, NULL
);
2644 rb
->set_option("Control Style",&new_setting
,INT
, remote_option
, 2, NULL
);
2646 rb
->set_option("Control Style",&new_setting
,INT
, key2_option
, 1, NULL
);
2650 rb
->set_option("Control Style",&new_setting
,INT
, remoteonly_option
, 1, NULL
);
2653 if (new_setting
!= use_remote
)
2654 use_remote
= new_setting
;
2657 new_setting
= worm_food
;
2658 rb
->set_option("Worm Growth Per Food", &new_setting
,INT
,worm_food_option
, 16, NULL
);
2659 if (new_setting
!= worm_food
)
2660 worm_food
= new_setting
;
2663 new_setting
= speed
;
2664 new_setting
= 20 - new_setting
;
2665 rb
->set_option("Worm Speed", &new_setting
,INT
,speed_option
, 21, NULL
);
2666 new_setting
= 20 - new_setting
;
2667 if (new_setting
!= speed
)
2668 speed
= new_setting
;
2671 new_setting
= arghs_per_food
;
2672 rb
->set_option("Arghs Per Food", &new_setting
,INT
,argh_food_option
, 9, NULL
);
2673 if (new_setting
!= arghs_per_food
)
2674 arghs_per_food
= new_setting
;
2677 new_setting
= argh_size
-2;
2678 rb
->set_option("Argh Size", &new_setting
,INT
,argh_size_option
, 9, NULL
);
2679 if (new_setting
!= argh_size
)
2680 argh_size
= new_setting
+2;
2683 new_setting
= food_size
-2;
2684 rb
->set_option("Food Size", &new_setting
,INT
,food_size_option
, 9, NULL
);
2685 if (new_setting
!= food_size
)
2686 food_size
= new_setting
+2;
2690 rb
->set_option("Reset Settings?", &new_setting
,INT
, noyes
, 2, NULL
);
2691 if (new_setting
== 1)
2700 configfile_save(SETTINGS_FILENAME
, config
,
2701 sizeof(config
)/sizeof(*config
),