Hide symbols by default on 64 bit sim buildsto avoid clashing, fixes crashing on...
[kugel-rb.git] / apps / plugins / wormlet.c
blobc05918e9b435167b1499b7f36727b714b965a93d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Philipp Pertermann
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "plugin.h"
20 #include "configfile.h"
21 #include "helper.h"
23 PLUGIN_HEADER
25 /* size of the field the worm lives in */
26 #define FIELD_RECT_X 1
27 #define FIELD_RECT_Y 1
28 #define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
29 #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
31 /* when the game starts */
32 #define INITIAL_WORM_LENGTH 10
34 /* num of pixel the worm grows per eaten food */
35 #define WORM_PER_FOOD 7
37 /* num of worms creeping in the FIELD */
38 #define MAX_WORMS 3
40 /* minimal distance between a worm and an argh
41 when a new argh is made */
42 #define MIN_ARGH_DIST 5
44 #if (CONFIG_KEYPAD == RECORDER_PAD)
45 #define BTN_DIR_UP BUTTON_UP
46 #define BTN_DIR_DOWN BUTTON_DOWN
47 #define BTN_DIR_LEFT BUTTON_LEFT
48 #define BTN_DIR_RIGHT BUTTON_RIGHT
49 #define BTN_PLAYER2_DIR1 BUTTON_F2
50 #define BTN_PLAYER2_DIR2 BUTTON_F3
51 #define BTN_STARTPAUSE BUTTON_PLAY
52 #define BTN_QUIT BUTTON_OFF
53 #define BTN_STOPRESET BUTTON_ON
54 #define BTN_TOGGLE_KEYS BUTTON_F1
56 #if BUTTON_REMOTE != 0
57 #define BTN_RC_UP BUTTON_RC_VOL_UP
58 #define BTN_RC_DOWN BUTTON_RC_VOL_DOWN
59 #define REMOTE
60 #define MULTIPLAYER
61 #endif
63 #define PLAYERS_TEXT "UP/DN"
64 #define WORMS_TEXT "L/R"
65 #define KEY_CONTROL_TEXT "F1"
67 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
68 #define BTN_DIR_UP BUTTON_UP
69 #define BTN_DIR_DOWN BUTTON_DOWN
70 #define BTN_DIR_LEFT BUTTON_LEFT
71 #define BTN_DIR_RIGHT BUTTON_RIGHT
72 #define BTN_PLAYER2_DIR1 BUTTON_F2
73 #define BTN_PLAYER2_DIR2 BUTTON_F3
74 #define BTN_STARTPAUSE BUTTON_SELECT
75 #define BTN_QUIT BUTTON_OFF
76 #define BTN_STOPRESET BUTTON_ON
77 #define BTN_TOGGLE_KEYS BUTTON_F1
79 #define PLAYERS_TEXT "UP/DN"
80 #define WORMS_TEXT "L/R"
81 #define KEY_CONTROL_TEXT "F1"
83 #elif (CONFIG_KEYPAD == ONDIO_PAD)
84 #define BTN_DIR_UP BUTTON_UP
85 #define BTN_DIR_DOWN BUTTON_DOWN
86 #define BTN_DIR_LEFT BUTTON_LEFT
87 #define BTN_DIR_RIGHT BUTTON_RIGHT
88 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
89 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
90 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
92 #define PLAYERS_TEXT "UP/DN"
93 #define WORMS_TEXT "L/R"
95 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
96 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
98 #define BTN_DIR_UP BUTTON_MENU
99 #define BTN_DIR_DOWN BUTTON_PLAY
100 #define BTN_DIR_LEFT BUTTON_LEFT
101 #define BTN_DIR_RIGHT BUTTON_RIGHT
102 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
103 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
104 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
106 #define PLAYERS_TEXT "Menu/Play"
107 #define WORMS_TEXT "Left/Right"
109 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
111 #define BTN_DIR_UP BUTTON_UP
112 #define BTN_DIR_DOWN BUTTON_DOWN
113 #define BTN_DIR_LEFT BUTTON_LEFT
114 #define BTN_DIR_RIGHT BUTTON_RIGHT
115 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
116 #define BTN_QUIT BUTTON_OFF
117 #define BTN_STOPRESET BUTTON_ON
119 #define BTN_RC_QUIT BUTTON_RC_STOP
121 #define PLAYERS_TEXT "Up/Down"
122 #define WORMS_TEXT "Left/Right"
124 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
126 #define BTN_DIR_UP BUTTON_UP
127 #define BTN_DIR_DOWN BUTTON_DOWN
128 #define BTN_DIR_LEFT BUTTON_LEFT
129 #define BTN_DIR_RIGHT BUTTON_RIGHT
130 #define BTN_STARTPAUSE BUTTON_PLAY
131 #define BTN_QUIT BUTTON_POWER
132 #define BTN_STOPRESET BUTTON_REC
134 #define PLAYERS_TEXT "Up/Down"
135 #define WORMS_TEXT "Left/Right"
137 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
139 #define BTN_DIR_UP BUTTON_UP
140 #define BTN_DIR_DOWN BUTTON_DOWN
141 #define BTN_DIR_LEFT BUTTON_LEFT
142 #define BTN_DIR_RIGHT BUTTON_RIGHT
143 #define BTN_STARTPAUSE BUTTON_SELECT
144 #define BTN_QUIT BUTTON_POWER
145 #define BTN_STOPRESET BUTTON_A
147 #define PLAYERS_TEXT "Up/Down"
148 #define WORMS_TEXT "Left/Right"
151 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
152 (CONFIG_KEYPAD == SANSA_C200_PAD)
154 #define BTN_DIR_UP BUTTON_UP
155 #define BTN_DIR_DOWN BUTTON_DOWN
156 #define BTN_DIR_LEFT BUTTON_LEFT
157 #define BTN_DIR_RIGHT BUTTON_RIGHT
158 #define BTN_STARTPAUSE BUTTON_SELECT
159 #define BTN_QUIT BUTTON_POWER
160 #define BTN_STOPRESET BUTTON_REC
162 #define PLAYERS_TEXT "Up/Down"
163 #define WORMS_TEXT "Left/Right"
166 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
168 #define BTN_DIR_UP BUTTON_SCROLL_UP
169 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
170 #define BTN_DIR_LEFT BUTTON_LEFT
171 #define BTN_DIR_RIGHT BUTTON_RIGHT
172 #define BTN_STARTPAUSE BUTTON_PLAY
173 #define BTN_QUIT BUTTON_POWER
174 #define BTN_STOPRESET BUTTON_REW
176 #define PLAYERS_TEXT "Up/Down"
177 #define WORMS_TEXT "Left/Right"
179 #endif
181 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
182 #define FOOD_SIZE 3
183 #define ARGH_SIZE 4
184 #define SPEED 14
185 #define MAX_WORM_SEGMENTS 128
186 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
187 #define FOOD_SIZE 3
188 #define ARGH_SIZE 4
189 #define SPEED 14
190 #define MAX_WORM_SEGMENTS 128
191 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
192 #define FOOD_SIZE 4
193 #define ARGH_SIZE 5
194 #define SPEED 10
195 #define MAX_WORM_SEGMENTS 128
196 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
197 #define FOOD_SIZE 4
198 #define ARGH_SIZE 5
199 #define SPEED 9
200 #define MAX_WORM_SEGMENTS 128
201 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
202 #define FOOD_SIZE 4
203 #define ARGH_SIZE 5
204 #define SPEED 8
205 #define MAX_WORM_SEGMENTS 256
206 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
207 #define FOOD_SIZE 4
208 #define ARGH_SIZE 5
209 #define SPEED 6
210 #define MAX_WORM_SEGMENTS 256
211 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
212 #define FOOD_SIZE 5
213 #define ARGH_SIZE 6
214 #define SPEED 4
215 #define MAX_WORM_SEGMENTS 512
216 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
217 #define FOOD_SIZE 5
218 #define ARGH_SIZE 6
219 #define SPEED 4
220 #define MAX_WORM_SEGMENTS 512
221 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
222 #define FOOD_SIZE 7
223 #define ARGH_SIZE 8
224 #define SPEED 4
225 #define MAX_WORM_SEGMENTS 512
226 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
227 #define FOOD_SIZE 7
228 #define ARGH_SIZE 8
229 #define SPEED 4
230 #define MAX_WORM_SEGMENTS 512
231 #endif
233 #ifdef HAVE_LCD_COLOR
234 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
235 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
236 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
237 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
238 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
239 #endif
242 * All the properties that a worm has.
244 static struct worm {
245 /* The worm is stored in a ring of xy coordinates */
246 int x[MAX_WORM_SEGMENTS];
247 int y[MAX_WORM_SEGMENTS];
249 int head; /* index of the head within the buffer */
250 int tail; /* index of the tail within the buffer */
251 int growing; /* number of cyles the worm still keeps growing */
252 bool alive; /* the worms living state */
254 /* direction vector in which the worm moves */
255 int dirx; /* only values -1 0 1 allowed */
256 int diry; /* only values -1 0 1 allowed */
258 /* this method is used to fetch the direction the user
259 has selected. It can be one of the values
260 human_player1, human_player2, remote_player, virtual_player.
261 All these values are fuctions, that can change the direction
262 of the worm */
263 void (*fetch_worm_direction)(struct worm *w);
264 } worms[MAX_WORMS];
266 /* stores the highscore - besides it was scored by a virtual player */
267 static int highscore;
269 #define MAX_FOOD 5 /* maximal number of food items */
271 /* The arrays store the food coordinates */
272 static int foodx[MAX_FOOD];
273 static int foody[MAX_FOOD];
275 #define MAX_ARGH 100 /* maximal number of argh items */
276 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
278 /* The arrays store the argh coordinates */
279 static int arghx[MAX_ARGH];
280 static int arghy[MAX_ARGH];
282 /* the number of arghs that are currently in use */
283 static int argh_count;
285 /* the number of arghs per food, settable by user */
286 static int arghs_per_food = ARGHS_PER_FOOD;
287 /* the size of the argh, settable by user */
288 static int argh_size = ARGH_SIZE;
289 /* the size of the food, settable by user */
290 static int food_size = FOOD_SIZE;
291 /* the speed of the worm, settable by user */
292 static int speed = SPEED;
293 /* the amount a worm grows by eating a food, settable by user */
294 static int worm_food = WORM_PER_FOOD;
296 /* End additional variables */
298 #ifdef DEBUG_WORMLET
299 /* just a buffer used for debug output */
300 static char debugout[15];
301 #endif
303 /* the number of active worms (dead or alive) */
304 static int worm_count = MAX_WORMS;
306 /* in multiplayer mode: en- / disables the remote worm control
307 in singleplayer mode: toggles 4 / 2 button worm control */
308 static bool use_remote = false;
310 /* return values of check_collision */
311 #define COLLISION_NONE 0
312 #define COLLISION_WORM 1
313 #define COLLISION_FOOD 2
314 #define COLLISION_ARGH 3
315 #define COLLISION_FIELD 4
317 /* constants for use as directions.
318 Note that the values are ordered clockwise.
319 Thus increasing / decreasing the values
320 is equivalent to right / left turns. */
321 #define WEST 0
322 #define NORTH 1
323 #define EAST 2
324 #define SOUTH 3
326 /* direction of human player 1 */
327 static int player1_dir = EAST;
328 /* direction of human player 2 */
329 static int player2_dir = EAST;
330 /* direction of human player 3 */
331 static int player3_dir = EAST;
333 /* the number of (human) players that currently
334 control a worm */
335 static int players = 1;
337 /* the rockbox plugin api */
338 static struct plugin_api* rb;
340 #define SETTINGS_VERSION 1
341 #define SETTINGS_MIN_VERSION 1
342 #define SETTINGS_FILENAME "wormlet.cfg"
344 static struct configdata config[] =
346 {TYPE_INT, 0, 1024, &highscore, "highscore", NULL, NULL},
347 {TYPE_INT, 0, 15, &arghs_per_food, "arghs per food", NULL, NULL},
348 {TYPE_INT, 0, 15, &argh_size, "argh size", NULL, NULL},
349 {TYPE_INT, 0, 15, &food_size, "food size", NULL, NULL},
350 {TYPE_INT, 0, 3, &players, "players", NULL, NULL},
351 {TYPE_INT, 0, 3, &worm_count, "worms", NULL, NULL},
352 {TYPE_INT, 0, 20, &speed, "speed", NULL, NULL},
353 {TYPE_INT, 0, 15, &worm_food, "Worm Growth Per Food", NULL, NULL}//,
354 //{TYPE_INT, 0, 3, &use_remote, "use remote", NULL, NULL}
357 #ifdef DEBUG_WORMLET
358 static void set_debug_out(char *str){
359 strcpy(debugout, str);
361 #endif
364 * Returns the direction id in which the worm
365 * currently is creeping.
366 * @param struct worm *w The worm that is to be investigated.
367 * w Must not be null.
368 * @return int A value 0 <= value < 4
369 * Note the predefined constants NORTH, SOUTH, EAST, WEST
371 static int get_worm_dir(struct worm *w) {
372 int retVal ;
373 if (w->dirx == 0) {
374 if (w->diry == 1) {
375 retVal = SOUTH;
376 } else {
377 retVal = NORTH;
379 } else {
380 if (w->dirx == 1) {
381 retVal = EAST;
382 } else {
383 retVal = WEST;
386 return retVal;
390 * Set the direction of the specified worm with a direction id.
391 * Increasing the value by 1 means to turn the worm direction
392 * to right by 90 degree.
393 * @param struct worm *w The worm that is to be altered. w Must not be null.
394 * @param int dir The new direction in which the worm is to creep.
395 * dir must be 0 <= dir < 4. Use predefined constants
396 * NORTH, SOUTH, EAST, WEST
398 static void set_worm_dir(struct worm *w, int dir) {
399 switch (dir) {
400 case WEST:
401 w->dirx = -1;
402 w->diry = 0;
403 break;
404 case NORTH:
405 w->dirx = 0;
406 w->diry = - 1;
407 break;
408 case EAST:
409 w->dirx = 1;
410 w->diry = 0;
411 break;
412 case SOUTH:
413 w->dirx = 0;
414 w->diry = 1;
415 break;
420 * Returns the current length of the worm array. This
421 * is also a value for the number of bends that are in the worm.
422 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
424 static int get_worm_array_length(struct worm *w) {
425 /* initial simple calculation will be overwritten if wrong. */
426 int retVal = w->head - w->tail;
428 /* if the worm 'crosses' the boundaries of the ringbuffer */
429 if (retVal < 0) {
430 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
433 return retVal;
437 * Returns the score the specified worm. The score is the length
438 * of the worm.
439 * @param struct worm *w The worm that is to be investigated.
440 * w must not be null.
441 * @return int The length of the worm (>= 0).
443 static int get_score(struct worm *w) {
444 int retval = 0;
445 int length = get_worm_array_length(w);
446 int i;
447 for (i = 0; i < length; i++) {
449 /* The iteration iterates the length of the worm.
450 Here's the conversion to the true indices within the worm arrays. */
451 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
452 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
453 int startx = w->x[linestart];
454 int starty = w->y[linestart];
455 int endx = w->x[lineend];
456 int endy = w->y[lineend];
458 int minimum, maximum;
460 if (startx == endx) {
461 minimum = MIN(starty, endy);
462 maximum = MAX(starty, endy);
463 } else {
464 minimum = MIN(startx, endx);
465 maximum = MAX(startx, endx);
467 retval += abs(maximum - minimum);
469 return retval;
473 * Determines wether the line specified by startx, starty, endx, endy intersects
474 * the rectangle specified by x, y, width, height. Note that the line must be exactly
475 * horizontal or vertical (startx == endx or starty == endy).
476 * @param int startx The x coordinate of the start point of the line.
477 * @param int starty The y coordinate of the start point of the line.
478 * @param int endx The x coordinate of the end point of the line.
479 * @param int endy The y coordinate of the end point of the line.
480 * @param int x The x coordinate of the top left corner of the rectangle.
481 * @param int y The y coordinate of the top left corner of the rectangle.
482 * @param int width The width of the rectangle.
483 * @param int height The height of the rectangle.
484 * @return bool Returns true if the specified line intersects with the recangle.
486 static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) {
487 bool retval = false;
488 int simple, simplemin, simplemax;
489 int compa, compb, compmin, compmax;
490 int temp;
491 if (startx == endx) {
492 simple = startx;
493 simplemin = x;
494 simplemax = x + width;
496 compa = starty;
497 compb = endy;
498 compmin = y;
499 compmax = y + height;
500 } else {
501 simple = starty;
502 simplemin = y;
503 simplemax = y + height;
505 compa = startx;
506 compb = endx;
507 compmin = x;
508 compmax = x + width;
511 temp = compa;
512 compa = MIN(compa, compb);
513 compb = MAX(temp, compb);
515 if (simplemin <= simple && simple <= simplemax) {
516 if ((compmin <= compa && compa <= compmax) ||
517 (compmin <= compb && compb <= compmax) ||
518 (compa <= compmin && compb >= compmax)) {
519 retval = true;
522 return retval;
526 * Tests wether the specified worm intersects with the rect.
527 * @param struct worm *w The worm to be investigated
528 * @param int x The x coordinate of the top left corner of the rect
529 * @param int y The y coordinate of the top left corner of the rect
530 * @param int widht The width of the rect
531 * @param int height The height of the rect
532 * @return bool Returns true if the worm intersects with the rect
534 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) {
535 bool retval = false;
538 /* get_worm_array_length is expensive -> buffer the value */
539 int wormLength = get_worm_array_length(w);
540 int i;
542 /* test each entry that is part of the worm */
543 for (i = 0; i < wormLength && retval == false; i++) {
545 /* The iteration iterates the length of the worm.
546 Here's the conversion to the true indices within the worm arrays. */
547 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
548 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
549 int startx = w->x[linestart];
550 int starty = w->y[linestart];
551 int endx = w->x[lineend];
552 int endy = w->y[lineend];
554 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
557 return retval;
561 * Checks wether a specific food in the food arrays is at the
562 * specified coordinates.
563 * @param int foodIndex The index of the food in the food arrays
564 * @param int x the x coordinate.
565 * @param int y the y coordinate.
566 * @return Returns true if the coordinate hits the food specified by
567 * foodIndex.
569 static bool specific_food_collision(int foodIndex, int x, int y) {
570 bool retVal = false;
571 if (x >= foodx[foodIndex] &&
572 x < foodx[foodIndex] + food_size &&
573 y >= foody[foodIndex] &&
574 y < foody[foodIndex] + food_size) {
576 retVal = true;
578 return retVal;
582 * Returns the index of the food that is at the
583 * given coordinates. If no food is at the coordinates
584 * -1 is returned.
585 * @return int -1 <= value < MAX_FOOD
587 static int food_collision(int x, int y) {
588 int i = 0;
589 int retVal = -1;
590 for (i = 0; i < MAX_FOOD; i++) {
591 if (specific_food_collision(i, x, y)) {
592 retVal = i;
593 break;
596 return retVal;
600 * Checks wether a specific argh in the argh arrays is at the
601 * specified coordinates.
602 * @param int arghIndex The index of the argh in the argh arrays
603 * @param int x the x coordinate.
604 * @param int y the y coordinate.
605 * @return Returns true if the coordinate hits the argh specified by
606 * arghIndex.
608 static bool specific_argh_collision(int arghIndex, int x, int y) {
610 if ( x >= arghx[arghIndex] &&
611 y >= arghy[arghIndex] &&
612 x < arghx[arghIndex] + argh_size &&
613 y < arghy[arghIndex] + argh_size )
615 return true;
618 return false;
622 * Returns the index of the argh that is at the
623 * given coordinates. If no argh is at the coordinates
624 * -1 is returned.
625 * @param int x The x coordinate.
626 * @param int y The y coordinate.
627 * @return int -1 <= value < argh_count <= MAX_ARGH
629 static int argh_collision(int x, int y) {
630 int i = 0;
631 int retVal = -1;
633 /* search for the argh that has the specified coords */
634 for (i = 0; i < argh_count; i++) {
635 if (specific_argh_collision(i, x, y)) {
636 retVal = i;
637 break;
640 return retVal;
644 * Checks wether the worm collides with the food at the specfied food-arrays.
645 * @param int foodIndex The index of the food in the arrays. Ensure the value is
646 * 0 <= foodIndex <= MAX_FOOD
647 * @return Returns true if the worm collides with the specified food.
649 static bool worm_food_collision(struct worm *w, int foodIndex)
651 bool retVal = false;
653 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
654 food_size - 1, food_size - 1);
656 return retVal;
660 * Returns true if the worm hits the argh within the next moves (unless
661 * the worm changes it's direction).
662 * @param struct worm *w - The worm to investigate
663 * @param int argh_idx - The index of the argh
664 * @param int moves - The number of moves that are considered.
665 * @return Returns false if the specified argh is not hit within the next
666 * moves.
668 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){
669 bool retVal = false;
670 int x1, y1, x2, y2;
671 x1 = w->x[w->head];
672 y1 = w->y[w->head];
674 x2 = w->x[w->head] + moves * w->dirx;
675 y2 = w->y[w->head] + moves * w->diry;
677 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
678 argh_size, argh_size);
679 return retVal;
683 * Checks wether the worm collides with the argh at the specfied argh-arrays.
684 * @param int arghIndex The index of the argh in the arrays.
685 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
686 * @return Returns true if the worm collides with the specified argh.
688 static bool worm_argh_collision(struct worm *w, int arghIndex)
690 bool retVal = false;
692 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
693 argh_size - 1, argh_size - 1);
695 return retVal;
699 * Find new coordinates for the food stored in foodx[index], foody[index]
700 * that don't collide with any other food or argh
701 * @param int index
702 * Ensure that 0 <= index < MAX_FOOD.
704 static void make_food(int index) {
706 int x = 0;
707 int y = 0;
708 bool collisionDetected = false;
709 int i;
711 do {
712 /* make coordinates for a new food so that
713 the entire food lies within the FIELD */
714 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
715 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
717 /* Ensure that the new food doesn't collide with any
718 existing foods or arghs.
719 If one or more corners of the new food hit any existing
720 argh or food a collision is detected.
722 collisionDetected =
723 food_collision(x , y ) >= 0 ||
724 food_collision(x , y + food_size - 1) >= 0 ||
725 food_collision(x + food_size - 1, y ) >= 0 ||
726 food_collision(x + food_size - 1, y + food_size - 1) >= 0 ||
727 argh_collision(x , y ) >= 0 ||
728 argh_collision(x , y + food_size - 1) >= 0 ||
729 argh_collision(x + food_size - 1, y ) >= 0 ||
730 argh_collision(x + food_size - 1, y + food_size - 1) >= 0;
732 /* use coordinates for further testing */
733 foodx[index] = x;
734 foody[index] = y;
736 /* now test wether we accidently hit the worm with food ;) */
737 i = 0;
738 for (i = 0; i < worm_count && !collisionDetected; i++) {
739 collisionDetected |= worm_food_collision(&worms[i], index);
742 while (collisionDetected);
743 return;
747 * Clears a food from the lcd buffer.
748 * @param int index The index of the food arrays under which
749 * the coordinates of the desired food can be found. Ensure
750 * that the value is 0 <= index <= MAX_FOOD.
752 static void clear_food(int index)
754 /* remove the old food from the screen */
755 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
756 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
757 foody[index] + FIELD_RECT_Y,
758 food_size, food_size);
759 rb->lcd_set_drawmode(DRMODE_SOLID);
763 * Draws a food in the lcd buffer.
764 * @param int index The index of the food arrays under which
765 * the coordinates of the desired food can be found. Ensure
766 * that the value is 0 <= index <= MAX_FOOD.
768 static void draw_food(int index)
770 /* draw the food object */
771 #ifdef HAVE_LCD_COLOR
772 rb->lcd_set_foreground(COLOR_FOOD);
773 #endif
774 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
775 foody[index] + FIELD_RECT_Y,
776 food_size, food_size);
777 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
778 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
779 foody[index] + FIELD_RECT_Y + 1,
780 food_size - 2, food_size - 2);
781 rb->lcd_set_drawmode(DRMODE_SOLID);
782 #ifdef HAVE_LCD_COLOR
783 rb->lcd_set_foreground(COLOR_FG);
784 #endif
788 * Find new coordinates for the argh stored in arghx[index], arghy[index]
789 * that don't collide with any other food or argh.
790 * @param int index
791 * Ensure that 0 <= index < argh_count < MAX_ARGH.
793 static void make_argh(int index)
795 int x = -1;
796 int y = -1;
797 bool collisionDetected = false;
798 int i;
800 do {
801 /* make coordinates for a new argh so that
802 the entire food lies within the FIELD */
803 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
804 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
806 /* Ensure that the new argh doesn't intersect with any
807 existing foods or arghs.
808 If one or more corners of the new argh hit any existing
809 argh or food an intersection is detected.
811 collisionDetected =
812 food_collision(x , y ) >= 0 ||
813 food_collision(x , y + argh_size - 1) >= 0 ||
814 food_collision(x + argh_size - 1, y ) >= 0 ||
815 food_collision(x + argh_size - 1, y + argh_size - 1) >= 0 ||
816 argh_collision(x , y ) >= 0 ||
817 argh_collision(x , y + argh_size - 1) >= 0 ||
818 argh_collision(x + argh_size - 1, y ) >= 0 ||
819 argh_collision(x + argh_size - 1, y + argh_size - 1) >= 0;
821 /* use the candidate coordinates to make a real argh */
822 arghx[index] = x;
823 arghy[index] = y;
825 /* now test wether we accidently hit the worm with argh ;) */
826 for (i = 0; i < worm_count && !collisionDetected; i++) {
827 collisionDetected |= worm_argh_collision(&worms[i], index);
828 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
829 MIN_ARGH_DIST);
832 while (collisionDetected);
833 return;
837 * Draws an argh in the lcd buffer.
838 * @param int index The index of the argh arrays under which
839 * the coordinates of the desired argh can be found. Ensure
840 * that the value is 0 <= index < argh_count <= MAX_ARGH.
842 static void draw_argh(int index)
844 /* draw the new argh */
845 #ifdef HAVE_LCD_COLOR
846 rb->lcd_set_foreground(COLOR_ARGH);
847 #endif
848 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
849 arghy[index] + FIELD_RECT_Y,
850 argh_size, argh_size);
851 #ifdef HAVE_LCD_COLOR
852 rb->lcd_set_foreground(COLOR_FG);
853 #endif
856 static void virtual_player(struct worm *w);
858 * Initialzes the specified worm with INITIAL_WORM_LENGTH
859 * and the tail at the specified position. The worm will
860 * be initialized alive and creeping EAST.
861 * @param struct worm *w The worm that is to be initialized
862 * @param int x The x coordinate at which the tail of the worm starts.
863 * x must be 0 <= x < FIELD_RECT_WIDTH.
864 * @param int y The y coordinate at which the tail of the worm starts
865 * y must be 0 <= y < FIELD_RECT_WIDTH.
867 static void init_worm(struct worm *w, int x, int y){
868 /* initialize the worm size */
869 w->head = 1;
870 w->tail = 0;
872 w->x[w->head] = x + 1;
873 w->y[w->head] = y;
875 w->x[w->tail] = x;
876 w->y[w->tail] = y;
878 /* set the initial direction the worm creeps to */
879 w->dirx = 1;
880 w->diry = 0;
882 w->growing = INITIAL_WORM_LENGTH - 1;
883 w->alive = true;
884 w->fetch_worm_direction = virtual_player;
888 * Writes the direction that was stored for
889 * human player 1 into the specified worm. This function
890 * may be used to be stored in worm.fetch_worm_direction.
891 * The value of
892 * the direction is read from player1_dir.
893 * @param struct worm *w - The worm of which the direction
894 * is altered.
896 static void human_player1(struct worm *w) {
897 set_worm_dir(w, player1_dir);
901 * Writes the direction that was stored for
902 * human player 2 into the specified worm. This function
903 * may be used to be stored in worm.fetch_worm_direction.
904 * The value of
905 * the direction is read from player2_dir.
906 * @param struct worm *w - The worm of which the direction
907 * is altered.
909 static void human_player2(struct worm *w) {
910 set_worm_dir(w, player2_dir);
914 * Writes the direction that was stored for
915 * human player using a remote control
916 * into the specified worm. This function
917 * may be used to be stored in worm.fetch_worm_direction.
918 * The value of
919 * the direction is read from player3_dir.
920 * @param struct worm *w - The worm of which the direction
921 * is altered.
923 static void remote_player(struct worm *w) {
924 set_worm_dir(w, player3_dir);
928 * Initializes the worm-, food- and argh-arrays, draws a frame,
929 * makes some food and argh and display all that stuff.
931 static void init_wormlet(void)
933 int i;
935 for (i = 0; i< worm_count; i++) {
936 /* Initialize all the worm coordinates to center. */
937 int x = (int)(FIELD_RECT_WIDTH / 2);
938 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
940 init_worm(&worms[i], x, y);
943 player1_dir = EAST;
944 player2_dir = EAST;
945 player3_dir = EAST;
947 if (players > 0) {
948 worms[0].fetch_worm_direction = human_player1;
951 if (players > 1) {
952 if (use_remote) {
953 worms[1].fetch_worm_direction = remote_player;
954 } else {
955 worms[1].fetch_worm_direction = human_player2;
959 if (players > 2) {
960 worms[2].fetch_worm_direction = human_player2;
963 /* Needed when the game is restarted using BTN_STOPRESET */
964 rb->lcd_clear_display();
966 /* make and display some food and argh */
967 argh_count = MAX_FOOD;
968 for (i = 0; i < MAX_FOOD; i++) {
969 make_food(i);
970 draw_food(i);
971 make_argh(i);
972 draw_argh(i);
975 /* draw the game field */
976 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
977 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
978 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
979 rb->lcd_set_drawmode(DRMODE_SOLID);
981 /* make everything visible */
982 rb->lcd_update();
987 * Move the worm one step further if it is alive.
988 * The direction in which the worm moves is taken from dirx and diry.
989 * move_worm decreases growing if > 0. While the worm is growing the tail
990 * is left untouched.
991 * @param struct worm *w The worm to move. w must not be NULL.
993 static void move_worm(struct worm *w)
995 if (w->alive) {
996 /* determine the head point and its precessor */
997 int headx = w->x[w->head];
998 int heady = w->y[w->head];
999 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
1000 int preheadx = w->x[prehead];
1001 int preheady = w->y[prehead];
1003 /* determine the old direction */
1004 int olddirx;
1005 int olddiry;
1006 if (headx == preheadx) {
1007 olddirx = 0;
1008 olddiry = (heady > preheady) ? 1 : -1;
1009 } else {
1010 olddiry = 0;
1011 olddirx = (headx > preheadx) ? 1 : -1;
1014 /* olddir == dir?
1015 a change of direction means a new segment
1016 has been opened */
1017 if (olddirx != w->dirx ||
1018 olddiry != w->diry) {
1019 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1022 /* new head position */
1023 w->x[w->head] = headx + w->dirx;
1024 w->y[w->head] = heady + w->diry;
1027 /* while the worm is growing no tail procession is necessary */
1028 if (w->growing > 0) {
1029 /* update the worms grow state */
1030 w->growing--;
1033 /* if the worm isn't growing the tail has to be dragged */
1034 else {
1035 /* index of the end of the tail segment */
1036 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1038 /* drag the end of the tail */
1039 /* only one coordinate has to be altered. Here it is
1040 determined which one */
1041 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1042 if (w->x[w->tail] == w->x[tail_segment_end]) {
1043 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1044 w->y[w->tail] += dir;
1045 } else {
1046 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1047 w->x[w->tail] += dir;
1050 /* when the tail has been dragged so far that it meets
1051 the next segment start the tail segment is obsolete and
1052 must be freed */
1053 if (w->x[w->tail] == w->x[tail_segment_end] &&
1054 w->y[w->tail] == w->y[tail_segment_end]){
1056 /* drop the last tail point */
1057 w->tail = tail_segment_end;
1064 * Draws the head and clears the tail of the worm in
1065 * the display buffer. lcd_update() is NOT called thus
1066 * the caller has to take care that the buffer is displayed.
1068 static void draw_worm(struct worm *w)
1070 #ifdef HAVE_LCD_COLOR
1071 rb->lcd_set_foreground(COLOR_WORM);
1072 #endif
1073 /* draw the new head */
1074 int x = w->x[w->head];
1075 int y = w->y[w->head];
1076 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1077 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1080 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1082 /* clear the space behind the worm */
1083 x = w->x[w->tail] ;
1084 y = w->y[w->tail] ;
1085 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1086 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1088 rb->lcd_set_drawmode(DRMODE_SOLID);
1089 #ifdef HAVE_LCD_COLOR
1090 rb->lcd_set_foreground(COLOR_FG);
1091 #endif
1095 * Checks wether the coordinate is part of the worm. Returns
1096 * true if any part of the worm was hit - including the head.
1097 * @param x int The x coordinate
1098 * @param y int The y coordinate
1099 * @return int The index of the worm arrays that contain x, y.
1100 * Returns -1 if the coordinates are not part of the worm.
1102 static int specific_worm_collision(struct worm *w, int x, int y)
1104 int retVal = -1;
1106 /* get_worm_array_length is expensive -> buffer the value */
1107 int wormLength = get_worm_array_length(w);
1108 int i;
1110 /* test each entry that is part of the worm */
1111 for (i = 0; i < wormLength && retVal == -1; i++) {
1113 /* The iteration iterates the length of the worm.
1114 Here's the conversion to the true indices within the worm arrays. */
1115 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1116 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1117 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1118 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1119 if (samex || samey){
1120 int test, min, max, tmp;
1122 if (samey) {
1123 min = w->x[linestart];
1124 max = w->x[lineend];
1125 test = x;
1126 } else {
1127 min = w->y[linestart];
1128 max = w->y[lineend];
1129 test = y;
1132 tmp = min;
1133 min = MIN(min, max);
1134 max = MAX(tmp, max);
1136 if (min <= test && test <= max) {
1137 retVal = lineend;
1141 return retVal;
1145 * Increases the length of the specified worm by marking
1146 * that it may grow by len pixels. Note that the worm has
1147 * to move to make the growing happen.
1148 * @param worm *w The worm that is to be altered.
1149 * @param int len A positive value specifying the amount of
1150 * pixels the worm may grow.
1152 static void add_growing(struct worm *w, int len) {
1153 w->growing += len;
1157 * Determins the worm that is at the coordinates x, y. The parameter
1158 * w is a switch parameter that changes the functionality of worm_collision.
1159 * If w is specified and x,y hits the head of w NULL is returned.
1160 * This is a useful way to determine wether the head of w hits
1161 * any worm but including itself but excluding its own head.
1162 * (It hits always its own head ;))
1163 * If w is set to NULL worm_collision returns any worm including all heads
1164 * that is at position of x,y.
1165 * @param struct worm *w The worm of which the head should be excluded in
1166 * the test. w may be set to NULL.
1167 * @param int x The x coordinate that is checked
1168 * @param int y The y coordinate that is checkec
1169 * @return struct worm* The worm that has been hit by x,y. If no worm
1170 * was at the position NULL is returned.
1172 static struct worm* worm_collision(struct worm *w, int x, int y){
1173 struct worm *retVal = NULL;
1174 int i;
1175 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1176 int collision_at = specific_worm_collision(&worms[i], x, y);
1177 if (collision_at != -1) {
1178 if (!(w == &worms[i] && collision_at == w->head)){
1179 retVal = &worms[i];
1183 return retVal;
1187 * Returns true if the head of the worm just has
1188 * crossed the field boundaries.
1189 * @return bool true if the worm just has wrapped.
1191 static bool field_collision(struct worm *w)
1193 bool retVal = false;
1194 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1195 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1196 (w->x[w->head] < 0) ||
1197 (w->y[w->head] < 0))
1199 retVal = true;
1201 return retVal;
1206 * Returns true if the specified coordinates are within the
1207 * field specified by the FIELD_RECT_XXX constants.
1208 * @param int x The x coordinate of the point that is investigated
1209 * @param int y The y coordinate of the point that is investigated
1210 * @return bool Returns false if x,y specifies a point outside the
1211 * field of worms.
1213 static bool is_in_field_rect(int x, int y) {
1214 bool retVal = false;
1215 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1216 y >= 0 && y < FIELD_RECT_HEIGHT);
1217 return retVal;
1221 * Checks and returns wether the head of the w
1222 * is colliding with something currently.
1223 * @return int One of the values:
1224 * COLLISION_NONE
1225 * COLLISION_w
1226 * COLLISION_FOOD
1227 * COLLISION_ARGH
1228 * COLLISION_FIELD
1230 static int check_collision(struct worm *w)
1232 int retVal = COLLISION_NONE;
1234 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1235 retVal = COLLISION_WORM;
1237 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1238 retVal = COLLISION_FOOD;
1240 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1241 retVal = COLLISION_ARGH;
1243 if (field_collision(w))
1244 retVal = COLLISION_FIELD;
1246 return retVal;
1250 * Returns the index of the food that is closest to the point
1251 * specified by x, y. This index may be used in the foodx and
1252 * foody arrays.
1253 * @param int x The x coordinate of the point
1254 * @param int y The y coordinate of the point
1255 * @return int A value usable as index in foodx and foody.
1257 static int get_nearest_food(int x, int y){
1258 int nearestfood = 0;
1259 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1260 int deltax = 0;
1261 int deltay = 0;
1262 int foodindex;
1263 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1264 int distance;
1265 deltax = foodx[foodindex] - x;
1266 deltay = foody[foodindex] - y;
1267 deltax = deltax > 0 ? deltax : deltax * (-1);
1268 deltay = deltay > 0 ? deltay : deltay * (-1);
1269 distance = deltax + deltay;
1271 if (distance < olddistance) {
1272 olddistance = distance;
1273 nearestfood = foodindex;
1276 return nearestfood;
1280 * Returns wether the specified position is next to the worm
1281 * and in the direction the worm looks. Use this method to
1282 * test wether this position would be hit with the next move of
1283 * the worm unless the worm changes its direction.
1284 * @param struct worm *w - The worm to be investigated
1285 * @param int x - The x coordinate of the position to test.
1286 * @param int y - The y coordinate of the position to test.
1287 * @return Returns true if the worm will hit the position unless
1288 * it change its direction before the next move.
1290 static bool is_in_front_of_worm(struct worm *w, int x, int y) {
1291 bool infront = false;
1292 int deltax = x - w->x[w->head];
1293 int deltay = y - w->y[w->head];
1295 if (w->dirx == 0) {
1296 infront = (w->diry * deltay) > 0;
1297 } else {
1298 infront = (w->dirx * deltax) > 0;
1300 return infront;
1304 * Returns true if the worm will collide with the next move unless
1305 * it changes its direction.
1306 * @param struct worm *w - The worm to be investigated.
1307 * @return Returns true if the worm will collide with the next move
1308 * unless it changes its direction.
1310 static bool will_worm_collide(struct worm *w) {
1311 int x = w->x[w->head] + w->dirx;
1312 int y = w->y[w->head] + w->diry;
1313 bool retVal = !is_in_field_rect(x, y);
1314 if (!retVal) {
1315 retVal = (argh_collision(x, y) != -1);
1318 if (!retVal) {
1319 retVal = (worm_collision(w, x, y) != NULL);
1321 return retVal;
1325 * This function
1326 * may be used to be stored in worm.fetch_worm_direction for
1327 * worms that are not controlled by humans but by artificial stupidity.
1328 * A direction is searched that doesn't lead to collision but to the nearest
1329 * food - but not very intelligent. The direction is written to the specified
1330 * worm.
1331 * @param struct worm *w - The worm of which the direction
1332 * is altered.
1334 static void virtual_player(struct worm *w) {
1335 bool isright;
1336 int plana, planb, planc;
1337 /* find the next lunch */
1338 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1340 /* determine in which direction it is */
1342 /* in front of me? */
1343 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1345 /* left right of me? */
1346 int olddir = get_worm_dir(w);
1347 set_worm_dir(w, (olddir + 1) % 4);
1348 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1349 set_worm_dir(w, olddir);
1351 /* detect situation, set strategy */
1352 if (infront) {
1353 if (isright) {
1354 plana = olddir;
1355 planb = (olddir + 1) % 4;
1356 planc = (olddir + 3) % 4;
1357 } else {
1358 plana = olddir;
1359 planb = (olddir + 3) % 4;
1360 planc = (olddir + 1) % 4;
1362 } else {
1363 if (isright) {
1364 plana = (olddir + 1) % 4;
1365 planb = olddir;
1366 planc = (olddir + 3) % 4;
1367 } else {
1368 plana = (olddir + 3) % 4;
1369 planb = olddir;
1370 planc = (olddir + 1) % 4;
1374 /* test for collision */
1375 set_worm_dir(w, plana);
1376 if (will_worm_collide(w)){
1378 /* plan b */
1379 set_worm_dir(w, planb);
1381 /* test for collision */
1382 if (will_worm_collide(w)) {
1384 /* plan c */
1385 set_worm_dir(w, planc);
1391 * prints out the score board with all the status information
1392 * about the game.
1394 static void score_board(void)
1396 char buf[15];
1397 char* buf2 = NULL;
1398 int i;
1399 int y = 0;
1400 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1401 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1402 rb->lcd_set_drawmode(DRMODE_SOLID);
1403 for (i = 0; i < worm_count; i++) {
1404 int score = get_score(&worms[i]);
1406 /* high score */
1407 if (worms[i].fetch_worm_direction != virtual_player){
1408 if (highscore < score) {
1409 highscore = score;
1413 /* length */
1414 rb->snprintf(buf, sizeof (buf),"Len:%d", score);
1416 /* worm state */
1417 switch (check_collision(&worms[i])) {
1418 case COLLISION_NONE:
1419 if (worms[i].growing > 0)
1420 buf2 = "Growing";
1421 else {
1422 if (worms[i].alive)
1423 buf2 = "Hungry";
1424 else
1425 buf2 = "Wormed";
1427 break;
1429 case COLLISION_WORM:
1430 buf2 = "Wormed";
1431 break;
1433 case COLLISION_FOOD:
1434 buf2 = "Growing";
1435 break;
1437 case COLLISION_ARGH:
1438 buf2 = "Argh";
1439 break;
1441 case COLLISION_FIELD:
1442 buf2 = "Crashed";
1443 break;
1445 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf);
1446 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2);
1448 if (!worms[i].alive){
1449 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1450 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1451 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1452 rb->lcd_set_drawmode(DRMODE_SOLID);
1454 y += 19;
1456 rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore);
1457 #ifndef DEBUG_WORMLET
1458 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);
1459 #else
1460 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
1461 #endif
1465 * Checks for collisions of the worm and its environment and
1466 * takes appropriate actions like growing the worm or killing it.
1467 * @return bool Returns true if the worm is dead. Returns
1468 * false if the worm is healthy, up and creeping.
1470 static bool process_collisions(struct worm *w)
1472 int index = -1;
1474 w->alive &= !field_collision(w);
1476 if (w->alive) {
1478 /* check if food was eaten */
1479 index = food_collision(w->x[w->head], w->y[w->head]);
1480 if (index != -1){
1481 int i;
1483 clear_food(index);
1484 make_food(index);
1485 draw_food(index);
1487 for (i = 0; i < arghs_per_food; i++) {
1488 argh_count++;
1489 if (argh_count > MAX_ARGH)
1490 argh_count = MAX_ARGH;
1491 make_argh(argh_count - 1);
1492 draw_argh(argh_count - 1);
1495 add_growing(w, worm_food);
1497 draw_worm(w);
1500 /* check if argh was eaten */
1501 else {
1502 index = argh_collision(w->x[w->head], w->y[w->head]);
1503 if (index != -1) {
1504 w->alive = false;
1506 else {
1507 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1508 w->alive = false;
1513 return !w->alive;
1517 * The main loop of the game.
1518 * @return bool Returns true if the game ended
1519 * with a dead worm. Returns false if the user
1520 * aborted the game manually.
1522 static int run(void)
1524 int button = 0;
1525 int wormDead = false;
1526 bool paused = false;
1528 /* ticks are counted to compensate speed variations */
1529 long cycle_start = 0, cycle_end = 0;
1530 #ifdef DEBUG_WORMLET
1531 int ticks_to_max_cycle_reset = 20;
1532 long max_cycle = 0;
1533 char buf[20];
1534 #endif
1536 /* initialize the board and so on */
1537 init_wormlet();
1539 cycle_start = *rb->current_tick;
1540 /* change the direction of the worm */
1541 while (!wormDead)
1543 int i;
1544 long cycle_duration=0;
1546 #ifdef HAS_BUTTON_HOLD
1547 if (rb->button_hold())
1548 paused = true;
1549 #endif
1551 switch (button) {
1552 case BTN_STARTPAUSE:
1553 paused = !paused;
1554 break;
1555 case BTN_STOPRESET:
1556 if (paused)
1557 return 1; /* restart game */
1558 else
1559 paused = true;
1560 break;
1561 #ifdef BTN_RC_QUIT
1562 case BTN_RC_QUIT:
1563 #endif
1564 case BTN_QUIT:
1565 return 2; /* back to menu */
1566 break;
1568 if (!paused)
1570 switch (button) {
1571 case BTN_DIR_UP:
1572 if (players == 1 && !use_remote) {
1573 player1_dir = NORTH;
1575 break;
1577 case BTN_DIR_DOWN:
1578 if (players == 1 && !use_remote) {
1579 player1_dir = SOUTH;
1581 break;
1583 case BTN_DIR_LEFT:
1584 if (players != 1 || use_remote) {
1585 player1_dir = (player1_dir + 3) % 4;
1586 } else {
1587 player1_dir = WEST;
1589 break;
1591 case BTN_DIR_RIGHT:
1592 if (players != 1 || use_remote) {
1593 player1_dir = (player1_dir + 1) % 4;
1594 } else {
1595 player1_dir = EAST;
1597 break;
1599 #ifdef MULTIPLAYER
1600 case BTN_PLAYER2_DIR1:
1601 player2_dir = (player2_dir + 3) % 4;
1602 break;
1604 case BTN_PLAYER2_DIR2:
1605 player2_dir = (player2_dir + 1) % 4;
1606 break;
1607 #endif
1609 #ifdef REMOTE
1610 case BTN_RC_UP:
1611 player3_dir = (player3_dir + 1) % 4;
1612 break;
1614 case BTN_RC_DOWN:
1615 player3_dir = (player3_dir + 3) % 4;
1616 break;
1617 #endif
1621 for (i = 0; i < worm_count; i++) {
1622 worms[i].fetch_worm_direction(&worms[i]);
1625 wormDead = true;
1626 for (i = 0; i < worm_count; i++){
1627 struct worm *w = &worms[i];
1628 move_worm(w);
1629 wormDead &= process_collisions(w);
1630 draw_worm(w);
1632 score_board();
1633 rb->lcd_update();
1634 if (button == BTN_STOPRESET) {
1635 wormDead = true;
1638 /* here the wormlet game cycle ends
1639 thus the current tick is stored
1640 as end time */
1641 cycle_end = *rb->current_tick;
1643 /* The duration of the game cycle */
1644 cycle_duration = cycle_end - cycle_start;
1645 cycle_duration = MAX(0, cycle_duration);
1646 cycle_duration = MIN(speed -1, cycle_duration);
1649 #ifdef DEBUG_WORMLET
1650 ticks_to_max_cycle_reset--;
1651 if (ticks_to_max_cycle_reset <= 0) {
1652 max_cycle = 0;
1655 if (max_cycle < cycle_duration) {
1656 max_cycle = cycle_duration;
1657 ticks_to_max_cycle_reset = 20;
1659 rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle);
1660 set_debug_out(buf);
1661 #endif
1663 /* adjust the number of ticks to wait for a button.
1664 This ensures that a complete game cycle including
1665 user input runs in constant time */
1666 button = rb->button_get_w_tmo(speed - cycle_duration);
1667 cycle_start = *rb->current_tick;
1670 rb->splash(HZ*2, "Game Over!");
1672 return 2; /* back to menu */
1675 #ifdef DEBUG_WORMLET
1678 * Just a test routine that checks that worm_food_collision works
1679 * in some typical situations.
1681 static void test_worm_food_collision(void) {
1682 int collision_count = 0;
1683 int i;
1684 rb->lcd_clear_display();
1685 init_worm(&worms[0], 10, 10);
1686 add_growing(&worms[0], 10);
1687 set_worm_dir(&worms[0], EAST);
1688 for (i = 0; i < 10; i++) {
1689 move_worm(&worms[0]);
1690 draw_worm(&worms[0]);
1693 set_worm_dir(&worms[0], SOUTH);
1694 for (i = 0; i < 10; i++) {
1695 move_worm(&worms[0]);
1696 draw_worm(&worms[0]);
1699 foodx[0] = 15;
1700 foody[0] = 12;
1701 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1702 char buf[20];
1703 bool collision;
1704 draw_worm(&worms[0]);
1705 draw_food(0);
1706 collision = worm_food_collision(&worms[0], 0);
1707 if (collision) {
1708 collision_count++;
1710 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1711 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1712 rb->lcd_update();
1714 if (collision_count != food_size) {
1715 rb->button_get(true);
1719 foody[0] = 15;
1720 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1721 char buf[20];
1722 bool collision;
1723 draw_worm(&worms[0]);
1724 draw_food(0);
1725 collision = worm_food_collision(&worms[0], 0);
1726 if (collision) {
1727 collision_count ++;
1729 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1730 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1731 rb->lcd_update();
1733 if (collision_count != food_size * 2) {
1734 rb->button_get(true);
1739 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){
1740 int x, y;
1741 bool retVal = false;
1742 for (x = rx; x < rx + rw; x++){
1743 for (y = ry; y < ry + rh; y++) {
1744 if (specific_worm_collision(w, x, y) != -1) {
1745 retVal = true;
1749 return retVal;
1752 static void test_worm_argh_collision(void) {
1753 int i;
1754 int dir;
1755 int collision_count = 0;
1756 rb->lcd_clear_display();
1757 init_worm(&worms[0], 10, 10);
1758 add_growing(&worms[0], 40);
1759 for (dir = 0; dir < 4; dir++) {
1760 set_worm_dir(&worms[0], (EAST + dir) % 4);
1761 for (i = 0; i < 10; i++) {
1762 move_worm(&worms[0]);
1763 draw_worm(&worms[0]);
1767 arghx[0] = 12;
1768 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1769 char buf[20];
1770 bool collision;
1771 draw_argh(0);
1772 collision = worm_argh_collision(&worms[0], 0);
1773 if (collision) {
1774 collision_count ++;
1776 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1777 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1778 rb->lcd_update();
1780 if (collision_count != argh_size * 2) {
1781 rb->button_get(true);
1784 arghy[0] = 12;
1785 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1786 char buf[20];
1787 bool collision;
1788 draw_argh(0);
1789 collision = worm_argh_collision(&worms[0], 0);
1790 if (collision) {
1791 collision_count ++;
1793 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1794 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1795 rb->lcd_update();
1797 if (collision_count != argh_size * 4) {
1798 rb->button_get(true);
1802 static int testline_in_rect(void) {
1803 int testfailed = -1;
1805 int rx = 10;
1806 int ry = 15;
1807 int rw = 20;
1808 int rh = 25;
1810 /* Test 1 */
1811 int x1 = 12;
1812 int y1 = 8;
1813 int x2 = 12;
1814 int y2 = 42;
1816 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1817 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1818 rb->lcd_drawrect(rx, ry, rw, rh);
1819 rb->lcd_drawline(x1, y1, x2, y2);
1820 rb->lcd_update();
1821 rb->lcd_putsxy(0, 0, "failed 1");
1822 rb->button_get(true);
1823 testfailed = 1;
1826 /* test 2 */
1827 y2 = 20;
1828 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1829 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1830 rb->lcd_drawrect(rx, ry, rw, rh);
1831 rb->lcd_drawline(x1, y1, x2, y2);
1832 rb->lcd_putsxy(0, 0, "failed 2");
1833 rb->lcd_update();
1834 rb->button_get(true);
1835 testfailed = 2;
1838 /* test 3 */
1839 y1 = 30;
1840 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1841 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1842 rb->lcd_drawrect(rx, ry, rw, rh);
1843 rb->lcd_drawline(x1, y1, x2, y2);
1844 rb->lcd_putsxy(0, 0, "failed 3");
1845 rb->lcd_update();
1846 rb->button_get(true);
1847 testfailed = 3;
1850 /* test 4 */
1851 y2 = 45;
1852 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1853 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1854 rb->lcd_drawrect(rx, ry, rw, rh);
1855 rb->lcd_drawline(x1, y1, x2, y2);
1856 rb->lcd_putsxy(0, 0, "failed 4");
1857 rb->lcd_update();
1858 rb->button_get(true);
1859 testfailed = 4;
1862 /* test 5 */
1863 y1 = 50;
1864 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1865 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1866 rb->lcd_drawrect(rx, ry, rw, rh);
1867 rb->lcd_drawline(x1, y1, x2, y2);
1868 rb->lcd_putsxy(0, 0, "failed 5");
1869 rb->lcd_update();
1870 rb->button_get(true);
1871 testfailed = 5;
1874 /* test 6 */
1875 y1 = 5;
1876 y2 = 7;
1877 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1878 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1879 rb->lcd_drawrect(rx, ry, rw, rh);
1880 rb->lcd_drawline(x1, y1, x2, y2);
1881 rb->lcd_putsxy(0, 0, "failed 6");
1882 rb->lcd_update();
1883 rb->button_get(true);
1884 testfailed = 6;
1887 /* test 7 */
1888 x1 = 8;
1889 y1 = 20;
1890 x2 = 35;
1891 y2 = 20;
1892 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1893 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1894 rb->lcd_drawrect(rx, ry, rw, rh);
1895 rb->lcd_drawline(x1, y1, x2, y2);
1896 rb->lcd_putsxy(0, 0, "failed 7");
1897 rb->lcd_update();
1898 rb->button_get(true);
1899 testfailed = 7;
1902 /* test 8 */
1903 x2 = 12;
1904 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1905 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1906 rb->lcd_drawrect(rx, ry, rw, rh);
1907 rb->lcd_drawline(x1, y1, x2, y2);
1908 rb->lcd_putsxy(0, 0, "failed 8");
1909 rb->lcd_update();
1910 rb->button_get(true);
1911 testfailed = 8;
1914 /* test 9 */
1915 x1 = 25;
1916 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1917 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1918 rb->lcd_drawrect(rx, ry, rw, rh);
1919 rb->lcd_drawline(x1, y1, x2, y2);
1920 rb->lcd_putsxy(0, 0, "failed 9");
1921 rb->lcd_update();
1922 rb->button_get(true);
1923 testfailed = 9;
1926 /* test 10 */
1927 x2 = 37;
1928 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1929 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1930 rb->lcd_drawrect(rx, ry, rw, rh);
1931 rb->lcd_drawline(x1, y1, x2, y2);
1932 rb->lcd_putsxy(0, 0, "failed 10");
1933 rb->lcd_update();
1934 rb->button_get(true);
1935 testfailed = 10;
1938 /* test 11 */
1939 x1 = 42;
1940 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1941 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1942 rb->lcd_drawrect(rx, ry, rw, rh);
1943 rb->lcd_drawline(x1, y1, x2, y2);
1944 rb->lcd_putsxy(0, 0, "failed 11");
1945 rb->lcd_update();
1946 rb->button_get(true);
1947 testfailed = 11;
1950 /* test 12 */
1951 x1 = 5;
1952 x2 = 7;
1953 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1954 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1955 rb->lcd_drawrect(rx, ry, rw, rh);
1956 rb->lcd_drawline(x1, y1, x2, y2);
1957 rb->lcd_putsxy(0, 0, "failed 12");
1958 rb->lcd_update();
1959 rb->button_get(true);
1960 testfailed = 12;
1963 /* test 13 */
1964 rx = 9;
1965 ry = 15;
1966 rw = food_size;
1967 rh = food_size;
1969 x1 = 10;
1970 y1 = 10;
1971 x2 = 10;
1972 y2 = 20;
1973 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1974 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
1975 rb->lcd_drawrect(rx, ry, rw, rh);
1976 rb->lcd_drawline(x1, y1, x2, y2);
1977 rb->lcd_putsxy(0, 0, "failed 13");
1978 rb->lcd_update();
1979 rb->button_get(true);
1980 testfailed = 13;
1983 /* test 14 */
1984 rx = 9;
1985 ry = 15;
1986 rw = 4;
1987 rh = 4;
1989 x1 = 10;
1990 y1 = 10;
1991 x2 = 10;
1992 y2 = 19;
1993 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1994 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
1995 rb->lcd_drawline(x1, y1, x2, y2);
1996 rb->lcd_invertrect(rx, ry, rw, rh);
1997 rb->lcd_putsxy(0, 0, "failed 14");
1998 rb->lcd_update();
1999 rb->button_get(true);
2000 testfailed = 14;
2003 rb->lcd_clear_display();
2005 return testfailed;
2009 * Just a test routine to test wether specific_worm_collision might work properly
2011 static int test_specific_worm_collision(void) {
2012 int collisions = 0;
2013 int dir;
2014 int x = 0;
2015 int y = 0;
2016 char buf[20];
2017 rb->lcd_clear_display();
2018 init_worm(&worms[0], 10, 20);
2019 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2021 for (dir = EAST; dir < EAST + 4; dir++) {
2022 int i;
2023 set_worm_dir(&worms[0], dir % 4);
2024 for (i = 0; i < 5; i++) {
2025 if (!(dir % 4 == NORTH && i == 9)) {
2026 move_worm(&worms[0]);
2027 draw_worm(&worms[0]);
2032 for (y = 15; y < 30; y ++){
2033 for (x = 5; x < 20; x++) {
2034 if (specific_worm_collision(&worms[0], x, y) != -1) {
2035 collisions ++;
2037 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2038 rb->snprintf(buf, sizeof buf, "collisions %d", collisions);
2039 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2040 rb->lcd_update();
2043 if (collisions != 21) {
2044 rb->button_get(true);
2046 return collisions;
2049 static void test_make_argh(void){
2050 int dir;
2051 int seed = 0;
2052 int hit = 0;
2053 int failures = 0;
2054 int last_failures = 0;
2055 int i, worm_idx;
2056 rb->lcd_clear_display();
2057 worm_count = 3;
2059 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2060 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2061 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2064 for (dir = EAST; dir < EAST + 4; dir++) {
2065 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2066 set_worm_dir(&worms[worm_idx], dir % 4);
2067 for (i = 0; i < 10; i++) {
2068 if (!(dir % 4 == NORTH && i == 9)) {
2069 move_worm(&worms[worm_idx]);
2070 draw_worm(&worms[worm_idx]);
2076 rb->lcd_update();
2078 for (seed = 0; hit < 20; seed += 2) {
2079 char buf[20];
2080 int x, y;
2081 rb->srand(seed);
2082 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2083 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2085 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2086 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2087 int tries = 0;
2088 rb->srand(seed);
2090 tries = make_argh(0);
2091 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2092 failures ++;
2095 rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d", x, y, failures, tries);
2096 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2097 rb->lcd_update();
2098 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, argh_size, argh_size);
2099 rb->lcd_update();
2100 draw_argh(0);
2101 rb->lcd_update();
2102 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, argh_size, argh_size);
2103 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, argh_size, argh_size);
2105 if (failures > last_failures) {
2106 rb->button_get(true);
2108 last_failures = failures;
2109 hit ++;
2115 static void test_worm_argh_collision_in_moves(void) {
2116 int hit_count = 0;
2117 int i;
2118 rb->lcd_clear_display();
2119 init_worm(&worms[0], 10, 20);
2121 arghx[0] = 20;
2122 arghy[0] = 18;
2123 draw_argh(0);
2125 set_worm_dir(&worms[0], EAST);
2126 for (i = 0; i < 20; i++) {
2127 char buf[20];
2128 move_worm(&worms[0]);
2129 draw_worm(&worms[0]);
2130 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2131 hit_count ++;
2133 rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
2134 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2135 rb->lcd_update();
2137 if (hit_count != argh_size + 5) {
2138 rb->button_get(true);
2141 #endif /* DEBUG_WORMLET */
2143 extern bool use_old_rect;
2146 * These are additional functions required to set various wormlet settings
2147 * and to enable saving of settings and high score
2151 Sets the total number of worms, both human and machine
2153 bool set_worm_num_worms(void)
2155 bool ret;
2156 static const struct opt_items num_worms_option[3] = {
2157 { "1", -1 },
2158 { "2", -1 },
2159 { "3", -1 },
2162 ret = rb->set_option("Number Of Worms", &worm_count,INT, num_worms_option, 3, NULL);
2163 if (worm_count < players) {
2164 worm_count=players;
2166 return ret;
2170 Sets the number of human players
2172 bool set_worm_num_players(void)
2174 bool ret;
2175 static const struct opt_items num_players_option[4] = {
2176 { "0", -1 },
2177 { "1", -1 },
2178 { "2", -1 },
2179 { "3", -1 }
2182 ret = rb->set_option("Number of Players", &players, INT,num_players_option , 4, NULL);
2183 if (players > worm_count) {
2184 worm_count = players;
2186 if (players > 2) {
2187 use_remote = true;
2189 return ret;
2193 Sets the size of each argh block created
2195 bool set_worm_argh_size(void)
2197 static const struct opt_items argh_size_option[11] = {
2198 { "0", -1 },
2199 { "1", -1 },
2200 { "2", -1 },
2201 { "3", -1 },
2202 { "4", -1 },
2203 { "5", -1 },
2204 { "6", -1 },
2205 { "7", -1 },
2206 { "8", -1 },
2207 { "9", -1 },
2208 { "10", -1 }
2211 return rb->set_option("Argh Size", &argh_size,INT,argh_size_option , 11, NULL);
2215 Sets the amount a worm grows per food
2217 bool set_worm_food(void)
2219 static const struct opt_items worm_food_option[11] = {
2220 { "0", -1 },
2221 { "1", -1 },
2222 { "2", -1 },
2223 { "3", -1 },
2224 { "4", -1 },
2225 { "5", -1 },
2226 { "6", -1 },
2227 { "7", -1 },
2228 { "8", -1 },
2229 { "9", -1 },
2230 { "10", -1 }
2232 return rb->set_option("Worm Growth Per Food", &worm_food,INT,worm_food_option , 11, NULL);
2236 Sets the number of arghs created per food
2238 bool set_argh_per_food(void)
2240 static const struct opt_items argh_food_option[5] = {
2241 { "0", -1 },
2242 { "1", -1 },
2243 { "2", -1 },
2244 { "3", -1 },
2245 { "4", -1 },
2247 return rb->set_option("Arghs Per Food", &arghs_per_food,INT,argh_food_option , 5, NULL);
2251 Sets the number of ticks per move
2253 bool set_worm_speed(void)
2255 bool ret;
2256 static const struct opt_items speed_option[19] = {
2257 { "0", -1 },
2258 { "1", -1 },
2259 { "2", -1 },
2260 { "3", -1 },
2261 { "4", -1 },
2262 { "5", -1 },
2263 { "6", -1 },
2264 { "7", -1 },
2265 { "8", -1 },
2266 { "9", -1 },
2267 { "10", -1 },
2268 { "11", -1 },
2269 { "12", -1 },
2270 { "13", -1 },
2271 { "14", -1 },
2272 { "15", -1 },
2273 { "16", -1 },
2274 { "17", -1 },
2275 { "18", -1 },
2279 speed = 20 - speed;
2280 ret = rb->set_option("Worm Speed", &speed,INT,speed_option , 19, NULL);
2281 speed = 20 - speed;
2282 return ret;
2286 Sets the control style, which depends on the number of players,
2287 remote control existing, etc
2288 bool set_bool_options(char* string, bool* variable,
2289 char* yes_str, char* no_str, void (*function)(bool))
2291 bool set_worm_control_style(void)
2293 static const struct opt_items key1_option[2] = {
2294 { "Remote Control", -1 },
2295 { "No Rem. Control", -1 },
2298 static const struct opt_items key2_option[2] = {
2299 { "2 Key Control", -1 },
2300 { "4 Key COntrol", -1 },
2303 static const struct opt_items key3_option[1] = {
2304 { "Out of Control", -1 },
2307 if (players > 1) {
2308 rb->set_option("Control Style",&use_remote,INT, key1_option, 2, NULL);
2309 } else {
2310 if (players > 0) {
2311 rb->set_option("Control Style",&use_remote,INT, key2_option, 2, NULL);
2313 else {
2314 rb->set_option("Control Style",&use_remote,INT, key3_option, 1, NULL);
2317 return false;
2320 void default_settings(void)
2322 arghs_per_food = ARGHS_PER_FOOD;
2323 argh_size = ARGH_SIZE;
2324 food_size = FOOD_SIZE;
2325 speed = SPEED;
2326 worm_food = WORM_PER_FOOD;
2327 players = 1;
2328 worm_count = MAX_WORMS;
2329 use_remote = false;
2330 return;
2334 Launches the wormlet game
2336 bool launch_wormlet(void)
2338 int game_result = 1;
2340 rb->lcd_clear_display();
2342 /* Turn off backlight timeout */
2343 backlight_force_on(rb); /* backlight control in lib/helper.c */
2345 /* start the game */
2346 while (game_result == 1)
2347 game_result = run();
2349 switch (game_result)
2351 case 2:
2352 /* Turn on backlight timeout (revert to settings) */
2353 backlight_use_settings(rb); /* backlight control in lib/helper.c */
2354 return false;
2355 break;
2357 return false;
2360 /* End of settings/changes etc */
2363 * Main entry point
2365 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
2367 int result;
2368 int menu_quit = 0;
2369 int new_setting;
2371 (void)(parameter);
2372 rb = api;
2374 default_settings();
2375 configfile_init(rb);
2376 if (configfile_load(SETTINGS_FILENAME, config,
2377 sizeof(config)/sizeof(*config),
2378 SETTINGS_MIN_VERSION ) < 0)
2380 /* If the loading failed, save a new config file (as the disk is
2381 already spinning) */
2382 configfile_save(SETTINGS_FILENAME, config,
2383 sizeof(config)/sizeof(*config),
2384 SETTINGS_VERSION);
2387 #ifdef HAVE_LCD_COLOR
2388 rb->lcd_set_foreground(COLOR_FG);
2389 rb->lcd_set_background(COLOR_BG);
2390 #endif
2392 #if LCD_DEPTH > 1
2393 rb->lcd_set_backdrop(NULL);
2394 #endif
2396 #ifdef DEBUG_WORMLET
2397 testline_in_rect();
2398 test_worm_argh_collision_in_moves();
2399 test_make_argh();
2400 test_worm_food_collision();
2401 test_worm_argh_collision();
2402 test_specific_worm_collision();
2403 #endif
2405 /* Setup screen */
2407 static const struct opt_items noyes[2] = {
2408 { "No", -1 },
2409 { "Yes", -1 },
2412 static const struct opt_items num_worms_option[3] = {
2413 { "1", -1 },
2414 { "2", -1 },
2415 { "3", -1 }
2418 #ifdef MULTIPLAYER
2419 static const struct opt_items num_players_option[4] = {
2420 #else
2421 static const struct opt_items num_players_option[2] = {
2422 #endif
2423 { "0", -1 },
2424 { "1", -1 }
2425 #ifdef MULTIPLAYER
2426 ,{ "2", -1 },
2427 { "3", -1 }
2428 #endif
2431 static const struct opt_items argh_size_option[9] = {
2432 { "2", -1 },
2433 { "3", -1 },
2434 { "4", -1 },
2435 { "5", -1 },
2436 { "6", -1 },
2437 { "7", -1 },
2438 { "8", -1 },
2439 { "9", -1 },
2440 { "10", -1 }
2443 static const struct opt_items food_size_option[9] = {
2444 { "2", -1 },
2445 { "3", -1 },
2446 { "4", -1 },
2447 { "5", -1 },
2448 { "6", -1 },
2449 { "7", -1 },
2450 { "8", -1 },
2451 { "9", -1 },
2452 { "10", -1 }
2455 static const struct opt_items worm_food_option[16] = {
2456 { "0", -1 },
2457 { "1", -1 },
2458 { "2", -1 },
2459 { "3", -1 },
2460 { "4", -1 },
2461 { "5", -1 },
2462 { "6", -1 },
2463 { "7", -1 },
2464 { "8", -1 },
2465 { "9", -1 },
2466 { "10", -1 },
2467 { "11", -1 },
2468 { "12", -1 },
2469 { "13", -1 },
2470 { "14", -1 },
2471 { "15", -1 }
2474 static const struct opt_items argh_food_option[9] = {
2475 { "0", -1 },
2476 { "1", -1 },
2477 { "2", -1 },
2478 { "3", -1 },
2479 { "4", -1 },
2480 { "5", -1 },
2481 { "6", -1 },
2482 { "7", -1 },
2483 { "8", -1 }
2486 static const struct opt_items speed_option[21] = {
2487 { "0", -1 },
2488 { "1", -1 },
2489 { "2", -1 },
2490 { "3", -1 },
2491 { "4", -1 },
2492 { "5", -1 },
2493 { "6", -1 },
2494 { "7", -1 },
2495 { "8", -1 },
2496 { "9", -1 },
2497 { "10", -1 },
2498 { "11", -1 },
2499 { "12", -1 },
2500 { "13", -1 },
2501 { "14", -1 },
2502 { "15", -1 },
2503 { "16", -1 },
2504 { "17", -1 },
2505 { "18", -1 },
2506 { "19", -1 },
2507 { "20", -1 }
2510 static const struct opt_items remoteonly_option[1] = {
2511 { "Remote Control", -1 }
2514 static const struct opt_items key24_option[2] = {
2515 { "4 Key Control", -1 },
2516 { "2 Key Control", -1 }
2519 #ifdef REMOTE
2520 static const struct opt_items remote_option[2] = {
2521 { "Remote Control", -1 },
2522 { "No Rem. Control", -1 }
2524 #else
2525 static const struct opt_items key2_option[1] = {
2526 { "2 Key Control", -1 }
2528 #endif
2530 static const struct opt_items nokey_option[1] = {
2531 { "Out of Control", -1 }
2534 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, "Play Wormlet!",
2535 "Number of Worms", "Number of Players", "Control Style",
2536 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2537 "Argh Size","Food Size","Revert to Default Settings",
2538 "Quit");
2540 rb->button_clear_queue();
2542 while (!menu_quit) {
2543 switch(rb->do_menu(&menu, &result))
2545 case 0:
2546 rb->lcd_setfont(FONT_SYSFIXED);
2547 launch_wormlet();
2548 break;
2549 case 1:
2550 new_setting = worm_count - 1;
2551 rb->set_option("Number of Worms", &new_setting, INT, num_worms_option, 3, NULL);
2552 if (new_setting != worm_count)
2553 worm_count = new_setting + 1;
2554 if (worm_count < players) {
2555 worm_count = players;
2557 break;
2558 case 2:
2559 new_setting = players;
2560 #ifdef MULTIPLAYER
2561 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 4, NULL);
2562 #else
2563 rb->set_option("Number of Players", &new_setting, INT, num_players_option , 2, NULL);
2564 #endif
2565 if (new_setting != players)
2566 players = new_setting;
2567 if (players > worm_count) {
2568 worm_count = players;
2570 if (players > 2) {
2571 use_remote = true;
2573 break;
2574 case 3:
2575 new_setting = use_remote;
2576 switch(players) {
2577 case 0:
2578 rb->set_option("Control Style",&new_setting,INT, nokey_option, 1, NULL);
2579 break;
2580 case 1:
2581 rb->set_option("Control Style",&new_setting,INT, key24_option, 2, NULL);
2582 break;
2583 case 2:
2584 #ifdef REMOTE
2585 rb->set_option("Control Style",&new_setting,INT, remote_option, 2, NULL);
2586 #else
2587 rb->set_option("Control Style",&new_setting,INT, key2_option, 1, NULL);
2588 #endif
2589 break;
2590 case 3:
2591 rb->set_option("Control Style",&new_setting,INT, remoteonly_option, 1, NULL);
2592 break;
2594 if (new_setting != use_remote)
2595 use_remote = new_setting;
2596 break;
2597 case 4:
2598 new_setting = worm_food;
2599 rb->set_option("Worm Growth Per Food", &new_setting,INT,worm_food_option , 16, NULL);
2600 if (new_setting != worm_food)
2601 worm_food = new_setting;
2602 break;
2603 case 5:
2604 new_setting = speed;
2605 new_setting = 20 - new_setting;
2606 rb->set_option("Worm Speed", &new_setting,INT,speed_option , 21, NULL);
2607 new_setting = 20 - new_setting;
2608 if (new_setting != speed)
2609 speed = new_setting;
2610 break;
2611 case 6:
2612 new_setting = arghs_per_food;
2613 rb->set_option("Arghs Per Food", &new_setting,INT,argh_food_option , 9, NULL);
2614 if (new_setting != arghs_per_food)
2615 arghs_per_food = new_setting;
2616 break;
2617 case 7:
2618 new_setting = argh_size-2;
2619 rb->set_option("Argh Size", &new_setting,INT,argh_size_option , 9, NULL);
2620 if (new_setting != argh_size)
2621 argh_size = new_setting+2;
2622 break;
2623 case 8:
2624 new_setting = food_size-2;
2625 rb->set_option("Food Size", &new_setting,INT,food_size_option, 9, NULL);
2626 if (new_setting != food_size)
2627 food_size = new_setting+2;
2628 break;
2629 case 9:
2630 new_setting = 0;
2631 rb->set_option("Reset Settings?", &new_setting,INT, noyes , 2, NULL);
2632 if (new_setting == 1)
2633 default_settings();
2634 break;
2635 default:
2636 menu_quit=1;
2637 break;
2641 configfile_save(SETTINGS_FILENAME, config,
2642 sizeof(config)/sizeof(*config),
2643 SETTINGS_VERSION);
2645 return PLUGIN_OK;