fixing red:
[Rockbox.git] / apps / plugins / wormlet.c
blob8b752bf6f5369fc734d9a7d5621d44972fe67758
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
19 #include "plugin.h"
20 #include "configfile.h"
21 #include "helper.h"
23 PLUGIN_HEADER
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 */
38 #define MAX_WORMS 3
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
59 #define REMOTE
60 #define MULTIPLAYER
61 #endif
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"
231 #else
232 #error No keymap defined!
233 #endif
235 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
236 #define FOOD_SIZE 3
237 #define ARGH_SIZE 4
238 #define SPEED 14
239 #define MAX_WORM_SEGMENTS 128
240 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
241 #define FOOD_SIZE 3
242 #define ARGH_SIZE 4
243 #define SPEED 14
244 #define MAX_WORM_SEGMENTS 128
245 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
246 #define FOOD_SIZE 3
247 #define ARGH_SIZE 4
248 #define SPEED 12
249 #define MAX_WORM_SEGMENTS 128
250 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
251 #define FOOD_SIZE 4
252 #define ARGH_SIZE 5
253 #define SPEED 10
254 #define MAX_WORM_SEGMENTS 128
255 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
256 #define FOOD_SIZE 4
257 #define ARGH_SIZE 5
258 #define SPEED 9
259 #define MAX_WORM_SEGMENTS 128
260 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
261 #define FOOD_SIZE 4
262 #define ARGH_SIZE 5
263 #define SPEED 8
264 #define MAX_WORM_SEGMENTS 256
265 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
266 #define FOOD_SIZE 4
267 #define ARGH_SIZE 5
268 #define SPEED 6
269 #define MAX_WORM_SEGMENTS 256
270 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
271 #define FOOD_SIZE 5
272 #define ARGH_SIZE 6
273 #define SPEED 4
274 #define MAX_WORM_SEGMENTS 512
275 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
276 #define FOOD_SIZE 5
277 #define ARGH_SIZE 6
278 #define SPEED 4
279 #define MAX_WORM_SEGMENTS 512
280 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
281 #define FOOD_SIZE 7
282 #define ARGH_SIZE 8
283 #define SPEED 4
284 #define MAX_WORM_SEGMENTS 512
285 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
286 #define FOOD_SIZE 7
287 #define ARGH_SIZE 8
288 #define SPEED 4
289 #define MAX_WORM_SEGMENTS 512
290 #endif
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)
298 #endif
301 * All the properties that a worm has.
303 static struct worm {
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
321 of the worm */
322 void (*fetch_worm_direction)(struct worm *w);
323 } worms[MAX_WORMS];
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 */
357 #ifdef DEBUG_WORMLET
358 /* just a buffer used for debug output */
359 static char debugout[15];
360 #endif
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. */
380 #define WEST 0
381 #define NORTH 1
382 #define EAST 2
383 #define SOUTH 3
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
393 control a worm */
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}
416 #ifdef DEBUG_WORMLET
417 static void set_debug_out(char *str){
418 strcpy(debugout, str);
420 #endif
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) {
431 int retVal ;
432 if (w->dirx == 0) {
433 if (w->diry == 1) {
434 retVal = SOUTH;
435 } else {
436 retVal = NORTH;
438 } else {
439 if (w->dirx == 1) {
440 retVal = EAST;
441 } else {
442 retVal = WEST;
445 return retVal;
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) {
458 switch (dir) {
459 case WEST:
460 w->dirx = -1;
461 w->diry = 0;
462 break;
463 case NORTH:
464 w->dirx = 0;
465 w->diry = - 1;
466 break;
467 case EAST:
468 w->dirx = 1;
469 w->diry = 0;
470 break;
471 case SOUTH:
472 w->dirx = 0;
473 w->diry = 1;
474 break;
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 */
488 if (retVal < 0) {
489 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
492 return retVal;
496 * Returns the score the specified worm. The score is the length
497 * of the worm.
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) {
503 int retval = 0;
504 int length = get_worm_array_length(w);
505 int i;
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);
522 } else {
523 minimum = MIN(startx, endx);
524 maximum = MAX(startx, endx);
526 retval += abs(maximum - minimum);
528 return retval;
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) {
546 bool retval = false;
547 int simple, simplemin, simplemax;
548 int compa, compb, compmin, compmax;
549 int temp;
550 if (startx == endx) {
551 simple = startx;
552 simplemin = x;
553 simplemax = x + width;
555 compa = starty;
556 compb = endy;
557 compmin = y;
558 compmax = y + height;
559 } else {
560 simple = starty;
561 simplemin = y;
562 simplemax = y + height;
564 compa = startx;
565 compb = endx;
566 compmin = x;
567 compmax = x + width;
570 temp = compa;
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)) {
578 retval = true;
581 return retval;
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) {
594 bool retval = false;
597 /* get_worm_array_length is expensive -> buffer the value */
598 int wormLength = get_worm_array_length(w);
599 int i;
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);
616 return retval;
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
626 * foodIndex.
628 static bool specific_food_collision(int foodIndex, int x, int y) {
629 bool retVal = false;
630 if (x >= foodx[foodIndex] &&
631 x < foodx[foodIndex] + food_size &&
632 y >= foody[foodIndex] &&
633 y < foody[foodIndex] + food_size) {
635 retVal = true;
637 return retVal;
641 * Returns the index of the food that is at the
642 * given coordinates. If no food is at the coordinates
643 * -1 is returned.
644 * @return int -1 <= value < MAX_FOOD
646 static int food_collision(int x, int y) {
647 int i = 0;
648 int retVal = -1;
649 for (i = 0; i < MAX_FOOD; i++) {
650 if (specific_food_collision(i, x, y)) {
651 retVal = i;
652 break;
655 return retVal;
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
665 * arghIndex.
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 )
674 return true;
677 return false;
681 * Returns the index of the argh that is at the
682 * given coordinates. If no argh is at the coordinates
683 * -1 is returned.
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) {
689 int i = 0;
690 int retVal = -1;
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)) {
695 retVal = i;
696 break;
699 return retVal;
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)
710 bool retVal = false;
712 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
713 food_size - 1, food_size - 1);
715 return retVal;
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
725 * moves.
727 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){
728 bool retVal = false;
729 int x1, y1, x2, y2;
730 x1 = w->x[w->head];
731 y1 = w->y[w->head];
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);
738 return retVal;
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)
749 bool retVal = false;
751 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
752 argh_size - 1, argh_size - 1);
754 return retVal;
758 * Find new coordinates for the food stored in foodx[index], foody[index]
759 * that don't collide with any other food or argh
760 * @param int index
761 * Ensure that 0 <= index < MAX_FOOD.
763 static void make_food(int index) {
765 int x = 0;
766 int y = 0;
767 bool collisionDetected = false;
768 int i;
770 do {
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.
781 collisionDetected =
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 */
792 foodx[index] = x;
793 foody[index] = y;
795 /* now test wether we accidently hit the worm with food ;) */
796 i = 0;
797 for (i = 0; i < worm_count && !collisionDetected; i++) {
798 collisionDetected |= worm_food_collision(&worms[i], index);
801 while (collisionDetected);
802 return;
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);
832 #endif
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);
843 #endif
847 * Find new coordinates for the argh stored in arghx[index], arghy[index]
848 * that don't collide with any other food or argh.
849 * @param int index
850 * Ensure that 0 <= index < argh_count < MAX_ARGH.
852 static void make_argh(int index)
854 int x = -1;
855 int y = -1;
856 bool collisionDetected = false;
857 int i;
859 do {
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.
870 collisionDetected =
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 */
881 arghx[index] = x;
882 arghy[index] = y;
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,
888 MIN_ARGH_DIST);
891 while (collisionDetected);
892 return;
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);
906 #endif
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);
912 #endif
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 */
928 w->head = 1;
929 w->tail = 0;
931 w->x[w->head] = x + 1;
932 w->y[w->head] = y;
934 w->x[w->tail] = x;
935 w->y[w->tail] = y;
937 /* set the initial direction the worm creeps to */
938 w->dirx = 1;
939 w->diry = 0;
941 w->growing = INITIAL_WORM_LENGTH - 1;
942 w->alive = true;
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.
950 * The value of
951 * the direction is read from player1_dir.
952 * @param struct worm *w - The worm of which the direction
953 * is altered.
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.
963 * The value of
964 * the direction is read from player2_dir.
965 * @param struct worm *w - The worm of which the direction
966 * is altered.
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.
977 * The value of
978 * the direction is read from player3_dir.
979 * @param struct worm *w - The worm of which the direction
980 * is altered.
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)
992 int i;
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);
1002 player1_dir = EAST;
1003 player2_dir = EAST;
1004 player3_dir = EAST;
1006 if (players > 0) {
1007 worms[0].fetch_worm_direction = human_player1;
1010 if (players > 1) {
1011 if (use_remote) {
1012 worms[1].fetch_worm_direction = remote_player;
1013 } else {
1014 worms[1].fetch_worm_direction = human_player2;
1018 if (players > 2) {
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++) {
1028 make_food(i);
1029 draw_food(i);
1030 make_argh(i);
1031 draw_argh(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 */
1041 rb->lcd_update();
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)
1054 if (w->alive) {
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 */
1063 int olddirx;
1064 int olddiry;
1065 if (headx == preheadx) {
1066 olddirx = 0;
1067 olddiry = (heady > preheady) ? 1 : -1;
1068 } else {
1069 olddiry = 0;
1070 olddirx = (headx > preheadx) ? 1 : -1;
1073 /* olddir == dir?
1074 a change of direction means a new segment
1075 has been opened */
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 */
1089 w->growing--;
1092 /* if the worm isn't growing the tail has to be dragged */
1093 else {
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;
1104 } else {
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
1111 must be freed */
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);
1131 #endif
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 */
1142 x = w->x[w->tail] ;
1143 y = w->y[w->tail] ;
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);
1150 #endif
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)
1163 int retVal = -1;
1165 /* get_worm_array_length is expensive -> buffer the value */
1166 int wormLength = get_worm_array_length(w);
1167 int i;
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;
1181 if (samey) {
1182 min = w->x[linestart];
1183 max = w->x[lineend];
1184 test = x;
1185 } else {
1186 min = w->y[linestart];
1187 max = w->y[lineend];
1188 test = y;
1191 tmp = min;
1192 min = MIN(min, max);
1193 max = MAX(tmp, max);
1195 if (min <= test && test <= max) {
1196 retVal = lineend;
1200 return retVal;
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) {
1212 w->growing += 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;
1233 int i;
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)){
1238 retVal = &worms[i];
1242 return retVal;
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))
1258 retVal = true;
1260 return retVal;
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
1270 * field of worms.
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);
1276 return retVal;
1280 * Checks and returns wether the head of the w
1281 * is colliding with something currently.
1282 * @return int One of the values:
1283 * COLLISION_NONE
1284 * COLLISION_w
1285 * COLLISION_FOOD
1286 * COLLISION_ARGH
1287 * COLLISION_FIELD
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;
1305 return retVal;
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
1311 * foody arrays.
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;
1319 int deltax = 0;
1320 int deltay = 0;
1321 int foodindex;
1322 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1323 int distance;
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;
1335 return nearestfood;
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];
1354 if (w->dirx == 0) {
1355 infront = (w->diry * deltay) > 0;
1356 } else {
1357 infront = (w->dirx * deltax) > 0;
1359 return infront;
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);
1373 if (!retVal) {
1374 retVal = (argh_collision(x, y) != -1);
1377 if (!retVal) {
1378 retVal = (worm_collision(w, x, y) != NULL);
1380 return retVal;
1384 * This function
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
1389 * worm.
1390 * @param struct worm *w - The worm of which the direction
1391 * is altered.
1393 static void virtual_player(struct worm *w) {
1394 bool isright;
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 */
1411 if (infront) {
1412 if (isright) {
1413 plana = olddir;
1414 planb = (olddir + 1) % 4;
1415 planc = (olddir + 3) % 4;
1416 } else {
1417 plana = olddir;
1418 planb = (olddir + 3) % 4;
1419 planc = (olddir + 1) % 4;
1421 } else {
1422 if (isright) {
1423 plana = (olddir + 1) % 4;
1424 planb = olddir;
1425 planc = (olddir + 3) % 4;
1426 } else {
1427 plana = (olddir + 3) % 4;
1428 planb = olddir;
1429 planc = (olddir + 1) % 4;
1433 /* test for collision */
1434 set_worm_dir(w, plana);
1435 if (will_worm_collide(w)){
1437 /* plan b */
1438 set_worm_dir(w, planb);
1440 /* test for collision */
1441 if (will_worm_collide(w)) {
1443 /* plan c */
1444 set_worm_dir(w, planc);
1450 * prints out the score board with all the status information
1451 * about the game.
1453 static void score_board(void)
1455 char buf[15];
1456 char* buf2 = NULL;
1457 int i;
1458 int y = 0;
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]);
1465 /* high score */
1466 if (worms[i].fetch_worm_direction != virtual_player){
1467 if (highscore < score) {
1468 highscore = score;
1472 /* length */
1473 rb->snprintf(buf, sizeof (buf),"Len:%d", score);
1475 /* worm state */
1476 switch (check_collision(&worms[i])) {
1477 case COLLISION_NONE:
1478 if (worms[i].growing > 0)
1479 buf2 = "Growing";
1480 else {
1481 if (worms[i].alive)
1482 buf2 = "Hungry";
1483 else
1484 buf2 = "Wormed";
1486 break;
1488 case COLLISION_WORM:
1489 buf2 = "Wormed";
1490 break;
1492 case COLLISION_FOOD:
1493 buf2 = "Growing";
1494 break;
1496 case COLLISION_ARGH:
1497 buf2 = "Argh";
1498 break;
1500 case COLLISION_FIELD:
1501 buf2 = "Crashed";
1502 break;
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);
1513 y += 19;
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);
1518 #else
1519 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
1520 #endif
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)
1531 int index = -1;
1533 w->alive &= !field_collision(w);
1535 if (w->alive) {
1537 /* check if food was eaten */
1538 index = food_collision(w->x[w->head], w->y[w->head]);
1539 if (index != -1){
1540 int i;
1542 clear_food(index);
1543 make_food(index);
1544 draw_food(index);
1546 for (i = 0; i < arghs_per_food; i++) {
1547 argh_count++;
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);
1556 draw_worm(w);
1559 /* check if argh was eaten */
1560 else {
1561 index = argh_collision(w->x[w->head], w->y[w->head]);
1562 if (index != -1) {
1563 w->alive = false;
1565 else {
1566 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1567 w->alive = false;
1572 return !w->alive;
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)
1583 int button = 0;
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;
1591 long max_cycle = 0;
1592 char buf[20];
1593 #endif
1595 /* initialize the board and so on */
1596 init_wormlet();
1598 cycle_start = *rb->current_tick;
1599 /* change the direction of the worm */
1600 while (!wormDead)
1602 int i;
1603 long cycle_duration=0;
1605 #ifdef HAS_BUTTON_HOLD
1606 if (rb->button_hold())
1607 paused = true;
1608 #endif
1610 switch (button) {
1611 case BTN_STARTPAUSE:
1612 paused = !paused;
1613 break;
1614 case BTN_STOPRESET:
1615 if (paused)
1616 return 1; /* restart game */
1617 else
1618 paused = true;
1619 break;
1620 #ifdef BTN_RC_QUIT
1621 case BTN_RC_QUIT:
1622 #endif
1623 case BTN_QUIT:
1624 return 2; /* back to menu */
1625 break;
1627 if (!paused)
1629 switch (button) {
1630 case BTN_DIR_UP:
1631 if (players == 1 && !use_remote) {
1632 player1_dir = NORTH;
1634 break;
1636 case BTN_DIR_DOWN:
1637 if (players == 1 && !use_remote) {
1638 player1_dir = SOUTH;
1640 break;
1642 case BTN_DIR_LEFT:
1643 if (players != 1 || use_remote) {
1644 player1_dir = (player1_dir + 3) % 4;
1645 } else {
1646 player1_dir = WEST;
1648 break;
1650 case BTN_DIR_RIGHT:
1651 if (players != 1 || use_remote) {
1652 player1_dir = (player1_dir + 1) % 4;
1653 } else {
1654 player1_dir = EAST;
1656 break;
1658 #ifdef MULTIPLAYER
1659 case BTN_PLAYER2_DIR1:
1660 player2_dir = (player2_dir + 3) % 4;
1661 break;
1663 case BTN_PLAYER2_DIR2:
1664 player2_dir = (player2_dir + 1) % 4;
1665 break;
1666 #endif
1668 #ifdef REMOTE
1669 case BTN_RC_UP:
1670 player3_dir = (player3_dir + 1) % 4;
1671 break;
1673 case BTN_RC_DOWN:
1674 player3_dir = (player3_dir + 3) % 4;
1675 break;
1676 #endif
1680 for (i = 0; i < worm_count; i++) {
1681 worms[i].fetch_worm_direction(&worms[i]);
1684 wormDead = true;
1685 for (i = 0; i < worm_count; i++){
1686 struct worm *w = &worms[i];
1687 move_worm(w);
1688 wormDead &= process_collisions(w);
1689 draw_worm(w);
1691 score_board();
1692 rb->lcd_update();
1693 if (button == BTN_STOPRESET) {
1694 wormDead = true;
1697 /* here the wormlet game cycle ends
1698 thus the current tick is stored
1699 as end time */
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) {
1711 max_cycle = 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);
1719 set_debug_out(buf);
1720 #endif
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;
1742 int i;
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]);
1758 foodx[0] = 15;
1759 foody[0] = 12;
1760 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1761 char buf[20];
1762 bool collision;
1763 draw_worm(&worms[0]);
1764 draw_food(0);
1765 collision = worm_food_collision(&worms[0], 0);
1766 if (collision) {
1767 collision_count++;
1769 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1770 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1771 rb->lcd_update();
1773 if (collision_count != food_size) {
1774 rb->button_get(true);
1778 foody[0] = 15;
1779 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1780 char buf[20];
1781 bool collision;
1782 draw_worm(&worms[0]);
1783 draw_food(0);
1784 collision = worm_food_collision(&worms[0], 0);
1785 if (collision) {
1786 collision_count ++;
1788 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1789 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1790 rb->lcd_update();
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){
1799 int x, y;
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) {
1804 retVal = true;
1808 return retVal;
1811 static void test_worm_argh_collision(void) {
1812 int i;
1813 int dir;
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]);
1826 arghx[0] = 12;
1827 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1828 char buf[20];
1829 bool collision;
1830 draw_argh(0);
1831 collision = worm_argh_collision(&worms[0], 0);
1832 if (collision) {
1833 collision_count ++;
1835 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1836 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1837 rb->lcd_update();
1839 if (collision_count != argh_size * 2) {
1840 rb->button_get(true);
1843 arghy[0] = 12;
1844 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1845 char buf[20];
1846 bool collision;
1847 draw_argh(0);
1848 collision = worm_argh_collision(&worms[0], 0);
1849 if (collision) {
1850 collision_count ++;
1852 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1853 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1854 rb->lcd_update();
1856 if (collision_count != argh_size * 4) {
1857 rb->button_get(true);
1861 static int testline_in_rect(void) {
1862 int testfailed = -1;
1864 int rx = 10;
1865 int ry = 15;
1866 int rw = 20;
1867 int rh = 25;
1869 /* Test 1 */
1870 int x1 = 12;
1871 int y1 = 8;
1872 int x2 = 12;
1873 int y2 = 42;
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);
1879 rb->lcd_update();
1880 rb->lcd_putsxy(0, 0, "failed 1");
1881 rb->button_get(true);
1882 testfailed = 1;
1885 /* test 2 */
1886 y2 = 20;
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");
1892 rb->lcd_update();
1893 rb->button_get(true);
1894 testfailed = 2;
1897 /* test 3 */
1898 y1 = 30;
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");
1904 rb->lcd_update();
1905 rb->button_get(true);
1906 testfailed = 3;
1909 /* test 4 */
1910 y2 = 45;
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");
1916 rb->lcd_update();
1917 rb->button_get(true);
1918 testfailed = 4;
1921 /* test 5 */
1922 y1 = 50;
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");
1928 rb->lcd_update();
1929 rb->button_get(true);
1930 testfailed = 5;
1933 /* test 6 */
1934 y1 = 5;
1935 y2 = 7;
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");
1941 rb->lcd_update();
1942 rb->button_get(true);
1943 testfailed = 6;
1946 /* test 7 */
1947 x1 = 8;
1948 y1 = 20;
1949 x2 = 35;
1950 y2 = 20;
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");
1956 rb->lcd_update();
1957 rb->button_get(true);
1958 testfailed = 7;
1961 /* test 8 */
1962 x2 = 12;
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");
1968 rb->lcd_update();
1969 rb->button_get(true);
1970 testfailed = 8;
1973 /* test 9 */
1974 x1 = 25;
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");
1980 rb->lcd_update();
1981 rb->button_get(true);
1982 testfailed = 9;
1985 /* test 10 */
1986 x2 = 37;
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");
1992 rb->lcd_update();
1993 rb->button_get(true);
1994 testfailed = 10;
1997 /* test 11 */
1998 x1 = 42;
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");
2004 rb->lcd_update();
2005 rb->button_get(true);
2006 testfailed = 11;
2009 /* test 12 */
2010 x1 = 5;
2011 x2 = 7;
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");
2017 rb->lcd_update();
2018 rb->button_get(true);
2019 testfailed = 12;
2022 /* test 13 */
2023 rx = 9;
2024 ry = 15;
2025 rw = food_size;
2026 rh = food_size;
2028 x1 = 10;
2029 y1 = 10;
2030 x2 = 10;
2031 y2 = 20;
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");
2037 rb->lcd_update();
2038 rb->button_get(true);
2039 testfailed = 13;
2042 /* test 14 */
2043 rx = 9;
2044 ry = 15;
2045 rw = 4;
2046 rh = 4;
2048 x1 = 10;
2049 y1 = 10;
2050 x2 = 10;
2051 y2 = 19;
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");
2057 rb->lcd_update();
2058 rb->button_get(true);
2059 testfailed = 14;
2062 rb->lcd_clear_display();
2064 return testfailed;
2068 * Just a test routine to test wether specific_worm_collision might work properly
2070 static int test_specific_worm_collision(void) {
2071 int collisions = 0;
2072 int dir;
2073 int x = 0;
2074 int y = 0;
2075 char buf[20];
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++) {
2081 int i;
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) {
2094 collisions ++;
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);
2099 rb->lcd_update();
2102 if (collisions != 21) {
2103 rb->button_get(true);
2105 return collisions;
2108 static void test_make_argh(void){
2109 int dir;
2110 int seed = 0;
2111 int hit = 0;
2112 int failures = 0;
2113 int last_failures = 0;
2114 int i, worm_idx;
2115 rb->lcd_clear_display();
2116 worm_count = 3;
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]);
2135 rb->lcd_update();
2137 for (seed = 0; hit < 20; seed += 2) {
2138 char buf[20];
2139 int x, y;
2140 rb->srand(seed);
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)) {
2146 int tries = 0;
2147 rb->srand(seed);
2149 tries = make_argh(0);
2150 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2151 failures ++;
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);
2156 rb->lcd_update();
2157 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, argh_size, argh_size);
2158 rb->lcd_update();
2159 draw_argh(0);
2160 rb->lcd_update();
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;
2168 hit ++;
2174 static void test_worm_argh_collision_in_moves(void) {
2175 int hit_count = 0;
2176 int i;
2177 rb->lcd_clear_display();
2178 init_worm(&worms[0], 10, 20);
2180 arghx[0] = 20;
2181 arghy[0] = 18;
2182 draw_argh(0);
2184 set_worm_dir(&worms[0], EAST);
2185 for (i = 0; i < 20; i++) {
2186 char buf[20];
2187 move_worm(&worms[0]);
2188 draw_worm(&worms[0]);
2189 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2190 hit_count ++;
2192 rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
2193 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2194 rb->lcd_update();
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)
2214 bool ret;
2215 static const struct opt_items num_worms_option[3] = {
2216 { "1", -1 },
2217 { "2", -1 },
2218 { "3", -1 },
2221 ret = rb->set_option("Number Of Worms", &worm_count,INT, num_worms_option, 3, NULL);
2222 if (worm_count < players) {
2223 worm_count=players;
2225 return ret;
2229 Sets the number of human players
2231 bool set_worm_num_players(void)
2233 bool ret;
2234 static const struct opt_items num_players_option[4] = {
2235 { "0", -1 },
2236 { "1", -1 },
2237 { "2", -1 },
2238 { "3", -1 }
2241 ret = rb->set_option("Number of Players", &players, INT,num_players_option , 4, NULL);
2242 if (players > worm_count) {
2243 worm_count = players;
2245 if (players > 2) {
2246 use_remote = true;
2248 return ret;
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] = {
2257 { "0", -1 },
2258 { "1", -1 },
2259 { "2", -1 },
2260 { "3", -1 },
2261 { "4", -1 },
2262 { "5", -1 },
2263 { "6", -1 },
2264 { "7", -1 },
2265 { "8", -1 },
2266 { "9", -1 },
2267 { "10", -1 }
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] = {
2279 { "0", -1 },
2280 { "1", -1 },
2281 { "2", -1 },
2282 { "3", -1 },
2283 { "4", -1 },
2284 { "5", -1 },
2285 { "6", -1 },
2286 { "7", -1 },
2287 { "8", -1 },
2288 { "9", -1 },
2289 { "10", -1 }
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] = {
2300 { "0", -1 },
2301 { "1", -1 },
2302 { "2", -1 },
2303 { "3", -1 },
2304 { "4", -1 },
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)
2314 bool ret;
2315 static const struct opt_items speed_option[19] = {
2316 { "0", -1 },
2317 { "1", -1 },
2318 { "2", -1 },
2319 { "3", -1 },
2320 { "4", -1 },
2321 { "5", -1 },
2322 { "6", -1 },
2323 { "7", -1 },
2324 { "8", -1 },
2325 { "9", -1 },
2326 { "10", -1 },
2327 { "11", -1 },
2328 { "12", -1 },
2329 { "13", -1 },
2330 { "14", -1 },
2331 { "15", -1 },
2332 { "16", -1 },
2333 { "17", -1 },
2334 { "18", -1 },
2338 speed = 20 - speed;
2339 ret = rb->set_option("Worm Speed", &speed,INT,speed_option , 19, NULL);
2340 speed = 20 - speed;
2341 return ret;
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 },
2366 if (players > 1) {
2367 rb->set_option("Control Style",&use_remote,INT, key1_option, 2, NULL);
2368 } else {
2369 if (players > 0) {
2370 rb->set_option("Control Style",&use_remote,INT, key2_option, 2, NULL);
2372 else {
2373 rb->set_option("Control Style",&use_remote,INT, key3_option, 1, NULL);
2376 return false;
2379 void default_settings(void)
2381 arghs_per_food = ARGHS_PER_FOOD;
2382 argh_size = ARGH_SIZE;
2383 food_size = FOOD_SIZE;
2384 speed = SPEED;
2385 worm_food = WORM_PER_FOOD;
2386 players = 1;
2387 worm_count = MAX_WORMS;
2388 use_remote = false;
2389 return;
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)
2410 case 2:
2411 /* Turn on backlight timeout (revert to settings) */
2412 backlight_use_settings(rb); /* backlight control in lib/helper.c */
2413 return false;
2414 break;
2416 return false;
2419 /* End of settings/changes etc */
2422 * Main entry point
2424 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
2426 int result;
2427 int menu_quit = 0;
2428 int new_setting;
2430 (void)(parameter);
2431 rb = api;
2433 default_settings();
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),
2443 SETTINGS_VERSION);
2446 #ifdef HAVE_LCD_COLOR
2447 rb->lcd_set_foreground(COLOR_FG);
2448 rb->lcd_set_background(COLOR_BG);
2449 #endif
2451 #if LCD_DEPTH > 1
2452 rb->lcd_set_backdrop(NULL);
2453 #endif
2455 #ifdef DEBUG_WORMLET
2456 testline_in_rect();
2457 test_worm_argh_collision_in_moves();
2458 test_make_argh();
2459 test_worm_food_collision();
2460 test_worm_argh_collision();
2461 test_specific_worm_collision();
2462 #endif
2464 /* Setup screen */
2466 static const struct opt_items noyes[2] = {
2467 { "No", -1 },
2468 { "Yes", -1 },
2471 static const struct opt_items num_worms_option[3] = {
2472 { "1", -1 },
2473 { "2", -1 },
2474 { "3", -1 }
2477 #ifdef MULTIPLAYER
2478 static const struct opt_items num_players_option[4] = {
2479 #else
2480 static const struct opt_items num_players_option[2] = {
2481 #endif
2482 { "0", -1 },
2483 { "1", -1 }
2484 #ifdef MULTIPLAYER
2485 ,{ "2", -1 },
2486 { "3", -1 }
2487 #endif
2490 static const struct opt_items argh_size_option[9] = {
2491 { "2", -1 },
2492 { "3", -1 },
2493 { "4", -1 },
2494 { "5", -1 },
2495 { "6", -1 },
2496 { "7", -1 },
2497 { "8", -1 },
2498 { "9", -1 },
2499 { "10", -1 }
2502 static const struct opt_items food_size_option[9] = {
2503 { "2", -1 },
2504 { "3", -1 },
2505 { "4", -1 },
2506 { "5", -1 },
2507 { "6", -1 },
2508 { "7", -1 },
2509 { "8", -1 },
2510 { "9", -1 },
2511 { "10", -1 }
2514 static const struct opt_items worm_food_option[16] = {
2515 { "0", -1 },
2516 { "1", -1 },
2517 { "2", -1 },
2518 { "3", -1 },
2519 { "4", -1 },
2520 { "5", -1 },
2521 { "6", -1 },
2522 { "7", -1 },
2523 { "8", -1 },
2524 { "9", -1 },
2525 { "10", -1 },
2526 { "11", -1 },
2527 { "12", -1 },
2528 { "13", -1 },
2529 { "14", -1 },
2530 { "15", -1 }
2533 static const struct opt_items argh_food_option[9] = {
2534 { "0", -1 },
2535 { "1", -1 },
2536 { "2", -1 },
2537 { "3", -1 },
2538 { "4", -1 },
2539 { "5", -1 },
2540 { "6", -1 },
2541 { "7", -1 },
2542 { "8", -1 }
2545 static const struct opt_items speed_option[21] = {
2546 { "0", -1 },
2547 { "1", -1 },
2548 { "2", -1 },
2549 { "3", -1 },
2550 { "4", -1 },
2551 { "5", -1 },
2552 { "6", -1 },
2553 { "7", -1 },
2554 { "8", -1 },
2555 { "9", -1 },
2556 { "10", -1 },
2557 { "11", -1 },
2558 { "12", -1 },
2559 { "13", -1 },
2560 { "14", -1 },
2561 { "15", -1 },
2562 { "16", -1 },
2563 { "17", -1 },
2564 { "18", -1 },
2565 { "19", -1 },
2566 { "20", -1 }
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 }
2578 #ifdef REMOTE
2579 static const struct opt_items remote_option[2] = {
2580 { "Remote Control", -1 },
2581 { "No Rem. Control", -1 }
2583 #else
2584 static const struct opt_items key2_option[1] = {
2585 { "2 Key Control", -1 }
2587 #endif
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",
2597 "Quit");
2599 rb->button_clear_queue();
2601 while (!menu_quit) {
2602 switch(rb->do_menu(&menu, &result, NULL, false))
2604 case 0:
2605 rb->lcd_setfont(FONT_SYSFIXED);
2606 launch_wormlet();
2607 break;
2608 case 1:
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;
2616 break;
2617 case 2:
2618 new_setting = players;
2619 #ifdef MULTIPLAYER
2620 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 4, NULL);
2621 #else
2622 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 2, NULL);
2623 #endif
2624 if (new_setting != players)
2625 players = new_setting;
2626 if (players > worm_count) {
2627 worm_count = players;
2629 if (players > 2) {
2630 use_remote = true;
2632 break;
2633 case 3:
2634 new_setting = use_remote;
2635 switch(players) {
2636 case 0:
2637 rb->set_option("Control Style",&new_setting,INT, nokey_option, 1, NULL);
2638 break;
2639 case 1:
2640 rb->set_option("Control Style",&new_setting,INT, key24_option, 2, NULL);
2641 break;
2642 case 2:
2643 #ifdef REMOTE
2644 rb->set_option("Control Style",&new_setting,INT, remote_option, 2, NULL);
2645 #else
2646 rb->set_option("Control Style",&new_setting,INT, key2_option, 1, NULL);
2647 #endif
2648 break;
2649 case 3:
2650 rb->set_option("Control Style",&new_setting,INT, remoteonly_option, 1, NULL);
2651 break;
2653 if (new_setting != use_remote)
2654 use_remote = new_setting;
2655 break;
2656 case 4:
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;
2661 break;
2662 case 5:
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;
2669 break;
2670 case 6:
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;
2675 break;
2676 case 7:
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;
2681 break;
2682 case 8:
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;
2687 break;
2688 case 9:
2689 new_setting = 0;
2690 rb->set_option("Reset Settings?", &new_setting,INT, noyes , 2, NULL);
2691 if (new_setting == 1)
2692 default_settings();
2693 break;
2694 default:
2695 menu_quit=1;
2696 break;
2700 configfile_save(SETTINGS_FILENAME, config,
2701 sizeof(config)/sizeof(*config),
2702 SETTINGS_VERSION);
2704 return PLUGIN_OK;