Device detection based on USB PIDs. This is currently linux only and requires libusb...
[Rockbox.git] / apps / plugins / wormlet.c
blob1c78f33ad045a8375d56c69dc173bcaf6765ca66
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)
153 #define BTN_DIR_UP BUTTON_UP
154 #define BTN_DIR_DOWN BUTTON_DOWN
155 #define BTN_DIR_LEFT BUTTON_LEFT
156 #define BTN_DIR_RIGHT BUTTON_RIGHT
157 #define BTN_STARTPAUSE BUTTON_SELECT
158 #define BTN_QUIT BUTTON_POWER
159 #define BTN_STOPRESET BUTTON_REC
161 #define PLAYERS_TEXT "Up/Down"
162 #define WORMS_TEXT "Left/Right"
165 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
167 #define BTN_DIR_UP BUTTON_SCROLL_UP
168 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
169 #define BTN_DIR_LEFT BUTTON_LEFT
170 #define BTN_DIR_RIGHT BUTTON_RIGHT
171 #define BTN_STARTPAUSE BUTTON_PLAY
172 #define BTN_QUIT BUTTON_POWER
173 #define BTN_STOPRESET BUTTON_REW
175 #define PLAYERS_TEXT "Up/Down"
176 #define WORMS_TEXT "Left/Right"
178 #endif
180 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
181 #define FOOD_SIZE 3
182 #define ARGH_SIZE 4
183 #define SPEED 14
184 #define MAX_WORM_SEGMENTS 128
185 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
186 #define FOOD_SIZE 4
187 #define ARGH_SIZE 5
188 #define SPEED 10
189 #define MAX_WORM_SEGMENTS 128
190 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
191 #define FOOD_SIZE 4
192 #define ARGH_SIZE 5
193 #define SPEED 9
194 #define MAX_WORM_SEGMENTS 128
195 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
196 #define FOOD_SIZE 4
197 #define ARGH_SIZE 5
198 #define SPEED 8
199 #define MAX_WORM_SEGMENTS 256
200 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
201 #define FOOD_SIZE 4
202 #define ARGH_SIZE 5
203 #define SPEED 6
204 #define MAX_WORM_SEGMENTS 256
205 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
206 #define FOOD_SIZE 5
207 #define ARGH_SIZE 6
208 #define SPEED 4
209 #define MAX_WORM_SEGMENTS 512
210 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
211 #define FOOD_SIZE 5
212 #define ARGH_SIZE 6
213 #define SPEED 4
214 #define MAX_WORM_SEGMENTS 512
215 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
216 #define FOOD_SIZE 7
217 #define ARGH_SIZE 8
218 #define SPEED 4
219 #define MAX_WORM_SEGMENTS 512
220 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
221 #define FOOD_SIZE 7
222 #define ARGH_SIZE 8
223 #define SPEED 4
224 #define MAX_WORM_SEGMENTS 512
225 #endif
227 #ifdef HAVE_LCD_COLOR
228 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
229 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
230 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
231 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
232 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
233 #endif
236 * All the properties that a worm has.
238 static struct worm {
239 /* The worm is stored in a ring of xy coordinates */
240 int x[MAX_WORM_SEGMENTS];
241 int y[MAX_WORM_SEGMENTS];
243 int head; /* index of the head within the buffer */
244 int tail; /* index of the tail within the buffer */
245 int growing; /* number of cyles the worm still keeps growing */
246 bool alive; /* the worms living state */
248 /* direction vector in which the worm moves */
249 int dirx; /* only values -1 0 1 allowed */
250 int diry; /* only values -1 0 1 allowed */
252 /* this method is used to fetch the direction the user
253 has selected. It can be one of the values
254 human_player1, human_player2, remote_player, virtual_player.
255 All these values are fuctions, that can change the direction
256 of the worm */
257 void (*fetch_worm_direction)(struct worm *w);
258 } worms[MAX_WORMS];
260 /* stores the highscore - besides it was scored by a virtual player */
261 static int highscore;
263 #define MAX_FOOD 5 /* maximal number of food items */
265 /* The arrays store the food coordinates */
266 static int foodx[MAX_FOOD];
267 static int foody[MAX_FOOD];
269 #define MAX_ARGH 100 /* maximal number of argh items */
270 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
272 /* The arrays store the argh coordinates */
273 static int arghx[MAX_ARGH];
274 static int arghy[MAX_ARGH];
276 /* the number of arghs that are currently in use */
277 static int argh_count;
279 /* the number of arghs per food, settable by user */
280 static int arghs_per_food = ARGHS_PER_FOOD;
281 /* the size of the argh, settable by user */
282 static int argh_size = ARGH_SIZE;
283 /* the size of the food, settable by user */
284 static int food_size = FOOD_SIZE;
285 /* the speed of the worm, settable by user */
286 static int speed = SPEED;
287 /* the amount a worm grows by eating a food, settable by user */
288 static int worm_food = WORM_PER_FOOD;
290 /* End additional variables */
292 #ifdef DEBUG_WORMLET
293 /* just a buffer used for debug output */
294 static char debugout[15];
295 #endif
297 /* the number of active worms (dead or alive) */
298 static int worm_count = MAX_WORMS;
300 /* in multiplayer mode: en- / disables the remote worm control
301 in singleplayer mode: toggles 4 / 2 button worm control */
302 static bool use_remote = false;
304 /* return values of check_collision */
305 #define COLLISION_NONE 0
306 #define COLLISION_WORM 1
307 #define COLLISION_FOOD 2
308 #define COLLISION_ARGH 3
309 #define COLLISION_FIELD 4
311 /* constants for use as directions.
312 Note that the values are ordered clockwise.
313 Thus increasing / decreasing the values
314 is equivalent to right / left turns. */
315 #define WEST 0
316 #define NORTH 1
317 #define EAST 2
318 #define SOUTH 3
320 /* direction of human player 1 */
321 static int player1_dir = EAST;
322 /* direction of human player 2 */
323 static int player2_dir = EAST;
324 /* direction of human player 3 */
325 static int player3_dir = EAST;
327 /* the number of (human) players that currently
328 control a worm */
329 static int players = 1;
331 /* the rockbox plugin api */
332 static struct plugin_api* rb;
334 #define SETTINGS_VERSION 1
335 #define SETTINGS_MIN_VERSION 1
336 #define SETTINGS_FILENAME "wormlet.cfg"
338 static struct configdata config[] =
340 {TYPE_INT, 0, 1024, &highscore, "highscore", NULL, NULL},
341 {TYPE_INT, 0, 15, &arghs_per_food, "arghs per food", NULL, NULL},
342 {TYPE_INT, 0, 15, &argh_size, "argh size", NULL, NULL},
343 {TYPE_INT, 0, 15, &food_size, "food size", NULL, NULL},
344 {TYPE_INT, 0, 3, &players, "players", NULL, NULL},
345 {TYPE_INT, 0, 3, &worm_count, "worms", NULL, NULL},
346 {TYPE_INT, 0, 20, &speed, "speed", NULL, NULL},
347 {TYPE_INT, 0, 15, &worm_food, "Worm Growth Per Food", NULL, NULL}//,
348 //{TYPE_INT, 0, 3, &use_remote, "use remote", NULL, NULL}
351 #ifdef DEBUG_WORMLET
352 static void set_debug_out(char *str){
353 strcpy(debugout, str);
355 #endif
358 * Returns the direction id in which the worm
359 * currently is creeping.
360 * @param struct worm *w The worm that is to be investigated.
361 * w Must not be null.
362 * @return int A value 0 <= value < 4
363 * Note the predefined constants NORTH, SOUTH, EAST, WEST
365 static int get_worm_dir(struct worm *w) {
366 int retVal ;
367 if (w->dirx == 0) {
368 if (w->diry == 1) {
369 retVal = SOUTH;
370 } else {
371 retVal = NORTH;
373 } else {
374 if (w->dirx == 1) {
375 retVal = EAST;
376 } else {
377 retVal = WEST;
380 return retVal;
384 * Set the direction of the specified worm with a direction id.
385 * Increasing the value by 1 means to turn the worm direction
386 * to right by 90 degree.
387 * @param struct worm *w The worm that is to be altered. w Must not be null.
388 * @param int dir The new direction in which the worm is to creep.
389 * dir must be 0 <= dir < 4. Use predefined constants
390 * NORTH, SOUTH, EAST, WEST
392 static void set_worm_dir(struct worm *w, int dir) {
393 switch (dir) {
394 case WEST:
395 w->dirx = -1;
396 w->diry = 0;
397 break;
398 case NORTH:
399 w->dirx = 0;
400 w->diry = - 1;
401 break;
402 case EAST:
403 w->dirx = 1;
404 w->diry = 0;
405 break;
406 case SOUTH:
407 w->dirx = 0;
408 w->diry = 1;
409 break;
414 * Returns the current length of the worm array. This
415 * is also a value for the number of bends that are in the worm.
416 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
418 static int get_worm_array_length(struct worm *w) {
419 /* initial simple calculation will be overwritten if wrong. */
420 int retVal = w->head - w->tail;
422 /* if the worm 'crosses' the boundaries of the ringbuffer */
423 if (retVal < 0) {
424 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
427 return retVal;
431 * Returns the score the specified worm. The score is the length
432 * of the worm.
433 * @param struct worm *w The worm that is to be investigated.
434 * w must not be null.
435 * @return int The length of the worm (>= 0).
437 static int get_score(struct worm *w) {
438 int retval = 0;
439 int length = get_worm_array_length(w);
440 int i;
441 for (i = 0; i < length; i++) {
443 /* The iteration iterates the length of the worm.
444 Here's the conversion to the true indices within the worm arrays. */
445 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
446 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
447 int startx = w->x[linestart];
448 int starty = w->y[linestart];
449 int endx = w->x[lineend];
450 int endy = w->y[lineend];
452 int minimum, maximum;
454 if (startx == endx) {
455 minimum = MIN(starty, endy);
456 maximum = MAX(starty, endy);
457 } else {
458 minimum = MIN(startx, endx);
459 maximum = MAX(startx, endx);
461 retval += abs(maximum - minimum);
463 return retval;
467 * Determines wether the line specified by startx, starty, endx, endy intersects
468 * the rectangle specified by x, y, width, height. Note that the line must be exactly
469 * horizontal or vertical (startx == endx or starty == endy).
470 * @param int startx The x coordinate of the start point of the line.
471 * @param int starty The y coordinate of the start point of the line.
472 * @param int endx The x coordinate of the end point of the line.
473 * @param int endy The y coordinate of the end point of the line.
474 * @param int x The x coordinate of the top left corner of the rectangle.
475 * @param int y The y coordinate of the top left corner of the rectangle.
476 * @param int width The width of the rectangle.
477 * @param int height The height of the rectangle.
478 * @return bool Returns true if the specified line intersects with the recangle.
480 static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) {
481 bool retval = false;
482 int simple, simplemin, simplemax;
483 int compa, compb, compmin, compmax;
484 int temp;
485 if (startx == endx) {
486 simple = startx;
487 simplemin = x;
488 simplemax = x + width;
490 compa = starty;
491 compb = endy;
492 compmin = y;
493 compmax = y + height;
494 } else {
495 simple = starty;
496 simplemin = y;
497 simplemax = y + height;
499 compa = startx;
500 compb = endx;
501 compmin = x;
502 compmax = x + width;
505 temp = compa;
506 compa = MIN(compa, compb);
507 compb = MAX(temp, compb);
509 if (simplemin <= simple && simple <= simplemax) {
510 if ((compmin <= compa && compa <= compmax) ||
511 (compmin <= compb && compb <= compmax) ||
512 (compa <= compmin && compb >= compmax)) {
513 retval = true;
516 return retval;
520 * Tests wether the specified worm intersects with the rect.
521 * @param struct worm *w The worm to be investigated
522 * @param int x The x coordinate of the top left corner of the rect
523 * @param int y The y coordinate of the top left corner of the rect
524 * @param int widht The width of the rect
525 * @param int height The height of the rect
526 * @return bool Returns true if the worm intersects with the rect
528 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) {
529 bool retval = false;
532 /* get_worm_array_length is expensive -> buffer the value */
533 int wormLength = get_worm_array_length(w);
534 int i;
536 /* test each entry that is part of the worm */
537 for (i = 0; i < wormLength && retval == false; i++) {
539 /* The iteration iterates the length of the worm.
540 Here's the conversion to the true indices within the worm arrays. */
541 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
542 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
543 int startx = w->x[linestart];
544 int starty = w->y[linestart];
545 int endx = w->x[lineend];
546 int endy = w->y[lineend];
548 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
551 return retval;
555 * Checks wether a specific food in the food arrays is at the
556 * specified coordinates.
557 * @param int foodIndex The index of the food in the food arrays
558 * @param int x the x coordinate.
559 * @param int y the y coordinate.
560 * @return Returns true if the coordinate hits the food specified by
561 * foodIndex.
563 static bool specific_food_collision(int foodIndex, int x, int y) {
564 bool retVal = false;
565 if (x >= foodx[foodIndex] &&
566 x < foodx[foodIndex] + food_size &&
567 y >= foody[foodIndex] &&
568 y < foody[foodIndex] + food_size) {
570 retVal = true;
572 return retVal;
576 * Returns the index of the food that is at the
577 * given coordinates. If no food is at the coordinates
578 * -1 is returned.
579 * @return int -1 <= value < MAX_FOOD
581 static int food_collision(int x, int y) {
582 int i = 0;
583 int retVal = -1;
584 for (i = 0; i < MAX_FOOD; i++) {
585 if (specific_food_collision(i, x, y)) {
586 retVal = i;
587 break;
590 return retVal;
594 * Checks wether a specific argh in the argh arrays is at the
595 * specified coordinates.
596 * @param int arghIndex The index of the argh in the argh arrays
597 * @param int x the x coordinate.
598 * @param int y the y coordinate.
599 * @return Returns true if the coordinate hits the argh specified by
600 * arghIndex.
602 static bool specific_argh_collision(int arghIndex, int x, int y) {
604 if ( x >= arghx[arghIndex] &&
605 y >= arghy[arghIndex] &&
606 x < arghx[arghIndex] + argh_size &&
607 y < arghy[arghIndex] + argh_size )
609 return true;
612 return false;
616 * Returns the index of the argh that is at the
617 * given coordinates. If no argh is at the coordinates
618 * -1 is returned.
619 * @param int x The x coordinate.
620 * @param int y The y coordinate.
621 * @return int -1 <= value < argh_count <= MAX_ARGH
623 static int argh_collision(int x, int y) {
624 int i = 0;
625 int retVal = -1;
627 /* search for the argh that has the specified coords */
628 for (i = 0; i < argh_count; i++) {
629 if (specific_argh_collision(i, x, y)) {
630 retVal = i;
631 break;
634 return retVal;
638 * Checks wether the worm collides with the food at the specfied food-arrays.
639 * @param int foodIndex The index of the food in the arrays. Ensure the value is
640 * 0 <= foodIndex <= MAX_FOOD
641 * @return Returns true if the worm collides with the specified food.
643 static bool worm_food_collision(struct worm *w, int foodIndex)
645 bool retVal = false;
647 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
648 food_size - 1, food_size - 1);
650 return retVal;
654 * Returns true if the worm hits the argh within the next moves (unless
655 * the worm changes it's direction).
656 * @param struct worm *w - The worm to investigate
657 * @param int argh_idx - The index of the argh
658 * @param int moves - The number of moves that are considered.
659 * @return Returns false if the specified argh is not hit within the next
660 * moves.
662 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){
663 bool retVal = false;
664 int x1, y1, x2, y2;
665 x1 = w->x[w->head];
666 y1 = w->y[w->head];
668 x2 = w->x[w->head] + moves * w->dirx;
669 y2 = w->y[w->head] + moves * w->diry;
671 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
672 argh_size, argh_size);
673 return retVal;
677 * Checks wether the worm collides with the argh at the specfied argh-arrays.
678 * @param int arghIndex The index of the argh in the arrays.
679 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
680 * @return Returns true if the worm collides with the specified argh.
682 static bool worm_argh_collision(struct worm *w, int arghIndex)
684 bool retVal = false;
686 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
687 argh_size - 1, argh_size - 1);
689 return retVal;
693 * Find new coordinates for the food stored in foodx[index], foody[index]
694 * that don't collide with any other food or argh
695 * @param int index
696 * Ensure that 0 <= index < MAX_FOOD.
698 static void make_food(int index) {
700 int x = 0;
701 int y = 0;
702 bool collisionDetected = false;
703 int i;
705 do {
706 /* make coordinates for a new food so that
707 the entire food lies within the FIELD */
708 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
709 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
711 /* Ensure that the new food doesn't collide with any
712 existing foods or arghs.
713 If one or more corners of the new food hit any existing
714 argh or food a collision is detected.
716 collisionDetected =
717 food_collision(x , y ) >= 0 ||
718 food_collision(x , y + food_size - 1) >= 0 ||
719 food_collision(x + food_size - 1, y ) >= 0 ||
720 food_collision(x + food_size - 1, y + food_size - 1) >= 0 ||
721 argh_collision(x , y ) >= 0 ||
722 argh_collision(x , y + food_size - 1) >= 0 ||
723 argh_collision(x + food_size - 1, y ) >= 0 ||
724 argh_collision(x + food_size - 1, y + food_size - 1) >= 0;
726 /* use coordinates for further testing */
727 foodx[index] = x;
728 foody[index] = y;
730 /* now test wether we accidently hit the worm with food ;) */
731 i = 0;
732 for (i = 0; i < worm_count && !collisionDetected; i++) {
733 collisionDetected |= worm_food_collision(&worms[i], index);
736 while (collisionDetected);
737 return;
741 * Clears a food from the lcd buffer.
742 * @param int index The index of the food arrays under which
743 * the coordinates of the desired food can be found. Ensure
744 * that the value is 0 <= index <= MAX_FOOD.
746 static void clear_food(int index)
748 /* remove the old food from the screen */
749 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
750 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
751 foody[index] + FIELD_RECT_Y,
752 food_size, food_size);
753 rb->lcd_set_drawmode(DRMODE_SOLID);
757 * Draws a food in the lcd buffer.
758 * @param int index The index of the food arrays under which
759 * the coordinates of the desired food can be found. Ensure
760 * that the value is 0 <= index <= MAX_FOOD.
762 static void draw_food(int index)
764 /* draw the food object */
765 #ifdef HAVE_LCD_COLOR
766 rb->lcd_set_foreground(COLOR_FOOD);
767 #endif
768 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
769 foody[index] + FIELD_RECT_Y,
770 food_size, food_size);
771 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
772 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
773 foody[index] + FIELD_RECT_Y + 1,
774 food_size - 2, food_size - 2);
775 rb->lcd_set_drawmode(DRMODE_SOLID);
776 #ifdef HAVE_LCD_COLOR
777 rb->lcd_set_foreground(COLOR_FG);
778 #endif
782 * Find new coordinates for the argh stored in arghx[index], arghy[index]
783 * that don't collide with any other food or argh.
784 * @param int index
785 * Ensure that 0 <= index < argh_count < MAX_ARGH.
787 static void make_argh(int index)
789 int x = -1;
790 int y = -1;
791 bool collisionDetected = false;
792 int i;
794 do {
795 /* make coordinates for a new argh so that
796 the entire food lies within the FIELD */
797 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
798 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
800 /* Ensure that the new argh doesn't intersect with any
801 existing foods or arghs.
802 If one or more corners of the new argh hit any existing
803 argh or food an intersection is detected.
805 collisionDetected =
806 food_collision(x , y ) >= 0 ||
807 food_collision(x , y + argh_size - 1) >= 0 ||
808 food_collision(x + argh_size - 1, y ) >= 0 ||
809 food_collision(x + argh_size - 1, y + argh_size - 1) >= 0 ||
810 argh_collision(x , y ) >= 0 ||
811 argh_collision(x , y + argh_size - 1) >= 0 ||
812 argh_collision(x + argh_size - 1, y ) >= 0 ||
813 argh_collision(x + argh_size - 1, y + argh_size - 1) >= 0;
815 /* use the candidate coordinates to make a real argh */
816 arghx[index] = x;
817 arghy[index] = y;
819 /* now test wether we accidently hit the worm with argh ;) */
820 for (i = 0; i < worm_count && !collisionDetected; i++) {
821 collisionDetected |= worm_argh_collision(&worms[i], index);
822 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
823 MIN_ARGH_DIST);
826 while (collisionDetected);
827 return;
831 * Draws an argh in the lcd buffer.
832 * @param int index The index of the argh arrays under which
833 * the coordinates of the desired argh can be found. Ensure
834 * that the value is 0 <= index < argh_count <= MAX_ARGH.
836 static void draw_argh(int index)
838 /* draw the new argh */
839 #ifdef HAVE_LCD_COLOR
840 rb->lcd_set_foreground(COLOR_ARGH);
841 #endif
842 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
843 arghy[index] + FIELD_RECT_Y,
844 argh_size, argh_size);
845 #ifdef HAVE_LCD_COLOR
846 rb->lcd_set_foreground(COLOR_FG);
847 #endif
850 static void virtual_player(struct worm *w);
852 * Initialzes the specified worm with INITIAL_WORM_LENGTH
853 * and the tail at the specified position. The worm will
854 * be initialized alive and creeping EAST.
855 * @param struct worm *w The worm that is to be initialized
856 * @param int x The x coordinate at which the tail of the worm starts.
857 * x must be 0 <= x < FIELD_RECT_WIDTH.
858 * @param int y The y coordinate at which the tail of the worm starts
859 * y must be 0 <= y < FIELD_RECT_WIDTH.
861 static void init_worm(struct worm *w, int x, int y){
862 /* initialize the worm size */
863 w->head = 1;
864 w->tail = 0;
866 w->x[w->head] = x + 1;
867 w->y[w->head] = y;
869 w->x[w->tail] = x;
870 w->y[w->tail] = y;
872 /* set the initial direction the worm creeps to */
873 w->dirx = 1;
874 w->diry = 0;
876 w->growing = INITIAL_WORM_LENGTH - 1;
877 w->alive = true;
878 w->fetch_worm_direction = virtual_player;
882 * Writes the direction that was stored for
883 * human player 1 into the specified worm. This function
884 * may be used to be stored in worm.fetch_worm_direction.
885 * The value of
886 * the direction is read from player1_dir.
887 * @param struct worm *w - The worm of which the direction
888 * is altered.
890 static void human_player1(struct worm *w) {
891 set_worm_dir(w, player1_dir);
895 * Writes the direction that was stored for
896 * human player 2 into the specified worm. This function
897 * may be used to be stored in worm.fetch_worm_direction.
898 * The value of
899 * the direction is read from player2_dir.
900 * @param struct worm *w - The worm of which the direction
901 * is altered.
903 static void human_player2(struct worm *w) {
904 set_worm_dir(w, player2_dir);
908 * Writes the direction that was stored for
909 * human player using a remote control
910 * into the specified worm. This function
911 * may be used to be stored in worm.fetch_worm_direction.
912 * The value of
913 * the direction is read from player3_dir.
914 * @param struct worm *w - The worm of which the direction
915 * is altered.
917 static void remote_player(struct worm *w) {
918 set_worm_dir(w, player3_dir);
922 * Initializes the worm-, food- and argh-arrays, draws a frame,
923 * makes some food and argh and display all that stuff.
925 static void init_wormlet(void)
927 int i;
929 for (i = 0; i< worm_count; i++) {
930 /* Initialize all the worm coordinates to center. */
931 int x = (int)(FIELD_RECT_WIDTH / 2);
932 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
934 init_worm(&worms[i], x, y);
937 player1_dir = EAST;
938 player2_dir = EAST;
939 player3_dir = EAST;
941 if (players > 0) {
942 worms[0].fetch_worm_direction = human_player1;
945 if (players > 1) {
946 if (use_remote) {
947 worms[1].fetch_worm_direction = remote_player;
948 } else {
949 worms[1].fetch_worm_direction = human_player2;
953 if (players > 2) {
954 worms[2].fetch_worm_direction = human_player2;
957 /* Needed when the game is restarted using BTN_STOPRESET */
958 rb->lcd_clear_display();
960 /* make and display some food and argh */
961 argh_count = MAX_FOOD;
962 for (i = 0; i < MAX_FOOD; i++) {
963 make_food(i);
964 draw_food(i);
965 make_argh(i);
966 draw_argh(i);
969 /* draw the game field */
970 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
971 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
972 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
973 rb->lcd_set_drawmode(DRMODE_SOLID);
975 /* make everything visible */
976 rb->lcd_update();
981 * Move the worm one step further if it is alive.
982 * The direction in which the worm moves is taken from dirx and diry.
983 * move_worm decreases growing if > 0. While the worm is growing the tail
984 * is left untouched.
985 * @param struct worm *w The worm to move. w must not be NULL.
987 static void move_worm(struct worm *w)
989 if (w->alive) {
990 /* determine the head point and its precessor */
991 int headx = w->x[w->head];
992 int heady = w->y[w->head];
993 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
994 int preheadx = w->x[prehead];
995 int preheady = w->y[prehead];
997 /* determine the old direction */
998 int olddirx;
999 int olddiry;
1000 if (headx == preheadx) {
1001 olddirx = 0;
1002 olddiry = (heady > preheady) ? 1 : -1;
1003 } else {
1004 olddiry = 0;
1005 olddirx = (headx > preheadx) ? 1 : -1;
1008 /* olddir == dir?
1009 a change of direction means a new segment
1010 has been opened */
1011 if (olddirx != w->dirx ||
1012 olddiry != w->diry) {
1013 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1016 /* new head position */
1017 w->x[w->head] = headx + w->dirx;
1018 w->y[w->head] = heady + w->diry;
1021 /* while the worm is growing no tail procession is necessary */
1022 if (w->growing > 0) {
1023 /* update the worms grow state */
1024 w->growing--;
1027 /* if the worm isn't growing the tail has to be dragged */
1028 else {
1029 /* index of the end of the tail segment */
1030 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1032 /* drag the end of the tail */
1033 /* only one coordinate has to be altered. Here it is
1034 determined which one */
1035 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1036 if (w->x[w->tail] == w->x[tail_segment_end]) {
1037 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1038 w->y[w->tail] += dir;
1039 } else {
1040 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1041 w->x[w->tail] += dir;
1044 /* when the tail has been dragged so far that it meets
1045 the next segment start the tail segment is obsolete and
1046 must be freed */
1047 if (w->x[w->tail] == w->x[tail_segment_end] &&
1048 w->y[w->tail] == w->y[tail_segment_end]){
1050 /* drop the last tail point */
1051 w->tail = tail_segment_end;
1058 * Draws the head and clears the tail of the worm in
1059 * the display buffer. lcd_update() is NOT called thus
1060 * the caller has to take care that the buffer is displayed.
1062 static void draw_worm(struct worm *w)
1064 #ifdef HAVE_LCD_COLOR
1065 rb->lcd_set_foreground(COLOR_WORM);
1066 #endif
1067 /* draw the new head */
1068 int x = w->x[w->head];
1069 int y = w->y[w->head];
1070 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1071 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1074 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1076 /* clear the space behind the worm */
1077 x = w->x[w->tail] ;
1078 y = w->y[w->tail] ;
1079 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1080 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1082 rb->lcd_set_drawmode(DRMODE_SOLID);
1083 #ifdef HAVE_LCD_COLOR
1084 rb->lcd_set_foreground(COLOR_FG);
1085 #endif
1089 * Checks wether the coordinate is part of the worm. Returns
1090 * true if any part of the worm was hit - including the head.
1091 * @param x int The x coordinate
1092 * @param y int The y coordinate
1093 * @return int The index of the worm arrays that contain x, y.
1094 * Returns -1 if the coordinates are not part of the worm.
1096 static int specific_worm_collision(struct worm *w, int x, int y)
1098 int retVal = -1;
1100 /* get_worm_array_length is expensive -> buffer the value */
1101 int wormLength = get_worm_array_length(w);
1102 int i;
1104 /* test each entry that is part of the worm */
1105 for (i = 0; i < wormLength && retVal == -1; i++) {
1107 /* The iteration iterates the length of the worm.
1108 Here's the conversion to the true indices within the worm arrays. */
1109 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1110 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1111 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1112 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1113 if (samex || samey){
1114 int test, min, max, tmp;
1116 if (samey) {
1117 min = w->x[linestart];
1118 max = w->x[lineend];
1119 test = x;
1120 } else {
1121 min = w->y[linestart];
1122 max = w->y[lineend];
1123 test = y;
1126 tmp = min;
1127 min = MIN(min, max);
1128 max = MAX(tmp, max);
1130 if (min <= test && test <= max) {
1131 retVal = lineend;
1135 return retVal;
1139 * Increases the length of the specified worm by marking
1140 * that it may grow by len pixels. Note that the worm has
1141 * to move to make the growing happen.
1142 * @param worm *w The worm that is to be altered.
1143 * @param int len A positive value specifying the amount of
1144 * pixels the worm may grow.
1146 static void add_growing(struct worm *w, int len) {
1147 w->growing += len;
1151 * Determins the worm that is at the coordinates x, y. The parameter
1152 * w is a switch parameter that changes the functionality of worm_collision.
1153 * If w is specified and x,y hits the head of w NULL is returned.
1154 * This is a useful way to determine wether the head of w hits
1155 * any worm but including itself but excluding its own head.
1156 * (It hits always its own head ;))
1157 * If w is set to NULL worm_collision returns any worm including all heads
1158 * that is at position of x,y.
1159 * @param struct worm *w The worm of which the head should be excluded in
1160 * the test. w may be set to NULL.
1161 * @param int x The x coordinate that is checked
1162 * @param int y The y coordinate that is checkec
1163 * @return struct worm* The worm that has been hit by x,y. If no worm
1164 * was at the position NULL is returned.
1166 static struct worm* worm_collision(struct worm *w, int x, int y){
1167 struct worm *retVal = NULL;
1168 int i;
1169 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1170 int collision_at = specific_worm_collision(&worms[i], x, y);
1171 if (collision_at != -1) {
1172 if (!(w == &worms[i] && collision_at == w->head)){
1173 retVal = &worms[i];
1177 return retVal;
1181 * Returns true if the head of the worm just has
1182 * crossed the field boundaries.
1183 * @return bool true if the worm just has wrapped.
1185 static bool field_collision(struct worm *w)
1187 bool retVal = false;
1188 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1189 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1190 (w->x[w->head] < 0) ||
1191 (w->y[w->head] < 0))
1193 retVal = true;
1195 return retVal;
1200 * Returns true if the specified coordinates are within the
1201 * field specified by the FIELD_RECT_XXX constants.
1202 * @param int x The x coordinate of the point that is investigated
1203 * @param int y The y coordinate of the point that is investigated
1204 * @return bool Returns false if x,y specifies a point outside the
1205 * field of worms.
1207 static bool is_in_field_rect(int x, int y) {
1208 bool retVal = false;
1209 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1210 y >= 0 && y < FIELD_RECT_HEIGHT);
1211 return retVal;
1215 * Checks and returns wether the head of the w
1216 * is colliding with something currently.
1217 * @return int One of the values:
1218 * COLLISION_NONE
1219 * COLLISION_w
1220 * COLLISION_FOOD
1221 * COLLISION_ARGH
1222 * COLLISION_FIELD
1224 static int check_collision(struct worm *w)
1226 int retVal = COLLISION_NONE;
1228 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1229 retVal = COLLISION_WORM;
1231 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1232 retVal = COLLISION_FOOD;
1234 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1235 retVal = COLLISION_ARGH;
1237 if (field_collision(w))
1238 retVal = COLLISION_FIELD;
1240 return retVal;
1244 * Returns the index of the food that is closest to the point
1245 * specified by x, y. This index may be used in the foodx and
1246 * foody arrays.
1247 * @param int x The x coordinate of the point
1248 * @param int y The y coordinate of the point
1249 * @return int A value usable as index in foodx and foody.
1251 static int get_nearest_food(int x, int y){
1252 int nearestfood = 0;
1253 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1254 int deltax = 0;
1255 int deltay = 0;
1256 int foodindex;
1257 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1258 int distance;
1259 deltax = foodx[foodindex] - x;
1260 deltay = foody[foodindex] - y;
1261 deltax = deltax > 0 ? deltax : deltax * (-1);
1262 deltay = deltay > 0 ? deltay : deltay * (-1);
1263 distance = deltax + deltay;
1265 if (distance < olddistance) {
1266 olddistance = distance;
1267 nearestfood = foodindex;
1270 return nearestfood;
1274 * Returns wether the specified position is next to the worm
1275 * and in the direction the worm looks. Use this method to
1276 * test wether this position would be hit with the next move of
1277 * the worm unless the worm changes its direction.
1278 * @param struct worm *w - The worm to be investigated
1279 * @param int x - The x coordinate of the position to test.
1280 * @param int y - The y coordinate of the position to test.
1281 * @return Returns true if the worm will hit the position unless
1282 * it change its direction before the next move.
1284 static bool is_in_front_of_worm(struct worm *w, int x, int y) {
1285 bool infront = false;
1286 int deltax = x - w->x[w->head];
1287 int deltay = y - w->y[w->head];
1289 if (w->dirx == 0) {
1290 infront = (w->diry * deltay) > 0;
1291 } else {
1292 infront = (w->dirx * deltax) > 0;
1294 return infront;
1298 * Returns true if the worm will collide with the next move unless
1299 * it changes its direction.
1300 * @param struct worm *w - The worm to be investigated.
1301 * @return Returns true if the worm will collide with the next move
1302 * unless it changes its direction.
1304 static bool will_worm_collide(struct worm *w) {
1305 int x = w->x[w->head] + w->dirx;
1306 int y = w->y[w->head] + w->diry;
1307 bool retVal = !is_in_field_rect(x, y);
1308 if (!retVal) {
1309 retVal = (argh_collision(x, y) != -1);
1312 if (!retVal) {
1313 retVal = (worm_collision(w, x, y) != NULL);
1315 return retVal;
1319 * This function
1320 * may be used to be stored in worm.fetch_worm_direction for
1321 * worms that are not controlled by humans but by artificial stupidity.
1322 * A direction is searched that doesn't lead to collision but to the nearest
1323 * food - but not very intelligent. The direction is written to the specified
1324 * worm.
1325 * @param struct worm *w - The worm of which the direction
1326 * is altered.
1328 static void virtual_player(struct worm *w) {
1329 bool isright;
1330 int plana, planb, planc;
1331 /* find the next lunch */
1332 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1334 /* determine in which direction it is */
1336 /* in front of me? */
1337 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1339 /* left right of me? */
1340 int olddir = get_worm_dir(w);
1341 set_worm_dir(w, (olddir + 1) % 4);
1342 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1343 set_worm_dir(w, olddir);
1345 /* detect situation, set strategy */
1346 if (infront) {
1347 if (isright) {
1348 plana = olddir;
1349 planb = (olddir + 1) % 4;
1350 planc = (olddir + 3) % 4;
1351 } else {
1352 plana = olddir;
1353 planb = (olddir + 3) % 4;
1354 planc = (olddir + 1) % 4;
1356 } else {
1357 if (isright) {
1358 plana = (olddir + 1) % 4;
1359 planb = olddir;
1360 planc = (olddir + 3) % 4;
1361 } else {
1362 plana = (olddir + 3) % 4;
1363 planb = olddir;
1364 planc = (olddir + 1) % 4;
1368 /* test for collision */
1369 set_worm_dir(w, plana);
1370 if (will_worm_collide(w)){
1372 /* plan b */
1373 set_worm_dir(w, planb);
1375 /* test for collision */
1376 if (will_worm_collide(w)) {
1378 /* plan c */
1379 set_worm_dir(w, planc);
1385 * prints out the score board with all the status information
1386 * about the game.
1388 static void score_board(void)
1390 char buf[15];
1391 char* buf2 = NULL;
1392 int i;
1393 int y = 0;
1394 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1395 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1396 rb->lcd_set_drawmode(DRMODE_SOLID);
1397 for (i = 0; i < worm_count; i++) {
1398 int score = get_score(&worms[i]);
1400 /* high score */
1401 if (worms[i].fetch_worm_direction != virtual_player){
1402 if (highscore < score) {
1403 highscore = score;
1407 /* length */
1408 rb->snprintf(buf, sizeof (buf),"Len:%d", score);
1410 /* worm state */
1411 switch (check_collision(&worms[i])) {
1412 case COLLISION_NONE:
1413 if (worms[i].growing > 0)
1414 buf2 = "Growing";
1415 else {
1416 if (worms[i].alive)
1417 buf2 = "Hungry";
1418 else
1419 buf2 = "Wormed";
1421 break;
1423 case COLLISION_WORM:
1424 buf2 = "Wormed";
1425 break;
1427 case COLLISION_FOOD:
1428 buf2 = "Growing";
1429 break;
1431 case COLLISION_ARGH:
1432 buf2 = "Argh";
1433 break;
1435 case COLLISION_FIELD:
1436 buf2 = "Crashed";
1437 break;
1439 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf);
1440 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2);
1442 if (!worms[i].alive){
1443 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1444 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1445 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1446 rb->lcd_set_drawmode(DRMODE_SOLID);
1448 y += 19;
1450 rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore);
1451 #ifndef DEBUG_WORMLET
1452 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);
1453 #else
1454 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
1455 #endif
1459 * Checks for collisions of the worm and its environment and
1460 * takes appropriate actions like growing the worm or killing it.
1461 * @return bool Returns true if the worm is dead. Returns
1462 * false if the worm is healthy, up and creeping.
1464 static bool process_collisions(struct worm *w)
1466 int index = -1;
1468 w->alive &= !field_collision(w);
1470 if (w->alive) {
1472 /* check if food was eaten */
1473 index = food_collision(w->x[w->head], w->y[w->head]);
1474 if (index != -1){
1475 int i;
1477 clear_food(index);
1478 make_food(index);
1479 draw_food(index);
1481 for (i = 0; i < arghs_per_food; i++) {
1482 argh_count++;
1483 if (argh_count > MAX_ARGH)
1484 argh_count = MAX_ARGH;
1485 make_argh(argh_count - 1);
1486 draw_argh(argh_count - 1);
1489 add_growing(w, worm_food);
1491 draw_worm(w);
1494 /* check if argh was eaten */
1495 else {
1496 index = argh_collision(w->x[w->head], w->y[w->head]);
1497 if (index != -1) {
1498 w->alive = false;
1500 else {
1501 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1502 w->alive = false;
1507 return !w->alive;
1511 * The main loop of the game.
1512 * @return bool Returns true if the game ended
1513 * with a dead worm. Returns false if the user
1514 * aborted the game manually.
1516 static int run(void)
1518 int button = 0;
1519 int wormDead = false;
1520 bool paused = false;
1522 /* ticks are counted to compensate speed variations */
1523 long cycle_start = 0, cycle_end = 0;
1524 #ifdef DEBUG_WORMLET
1525 int ticks_to_max_cycle_reset = 20;
1526 long max_cycle = 0;
1527 char buf[20];
1528 #endif
1530 /* initialize the board and so on */
1531 init_wormlet();
1533 cycle_start = *rb->current_tick;
1534 /* change the direction of the worm */
1535 while (!wormDead)
1537 int i;
1538 long cycle_duration=0;
1540 #ifdef HAS_BUTTON_HOLD
1541 if (rb->button_hold())
1542 paused = true;
1543 #endif
1545 switch (button) {
1546 case BTN_STARTPAUSE:
1547 paused = !paused;
1548 break;
1549 case BTN_STOPRESET:
1550 if (paused)
1551 return 1; /* restart game */
1552 else
1553 paused = true;
1554 break;
1555 #ifdef BTN_RC_QUIT
1556 case BTN_RC_QUIT:
1557 #endif
1558 case BTN_QUIT:
1559 return 2; /* back to menu */
1560 break;
1562 if (!paused)
1564 switch (button) {
1565 case BTN_DIR_UP:
1566 if (players == 1 && !use_remote) {
1567 player1_dir = NORTH;
1569 break;
1571 case BTN_DIR_DOWN:
1572 if (players == 1 && !use_remote) {
1573 player1_dir = SOUTH;
1575 break;
1577 case BTN_DIR_LEFT:
1578 if (players != 1 || use_remote) {
1579 player1_dir = (player1_dir + 3) % 4;
1580 } else {
1581 player1_dir = WEST;
1583 break;
1585 case BTN_DIR_RIGHT:
1586 if (players != 1 || use_remote) {
1587 player1_dir = (player1_dir + 1) % 4;
1588 } else {
1589 player1_dir = EAST;
1591 break;
1593 #ifdef MULTIPLAYER
1594 case BTN_PLAYER2_DIR1:
1595 player2_dir = (player2_dir + 3) % 4;
1596 break;
1598 case BTN_PLAYER2_DIR2:
1599 player2_dir = (player2_dir + 1) % 4;
1600 break;
1601 #endif
1603 #ifdef REMOTE
1604 case BTN_RC_UP:
1605 player3_dir = (player3_dir + 1) % 4;
1606 break;
1608 case BTN_RC_DOWN:
1609 player3_dir = (player3_dir + 3) % 4;
1610 break;
1611 #endif
1615 for (i = 0; i < worm_count; i++) {
1616 worms[i].fetch_worm_direction(&worms[i]);
1619 wormDead = true;
1620 for (i = 0; i < worm_count; i++){
1621 struct worm *w = &worms[i];
1622 move_worm(w);
1623 wormDead &= process_collisions(w);
1624 draw_worm(w);
1626 score_board();
1627 rb->lcd_update();
1628 if (button == BTN_STOPRESET) {
1629 wormDead = true;
1632 /* here the wormlet game cycle ends
1633 thus the current tick is stored
1634 as end time */
1635 cycle_end = *rb->current_tick;
1637 /* The duration of the game cycle */
1638 cycle_duration = cycle_end - cycle_start;
1639 cycle_duration = MAX(0, cycle_duration);
1640 cycle_duration = MIN(speed -1, cycle_duration);
1643 #ifdef DEBUG_WORMLET
1644 ticks_to_max_cycle_reset--;
1645 if (ticks_to_max_cycle_reset <= 0) {
1646 max_cycle = 0;
1649 if (max_cycle < cycle_duration) {
1650 max_cycle = cycle_duration;
1651 ticks_to_max_cycle_reset = 20;
1653 rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle);
1654 set_debug_out(buf);
1655 #endif
1657 /* adjust the number of ticks to wait for a button.
1658 This ensures that a complete game cycle including
1659 user input runs in constant time */
1660 button = rb->button_get_w_tmo(speed - cycle_duration);
1661 cycle_start = *rb->current_tick;
1664 rb->splash(HZ*2, "Game Over!");
1666 return 2; /* back to menu */
1669 #ifdef DEBUG_WORMLET
1672 * Just a test routine that checks that worm_food_collision works
1673 * in some typical situations.
1675 static void test_worm_food_collision(void) {
1676 int collision_count = 0;
1677 int i;
1678 rb->lcd_clear_display();
1679 init_worm(&worms[0], 10, 10);
1680 add_growing(&worms[0], 10);
1681 set_worm_dir(&worms[0], EAST);
1682 for (i = 0; i < 10; i++) {
1683 move_worm(&worms[0]);
1684 draw_worm(&worms[0]);
1687 set_worm_dir(&worms[0], SOUTH);
1688 for (i = 0; i < 10; i++) {
1689 move_worm(&worms[0]);
1690 draw_worm(&worms[0]);
1693 foodx[0] = 15;
1694 foody[0] = 12;
1695 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1696 char buf[20];
1697 bool collision;
1698 draw_worm(&worms[0]);
1699 draw_food(0);
1700 collision = worm_food_collision(&worms[0], 0);
1701 if (collision) {
1702 collision_count++;
1704 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1705 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1706 rb->lcd_update();
1708 if (collision_count != food_size) {
1709 rb->button_get(true);
1713 foody[0] = 15;
1714 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1715 char buf[20];
1716 bool collision;
1717 draw_worm(&worms[0]);
1718 draw_food(0);
1719 collision = worm_food_collision(&worms[0], 0);
1720 if (collision) {
1721 collision_count ++;
1723 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1724 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1725 rb->lcd_update();
1727 if (collision_count != food_size * 2) {
1728 rb->button_get(true);
1733 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){
1734 int x, y;
1735 bool retVal = false;
1736 for (x = rx; x < rx + rw; x++){
1737 for (y = ry; y < ry + rh; y++) {
1738 if (specific_worm_collision(w, x, y) != -1) {
1739 retVal = true;
1743 return retVal;
1746 static void test_worm_argh_collision(void) {
1747 int i;
1748 int dir;
1749 int collision_count = 0;
1750 rb->lcd_clear_display();
1751 init_worm(&worms[0], 10, 10);
1752 add_growing(&worms[0], 40);
1753 for (dir = 0; dir < 4; dir++) {
1754 set_worm_dir(&worms[0], (EAST + dir) % 4);
1755 for (i = 0; i < 10; i++) {
1756 move_worm(&worms[0]);
1757 draw_worm(&worms[0]);
1761 arghx[0] = 12;
1762 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1763 char buf[20];
1764 bool collision;
1765 draw_argh(0);
1766 collision = worm_argh_collision(&worms[0], 0);
1767 if (collision) {
1768 collision_count ++;
1770 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1771 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1772 rb->lcd_update();
1774 if (collision_count != argh_size * 2) {
1775 rb->button_get(true);
1778 arghy[0] = 12;
1779 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1780 char buf[20];
1781 bool collision;
1782 draw_argh(0);
1783 collision = worm_argh_collision(&worms[0], 0);
1784 if (collision) {
1785 collision_count ++;
1787 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1788 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1789 rb->lcd_update();
1791 if (collision_count != argh_size * 4) {
1792 rb->button_get(true);
1796 static int testline_in_rect(void) {
1797 int testfailed = -1;
1799 int rx = 10;
1800 int ry = 15;
1801 int rw = 20;
1802 int rh = 25;
1804 /* Test 1 */
1805 int x1 = 12;
1806 int y1 = 8;
1807 int x2 = 12;
1808 int y2 = 42;
1810 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1811 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1812 rb->lcd_drawrect(rx, ry, rw, rh);
1813 rb->lcd_drawline(x1, y1, x2, y2);
1814 rb->lcd_update();
1815 rb->lcd_putsxy(0, 0, "failed 1");
1816 rb->button_get(true);
1817 testfailed = 1;
1820 /* test 2 */
1821 y2 = 20;
1822 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1823 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1824 rb->lcd_drawrect(rx, ry, rw, rh);
1825 rb->lcd_drawline(x1, y1, x2, y2);
1826 rb->lcd_putsxy(0, 0, "failed 2");
1827 rb->lcd_update();
1828 rb->button_get(true);
1829 testfailed = 2;
1832 /* test 3 */
1833 y1 = 30;
1834 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1835 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1836 rb->lcd_drawrect(rx, ry, rw, rh);
1837 rb->lcd_drawline(x1, y1, x2, y2);
1838 rb->lcd_putsxy(0, 0, "failed 3");
1839 rb->lcd_update();
1840 rb->button_get(true);
1841 testfailed = 3;
1844 /* test 4 */
1845 y2 = 45;
1846 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1847 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1848 rb->lcd_drawrect(rx, ry, rw, rh);
1849 rb->lcd_drawline(x1, y1, x2, y2);
1850 rb->lcd_putsxy(0, 0, "failed 4");
1851 rb->lcd_update();
1852 rb->button_get(true);
1853 testfailed = 4;
1856 /* test 5 */
1857 y1 = 50;
1858 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1859 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1860 rb->lcd_drawrect(rx, ry, rw, rh);
1861 rb->lcd_drawline(x1, y1, x2, y2);
1862 rb->lcd_putsxy(0, 0, "failed 5");
1863 rb->lcd_update();
1864 rb->button_get(true);
1865 testfailed = 5;
1868 /* test 6 */
1869 y1 = 5;
1870 y2 = 7;
1871 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1872 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1873 rb->lcd_drawrect(rx, ry, rw, rh);
1874 rb->lcd_drawline(x1, y1, x2, y2);
1875 rb->lcd_putsxy(0, 0, "failed 6");
1876 rb->lcd_update();
1877 rb->button_get(true);
1878 testfailed = 6;
1881 /* test 7 */
1882 x1 = 8;
1883 y1 = 20;
1884 x2 = 35;
1885 y2 = 20;
1886 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1887 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1888 rb->lcd_drawrect(rx, ry, rw, rh);
1889 rb->lcd_drawline(x1, y1, x2, y2);
1890 rb->lcd_putsxy(0, 0, "failed 7");
1891 rb->lcd_update();
1892 rb->button_get(true);
1893 testfailed = 7;
1896 /* test 8 */
1897 x2 = 12;
1898 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1899 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1900 rb->lcd_drawrect(rx, ry, rw, rh);
1901 rb->lcd_drawline(x1, y1, x2, y2);
1902 rb->lcd_putsxy(0, 0, "failed 8");
1903 rb->lcd_update();
1904 rb->button_get(true);
1905 testfailed = 8;
1908 /* test 9 */
1909 x1 = 25;
1910 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1911 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1912 rb->lcd_drawrect(rx, ry, rw, rh);
1913 rb->lcd_drawline(x1, y1, x2, y2);
1914 rb->lcd_putsxy(0, 0, "failed 9");
1915 rb->lcd_update();
1916 rb->button_get(true);
1917 testfailed = 9;
1920 /* test 10 */
1921 x2 = 37;
1922 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1923 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1924 rb->lcd_drawrect(rx, ry, rw, rh);
1925 rb->lcd_drawline(x1, y1, x2, y2);
1926 rb->lcd_putsxy(0, 0, "failed 10");
1927 rb->lcd_update();
1928 rb->button_get(true);
1929 testfailed = 10;
1932 /* test 11 */
1933 x1 = 42;
1934 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1935 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1936 rb->lcd_drawrect(rx, ry, rw, rh);
1937 rb->lcd_drawline(x1, y1, x2, y2);
1938 rb->lcd_putsxy(0, 0, "failed 11");
1939 rb->lcd_update();
1940 rb->button_get(true);
1941 testfailed = 11;
1944 /* test 12 */
1945 x1 = 5;
1946 x2 = 7;
1947 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1948 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1949 rb->lcd_drawrect(rx, ry, rw, rh);
1950 rb->lcd_drawline(x1, y1, x2, y2);
1951 rb->lcd_putsxy(0, 0, "failed 12");
1952 rb->lcd_update();
1953 rb->button_get(true);
1954 testfailed = 12;
1957 /* test 13 */
1958 rx = 9;
1959 ry = 15;
1960 rw = food_size;
1961 rh = food_size;
1963 x1 = 10;
1964 y1 = 10;
1965 x2 = 10;
1966 y2 = 20;
1967 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1968 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
1969 rb->lcd_drawrect(rx, ry, rw, rh);
1970 rb->lcd_drawline(x1, y1, x2, y2);
1971 rb->lcd_putsxy(0, 0, "failed 13");
1972 rb->lcd_update();
1973 rb->button_get(true);
1974 testfailed = 13;
1977 /* test 14 */
1978 rx = 9;
1979 ry = 15;
1980 rw = 4;
1981 rh = 4;
1983 x1 = 10;
1984 y1 = 10;
1985 x2 = 10;
1986 y2 = 19;
1987 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1988 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
1989 rb->lcd_drawline(x1, y1, x2, y2);
1990 rb->lcd_invertrect(rx, ry, rw, rh);
1991 rb->lcd_putsxy(0, 0, "failed 14");
1992 rb->lcd_update();
1993 rb->button_get(true);
1994 testfailed = 14;
1997 rb->lcd_clear_display();
1999 return testfailed;
2003 * Just a test routine to test wether specific_worm_collision might work properly
2005 static int test_specific_worm_collision(void) {
2006 int collisions = 0;
2007 int dir;
2008 int x = 0;
2009 int y = 0;
2010 char buf[20];
2011 rb->lcd_clear_display();
2012 init_worm(&worms[0], 10, 20);
2013 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2015 for (dir = EAST; dir < EAST + 4; dir++) {
2016 int i;
2017 set_worm_dir(&worms[0], dir % 4);
2018 for (i = 0; i < 5; i++) {
2019 if (!(dir % 4 == NORTH && i == 9)) {
2020 move_worm(&worms[0]);
2021 draw_worm(&worms[0]);
2026 for (y = 15; y < 30; y ++){
2027 for (x = 5; x < 20; x++) {
2028 if (specific_worm_collision(&worms[0], x, y) != -1) {
2029 collisions ++;
2031 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2032 rb->snprintf(buf, sizeof buf, "collisions %d", collisions);
2033 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2034 rb->lcd_update();
2037 if (collisions != 21) {
2038 rb->button_get(true);
2040 return collisions;
2043 static void test_make_argh(void){
2044 int dir;
2045 int seed = 0;
2046 int hit = 0;
2047 int failures = 0;
2048 int last_failures = 0;
2049 int i, worm_idx;
2050 rb->lcd_clear_display();
2051 worm_count = 3;
2053 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2054 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2055 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2058 for (dir = EAST; dir < EAST + 4; dir++) {
2059 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2060 set_worm_dir(&worms[worm_idx], dir % 4);
2061 for (i = 0; i < 10; i++) {
2062 if (!(dir % 4 == NORTH && i == 9)) {
2063 move_worm(&worms[worm_idx]);
2064 draw_worm(&worms[worm_idx]);
2070 rb->lcd_update();
2072 for (seed = 0; hit < 20; seed += 2) {
2073 char buf[20];
2074 int x, y;
2075 rb->srand(seed);
2076 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2077 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2079 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2080 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2081 int tries = 0;
2082 rb->srand(seed);
2084 tries = make_argh(0);
2085 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2086 failures ++;
2089 rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d", x, y, failures, tries);
2090 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2091 rb->lcd_update();
2092 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, argh_size, argh_size);
2093 rb->lcd_update();
2094 draw_argh(0);
2095 rb->lcd_update();
2096 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, argh_size, argh_size);
2097 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, argh_size, argh_size);
2099 if (failures > last_failures) {
2100 rb->button_get(true);
2102 last_failures = failures;
2103 hit ++;
2109 static void test_worm_argh_collision_in_moves(void) {
2110 int hit_count = 0;
2111 int i;
2112 rb->lcd_clear_display();
2113 init_worm(&worms[0], 10, 20);
2115 arghx[0] = 20;
2116 arghy[0] = 18;
2117 draw_argh(0);
2119 set_worm_dir(&worms[0], EAST);
2120 for (i = 0; i < 20; i++) {
2121 char buf[20];
2122 move_worm(&worms[0]);
2123 draw_worm(&worms[0]);
2124 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2125 hit_count ++;
2127 rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
2128 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2129 rb->lcd_update();
2131 if (hit_count != argh_size + 5) {
2132 rb->button_get(true);
2135 #endif /* DEBUG_WORMLET */
2137 extern bool use_old_rect;
2140 * These are additional functions required to set various wormlet settings
2141 * and to enable saving of settings and high score
2145 Sets the total number of worms, both human and machine
2147 bool set_worm_num_worms(void)
2149 bool ret;
2150 static const struct opt_items num_worms_option[3] = {
2151 { "1", -1 },
2152 { "2", -1 },
2153 { "3", -1 },
2156 ret = rb->set_option("Number Of Worms", &worm_count,INT, num_worms_option, 3, NULL);
2157 if (worm_count < players) {
2158 worm_count=players;
2160 return ret;
2164 Sets the number of human players
2166 bool set_worm_num_players(void)
2168 bool ret;
2169 static const struct opt_items num_players_option[4] = {
2170 { "0", -1 },
2171 { "1", -1 },
2172 { "2", -1 },
2173 { "3", -1 }
2176 ret = rb->set_option("Number of Players", &players, INT,num_players_option , 4, NULL);
2177 if (players > worm_count) {
2178 worm_count = players;
2180 if (players > 2) {
2181 use_remote = true;
2183 return ret;
2187 Sets the size of each argh block created
2189 bool set_worm_argh_size(void)
2191 static const struct opt_items argh_size_option[11] = {
2192 { "0", -1 },
2193 { "1", -1 },
2194 { "2", -1 },
2195 { "3", -1 },
2196 { "4", -1 },
2197 { "5", -1 },
2198 { "6", -1 },
2199 { "7", -1 },
2200 { "8", -1 },
2201 { "9", -1 },
2202 { "10", -1 }
2205 return rb->set_option("Argh Size", &argh_size,INT,argh_size_option , 11, NULL);
2209 Sets the amount a worm grows per food
2211 bool set_worm_food(void)
2213 static const struct opt_items worm_food_option[11] = {
2214 { "0", -1 },
2215 { "1", -1 },
2216 { "2", -1 },
2217 { "3", -1 },
2218 { "4", -1 },
2219 { "5", -1 },
2220 { "6", -1 },
2221 { "7", -1 },
2222 { "8", -1 },
2223 { "9", -1 },
2224 { "10", -1 }
2226 return rb->set_option("Worm Growth Per Food", &worm_food,INT,worm_food_option , 11, NULL);
2230 Sets the number of arghs created per food
2232 bool set_argh_per_food(void)
2234 static const struct opt_items argh_food_option[5] = {
2235 { "0", -1 },
2236 { "1", -1 },
2237 { "2", -1 },
2238 { "3", -1 },
2239 { "4", -1 },
2241 return rb->set_option("Arghs Per Food", &arghs_per_food,INT,argh_food_option , 5, NULL);
2245 Sets the number of ticks per move
2247 bool set_worm_speed(void)
2249 bool ret;
2250 static const struct opt_items speed_option[19] = {
2251 { "0", -1 },
2252 { "1", -1 },
2253 { "2", -1 },
2254 { "3", -1 },
2255 { "4", -1 },
2256 { "5", -1 },
2257 { "6", -1 },
2258 { "7", -1 },
2259 { "8", -1 },
2260 { "9", -1 },
2261 { "10", -1 },
2262 { "11", -1 },
2263 { "12", -1 },
2264 { "13", -1 },
2265 { "14", -1 },
2266 { "15", -1 },
2267 { "16", -1 },
2268 { "17", -1 },
2269 { "18", -1 },
2273 speed = 20 - speed;
2274 ret = rb->set_option("Worm Speed", &speed,INT,speed_option , 19, NULL);
2275 speed = 20 - speed;
2276 return ret;
2280 Sets the control style, which depends on the number of players,
2281 remote control existing, etc
2282 bool set_bool_options(char* string, bool* variable,
2283 char* yes_str, char* no_str, void (*function)(bool))
2285 bool set_worm_control_style(void)
2287 static const struct opt_items key1_option[2] = {
2288 { "Remote Control", -1 },
2289 { "No Rem. Control", -1 },
2292 static const struct opt_items key2_option[2] = {
2293 { "2 Key Control", -1 },
2294 { "4 Key COntrol", -1 },
2297 static const struct opt_items key3_option[1] = {
2298 { "Out of Control", -1 },
2301 if (players > 1) {
2302 rb->set_option("Control Style",&use_remote,INT, key1_option, 2, NULL);
2303 } else {
2304 if (players > 0) {
2305 rb->set_option("Control Style",&use_remote,INT, key2_option, 2, NULL);
2307 else {
2308 rb->set_option("Control Style",&use_remote,INT, key3_option, 1, NULL);
2311 return false;
2314 void default_settings(void)
2316 arghs_per_food = ARGHS_PER_FOOD;
2317 argh_size = ARGH_SIZE;
2318 food_size = FOOD_SIZE;
2319 speed = SPEED;
2320 worm_food = WORM_PER_FOOD;
2321 players = 1;
2322 worm_count = MAX_WORMS;
2323 use_remote = false;
2324 return;
2328 Launches the wormlet game
2330 bool launch_wormlet(void)
2332 int game_result = 1;
2334 rb->lcd_clear_display();
2336 /* Turn off backlight timeout */
2337 backlight_force_on(rb); /* backlight control in lib/helper.c */
2339 /* start the game */
2340 while (game_result == 1)
2341 game_result = run();
2343 switch (game_result)
2345 case 2:
2346 /* Turn on backlight timeout (revert to settings) */
2347 backlight_use_settings(rb); /* backlight control in lib/helper.c */
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;