Add platform file for Ipod 1G / 2G. Now only the front image is missing for building...
[Rockbox.git] / apps / plugins / wormlet.c
blob6abcc43db19ae6655e1ee93b2f17d4079f02da7a
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"
22 PLUGIN_HEADER
24 /* size of the field the worm lives in */
25 #define FIELD_RECT_X 1
26 #define FIELD_RECT_Y 1
27 #define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
28 #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
30 /* when the game starts */
31 #define INITIAL_WORM_LENGTH 10
33 /* num of pixel the worm grows per eaten food */
34 #define WORM_PER_FOOD 7
36 /* num of worms creeping in the FIELD */
37 #define MAX_WORMS 3
39 /* minimal distance between a worm and an argh
40 when a new argh is made */
41 #define MIN_ARGH_DIST 5
43 #if (CONFIG_KEYPAD == RECORDER_PAD)
44 #define BTN_DIR_UP BUTTON_UP
45 #define BTN_DIR_DOWN BUTTON_DOWN
46 #define BTN_DIR_LEFT BUTTON_LEFT
47 #define BTN_DIR_RIGHT BUTTON_RIGHT
48 #define BTN_PLAYER2_DIR1 BUTTON_F2
49 #define BTN_PLAYER2_DIR2 BUTTON_F3
50 #define BTN_STARTPAUSE BUTTON_PLAY
51 #define BTN_QUIT BUTTON_OFF
52 #define BTN_STOPRESET BUTTON_ON
53 #define BTN_TOGGLE_KEYS BUTTON_F1
55 #if BUTTON_REMOTE != 0
56 #define BTN_RC_UP BUTTON_RC_VOL_UP
57 #define BTN_RC_DOWN BUTTON_RC_VOL_DOWN
58 #define REMOTE
59 #define MULTIPLAYER
60 #endif
62 #define PLAYERS_TEXT "UP/DN"
63 #define WORMS_TEXT "L/R"
64 #define KEY_CONTROL_TEXT "F1"
66 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
67 #define BTN_DIR_UP BUTTON_UP
68 #define BTN_DIR_DOWN BUTTON_DOWN
69 #define BTN_DIR_LEFT BUTTON_LEFT
70 #define BTN_DIR_RIGHT BUTTON_RIGHT
71 #define BTN_PLAYER2_DIR1 BUTTON_F2
72 #define BTN_PLAYER2_DIR2 BUTTON_F3
73 #define BTN_STARTPAUSE BUTTON_SELECT
74 #define BTN_QUIT BUTTON_OFF
75 #define BTN_STOPRESET BUTTON_ON
76 #define BTN_TOGGLE_KEYS BUTTON_F1
78 #define PLAYERS_TEXT "UP/DN"
79 #define WORMS_TEXT "L/R"
80 #define KEY_CONTROL_TEXT "F1"
82 #elif (CONFIG_KEYPAD == ONDIO_PAD)
83 #define BTN_DIR_UP BUTTON_UP
84 #define BTN_DIR_DOWN BUTTON_DOWN
85 #define BTN_DIR_LEFT BUTTON_LEFT
86 #define BTN_DIR_RIGHT BUTTON_RIGHT
87 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
88 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
89 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
91 #define PLAYERS_TEXT "UP/DN"
92 #define WORMS_TEXT "L/R"
94 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
95 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
97 #define BTN_DIR_UP BUTTON_MENU
98 #define BTN_DIR_DOWN BUTTON_PLAY
99 #define BTN_DIR_LEFT BUTTON_LEFT
100 #define BTN_DIR_RIGHT BUTTON_RIGHT
101 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
102 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
103 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
105 #define PLAYERS_TEXT "Menu/Play"
106 #define WORMS_TEXT "Left/Right"
108 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
110 #define BTN_DIR_UP BUTTON_UP
111 #define BTN_DIR_DOWN BUTTON_DOWN
112 #define BTN_DIR_LEFT BUTTON_LEFT
113 #define BTN_DIR_RIGHT BUTTON_RIGHT
114 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
115 #define BTN_QUIT BUTTON_OFF
116 #define BTN_STOPRESET BUTTON_ON
118 #define BTN_RC_QUIT BUTTON_RC_STOP
120 #define PLAYERS_TEXT "Up/Down"
121 #define WORMS_TEXT "Left/Right"
123 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
125 #define BTN_DIR_UP BUTTON_UP
126 #define BTN_DIR_DOWN BUTTON_DOWN
127 #define BTN_DIR_LEFT BUTTON_LEFT
128 #define BTN_DIR_RIGHT BUTTON_RIGHT
129 #define BTN_STARTPAUSE BUTTON_PLAY
130 #define BTN_QUIT BUTTON_POWER
131 #define BTN_STOPRESET BUTTON_REC
133 #define PLAYERS_TEXT "Up/Down"
134 #define WORMS_TEXT "Left/Right"
136 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
138 #define BTN_DIR_UP BUTTON_UP
139 #define BTN_DIR_DOWN BUTTON_DOWN
140 #define BTN_DIR_LEFT BUTTON_LEFT
141 #define BTN_DIR_RIGHT BUTTON_RIGHT
142 #define BTN_STARTPAUSE BUTTON_SELECT
143 #define BTN_QUIT BUTTON_POWER
144 #define BTN_STOPRESET BUTTON_A
146 #define PLAYERS_TEXT "Up/Down"
147 #define WORMS_TEXT "Left/Right"
150 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
152 #define BTN_DIR_UP BUTTON_UP
153 #define BTN_DIR_DOWN BUTTON_DOWN
154 #define BTN_DIR_LEFT BUTTON_LEFT
155 #define BTN_DIR_RIGHT BUTTON_RIGHT
156 #define BTN_STARTPAUSE BUTTON_SELECT
157 #define BTN_QUIT BUTTON_POWER
158 #define BTN_STOPRESET BUTTON_REC
160 #define PLAYERS_TEXT "Up/Down"
161 #define WORMS_TEXT "Left/Right"
164 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
166 #define BTN_DIR_UP BUTTON_SCROLL_UP
167 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
168 #define BTN_DIR_LEFT BUTTON_LEFT
169 #define BTN_DIR_RIGHT BUTTON_RIGHT
170 #define BTN_STARTPAUSE BUTTON_PLAY
171 #define BTN_QUIT BUTTON_POWER
172 #define BTN_STOPRESET BUTTON_REW
174 #define PLAYERS_TEXT "Up/Down"
175 #define WORMS_TEXT "Left/Right"
177 #endif
179 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
180 #define FOOD_SIZE 3
181 #define ARGH_SIZE 4
182 #define SPEED 14
183 #define MAX_WORM_SEGMENTS 128
184 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
185 #define FOOD_SIZE 4
186 #define ARGH_SIZE 5
187 #define SPEED 10
188 #define MAX_WORM_SEGMENTS 128
189 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
190 #define FOOD_SIZE 4
191 #define ARGH_SIZE 5
192 #define SPEED 9
193 #define MAX_WORM_SEGMENTS 128
194 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
195 #define FOOD_SIZE 4
196 #define ARGH_SIZE 5
197 #define SPEED 8
198 #define MAX_WORM_SEGMENTS 256
199 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
200 #define FOOD_SIZE 4
201 #define ARGH_SIZE 5
202 #define SPEED 6
203 #define MAX_WORM_SEGMENTS 256
204 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
205 #define FOOD_SIZE 5
206 #define ARGH_SIZE 6
207 #define SPEED 4
208 #define MAX_WORM_SEGMENTS 512
209 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
210 #define FOOD_SIZE 5
211 #define ARGH_SIZE 6
212 #define SPEED 4
213 #define MAX_WORM_SEGMENTS 512
214 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
215 #define FOOD_SIZE 7
216 #define ARGH_SIZE 8
217 #define SPEED 4
218 #define MAX_WORM_SEGMENTS 512
219 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
220 #define FOOD_SIZE 7
221 #define ARGH_SIZE 8
222 #define SPEED 4
223 #define MAX_WORM_SEGMENTS 512
224 #endif
226 #ifdef HAVE_LCD_COLOR
227 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
228 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
229 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
230 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
231 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
232 #endif
235 * All the properties that a worm has.
237 static struct worm {
238 /* The worm is stored in a ring of xy coordinates */
239 int x[MAX_WORM_SEGMENTS];
240 int y[MAX_WORM_SEGMENTS];
242 int head; /* index of the head within the buffer */
243 int tail; /* index of the tail within the buffer */
244 int growing; /* number of cyles the worm still keeps growing */
245 bool alive; /* the worms living state */
247 /* direction vector in which the worm moves */
248 int dirx; /* only values -1 0 1 allowed */
249 int diry; /* only values -1 0 1 allowed */
251 /* this method is used to fetch the direction the user
252 has selected. It can be one of the values
253 human_player1, human_player2, remote_player, virtual_player.
254 All these values are fuctions, that can change the direction
255 of the worm */
256 void (*fetch_worm_direction)(struct worm *w);
257 } worms[MAX_WORMS];
259 /* stores the highscore - besides it was scored by a virtual player */
260 static int highscore;
262 #define MAX_FOOD 5 /* maximal number of food items */
264 /* The arrays store the food coordinates */
265 static int foodx[MAX_FOOD];
266 static int foody[MAX_FOOD];
268 #define MAX_ARGH 100 /* maximal number of argh items */
269 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
271 /* The arrays store the argh coordinates */
272 static int arghx[MAX_ARGH];
273 static int arghy[MAX_ARGH];
275 /* the number of arghs that are currently in use */
276 static int argh_count;
278 /* the number of arghs per food, settable by user */
279 static int arghs_per_food = ARGHS_PER_FOOD;
280 /* the size of the argh, settable by user */
281 static int argh_size = ARGH_SIZE;
282 /* the size of the food, settable by user */
283 static int food_size = FOOD_SIZE;
284 /* the speed of the worm, settable by user */
285 static int speed = SPEED;
286 /* the amount a worm grows by eating a food, settable by user */
287 static int worm_food = WORM_PER_FOOD;
289 /* End additional variables */
291 #ifdef DEBUG_WORMLET
292 /* just a buffer used for debug output */
293 static char debugout[15];
294 #endif
296 /* the number of active worms (dead or alive) */
297 static int worm_count = MAX_WORMS;
299 /* in multiplayer mode: en- / disables the remote worm control
300 in singleplayer mode: toggles 4 / 2 button worm control */
301 static bool use_remote = false;
303 /* return values of check_collision */
304 #define COLLISION_NONE 0
305 #define COLLISION_WORM 1
306 #define COLLISION_FOOD 2
307 #define COLLISION_ARGH 3
308 #define COLLISION_FIELD 4
310 /* constants for use as directions.
311 Note that the values are ordered clockwise.
312 Thus increasing / decreasing the values
313 is equivalent to right / left turns. */
314 #define WEST 0
315 #define NORTH 1
316 #define EAST 2
317 #define SOUTH 3
319 /* direction of human player 1 */
320 static int player1_dir = EAST;
321 /* direction of human player 2 */
322 static int player2_dir = EAST;
323 /* direction of human player 3 */
324 static int player3_dir = EAST;
326 /* the number of (human) players that currently
327 control a worm */
328 static int players = 1;
330 /* the rockbox plugin api */
331 static struct plugin_api* rb;
333 #define SETTINGS_VERSION 1
334 #define SETTINGS_MIN_VERSION 1
335 #define SETTINGS_FILENAME "wormlet.cfg"
337 static struct configdata config[] =
339 {TYPE_INT, 0, 1024, &highscore, "highscore", NULL, NULL},
340 {TYPE_INT, 0, 15, &arghs_per_food, "arghs per food", NULL, NULL},
341 {TYPE_INT, 0, 15, &argh_size, "argh size", NULL, NULL},
342 {TYPE_INT, 0, 15, &food_size, "food size", NULL, NULL},
343 {TYPE_INT, 0, 3, &players, "players", NULL, NULL},
344 {TYPE_INT, 0, 3, &worm_count, "worms", NULL, NULL},
345 {TYPE_INT, 0, 20, &speed, "speed", NULL, NULL},
346 {TYPE_INT, 0, 15, &worm_food, "Worm Growth Per Food", NULL, NULL}//,
347 //{TYPE_INT, 0, 3, &use_remote, "use remote", NULL, NULL}
350 #ifdef DEBUG_WORMLET
351 static void set_debug_out(char *str){
352 strcpy(debugout, str);
354 #endif
357 * Returns the direction id in which the worm
358 * currently is creeping.
359 * @param struct worm *w The worm that is to be investigated.
360 * w Must not be null.
361 * @return int A value 0 <= value < 4
362 * Note the predefined constants NORTH, SOUTH, EAST, WEST
364 static int get_worm_dir(struct worm *w) {
365 int retVal ;
366 if (w->dirx == 0) {
367 if (w->diry == 1) {
368 retVal = SOUTH;
369 } else {
370 retVal = NORTH;
372 } else {
373 if (w->dirx == 1) {
374 retVal = EAST;
375 } else {
376 retVal = WEST;
379 return retVal;
383 * Set the direction of the specified worm with a direction id.
384 * Increasing the value by 1 means to turn the worm direction
385 * to right by 90 degree.
386 * @param struct worm *w The worm that is to be altered. w Must not be null.
387 * @param int dir The new direction in which the worm is to creep.
388 * dir must be 0 <= dir < 4. Use predefined constants
389 * NORTH, SOUTH, EAST, WEST
391 static void set_worm_dir(struct worm *w, int dir) {
392 switch (dir) {
393 case WEST:
394 w->dirx = -1;
395 w->diry = 0;
396 break;
397 case NORTH:
398 w->dirx = 0;
399 w->diry = - 1;
400 break;
401 case EAST:
402 w->dirx = 1;
403 w->diry = 0;
404 break;
405 case SOUTH:
406 w->dirx = 0;
407 w->diry = 1;
408 break;
413 * Returns the current length of the worm array. This
414 * is also a value for the number of bends that are in the worm.
415 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
417 static int get_worm_array_length(struct worm *w) {
418 /* initial simple calculation will be overwritten if wrong. */
419 int retVal = w->head - w->tail;
421 /* if the worm 'crosses' the boundaries of the ringbuffer */
422 if (retVal < 0) {
423 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
426 return retVal;
430 * Returns the score the specified worm. The score is the length
431 * of the worm.
432 * @param struct worm *w The worm that is to be investigated.
433 * w must not be null.
434 * @return int The length of the worm (>= 0).
436 static int get_score(struct worm *w) {
437 int retval = 0;
438 int length = get_worm_array_length(w);
439 int i;
440 for (i = 0; i < length; i++) {
442 /* The iteration iterates the length of the worm.
443 Here's the conversion to the true indices within the worm arrays. */
444 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
445 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
446 int startx = w->x[linestart];
447 int starty = w->y[linestart];
448 int endx = w->x[lineend];
449 int endy = w->y[lineend];
451 int minimum, maximum;
453 if (startx == endx) {
454 minimum = MIN(starty, endy);
455 maximum = MAX(starty, endy);
456 } else {
457 minimum = MIN(startx, endx);
458 maximum = MAX(startx, endx);
460 retval += abs(maximum - minimum);
462 return retval;
466 * Determines wether the line specified by startx, starty, endx, endy intersects
467 * the rectangle specified by x, y, width, height. Note that the line must be exactly
468 * horizontal or vertical (startx == endx or starty == endy).
469 * @param int startx The x coordinate of the start point of the line.
470 * @param int starty The y coordinate of the start point of the line.
471 * @param int endx The x coordinate of the end point of the line.
472 * @param int endy The y coordinate of the end point of the line.
473 * @param int x The x coordinate of the top left corner of the rectangle.
474 * @param int y The y coordinate of the top left corner of the rectangle.
475 * @param int width The width of the rectangle.
476 * @param int height The height of the rectangle.
477 * @return bool Returns true if the specified line intersects with the recangle.
479 static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) {
480 bool retval = false;
481 int simple, simplemin, simplemax;
482 int compa, compb, compmin, compmax;
483 int temp;
484 if (startx == endx) {
485 simple = startx;
486 simplemin = x;
487 simplemax = x + width;
489 compa = starty;
490 compb = endy;
491 compmin = y;
492 compmax = y + height;
493 } else {
494 simple = starty;
495 simplemin = y;
496 simplemax = y + height;
498 compa = startx;
499 compb = endx;
500 compmin = x;
501 compmax = x + width;
504 temp = compa;
505 compa = MIN(compa, compb);
506 compb = MAX(temp, compb);
508 if (simplemin <= simple && simple <= simplemax) {
509 if ((compmin <= compa && compa <= compmax) ||
510 (compmin <= compb && compb <= compmax) ||
511 (compa <= compmin && compb >= compmax)) {
512 retval = true;
515 return retval;
519 * Tests wether the specified worm intersects with the rect.
520 * @param struct worm *w The worm to be investigated
521 * @param int x The x coordinate of the top left corner of the rect
522 * @param int y The y coordinate of the top left corner of the rect
523 * @param int widht The width of the rect
524 * @param int height The height of the rect
525 * @return bool Returns true if the worm intersects with the rect
527 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) {
528 bool retval = false;
531 /* get_worm_array_length is expensive -> buffer the value */
532 int wormLength = get_worm_array_length(w);
533 int i;
535 /* test each entry that is part of the worm */
536 for (i = 0; i < wormLength && retval == false; i++) {
538 /* The iteration iterates the length of the worm.
539 Here's the conversion to the true indices within the worm arrays. */
540 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
541 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
542 int startx = w->x[linestart];
543 int starty = w->y[linestart];
544 int endx = w->x[lineend];
545 int endy = w->y[lineend];
547 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
550 return retval;
554 * Checks wether a specific food in the food arrays is at the
555 * specified coordinates.
556 * @param int foodIndex The index of the food in the food arrays
557 * @param int x the x coordinate.
558 * @param int y the y coordinate.
559 * @return Returns true if the coordinate hits the food specified by
560 * foodIndex.
562 static bool specific_food_collision(int foodIndex, int x, int y) {
563 bool retVal = false;
564 if (x >= foodx[foodIndex] &&
565 x < foodx[foodIndex] + food_size &&
566 y >= foody[foodIndex] &&
567 y < foody[foodIndex] + food_size) {
569 retVal = true;
571 return retVal;
575 * Returns the index of the food that is at the
576 * given coordinates. If no food is at the coordinates
577 * -1 is returned.
578 * @return int -1 <= value < MAX_FOOD
580 static int food_collision(int x, int y) {
581 int i = 0;
582 int retVal = -1;
583 for (i = 0; i < MAX_FOOD; i++) {
584 if (specific_food_collision(i, x, y)) {
585 retVal = i;
586 break;
589 return retVal;
593 * Checks wether a specific argh in the argh arrays is at the
594 * specified coordinates.
595 * @param int arghIndex The index of the argh in the argh arrays
596 * @param int x the x coordinate.
597 * @param int y the y coordinate.
598 * @return Returns true if the coordinate hits the argh specified by
599 * arghIndex.
601 static bool specific_argh_collision(int arghIndex, int x, int y) {
603 if ( x >= arghx[arghIndex] &&
604 y >= arghy[arghIndex] &&
605 x < arghx[arghIndex] + argh_size &&
606 y < arghy[arghIndex] + argh_size )
608 return true;
611 return false;
615 * Returns the index of the argh that is at the
616 * given coordinates. If no argh is at the coordinates
617 * -1 is returned.
618 * @param int x The x coordinate.
619 * @param int y The y coordinate.
620 * @return int -1 <= value < argh_count <= MAX_ARGH
622 static int argh_collision(int x, int y) {
623 int i = 0;
624 int retVal = -1;
626 /* search for the argh that has the specified coords */
627 for (i = 0; i < argh_count; i++) {
628 if (specific_argh_collision(i, x, y)) {
629 retVal = i;
630 break;
633 return retVal;
637 * Checks wether the worm collides with the food at the specfied food-arrays.
638 * @param int foodIndex The index of the food in the arrays. Ensure the value is
639 * 0 <= foodIndex <= MAX_FOOD
640 * @return Returns true if the worm collides with the specified food.
642 static bool worm_food_collision(struct worm *w, int foodIndex)
644 bool retVal = false;
646 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
647 food_size - 1, food_size - 1);
649 return retVal;
653 * Returns true if the worm hits the argh within the next moves (unless
654 * the worm changes it's direction).
655 * @param struct worm *w - The worm to investigate
656 * @param int argh_idx - The index of the argh
657 * @param int moves - The number of moves that are considered.
658 * @return Returns false if the specified argh is not hit within the next
659 * moves.
661 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){
662 bool retVal = false;
663 int x1, y1, x2, y2;
664 x1 = w->x[w->head];
665 y1 = w->y[w->head];
667 x2 = w->x[w->head] + moves * w->dirx;
668 y2 = w->y[w->head] + moves * w->diry;
670 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
671 argh_size, argh_size);
672 return retVal;
676 * Checks wether the worm collides with the argh at the specfied argh-arrays.
677 * @param int arghIndex The index of the argh in the arrays.
678 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
679 * @return Returns true if the worm collides with the specified argh.
681 static bool worm_argh_collision(struct worm *w, int arghIndex)
683 bool retVal = false;
685 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
686 argh_size - 1, argh_size - 1);
688 return retVal;
692 * Find new coordinates for the food stored in foodx[index], foody[index]
693 * that don't collide with any other food or argh
694 * @param int index
695 * Ensure that 0 <= index < MAX_FOOD.
697 static void make_food(int index) {
699 int x = 0;
700 int y = 0;
701 bool collisionDetected = false;
702 int i;
704 do {
705 /* make coordinates for a new food so that
706 the entire food lies within the FIELD */
707 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
708 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
710 /* Ensure that the new food doesn't collide with any
711 existing foods or arghs.
712 If one or more corners of the new food hit any existing
713 argh or food a collision is detected.
715 collisionDetected =
716 food_collision(x , y ) >= 0 ||
717 food_collision(x , y + food_size - 1) >= 0 ||
718 food_collision(x + food_size - 1, y ) >= 0 ||
719 food_collision(x + food_size - 1, y + food_size - 1) >= 0 ||
720 argh_collision(x , y ) >= 0 ||
721 argh_collision(x , y + food_size - 1) >= 0 ||
722 argh_collision(x + food_size - 1, y ) >= 0 ||
723 argh_collision(x + food_size - 1, y + food_size - 1) >= 0;
725 /* use coordinates for further testing */
726 foodx[index] = x;
727 foody[index] = y;
729 /* now test wether we accidently hit the worm with food ;) */
730 i = 0;
731 for (i = 0; i < worm_count && !collisionDetected; i++) {
732 collisionDetected |= worm_food_collision(&worms[i], index);
735 while (collisionDetected);
736 return;
740 * Clears a food from the lcd buffer.
741 * @param int index The index of the food arrays under which
742 * the coordinates of the desired food can be found. Ensure
743 * that the value is 0 <= index <= MAX_FOOD.
745 static void clear_food(int index)
747 /* remove the old food from the screen */
748 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
749 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
750 foody[index] + FIELD_RECT_Y,
751 food_size, food_size);
752 rb->lcd_set_drawmode(DRMODE_SOLID);
756 * Draws a food in the lcd buffer.
757 * @param int index The index of the food arrays under which
758 * the coordinates of the desired food can be found. Ensure
759 * that the value is 0 <= index <= MAX_FOOD.
761 static void draw_food(int index)
763 /* draw the food object */
764 #ifdef HAVE_LCD_COLOR
765 rb->lcd_set_foreground(COLOR_FOOD);
766 #endif
767 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
768 foody[index] + FIELD_RECT_Y,
769 food_size, food_size);
770 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
771 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
772 foody[index] + FIELD_RECT_Y + 1,
773 food_size - 2, food_size - 2);
774 rb->lcd_set_drawmode(DRMODE_SOLID);
775 #ifdef HAVE_LCD_COLOR
776 rb->lcd_set_foreground(COLOR_FG);
777 #endif
781 * Find new coordinates for the argh stored in arghx[index], arghy[index]
782 * that don't collide with any other food or argh.
783 * @param int index
784 * Ensure that 0 <= index < argh_count < MAX_ARGH.
786 static void make_argh(int index)
788 int x = -1;
789 int y = -1;
790 bool collisionDetected = false;
791 int i;
793 do {
794 /* make coordinates for a new argh so that
795 the entire food lies within the FIELD */
796 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
797 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
799 /* Ensure that the new argh doesn't intersect with any
800 existing foods or arghs.
801 If one or more corners of the new argh hit any existing
802 argh or food an intersection is detected.
804 collisionDetected =
805 food_collision(x , y ) >= 0 ||
806 food_collision(x , y + argh_size - 1) >= 0 ||
807 food_collision(x + argh_size - 1, y ) >= 0 ||
808 food_collision(x + argh_size - 1, y + argh_size - 1) >= 0 ||
809 argh_collision(x , y ) >= 0 ||
810 argh_collision(x , y + argh_size - 1) >= 0 ||
811 argh_collision(x + argh_size - 1, y ) >= 0 ||
812 argh_collision(x + argh_size - 1, y + argh_size - 1) >= 0;
814 /* use the candidate coordinates to make a real argh */
815 arghx[index] = x;
816 arghy[index] = y;
818 /* now test wether we accidently hit the worm with argh ;) */
819 for (i = 0; i < worm_count && !collisionDetected; i++) {
820 collisionDetected |= worm_argh_collision(&worms[i], index);
821 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
822 MIN_ARGH_DIST);
825 while (collisionDetected);
826 return;
830 * Draws an argh in the lcd buffer.
831 * @param int index The index of the argh arrays under which
832 * the coordinates of the desired argh can be found. Ensure
833 * that the value is 0 <= index < argh_count <= MAX_ARGH.
835 static void draw_argh(int index)
837 /* draw the new argh */
838 #ifdef HAVE_LCD_COLOR
839 rb->lcd_set_foreground(COLOR_ARGH);
840 #endif
841 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
842 arghy[index] + FIELD_RECT_Y,
843 argh_size, argh_size);
844 #ifdef HAVE_LCD_COLOR
845 rb->lcd_set_foreground(COLOR_FG);
846 #endif
849 static void virtual_player(struct worm *w);
851 * Initialzes the specified worm with INITIAL_WORM_LENGTH
852 * and the tail at the specified position. The worm will
853 * be initialized alive and creeping EAST.
854 * @param struct worm *w The worm that is to be initialized
855 * @param int x The x coordinate at which the tail of the worm starts.
856 * x must be 0 <= x < FIELD_RECT_WIDTH.
857 * @param int y The y coordinate at which the tail of the worm starts
858 * y must be 0 <= y < FIELD_RECT_WIDTH.
860 static void init_worm(struct worm *w, int x, int y){
861 /* initialize the worm size */
862 w->head = 1;
863 w->tail = 0;
865 w->x[w->head] = x + 1;
866 w->y[w->head] = y;
868 w->x[w->tail] = x;
869 w->y[w->tail] = y;
871 /* set the initial direction the worm creeps to */
872 w->dirx = 1;
873 w->diry = 0;
875 w->growing = INITIAL_WORM_LENGTH - 1;
876 w->alive = true;
877 w->fetch_worm_direction = virtual_player;
881 * Writes the direction that was stored for
882 * human player 1 into the specified worm. This function
883 * may be used to be stored in worm.fetch_worm_direction.
884 * The value of
885 * the direction is read from player1_dir.
886 * @param struct worm *w - The worm of which the direction
887 * is altered.
889 static void human_player1(struct worm *w) {
890 set_worm_dir(w, player1_dir);
894 * Writes the direction that was stored for
895 * human player 2 into the specified worm. This function
896 * may be used to be stored in worm.fetch_worm_direction.
897 * The value of
898 * the direction is read from player2_dir.
899 * @param struct worm *w - The worm of which the direction
900 * is altered.
902 static void human_player2(struct worm *w) {
903 set_worm_dir(w, player2_dir);
907 * Writes the direction that was stored for
908 * human player using a remote control
909 * into the specified worm. This function
910 * may be used to be stored in worm.fetch_worm_direction.
911 * The value of
912 * the direction is read from player3_dir.
913 * @param struct worm *w - The worm of which the direction
914 * is altered.
916 static void remote_player(struct worm *w) {
917 set_worm_dir(w, player3_dir);
921 * Initializes the worm-, food- and argh-arrays, draws a frame,
922 * makes some food and argh and display all that stuff.
924 static void init_wormlet(void)
926 int i;
928 for (i = 0; i< worm_count; i++) {
929 /* Initialize all the worm coordinates to center. */
930 int x = (int)(FIELD_RECT_WIDTH / 2);
931 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
933 init_worm(&worms[i], x, y);
936 player1_dir = EAST;
937 player2_dir = EAST;
938 player3_dir = EAST;
940 if (players > 0) {
941 worms[0].fetch_worm_direction = human_player1;
944 if (players > 1) {
945 if (use_remote) {
946 worms[1].fetch_worm_direction = remote_player;
947 } else {
948 worms[1].fetch_worm_direction = human_player2;
952 if (players > 2) {
953 worms[2].fetch_worm_direction = human_player2;
956 /* Needed when the game is restarted using BTN_STOPRESET */
957 rb->lcd_clear_display();
959 /* make and display some food and argh */
960 argh_count = MAX_FOOD;
961 for (i = 0; i < MAX_FOOD; i++) {
962 make_food(i);
963 draw_food(i);
964 make_argh(i);
965 draw_argh(i);
968 /* draw the game field */
969 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
970 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
971 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
972 rb->lcd_set_drawmode(DRMODE_SOLID);
974 /* make everything visible */
975 rb->lcd_update();
980 * Move the worm one step further if it is alive.
981 * The direction in which the worm moves is taken from dirx and diry.
982 * move_worm decreases growing if > 0. While the worm is growing the tail
983 * is left untouched.
984 * @param struct worm *w The worm to move. w must not be NULL.
986 static void move_worm(struct worm *w)
988 if (w->alive) {
989 /* determine the head point and its precessor */
990 int headx = w->x[w->head];
991 int heady = w->y[w->head];
992 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
993 int preheadx = w->x[prehead];
994 int preheady = w->y[prehead];
996 /* determine the old direction */
997 int olddirx;
998 int olddiry;
999 if (headx == preheadx) {
1000 olddirx = 0;
1001 olddiry = (heady > preheady) ? 1 : -1;
1002 } else {
1003 olddiry = 0;
1004 olddirx = (headx > preheadx) ? 1 : -1;
1007 /* olddir == dir?
1008 a change of direction means a new segment
1009 has been opened */
1010 if (olddirx != w->dirx ||
1011 olddiry != w->diry) {
1012 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1015 /* new head position */
1016 w->x[w->head] = headx + w->dirx;
1017 w->y[w->head] = heady + w->diry;
1020 /* while the worm is growing no tail procession is necessary */
1021 if (w->growing > 0) {
1022 /* update the worms grow state */
1023 w->growing--;
1026 /* if the worm isn't growing the tail has to be dragged */
1027 else {
1028 /* index of the end of the tail segment */
1029 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1031 /* drag the end of the tail */
1032 /* only one coordinate has to be altered. Here it is
1033 determined which one */
1034 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1035 if (w->x[w->tail] == w->x[tail_segment_end]) {
1036 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1037 w->y[w->tail] += dir;
1038 } else {
1039 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1040 w->x[w->tail] += dir;
1043 /* when the tail has been dragged so far that it meets
1044 the next segment start the tail segment is obsolete and
1045 must be freed */
1046 if (w->x[w->tail] == w->x[tail_segment_end] &&
1047 w->y[w->tail] == w->y[tail_segment_end]){
1049 /* drop the last tail point */
1050 w->tail = tail_segment_end;
1057 * Draws the head and clears the tail of the worm in
1058 * the display buffer. lcd_update() is NOT called thus
1059 * the caller has to take care that the buffer is displayed.
1061 static void draw_worm(struct worm *w)
1063 #ifdef HAVE_LCD_COLOR
1064 rb->lcd_set_foreground(COLOR_WORM);
1065 #endif
1066 /* draw the new head */
1067 int x = w->x[w->head];
1068 int y = w->y[w->head];
1069 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1070 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1073 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1075 /* clear the space behind the worm */
1076 x = w->x[w->tail] ;
1077 y = w->y[w->tail] ;
1078 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1079 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1081 rb->lcd_set_drawmode(DRMODE_SOLID);
1082 #ifdef HAVE_LCD_COLOR
1083 rb->lcd_set_foreground(COLOR_FG);
1084 #endif
1088 * Checks wether the coordinate is part of the worm. Returns
1089 * true if any part of the worm was hit - including the head.
1090 * @param x int The x coordinate
1091 * @param y int The y coordinate
1092 * @return int The index of the worm arrays that contain x, y.
1093 * Returns -1 if the coordinates are not part of the worm.
1095 static int specific_worm_collision(struct worm *w, int x, int y)
1097 int retVal = -1;
1099 /* get_worm_array_length is expensive -> buffer the value */
1100 int wormLength = get_worm_array_length(w);
1101 int i;
1103 /* test each entry that is part of the worm */
1104 for (i = 0; i < wormLength && retVal == -1; i++) {
1106 /* The iteration iterates the length of the worm.
1107 Here's the conversion to the true indices within the worm arrays. */
1108 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1109 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1110 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1111 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1112 if (samex || samey){
1113 int test, min, max, tmp;
1115 if (samey) {
1116 min = w->x[linestart];
1117 max = w->x[lineend];
1118 test = x;
1119 } else {
1120 min = w->y[linestart];
1121 max = w->y[lineend];
1122 test = y;
1125 tmp = min;
1126 min = MIN(min, max);
1127 max = MAX(tmp, max);
1129 if (min <= test && test <= max) {
1130 retVal = lineend;
1134 return retVal;
1138 * Increases the length of the specified worm by marking
1139 * that it may grow by len pixels. Note that the worm has
1140 * to move to make the growing happen.
1141 * @param worm *w The worm that is to be altered.
1142 * @param int len A positive value specifying the amount of
1143 * pixels the worm may grow.
1145 static void add_growing(struct worm *w, int len) {
1146 w->growing += len;
1150 * Determins the worm that is at the coordinates x, y. The parameter
1151 * w is a switch parameter that changes the functionality of worm_collision.
1152 * If w is specified and x,y hits the head of w NULL is returned.
1153 * This is a useful way to determine wether the head of w hits
1154 * any worm but including itself but excluding its own head.
1155 * (It hits always its own head ;))
1156 * If w is set to NULL worm_collision returns any worm including all heads
1157 * that is at position of x,y.
1158 * @param struct worm *w The worm of which the head should be excluded in
1159 * the test. w may be set to NULL.
1160 * @param int x The x coordinate that is checked
1161 * @param int y The y coordinate that is checkec
1162 * @return struct worm* The worm that has been hit by x,y. If no worm
1163 * was at the position NULL is returned.
1165 static struct worm* worm_collision(struct worm *w, int x, int y){
1166 struct worm *retVal = NULL;
1167 int i;
1168 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1169 int collision_at = specific_worm_collision(&worms[i], x, y);
1170 if (collision_at != -1) {
1171 if (!(w == &worms[i] && collision_at == w->head)){
1172 retVal = &worms[i];
1176 return retVal;
1180 * Returns true if the head of the worm just has
1181 * crossed the field boundaries.
1182 * @return bool true if the worm just has wrapped.
1184 static bool field_collision(struct worm *w)
1186 bool retVal = false;
1187 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1188 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1189 (w->x[w->head] < 0) ||
1190 (w->y[w->head] < 0))
1192 retVal = true;
1194 return retVal;
1199 * Returns true if the specified coordinates are within the
1200 * field specified by the FIELD_RECT_XXX constants.
1201 * @param int x The x coordinate of the point that is investigated
1202 * @param int y The y coordinate of the point that is investigated
1203 * @return bool Returns false if x,y specifies a point outside the
1204 * field of worms.
1206 static bool is_in_field_rect(int x, int y) {
1207 bool retVal = false;
1208 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1209 y >= 0 && y < FIELD_RECT_HEIGHT);
1210 return retVal;
1214 * Checks and returns wether the head of the w
1215 * is colliding with something currently.
1216 * @return int One of the values:
1217 * COLLISION_NONE
1218 * COLLISION_w
1219 * COLLISION_FOOD
1220 * COLLISION_ARGH
1221 * COLLISION_FIELD
1223 static int check_collision(struct worm *w)
1225 int retVal = COLLISION_NONE;
1227 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1228 retVal = COLLISION_WORM;
1230 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1231 retVal = COLLISION_FOOD;
1233 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1234 retVal = COLLISION_ARGH;
1236 if (field_collision(w))
1237 retVal = COLLISION_FIELD;
1239 return retVal;
1243 * Returns the index of the food that is closest to the point
1244 * specified by x, y. This index may be used in the foodx and
1245 * foody arrays.
1246 * @param int x The x coordinate of the point
1247 * @param int y The y coordinate of the point
1248 * @return int A value usable as index in foodx and foody.
1250 static int get_nearest_food(int x, int y){
1251 int nearestfood = 0;
1252 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1253 int deltax = 0;
1254 int deltay = 0;
1255 int foodindex;
1256 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1257 int distance;
1258 deltax = foodx[foodindex] - x;
1259 deltay = foody[foodindex] - y;
1260 deltax = deltax > 0 ? deltax : deltax * (-1);
1261 deltay = deltay > 0 ? deltay : deltay * (-1);
1262 distance = deltax + deltay;
1264 if (distance < olddistance) {
1265 olddistance = distance;
1266 nearestfood = foodindex;
1269 return nearestfood;
1273 * Returns wether the specified position is next to the worm
1274 * and in the direction the worm looks. Use this method to
1275 * test wether this position would be hit with the next move of
1276 * the worm unless the worm changes its direction.
1277 * @param struct worm *w - The worm to be investigated
1278 * @param int x - The x coordinate of the position to test.
1279 * @param int y - The y coordinate of the position to test.
1280 * @return Returns true if the worm will hit the position unless
1281 * it change its direction before the next move.
1283 static bool is_in_front_of_worm(struct worm *w, int x, int y) {
1284 bool infront = false;
1285 int deltax = x - w->x[w->head];
1286 int deltay = y - w->y[w->head];
1288 if (w->dirx == 0) {
1289 infront = (w->diry * deltay) > 0;
1290 } else {
1291 infront = (w->dirx * deltax) > 0;
1293 return infront;
1297 * Returns true if the worm will collide with the next move unless
1298 * it changes its direction.
1299 * @param struct worm *w - The worm to be investigated.
1300 * @return Returns true if the worm will collide with the next move
1301 * unless it changes its direction.
1303 static bool will_worm_collide(struct worm *w) {
1304 int x = w->x[w->head] + w->dirx;
1305 int y = w->y[w->head] + w->diry;
1306 bool retVal = !is_in_field_rect(x, y);
1307 if (!retVal) {
1308 retVal = (argh_collision(x, y) != -1);
1311 if (!retVal) {
1312 retVal = (worm_collision(w, x, y) != NULL);
1314 return retVal;
1318 * This function
1319 * may be used to be stored in worm.fetch_worm_direction for
1320 * worms that are not controlled by humans but by artificial stupidity.
1321 * A direction is searched that doesn't lead to collision but to the nearest
1322 * food - but not very intelligent. The direction is written to the specified
1323 * worm.
1324 * @param struct worm *w - The worm of which the direction
1325 * is altered.
1327 static void virtual_player(struct worm *w) {
1328 bool isright;
1329 int plana, planb, planc;
1330 /* find the next lunch */
1331 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1333 /* determine in which direction it is */
1335 /* in front of me? */
1336 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1338 /* left right of me? */
1339 int olddir = get_worm_dir(w);
1340 set_worm_dir(w, (olddir + 1) % 4);
1341 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1342 set_worm_dir(w, olddir);
1344 /* detect situation, set strategy */
1345 if (infront) {
1346 if (isright) {
1347 plana = olddir;
1348 planb = (olddir + 1) % 4;
1349 planc = (olddir + 3) % 4;
1350 } else {
1351 plana = olddir;
1352 planb = (olddir + 3) % 4;
1353 planc = (olddir + 1) % 4;
1355 } else {
1356 if (isright) {
1357 plana = (olddir + 1) % 4;
1358 planb = olddir;
1359 planc = (olddir + 3) % 4;
1360 } else {
1361 plana = (olddir + 3) % 4;
1362 planb = olddir;
1363 planc = (olddir + 1) % 4;
1367 /* test for collision */
1368 set_worm_dir(w, plana);
1369 if (will_worm_collide(w)){
1371 /* plan b */
1372 set_worm_dir(w, planb);
1374 /* test for collision */
1375 if (will_worm_collide(w)) {
1377 /* plan c */
1378 set_worm_dir(w, planc);
1384 * prints out the score board with all the status information
1385 * about the game.
1387 static void score_board(void)
1389 char buf[15];
1390 char* buf2 = NULL;
1391 int i;
1392 int y = 0;
1393 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1394 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1395 rb->lcd_set_drawmode(DRMODE_SOLID);
1396 for (i = 0; i < worm_count; i++) {
1397 int score = get_score(&worms[i]);
1399 /* high score */
1400 if (worms[i].fetch_worm_direction != virtual_player){
1401 if (highscore < score) {
1402 highscore = score;
1406 /* length */
1407 rb->snprintf(buf, sizeof (buf),"Len:%d", score);
1409 /* worm state */
1410 switch (check_collision(&worms[i])) {
1411 case COLLISION_NONE:
1412 if (worms[i].growing > 0)
1413 buf2 = "Growing";
1414 else {
1415 if (worms[i].alive)
1416 buf2 = "Hungry";
1417 else
1418 buf2 = "Wormed";
1420 break;
1422 case COLLISION_WORM:
1423 buf2 = "Wormed";
1424 break;
1426 case COLLISION_FOOD:
1427 buf2 = "Growing";
1428 break;
1430 case COLLISION_ARGH:
1431 buf2 = "Argh";
1432 break;
1434 case COLLISION_FIELD:
1435 buf2 = "Crashed";
1436 break;
1438 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf);
1439 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2);
1441 if (!worms[i].alive){
1442 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1443 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1444 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1445 rb->lcd_set_drawmode(DRMODE_SOLID);
1447 y += 19;
1449 rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore);
1450 #ifndef DEBUG_WORMLET
1451 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);
1452 #else
1453 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
1454 #endif
1458 * Checks for collisions of the worm and its environment and
1459 * takes appropriate actions like growing the worm or killing it.
1460 * @return bool Returns true if the worm is dead. Returns
1461 * false if the worm is healthy, up and creeping.
1463 static bool process_collisions(struct worm *w)
1465 int index = -1;
1467 w->alive &= !field_collision(w);
1469 if (w->alive) {
1471 /* check if food was eaten */
1472 index = food_collision(w->x[w->head], w->y[w->head]);
1473 if (index != -1){
1474 int i;
1476 clear_food(index);
1477 make_food(index);
1478 draw_food(index);
1480 for (i = 0; i < arghs_per_food; i++) {
1481 argh_count++;
1482 if (argh_count > MAX_ARGH)
1483 argh_count = MAX_ARGH;
1484 make_argh(argh_count - 1);
1485 draw_argh(argh_count - 1);
1488 add_growing(w, worm_food);
1490 draw_worm(w);
1493 /* check if argh was eaten */
1494 else {
1495 index = argh_collision(w->x[w->head], w->y[w->head]);
1496 if (index != -1) {
1497 w->alive = false;
1499 else {
1500 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1501 w->alive = false;
1506 return !w->alive;
1510 * The main loop of the game.
1511 * @return bool Returns true if the game ended
1512 * with a dead worm. Returns false if the user
1513 * aborted the game manually.
1515 static int run(void)
1517 int button = 0;
1518 int wormDead = false;
1519 bool paused = false;
1521 /* ticks are counted to compensate speed variations */
1522 long cycle_start = 0, cycle_end = 0;
1523 #ifdef DEBUG_WORMLET
1524 int ticks_to_max_cycle_reset = 20;
1525 long max_cycle = 0;
1526 char buf[20];
1527 #endif
1529 /* initialize the board and so on */
1530 init_wormlet();
1532 cycle_start = *rb->current_tick;
1533 /* change the direction of the worm */
1534 while (!wormDead)
1536 int i;
1537 long cycle_duration=0;
1539 #ifdef HAS_BUTTON_HOLD
1540 if (rb->button_hold())
1541 paused = true;
1542 #endif
1544 switch (button) {
1545 case BTN_STARTPAUSE:
1546 paused = !paused;
1547 break;
1548 case BTN_STOPRESET:
1549 if (paused)
1550 return 1; /* restart game */
1551 else
1552 paused = true;
1553 break;
1554 #ifdef BTN_RC_QUIT
1555 case BTN_RC_QUIT:
1556 #endif
1557 case BTN_QUIT:
1558 return 2; /* back to menu */
1559 break;
1561 if (!paused)
1563 switch (button) {
1564 case BTN_DIR_UP:
1565 if (players == 1 && !use_remote) {
1566 player1_dir = NORTH;
1568 break;
1570 case BTN_DIR_DOWN:
1571 if (players == 1 && !use_remote) {
1572 player1_dir = SOUTH;
1574 break;
1576 case BTN_DIR_LEFT:
1577 if (players != 1 || use_remote) {
1578 player1_dir = (player1_dir + 3) % 4;
1579 } else {
1580 player1_dir = WEST;
1582 break;
1584 case BTN_DIR_RIGHT:
1585 if (players != 1 || use_remote) {
1586 player1_dir = (player1_dir + 1) % 4;
1587 } else {
1588 player1_dir = EAST;
1590 break;
1592 #ifdef MULTIPLAYER
1593 case BTN_PLAYER2_DIR1:
1594 player2_dir = (player2_dir + 3) % 4;
1595 break;
1597 case BTN_PLAYER2_DIR2:
1598 player2_dir = (player2_dir + 1) % 4;
1599 break;
1600 #endif
1602 #ifdef REMOTE
1603 case BTN_RC_UP:
1604 player3_dir = (player3_dir + 1) % 4;
1605 break;
1607 case BTN_RC_DOWN:
1608 player3_dir = (player3_dir + 3) % 4;
1609 break;
1610 #endif
1614 for (i = 0; i < worm_count; i++) {
1615 worms[i].fetch_worm_direction(&worms[i]);
1618 wormDead = true;
1619 for (i = 0; i < worm_count; i++){
1620 struct worm *w = &worms[i];
1621 move_worm(w);
1622 wormDead &= process_collisions(w);
1623 draw_worm(w);
1625 score_board();
1626 rb->lcd_update();
1627 if (button == BTN_STOPRESET) {
1628 wormDead = true;
1631 /* here the wormlet game cycle ends
1632 thus the current tick is stored
1633 as end time */
1634 cycle_end = *rb->current_tick;
1636 /* The duration of the game cycle */
1637 cycle_duration = cycle_end - cycle_start;
1638 cycle_duration = MAX(0, cycle_duration);
1639 cycle_duration = MIN(speed -1, cycle_duration);
1642 #ifdef DEBUG_WORMLET
1643 ticks_to_max_cycle_reset--;
1644 if (ticks_to_max_cycle_reset <= 0) {
1645 max_cycle = 0;
1648 if (max_cycle < cycle_duration) {
1649 max_cycle = cycle_duration;
1650 ticks_to_max_cycle_reset = 20;
1652 rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle);
1653 set_debug_out(buf);
1654 #endif
1656 /* adjust the number of ticks to wait for a button.
1657 This ensures that a complete game cycle including
1658 user input runs in constant time */
1659 button = rb->button_get_w_tmo(speed - cycle_duration);
1660 cycle_start = *rb->current_tick;
1663 rb->splash(HZ*2, "Game Over!");
1665 return 2; /* back to menu */
1668 #ifdef DEBUG_WORMLET
1671 * Just a test routine that checks that worm_food_collision works
1672 * in some typical situations.
1674 static void test_worm_food_collision(void) {
1675 int collision_count = 0;
1676 int i;
1677 rb->lcd_clear_display();
1678 init_worm(&worms[0], 10, 10);
1679 add_growing(&worms[0], 10);
1680 set_worm_dir(&worms[0], EAST);
1681 for (i = 0; i < 10; i++) {
1682 move_worm(&worms[0]);
1683 draw_worm(&worms[0]);
1686 set_worm_dir(&worms[0], SOUTH);
1687 for (i = 0; i < 10; i++) {
1688 move_worm(&worms[0]);
1689 draw_worm(&worms[0]);
1692 foodx[0] = 15;
1693 foody[0] = 12;
1694 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1695 char buf[20];
1696 bool collision;
1697 draw_worm(&worms[0]);
1698 draw_food(0);
1699 collision = worm_food_collision(&worms[0], 0);
1700 if (collision) {
1701 collision_count++;
1703 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1704 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1705 rb->lcd_update();
1707 if (collision_count != food_size) {
1708 rb->button_get(true);
1712 foody[0] = 15;
1713 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1714 char buf[20];
1715 bool collision;
1716 draw_worm(&worms[0]);
1717 draw_food(0);
1718 collision = worm_food_collision(&worms[0], 0);
1719 if (collision) {
1720 collision_count ++;
1722 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1723 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1724 rb->lcd_update();
1726 if (collision_count != food_size * 2) {
1727 rb->button_get(true);
1732 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){
1733 int x, y;
1734 bool retVal = false;
1735 for (x = rx; x < rx + rw; x++){
1736 for (y = ry; y < ry + rh; y++) {
1737 if (specific_worm_collision(w, x, y) != -1) {
1738 retVal = true;
1742 return retVal;
1745 static void test_worm_argh_collision(void) {
1746 int i;
1747 int dir;
1748 int collision_count = 0;
1749 rb->lcd_clear_display();
1750 init_worm(&worms[0], 10, 10);
1751 add_growing(&worms[0], 40);
1752 for (dir = 0; dir < 4; dir++) {
1753 set_worm_dir(&worms[0], (EAST + dir) % 4);
1754 for (i = 0; i < 10; i++) {
1755 move_worm(&worms[0]);
1756 draw_worm(&worms[0]);
1760 arghx[0] = 12;
1761 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1762 char buf[20];
1763 bool collision;
1764 draw_argh(0);
1765 collision = worm_argh_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 != argh_size * 2) {
1774 rb->button_get(true);
1777 arghy[0] = 12;
1778 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1779 char buf[20];
1780 bool collision;
1781 draw_argh(0);
1782 collision = worm_argh_collision(&worms[0], 0);
1783 if (collision) {
1784 collision_count ++;
1786 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1787 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1788 rb->lcd_update();
1790 if (collision_count != argh_size * 4) {
1791 rb->button_get(true);
1795 static int testline_in_rect(void) {
1796 int testfailed = -1;
1798 int rx = 10;
1799 int ry = 15;
1800 int rw = 20;
1801 int rh = 25;
1803 /* Test 1 */
1804 int x1 = 12;
1805 int y1 = 8;
1806 int x2 = 12;
1807 int y2 = 42;
1809 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1810 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1811 rb->lcd_drawrect(rx, ry, rw, rh);
1812 rb->lcd_drawline(x1, y1, x2, y2);
1813 rb->lcd_update();
1814 rb->lcd_putsxy(0, 0, "failed 1");
1815 rb->button_get(true);
1816 testfailed = 1;
1819 /* test 2 */
1820 y2 = 20;
1821 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1822 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1823 rb->lcd_drawrect(rx, ry, rw, rh);
1824 rb->lcd_drawline(x1, y1, x2, y2);
1825 rb->lcd_putsxy(0, 0, "failed 2");
1826 rb->lcd_update();
1827 rb->button_get(true);
1828 testfailed = 2;
1831 /* test 3 */
1832 y1 = 30;
1833 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1834 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1835 rb->lcd_drawrect(rx, ry, rw, rh);
1836 rb->lcd_drawline(x1, y1, x2, y2);
1837 rb->lcd_putsxy(0, 0, "failed 3");
1838 rb->lcd_update();
1839 rb->button_get(true);
1840 testfailed = 3;
1843 /* test 4 */
1844 y2 = 45;
1845 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1846 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1847 rb->lcd_drawrect(rx, ry, rw, rh);
1848 rb->lcd_drawline(x1, y1, x2, y2);
1849 rb->lcd_putsxy(0, 0, "failed 4");
1850 rb->lcd_update();
1851 rb->button_get(true);
1852 testfailed = 4;
1855 /* test 5 */
1856 y1 = 50;
1857 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1858 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1859 rb->lcd_drawrect(rx, ry, rw, rh);
1860 rb->lcd_drawline(x1, y1, x2, y2);
1861 rb->lcd_putsxy(0, 0, "failed 5");
1862 rb->lcd_update();
1863 rb->button_get(true);
1864 testfailed = 5;
1867 /* test 6 */
1868 y1 = 5;
1869 y2 = 7;
1870 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1871 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1872 rb->lcd_drawrect(rx, ry, rw, rh);
1873 rb->lcd_drawline(x1, y1, x2, y2);
1874 rb->lcd_putsxy(0, 0, "failed 6");
1875 rb->lcd_update();
1876 rb->button_get(true);
1877 testfailed = 6;
1880 /* test 7 */
1881 x1 = 8;
1882 y1 = 20;
1883 x2 = 35;
1884 y2 = 20;
1885 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1886 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1887 rb->lcd_drawrect(rx, ry, rw, rh);
1888 rb->lcd_drawline(x1, y1, x2, y2);
1889 rb->lcd_putsxy(0, 0, "failed 7");
1890 rb->lcd_update();
1891 rb->button_get(true);
1892 testfailed = 7;
1895 /* test 8 */
1896 x2 = 12;
1897 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1898 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1899 rb->lcd_drawrect(rx, ry, rw, rh);
1900 rb->lcd_drawline(x1, y1, x2, y2);
1901 rb->lcd_putsxy(0, 0, "failed 8");
1902 rb->lcd_update();
1903 rb->button_get(true);
1904 testfailed = 8;
1907 /* test 9 */
1908 x1 = 25;
1909 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1910 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1911 rb->lcd_drawrect(rx, ry, rw, rh);
1912 rb->lcd_drawline(x1, y1, x2, y2);
1913 rb->lcd_putsxy(0, 0, "failed 9");
1914 rb->lcd_update();
1915 rb->button_get(true);
1916 testfailed = 9;
1919 /* test 10 */
1920 x2 = 37;
1921 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1922 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1923 rb->lcd_drawrect(rx, ry, rw, rh);
1924 rb->lcd_drawline(x1, y1, x2, y2);
1925 rb->lcd_putsxy(0, 0, "failed 10");
1926 rb->lcd_update();
1927 rb->button_get(true);
1928 testfailed = 10;
1931 /* test 11 */
1932 x1 = 42;
1933 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1934 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1935 rb->lcd_drawrect(rx, ry, rw, rh);
1936 rb->lcd_drawline(x1, y1, x2, y2);
1937 rb->lcd_putsxy(0, 0, "failed 11");
1938 rb->lcd_update();
1939 rb->button_get(true);
1940 testfailed = 11;
1943 /* test 12 */
1944 x1 = 5;
1945 x2 = 7;
1946 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1947 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1948 rb->lcd_drawrect(rx, ry, rw, rh);
1949 rb->lcd_drawline(x1, y1, x2, y2);
1950 rb->lcd_putsxy(0, 0, "failed 12");
1951 rb->lcd_update();
1952 rb->button_get(true);
1953 testfailed = 12;
1956 /* test 13 */
1957 rx = 9;
1958 ry = 15;
1959 rw = food_size;
1960 rh = food_size;
1962 x1 = 10;
1963 y1 = 10;
1964 x2 = 10;
1965 y2 = 20;
1966 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1967 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
1968 rb->lcd_drawrect(rx, ry, rw, rh);
1969 rb->lcd_drawline(x1, y1, x2, y2);
1970 rb->lcd_putsxy(0, 0, "failed 13");
1971 rb->lcd_update();
1972 rb->button_get(true);
1973 testfailed = 13;
1976 /* test 14 */
1977 rx = 9;
1978 ry = 15;
1979 rw = 4;
1980 rh = 4;
1982 x1 = 10;
1983 y1 = 10;
1984 x2 = 10;
1985 y2 = 19;
1986 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1987 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
1988 rb->lcd_drawline(x1, y1, x2, y2);
1989 rb->lcd_invertrect(rx, ry, rw, rh);
1990 rb->lcd_putsxy(0, 0, "failed 14");
1991 rb->lcd_update();
1992 rb->button_get(true);
1993 testfailed = 14;
1996 rb->lcd_clear_display();
1998 return testfailed;
2002 * Just a test routine to test wether specific_worm_collision might work properly
2004 static int test_specific_worm_collision(void) {
2005 int collisions = 0;
2006 int dir;
2007 int x = 0;
2008 int y = 0;
2009 char buf[20];
2010 rb->lcd_clear_display();
2011 init_worm(&worms[0], 10, 20);
2012 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2014 for (dir = EAST; dir < EAST + 4; dir++) {
2015 int i;
2016 set_worm_dir(&worms[0], dir % 4);
2017 for (i = 0; i < 5; i++) {
2018 if (!(dir % 4 == NORTH && i == 9)) {
2019 move_worm(&worms[0]);
2020 draw_worm(&worms[0]);
2025 for (y = 15; y < 30; y ++){
2026 for (x = 5; x < 20; x++) {
2027 if (specific_worm_collision(&worms[0], x, y) != -1) {
2028 collisions ++;
2030 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2031 rb->snprintf(buf, sizeof buf, "collisions %d", collisions);
2032 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2033 rb->lcd_update();
2036 if (collisions != 21) {
2037 rb->button_get(true);
2039 return collisions;
2042 static void test_make_argh(void){
2043 int dir;
2044 int seed = 0;
2045 int hit = 0;
2046 int failures = 0;
2047 int last_failures = 0;
2048 int i, worm_idx;
2049 rb->lcd_clear_display();
2050 worm_count = 3;
2052 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2053 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2054 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2057 for (dir = EAST; dir < EAST + 4; dir++) {
2058 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2059 set_worm_dir(&worms[worm_idx], dir % 4);
2060 for (i = 0; i < 10; i++) {
2061 if (!(dir % 4 == NORTH && i == 9)) {
2062 move_worm(&worms[worm_idx]);
2063 draw_worm(&worms[worm_idx]);
2069 rb->lcd_update();
2071 for (seed = 0; hit < 20; seed += 2) {
2072 char buf[20];
2073 int x, y;
2074 rb->srand(seed);
2075 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2076 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2078 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2079 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2080 int tries = 0;
2081 rb->srand(seed);
2083 tries = make_argh(0);
2084 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2085 failures ++;
2088 rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d", x, y, failures, tries);
2089 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2090 rb->lcd_update();
2091 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, argh_size, argh_size);
2092 rb->lcd_update();
2093 draw_argh(0);
2094 rb->lcd_update();
2095 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, argh_size, argh_size);
2096 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, argh_size, argh_size);
2098 if (failures > last_failures) {
2099 rb->button_get(true);
2101 last_failures = failures;
2102 hit ++;
2108 static void test_worm_argh_collision_in_moves(void) {
2109 int hit_count = 0;
2110 int i;
2111 rb->lcd_clear_display();
2112 init_worm(&worms[0], 10, 20);
2114 arghx[0] = 20;
2115 arghy[0] = 18;
2116 draw_argh(0);
2118 set_worm_dir(&worms[0], EAST);
2119 for (i = 0; i < 20; i++) {
2120 char buf[20];
2121 move_worm(&worms[0]);
2122 draw_worm(&worms[0]);
2123 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2124 hit_count ++;
2126 rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
2127 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2128 rb->lcd_update();
2130 if (hit_count != argh_size + 5) {
2131 rb->button_get(true);
2134 #endif /* DEBUG_WORMLET */
2136 extern bool use_old_rect;
2139 * These are additional functions required to set various wormlet settings
2140 * and to enable saving of settings and high score
2144 Sets the total number of worms, both human and machine
2146 bool set_worm_num_worms(void)
2148 bool ret;
2149 static const struct opt_items num_worms_option[3] = {
2150 { "1", -1 },
2151 { "2", -1 },
2152 { "3", -1 },
2155 ret = rb->set_option("Number Of Worms", &worm_count,INT, num_worms_option, 3, NULL);
2156 if (worm_count < players) {
2157 worm_count=players;
2159 return ret;
2163 Sets the number of human players
2165 bool set_worm_num_players(void)
2167 bool ret;
2168 static const struct opt_items num_players_option[4] = {
2169 { "0", -1 },
2170 { "1", -1 },
2171 { "2", -1 },
2172 { "3", -1 }
2175 ret = rb->set_option("Number of Players", &players, INT,num_players_option , 4, NULL);
2176 if (players > worm_count) {
2177 worm_count = players;
2179 if (players > 2) {
2180 use_remote = true;
2182 return ret;
2186 Sets the size of each argh block created
2188 bool set_worm_argh_size(void)
2190 static const struct opt_items argh_size_option[11] = {
2191 { "0", -1 },
2192 { "1", -1 },
2193 { "2", -1 },
2194 { "3", -1 },
2195 { "4", -1 },
2196 { "5", -1 },
2197 { "6", -1 },
2198 { "7", -1 },
2199 { "8", -1 },
2200 { "9", -1 },
2201 { "10", -1 }
2204 return rb->set_option("Argh Size", &argh_size,INT,argh_size_option , 11, NULL);
2208 Sets the amount a worm grows per food
2210 bool set_worm_food(void)
2212 static const struct opt_items worm_food_option[11] = {
2213 { "0", -1 },
2214 { "1", -1 },
2215 { "2", -1 },
2216 { "3", -1 },
2217 { "4", -1 },
2218 { "5", -1 },
2219 { "6", -1 },
2220 { "7", -1 },
2221 { "8", -1 },
2222 { "9", -1 },
2223 { "10", -1 }
2225 return rb->set_option("Worm Growth Per Food", &worm_food,INT,worm_food_option , 11, NULL);
2229 Sets the number of arghs created per food
2231 bool set_argh_per_food(void)
2233 static const struct opt_items argh_food_option[5] = {
2234 { "0", -1 },
2235 { "1", -1 },
2236 { "2", -1 },
2237 { "3", -1 },
2238 { "4", -1 },
2240 return rb->set_option("Arghs Per Food", &arghs_per_food,INT,argh_food_option , 5, NULL);
2244 Sets the number of ticks per move
2246 bool set_worm_speed(void)
2248 bool ret;
2249 static const struct opt_items speed_option[19] = {
2250 { "0", -1 },
2251 { "1", -1 },
2252 { "2", -1 },
2253 { "3", -1 },
2254 { "4", -1 },
2255 { "5", -1 },
2256 { "6", -1 },
2257 { "7", -1 },
2258 { "8", -1 },
2259 { "9", -1 },
2260 { "10", -1 },
2261 { "11", -1 },
2262 { "12", -1 },
2263 { "13", -1 },
2264 { "14", -1 },
2265 { "15", -1 },
2266 { "16", -1 },
2267 { "17", -1 },
2268 { "18", -1 },
2272 speed = 20 - speed;
2273 ret = rb->set_option("Worm Speed", &speed,INT,speed_option , 19, NULL);
2274 speed = 20 - speed;
2275 return ret;
2279 Sets the control style, which depends on the number of players,
2280 remote control existing, etc
2281 bool set_bool_options(char* string, bool* variable,
2282 char* yes_str, char* no_str, void (*function)(bool))
2284 bool set_worm_control_style(void)
2286 static const struct opt_items key1_option[2] = {
2287 { "Remote Control", -1 },
2288 { "No Rem. Control", -1 },
2291 static const struct opt_items key2_option[2] = {
2292 { "2 Key Control", -1 },
2293 { "4 Key COntrol", -1 },
2296 static const struct opt_items key3_option[1] = {
2297 { "Out of Control", -1 },
2300 if (players > 1) {
2301 rb->set_option("Control Style",&use_remote,INT, key1_option, 2, NULL);
2302 } else {
2303 if (players > 0) {
2304 rb->set_option("Control Style",&use_remote,INT, key2_option, 2, NULL);
2306 else {
2307 rb->set_option("Control Style",&use_remote,INT, key3_option, 1, NULL);
2310 return false;
2313 void default_settings(void)
2315 arghs_per_food = ARGHS_PER_FOOD;
2316 argh_size = ARGH_SIZE;
2317 food_size = FOOD_SIZE;
2318 speed = SPEED;
2319 worm_food = WORM_PER_FOOD;
2320 players = 1;
2321 worm_count = MAX_WORMS;
2322 use_remote = false;
2323 return;
2327 Launches the wormlet game
2329 bool launch_wormlet(void)
2331 int game_result = 1;
2333 rb->lcd_clear_display();
2335 /* Permanently enable the backlight (unless the user has turned it off) */
2336 if (rb->global_settings->backlight_timeout > 0)
2337 rb->backlight_set_timeout(1);
2339 /* start the game */
2340 while (game_result == 1)
2341 game_result = run();
2343 switch (game_result)
2345 case 2:
2346 /* Restore user's original backlight setting */
2347 rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
2348 return false;
2349 break;
2351 return false;
2354 /* End of settings/changes etc */
2357 * Main entry point
2359 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
2361 int result;
2362 int menu_quit = 0;
2363 int new_setting;
2365 (void)(parameter);
2366 rb = api;
2368 default_settings();
2369 configfile_init(rb);
2370 if (configfile_load(SETTINGS_FILENAME, config,
2371 sizeof(config)/sizeof(*config),
2372 SETTINGS_MIN_VERSION ) < 0)
2374 /* If the loading failed, save a new config file (as the disk is
2375 already spinning) */
2376 configfile_save(SETTINGS_FILENAME, config,
2377 sizeof(config)/sizeof(*config),
2378 SETTINGS_VERSION);
2381 #ifdef HAVE_LCD_COLOR
2382 rb->lcd_set_foreground(COLOR_FG);
2383 rb->lcd_set_background(COLOR_BG);
2384 #endif
2386 #if LCD_DEPTH > 1
2387 rb->lcd_set_backdrop(NULL);
2388 #endif
2390 #ifdef DEBUG_WORMLET
2391 testline_in_rect();
2392 test_worm_argh_collision_in_moves();
2393 test_make_argh();
2394 test_worm_food_collision();
2395 test_worm_argh_collision();
2396 test_specific_worm_collision();
2397 #endif
2399 /* Setup screen */
2401 static const struct opt_items noyes[2] = {
2402 { "No", -1 },
2403 { "Yes", -1 },
2406 static const struct opt_items num_worms_option[3] = {
2407 { "1", -1 },
2408 { "2", -1 },
2409 { "3", -1 }
2412 #ifdef MULTIPLAYER
2413 static const struct opt_items num_players_option[4] = {
2414 #else
2415 static const struct opt_items num_players_option[2] = {
2416 #endif
2417 { "0", -1 },
2418 { "1", -1 }
2419 #ifdef MULTIPLAYER
2420 ,{ "2", -1 },
2421 { "3", -1 }
2422 #endif
2425 static const struct opt_items argh_size_option[9] = {
2426 { "2", -1 },
2427 { "3", -1 },
2428 { "4", -1 },
2429 { "5", -1 },
2430 { "6", -1 },
2431 { "7", -1 },
2432 { "8", -1 },
2433 { "9", -1 },
2434 { "10", -1 }
2437 static const struct opt_items food_size_option[9] = {
2438 { "2", -1 },
2439 { "3", -1 },
2440 { "4", -1 },
2441 { "5", -1 },
2442 { "6", -1 },
2443 { "7", -1 },
2444 { "8", -1 },
2445 { "9", -1 },
2446 { "10", -1 }
2449 static const struct opt_items worm_food_option[16] = {
2450 { "0", -1 },
2451 { "1", -1 },
2452 { "2", -1 },
2453 { "3", -1 },
2454 { "4", -1 },
2455 { "5", -1 },
2456 { "6", -1 },
2457 { "7", -1 },
2458 { "8", -1 },
2459 { "9", -1 },
2460 { "10", -1 },
2461 { "11", -1 },
2462 { "12", -1 },
2463 { "13", -1 },
2464 { "14", -1 },
2465 { "15", -1 }
2468 static const struct opt_items argh_food_option[9] = {
2469 { "0", -1 },
2470 { "1", -1 },
2471 { "2", -1 },
2472 { "3", -1 },
2473 { "4", -1 },
2474 { "5", -1 },
2475 { "6", -1 },
2476 { "7", -1 },
2477 { "8", -1 }
2480 static const struct opt_items speed_option[21] = {
2481 { "0", -1 },
2482 { "1", -1 },
2483 { "2", -1 },
2484 { "3", -1 },
2485 { "4", -1 },
2486 { "5", -1 },
2487 { "6", -1 },
2488 { "7", -1 },
2489 { "8", -1 },
2490 { "9", -1 },
2491 { "10", -1 },
2492 { "11", -1 },
2493 { "12", -1 },
2494 { "13", -1 },
2495 { "14", -1 },
2496 { "15", -1 },
2497 { "16", -1 },
2498 { "17", -1 },
2499 { "18", -1 },
2500 { "19", -1 },
2501 { "20", -1 }
2504 static const struct opt_items remoteonly_option[1] = {
2505 { "Remote Control", -1 }
2508 static const struct opt_items key24_option[2] = {
2509 { "4 Key Control", -1 },
2510 { "2 Key Control", -1 }
2513 #ifdef REMOTE
2514 static const struct opt_items remote_option[2] = {
2515 { "Remote Control", -1 },
2516 { "No Rem. Control", -1 }
2518 #else
2519 static const struct opt_items key2_option[1] = {
2520 { "2 Key Control", -1 }
2522 #endif
2524 static const struct opt_items nokey_option[1] = {
2525 { "Out of Control", -1 }
2528 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, "Play Wormlet!",
2529 "Number of Worms", "Number of Players", "Control Style",
2530 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2531 "Argh Size","Food Size","Revert to Default Settings",
2532 "Quit");
2534 rb->button_clear_queue();
2536 while (!menu_quit) {
2537 switch(rb->do_menu(&menu, &result))
2539 case 0:
2540 rb->lcd_setfont(FONT_SYSFIXED);
2541 launch_wormlet();
2542 break;
2543 case 1:
2544 new_setting = worm_count - 1;
2545 rb->set_option("Number of Worms", &new_setting, INT, num_worms_option, 3, NULL);
2546 if (new_setting != worm_count)
2547 worm_count = new_setting + 1;
2548 if (worm_count < players) {
2549 worm_count = players;
2551 break;
2552 case 2:
2553 new_setting = players;
2554 #ifdef MULTIPLAYER
2555 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 4, NULL);
2556 #else
2557 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 2, NULL);
2558 #endif
2559 if (new_setting != players)
2560 players = new_setting;
2561 if (players > worm_count) {
2562 worm_count = players;
2564 if (players > 2) {
2565 use_remote = true;
2567 break;
2568 case 3:
2569 new_setting = use_remote;
2570 switch(players) {
2571 case 0:
2572 rb->set_option("Control Style",&new_setting,INT, nokey_option, 1, NULL);
2573 break;
2574 case 1:
2575 rb->set_option("Control Style",&new_setting,INT, key24_option, 2, NULL);
2576 break;
2577 case 2:
2578 #ifdef REMOTE
2579 rb->set_option("Control Style",&new_setting,INT, remote_option, 2, NULL);
2580 #else
2581 rb->set_option("Control Style",&new_setting,INT, key2_option, 1, NULL);
2582 #endif
2583 break;
2584 case 3:
2585 rb->set_option("Control Style",&new_setting,INT, remoteonly_option, 1, NULL);
2586 break;
2588 if (new_setting != use_remote)
2589 use_remote = new_setting;
2590 break;
2591 case 4:
2592 new_setting = worm_food;
2593 rb->set_option("Worm Growth Per Food", &new_setting,INT,worm_food_option , 16, NULL);
2594 if (new_setting != worm_food)
2595 worm_food = new_setting;
2596 break;
2597 case 5:
2598 new_setting = speed;
2599 new_setting = 20 - new_setting;
2600 rb->set_option("Worm Speed", &new_setting,INT,speed_option , 21, NULL);
2601 new_setting = 20 - new_setting;
2602 if (new_setting != speed)
2603 speed = new_setting;
2604 break;
2605 case 6:
2606 new_setting = arghs_per_food;
2607 rb->set_option("Arghs Per Food", &new_setting,INT,argh_food_option , 9, NULL);
2608 if (new_setting != arghs_per_food)
2609 arghs_per_food = new_setting;
2610 break;
2611 case 7:
2612 new_setting = argh_size-2;
2613 rb->set_option("Argh Size", &new_setting,INT,argh_size_option , 9, NULL);
2614 if (new_setting != argh_size)
2615 argh_size = new_setting+2;
2616 break;
2617 case 8:
2618 new_setting = food_size-2;
2619 rb->set_option("Food Size", &new_setting,INT,food_size_option, 9, NULL);
2620 if (new_setting != food_size)
2621 food_size = new_setting+2;
2622 break;
2623 case 9:
2624 new_setting = 0;
2625 rb->set_option("Reset Settings?", &new_setting,INT, noyes , 2, NULL);
2626 if (new_setting == 1)
2627 default_settings();
2628 break;
2629 default:
2630 menu_quit=1;
2631 break;
2635 configfile_save(SETTINGS_FILENAME, config,
2636 sizeof(config)/sizeof(*config),
2637 SETTINGS_VERSION);
2639 return PLUGIN_OK;