Minor corrections to the .colours file editing; added .colours to the list of support...
[kugel-rb.git] / apps / plugins / wormlet.c
blob2eed1aa357d3852339f336ca13cd0b88dd40dd84
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
21 #include "plugin.h"
22 #include "lib/configfile.h"
23 #include "lib/helper.h"
24 #include "lib/playback_control.h"
26 PLUGIN_HEADER
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 */
41 #define MAX_WORMS 3
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
62 #define REMOTE
63 #define MULTIPLAYER
64 #endif
66 #define PLAYERS_TEXT "UP/DN"
67 #define WORMS_TEXT "L/R"
68 #define KEY_CONTROL_TEXT "F1"
70 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
71 #define BTN_DIR_UP BUTTON_UP
72 #define BTN_DIR_DOWN BUTTON_DOWN
73 #define BTN_DIR_LEFT BUTTON_LEFT
74 #define BTN_DIR_RIGHT BUTTON_RIGHT
75 #define BTN_PLAYER2_DIR1 BUTTON_F2
76 #define BTN_PLAYER2_DIR2 BUTTON_F3
77 #define BTN_STARTPAUSE BUTTON_SELECT
78 #define BTN_QUIT BUTTON_OFF
79 #define BTN_STOPRESET BUTTON_ON
80 #define BTN_TOGGLE_KEYS BUTTON_F1
82 #define PLAYERS_TEXT "UP/DN"
83 #define WORMS_TEXT "L/R"
84 #define KEY_CONTROL_TEXT "F1"
86 #elif (CONFIG_KEYPAD == ONDIO_PAD)
87 #define BTN_DIR_UP BUTTON_UP
88 #define BTN_DIR_DOWN BUTTON_DOWN
89 #define BTN_DIR_LEFT BUTTON_LEFT
90 #define BTN_DIR_RIGHT BUTTON_RIGHT
91 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
92 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
93 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
95 #define PLAYERS_TEXT "UP/DN"
96 #define WORMS_TEXT "L/R"
98 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
99 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
101 #define BTN_DIR_UP BUTTON_MENU
102 #define BTN_DIR_DOWN BUTTON_PLAY
103 #define BTN_DIR_LEFT BUTTON_LEFT
104 #define BTN_DIR_RIGHT BUTTON_RIGHT
105 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
106 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
107 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
109 #define PLAYERS_TEXT "Menu/Play"
110 #define WORMS_TEXT "Left/Right"
112 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
114 #define BTN_DIR_UP BUTTON_UP
115 #define BTN_DIR_DOWN BUTTON_DOWN
116 #define BTN_DIR_LEFT BUTTON_LEFT
117 #define BTN_DIR_RIGHT BUTTON_RIGHT
118 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
119 #define BTN_QUIT BUTTON_OFF
120 #define BTN_STOPRESET BUTTON_ON
122 #define BTN_RC_QUIT BUTTON_RC_STOP
124 #define PLAYERS_TEXT "Up/Down"
125 #define WORMS_TEXT "Left/Right"
127 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
129 #define BTN_DIR_UP BUTTON_UP
130 #define BTN_DIR_DOWN BUTTON_DOWN
131 #define BTN_DIR_LEFT BUTTON_LEFT
132 #define BTN_DIR_RIGHT BUTTON_RIGHT
133 #define BTN_STARTPAUSE BUTTON_PLAY
134 #define BTN_QUIT BUTTON_POWER
135 #define BTN_STOPRESET BUTTON_REC
137 #define PLAYERS_TEXT "Up/Down"
138 #define WORMS_TEXT "Left/Right"
140 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
142 #define BTN_DIR_UP BUTTON_UP
143 #define BTN_DIR_DOWN BUTTON_DOWN
144 #define BTN_DIR_LEFT BUTTON_LEFT
145 #define BTN_DIR_RIGHT BUTTON_RIGHT
146 #define BTN_STARTPAUSE BUTTON_SELECT
147 #define BTN_QUIT BUTTON_POWER
148 #define BTN_STOPRESET BUTTON_A
150 #define PLAYERS_TEXT "Up/Down"
151 #define WORMS_TEXT "Left/Right"
154 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
155 (CONFIG_KEYPAD == SANSA_C200_PAD)
157 #define BTN_DIR_UP BUTTON_UP
158 #define BTN_DIR_DOWN BUTTON_DOWN
159 #define BTN_DIR_LEFT BUTTON_LEFT
160 #define BTN_DIR_RIGHT BUTTON_RIGHT
161 #define BTN_STARTPAUSE BUTTON_SELECT
162 #define BTN_QUIT BUTTON_POWER
163 #define BTN_STOPRESET BUTTON_REC
165 #define PLAYERS_TEXT "Up/Down"
166 #define WORMS_TEXT "Left/Right"
169 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
170 (CONFIG_KEYPAD == SANSA_FUZE_PAD)
172 #define BTN_DIR_UP BUTTON_UP
173 #define BTN_DIR_DOWN BUTTON_DOWN
174 #define BTN_DIR_LEFT BUTTON_LEFT
175 #define BTN_DIR_RIGHT BUTTON_RIGHT
176 #define BTN_STARTPAUSE BUTTON_SELECT
177 #define BTN_QUIT BUTTON_POWER
178 #define BTN_STOPRESET BUTTON_HOME
180 #define PLAYERS_TEXT "Up/Down"
181 #define WORMS_TEXT "Left/Right"
183 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
185 #define BTN_DIR_UP BUTTON_UP
186 #define BTN_DIR_DOWN BUTTON_DOWN
187 #define BTN_DIR_LEFT BUTTON_LEFT
188 #define BTN_DIR_RIGHT BUTTON_RIGHT
189 #define BTN_STARTPAUSE (BUTTON_SELECT | BUTTON_REL)
190 #define BTN_QUIT BUTTON_POWER
191 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
193 #define PLAYERS_TEXT "Up/Down"
194 #define WORMS_TEXT "Left/Right"
196 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
198 #define BTN_DIR_UP BUTTON_SCROLL_UP
199 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
200 #define BTN_DIR_LEFT BUTTON_LEFT
201 #define BTN_DIR_RIGHT BUTTON_RIGHT
202 #define BTN_STARTPAUSE BUTTON_PLAY
203 #define BTN_QUIT BUTTON_POWER
204 #define BTN_STOPRESET BUTTON_REW
206 #define PLAYERS_TEXT "Up/Down"
207 #define WORMS_TEXT "Left/Right"
209 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
211 #define BTN_DIR_UP BUTTON_UP
212 #define BTN_DIR_DOWN BUTTON_DOWN
213 #define BTN_DIR_LEFT BUTTON_LEFT
214 #define BTN_DIR_RIGHT BUTTON_RIGHT
215 #define BTN_STARTPAUSE BUTTON_SELECT
216 #define BTN_QUIT BUTTON_BACK
217 #define BTN_STOPRESET BUTTON_MENU
219 #define PLAYERS_TEXT "Up/Down"
220 #define WORMS_TEXT "Left/Right"
222 #elif (CONFIG_KEYPAD == MROBE100_PAD)
224 #define BTN_DIR_UP BUTTON_UP
225 #define BTN_DIR_DOWN BUTTON_DOWN
226 #define BTN_DIR_LEFT BUTTON_LEFT
227 #define BTN_DIR_RIGHT BUTTON_RIGHT
228 #define BTN_STARTPAUSE BUTTON_SELECT
229 #define BTN_QUIT BUTTON_POWER
230 #define BTN_STOPRESET BUTTON_DISPLAY
232 #define PLAYERS_TEXT "Up/Down"
233 #define WORMS_TEXT "Left/Right"
235 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
237 #define BTN_DIR_UP BUTTON_RC_VOL_UP
238 #define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN
239 #define BTN_DIR_LEFT BUTTON_RC_REW
240 #define BTN_DIR_RIGHT BUTTON_RC_FF
241 #define BTN_STARTPAUSE BUTTON_RC_PLAY
242 #define BTN_QUIT BUTTON_RC_REC
243 #define BTN_STOPRESET BUTTON_RC_MODE
245 #define PLAYERS_TEXT "VOL UP/DN"
246 #define WORMS_TEXT "REW/FF"
248 #elif (CONFIG_KEYPAD == COWOND2_PAD)
250 #define BTN_QUIT BUTTON_POWER
252 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
254 #define BTN_DIR_UP BUTTON_UP
255 #define BTN_DIR_DOWN BUTTON_DOWN
256 #define BTN_DIR_LEFT BUTTON_LEFT
257 #define BTN_DIR_RIGHT BUTTON_RIGHT
258 #define BTN_STARTPAUSE BUTTON_PLAY
259 #define BTN_QUIT BUTTON_BACK
260 #define BTN_STOPRESET BUTTON_MENU
262 #define PLAYERS_TEXT "Up/Down"
263 #define WORMS_TEXT "Left/Right"
265 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
267 #define BTN_DIR_UP BUTTON_UP
268 #define BTN_DIR_DOWN BUTTON_DOWN
269 #define BTN_DIR_LEFT BUTTON_LEFT
270 #define BTN_DIR_RIGHT BUTTON_RIGHT
271 #define BTN_STARTPAUSE BUTTON_MENU
272 #define BTN_QUIT BUTTON_POWER
273 #define BTN_STOPRESET BUTTON_VIEW
275 #define PLAYERS_TEXT "Up/Down"
276 #define WORMS_TEXT "Left/Right"
278 #else
279 #error No keymap defined!
280 #endif
282 #ifdef HAVE_TOUCHSCREEN
283 #ifndef BTN_DIR_UP
284 #define BTN_DIR_UP BUTTON_TOPMIDDLE
285 #endif
286 #ifndef BTN_DIR_DOWN
287 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
288 #endif
289 #ifndef BTN_DIR_LEFT
290 #define BTN_DIR_LEFT BUTTON_MIDLEFT
291 #endif
292 #ifndef BTN_DIR_RIGHT
293 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
294 #endif
295 #ifndef BTN_STARTPAUSE
296 #define BTN_STARTPAUSE BUTTON_CENTER
297 #endif
298 #ifndef BTN_QUIT
299 #define BTN_QUIT BUTTON_TOPLEFT
300 #endif
301 #ifndef BTN_STOPRESET
302 #define BTN_STOPRESET BUTTON_TOPRIGHT
304 #endif
305 #ifndef PLAYERS_TEXT
306 #define PLAYERS_TEXT "Up/Down"
307 #endif
308 #ifndef WORMS_TEXT
309 #define WORMS_TEXT "Left/Right"
310 #endif
311 #endif
314 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
315 #define FOOD_SIZE 3
316 #define ARGH_SIZE 4
317 #define SPEED 14
318 #define MAX_WORM_SEGMENTS 128
319 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
320 #define FOOD_SIZE 3
321 #define ARGH_SIZE 4
322 #define SPEED 14
323 #define MAX_WORM_SEGMENTS 128
324 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
325 #define FOOD_SIZE 3
326 #define ARGH_SIZE 4
327 #define SPEED 14
328 #define MAX_WORM_SEGMENTS 128
329 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
330 #define FOOD_SIZE 3
331 #define ARGH_SIZE 4
332 #define SPEED 12
333 #define MAX_WORM_SEGMENTS 128
334 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
335 #define FOOD_SIZE 4
336 #define ARGH_SIZE 5
337 #define SPEED 10
338 #define MAX_WORM_SEGMENTS 128
339 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
340 #define FOOD_SIZE 4
341 #define ARGH_SIZE 5
342 #define SPEED 9
343 #define MAX_WORM_SEGMENTS 128
344 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
345 #define FOOD_SIZE 4
346 #define ARGH_SIZE 5
347 #define SPEED 8
348 #define MAX_WORM_SEGMENTS 256
349 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
350 #define FOOD_SIZE 4
351 #define ARGH_SIZE 5
352 #define SPEED 6
353 #define MAX_WORM_SEGMENTS 256
354 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
355 #define FOOD_SIZE 5
356 #define ARGH_SIZE 6
357 #define SPEED 4
358 #define MAX_WORM_SEGMENTS 512
359 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
360 #define FOOD_SIZE 5
361 #define ARGH_SIZE 6
362 #define SPEED 4
363 #define MAX_WORM_SEGMENTS 512
364 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
365 #define FOOD_SIZE 7
366 #define ARGH_SIZE 8
367 #define SPEED 4
368 #define MAX_WORM_SEGMENTS 512
369 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
370 #define FOOD_SIZE 7
371 #define ARGH_SIZE 8
372 #define SPEED 4
373 #define MAX_WORM_SEGMENTS 512
374 #endif
376 #ifdef HAVE_LCD_COLOR
377 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
378 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
379 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
380 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
381 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
382 #endif
385 * All the properties that a worm has.
387 static struct worm {
388 /* The worm is stored in a ring of xy coordinates */
389 int x[MAX_WORM_SEGMENTS];
390 int y[MAX_WORM_SEGMENTS];
392 int head; /* index of the head within the buffer */
393 int tail; /* index of the tail within the buffer */
394 int growing; /* number of cyles the worm still keeps growing */
395 bool alive; /* the worms living state */
397 /* direction vector in which the worm moves */
398 int dirx; /* only values -1 0 1 allowed */
399 int diry; /* only values -1 0 1 allowed */
401 /* this method is used to fetch the direction the user
402 has selected. It can be one of the values
403 human_player1, human_player2, remote_player, virtual_player.
404 All these values are fuctions, that can change the direction
405 of the worm */
406 void (*fetch_worm_direction)(struct worm *w);
407 } worms[MAX_WORMS];
409 /* stores the highscore - besides it was scored by a virtual player */
410 static int highscore;
412 #define MAX_FOOD 5 /* maximal number of food items */
414 /* The arrays store the food coordinates */
415 static int foodx[MAX_FOOD];
416 static int foody[MAX_FOOD];
418 #define MAX_ARGH 100 /* maximal number of argh items */
419 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
421 /* The arrays store the argh coordinates */
422 static int arghx[MAX_ARGH];
423 static int arghy[MAX_ARGH];
425 /* the number of arghs that are currently in use */
426 static int argh_count;
428 /* the number of arghs per food, settable by user */
429 static int arghs_per_food = ARGHS_PER_FOOD;
430 /* the size of the argh, settable by user */
431 static int argh_size = ARGH_SIZE;
432 /* the size of the food, settable by user */
433 static int food_size = FOOD_SIZE;
434 /* the speed of the worm, settable by user */
435 static int speed = SPEED;
436 /* the amount a worm grows by eating a food, settable by user */
437 static int worm_food = WORM_PER_FOOD;
439 /* End additional variables */
441 #ifdef DEBUG_WORMLET
442 /* just a buffer used for debug output */
443 static char debugout[15];
444 #endif
446 /* the number of active worms (dead or alive) */
447 static int worm_count = MAX_WORMS;
449 /* in multiplayer mode: en- / disables the remote worm control
450 in singleplayer mode: toggles 4 / 2 button worm control */
451 static bool use_remote = false;
453 /* return values of check_collision */
454 #define COLLISION_NONE 0
455 #define COLLISION_WORM 1
456 #define COLLISION_FOOD 2
457 #define COLLISION_ARGH 3
458 #define COLLISION_FIELD 4
460 /* constants for use as directions.
461 Note that the values are ordered clockwise.
462 Thus increasing / decreasing the values
463 is equivalent to right / left turns. */
464 #define WEST 0
465 #define NORTH 1
466 #define EAST 2
467 #define SOUTH 3
469 /* direction of human player 1 */
470 static int player1_dir = EAST;
471 /* direction of human player 2 */
472 static int player2_dir = EAST;
473 /* direction of human player 3 */
474 static int player3_dir = EAST;
476 /* the number of (human) players that currently
477 control a worm */
478 static int players = 1;
480 #define SETTINGS_VERSION 1
481 #define SETTINGS_MIN_VERSION 1
482 #define SETTINGS_FILENAME "wormlet.cfg"
484 static struct configdata config[] =
486 {TYPE_INT, 0, 1024, { .int_p = &highscore }, "highscore", NULL},
487 {TYPE_INT, 0, 15, { .int_p = &arghs_per_food }, "arghs per food", NULL},
488 {TYPE_INT, 0, 15, { .int_p = &argh_size }, "argh size", NULL},
489 {TYPE_INT, 0, 15, { .int_p = &food_size }, "food size", NULL},
490 {TYPE_INT, 0, 3, { .int_p = &players }, "players", NULL},
491 {TYPE_INT, 0, 3, { .int_p = &worm_count }, "worms", NULL},
492 {TYPE_INT, 0, 20, { .int_p = &speed }, "speed", NULL},
493 {TYPE_INT, 0, 15, { .int_p = &worm_food }, "Worm Growth Per Food", NULL}
496 #ifdef DEBUG_WORMLET
497 static void set_debug_out(char *str){
498 strcpy(debugout, str);
500 #endif
503 * Returns the direction id in which the worm
504 * currently is creeping.
505 * @param struct worm *w The worm that is to be investigated.
506 * w Must not be null.
507 * @return int A value 0 <= value < 4
508 * Note the predefined constants NORTH, SOUTH, EAST, WEST
510 static int get_worm_dir(struct worm *w) {
511 int retVal ;
512 if (w->dirx == 0) {
513 if (w->diry == 1) {
514 retVal = SOUTH;
515 } else {
516 retVal = NORTH;
518 } else {
519 if (w->dirx == 1) {
520 retVal = EAST;
521 } else {
522 retVal = WEST;
525 return retVal;
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) {
538 switch (dir) {
539 case WEST:
540 w->dirx = -1;
541 w->diry = 0;
542 break;
543 case NORTH:
544 w->dirx = 0;
545 w->diry = - 1;
546 break;
547 case EAST:
548 w->dirx = 1;
549 w->diry = 0;
550 break;
551 case SOUTH:
552 w->dirx = 0;
553 w->diry = 1;
554 break;
559 * Returns the current length of the worm array. This
560 * is also a value for the number of bends that are in the worm.
561 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
563 static int get_worm_array_length(struct worm *w) {
564 /* initial simple calculation will be overwritten if wrong. */
565 int retVal = w->head - w->tail;
567 /* if the worm 'crosses' the boundaries of the ringbuffer */
568 if (retVal < 0) {
569 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
572 return retVal;
576 * Returns the score the specified worm. The score is the length
577 * of the worm.
578 * @param struct worm *w The worm that is to be investigated.
579 * w must not be null.
580 * @return int The length of the worm (>= 0).
582 static int get_score(struct worm *w) {
583 int retval = 0;
584 int length = get_worm_array_length(w);
585 int i;
586 for (i = 0; i < length; i++) {
588 /* The iteration iterates the length of the worm.
589 Here's the conversion to the true indices within the worm arrays. */
590 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
591 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
592 int startx = w->x[linestart];
593 int starty = w->y[linestart];
594 int endx = w->x[lineend];
595 int endy = w->y[lineend];
597 int minimum, maximum;
599 if (startx == endx) {
600 minimum = MIN(starty, endy);
601 maximum = MAX(starty, endy);
602 } else {
603 minimum = MIN(startx, endx);
604 maximum = MAX(startx, endx);
606 retval += abs(maximum - minimum);
608 return retval;
612 * Determines wether the line specified by startx, starty, endx, endy intersects
613 * the rectangle specified by x, y, width, height. Note that the line must be exactly
614 * horizontal or vertical (startx == endx or starty == endy).
615 * @param int startx The x coordinate of the start point of the line.
616 * @param int starty The y coordinate of the start point of the line.
617 * @param int endx The x coordinate of the end point of the line.
618 * @param int endy The y coordinate of the end point of the line.
619 * @param int x The x coordinate of the top left corner of the rectangle.
620 * @param int y The y coordinate of the top left corner of the rectangle.
621 * @param int width The width of the rectangle.
622 * @param int height The height of the rectangle.
623 * @return bool Returns true if the specified line intersects with the recangle.
625 static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) {
626 bool retval = false;
627 int simple, simplemin, simplemax;
628 int compa, compb, compmin, compmax;
629 int temp;
630 if (startx == endx) {
631 simple = startx;
632 simplemin = x;
633 simplemax = x + width;
635 compa = starty;
636 compb = endy;
637 compmin = y;
638 compmax = y + height;
639 } else {
640 simple = starty;
641 simplemin = y;
642 simplemax = y + height;
644 compa = startx;
645 compb = endx;
646 compmin = x;
647 compmax = x + width;
650 temp = compa;
651 compa = MIN(compa, compb);
652 compb = MAX(temp, compb);
654 if (simplemin <= simple && simple <= simplemax) {
655 if ((compmin <= compa && compa <= compmax) ||
656 (compmin <= compb && compb <= compmax) ||
657 (compa <= compmin && compb >= compmax)) {
658 retval = true;
661 return retval;
665 * Tests wether the specified worm intersects with the rect.
666 * @param struct worm *w The worm to be investigated
667 * @param int x The x coordinate of the top left corner of the rect
668 * @param int y The y coordinate of the top left corner of the rect
669 * @param int widht The width of the rect
670 * @param int height The height of the rect
671 * @return bool Returns true if the worm intersects with the rect
673 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) {
674 bool retval = false;
677 /* get_worm_array_length is expensive -> buffer the value */
678 int wormLength = get_worm_array_length(w);
679 int i;
681 /* test each entry that is part of the worm */
682 for (i = 0; i < wormLength && retval == false; i++) {
684 /* The iteration iterates the length of the worm.
685 Here's the conversion to the true indices within the worm arrays. */
686 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
687 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
688 int startx = w->x[linestart];
689 int starty = w->y[linestart];
690 int endx = w->x[lineend];
691 int endy = w->y[lineend];
693 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
696 return retval;
700 * Checks wether a specific food in the food arrays is at the
701 * specified coordinates.
702 * @param int foodIndex The index of the food in the food arrays
703 * @param int x the x coordinate.
704 * @param int y the y coordinate.
705 * @return Returns true if the coordinate hits the food specified by
706 * foodIndex.
708 static bool specific_food_collision(int foodIndex, int x, int y) {
709 bool retVal = false;
710 if (x >= foodx[foodIndex] &&
711 x < foodx[foodIndex] + food_size &&
712 y >= foody[foodIndex] &&
713 y < foody[foodIndex] + food_size) {
715 retVal = true;
717 return retVal;
721 * Returns the index of the food that is at the
722 * given coordinates. If no food is at the coordinates
723 * -1 is returned.
724 * @return int -1 <= value < MAX_FOOD
726 static int food_collision(int x, int y) {
727 int i = 0;
728 int retVal = -1;
729 for (i = 0; i < MAX_FOOD; i++) {
730 if (specific_food_collision(i, x, y)) {
731 retVal = i;
732 break;
735 return retVal;
739 * Checks wether a specific argh in the argh arrays is at the
740 * specified coordinates.
741 * @param int arghIndex The index of the argh in the argh arrays
742 * @param int x the x coordinate.
743 * @param int y the y coordinate.
744 * @return Returns true if the coordinate hits the argh specified by
745 * arghIndex.
747 static bool specific_argh_collision(int arghIndex, int x, int y) {
749 if ( x >= arghx[arghIndex] &&
750 y >= arghy[arghIndex] &&
751 x < arghx[arghIndex] + argh_size &&
752 y < arghy[arghIndex] + argh_size )
754 return true;
757 return false;
761 * Returns the index of the argh that is at the
762 * given coordinates. If no argh is at the coordinates
763 * -1 is returned.
764 * @param int x The x coordinate.
765 * @param int y The y coordinate.
766 * @return int -1 <= value < argh_count <= MAX_ARGH
768 static int argh_collision(int x, int y) {
769 int i = 0;
770 int retVal = -1;
772 /* search for the argh that has the specified coords */
773 for (i = 0; i < argh_count; i++) {
774 if (specific_argh_collision(i, x, y)) {
775 retVal = i;
776 break;
779 return retVal;
783 * Checks wether the worm collides with the food at the specfied food-arrays.
784 * @param int foodIndex The index of the food in the arrays. Ensure the value is
785 * 0 <= foodIndex <= MAX_FOOD
786 * @return Returns true if the worm collides with the specified food.
788 static bool worm_food_collision(struct worm *w, int foodIndex)
790 bool retVal = false;
792 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
793 food_size - 1, food_size - 1);
795 return retVal;
799 * Returns true if the worm hits the argh within the next moves (unless
800 * the worm changes it's direction).
801 * @param struct worm *w - The worm to investigate
802 * @param int argh_idx - The index of the argh
803 * @param int moves - The number of moves that are considered.
804 * @return Returns false if the specified argh is not hit within the next
805 * moves.
807 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){
808 bool retVal = false;
809 int x1, y1, x2, y2;
810 x1 = w->x[w->head];
811 y1 = w->y[w->head];
813 x2 = w->x[w->head] + moves * w->dirx;
814 y2 = w->y[w->head] + moves * w->diry;
816 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
817 argh_size, argh_size);
818 return retVal;
822 * Checks wether the worm collides with the argh at the specfied argh-arrays.
823 * @param int arghIndex The index of the argh in the arrays.
824 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
825 * @return Returns true if the worm collides with the specified argh.
827 static bool worm_argh_collision(struct worm *w, int arghIndex)
829 bool retVal = false;
831 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
832 argh_size - 1, argh_size - 1);
834 return retVal;
838 * Find new coordinates for the food stored in foodx[index], foody[index]
839 * that don't collide with any other food or argh
840 * @param int index
841 * Ensure that 0 <= index < MAX_FOOD.
843 static void make_food(int index) {
845 int x = 0;
846 int y = 0;
847 bool collisionDetected = false;
848 int i;
850 do {
851 /* make coordinates for a new food so that
852 the entire food lies within the FIELD */
853 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
854 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
856 /* Ensure that the new food doesn't collide with any
857 existing foods or arghs.
858 If one or more corners of the new food hit any existing
859 argh or food a collision is detected.
861 collisionDetected =
862 food_collision(x , y ) >= 0 ||
863 food_collision(x , y + food_size - 1) >= 0 ||
864 food_collision(x + food_size - 1, y ) >= 0 ||
865 food_collision(x + food_size - 1, y + food_size - 1) >= 0 ||
866 argh_collision(x , y ) >= 0 ||
867 argh_collision(x , y + food_size - 1) >= 0 ||
868 argh_collision(x + food_size - 1, y ) >= 0 ||
869 argh_collision(x + food_size - 1, y + food_size - 1) >= 0;
871 /* use coordinates for further testing */
872 foodx[index] = x;
873 foody[index] = y;
875 /* now test wether we accidently hit the worm with food ;) */
876 i = 0;
877 for (i = 0; i < worm_count && !collisionDetected; i++) {
878 collisionDetected |= worm_food_collision(&worms[i], index);
881 while (collisionDetected);
882 return;
886 * Clears a food from the lcd buffer.
887 * @param int index The index of the food arrays under which
888 * the coordinates of the desired food can be found. Ensure
889 * that the value is 0 <= index <= MAX_FOOD.
891 static void clear_food(int index)
893 /* remove the old food from the screen */
894 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
895 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
896 foody[index] + FIELD_RECT_Y,
897 food_size, food_size);
898 rb->lcd_set_drawmode(DRMODE_SOLID);
902 * Draws a food in the lcd buffer.
903 * @param int index The index of the food arrays under which
904 * the coordinates of the desired food can be found. Ensure
905 * that the value is 0 <= index <= MAX_FOOD.
907 static void draw_food(int index)
909 /* draw the food object */
910 #ifdef HAVE_LCD_COLOR
911 rb->lcd_set_foreground(COLOR_FOOD);
912 #endif
913 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
914 foody[index] + FIELD_RECT_Y,
915 food_size, food_size);
916 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
917 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
918 foody[index] + FIELD_RECT_Y + 1,
919 food_size - 2, food_size - 2);
920 rb->lcd_set_drawmode(DRMODE_SOLID);
921 #ifdef HAVE_LCD_COLOR
922 rb->lcd_set_foreground(COLOR_FG);
923 #endif
927 * Find new coordinates for the argh stored in arghx[index], arghy[index]
928 * that don't collide with any other food or argh.
929 * @param int index
930 * Ensure that 0 <= index < argh_count < MAX_ARGH.
932 static void make_argh(int index)
934 int x = -1;
935 int y = -1;
936 bool collisionDetected = false;
937 int i;
939 do {
940 /* make coordinates for a new argh so that
941 the entire food lies within the FIELD */
942 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
943 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
945 /* Ensure that the new argh doesn't intersect with any
946 existing foods or arghs.
947 If one or more corners of the new argh hit any existing
948 argh or food an intersection is detected.
950 collisionDetected =
951 food_collision(x , y ) >= 0 ||
952 food_collision(x , y + argh_size - 1) >= 0 ||
953 food_collision(x + argh_size - 1, y ) >= 0 ||
954 food_collision(x + argh_size - 1, y + argh_size - 1) >= 0 ||
955 argh_collision(x , y ) >= 0 ||
956 argh_collision(x , y + argh_size - 1) >= 0 ||
957 argh_collision(x + argh_size - 1, y ) >= 0 ||
958 argh_collision(x + argh_size - 1, y + argh_size - 1) >= 0;
960 /* use the candidate coordinates to make a real argh */
961 arghx[index] = x;
962 arghy[index] = y;
964 /* now test wether we accidently hit the worm with argh ;) */
965 for (i = 0; i < worm_count && !collisionDetected; i++) {
966 collisionDetected |= worm_argh_collision(&worms[i], index);
967 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
968 MIN_ARGH_DIST);
971 while (collisionDetected);
972 return;
976 * Draws an argh in the lcd buffer.
977 * @param int index The index of the argh arrays under which
978 * the coordinates of the desired argh can be found. Ensure
979 * that the value is 0 <= index < argh_count <= MAX_ARGH.
981 static void draw_argh(int index)
983 /* draw the new argh */
984 #ifdef HAVE_LCD_COLOR
985 rb->lcd_set_foreground(COLOR_ARGH);
986 #endif
987 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
988 arghy[index] + FIELD_RECT_Y,
989 argh_size, argh_size);
990 #ifdef HAVE_LCD_COLOR
991 rb->lcd_set_foreground(COLOR_FG);
992 #endif
995 static void virtual_player(struct worm *w);
997 * Initialzes the specified worm with INITIAL_WORM_LENGTH
998 * and the tail at the specified position. The worm will
999 * be initialized alive and creeping EAST.
1000 * @param struct worm *w The worm that is to be initialized
1001 * @param int x The x coordinate at which the tail of the worm starts.
1002 * x must be 0 <= x < FIELD_RECT_WIDTH.
1003 * @param int y The y coordinate at which the tail of the worm starts
1004 * y must be 0 <= y < FIELD_RECT_WIDTH.
1006 static void init_worm(struct worm *w, int x, int y){
1007 /* initialize the worm size */
1008 w->head = 1;
1009 w->tail = 0;
1011 w->x[w->head] = x + 1;
1012 w->y[w->head] = y;
1014 w->x[w->tail] = x;
1015 w->y[w->tail] = y;
1017 /* set the initial direction the worm creeps to */
1018 w->dirx = 1;
1019 w->diry = 0;
1021 w->growing = INITIAL_WORM_LENGTH - 1;
1022 w->alive = true;
1023 w->fetch_worm_direction = virtual_player;
1027 * Writes the direction that was stored for
1028 * human player 1 into the specified worm. This function
1029 * may be used to be stored in worm.fetch_worm_direction.
1030 * The value of
1031 * the direction is read from player1_dir.
1032 * @param struct worm *w - The worm of which the direction
1033 * is altered.
1035 static void human_player1(struct worm *w) {
1036 set_worm_dir(w, player1_dir);
1040 * Writes the direction that was stored for
1041 * human player 2 into the specified worm. This function
1042 * may be used to be stored in worm.fetch_worm_direction.
1043 * The value of
1044 * the direction is read from player2_dir.
1045 * @param struct worm *w - The worm of which the direction
1046 * is altered.
1048 static void human_player2(struct worm *w) {
1049 set_worm_dir(w, player2_dir);
1053 * Writes the direction that was stored for
1054 * human player using a remote control
1055 * into the specified worm. This function
1056 * may be used to be stored in worm.fetch_worm_direction.
1057 * The value of
1058 * the direction is read from player3_dir.
1059 * @param struct worm *w - The worm of which the direction
1060 * is altered.
1062 static void remote_player(struct worm *w) {
1063 set_worm_dir(w, player3_dir);
1067 * Initializes the worm-, food- and argh-arrays, draws a frame,
1068 * makes some food and argh and display all that stuff.
1070 static void init_wormlet(void)
1072 int i;
1074 for (i = 0; i< worm_count; i++) {
1075 /* Initialize all the worm coordinates to center. */
1076 int x = (int)(FIELD_RECT_WIDTH / 2);
1077 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
1079 init_worm(&worms[i], x, y);
1082 player1_dir = EAST;
1083 player2_dir = EAST;
1084 player3_dir = EAST;
1086 if (players > 0) {
1087 worms[0].fetch_worm_direction = human_player1;
1090 if (players > 1) {
1091 if (use_remote) {
1092 worms[1].fetch_worm_direction = remote_player;
1093 } else {
1094 worms[1].fetch_worm_direction = human_player2;
1098 if (players > 2) {
1099 worms[2].fetch_worm_direction = human_player2;
1102 /* Needed when the game is restarted using BTN_STOPRESET */
1103 rb->lcd_clear_display();
1105 /* make and display some food and argh */
1106 argh_count = MAX_FOOD;
1107 for (i = 0; i < MAX_FOOD; i++) {
1108 make_food(i);
1109 draw_food(i);
1110 make_argh(i);
1111 draw_argh(i);
1114 /* draw the game field */
1115 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1116 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
1117 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
1118 rb->lcd_set_drawmode(DRMODE_SOLID);
1120 /* make everything visible */
1121 rb->lcd_update();
1126 * Move the worm one step further if it is alive.
1127 * The direction in which the worm moves is taken from dirx and diry.
1128 * move_worm decreases growing if > 0. While the worm is growing the tail
1129 * is left untouched.
1130 * @param struct worm *w The worm to move. w must not be NULL.
1132 static void move_worm(struct worm *w)
1134 if (w->alive) {
1135 /* determine the head point and its precessor */
1136 int headx = w->x[w->head];
1137 int heady = w->y[w->head];
1138 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
1139 int preheadx = w->x[prehead];
1140 int preheady = w->y[prehead];
1142 /* determine the old direction */
1143 int olddirx;
1144 int olddiry;
1145 if (headx == preheadx) {
1146 olddirx = 0;
1147 olddiry = (heady > preheady) ? 1 : -1;
1148 } else {
1149 olddiry = 0;
1150 olddirx = (headx > preheadx) ? 1 : -1;
1153 /* olddir == dir?
1154 a change of direction means a new segment
1155 has been opened */
1156 if (olddirx != w->dirx ||
1157 olddiry != w->diry) {
1158 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1161 /* new head position */
1162 w->x[w->head] = headx + w->dirx;
1163 w->y[w->head] = heady + w->diry;
1166 /* while the worm is growing no tail procession is necessary */
1167 if (w->growing > 0) {
1168 /* update the worms grow state */
1169 w->growing--;
1172 /* if the worm isn't growing the tail has to be dragged */
1173 else {
1174 /* index of the end of the tail segment */
1175 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1177 /* drag the end of the tail */
1178 /* only one coordinate has to be altered. Here it is
1179 determined which one */
1180 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1181 if (w->x[w->tail] == w->x[tail_segment_end]) {
1182 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1183 w->y[w->tail] += dir;
1184 } else {
1185 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1186 w->x[w->tail] += dir;
1189 /* when the tail has been dragged so far that it meets
1190 the next segment start the tail segment is obsolete and
1191 must be freed */
1192 if (w->x[w->tail] == w->x[tail_segment_end] &&
1193 w->y[w->tail] == w->y[tail_segment_end]){
1195 /* drop the last tail point */
1196 w->tail = tail_segment_end;
1203 * Draws the head and clears the tail of the worm in
1204 * the display buffer. lcd_update() is NOT called thus
1205 * the caller has to take care that the buffer is displayed.
1207 static void draw_worm(struct worm *w)
1209 #ifdef HAVE_LCD_COLOR
1210 rb->lcd_set_foreground(COLOR_WORM);
1211 #endif
1212 /* draw the new head */
1213 int x = w->x[w->head];
1214 int y = w->y[w->head];
1215 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1216 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1219 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1221 /* clear the space behind the worm */
1222 x = w->x[w->tail] ;
1223 y = w->y[w->tail] ;
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);
1227 rb->lcd_set_drawmode(DRMODE_SOLID);
1228 #ifdef HAVE_LCD_COLOR
1229 rb->lcd_set_foreground(COLOR_FG);
1230 #endif
1234 * Checks wether the coordinate is part of the worm. Returns
1235 * true if any part of the worm was hit - including the head.
1236 * @param x int The x coordinate
1237 * @param y int The y coordinate
1238 * @return int The index of the worm arrays that contain x, y.
1239 * Returns -1 if the coordinates are not part of the worm.
1241 static int specific_worm_collision(struct worm *w, int x, int y)
1243 int retVal = -1;
1245 /* get_worm_array_length is expensive -> buffer the value */
1246 int wormLength = get_worm_array_length(w);
1247 int i;
1249 /* test each entry that is part of the worm */
1250 for (i = 0; i < wormLength && retVal == -1; i++) {
1252 /* The iteration iterates the length of the worm.
1253 Here's the conversion to the true indices within the worm arrays. */
1254 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1255 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1256 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1257 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1258 if (samex || samey){
1259 int test, min, max, tmp;
1261 if (samey) {
1262 min = w->x[linestart];
1263 max = w->x[lineend];
1264 test = x;
1265 } else {
1266 min = w->y[linestart];
1267 max = w->y[lineend];
1268 test = y;
1271 tmp = min;
1272 min = MIN(min, max);
1273 max = MAX(tmp, max);
1275 if (min <= test && test <= max) {
1276 retVal = lineend;
1280 return retVal;
1284 * Increases the length of the specified worm by marking
1285 * that it may grow by len pixels. Note that the worm has
1286 * to move to make the growing happen.
1287 * @param worm *w The worm that is to be altered.
1288 * @param int len A positive value specifying the amount of
1289 * pixels the worm may grow.
1291 static void add_growing(struct worm *w, int len) {
1292 w->growing += len;
1296 * Determins the worm that is at the coordinates x, y. The parameter
1297 * w is a switch parameter that changes the functionality of worm_collision.
1298 * If w is specified and x,y hits the head of w NULL is returned.
1299 * This is a useful way to determine wether the head of w hits
1300 * any worm but including itself but excluding its own head.
1301 * (It hits always its own head ;))
1302 * If w is set to NULL worm_collision returns any worm including all heads
1303 * that is at position of x,y.
1304 * @param struct worm *w The worm of which the head should be excluded in
1305 * the test. w may be set to NULL.
1306 * @param int x The x coordinate that is checked
1307 * @param int y The y coordinate that is checkec
1308 * @return struct worm* The worm that has been hit by x,y. If no worm
1309 * was at the position NULL is returned.
1311 static struct worm* worm_collision(struct worm *w, int x, int y){
1312 struct worm *retVal = NULL;
1313 int i;
1314 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1315 int collision_at = specific_worm_collision(&worms[i], x, y);
1316 if (collision_at != -1) {
1317 if (!(w == &worms[i] && collision_at == w->head)){
1318 retVal = &worms[i];
1322 return retVal;
1326 * Returns true if the head of the worm just has
1327 * crossed the field boundaries.
1328 * @return bool true if the worm just has wrapped.
1330 static bool field_collision(struct worm *w)
1332 bool retVal = false;
1333 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1334 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1335 (w->x[w->head] < 0) ||
1336 (w->y[w->head] < 0))
1338 retVal = true;
1340 return retVal;
1345 * Returns true if the specified coordinates are within the
1346 * field specified by the FIELD_RECT_XXX constants.
1347 * @param int x The x coordinate of the point that is investigated
1348 * @param int y The y coordinate of the point that is investigated
1349 * @return bool Returns false if x,y specifies a point outside the
1350 * field of worms.
1352 static bool is_in_field_rect(int x, int y) {
1353 bool retVal = false;
1354 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1355 y >= 0 && y < FIELD_RECT_HEIGHT);
1356 return retVal;
1360 * Checks and returns wether the head of the w
1361 * is colliding with something currently.
1362 * @return int One of the values:
1363 * COLLISION_NONE
1364 * COLLISION_w
1365 * COLLISION_FOOD
1366 * COLLISION_ARGH
1367 * COLLISION_FIELD
1369 static int check_collision(struct worm *w)
1371 int retVal = COLLISION_NONE;
1373 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1374 retVal = COLLISION_WORM;
1376 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1377 retVal = COLLISION_FOOD;
1379 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1380 retVal = COLLISION_ARGH;
1382 if (field_collision(w))
1383 retVal = COLLISION_FIELD;
1385 return retVal;
1389 * Returns the index of the food that is closest to the point
1390 * specified by x, y. This index may be used in the foodx and
1391 * foody arrays.
1392 * @param int x The x coordinate of the point
1393 * @param int y The y coordinate of the point
1394 * @return int A value usable as index in foodx and foody.
1396 static int get_nearest_food(int x, int y){
1397 int nearestfood = 0;
1398 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1399 int deltax = 0;
1400 int deltay = 0;
1401 int foodindex;
1402 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1403 int distance;
1404 deltax = foodx[foodindex] - x;
1405 deltay = foody[foodindex] - y;
1406 deltax = deltax > 0 ? deltax : deltax * (-1);
1407 deltay = deltay > 0 ? deltay : deltay * (-1);
1408 distance = deltax + deltay;
1410 if (distance < olddistance) {
1411 olddistance = distance;
1412 nearestfood = foodindex;
1415 return nearestfood;
1419 * Returns wether the specified position is next to the worm
1420 * and in the direction the worm looks. Use this method to
1421 * test wether this position would be hit with the next move of
1422 * the worm unless the worm changes its direction.
1423 * @param struct worm *w - The worm to be investigated
1424 * @param int x - The x coordinate of the position to test.
1425 * @param int y - The y coordinate of the position to test.
1426 * @return Returns true if the worm will hit the position unless
1427 * it change its direction before the next move.
1429 static bool is_in_front_of_worm(struct worm *w, int x, int y) {
1430 bool infront = false;
1431 int deltax = x - w->x[w->head];
1432 int deltay = y - w->y[w->head];
1434 if (w->dirx == 0) {
1435 infront = (w->diry * deltay) > 0;
1436 } else {
1437 infront = (w->dirx * deltax) > 0;
1439 return infront;
1443 * Returns true if the worm will collide with the next move unless
1444 * it changes its direction.
1445 * @param struct worm *w - The worm to be investigated.
1446 * @return Returns true if the worm will collide with the next move
1447 * unless it changes its direction.
1449 static bool will_worm_collide(struct worm *w) {
1450 int x = w->x[w->head] + w->dirx;
1451 int y = w->y[w->head] + w->diry;
1452 bool retVal = !is_in_field_rect(x, y);
1453 if (!retVal) {
1454 retVal = (argh_collision(x, y) != -1);
1457 if (!retVal) {
1458 retVal = (worm_collision(w, x, y) != NULL);
1460 return retVal;
1464 * This function
1465 * may be used to be stored in worm.fetch_worm_direction for
1466 * worms that are not controlled by humans but by artificial stupidity.
1467 * A direction is searched that doesn't lead to collision but to the nearest
1468 * food - but not very intelligent. The direction is written to the specified
1469 * worm.
1470 * @param struct worm *w - The worm of which the direction
1471 * is altered.
1473 static void virtual_player(struct worm *w) {
1474 bool isright;
1475 int plana, planb, planc;
1476 /* find the next lunch */
1477 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1479 /* determine in which direction it is */
1481 /* in front of me? */
1482 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1484 /* left right of me? */
1485 int olddir = get_worm_dir(w);
1486 set_worm_dir(w, (olddir + 1) % 4);
1487 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1488 set_worm_dir(w, olddir);
1490 /* detect situation, set strategy */
1491 if (infront) {
1492 if (isright) {
1493 plana = olddir;
1494 planb = (olddir + 1) % 4;
1495 planc = (olddir + 3) % 4;
1496 } else {
1497 plana = olddir;
1498 planb = (olddir + 3) % 4;
1499 planc = (olddir + 1) % 4;
1501 } else {
1502 if (isright) {
1503 plana = (olddir + 1) % 4;
1504 planb = olddir;
1505 planc = (olddir + 3) % 4;
1506 } else {
1507 plana = (olddir + 3) % 4;
1508 planb = olddir;
1509 planc = (olddir + 1) % 4;
1513 /* test for collision */
1514 set_worm_dir(w, plana);
1515 if (will_worm_collide(w)){
1517 /* plan b */
1518 set_worm_dir(w, planb);
1520 /* test for collision */
1521 if (will_worm_collide(w)) {
1523 /* plan c */
1524 set_worm_dir(w, planc);
1530 * prints out the score board with all the status information
1531 * about the game.
1533 static void score_board(void)
1535 char buf[15];
1536 char* buf2 = NULL;
1537 int i;
1538 int y = 0;
1539 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1540 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1541 rb->lcd_set_drawmode(DRMODE_SOLID);
1542 for (i = 0; i < worm_count; i++) {
1543 int score = get_score(&worms[i]);
1545 /* high score */
1546 if (worms[i].fetch_worm_direction != virtual_player){
1547 if (highscore < score) {
1548 highscore = score;
1552 /* length */
1553 rb->snprintf(buf, sizeof (buf),"Len:%d", score);
1555 /* worm state */
1556 switch (check_collision(&worms[i])) {
1557 case COLLISION_NONE:
1558 if (worms[i].growing > 0)
1559 buf2 = "Growing";
1560 else {
1561 if (worms[i].alive)
1562 buf2 = "Hungry";
1563 else
1564 buf2 = "Wormed";
1566 break;
1568 case COLLISION_WORM:
1569 buf2 = "Wormed";
1570 break;
1572 case COLLISION_FOOD:
1573 buf2 = "Growing";
1574 break;
1576 case COLLISION_ARGH:
1577 buf2 = "Argh";
1578 break;
1580 case COLLISION_FIELD:
1581 buf2 = "Crashed";
1582 break;
1584 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf);
1585 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2);
1587 if (!worms[i].alive){
1588 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1589 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1590 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1591 rb->lcd_set_drawmode(DRMODE_SOLID);
1593 y += 19;
1595 rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore);
1596 #ifndef DEBUG_WORMLET
1597 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);
1598 #else
1599 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
1600 #endif
1604 * Checks for collisions of the worm and its environment and
1605 * takes appropriate actions like growing the worm or killing it.
1606 * @return bool Returns true if the worm is dead. Returns
1607 * false if the worm is healthy, up and creeping.
1609 static bool process_collisions(struct worm *w)
1611 int index = -1;
1613 w->alive &= !field_collision(w);
1615 if (w->alive) {
1617 /* check if food was eaten */
1618 index = food_collision(w->x[w->head], w->y[w->head]);
1619 if (index != -1){
1620 int i;
1622 clear_food(index);
1623 make_food(index);
1624 draw_food(index);
1626 for (i = 0; i < arghs_per_food; i++) {
1627 argh_count++;
1628 if (argh_count > MAX_ARGH)
1629 argh_count = MAX_ARGH;
1630 make_argh(argh_count - 1);
1631 draw_argh(argh_count - 1);
1634 add_growing(w, worm_food);
1636 draw_worm(w);
1639 /* check if argh was eaten */
1640 else {
1641 index = argh_collision(w->x[w->head], w->y[w->head]);
1642 if (index != -1) {
1643 w->alive = false;
1645 else {
1646 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1647 w->alive = false;
1652 return !w->alive;
1656 * The main loop of the game.
1657 * @return bool Returns true if the game ended
1658 * with a dead worm. Returns false if the user
1659 * aborted the game manually.
1661 static int run(void)
1663 int button = 0;
1664 int wormDead = false;
1665 bool paused = false;
1667 /* ticks are counted to compensate speed variations */
1668 long cycle_start = 0, cycle_end = 0;
1669 #ifdef DEBUG_WORMLET
1670 int ticks_to_max_cycle_reset = 20;
1671 long max_cycle = 0;
1672 char buf[20];
1673 #endif
1675 /* initialize the board and so on */
1676 init_wormlet();
1678 cycle_start = *rb->current_tick;
1679 /* change the direction of the worm */
1680 while (!wormDead)
1682 int i;
1683 long cycle_duration=0;
1685 #ifdef HAS_BUTTON_HOLD
1686 if (rb->button_hold())
1687 paused = true;
1688 #endif
1690 switch (button) {
1691 case BTN_STARTPAUSE:
1692 paused = !paused;
1693 break;
1694 case BTN_STOPRESET:
1695 if (paused)
1696 return 1; /* restart game */
1697 else
1698 paused = true;
1699 break;
1700 #ifdef BTN_RC_QUIT
1701 case BTN_RC_QUIT:
1702 #endif
1703 case BTN_QUIT:
1704 return 2; /* back to menu */
1705 break;
1707 if (!paused)
1709 switch (button) {
1710 case BTN_DIR_UP:
1711 if (players == 1 && !use_remote) {
1712 player1_dir = NORTH;
1714 break;
1716 case BTN_DIR_DOWN:
1717 if (players == 1 && !use_remote) {
1718 player1_dir = SOUTH;
1720 break;
1722 case BTN_DIR_LEFT:
1723 if (players != 1 || use_remote) {
1724 player1_dir = (player1_dir + 3) % 4;
1725 } else {
1726 player1_dir = WEST;
1728 break;
1730 case BTN_DIR_RIGHT:
1731 if (players != 1 || use_remote) {
1732 player1_dir = (player1_dir + 1) % 4;
1733 } else {
1734 player1_dir = EAST;
1736 break;
1738 #ifdef MULTIPLAYER
1739 case BTN_PLAYER2_DIR1:
1740 player2_dir = (player2_dir + 3) % 4;
1741 break;
1743 case BTN_PLAYER2_DIR2:
1744 player2_dir = (player2_dir + 1) % 4;
1745 break;
1746 #endif
1748 #ifdef REMOTE
1749 case BTN_RC_UP:
1750 player3_dir = (player3_dir + 1) % 4;
1751 break;
1753 case BTN_RC_DOWN:
1754 player3_dir = (player3_dir + 3) % 4;
1755 break;
1756 #endif
1760 for (i = 0; i < worm_count; i++) {
1761 worms[i].fetch_worm_direction(&worms[i]);
1764 wormDead = true;
1765 for (i = 0; i < worm_count; i++){
1766 struct worm *w = &worms[i];
1767 move_worm(w);
1768 wormDead &= process_collisions(w);
1769 draw_worm(w);
1771 score_board();
1772 rb->lcd_update();
1773 if (button == BTN_STOPRESET) {
1774 wormDead = true;
1777 /* here the wormlet game cycle ends
1778 thus the current tick is stored
1779 as end time */
1780 cycle_end = *rb->current_tick;
1782 /* The duration of the game cycle */
1783 cycle_duration = cycle_end - cycle_start;
1784 cycle_duration = MAX(0, cycle_duration);
1785 cycle_duration = MIN(speed -1, cycle_duration);
1788 #ifdef DEBUG_WORMLET
1789 ticks_to_max_cycle_reset--;
1790 if (ticks_to_max_cycle_reset <= 0) {
1791 max_cycle = 0;
1794 if (max_cycle < cycle_duration) {
1795 max_cycle = cycle_duration;
1796 ticks_to_max_cycle_reset = 20;
1798 rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle);
1799 set_debug_out(buf);
1800 #endif
1802 /* adjust the number of ticks to wait for a button.
1803 This ensures that a complete game cycle including
1804 user input runs in constant time */
1805 button = rb->button_get_w_tmo(speed - cycle_duration);
1806 cycle_start = *rb->current_tick;
1809 rb->splash(HZ*2, "Game Over!");
1811 return 2; /* back to menu */
1814 #ifdef DEBUG_WORMLET
1817 * Just a test routine that checks that worm_food_collision works
1818 * in some typical situations.
1820 static void test_worm_food_collision(void) {
1821 int collision_count = 0;
1822 int i;
1823 rb->lcd_clear_display();
1824 init_worm(&worms[0], 10, 10);
1825 add_growing(&worms[0], 10);
1826 set_worm_dir(&worms[0], EAST);
1827 for (i = 0; i < 10; i++) {
1828 move_worm(&worms[0]);
1829 draw_worm(&worms[0]);
1832 set_worm_dir(&worms[0], SOUTH);
1833 for (i = 0; i < 10; i++) {
1834 move_worm(&worms[0]);
1835 draw_worm(&worms[0]);
1838 foodx[0] = 15;
1839 foody[0] = 12;
1840 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1841 char buf[20];
1842 bool collision;
1843 draw_worm(&worms[0]);
1844 draw_food(0);
1845 collision = worm_food_collision(&worms[0], 0);
1846 if (collision) {
1847 collision_count++;
1849 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1850 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1851 rb->lcd_update();
1853 if (collision_count != food_size) {
1854 rb->button_get(true);
1858 foody[0] = 15;
1859 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1860 char buf[20];
1861 bool collision;
1862 draw_worm(&worms[0]);
1863 draw_food(0);
1864 collision = worm_food_collision(&worms[0], 0);
1865 if (collision) {
1866 collision_count ++;
1868 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1869 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1870 rb->lcd_update();
1872 if (collision_count != food_size * 2) {
1873 rb->button_get(true);
1878 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){
1879 int x, y;
1880 bool retVal = false;
1881 for (x = rx; x < rx + rw; x++){
1882 for (y = ry; y < ry + rh; y++) {
1883 if (specific_worm_collision(w, x, y) != -1) {
1884 retVal = true;
1888 return retVal;
1891 static void test_worm_argh_collision(void) {
1892 int i;
1893 int dir;
1894 int collision_count = 0;
1895 rb->lcd_clear_display();
1896 init_worm(&worms[0], 10, 10);
1897 add_growing(&worms[0], 40);
1898 for (dir = 0; dir < 4; dir++) {
1899 set_worm_dir(&worms[0], (EAST + dir) % 4);
1900 for (i = 0; i < 10; i++) {
1901 move_worm(&worms[0]);
1902 draw_worm(&worms[0]);
1906 arghx[0] = 12;
1907 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1908 char buf[20];
1909 bool collision;
1910 draw_argh(0);
1911 collision = worm_argh_collision(&worms[0], 0);
1912 if (collision) {
1913 collision_count ++;
1915 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1916 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1917 rb->lcd_update();
1919 if (collision_count != argh_size * 2) {
1920 rb->button_get(true);
1923 arghy[0] = 12;
1924 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1925 char buf[20];
1926 bool collision;
1927 draw_argh(0);
1928 collision = worm_argh_collision(&worms[0], 0);
1929 if (collision) {
1930 collision_count ++;
1932 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1933 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1934 rb->lcd_update();
1936 if (collision_count != argh_size * 4) {
1937 rb->button_get(true);
1941 static int testline_in_rect(void) {
1942 int testfailed = -1;
1944 int rx = 10;
1945 int ry = 15;
1946 int rw = 20;
1947 int rh = 25;
1949 /* Test 1 */
1950 int x1 = 12;
1951 int y1 = 8;
1952 int x2 = 12;
1953 int y2 = 42;
1955 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1956 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1957 rb->lcd_drawrect(rx, ry, rw, rh);
1958 rb->lcd_drawline(x1, y1, x2, y2);
1959 rb->lcd_update();
1960 rb->lcd_putsxy(0, 0, "failed 1");
1961 rb->button_get(true);
1962 testfailed = 1;
1965 /* test 2 */
1966 y2 = 20;
1967 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1968 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1969 rb->lcd_drawrect(rx, ry, rw, rh);
1970 rb->lcd_drawline(x1, y1, x2, y2);
1971 rb->lcd_putsxy(0, 0, "failed 2");
1972 rb->lcd_update();
1973 rb->button_get(true);
1974 testfailed = 2;
1977 /* test 3 */
1978 y1 = 30;
1979 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1980 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1981 rb->lcd_drawrect(rx, ry, rw, rh);
1982 rb->lcd_drawline(x1, y1, x2, y2);
1983 rb->lcd_putsxy(0, 0, "failed 3");
1984 rb->lcd_update();
1985 rb->button_get(true);
1986 testfailed = 3;
1989 /* test 4 */
1990 y2 = 45;
1991 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1992 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1993 rb->lcd_drawrect(rx, ry, rw, rh);
1994 rb->lcd_drawline(x1, y1, x2, y2);
1995 rb->lcd_putsxy(0, 0, "failed 4");
1996 rb->lcd_update();
1997 rb->button_get(true);
1998 testfailed = 4;
2001 /* test 5 */
2002 y1 = 50;
2003 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2004 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2005 rb->lcd_drawrect(rx, ry, rw, rh);
2006 rb->lcd_drawline(x1, y1, x2, y2);
2007 rb->lcd_putsxy(0, 0, "failed 5");
2008 rb->lcd_update();
2009 rb->button_get(true);
2010 testfailed = 5;
2013 /* test 6 */
2014 y1 = 5;
2015 y2 = 7;
2016 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2017 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2018 rb->lcd_drawrect(rx, ry, rw, rh);
2019 rb->lcd_drawline(x1, y1, x2, y2);
2020 rb->lcd_putsxy(0, 0, "failed 6");
2021 rb->lcd_update();
2022 rb->button_get(true);
2023 testfailed = 6;
2026 /* test 7 */
2027 x1 = 8;
2028 y1 = 20;
2029 x2 = 35;
2030 y2 = 20;
2031 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2032 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2033 rb->lcd_drawrect(rx, ry, rw, rh);
2034 rb->lcd_drawline(x1, y1, x2, y2);
2035 rb->lcd_putsxy(0, 0, "failed 7");
2036 rb->lcd_update();
2037 rb->button_get(true);
2038 testfailed = 7;
2041 /* test 8 */
2042 x2 = 12;
2043 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2044 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2045 rb->lcd_drawrect(rx, ry, rw, rh);
2046 rb->lcd_drawline(x1, y1, x2, y2);
2047 rb->lcd_putsxy(0, 0, "failed 8");
2048 rb->lcd_update();
2049 rb->button_get(true);
2050 testfailed = 8;
2053 /* test 9 */
2054 x1 = 25;
2055 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2056 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2057 rb->lcd_drawrect(rx, ry, rw, rh);
2058 rb->lcd_drawline(x1, y1, x2, y2);
2059 rb->lcd_putsxy(0, 0, "failed 9");
2060 rb->lcd_update();
2061 rb->button_get(true);
2062 testfailed = 9;
2065 /* test 10 */
2066 x2 = 37;
2067 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2068 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2069 rb->lcd_drawrect(rx, ry, rw, rh);
2070 rb->lcd_drawline(x1, y1, x2, y2);
2071 rb->lcd_putsxy(0, 0, "failed 10");
2072 rb->lcd_update();
2073 rb->button_get(true);
2074 testfailed = 10;
2077 /* test 11 */
2078 x1 = 42;
2079 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2080 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2081 rb->lcd_drawrect(rx, ry, rw, rh);
2082 rb->lcd_drawline(x1, y1, x2, y2);
2083 rb->lcd_putsxy(0, 0, "failed 11");
2084 rb->lcd_update();
2085 rb->button_get(true);
2086 testfailed = 11;
2089 /* test 12 */
2090 x1 = 5;
2091 x2 = 7;
2092 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2093 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2094 rb->lcd_drawrect(rx, ry, rw, rh);
2095 rb->lcd_drawline(x1, y1, x2, y2);
2096 rb->lcd_putsxy(0, 0, "failed 12");
2097 rb->lcd_update();
2098 rb->button_get(true);
2099 testfailed = 12;
2102 /* test 13 */
2103 rx = 9;
2104 ry = 15;
2105 rw = food_size;
2106 rh = food_size;
2108 x1 = 10;
2109 y1 = 10;
2110 x2 = 10;
2111 y2 = 20;
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 13");
2117 rb->lcd_update();
2118 rb->button_get(true);
2119 testfailed = 13;
2122 /* test 14 */
2123 rx = 9;
2124 ry = 15;
2125 rw = 4;
2126 rh = 4;
2128 x1 = 10;
2129 y1 = 10;
2130 x2 = 10;
2131 y2 = 19;
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_drawline(x1, y1, x2, y2);
2135 rb->lcd_invertrect(rx, ry, rw, rh);
2136 rb->lcd_putsxy(0, 0, "failed 14");
2137 rb->lcd_update();
2138 rb->button_get(true);
2139 testfailed = 14;
2142 rb->lcd_clear_display();
2144 return testfailed;
2148 * Just a test routine to test wether specific_worm_collision might work properly
2150 static int test_specific_worm_collision(void) {
2151 int collisions = 0;
2152 int dir;
2153 int x = 0;
2154 int y = 0;
2155 char buf[20];
2156 rb->lcd_clear_display();
2157 init_worm(&worms[0], 10, 20);
2158 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2160 for (dir = EAST; dir < EAST + 4; dir++) {
2161 int i;
2162 set_worm_dir(&worms[0], dir % 4);
2163 for (i = 0; i < 5; i++) {
2164 if (!(dir % 4 == NORTH && i == 9)) {
2165 move_worm(&worms[0]);
2166 draw_worm(&worms[0]);
2171 for (y = 15; y < 30; y ++){
2172 for (x = 5; x < 20; x++) {
2173 if (specific_worm_collision(&worms[0], x, y) != -1) {
2174 collisions ++;
2176 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2177 rb->snprintf(buf, sizeof buf, "collisions %d", collisions);
2178 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2179 rb->lcd_update();
2182 if (collisions != 21) {
2183 rb->button_get(true);
2185 return collisions;
2188 static void test_make_argh(void){
2189 int dir;
2190 int seed = 0;
2191 int hit = 0;
2192 int failures = 0;
2193 int last_failures = 0;
2194 int i, worm_idx;
2195 rb->lcd_clear_display();
2196 worm_count = 3;
2198 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2199 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2200 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2203 for (dir = EAST; dir < EAST + 4; dir++) {
2204 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2205 set_worm_dir(&worms[worm_idx], dir % 4);
2206 for (i = 0; i < 10; i++) {
2207 if (!(dir % 4 == NORTH && i == 9)) {
2208 move_worm(&worms[worm_idx]);
2209 draw_worm(&worms[worm_idx]);
2215 rb->lcd_update();
2217 for (seed = 0; hit < 20; seed += 2) {
2218 char buf[20];
2219 int x, y;
2220 rb->srand(seed);
2221 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2222 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2224 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2225 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2226 int tries = 0;
2227 rb->srand(seed);
2229 tries = make_argh(0);
2230 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2231 failures ++;
2234 rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d", x, y, failures, tries);
2235 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2236 rb->lcd_update();
2237 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, argh_size, argh_size);
2238 rb->lcd_update();
2239 draw_argh(0);
2240 rb->lcd_update();
2241 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, argh_size, argh_size);
2242 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, argh_size, argh_size);
2244 if (failures > last_failures) {
2245 rb->button_get(true);
2247 last_failures = failures;
2248 hit ++;
2254 static void test_worm_argh_collision_in_moves(void) {
2255 int hit_count = 0;
2256 int i;
2257 rb->lcd_clear_display();
2258 init_worm(&worms[0], 10, 20);
2260 arghx[0] = 20;
2261 arghy[0] = 18;
2262 draw_argh(0);
2264 set_worm_dir(&worms[0], EAST);
2265 for (i = 0; i < 20; i++) {
2266 char buf[20];
2267 move_worm(&worms[0]);
2268 draw_worm(&worms[0]);
2269 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2270 hit_count ++;
2272 rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
2273 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2274 rb->lcd_update();
2276 if (hit_count != argh_size + 5) {
2277 rb->button_get(true);
2280 #endif /* DEBUG_WORMLET */
2282 extern bool use_old_rect;
2285 * These are additional functions required to set various wormlet settings
2286 * and to enable saving of settings and high score
2290 Sets the total number of worms, both human and machine
2292 bool set_worm_num_worms(void)
2294 bool ret;
2295 static const struct opt_items num_worms_option[3] = {
2296 { "1", -1 },
2297 { "2", -1 },
2298 { "3", -1 },
2301 ret = rb->set_option("Number Of Worms", &worm_count,INT, num_worms_option, 3, NULL);
2302 if (worm_count < players) {
2303 worm_count=players;
2305 return ret;
2309 Sets the number of human players
2311 bool set_worm_num_players(void)
2313 bool ret;
2314 static const struct opt_items num_players_option[4] = {
2315 { "0", -1 },
2316 { "1", -1 },
2317 { "2", -1 },
2318 { "3", -1 }
2321 ret = rb->set_option("Number of Players", &players, INT,num_players_option , 4, NULL);
2322 if (players > worm_count) {
2323 worm_count = players;
2325 if (players > 2) {
2326 use_remote = true;
2328 return ret;
2332 Sets the size of each argh block created
2334 bool set_worm_argh_size(void)
2336 static const struct opt_items argh_size_option[11] = {
2337 { "0", -1 },
2338 { "1", -1 },
2339 { "2", -1 },
2340 { "3", -1 },
2341 { "4", -1 },
2342 { "5", -1 },
2343 { "6", -1 },
2344 { "7", -1 },
2345 { "8", -1 },
2346 { "9", -1 },
2347 { "10", -1 }
2350 return rb->set_option("Argh Size", &argh_size,INT,argh_size_option , 11, NULL);
2354 Sets the amount a worm grows per food
2356 bool set_worm_food(void)
2358 static const struct opt_items worm_food_option[11] = {
2359 { "0", -1 },
2360 { "1", -1 },
2361 { "2", -1 },
2362 { "3", -1 },
2363 { "4", -1 },
2364 { "5", -1 },
2365 { "6", -1 },
2366 { "7", -1 },
2367 { "8", -1 },
2368 { "9", -1 },
2369 { "10", -1 }
2371 return rb->set_option("Worm Growth Per Food", &worm_food,INT,worm_food_option , 11, NULL);
2375 Sets the number of arghs created per food
2377 bool set_argh_per_food(void)
2379 static const struct opt_items argh_food_option[5] = {
2380 { "0", -1 },
2381 { "1", -1 },
2382 { "2", -1 },
2383 { "3", -1 },
2384 { "4", -1 },
2386 return rb->set_option("Arghs Per Food", &arghs_per_food,INT,argh_food_option , 5, NULL);
2390 Sets the number of ticks per move
2392 bool set_worm_speed(void)
2394 bool ret;
2395 static const struct opt_items speed_option[19] = {
2396 { "0", -1 },
2397 { "1", -1 },
2398 { "2", -1 },
2399 { "3", -1 },
2400 { "4", -1 },
2401 { "5", -1 },
2402 { "6", -1 },
2403 { "7", -1 },
2404 { "8", -1 },
2405 { "9", -1 },
2406 { "10", -1 },
2407 { "11", -1 },
2408 { "12", -1 },
2409 { "13", -1 },
2410 { "14", -1 },
2411 { "15", -1 },
2412 { "16", -1 },
2413 { "17", -1 },
2414 { "18", -1 },
2418 speed = 20 - speed;
2419 ret = rb->set_option("Worm Speed", &speed,INT,speed_option , 19, NULL);
2420 speed = 20 - speed;
2421 return ret;
2425 Sets the control style, which depends on the number of players,
2426 remote control existing, etc
2427 bool set_bool_options(char* string, bool* variable,
2428 char* yes_str, char* no_str, void (*function)(bool))
2430 bool set_worm_control_style(void)
2432 static const struct opt_items key1_option[2] = {
2433 { "Remote Control", -1 },
2434 { "No Rem. Control", -1 },
2437 static const struct opt_items key2_option[2] = {
2438 { "2 Key Control", -1 },
2439 { "4 Key COntrol", -1 },
2442 static const struct opt_items key3_option[1] = {
2443 { "Out of Control", -1 },
2446 if (players > 1) {
2447 rb->set_option("Control Style",&use_remote,INT, key1_option, 2, NULL);
2448 } else {
2449 if (players > 0) {
2450 rb->set_option("Control Style",&use_remote,INT, key2_option, 2, NULL);
2452 else {
2453 rb->set_option("Control Style",&use_remote,INT, key3_option, 1, NULL);
2456 return false;
2459 void default_settings(void)
2461 arghs_per_food = ARGHS_PER_FOOD;
2462 argh_size = ARGH_SIZE;
2463 food_size = FOOD_SIZE;
2464 speed = SPEED;
2465 worm_food = WORM_PER_FOOD;
2466 players = 1;
2467 worm_count = MAX_WORMS;
2468 use_remote = false;
2469 return;
2473 Launches the wormlet game
2475 bool launch_wormlet(void)
2477 int game_result = 1;
2479 rb->lcd_clear_display();
2481 /* Turn off backlight timeout */
2482 backlight_force_on(); /* backlight control in lib/helper.c */
2484 /* start the game */
2485 while (game_result == 1)
2486 game_result = run();
2488 switch (game_result)
2490 case 2:
2491 /* Turn on backlight timeout (revert to settings) */
2492 backlight_use_settings(); /* backlight control in lib/helper.c */
2493 return false;
2494 break;
2496 return false;
2499 /* End of settings/changes etc */
2502 * Main entry point
2504 enum plugin_status plugin_start(const void* parameter)
2506 int result;
2507 int menu_quit = 0;
2508 int new_setting;
2510 (void)(parameter);
2512 default_settings();
2513 if (configfile_load(SETTINGS_FILENAME, config,
2514 sizeof(config)/sizeof(*config),
2515 SETTINGS_MIN_VERSION ) < 0)
2517 /* If the loading failed, save a new config file (as the disk is
2518 already spinning) */
2519 configfile_save(SETTINGS_FILENAME, config,
2520 sizeof(config)/sizeof(*config),
2521 SETTINGS_VERSION);
2524 #ifdef HAVE_LCD_COLOR
2525 rb->lcd_set_foreground(COLOR_FG);
2526 rb->lcd_set_background(COLOR_BG);
2527 #endif
2529 #if LCD_DEPTH > 1
2530 rb->lcd_set_backdrop(NULL);
2531 #endif
2533 #ifdef DEBUG_WORMLET
2534 testline_in_rect();
2535 test_worm_argh_collision_in_moves();
2536 test_make_argh();
2537 test_worm_food_collision();
2538 test_worm_argh_collision();
2539 test_specific_worm_collision();
2540 #endif
2542 /* Setup screen */
2544 static const struct opt_items noyes[2] = {
2545 { "No", -1 },
2546 { "Yes", -1 },
2549 static const struct opt_items num_worms_option[3] = {
2550 { "1", -1 },
2551 { "2", -1 },
2552 { "3", -1 }
2555 #ifdef MULTIPLAYER
2556 static const struct opt_items num_players_option[4] = {
2557 #else
2558 static const struct opt_items num_players_option[2] = {
2559 #endif
2560 { "0", -1 },
2561 { "1", -1 }
2562 #ifdef MULTIPLAYER
2563 ,{ "2", -1 },
2564 { "3", -1 }
2565 #endif
2568 static const struct opt_items argh_size_option[9] = {
2569 { "2", -1 },
2570 { "3", -1 },
2571 { "4", -1 },
2572 { "5", -1 },
2573 { "6", -1 },
2574 { "7", -1 },
2575 { "8", -1 },
2576 { "9", -1 },
2577 { "10", -1 }
2580 static const struct opt_items food_size_option[9] = {
2581 { "2", -1 },
2582 { "3", -1 },
2583 { "4", -1 },
2584 { "5", -1 },
2585 { "6", -1 },
2586 { "7", -1 },
2587 { "8", -1 },
2588 { "9", -1 },
2589 { "10", -1 }
2592 static const struct opt_items worm_food_option[16] = {
2593 { "0", -1 },
2594 { "1", -1 },
2595 { "2", -1 },
2596 { "3", -1 },
2597 { "4", -1 },
2598 { "5", -1 },
2599 { "6", -1 },
2600 { "7", -1 },
2601 { "8", -1 },
2602 { "9", -1 },
2603 { "10", -1 },
2604 { "11", -1 },
2605 { "12", -1 },
2606 { "13", -1 },
2607 { "14", -1 },
2608 { "15", -1 }
2611 static const struct opt_items argh_food_option[9] = {
2612 { "0", -1 },
2613 { "1", -1 },
2614 { "2", -1 },
2615 { "3", -1 },
2616 { "4", -1 },
2617 { "5", -1 },
2618 { "6", -1 },
2619 { "7", -1 },
2620 { "8", -1 }
2623 static const struct opt_items speed_option[21] = {
2624 { "0", -1 },
2625 { "1", -1 },
2626 { "2", -1 },
2627 { "3", -1 },
2628 { "4", -1 },
2629 { "5", -1 },
2630 { "6", -1 },
2631 { "7", -1 },
2632 { "8", -1 },
2633 { "9", -1 },
2634 { "10", -1 },
2635 { "11", -1 },
2636 { "12", -1 },
2637 { "13", -1 },
2638 { "14", -1 },
2639 { "15", -1 },
2640 { "16", -1 },
2641 { "17", -1 },
2642 { "18", -1 },
2643 { "19", -1 },
2644 { "20", -1 }
2647 static const struct opt_items remoteonly_option[1] = {
2648 { "Remote Control", -1 }
2651 static const struct opt_items key24_option[2] = {
2652 { "4 Key Control", -1 },
2653 { "2 Key Control", -1 }
2656 #ifdef REMOTE
2657 static const struct opt_items remote_option[2] = {
2658 { "Remote Control", -1 },
2659 { "No Rem. Control", -1 }
2661 #else
2662 static const struct opt_items key2_option[1] = {
2663 { "2 Key Control", -1 }
2665 #endif
2667 static const struct opt_items nokey_option[1] = {
2668 { "Out of Control", -1 }
2671 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, "Play Wormlet!",
2672 "Number of Worms", "Number of Players", "Control Style",
2673 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2674 "Argh Size","Food Size","Revert to Default Settings",
2675 "Playback Control", "Quit");
2677 rb->button_clear_queue();
2679 while (!menu_quit) {
2680 switch(rb->do_menu(&menu, &result, NULL, false))
2682 case 0:
2683 rb->lcd_setfont(FONT_SYSFIXED);
2684 launch_wormlet();
2685 break;
2686 case 1:
2687 new_setting = worm_count - 1;
2688 rb->set_option("Number of Worms", &new_setting, INT, num_worms_option, 3, NULL);
2689 if (new_setting != worm_count)
2690 worm_count = new_setting + 1;
2691 if (worm_count < players) {
2692 worm_count = players;
2694 break;
2695 case 2:
2696 new_setting = players;
2697 #ifdef MULTIPLAYER
2698 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 4, NULL);
2699 #else
2700 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 2, NULL);
2701 #endif
2702 if (new_setting != players)
2703 players = new_setting;
2704 if (players > worm_count) {
2705 worm_count = players;
2707 if (players > 2) {
2708 use_remote = true;
2710 break;
2711 case 3:
2712 new_setting = use_remote;
2713 switch(players) {
2714 case 0:
2715 rb->set_option("Control Style",&new_setting,INT, nokey_option, 1, NULL);
2716 break;
2717 case 1:
2718 rb->set_option("Control Style",&new_setting,INT, key24_option, 2, NULL);
2719 break;
2720 case 2:
2721 #ifdef REMOTE
2722 rb->set_option("Control Style",&new_setting,INT, remote_option, 2, NULL);
2723 #else
2724 rb->set_option("Control Style",&new_setting,INT, key2_option, 1, NULL);
2725 #endif
2726 break;
2727 case 3:
2728 rb->set_option("Control Style",&new_setting,INT, remoteonly_option, 1, NULL);
2729 break;
2731 if (new_setting != use_remote)
2732 use_remote = new_setting;
2733 break;
2734 case 4:
2735 new_setting = worm_food;
2736 rb->set_option("Worm Growth Per Food", &new_setting,INT,worm_food_option , 16, NULL);
2737 if (new_setting != worm_food)
2738 worm_food = new_setting;
2739 break;
2740 case 5:
2741 new_setting = speed;
2742 new_setting = 20 - new_setting;
2743 rb->set_option("Worm Speed", &new_setting,INT,speed_option , 21, NULL);
2744 new_setting = 20 - new_setting;
2745 if (new_setting != speed)
2746 speed = new_setting;
2747 break;
2748 case 6:
2749 new_setting = arghs_per_food;
2750 rb->set_option("Arghs Per Food", &new_setting,INT,argh_food_option , 9, NULL);
2751 if (new_setting != arghs_per_food)
2752 arghs_per_food = new_setting;
2753 break;
2754 case 7:
2755 new_setting = argh_size-2;
2756 rb->set_option("Argh Size", &new_setting,INT,argh_size_option , 9, NULL);
2757 if (new_setting != argh_size)
2758 argh_size = new_setting+2;
2759 break;
2760 case 8:
2761 new_setting = food_size-2;
2762 rb->set_option("Food Size", &new_setting,INT,food_size_option, 9, NULL);
2763 if (new_setting != food_size)
2764 food_size = new_setting+2;
2765 break;
2766 case 9:
2767 new_setting = 0;
2768 rb->set_option("Reset Settings?", &new_setting,INT, noyes , 2, NULL);
2769 if (new_setting == 1)
2770 default_settings();
2771 break;
2772 case 10:
2773 playback_control(NULL);
2774 break;
2775 default:
2776 menu_quit=1;
2777 break;
2781 configfile_save(SETTINGS_FILENAME, config,
2782 sizeof(config)/sizeof(*config),
2783 SETTINGS_VERSION);
2785 return PLUGIN_OK;