1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 Philipp Pertermann
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "lib/configfile.h"
23 #include "lib/helper.h"
24 #include "lib/playback_control.h"
28 /* size of the field the worm lives in */
29 #define FIELD_RECT_X 1
30 #define FIELD_RECT_Y 1
31 #define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
32 #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
34 /* when the game starts */
35 #define INITIAL_WORM_LENGTH 10
37 /* num of pixel the worm grows per eaten food */
38 #define WORM_PER_FOOD 7
40 /* num of worms creeping in the FIELD */
43 /* minimal distance between a worm and an argh
44 when a new argh is made */
45 #define MIN_ARGH_DIST 5
47 #if (CONFIG_KEYPAD == RECORDER_PAD)
48 #define BTN_DIR_UP BUTTON_UP
49 #define BTN_DIR_DOWN BUTTON_DOWN
50 #define BTN_DIR_LEFT BUTTON_LEFT
51 #define BTN_DIR_RIGHT BUTTON_RIGHT
52 #define BTN_PLAYER2_DIR1 BUTTON_F2
53 #define BTN_PLAYER2_DIR2 BUTTON_F3
54 #define BTN_STARTPAUSE BUTTON_PLAY
55 #define BTN_QUIT BUTTON_OFF
56 #define BTN_STOPRESET BUTTON_ON
57 #define BTN_TOGGLE_KEYS BUTTON_F1
59 #if BUTTON_REMOTE != 0
60 #define BTN_RC_UP BUTTON_RC_VOL_UP
61 #define BTN_RC_DOWN BUTTON_RC_VOL_DOWN
66 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
67 #define BTN_DIR_UP BUTTON_UP
68 #define BTN_DIR_DOWN BUTTON_DOWN
69 #define BTN_DIR_LEFT BUTTON_LEFT
70 #define BTN_DIR_RIGHT BUTTON_RIGHT
71 #define BTN_PLAYER2_DIR1 BUTTON_F2
72 #define BTN_PLAYER2_DIR2 BUTTON_F3
73 #define BTN_STARTPAUSE BUTTON_SELECT
74 #define BTN_QUIT BUTTON_OFF
75 #define BTN_STOPRESET BUTTON_ON
76 #define BTN_TOGGLE_KEYS BUTTON_F1
78 #elif (CONFIG_KEYPAD == ONDIO_PAD)
79 #define BTN_DIR_UP BUTTON_UP
80 #define BTN_DIR_DOWN BUTTON_DOWN
81 #define BTN_DIR_LEFT BUTTON_LEFT
82 #define BTN_DIR_RIGHT BUTTON_RIGHT
83 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
84 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
85 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
87 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
88 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
90 #define BTN_DIR_UP BUTTON_MENU
91 #define BTN_DIR_DOWN BUTTON_PLAY
92 #define BTN_DIR_LEFT BUTTON_LEFT
93 #define BTN_DIR_RIGHT BUTTON_RIGHT
94 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
95 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
96 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
98 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
100 #define BTN_DIR_UP BUTTON_UP
101 #define BTN_DIR_DOWN BUTTON_DOWN
102 #define BTN_DIR_LEFT BUTTON_LEFT
103 #define BTN_DIR_RIGHT BUTTON_RIGHT
104 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
105 #define BTN_QUIT BUTTON_OFF
106 #define BTN_STOPRESET BUTTON_ON
108 #define BTN_RC_QUIT BUTTON_RC_STOP
110 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
112 #define BTN_DIR_UP BUTTON_UP
113 #define BTN_DIR_DOWN BUTTON_DOWN
114 #define BTN_DIR_LEFT BUTTON_LEFT
115 #define BTN_DIR_RIGHT BUTTON_RIGHT
116 #define BTN_STARTPAUSE BUTTON_PLAY
117 #define BTN_QUIT BUTTON_POWER
118 #define BTN_STOPRESET BUTTON_REC
120 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
122 #define BTN_DIR_UP BUTTON_UP
123 #define BTN_DIR_DOWN BUTTON_DOWN
124 #define BTN_DIR_LEFT BUTTON_LEFT
125 #define BTN_DIR_RIGHT BUTTON_RIGHT
126 #define BTN_STARTPAUSE BUTTON_SELECT
127 #define BTN_QUIT BUTTON_POWER
128 #define BTN_STOPRESET BUTTON_A
130 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
131 (CONFIG_KEYPAD == SANSA_C200_PAD)
133 #define BTN_DIR_UP BUTTON_UP
134 #define BTN_DIR_DOWN BUTTON_DOWN
135 #define BTN_DIR_LEFT BUTTON_LEFT
136 #define BTN_DIR_RIGHT BUTTON_RIGHT
137 #define BTN_STARTPAUSE BUTTON_SELECT
138 #define BTN_QUIT BUTTON_POWER
139 #define BTN_STOPRESET BUTTON_REC
141 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
143 #define BTN_DIR_UP BUTTON_UP
144 #define BTN_DIR_DOWN BUTTON_DOWN
145 #define BTN_DIR_LEFT BUTTON_LEFT
146 #define BTN_DIR_RIGHT BUTTON_RIGHT
147 #define BTN_STARTPAUSE BUTTON_SELECT
148 #define BTN_QUIT BUTTON_POWER
149 #define BTN_STOPRESET BUTTON_HOME
151 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
153 #define BTN_DIR_UP BUTTON_UP
154 #define BTN_DIR_DOWN BUTTON_DOWN
155 #define BTN_DIR_LEFT BUTTON_LEFT
156 #define BTN_DIR_RIGHT BUTTON_RIGHT
157 #define BTN_STARTPAUSE BUTTON_SELECT
158 #define BTN_QUIT (BUTTON_HOME|BUTTON_REPEAT)
159 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
161 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
163 #define BTN_DIR_UP BUTTON_UP
164 #define BTN_DIR_DOWN BUTTON_DOWN
165 #define BTN_DIR_LEFT BUTTON_LEFT
166 #define BTN_DIR_RIGHT BUTTON_RIGHT
167 #define BTN_STARTPAUSE (BUTTON_SELECT | BUTTON_REL)
168 #define BTN_QUIT BUTTON_POWER
169 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
171 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
173 #define BTN_DIR_UP BUTTON_SCROLL_UP
174 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
175 #define BTN_DIR_LEFT BUTTON_LEFT
176 #define BTN_DIR_RIGHT BUTTON_RIGHT
177 #define BTN_STARTPAUSE BUTTON_PLAY
178 #define BTN_QUIT BUTTON_POWER
179 #define BTN_STOPRESET BUTTON_REW
181 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
183 #define BTN_DIR_UP BUTTON_UP
184 #define BTN_DIR_DOWN BUTTON_DOWN
185 #define BTN_DIR_LEFT BUTTON_LEFT
186 #define BTN_DIR_RIGHT BUTTON_RIGHT
187 #define BTN_STARTPAUSE BUTTON_SELECT
188 #define BTN_QUIT BUTTON_BACK
189 #define BTN_STOPRESET BUTTON_MENU
191 #elif (CONFIG_KEYPAD == MROBE100_PAD)
193 #define BTN_DIR_UP BUTTON_UP
194 #define BTN_DIR_DOWN BUTTON_DOWN
195 #define BTN_DIR_LEFT BUTTON_LEFT
196 #define BTN_DIR_RIGHT BUTTON_RIGHT
197 #define BTN_STARTPAUSE BUTTON_SELECT
198 #define BTN_QUIT BUTTON_POWER
199 #define BTN_STOPRESET BUTTON_DISPLAY
201 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
203 #define BTN_DIR_UP BUTTON_RC_VOL_UP
204 #define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN
205 #define BTN_DIR_LEFT BUTTON_RC_REW
206 #define BTN_DIR_RIGHT BUTTON_RC_FF
207 #define BTN_STARTPAUSE BUTTON_RC_PLAY
208 #define BTN_QUIT BUTTON_RC_REC
209 #define BTN_STOPRESET BUTTON_RC_MODE
211 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
213 #define BTN_QUIT BUTTON_POWER
215 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
217 #define BTN_DIR_UP BUTTON_UP
218 #define BTN_DIR_DOWN BUTTON_DOWN
219 #define BTN_DIR_LEFT BUTTON_LEFT
220 #define BTN_DIR_RIGHT BUTTON_RIGHT
221 #define BTN_STARTPAUSE BUTTON_PLAY
222 #define BTN_QUIT BUTTON_BACK
223 #define BTN_STOPRESET BUTTON_MENU
225 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
227 #define BTN_DIR_UP BUTTON_UP
228 #define BTN_DIR_DOWN BUTTON_DOWN
229 #define BTN_DIR_LEFT BUTTON_LEFT
230 #define BTN_DIR_RIGHT BUTTON_RIGHT
231 #define BTN_STARTPAUSE BUTTON_MENU
232 #define BTN_QUIT BUTTON_POWER
233 #define BTN_STOPRESET BUTTON_VIEW
235 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
237 #define BTN_DIR_UP BUTTON_UP
238 #define BTN_DIR_DOWN BUTTON_DOWN
239 #define BTN_DIR_LEFT BUTTON_PREV
240 #define BTN_DIR_RIGHT BUTTON_RIGHT
241 #define BTN_STARTPAUSE BUTTON_MENU
242 #define BTN_QUIT BUTTON_POWER
243 #define BTN_STOPRESET BUTTON_RIGHT
245 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
246 (CONFIG_KEYPAD == ONDAVX777_PAD) || \
247 CONFIG_KEYPAD == MROBE500_PAD
249 #define BTN_QUIT BUTTON_POWER
251 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
253 #define BTN_DIR_UP BUTTON_UP
254 #define BTN_DIR_DOWN BUTTON_DOWN
255 #define BTN_DIR_LEFT BUTTON_LEFT
256 #define BTN_DIR_RIGHT BUTTON_RIGHT
257 #define BTN_STARTPAUSE BUTTON_PLAY
258 #define BTN_QUIT BUTTON_FFWD
259 #define BTN_STOPRESET BUTTON_REW
261 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
263 #define BTN_DIR_UP BUTTON_UP
264 #define BTN_DIR_DOWN BUTTON_DOWN
265 #define BTN_DIR_LEFT BUTTON_PREV
266 #define BTN_DIR_RIGHT BUTTON_NEXT
267 #define BTN_STARTPAUSE BUTTON_PLAY
268 #define BTN_QUIT BUTTON_REC
269 #define BTN_STOPRESET BUTTON_CANCEL
271 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
273 #define BTN_DIR_UP BUTTON_REC
274 #define BTN_DIR_DOWN BUTTON_PLAY
275 #define BTN_DIR_LEFT BUTTON_PREV
276 #define BTN_DIR_RIGHT BUTTON_NEXT
277 #define BTN_STARTPAUSE BUTTON_SELECT
278 #define BTN_QUIT (BUTTON_REC|BUTTON_PLAY)
279 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_REPEAT)
282 #error No keymap defined!
285 #ifdef HAVE_TOUCHSCREEN
287 #define BTN_DIR_UP BUTTON_TOPMIDDLE
290 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
293 #define BTN_DIR_LEFT BUTTON_MIDLEFT
295 #ifndef BTN_DIR_RIGHT
296 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
298 #ifndef BTN_STARTPAUSE
299 #define BTN_STARTPAUSE BUTTON_CENTER
302 #define BTN_QUIT BUTTON_TOPLEFT
304 #ifndef BTN_STOPRESET
305 #define BTN_STOPRESET BUTTON_TOPRIGHT
309 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
313 #define MAX_WORM_SEGMENTS 128
314 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
318 #define MAX_WORM_SEGMENTS 128
319 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
323 #define MAX_WORM_SEGMENTS 128
324 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
328 #define MAX_WORM_SEGMENTS 128
329 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
333 #define MAX_WORM_SEGMENTS 128
334 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
338 #define MAX_WORM_SEGMENTS 128
339 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
343 #define MAX_WORM_SEGMENTS 256
344 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
348 #define MAX_WORM_SEGMENTS 256
349 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
353 #define MAX_WORM_SEGMENTS 512
354 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
358 #define MAX_WORM_SEGMENTS 512
359 #elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
360 ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
364 #define MAX_WORM_SEGMENTS 512
365 #elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
366 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
370 #define MAX_WORM_SEGMENTS 512
373 #ifdef HAVE_LCD_COLOR
374 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
375 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
376 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
377 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
378 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
381 #define CHECK_SQUARE_COLLISION(x1,y1,s1,x2,y2,s2) (x1+s1>x2)&&(x2+s2>x1)&&(y1+s1>y2)&&(y2+s2>y1)
384 * All the properties that a worm has.
387 /* The worm is stored in a ring of xy coordinates */
388 int x
[MAX_WORM_SEGMENTS
];
389 int y
[MAX_WORM_SEGMENTS
];
391 int head
; /* index of the head within the buffer */
392 int tail
; /* index of the tail within the buffer */
393 int growing
; /* number of cyles the worm still keeps growing */
394 bool alive
; /* the worms living state */
396 /* direction vector in which the worm moves */
397 int dirx
; /* only values -1 0 1 allowed */
398 int diry
; /* only values -1 0 1 allowed */
400 /* this method is used to fetch the direction the user
401 has selected. It can be one of the values
402 human_player1, human_player2, remote_player, virtual_player.
403 All these values are fuctions, that can change the direction
405 void (*fetch_worm_direction
)(struct worm
*w
);
408 /* stores the highscore - besides it was scored by a virtual player */
409 static int highscore
;
411 #define MAX_FOOD 5 /* maximal number of food items */
413 /* The arrays store the food coordinates */
414 static int foodx
[MAX_FOOD
];
415 static int foody
[MAX_FOOD
];
417 #define MAX_ARGH 100 /* maximal number of argh items */
418 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
420 /* The arrays store the argh coordinates */
421 static int arghx
[MAX_ARGH
];
422 static int arghy
[MAX_ARGH
];
424 /* the number of arghs that are currently in use */
425 static int argh_count
;
427 /* the number of arghs per food, settable by user */
428 static int arghs_per_food
= ARGHS_PER_FOOD
;
429 /* the size of the argh, settable by user */
430 static int argh_size
= ARGH_SIZE
;
431 /* the size of the food, settable by user */
432 static int food_size
= FOOD_SIZE
;
433 /* the speed of the worm, settable by user */
434 static int speed
= SPEED
;
435 /* the amount a worm grows by eating a food, settable by user */
436 static int worm_food
= WORM_PER_FOOD
;
438 /* End additional variables */
441 /* just a buffer used for debug output */
442 static char debugout
[15];
445 /* the number of active worms (dead or alive) */
446 static int worm_count
= MAX_WORMS
;
448 /* in multiplayer mode: en- / disables the remote worm control
449 in singleplayer mode: toggles 4 / 2 button worm control */
450 static bool use_remote
= false;
452 /* return values of check_collision */
453 #define COLLISION_NONE 0
454 #define COLLISION_WORM 1
455 #define COLLISION_FOOD 2
456 #define COLLISION_ARGH 3
457 #define COLLISION_FIELD 4
459 /* constants for use as directions.
460 Note that the values are ordered clockwise.
461 Thus increasing / decreasing the values
462 is equivalent to right / left turns. */
468 /* direction of human player 1 */
469 static int player1_dir
= EAST
;
470 /* direction of human player 2 */
471 static int player2_dir
= EAST
;
472 /* direction of human player 3 */
473 static int player3_dir
= EAST
;
475 /* the number of (human) players that currently
477 static int players
= 1;
479 #define SETTINGS_VERSION 1
480 #define SETTINGS_MIN_VERSION 1
481 #define SETTINGS_FILENAME "wormlet.cfg"
483 static struct configdata config
[] =
485 {TYPE_INT
, 0, 1024, { .int_p
= &highscore
}, "highscore", NULL
},
486 {TYPE_INT
, 0, 15, { .int_p
= &arghs_per_food
}, "arghs per food", NULL
},
487 {TYPE_INT
, 0, 15, { .int_p
= &argh_size
}, "argh size", NULL
},
488 {TYPE_INT
, 0, 15, { .int_p
= &food_size
}, "food size", NULL
},
489 {TYPE_INT
, 0, 3, { .int_p
= &players
}, "players", NULL
},
490 {TYPE_INT
, 0, 3, { .int_p
= &worm_count
}, "worms", NULL
},
491 {TYPE_INT
, 0, 20, { .int_p
= &speed
}, "speed", NULL
},
492 {TYPE_INT
, 0, 15, { .int_p
= &worm_food
}, "Worm Growth Per Food", NULL
}
496 static void set_debug_out(char *str
){
497 strcpy(debugout
, str
);
502 * Returns the direction id in which the worm
503 * currently is creeping.
504 * @param struct worm *w The worm that is to be investigated.
505 * w Must not be null.
506 * @return int A value 0 <= value < 4
507 * Note the predefined constants NORTH, SOUTH, EAST, WEST
509 static int get_worm_dir(struct worm
*w
)
529 * Set the direction of the specified worm with a direction id.
530 * Increasing the value by 1 means to turn the worm direction
531 * to right by 90 degree.
532 * @param struct worm *w The worm that is to be altered. w Must not be null.
533 * @param int dir The new direction in which the worm is to creep.
534 * dir must be 0 <= dir < 4. Use predefined constants
535 * NORTH, SOUTH, EAST, WEST
537 static void set_worm_dir(struct worm
*w
, int dir
)
560 * Returns the current length of the worm array. This
561 * is also a value for the number of bends that are in the worm.
562 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
564 static int get_worm_array_length(struct worm
*w
)
566 /* initial simple calculation will be overwritten if wrong. */
567 int retVal
= w
->head
- w
->tail
;
569 /* if the worm 'crosses' the boundaries of the ringbuffer */
571 retVal
= w
->head
+ MAX_WORM_SEGMENTS
- w
->tail
;
578 * Returns the score the specified worm. The score is the length
580 * @param struct worm *w The worm that is to be investigated.
581 * w must not be null.
582 * @return int The length of the worm (>= 0).
584 static int get_score(struct worm
*w
)
587 int length
= get_worm_array_length(w
);
589 for (i
= 0; i
< length
; i
++) {
591 /* The iteration iterates the length of the worm.
592 Here's the conversion to the true indices within the worm arrays. */
593 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
594 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
595 int startx
= w
->x
[linestart
];
596 int starty
= w
->y
[linestart
];
597 int endx
= w
->x
[lineend
];
598 int endy
= w
->y
[lineend
];
600 int minimum
, maximum
;
602 if (startx
== endx
) {
603 minimum
= MIN(starty
, endy
);
604 maximum
= MAX(starty
, endy
);
606 minimum
= MIN(startx
, endx
);
607 maximum
= MAX(startx
, endx
);
609 retval
+= abs(maximum
- minimum
);
615 * Determines wether the line specified by startx, starty, endx, endy intersects
616 * the rectangle specified by x, y, width, height. Note that the line must be exactly
617 * horizontal or vertical (startx == endx or starty == endy).
618 * @param int startx The x coordinate of the start point of the line.
619 * @param int starty The y coordinate of the start point of the line.
620 * @param int endx The x coordinate of the end point of the line.
621 * @param int endy The y coordinate of the end point of the line.
622 * @param int x The x coordinate of the top left corner of the rectangle.
623 * @param int y The y coordinate of the top left corner of the rectangle.
624 * @param int width The width of the rectangle.
625 * @param int height The height of the rectangle.
626 * @return bool Returns true if the specified line intersects with the recangle.
628 static bool line_in_rect(int startx
, int starty
, int endx
, int endy
,
629 int x
, int y
, int width
, int height
)
632 int simple
, simplemin
, simplemax
;
633 int compa
, compb
, compmin
, compmax
;
635 if (startx
== endx
) {
638 simplemax
= x
+ width
;
643 compmax
= y
+ height
;
647 simplemax
= y
+ height
;
656 compa
= MIN(compa
, compb
);
657 compb
= MAX(temp
, compb
);
659 if (simplemin
<= simple
&& simple
<= simplemax
) {
660 if ((compmin
<= compa
&& compa
<= compmax
) ||
661 (compmin
<= compb
&& compb
<= compmax
) ||
662 (compa
<= compmin
&& compb
>= compmax
)) {
670 * Tests wether the specified worm intersects with the rect.
671 * @param struct worm *w The worm to be investigated
672 * @param int x The x coordinate of the top left corner of the rect
673 * @param int y The y coordinate of the top left corner of the rect
674 * @param int widht The width of the rect
675 * @param int height The height of the rect
676 * @return bool Returns true if the worm intersects with the rect
678 static bool worm_in_rect(struct worm
*w
, int x
, int y
, int width
, int height
)
683 /* get_worm_array_length is expensive -> buffer the value */
684 int wormLength
= get_worm_array_length(w
);
687 /* test each entry that is part of the worm */
688 for (i
= 0; i
< wormLength
&& retval
== false; i
++) {
690 /* The iteration iterates the length of the worm.
691 Here's the conversion to the true indices within the worm arrays. */
692 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
693 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
694 int startx
= w
->x
[linestart
];
695 int starty
= w
->y
[linestart
];
696 int endx
= w
->x
[lineend
];
697 int endy
= w
->y
[lineend
];
699 retval
= line_in_rect(startx
, starty
, endx
, endy
, x
, y
, width
, height
);
706 * Checks wether a specific food in the food arrays is at the
707 * specified coordinates.
708 * @param int foodIndex The index of the food in the food arrays
709 * @param int x the x coordinate.
710 * @param int y the y coordinate.
711 * @return Returns true if the coordinate hits the food specified by
714 static bool specific_food_collision(int foodIndex
, int x
, int y
)
717 if (x
>= foodx
[foodIndex
] &&
718 x
< foodx
[foodIndex
] + food_size
&&
719 y
>= foody
[foodIndex
] &&
720 y
< foody
[foodIndex
] + food_size
) {
728 * Returns the index of the food that is at the
729 * given coordinates. If no food is at the coordinates
731 * @return int -1 <= value < MAX_FOOD
733 static int food_collision(int x
, int y
)
737 for (i
= 0; i
< MAX_FOOD
; i
++) {
738 if (specific_food_collision(i
, x
, y
)) {
747 * Checks wether a specific argh in the argh arrays is at the
748 * specified coordinates.
749 * @param int arghIndex The index of the argh in the argh arrays
750 * @param int x the x coordinate.
751 * @param int y the y coordinate.
752 * @return Returns true if the coordinate hits the argh specified by
755 static bool specific_argh_collision(int arghIndex
, int x
, int y
)
757 if ( x
>= arghx
[arghIndex
] &&
758 y
>= arghy
[arghIndex
] &&
759 x
< arghx
[arghIndex
] + argh_size
&&
760 y
< arghy
[arghIndex
] + argh_size
)
769 * Returns the index of the argh that is at the
770 * given coordinates. If no argh is at the coordinates
772 * @param int x The x coordinate.
773 * @param int y The y coordinate.
774 * @return int -1 <= value < argh_count <= MAX_ARGH
776 static int argh_collision(int x
, int y
)
781 /* search for the argh that has the specified coords */
782 for (i
= 0; i
< argh_count
; i
++) {
783 if (specific_argh_collision(i
, x
, y
)) {
792 * Checks wether the worm collides with the food at the specfied food-arrays.
793 * @param int foodIndex The index of the food in the arrays. Ensure the value is
794 * 0 <= foodIndex <= MAX_FOOD
795 * @return Returns true if the worm collides with the specified food.
797 static bool worm_food_collision(struct worm
*w
, int foodIndex
)
801 retVal
= worm_in_rect(w
, foodx
[foodIndex
], foody
[foodIndex
],
802 food_size
- 1, food_size
- 1);
808 * Returns true if the worm hits the argh within the next moves (unless
809 * the worm changes it's direction).
810 * @param struct worm *w - The worm to investigate
811 * @param int argh_idx - The index of the argh
812 * @param int moves - The number of moves that are considered.
813 * @return Returns false if the specified argh is not hit within the next
816 static bool worm_argh_collision_in_moves(struct worm
*w
, int argh_idx
, int moves
)
823 x2
= w
->x
[w
->head
] + moves
* w
->dirx
;
824 y2
= w
->y
[w
->head
] + moves
* w
->diry
;
826 retVal
= line_in_rect(x1
, y1
, x2
, y2
, arghx
[argh_idx
], arghy
[argh_idx
],
827 argh_size
, argh_size
);
832 * Checks wether the worm collides with the argh at the specfied argh-arrays.
833 * @param int arghIndex The index of the argh in the arrays.
834 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
835 * @return Returns true if the worm collides with the specified argh.
837 static bool worm_argh_collision(struct worm
*w
, int arghIndex
)
841 retVal
= worm_in_rect(w
, arghx
[arghIndex
], arghy
[arghIndex
],
842 argh_size
- 1, argh_size
- 1);
848 * Find new coordinates for the food stored in foodx[index], foody[index]
849 * that don't collide with any other food or argh
851 * Ensure that 0 <= index < MAX_FOOD.
853 static void make_food(int index
)
857 bool collisionDetected
= false;
861 /* make coordinates for a new food so that
862 the entire food lies within the FIELD */
863 x
= rb
->rand() % (FIELD_RECT_WIDTH
- food_size
);
864 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- food_size
);
865 collisionDetected
= false;
866 /* Ensure that the new food doesn't collide with any
867 existing foods or arghs.
868 If the new food hit any existing
869 argh or food a collision is detected.
872 for (i
=0; i
<MAX_FOOD
&& !collisionDetected
; i
++) {
873 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,food_size
,foodx
[i
],foody
[i
],food_size
);
875 for (i
=0; i
<argh_count
&& !collisionDetected
; i
++) {
876 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,food_size
,arghx
[i
],arghy
[i
],argh_size
);
879 /* use coordinates for further testing */
883 /* now test wether we accidently hit the worm with food ;) */
885 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
887 collisionDetected
= worm_food_collision(&worms
[i
], index
);
891 while (collisionDetected
);
896 * Clears a food from the lcd buffer.
897 * @param int index The index of the food arrays under which
898 * the coordinates of the desired food can be found. Ensure
899 * that the value is 0 <= index <= MAX_FOOD.
901 static void clear_food(int index
)
903 /* remove the old food from the screen */
904 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
905 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
906 foody
[index
] + FIELD_RECT_Y
,
907 food_size
, food_size
);
908 rb
->lcd_set_drawmode(DRMODE_SOLID
);
912 * Draws a food in the lcd buffer.
913 * @param int index The index of the food arrays under which
914 * the coordinates of the desired food can be found. Ensure
915 * that the value is 0 <= index <= MAX_FOOD.
917 static void draw_food(int index
)
919 /* draw the food object */
920 #ifdef HAVE_LCD_COLOR
921 rb
->lcd_set_foreground(COLOR_FOOD
);
923 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
924 foody
[index
] + FIELD_RECT_Y
,
925 food_size
, food_size
);
926 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
927 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
+ 1,
928 foody
[index
] + FIELD_RECT_Y
+ 1,
929 food_size
- 2, food_size
- 2);
930 rb
->lcd_set_drawmode(DRMODE_SOLID
);
931 #ifdef HAVE_LCD_COLOR
932 rb
->lcd_set_foreground(COLOR_FG
);
937 * Find new coordinates for the argh stored in arghx[index], arghy[index]
938 * that don't collide with any other food or argh.
940 * Ensure that 0 <= index < argh_count < MAX_ARGH.
942 static void make_argh(int index
)
946 bool collisionDetected
= false;
950 /* make coordinates for a new argh so that
951 the entire food lies within the FIELD */
952 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
953 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
954 collisionDetected
= false;
955 /* Ensure that the new argh doesn't intersect with any
956 existing foods or arghs.
957 If the new argh hit any existing
958 argh or food an intersection is detected.
961 for (i
=0; i
<MAX_FOOD
&& !collisionDetected
; i
++) {
962 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,argh_size
,foodx
[i
],foody
[i
],food_size
);
964 for (i
=0; i
<argh_count
&& !collisionDetected
; i
++) {
965 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,argh_size
,arghx
[i
],arghy
[i
],argh_size
);
968 /* use the candidate coordinates to make a real argh */
972 /* now test wether we accidently hit the worm with argh ;) */
973 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
974 collisionDetected
|= worm_argh_collision(&worms
[i
], index
);
975 collisionDetected
|= worm_argh_collision_in_moves(&worms
[i
], index
,
979 while (collisionDetected
);
984 * Draws an argh in the lcd buffer.
985 * @param int index The index of the argh arrays under which
986 * the coordinates of the desired argh can be found. Ensure
987 * that the value is 0 <= index < argh_count <= MAX_ARGH.
989 static void draw_argh(int index
)
991 /* draw the new argh */
992 #ifdef HAVE_LCD_COLOR
993 rb
->lcd_set_foreground(COLOR_ARGH
);
995 rb
->lcd_fillrect(arghx
[index
] + FIELD_RECT_X
,
996 arghy
[index
] + FIELD_RECT_Y
,
997 argh_size
, argh_size
);
998 #ifdef HAVE_LCD_COLOR
999 rb
->lcd_set_foreground(COLOR_FG
);
1003 static void virtual_player(struct worm
*w
);
1005 * Initialzes the specified worm with INITIAL_WORM_LENGTH
1006 * and the tail at the specified position. The worm will
1007 * be initialized alive and creeping EAST.
1008 * @param struct worm *w The worm that is to be initialized
1009 * @param int x The x coordinate at which the tail of the worm starts.
1010 * x must be 0 <= x < FIELD_RECT_WIDTH.
1011 * @param int y The y coordinate at which the tail of the worm starts
1012 * y must be 0 <= y < FIELD_RECT_WIDTH.
1014 static void init_worm(struct worm
*w
, int x
, int y
)
1016 /* initialize the worm size */
1020 w
->x
[w
->head
] = x
+ 1;
1026 /* set the initial direction the worm creeps to */
1030 w
->growing
= INITIAL_WORM_LENGTH
- 1;
1032 w
->fetch_worm_direction
= virtual_player
;
1036 * Writes the direction that was stored for
1037 * human player 1 into the specified worm. This function
1038 * may be used to be stored in worm.fetch_worm_direction.
1040 * the direction is read from player1_dir.
1041 * @param struct worm *w - The worm of which the direction
1044 static void human_player1(struct worm
*w
) {
1045 set_worm_dir(w
, player1_dir
);
1049 * Writes the direction that was stored for
1050 * human player 2 into the specified worm. This function
1051 * may be used to be stored in worm.fetch_worm_direction.
1053 * the direction is read from player2_dir.
1054 * @param struct worm *w - The worm of which the direction
1057 static void human_player2(struct worm
*w
) {
1058 set_worm_dir(w
, player2_dir
);
1062 * Writes the direction that was stored for
1063 * human player using a remote control
1064 * into the specified worm. This function
1065 * may be used to be stored in worm.fetch_worm_direction.
1067 * the direction is read from player3_dir.
1068 * @param struct worm *w - The worm of which the direction
1071 static void remote_player(struct worm
*w
) {
1072 set_worm_dir(w
, player3_dir
);
1076 * Initializes the worm-, food- and argh-arrays, draws a frame,
1077 * makes some food and argh and display all that stuff.
1079 static void init_wormlet(void)
1083 for (i
= 0; i
< worm_count
; i
++) {
1084 /* Initialize all the worm coordinates to center. */
1085 int x
= (int)(FIELD_RECT_WIDTH
/ 2);
1086 int y
= (int)((FIELD_RECT_HEIGHT
- 20)/ 2) + i
* 10;
1088 init_worm(&worms
[i
], x
, y
);
1096 worms
[0].fetch_worm_direction
= human_player1
;
1101 worms
[1].fetch_worm_direction
= remote_player
;
1103 worms
[1].fetch_worm_direction
= human_player2
;
1108 worms
[2].fetch_worm_direction
= human_player2
;
1111 /* Needed when the game is restarted using BTN_STOPRESET */
1112 rb
->lcd_clear_display();
1114 /* make and display some food and argh */
1115 argh_count
= MAX_FOOD
;
1116 for (i
= 0; i
< MAX_FOOD
; i
++) {
1123 /* draw the game field */
1124 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1125 rb
->lcd_fillrect(0, 0, FIELD_RECT_WIDTH
+ 2, FIELD_RECT_HEIGHT
+ 2);
1126 rb
->lcd_fillrect(1, 1, FIELD_RECT_WIDTH
, FIELD_RECT_HEIGHT
);
1127 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1129 /* make everything visible */
1135 * Move the worm one step further if it is alive.
1136 * The direction in which the worm moves is taken from dirx and diry.
1137 * move_worm decreases growing if > 0. While the worm is growing the tail
1138 * is left untouched.
1139 * @param struct worm *w The worm to move. w must not be NULL.
1141 static void move_worm(struct worm
*w
)
1144 /* determine the head point and its precessor */
1145 int headx
= w
->x
[w
->head
];
1146 int heady
= w
->y
[w
->head
];
1147 int prehead
= (w
->head
+ MAX_WORM_SEGMENTS
- 1) % MAX_WORM_SEGMENTS
;
1148 int preheadx
= w
->x
[prehead
];
1149 int preheady
= w
->y
[prehead
];
1151 /* determine the old direction */
1154 if (headx
== preheadx
) {
1156 olddiry
= (heady
> preheady
) ? 1 : -1;
1159 olddirx
= (headx
> preheadx
) ? 1 : -1;
1163 a change of direction means a new segment
1165 if (olddirx
!= w
->dirx
||
1166 olddiry
!= w
->diry
) {
1167 w
->head
= (w
->head
+ 1) % MAX_WORM_SEGMENTS
;
1170 /* new head position */
1171 w
->x
[w
->head
] = headx
+ w
->dirx
;
1172 w
->y
[w
->head
] = heady
+ w
->diry
;
1175 /* while the worm is growing no tail procession is necessary */
1176 if (w
->growing
> 0) {
1177 /* update the worms grow state */
1181 /* if the worm isn't growing the tail has to be dragged */
1183 /* index of the end of the tail segment */
1184 int tail_segment_end
= (w
->tail
+ 1) % MAX_WORM_SEGMENTS
;
1186 /* drag the end of the tail */
1187 /* only one coordinate has to be altered. Here it is
1188 determined which one */
1189 int dir
= 0; /* specifies wether the coord has to be in- or decreased */
1190 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
]) {
1191 dir
= (w
->y
[w
->tail
] - w
->y
[tail_segment_end
] < 0) ? 1 : -1;
1192 w
->y
[w
->tail
] += dir
;
1194 dir
= (w
->x
[w
->tail
] - w
->x
[tail_segment_end
] < 0) ? 1 : -1;
1195 w
->x
[w
->tail
] += dir
;
1198 /* when the tail has been dragged so far that it meets
1199 the next segment start the tail segment is obsolete and
1201 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
] &&
1202 w
->y
[w
->tail
] == w
->y
[tail_segment_end
]){
1204 /* drop the last tail point */
1205 w
->tail
= tail_segment_end
;
1212 * Draws the head and clears the tail of the worm in
1213 * the display buffer. lcd_update() is NOT called thus
1214 * the caller has to take care that the buffer is displayed.
1216 static void draw_worm(struct worm
*w
)
1218 /* draw the new head */
1219 int x
= w
->x
[w
->head
];
1220 int y
= w
->y
[w
->head
];
1221 #ifdef HAVE_LCD_COLOR
1222 rb
->lcd_set_foreground(COLOR_WORM
);
1224 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1225 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1228 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1230 /* clear the space behind the worm */
1233 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1234 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1236 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1237 #ifdef HAVE_LCD_COLOR
1238 rb
->lcd_set_foreground(COLOR_FG
);
1243 * Checks wether the coordinate is part of the worm. Returns
1244 * true if any part of the worm was hit - including the head.
1245 * @param x int The x coordinate
1246 * @param y int The y coordinate
1247 * @return int The index of the worm arrays that contain x, y.
1248 * Returns -1 if the coordinates are not part of the worm.
1250 static int specific_worm_collision(struct worm
*w
, int x
, int y
)
1254 /* get_worm_array_length is expensive -> buffer the value */
1255 int wormLength
= get_worm_array_length(w
);
1258 /* test each entry that is part of the worm */
1259 for (i
= 0; i
< wormLength
&& retVal
== -1; i
++) {
1261 /* The iteration iterates the length of the worm.
1262 Here's the conversion to the true indices within the worm arrays. */
1263 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
1264 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
1265 bool samex
= (w
->x
[linestart
] == x
) && (w
->x
[lineend
] == x
);
1266 bool samey
= (w
->y
[linestart
] == y
) && (w
->y
[lineend
] == y
);
1267 if (samex
|| samey
){
1268 int test
, min
, max
, tmp
;
1271 min
= w
->x
[linestart
];
1272 max
= w
->x
[lineend
];
1275 min
= w
->y
[linestart
];
1276 max
= w
->y
[lineend
];
1281 min
= MIN(min
, max
);
1282 max
= MAX(tmp
, max
);
1284 if (min
<= test
&& test
<= max
) {
1293 * Increases the length of the specified worm by marking
1294 * that it may grow by len pixels. Note that the worm has
1295 * to move to make the growing happen.
1296 * @param worm *w The worm that is to be altered.
1297 * @param int len A positive value specifying the amount of
1298 * pixels the worm may grow.
1300 static void add_growing(struct worm
*w
, int len
) {
1305 * Determins the worm that is at the coordinates x, y. The parameter
1306 * w is a switch parameter that changes the functionality of worm_collision.
1307 * If w is specified and x,y hits the head of w NULL is returned.
1308 * This is a useful way to determine wether the head of w hits
1309 * any worm but including itself but excluding its own head.
1310 * (It hits always its own head ;))
1311 * If w is set to NULL worm_collision returns any worm including all heads
1312 * that is at position of x,y.
1313 * @param struct worm *w The worm of which the head should be excluded in
1314 * the test. w may be set to NULL.
1315 * @param int x The x coordinate that is checked
1316 * @param int y The y coordinate that is checkec
1317 * @return struct worm* The worm that has been hit by x,y. If no worm
1318 * was at the position NULL is returned.
1320 static struct worm
* worm_collision(struct worm
*w
, int x
, int y
)
1322 struct worm
*retVal
= NULL
;
1324 for (i
= 0; (i
< worm_count
) && (retVal
== NULL
); i
++) {
1325 int collision_at
= specific_worm_collision(&worms
[i
], x
, y
);
1326 if (collision_at
!= -1) {
1327 if (!(w
== &worms
[i
] && collision_at
== w
->head
)){
1336 * Returns true if the head of the worm just has
1337 * crossed the field boundaries.
1338 * @return bool true if the worm just has wrapped.
1340 static bool field_collision(struct worm
*w
)
1342 bool retVal
= false;
1343 if ((w
->x
[w
->head
] >= FIELD_RECT_WIDTH
) ||
1344 (w
->y
[w
->head
] >= FIELD_RECT_HEIGHT
) ||
1345 (w
->x
[w
->head
] < 0) ||
1346 (w
->y
[w
->head
] < 0))
1355 * Returns true if the specified coordinates are within the
1356 * field specified by the FIELD_RECT_XXX constants.
1357 * @param int x The x coordinate of the point that is investigated
1358 * @param int y The y coordinate of the point that is investigated
1359 * @return bool Returns false if x,y specifies a point outside the
1362 static bool is_in_field_rect(int x
, int y
)
1364 bool retVal
= false;
1365 retVal
= (x
>= 0 && x
< FIELD_RECT_WIDTH
&&
1366 y
>= 0 && y
< FIELD_RECT_HEIGHT
);
1371 * Checks and returns wether the head of the w
1372 * is colliding with something currently.
1373 * @return int One of the values:
1380 static int check_collision(struct worm
*w
)
1382 int retVal
= COLLISION_NONE
;
1384 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
)
1385 retVal
= COLLISION_WORM
;
1387 if (food_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1388 retVal
= COLLISION_FOOD
;
1390 if (argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1391 retVal
= COLLISION_ARGH
;
1393 if (field_collision(w
))
1394 retVal
= COLLISION_FIELD
;
1400 * Returns the index of the food that is closest to the point
1401 * specified by x, y. This index may be used in the foodx and
1403 * @param int x The x coordinate of the point
1404 * @param int y The y coordinate of the point
1405 * @return int A value usable as index in foodx and foody.
1407 static int get_nearest_food(int x
, int y
)
1409 int nearestfood
= 0;
1410 int olddistance
= FIELD_RECT_WIDTH
+ FIELD_RECT_HEIGHT
;
1414 for (foodindex
= 0; foodindex
< MAX_FOOD
; foodindex
++) {
1416 deltax
= foodx
[foodindex
] - x
;
1417 deltay
= foody
[foodindex
] - y
;
1418 deltax
= deltax
> 0 ? deltax
: deltax
* (-1);
1419 deltay
= deltay
> 0 ? deltay
: deltay
* (-1);
1420 distance
= deltax
+ deltay
;
1422 if (distance
< olddistance
) {
1423 olddistance
= distance
;
1424 nearestfood
= foodindex
;
1431 * Returns wether the specified position is next to the worm
1432 * and in the direction the worm looks. Use this method to
1433 * test wether this position would be hit with the next move of
1434 * the worm unless the worm changes its direction.
1435 * @param struct worm *w - The worm to be investigated
1436 * @param int x - The x coordinate of the position to test.
1437 * @param int y - The y coordinate of the position to test.
1438 * @return Returns true if the worm will hit the position unless
1439 * it change its direction before the next move.
1441 static bool is_in_front_of_worm(struct worm
*w
, int x
, int y
)
1443 bool infront
= false;
1444 int deltax
= x
- w
->x
[w
->head
];
1445 int deltay
= y
- w
->y
[w
->head
];
1448 infront
= (w
->diry
* deltay
) > 0;
1450 infront
= (w
->dirx
* deltax
) > 0;
1456 * Returns true if the worm will collide with the next move unless
1457 * it changes its direction.
1458 * @param struct worm *w - The worm to be investigated.
1459 * @return Returns true if the worm will collide with the next move
1460 * unless it changes its direction.
1462 static bool will_worm_collide(struct worm
*w
)
1464 int x
= w
->x
[w
->head
] + w
->dirx
;
1465 int y
= w
->y
[w
->head
] + w
->diry
;
1466 bool retVal
= !is_in_field_rect(x
, y
);
1468 retVal
= (argh_collision(x
, y
) != -1);
1472 retVal
= (worm_collision(w
, x
, y
) != NULL
);
1479 * may be used to be stored in worm.fetch_worm_direction for
1480 * worms that are not controlled by humans but by artificial stupidity.
1481 * A direction is searched that doesn't lead to collision but to the nearest
1482 * food - but not very intelligent. The direction is written to the specified
1484 * @param struct worm *w - The worm of which the direction
1487 static void virtual_player(struct worm
*w
)
1490 int plana
, planb
, planc
;
1491 /* find the next lunch */
1492 int nearestfood
= get_nearest_food(w
->x
[w
->head
], w
->y
[w
->head
]);
1494 /* determine in which direction it is */
1496 /* in front of me? */
1497 bool infront
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1499 /* left right of me? */
1500 int olddir
= get_worm_dir(w
);
1501 set_worm_dir(w
, (olddir
+ 1) % 4);
1502 isright
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1503 set_worm_dir(w
, olddir
);
1505 /* detect situation, set strategy */
1509 planb
= (olddir
+ 1) % 4;
1510 planc
= (olddir
+ 3) % 4;
1513 planb
= (olddir
+ 3) % 4;
1514 planc
= (olddir
+ 1) % 4;
1518 plana
= (olddir
+ 1) % 4;
1520 planc
= (olddir
+ 3) % 4;
1522 plana
= (olddir
+ 3) % 4;
1524 planc
= (olddir
+ 1) % 4;
1528 /* test for collision */
1529 set_worm_dir(w
, plana
);
1530 if (will_worm_collide(w
)){
1533 set_worm_dir(w
, planb
);
1535 /* test for collision */
1536 if (will_worm_collide(w
)) {
1539 set_worm_dir(w
, planc
);
1545 * prints out the score board with all the status information
1548 static void score_board(void)
1554 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1555 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, 0,
1556 LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, LCD_HEIGHT
);
1557 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1558 for (i
= 0; i
< worm_count
; i
++) {
1559 int score
= get_score(&worms
[i
]);
1562 if (worms
[i
].fetch_worm_direction
!= virtual_player
){
1563 if (highscore
< score
) {
1569 rb
->snprintf(buf
, sizeof (buf
),"Len:%d", score
);
1572 switch (check_collision(&worms
[i
])) {
1573 case COLLISION_NONE
:
1574 if (worms
[i
].growing
> 0)
1584 case COLLISION_WORM
:
1588 case COLLISION_FOOD
:
1592 case COLLISION_ARGH
:
1596 case COLLISION_FIELD
:
1600 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, y
, buf
);
1601 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, y
+8, buf2
);
1603 if (!worms
[i
].alive
){
1604 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1605 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, y
,
1606 LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, 17);
1607 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1611 rb
->snprintf(buf
, sizeof(buf
), "Hs: %d", highscore
);
1612 #ifndef DEBUG_WORMLET
1613 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, buf
);
1615 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, debugout
);
1620 * Checks for collisions of the worm and its environment and
1621 * takes appropriate actions like growing the worm or killing it.
1622 * @return bool Returns true if the worm is dead. Returns
1623 * false if the worm is healthy, up and creeping.
1625 static bool process_collisions(struct worm
*w
)
1629 w
->alive
&= !field_collision(w
);
1633 /* check if food was eaten */
1634 index
= food_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1642 for (i
= 0; i
< arghs_per_food
; i
++) {
1644 if (argh_count
> MAX_ARGH
)
1645 argh_count
= MAX_ARGH
;
1646 make_argh(argh_count
- 1);
1647 draw_argh(argh_count
- 1);
1650 add_growing(w
, worm_food
);
1655 /* check if argh was eaten */
1657 index
= argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1662 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
) {
1672 * The main loop of the game.
1673 * @return bool Returns true if the game ended
1674 * with a dead worm. Returns false if the user
1675 * aborted the game manually.
1677 static int run(void)
1680 int wormDead
= false;
1681 bool paused
= false;
1683 /* ticks are counted to compensate speed variations */
1684 long cycle_start
= 0, cycle_end
= 0;
1685 #ifdef DEBUG_WORMLET
1686 int ticks_to_max_cycle_reset
= 20;
1691 /* initialize the board and so on */
1694 cycle_start
= *rb
->current_tick
;
1695 /* change the direction of the worm */
1699 long cycle_duration
=0;
1701 #ifdef HAS_BUTTON_HOLD
1702 if (rb
->button_hold())
1707 case BTN_STARTPAUSE
:
1712 return 1; /* restart game */
1720 return 2; /* back to menu */
1727 if (players
== 1 && !use_remote
) {
1728 player1_dir
= NORTH
;
1733 if (players
== 1 && !use_remote
) {
1734 player1_dir
= SOUTH
;
1739 if (players
!= 1 || use_remote
) {
1740 player1_dir
= (player1_dir
+ 3) % 4;
1747 if (players
!= 1 || use_remote
) {
1748 player1_dir
= (player1_dir
+ 1) % 4;
1755 case BTN_PLAYER2_DIR1
:
1756 player2_dir
= (player2_dir
+ 3) % 4;
1759 case BTN_PLAYER2_DIR2
:
1760 player2_dir
= (player2_dir
+ 1) % 4;
1766 player3_dir
= (player3_dir
+ 1) % 4;
1770 player3_dir
= (player3_dir
+ 3) % 4;
1776 for (i
= 0; i
< worm_count
; i
++) {
1777 worms
[i
].fetch_worm_direction(&worms
[i
]);
1781 for (i
= 0; i
< worm_count
; i
++){
1782 struct worm
*w
= &worms
[i
];
1784 wormDead
&= process_collisions(w
);
1789 if (button
== BTN_STOPRESET
) {
1793 /* here the wormlet game cycle ends
1794 thus the current tick is stored
1796 cycle_end
= *rb
->current_tick
;
1798 /* The duration of the game cycle */
1799 cycle_duration
= cycle_end
- cycle_start
;
1800 cycle_duration
= MAX(0, cycle_duration
);
1801 cycle_duration
= MIN(speed
-1, cycle_duration
);
1804 #ifdef DEBUG_WORMLET
1805 ticks_to_max_cycle_reset
--;
1806 if (ticks_to_max_cycle_reset
<= 0) {
1810 if (max_cycle
< cycle_duration
) {
1811 max_cycle
= cycle_duration
;
1812 ticks_to_max_cycle_reset
= 20;
1814 rb
->snprintf(buf
, sizeof buf
, "ticks %d", max_cycle
);
1818 /* adjust the number of ticks to wait for a button.
1819 This ensures that a complete game cycle including
1820 user input runs in constant time */
1821 button
= rb
->button_get_w_tmo(speed
- cycle_duration
);
1822 cycle_start
= *rb
->current_tick
;
1825 rb
->splash(HZ
*2, "Game Over!");
1827 return 2; /* back to menu */
1830 #ifdef DEBUG_WORMLET
1833 * Just a test routine that checks that worm_food_collision works
1834 * in some typical situations.
1836 static void test_worm_food_collision(void)
1838 int collision_count
= 0;
1840 rb
->lcd_clear_display();
1841 init_worm(&worms
[0], 10, 10);
1842 add_growing(&worms
[0], 10);
1843 set_worm_dir(&worms
[0], EAST
);
1844 for (i
= 0; i
< 10; i
++) {
1845 move_worm(&worms
[0]);
1846 draw_worm(&worms
[0]);
1849 set_worm_dir(&worms
[0], SOUTH
);
1850 for (i
= 0; i
< 10; i
++) {
1851 move_worm(&worms
[0]);
1852 draw_worm(&worms
[0]);
1857 for (foody
[0] = 20; foody
[0] > 0; foody
[0] --) {
1860 draw_worm(&worms
[0]);
1862 collision
= worm_food_collision(&worms
[0], 0);
1866 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1867 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1870 if (collision_count
!= food_size
) {
1871 rb
->button_get(true);
1876 for (foodx
[0] = 30; foodx
[0] > 0; foodx
[0] --) {
1879 draw_worm(&worms
[0]);
1881 collision
= worm_food_collision(&worms
[0], 0);
1885 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1886 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1889 if (collision_count
!= food_size
* 2) {
1890 rb
->button_get(true);
1895 static bool expensive_worm_in_rect(struct worm
*w
, int rx
, int ry
, int rw
, int rh
)
1898 bool retVal
= false;
1899 for (x
= rx
; x
< rx
+ rw
; x
++){
1900 for (y
= ry
; y
< ry
+ rh
; y
++) {
1901 if (specific_worm_collision(w
, x
, y
) != -1) {
1909 static void test_worm_argh_collision(void)
1913 int collision_count
= 0;
1914 rb
->lcd_clear_display();
1915 init_worm(&worms
[0], 10, 10);
1916 add_growing(&worms
[0], 40);
1917 for (dir
= 0; dir
< 4; dir
++) {
1918 set_worm_dir(&worms
[0], (EAST
+ dir
) % 4);
1919 for (i
= 0; i
< 10; i
++) {
1920 move_worm(&worms
[0]);
1921 draw_worm(&worms
[0]);
1926 for (arghy
[0] = 0; arghy
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghy
[0]++){
1930 collision
= worm_argh_collision(&worms
[0], 0);
1934 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1935 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1938 if (collision_count
!= argh_size
* 2) {
1939 rb
->button_get(true);
1943 for (arghx
[0] = 0; arghx
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghx
[0]++){
1947 collision
= worm_argh_collision(&worms
[0], 0);
1951 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1952 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1955 if (collision_count
!= argh_size
* 4) {
1956 rb
->button_get(true);
1960 static int testline_in_rect(void)
1962 int testfailed
= -1;
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
);
1980 rb
->lcd_putsxy(0, 0, "failed 1");
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 2");
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 3");
2005 rb
->button_get(true);
2011 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2012 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2013 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2014 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2015 rb
->lcd_putsxy(0, 0, "failed 4");
2017 rb
->button_get(true);
2023 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2024 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2025 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2026 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2027 rb
->lcd_putsxy(0, 0, "failed 5");
2029 rb
->button_get(true);
2036 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2037 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2038 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2039 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2040 rb
->lcd_putsxy(0, 0, "failed 6");
2042 rb
->button_get(true);
2051 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2052 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2053 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2054 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2055 rb
->lcd_putsxy(0, 0, "failed 7");
2057 rb
->button_get(true);
2063 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2064 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2065 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2066 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2067 rb
->lcd_putsxy(0, 0, "failed 8");
2069 rb
->button_get(true);
2075 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2076 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2077 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2078 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2079 rb
->lcd_putsxy(0, 0, "failed 9");
2081 rb
->button_get(true);
2087 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2088 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2089 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2090 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2091 rb
->lcd_putsxy(0, 0, "failed 10");
2093 rb
->button_get(true);
2099 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2100 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2101 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2102 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2103 rb
->lcd_putsxy(0, 0, "failed 11");
2105 rb
->button_get(true);
2112 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2113 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2114 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2115 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2116 rb
->lcd_putsxy(0, 0, "failed 12");
2118 rb
->button_get(true);
2132 if (!(line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2133 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
))) {
2134 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2135 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2136 rb
->lcd_putsxy(0, 0, "failed 13");
2138 rb
->button_get(true);
2152 if (!(line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2153 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
))) {
2154 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2155 rb
->lcd_invertrect(rx
, ry
, rw
, rh
);
2156 rb
->lcd_putsxy(0, 0, "failed 14");
2158 rb
->button_get(true);
2162 rb
->lcd_clear_display();
2168 * Just a test routine to test wether specific_worm_collision might work properly
2170 static int test_specific_worm_collision(void)
2177 rb
->lcd_clear_display();
2178 init_worm(&worms
[0], 10, 20);
2179 add_growing(&worms
[0], 20 - INITIAL_WORM_LENGTH
);
2181 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2183 set_worm_dir(&worms
[0], dir
% 4);
2184 for (i
= 0; i
< 5; i
++) {
2185 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2186 move_worm(&worms
[0]);
2187 draw_worm(&worms
[0]);
2192 for (y
= 15; y
< 30; y
++){
2193 for (x
= 5; x
< 20; x
++) {
2194 if (specific_worm_collision(&worms
[0], x
, y
) != -1) {
2197 rb
->lcd_invertpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
2198 rb
->snprintf(buf
, sizeof buf
, "collisions %d", collisions
);
2199 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2203 if (collisions
!= 21) {
2204 rb
->button_get(true);
2209 static void test_make_argh(void)
2215 int last_failures
= 0;
2217 rb
->lcd_clear_display();
2220 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2221 init_worm(&worms
[worm_idx
], 10 + worm_idx
* 20, 20);
2222 add_growing(&worms
[worm_idx
], 40 - INITIAL_WORM_LENGTH
);
2225 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2226 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2227 set_worm_dir(&worms
[worm_idx
], dir
% 4);
2228 for (i
= 0; i
< 10; i
++) {
2229 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2230 move_worm(&worms
[worm_idx
]);
2231 draw_worm(&worms
[worm_idx
]);
2239 for (seed
= 0; hit
< 20; seed
+= 2) {
2243 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
2244 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
2246 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++){
2247 if (expensive_worm_in_rect(&worms
[worm_idx
], x
, y
, argh_size
, argh_size
)) {
2251 tries
= make_argh(0);
2252 if ((x
== arghx
[0] && y
== arghy
[0]) || tries
< 2) {
2256 rb
->snprintf(buf
, sizeof buf
, "(%d;%d) fail%d try%d",
2257 x
, y
, failures
, tries
);
2258 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2260 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
,
2261 argh_size
, argh_size
);
2265 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
,
2266 argh_size
, argh_size
);
2267 rb
->lcd_clearrect(arghx
[0] + FIELD_RECT_X
, arghy
[0] + FIELD_RECT_Y
,
2268 argh_size
, argh_size
);
2270 if (failures
> last_failures
) {
2271 rb
->button_get(true);
2273 last_failures
= failures
;
2280 static void test_worm_argh_collision_in_moves(void) {
2283 rb
->lcd_clear_display();
2284 init_worm(&worms
[0], 10, 20);
2290 set_worm_dir(&worms
[0], EAST
);
2291 for (i
= 0; i
< 20; i
++) {
2293 move_worm(&worms
[0]);
2294 draw_worm(&worms
[0]);
2295 if (worm_argh_collision_in_moves(&worms
[0], 0, 5)){
2298 rb
->snprintf(buf
, sizeof buf
, "in 5 moves hits: %d", hit_count
);
2299 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2302 if (hit_count
!= argh_size
+ 5) {
2303 rb
->button_get(true);
2306 #endif /* DEBUG_WORMLET */
2309 * Reverts default settings
2311 static void default_settings(void)
2313 arghs_per_food
= ARGHS_PER_FOOD
;
2314 argh_size
= ARGH_SIZE
;
2315 food_size
= FOOD_SIZE
;
2317 worm_food
= WORM_PER_FOOD
;
2319 worm_count
= MAX_WORMS
;
2325 * Launches the wormlet game
2327 static bool launch_wormlet(void)
2329 int game_result
= 1;
2331 rb
->lcd_clear_display();
2333 /* Turn off backlight timeout */
2334 backlight_force_on(); /* backlight control in lib/helper.c */
2336 /* start the game */
2337 while (game_result
== 1)
2338 game_result
= run();
2340 switch (game_result
)
2343 /* Turn on backlight timeout (revert to settings) */
2344 backlight_use_settings(); /* backlight control in lib/helper.c */
2354 enum plugin_status
plugin_start(const void* parameter
)
2363 if (configfile_load(SETTINGS_FILENAME
, config
,
2364 sizeof(config
)/sizeof(*config
),
2365 SETTINGS_MIN_VERSION
) < 0)
2367 /* If the loading failed, save a new config file (as the disk is
2368 already spinning) */
2369 configfile_save(SETTINGS_FILENAME
, config
,
2370 sizeof(config
)/sizeof(*config
),
2374 #ifdef HAVE_LCD_COLOR
2375 rb
->lcd_set_foreground(COLOR_FG
);
2376 rb
->lcd_set_background(COLOR_BG
);
2380 rb
->lcd_set_backdrop(NULL
);
2383 #ifdef DEBUG_WORMLET
2385 test_worm_argh_collision_in_moves();
2387 test_worm_food_collision();
2388 test_worm_argh_collision();
2389 test_specific_worm_collision();
2394 static const struct opt_items noyes
[2] = {
2399 static const struct opt_items remoteonly_option
[1] = {
2400 { "Remote Control", -1 }
2403 static const struct opt_items key24_option
[2] = {
2404 { "4 Key Control", -1 },
2405 { "2 Key Control", -1 }
2409 static const struct opt_items remote_option
[2] = {
2410 { "Remote Control", -1 },
2411 { "No Rem. Control", -1 }
2414 static const struct opt_items key2_option
[1] = {
2415 { "2 Key Control", -1 }
2419 static const struct opt_items nokey_option
[1] = {
2420 { "Out of Control", -1 }
2423 MENUITEM_STRINGLIST(menu
, "Wormlet Menu", NULL
, "Play Wormlet!",
2424 "Number of Worms", "Number of Players", "Control Style",
2425 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2426 "Argh Size","Food Size","Revert to Default Settings",
2427 "Playback Control", "Quit");
2429 rb
->button_clear_queue();
2431 while (!menu_quit
) {
2432 switch(rb
->do_menu(&menu
, &result
, NULL
, false))
2435 rb
->lcd_setfont(FONT_SYSFIXED
);
2439 rb
->set_int("Number of Worms", "", UNIT_INT
, &worm_count
, NULL
,
2441 if (worm_count
< players
) {
2442 worm_count
= players
;
2447 rb
->set_int("Number of Players", "", UNIT_INT
, &players
, NULL
,
2450 rb
->set_int("Number of Players", "", UNIT_INT
, &players
, NULL
,
2453 if (players
> worm_count
) {
2454 worm_count
= players
;
2463 rb
->set_option("Control Style",&use_remote
,INT
,
2464 nokey_option
, 1, NULL
);
2467 rb
->set_option("Control Style",&use_remote
,INT
,
2468 key24_option
, 2, NULL
);
2472 rb
->set_option("Control Style",&use_remote
,INT
,
2473 remote_option
, 2, NULL
);
2475 rb
->set_option("Control Style",&use_remote
,INT
,
2476 key2_option
, 1, NULL
);
2480 rb
->set_option("Control Style",&use_remote
,INT
,
2481 remoteonly_option
, 1, NULL
);
2486 rb
->set_int("Worm Growth Per Food", "", UNIT_INT
, &worm_food
,
2487 NULL
, 1, 0, 15, NULL
);
2490 new_setting
= 20 - speed
;
2491 rb
->set_int("Worm Speed", "", UNIT_INT
, &new_setting
,
2492 NULL
, 1, 0, 20, NULL
);
2493 speed
= 20 - new_setting
;
2496 rb
->set_int("Arghs Per Food", "", UNIT_INT
, &arghs_per_food
,
2497 NULL
, 1, 0, 8, NULL
);
2500 rb
->set_int("Argh Size", "", UNIT_INT
, &argh_size
,
2501 NULL
, 1, 2, 10, NULL
);
2504 rb
->set_int("Food Size", "", UNIT_INT
, &food_size
,
2505 NULL
, 1, 2, 10, NULL
);
2509 rb
->set_option("Reset Settings?", &new_setting
, INT
, noyes
, 2, NULL
);
2510 if (new_setting
== 1)
2514 playback_control(NULL
);
2522 configfile_save(SETTINGS_FILENAME
, config
,
2523 sizeof(config
)/sizeof(*config
),