Theme Editor: Factored out code to skip over enum/arg lists while scanning for childr...
[kugel-rb.git] / apps / plugins / wormlet.c
blob037f4005a2349d6170f52f921862da7426bb834b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Philipp Pertermann
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "lib/configfile.h"
23 #include "lib/helper.h"
24 #include "lib/playback_control.h"
26 PLUGIN_HEADER
28 /* size of the field the worm lives in */
29 #define FIELD_RECT_X 1
30 #define FIELD_RECT_Y 1
31 #define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
32 #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
34 /* when the game starts */
35 #define INITIAL_WORM_LENGTH 10
37 /* num of pixel the worm grows per eaten food */
38 #define WORM_PER_FOOD 7
40 /* num of worms creeping in the FIELD */
41 #define MAX_WORMS 3
43 /* minimal distance between a worm and an argh
44 when a new argh is made */
45 #define MIN_ARGH_DIST 5
47 #if (CONFIG_KEYPAD == RECORDER_PAD)
48 #define BTN_DIR_UP BUTTON_UP
49 #define BTN_DIR_DOWN BUTTON_DOWN
50 #define BTN_DIR_LEFT BUTTON_LEFT
51 #define BTN_DIR_RIGHT BUTTON_RIGHT
52 #define BTN_PLAYER2_DIR1 BUTTON_F2
53 #define BTN_PLAYER2_DIR2 BUTTON_F3
54 #define BTN_STARTPAUSE BUTTON_PLAY
55 #define BTN_QUIT BUTTON_OFF
56 #define BTN_STOPRESET BUTTON_ON
57 #define BTN_TOGGLE_KEYS BUTTON_F1
59 #if BUTTON_REMOTE != 0
60 #define BTN_RC_UP BUTTON_RC_VOL_UP
61 #define BTN_RC_DOWN BUTTON_RC_VOL_DOWN
62 #define REMOTE
63 #define MULTIPLAYER
64 #endif
66 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
67 #define BTN_DIR_UP BUTTON_UP
68 #define BTN_DIR_DOWN BUTTON_DOWN
69 #define BTN_DIR_LEFT BUTTON_LEFT
70 #define BTN_DIR_RIGHT BUTTON_RIGHT
71 #define BTN_PLAYER2_DIR1 BUTTON_F2
72 #define BTN_PLAYER2_DIR2 BUTTON_F3
73 #define BTN_STARTPAUSE BUTTON_SELECT
74 #define BTN_QUIT BUTTON_OFF
75 #define BTN_STOPRESET BUTTON_ON
76 #define BTN_TOGGLE_KEYS BUTTON_F1
78 #elif (CONFIG_KEYPAD == ONDIO_PAD)
79 #define BTN_DIR_UP BUTTON_UP
80 #define BTN_DIR_DOWN BUTTON_DOWN
81 #define BTN_DIR_LEFT BUTTON_LEFT
82 #define BTN_DIR_RIGHT BUTTON_RIGHT
83 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
84 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
85 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
87 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
88 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
90 #define BTN_DIR_UP BUTTON_MENU
91 #define BTN_DIR_DOWN BUTTON_PLAY
92 #define BTN_DIR_LEFT BUTTON_LEFT
93 #define BTN_DIR_RIGHT BUTTON_RIGHT
94 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
95 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
96 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
98 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
100 #define BTN_DIR_UP BUTTON_UP
101 #define BTN_DIR_DOWN BUTTON_DOWN
102 #define BTN_DIR_LEFT BUTTON_LEFT
103 #define BTN_DIR_RIGHT BUTTON_RIGHT
104 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
105 #define BTN_QUIT BUTTON_OFF
106 #define BTN_STOPRESET BUTTON_ON
108 #define BTN_RC_QUIT BUTTON_RC_STOP
110 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
112 #define BTN_DIR_UP BUTTON_UP
113 #define BTN_DIR_DOWN BUTTON_DOWN
114 #define BTN_DIR_LEFT BUTTON_LEFT
115 #define BTN_DIR_RIGHT BUTTON_RIGHT
116 #define BTN_STARTPAUSE BUTTON_PLAY
117 #define BTN_QUIT BUTTON_POWER
118 #define BTN_STOPRESET BUTTON_REC
120 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
122 #define BTN_DIR_UP BUTTON_UP
123 #define BTN_DIR_DOWN BUTTON_DOWN
124 #define BTN_DIR_LEFT BUTTON_LEFT
125 #define BTN_DIR_RIGHT BUTTON_RIGHT
126 #define BTN_STARTPAUSE BUTTON_SELECT
127 #define BTN_QUIT BUTTON_POWER
128 #define BTN_STOPRESET BUTTON_A
130 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
131 (CONFIG_KEYPAD == SANSA_C200_PAD)
133 #define BTN_DIR_UP BUTTON_UP
134 #define BTN_DIR_DOWN BUTTON_DOWN
135 #define BTN_DIR_LEFT BUTTON_LEFT
136 #define BTN_DIR_RIGHT BUTTON_RIGHT
137 #define BTN_STARTPAUSE BUTTON_SELECT
138 #define BTN_QUIT BUTTON_POWER
139 #define BTN_STOPRESET BUTTON_REC
141 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
143 #define BTN_DIR_UP BUTTON_UP
144 #define BTN_DIR_DOWN BUTTON_DOWN
145 #define BTN_DIR_LEFT BUTTON_LEFT
146 #define BTN_DIR_RIGHT BUTTON_RIGHT
147 #define BTN_STARTPAUSE BUTTON_SELECT
148 #define BTN_QUIT BUTTON_POWER
149 #define BTN_STOPRESET BUTTON_HOME
151 #elif (CONFIG_KEYPAD == SANSA_FUZE_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_HOME|BUTTON_REPEAT)
159 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
161 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
163 #define BTN_DIR_UP BUTTON_UP
164 #define BTN_DIR_DOWN BUTTON_DOWN
165 #define BTN_DIR_LEFT BUTTON_LEFT
166 #define BTN_DIR_RIGHT BUTTON_RIGHT
167 #define BTN_STARTPAUSE (BUTTON_SELECT | BUTTON_REL)
168 #define BTN_QUIT BUTTON_POWER
169 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
171 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
173 #define BTN_DIR_UP BUTTON_SCROLL_UP
174 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
175 #define BTN_DIR_LEFT BUTTON_LEFT
176 #define BTN_DIR_RIGHT BUTTON_RIGHT
177 #define BTN_STARTPAUSE BUTTON_PLAY
178 #define BTN_QUIT BUTTON_POWER
179 #define BTN_STOPRESET BUTTON_REW
181 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
183 #define BTN_DIR_UP BUTTON_UP
184 #define BTN_DIR_DOWN BUTTON_DOWN
185 #define BTN_DIR_LEFT BUTTON_LEFT
186 #define BTN_DIR_RIGHT BUTTON_RIGHT
187 #define BTN_STARTPAUSE BUTTON_SELECT
188 #define BTN_QUIT BUTTON_BACK
189 #define BTN_STOPRESET BUTTON_MENU
191 #elif (CONFIG_KEYPAD == MROBE100_PAD)
193 #define BTN_DIR_UP BUTTON_UP
194 #define BTN_DIR_DOWN BUTTON_DOWN
195 #define BTN_DIR_LEFT BUTTON_LEFT
196 #define BTN_DIR_RIGHT BUTTON_RIGHT
197 #define BTN_STARTPAUSE BUTTON_SELECT
198 #define BTN_QUIT BUTTON_POWER
199 #define BTN_STOPRESET BUTTON_DISPLAY
201 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
203 #define BTN_DIR_UP BUTTON_RC_VOL_UP
204 #define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN
205 #define BTN_DIR_LEFT BUTTON_RC_REW
206 #define BTN_DIR_RIGHT BUTTON_RC_FF
207 #define BTN_STARTPAUSE BUTTON_RC_PLAY
208 #define BTN_QUIT BUTTON_RC_REC
209 #define BTN_STOPRESET BUTTON_RC_MODE
211 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
213 #define BTN_QUIT BUTTON_POWER
215 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
217 #define BTN_DIR_UP BUTTON_UP
218 #define BTN_DIR_DOWN BUTTON_DOWN
219 #define BTN_DIR_LEFT BUTTON_LEFT
220 #define BTN_DIR_RIGHT BUTTON_RIGHT
221 #define BTN_STARTPAUSE BUTTON_PLAY
222 #define BTN_QUIT BUTTON_BACK
223 #define BTN_STOPRESET BUTTON_MENU
225 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
227 #define BTN_DIR_UP BUTTON_UP
228 #define BTN_DIR_DOWN BUTTON_DOWN
229 #define BTN_DIR_LEFT BUTTON_LEFT
230 #define BTN_DIR_RIGHT BUTTON_RIGHT
231 #define BTN_STARTPAUSE BUTTON_MENU
232 #define BTN_QUIT BUTTON_POWER
233 #define BTN_STOPRESET BUTTON_VIEW
235 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
237 #define BTN_DIR_UP BUTTON_UP
238 #define BTN_DIR_DOWN BUTTON_DOWN
239 #define BTN_DIR_LEFT BUTTON_PREV
240 #define BTN_DIR_RIGHT BUTTON_RIGHT
241 #define BTN_STARTPAUSE BUTTON_MENU
242 #define BTN_QUIT BUTTON_POWER
243 #define BTN_STOPRESET BUTTON_RIGHT
245 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
246 (CONFIG_KEYPAD == ONDAVX777_PAD) || \
247 CONFIG_KEYPAD == MROBE500_PAD
249 #define BTN_QUIT BUTTON_POWER
251 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
253 #define BTN_DIR_UP BUTTON_UP
254 #define BTN_DIR_DOWN BUTTON_DOWN
255 #define BTN_DIR_LEFT BUTTON_LEFT
256 #define BTN_DIR_RIGHT BUTTON_RIGHT
257 #define BTN_STARTPAUSE BUTTON_PLAY
258 #define BTN_QUIT BUTTON_FFWD
259 #define BTN_STOPRESET BUTTON_REW
261 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
263 #define BTN_DIR_UP BUTTON_UP
264 #define BTN_DIR_DOWN BUTTON_DOWN
265 #define BTN_DIR_LEFT BUTTON_PREV
266 #define BTN_DIR_RIGHT BUTTON_NEXT
267 #define BTN_STARTPAUSE BUTTON_PLAY
268 #define BTN_QUIT BUTTON_REC
269 #define BTN_STOPRESET BUTTON_CANCEL
271 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
273 #define BTN_DIR_UP BUTTON_REC
274 #define BTN_DIR_DOWN BUTTON_PLAY
275 #define BTN_DIR_LEFT BUTTON_PREV
276 #define BTN_DIR_RIGHT BUTTON_NEXT
277 #define BTN_STARTPAUSE BUTTON_SELECT
278 #define BTN_QUIT (BUTTON_REC|BUTTON_PLAY)
279 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_REPEAT)
281 #else
282 #error No keymap defined!
283 #endif
285 #ifdef HAVE_TOUCHSCREEN
286 #ifndef BTN_DIR_UP
287 #define BTN_DIR_UP BUTTON_TOPMIDDLE
288 #endif
289 #ifndef BTN_DIR_DOWN
290 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
291 #endif
292 #ifndef BTN_DIR_LEFT
293 #define BTN_DIR_LEFT BUTTON_MIDLEFT
294 #endif
295 #ifndef BTN_DIR_RIGHT
296 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
297 #endif
298 #ifndef BTN_STARTPAUSE
299 #define BTN_STARTPAUSE BUTTON_CENTER
300 #endif
301 #ifndef BTN_QUIT
302 #define BTN_QUIT BUTTON_TOPLEFT
303 #endif
304 #ifndef BTN_STOPRESET
305 #define BTN_STOPRESET BUTTON_TOPRIGHT
306 #endif
307 #endif
309 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
310 #define FOOD_SIZE 3
311 #define ARGH_SIZE 4
312 #define SPEED 14
313 #define MAX_WORM_SEGMENTS 128
314 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
315 #define FOOD_SIZE 3
316 #define ARGH_SIZE 4
317 #define SPEED 14
318 #define MAX_WORM_SEGMENTS 128
319 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
320 #define FOOD_SIZE 3
321 #define ARGH_SIZE 4
322 #define SPEED 14
323 #define MAX_WORM_SEGMENTS 128
324 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
325 #define FOOD_SIZE 3
326 #define ARGH_SIZE 4
327 #define SPEED 12
328 #define MAX_WORM_SEGMENTS 128
329 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
330 #define FOOD_SIZE 4
331 #define ARGH_SIZE 5
332 #define SPEED 10
333 #define MAX_WORM_SEGMENTS 128
334 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
335 #define FOOD_SIZE 4
336 #define ARGH_SIZE 5
337 #define SPEED 9
338 #define MAX_WORM_SEGMENTS 128
339 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
340 #define FOOD_SIZE 4
341 #define ARGH_SIZE 5
342 #define SPEED 8
343 #define MAX_WORM_SEGMENTS 256
344 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
345 #define FOOD_SIZE 4
346 #define ARGH_SIZE 5
347 #define SPEED 6
348 #define MAX_WORM_SEGMENTS 256
349 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
350 #define FOOD_SIZE 5
351 #define ARGH_SIZE 6
352 #define SPEED 4
353 #define MAX_WORM_SEGMENTS 512
354 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
355 #define FOOD_SIZE 5
356 #define ARGH_SIZE 6
357 #define SPEED 4
358 #define MAX_WORM_SEGMENTS 512
359 #elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
360 ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
361 #define FOOD_SIZE 7
362 #define ARGH_SIZE 8
363 #define SPEED 4
364 #define MAX_WORM_SEGMENTS 512
365 #elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
366 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
367 #define FOOD_SIZE 14
368 #define ARGH_SIZE 16
369 #define SPEED 4
370 #define MAX_WORM_SEGMENTS 512
371 #endif
373 #ifdef HAVE_LCD_COLOR
374 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
375 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
376 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
377 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
378 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
379 #endif
381 #define CHECK_SQUARE_COLLISION(x1,y1,s1,x2,y2,s2) (x1+s1>x2)&&(x2+s2>x1)&&(y1+s1>y2)&&(y2+s2>y1)
384 * All the properties that a worm has.
386 static struct worm {
387 /* The worm is stored in a ring of xy coordinates */
388 int x[MAX_WORM_SEGMENTS];
389 int y[MAX_WORM_SEGMENTS];
391 int head; /* index of the head within the buffer */
392 int tail; /* index of the tail within the buffer */
393 int growing; /* number of cyles the worm still keeps growing */
394 bool alive; /* the worms living state */
396 /* direction vector in which the worm moves */
397 int dirx; /* only values -1 0 1 allowed */
398 int diry; /* only values -1 0 1 allowed */
400 /* this method is used to fetch the direction the user
401 has selected. It can be one of the values
402 human_player1, human_player2, remote_player, virtual_player.
403 All these values are fuctions, that can change the direction
404 of the worm */
405 void (*fetch_worm_direction)(struct worm *w);
406 } worms[MAX_WORMS];
408 /* stores the highscore - besides it was scored by a virtual player */
409 static int highscore;
411 #define MAX_FOOD 5 /* maximal number of food items */
413 /* The arrays store the food coordinates */
414 static int foodx[MAX_FOOD];
415 static int foody[MAX_FOOD];
417 #define MAX_ARGH 100 /* maximal number of argh items */
418 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
420 /* The arrays store the argh coordinates */
421 static int arghx[MAX_ARGH];
422 static int arghy[MAX_ARGH];
424 /* the number of arghs that are currently in use */
425 static int argh_count;
427 /* the number of arghs per food, settable by user */
428 static int arghs_per_food = ARGHS_PER_FOOD;
429 /* the size of the argh, settable by user */
430 static int argh_size = ARGH_SIZE;
431 /* the size of the food, settable by user */
432 static int food_size = FOOD_SIZE;
433 /* the speed of the worm, settable by user */
434 static int speed = SPEED;
435 /* the amount a worm grows by eating a food, settable by user */
436 static int worm_food = WORM_PER_FOOD;
438 /* End additional variables */
440 #ifdef DEBUG_WORMLET
441 /* just a buffer used for debug output */
442 static char debugout[15];
443 #endif
445 /* the number of active worms (dead or alive) */
446 static int worm_count = MAX_WORMS;
448 /* in multiplayer mode: en- / disables the remote worm control
449 in singleplayer mode: toggles 4 / 2 button worm control */
450 static bool use_remote = false;
452 /* return values of check_collision */
453 #define COLLISION_NONE 0
454 #define COLLISION_WORM 1
455 #define COLLISION_FOOD 2
456 #define COLLISION_ARGH 3
457 #define COLLISION_FIELD 4
459 /* constants for use as directions.
460 Note that the values are ordered clockwise.
461 Thus increasing / decreasing the values
462 is equivalent to right / left turns. */
463 #define WEST 0
464 #define NORTH 1
465 #define EAST 2
466 #define SOUTH 3
468 /* direction of human player 1 */
469 static int player1_dir = EAST;
470 /* direction of human player 2 */
471 static int player2_dir = EAST;
472 /* direction of human player 3 */
473 static int player3_dir = EAST;
475 /* the number of (human) players that currently
476 control a worm */
477 static int players = 1;
479 #define SETTINGS_VERSION 1
480 #define SETTINGS_MIN_VERSION 1
481 #define SETTINGS_FILENAME "wormlet.cfg"
483 static struct configdata config[] =
485 {TYPE_INT, 0, 1024, { .int_p = &highscore }, "highscore", NULL},
486 {TYPE_INT, 0, 15, { .int_p = &arghs_per_food }, "arghs per food", NULL},
487 {TYPE_INT, 0, 15, { .int_p = &argh_size }, "argh size", NULL},
488 {TYPE_INT, 0, 15, { .int_p = &food_size }, "food size", NULL},
489 {TYPE_INT, 0, 3, { .int_p = &players }, "players", NULL},
490 {TYPE_INT, 0, 3, { .int_p = &worm_count }, "worms", NULL},
491 {TYPE_INT, 0, 20, { .int_p = &speed }, "speed", NULL},
492 {TYPE_INT, 0, 15, { .int_p = &worm_food }, "Worm Growth Per Food", NULL}
495 #ifdef DEBUG_WORMLET
496 static void set_debug_out(char *str){
497 strcpy(debugout, str);
499 #endif
502 * Returns the direction id in which the worm
503 * currently is creeping.
504 * @param struct worm *w The worm that is to be investigated.
505 * w Must not be null.
506 * @return int A value 0 <= value < 4
507 * Note the predefined constants NORTH, SOUTH, EAST, WEST
509 static int get_worm_dir(struct worm *w)
511 int retVal ;
512 if (w->dirx == 0) {
513 if (w->diry == 1) {
514 retVal = SOUTH;
515 } else {
516 retVal = NORTH;
518 } else {
519 if (w->dirx == 1) {
520 retVal = EAST;
521 } else {
522 retVal = WEST;
525 return retVal;
529 * Set the direction of the specified worm with a direction id.
530 * Increasing the value by 1 means to turn the worm direction
531 * to right by 90 degree.
532 * @param struct worm *w The worm that is to be altered. w Must not be null.
533 * @param int dir The new direction in which the worm is to creep.
534 * dir must be 0 <= dir < 4. Use predefined constants
535 * NORTH, SOUTH, EAST, WEST
537 static void set_worm_dir(struct worm *w, int dir)
539 switch (dir) {
540 case WEST:
541 w->dirx = -1;
542 w->diry = 0;
543 break;
544 case NORTH:
545 w->dirx = 0;
546 w->diry = - 1;
547 break;
548 case EAST:
549 w->dirx = 1;
550 w->diry = 0;
551 break;
552 case SOUTH:
553 w->dirx = 0;
554 w->diry = 1;
555 break;
560 * Returns the current length of the worm array. This
561 * is also a value for the number of bends that are in the worm.
562 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
564 static int get_worm_array_length(struct worm *w)
566 /* initial simple calculation will be overwritten if wrong. */
567 int retVal = w->head - w->tail;
569 /* if the worm 'crosses' the boundaries of the ringbuffer */
570 if (retVal < 0) {
571 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
574 return retVal;
578 * Returns the score the specified worm. The score is the length
579 * of the worm.
580 * @param struct worm *w The worm that is to be investigated.
581 * w must not be null.
582 * @return int The length of the worm (>= 0).
584 static int get_score(struct worm *w)
586 int retval = 0;
587 int length = get_worm_array_length(w);
588 int i;
589 for (i = 0; i < length; i++) {
591 /* The iteration iterates the length of the worm.
592 Here's the conversion to the true indices within the worm arrays. */
593 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
594 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
595 int startx = w->x[linestart];
596 int starty = w->y[linestart];
597 int endx = w->x[lineend];
598 int endy = w->y[lineend];
600 int minimum, maximum;
602 if (startx == endx) {
603 minimum = MIN(starty, endy);
604 maximum = MAX(starty, endy);
605 } else {
606 minimum = MIN(startx, endx);
607 maximum = MAX(startx, endx);
609 retval += abs(maximum - minimum);
611 return retval;
615 * Determines wether the line specified by startx, starty, endx, endy intersects
616 * the rectangle specified by x, y, width, height. Note that the line must be exactly
617 * horizontal or vertical (startx == endx or starty == endy).
618 * @param int startx The x coordinate of the start point of the line.
619 * @param int starty The y coordinate of the start point of the line.
620 * @param int endx The x coordinate of the end point of the line.
621 * @param int endy The y coordinate of the end point of the line.
622 * @param int x The x coordinate of the top left corner of the rectangle.
623 * @param int y The y coordinate of the top left corner of the rectangle.
624 * @param int width The width of the rectangle.
625 * @param int height The height of the rectangle.
626 * @return bool Returns true if the specified line intersects with the recangle.
628 static bool line_in_rect(int startx, int starty, int endx, int endy,
629 int x, int y, int width, int height)
631 bool retval = false;
632 int simple, simplemin, simplemax;
633 int compa, compb, compmin, compmax;
634 int temp;
635 if (startx == endx) {
636 simple = startx;
637 simplemin = x;
638 simplemax = x + width;
640 compa = starty;
641 compb = endy;
642 compmin = y;
643 compmax = y + height;
644 } else {
645 simple = starty;
646 simplemin = y;
647 simplemax = y + height;
649 compa = startx;
650 compb = endx;
651 compmin = x;
652 compmax = x + width;
655 temp = compa;
656 compa = MIN(compa, compb);
657 compb = MAX(temp, compb);
659 if (simplemin <= simple && simple <= simplemax) {
660 if ((compmin <= compa && compa <= compmax) ||
661 (compmin <= compb && compb <= compmax) ||
662 (compa <= compmin && compb >= compmax)) {
663 retval = true;
666 return retval;
670 * Tests wether the specified worm intersects with the rect.
671 * @param struct worm *w The worm to be investigated
672 * @param int x The x coordinate of the top left corner of the rect
673 * @param int y The y coordinate of the top left corner of the rect
674 * @param int widht The width of the rect
675 * @param int height The height of the rect
676 * @return bool Returns true if the worm intersects with the rect
678 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height)
680 bool retval = false;
683 /* get_worm_array_length is expensive -> buffer the value */
684 int wormLength = get_worm_array_length(w);
685 int i;
687 /* test each entry that is part of the worm */
688 for (i = 0; i < wormLength && retval == false; i++) {
690 /* The iteration iterates the length of the worm.
691 Here's the conversion to the true indices within the worm arrays. */
692 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
693 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
694 int startx = w->x[linestart];
695 int starty = w->y[linestart];
696 int endx = w->x[lineend];
697 int endy = w->y[lineend];
699 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
702 return retval;
706 * Checks wether a specific food in the food arrays is at the
707 * specified coordinates.
708 * @param int foodIndex The index of the food in the food arrays
709 * @param int x the x coordinate.
710 * @param int y the y coordinate.
711 * @return Returns true if the coordinate hits the food specified by
712 * foodIndex.
714 static bool specific_food_collision(int foodIndex, int x, int y)
716 bool retVal = false;
717 if (x >= foodx[foodIndex] &&
718 x < foodx[foodIndex] + food_size &&
719 y >= foody[foodIndex] &&
720 y < foody[foodIndex] + food_size) {
722 retVal = true;
724 return retVal;
728 * Returns the index of the food that is at the
729 * given coordinates. If no food is at the coordinates
730 * -1 is returned.
731 * @return int -1 <= value < MAX_FOOD
733 static int food_collision(int x, int y)
735 int i = 0;
736 int retVal = -1;
737 for (i = 0; i < MAX_FOOD; i++) {
738 if (specific_food_collision(i, x, y)) {
739 retVal = i;
740 break;
743 return retVal;
747 * Checks wether a specific argh in the argh arrays is at the
748 * specified coordinates.
749 * @param int arghIndex The index of the argh in the argh arrays
750 * @param int x the x coordinate.
751 * @param int y the y coordinate.
752 * @return Returns true if the coordinate hits the argh specified by
753 * arghIndex.
755 static bool specific_argh_collision(int arghIndex, int x, int y)
757 if ( x >= arghx[arghIndex] &&
758 y >= arghy[arghIndex] &&
759 x < arghx[arghIndex] + argh_size &&
760 y < arghy[arghIndex] + argh_size )
762 return true;
765 return false;
769 * Returns the index of the argh that is at the
770 * given coordinates. If no argh is at the coordinates
771 * -1 is returned.
772 * @param int x The x coordinate.
773 * @param int y The y coordinate.
774 * @return int -1 <= value < argh_count <= MAX_ARGH
776 static int argh_collision(int x, int y)
778 int i = 0;
779 int retVal = -1;
781 /* search for the argh that has the specified coords */
782 for (i = 0; i < argh_count; i++) {
783 if (specific_argh_collision(i, x, y)) {
784 retVal = i;
785 break;
788 return retVal;
792 * Checks wether the worm collides with the food at the specfied food-arrays.
793 * @param int foodIndex The index of the food in the arrays. Ensure the value is
794 * 0 <= foodIndex <= MAX_FOOD
795 * @return Returns true if the worm collides with the specified food.
797 static bool worm_food_collision(struct worm *w, int foodIndex)
799 bool retVal = false;
801 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
802 food_size - 1, food_size - 1);
804 return retVal;
808 * Returns true if the worm hits the argh within the next moves (unless
809 * the worm changes it's direction).
810 * @param struct worm *w - The worm to investigate
811 * @param int argh_idx - The index of the argh
812 * @param int moves - The number of moves that are considered.
813 * @return Returns false if the specified argh is not hit within the next
814 * moves.
816 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves)
818 bool retVal = false;
819 int x1, y1, x2, y2;
820 x1 = w->x[w->head];
821 y1 = w->y[w->head];
823 x2 = w->x[w->head] + moves * w->dirx;
824 y2 = w->y[w->head] + moves * w->diry;
826 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
827 argh_size, argh_size);
828 return retVal;
832 * Checks wether the worm collides with the argh at the specfied argh-arrays.
833 * @param int arghIndex The index of the argh in the arrays.
834 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
835 * @return Returns true if the worm collides with the specified argh.
837 static bool worm_argh_collision(struct worm *w, int arghIndex)
839 bool retVal = false;
841 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
842 argh_size - 1, argh_size - 1);
844 return retVal;
848 * Find new coordinates for the food stored in foodx[index], foody[index]
849 * that don't collide with any other food or argh
850 * @param int index
851 * Ensure that 0 <= index < MAX_FOOD.
853 static void make_food(int index)
855 int x = 0;
856 int y = 0;
857 bool collisionDetected = false;
858 int i;
860 do {
861 /* make coordinates for a new food so that
862 the entire food lies within the FIELD */
863 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
864 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
865 collisionDetected = false;
866 /* Ensure that the new food doesn't collide with any
867 existing foods or arghs.
868 If the new food hit any existing
869 argh or food a collision is detected.
872 for (i=0; i<MAX_FOOD && !collisionDetected; i++) {
873 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,foodx[i],foody[i],food_size);
875 for (i=0; i<argh_count && !collisionDetected; i++) {
876 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,arghx[i],arghy[i],argh_size);
879 /* use coordinates for further testing */
880 foodx[index] = x;
881 foody[index] = y;
883 /* now test wether we accidently hit the worm with food ;) */
884 i = 0;
885 for (i = 0; i < worm_count && !collisionDetected; i++) {
887 collisionDetected = worm_food_collision(&worms[i], index);
891 while (collisionDetected);
892 return;
896 * Clears a food from the lcd buffer.
897 * @param int index The index of the food arrays under which
898 * the coordinates of the desired food can be found. Ensure
899 * that the value is 0 <= index <= MAX_FOOD.
901 static void clear_food(int index)
903 /* remove the old food from the screen */
904 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
905 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
906 foody[index] + FIELD_RECT_Y,
907 food_size, food_size);
908 rb->lcd_set_drawmode(DRMODE_SOLID);
912 * Draws a food in the lcd buffer.
913 * @param int index The index of the food arrays under which
914 * the coordinates of the desired food can be found. Ensure
915 * that the value is 0 <= index <= MAX_FOOD.
917 static void draw_food(int index)
919 /* draw the food object */
920 #ifdef HAVE_LCD_COLOR
921 rb->lcd_set_foreground(COLOR_FOOD);
922 #endif
923 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
924 foody[index] + FIELD_RECT_Y,
925 food_size, food_size);
926 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
927 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
928 foody[index] + FIELD_RECT_Y + 1,
929 food_size - 2, food_size - 2);
930 rb->lcd_set_drawmode(DRMODE_SOLID);
931 #ifdef HAVE_LCD_COLOR
932 rb->lcd_set_foreground(COLOR_FG);
933 #endif
937 * Find new coordinates for the argh stored in arghx[index], arghy[index]
938 * that don't collide with any other food or argh.
939 * @param int index
940 * Ensure that 0 <= index < argh_count < MAX_ARGH.
942 static void make_argh(int index)
944 int x = -1;
945 int y = -1;
946 bool collisionDetected = false;
947 int i;
949 do {
950 /* make coordinates for a new argh so that
951 the entire food lies within the FIELD */
952 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
953 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
954 collisionDetected = false;
955 /* Ensure that the new argh doesn't intersect with any
956 existing foods or arghs.
957 If the new argh hit any existing
958 argh or food an intersection is detected.
961 for (i=0; i<MAX_FOOD && !collisionDetected; i++) {
962 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,foodx[i],foody[i],food_size);
964 for (i=0; i<argh_count && !collisionDetected; i++) {
965 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,arghx[i],arghy[i],argh_size);
968 /* use the candidate coordinates to make a real argh */
969 arghx[index] = x;
970 arghy[index] = y;
972 /* now test wether we accidently hit the worm with argh ;) */
973 for (i = 0; i < worm_count && !collisionDetected; i++) {
974 collisionDetected |= worm_argh_collision(&worms[i], index);
975 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
976 MIN_ARGH_DIST);
979 while (collisionDetected);
980 return;
984 * Draws an argh in the lcd buffer.
985 * @param int index The index of the argh arrays under which
986 * the coordinates of the desired argh can be found. Ensure
987 * that the value is 0 <= index < argh_count <= MAX_ARGH.
989 static void draw_argh(int index)
991 /* draw the new argh */
992 #ifdef HAVE_LCD_COLOR
993 rb->lcd_set_foreground(COLOR_ARGH);
994 #endif
995 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
996 arghy[index] + FIELD_RECT_Y,
997 argh_size, argh_size);
998 #ifdef HAVE_LCD_COLOR
999 rb->lcd_set_foreground(COLOR_FG);
1000 #endif
1003 static void virtual_player(struct worm *w);
1005 * Initialzes the specified worm with INITIAL_WORM_LENGTH
1006 * and the tail at the specified position. The worm will
1007 * be initialized alive and creeping EAST.
1008 * @param struct worm *w The worm that is to be initialized
1009 * @param int x The x coordinate at which the tail of the worm starts.
1010 * x must be 0 <= x < FIELD_RECT_WIDTH.
1011 * @param int y The y coordinate at which the tail of the worm starts
1012 * y must be 0 <= y < FIELD_RECT_WIDTH.
1014 static void init_worm(struct worm *w, int x, int y)
1016 /* initialize the worm size */
1017 w->head = 1;
1018 w->tail = 0;
1020 w->x[w->head] = x + 1;
1021 w->y[w->head] = y;
1023 w->x[w->tail] = x;
1024 w->y[w->tail] = y;
1026 /* set the initial direction the worm creeps to */
1027 w->dirx = 1;
1028 w->diry = 0;
1030 w->growing = INITIAL_WORM_LENGTH - 1;
1031 w->alive = true;
1032 w->fetch_worm_direction = virtual_player;
1036 * Writes the direction that was stored for
1037 * human player 1 into the specified worm. This function
1038 * may be used to be stored in worm.fetch_worm_direction.
1039 * The value of
1040 * the direction is read from player1_dir.
1041 * @param struct worm *w - The worm of which the direction
1042 * is altered.
1044 static void human_player1(struct worm *w) {
1045 set_worm_dir(w, player1_dir);
1049 * Writes the direction that was stored for
1050 * human player 2 into the specified worm. This function
1051 * may be used to be stored in worm.fetch_worm_direction.
1052 * The value of
1053 * the direction is read from player2_dir.
1054 * @param struct worm *w - The worm of which the direction
1055 * is altered.
1057 static void human_player2(struct worm *w) {
1058 set_worm_dir(w, player2_dir);
1062 * Writes the direction that was stored for
1063 * human player using a remote control
1064 * into the specified worm. This function
1065 * may be used to be stored in worm.fetch_worm_direction.
1066 * The value of
1067 * the direction is read from player3_dir.
1068 * @param struct worm *w - The worm of which the direction
1069 * is altered.
1071 static void remote_player(struct worm *w) {
1072 set_worm_dir(w, player3_dir);
1076 * Initializes the worm-, food- and argh-arrays, draws a frame,
1077 * makes some food and argh and display all that stuff.
1079 static void init_wormlet(void)
1081 int i;
1083 for (i = 0; i< worm_count; i++) {
1084 /* Initialize all the worm coordinates to center. */
1085 int x = (int)(FIELD_RECT_WIDTH / 2);
1086 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
1088 init_worm(&worms[i], x, y);
1091 player1_dir = EAST;
1092 player2_dir = EAST;
1093 player3_dir = EAST;
1095 if (players > 0) {
1096 worms[0].fetch_worm_direction = human_player1;
1099 if (players > 1) {
1100 if (use_remote) {
1101 worms[1].fetch_worm_direction = remote_player;
1102 } else {
1103 worms[1].fetch_worm_direction = human_player2;
1107 if (players > 2) {
1108 worms[2].fetch_worm_direction = human_player2;
1111 /* Needed when the game is restarted using BTN_STOPRESET */
1112 rb->lcd_clear_display();
1114 /* make and display some food and argh */
1115 argh_count = MAX_FOOD;
1116 for (i = 0; i < MAX_FOOD; i++) {
1117 make_food(i);
1118 draw_food(i);
1119 make_argh(i);
1120 draw_argh(i);
1123 /* draw the game field */
1124 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1125 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
1126 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
1127 rb->lcd_set_drawmode(DRMODE_SOLID);
1129 /* make everything visible */
1130 rb->lcd_update();
1135 * Move the worm one step further if it is alive.
1136 * The direction in which the worm moves is taken from dirx and diry.
1137 * move_worm decreases growing if > 0. While the worm is growing the tail
1138 * is left untouched.
1139 * @param struct worm *w The worm to move. w must not be NULL.
1141 static void move_worm(struct worm *w)
1143 if (w->alive) {
1144 /* determine the head point and its precessor */
1145 int headx = w->x[w->head];
1146 int heady = w->y[w->head];
1147 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
1148 int preheadx = w->x[prehead];
1149 int preheady = w->y[prehead];
1151 /* determine the old direction */
1152 int olddirx;
1153 int olddiry;
1154 if (headx == preheadx) {
1155 olddirx = 0;
1156 olddiry = (heady > preheady) ? 1 : -1;
1157 } else {
1158 olddiry = 0;
1159 olddirx = (headx > preheadx) ? 1 : -1;
1162 /* olddir == dir?
1163 a change of direction means a new segment
1164 has been opened */
1165 if (olddirx != w->dirx ||
1166 olddiry != w->diry) {
1167 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1170 /* new head position */
1171 w->x[w->head] = headx + w->dirx;
1172 w->y[w->head] = heady + w->diry;
1175 /* while the worm is growing no tail procession is necessary */
1176 if (w->growing > 0) {
1177 /* update the worms grow state */
1178 w->growing--;
1181 /* if the worm isn't growing the tail has to be dragged */
1182 else {
1183 /* index of the end of the tail segment */
1184 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1186 /* drag the end of the tail */
1187 /* only one coordinate has to be altered. Here it is
1188 determined which one */
1189 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1190 if (w->x[w->tail] == w->x[tail_segment_end]) {
1191 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1192 w->y[w->tail] += dir;
1193 } else {
1194 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1195 w->x[w->tail] += dir;
1198 /* when the tail has been dragged so far that it meets
1199 the next segment start the tail segment is obsolete and
1200 must be freed */
1201 if (w->x[w->tail] == w->x[tail_segment_end] &&
1202 w->y[w->tail] == w->y[tail_segment_end]){
1204 /* drop the last tail point */
1205 w->tail = tail_segment_end;
1212 * Draws the head and clears the tail of the worm in
1213 * the display buffer. lcd_update() is NOT called thus
1214 * the caller has to take care that the buffer is displayed.
1216 static void draw_worm(struct worm *w)
1218 /* draw the new head */
1219 int x = w->x[w->head];
1220 int y = w->y[w->head];
1221 #ifdef HAVE_LCD_COLOR
1222 rb->lcd_set_foreground(COLOR_WORM);
1223 #endif
1224 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1225 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1228 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1230 /* clear the space behind the worm */
1231 x = w->x[w->tail] ;
1232 y = w->y[w->tail] ;
1233 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1234 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1236 rb->lcd_set_drawmode(DRMODE_SOLID);
1237 #ifdef HAVE_LCD_COLOR
1238 rb->lcd_set_foreground(COLOR_FG);
1239 #endif
1243 * Checks wether the coordinate is part of the worm. Returns
1244 * true if any part of the worm was hit - including the head.
1245 * @param x int The x coordinate
1246 * @param y int The y coordinate
1247 * @return int The index of the worm arrays that contain x, y.
1248 * Returns -1 if the coordinates are not part of the worm.
1250 static int specific_worm_collision(struct worm *w, int x, int y)
1252 int retVal = -1;
1254 /* get_worm_array_length is expensive -> buffer the value */
1255 int wormLength = get_worm_array_length(w);
1256 int i;
1258 /* test each entry that is part of the worm */
1259 for (i = 0; i < wormLength && retVal == -1; i++) {
1261 /* The iteration iterates the length of the worm.
1262 Here's the conversion to the true indices within the worm arrays. */
1263 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1264 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1265 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1266 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1267 if (samex || samey){
1268 int test, min, max, tmp;
1270 if (samey) {
1271 min = w->x[linestart];
1272 max = w->x[lineend];
1273 test = x;
1274 } else {
1275 min = w->y[linestart];
1276 max = w->y[lineend];
1277 test = y;
1280 tmp = min;
1281 min = MIN(min, max);
1282 max = MAX(tmp, max);
1284 if (min <= test && test <= max) {
1285 retVal = lineend;
1289 return retVal;
1293 * Increases the length of the specified worm by marking
1294 * that it may grow by len pixels. Note that the worm has
1295 * to move to make the growing happen.
1296 * @param worm *w The worm that is to be altered.
1297 * @param int len A positive value specifying the amount of
1298 * pixels the worm may grow.
1300 static void add_growing(struct worm *w, int len) {
1301 w->growing += len;
1305 * Determins the worm that is at the coordinates x, y. The parameter
1306 * w is a switch parameter that changes the functionality of worm_collision.
1307 * If w is specified and x,y hits the head of w NULL is returned.
1308 * This is a useful way to determine wether the head of w hits
1309 * any worm but including itself but excluding its own head.
1310 * (It hits always its own head ;))
1311 * If w is set to NULL worm_collision returns any worm including all heads
1312 * that is at position of x,y.
1313 * @param struct worm *w The worm of which the head should be excluded in
1314 * the test. w may be set to NULL.
1315 * @param int x The x coordinate that is checked
1316 * @param int y The y coordinate that is checkec
1317 * @return struct worm* The worm that has been hit by x,y. If no worm
1318 * was at the position NULL is returned.
1320 static struct worm* worm_collision(struct worm *w, int x, int y)
1322 struct worm *retVal = NULL;
1323 int i;
1324 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1325 int collision_at = specific_worm_collision(&worms[i], x, y);
1326 if (collision_at != -1) {
1327 if (!(w == &worms[i] && collision_at == w->head)){
1328 retVal = &worms[i];
1332 return retVal;
1336 * Returns true if the head of the worm just has
1337 * crossed the field boundaries.
1338 * @return bool true if the worm just has wrapped.
1340 static bool field_collision(struct worm *w)
1342 bool retVal = false;
1343 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1344 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1345 (w->x[w->head] < 0) ||
1346 (w->y[w->head] < 0))
1348 retVal = true;
1350 return retVal;
1355 * Returns true if the specified coordinates are within the
1356 * field specified by the FIELD_RECT_XXX constants.
1357 * @param int x The x coordinate of the point that is investigated
1358 * @param int y The y coordinate of the point that is investigated
1359 * @return bool Returns false if x,y specifies a point outside the
1360 * field of worms.
1362 static bool is_in_field_rect(int x, int y)
1364 bool retVal = false;
1365 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1366 y >= 0 && y < FIELD_RECT_HEIGHT);
1367 return retVal;
1371 * Checks and returns wether the head of the w
1372 * is colliding with something currently.
1373 * @return int One of the values:
1374 * COLLISION_NONE
1375 * COLLISION_w
1376 * COLLISION_FOOD
1377 * COLLISION_ARGH
1378 * COLLISION_FIELD
1380 static int check_collision(struct worm *w)
1382 int retVal = COLLISION_NONE;
1384 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1385 retVal = COLLISION_WORM;
1387 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1388 retVal = COLLISION_FOOD;
1390 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1391 retVal = COLLISION_ARGH;
1393 if (field_collision(w))
1394 retVal = COLLISION_FIELD;
1396 return retVal;
1400 * Returns the index of the food that is closest to the point
1401 * specified by x, y. This index may be used in the foodx and
1402 * foody arrays.
1403 * @param int x The x coordinate of the point
1404 * @param int y The y coordinate of the point
1405 * @return int A value usable as index in foodx and foody.
1407 static int get_nearest_food(int x, int y)
1409 int nearestfood = 0;
1410 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1411 int deltax = 0;
1412 int deltay = 0;
1413 int foodindex;
1414 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1415 int distance;
1416 deltax = foodx[foodindex] - x;
1417 deltay = foody[foodindex] - y;
1418 deltax = deltax > 0 ? deltax : deltax * (-1);
1419 deltay = deltay > 0 ? deltay : deltay * (-1);
1420 distance = deltax + deltay;
1422 if (distance < olddistance) {
1423 olddistance = distance;
1424 nearestfood = foodindex;
1427 return nearestfood;
1431 * Returns wether the specified position is next to the worm
1432 * and in the direction the worm looks. Use this method to
1433 * test wether this position would be hit with the next move of
1434 * the worm unless the worm changes its direction.
1435 * @param struct worm *w - The worm to be investigated
1436 * @param int x - The x coordinate of the position to test.
1437 * @param int y - The y coordinate of the position to test.
1438 * @return Returns true if the worm will hit the position unless
1439 * it change its direction before the next move.
1441 static bool is_in_front_of_worm(struct worm *w, int x, int y)
1443 bool infront = false;
1444 int deltax = x - w->x[w->head];
1445 int deltay = y - w->y[w->head];
1447 if (w->dirx == 0) {
1448 infront = (w->diry * deltay) > 0;
1449 } else {
1450 infront = (w->dirx * deltax) > 0;
1452 return infront;
1456 * Returns true if the worm will collide with the next move unless
1457 * it changes its direction.
1458 * @param struct worm *w - The worm to be investigated.
1459 * @return Returns true if the worm will collide with the next move
1460 * unless it changes its direction.
1462 static bool will_worm_collide(struct worm *w)
1464 int x = w->x[w->head] + w->dirx;
1465 int y = w->y[w->head] + w->diry;
1466 bool retVal = !is_in_field_rect(x, y);
1467 if (!retVal) {
1468 retVal = (argh_collision(x, y) != -1);
1471 if (!retVal) {
1472 retVal = (worm_collision(w, x, y) != NULL);
1474 return retVal;
1478 * This function
1479 * may be used to be stored in worm.fetch_worm_direction for
1480 * worms that are not controlled by humans but by artificial stupidity.
1481 * A direction is searched that doesn't lead to collision but to the nearest
1482 * food - but not very intelligent. The direction is written to the specified
1483 * worm.
1484 * @param struct worm *w - The worm of which the direction
1485 * is altered.
1487 static void virtual_player(struct worm *w)
1489 bool isright;
1490 int plana, planb, planc;
1491 /* find the next lunch */
1492 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1494 /* determine in which direction it is */
1496 /* in front of me? */
1497 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1499 /* left right of me? */
1500 int olddir = get_worm_dir(w);
1501 set_worm_dir(w, (olddir + 1) % 4);
1502 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1503 set_worm_dir(w, olddir);
1505 /* detect situation, set strategy */
1506 if (infront) {
1507 if (isright) {
1508 plana = olddir;
1509 planb = (olddir + 1) % 4;
1510 planc = (olddir + 3) % 4;
1511 } else {
1512 plana = olddir;
1513 planb = (olddir + 3) % 4;
1514 planc = (olddir + 1) % 4;
1516 } else {
1517 if (isright) {
1518 plana = (olddir + 1) % 4;
1519 planb = olddir;
1520 planc = (olddir + 3) % 4;
1521 } else {
1522 plana = (olddir + 3) % 4;
1523 planb = olddir;
1524 planc = (olddir + 1) % 4;
1528 /* test for collision */
1529 set_worm_dir(w, plana);
1530 if (will_worm_collide(w)){
1532 /* plan b */
1533 set_worm_dir(w, planb);
1535 /* test for collision */
1536 if (will_worm_collide(w)) {
1538 /* plan c */
1539 set_worm_dir(w, planc);
1545 * prints out the score board with all the status information
1546 * about the game.
1548 static void score_board(void)
1550 char buf[15];
1551 char* buf2 = NULL;
1552 int i;
1553 int y = 0;
1554 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1555 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0,
1556 LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1557 rb->lcd_set_drawmode(DRMODE_SOLID);
1558 for (i = 0; i < worm_count; i++) {
1559 int score = get_score(&worms[i]);
1561 /* high score */
1562 if (worms[i].fetch_worm_direction != virtual_player){
1563 if (highscore < score) {
1564 highscore = score;
1568 /* length */
1569 rb->snprintf(buf, sizeof (buf),"Len:%d", score);
1571 /* worm state */
1572 switch (check_collision(&worms[i])) {
1573 case COLLISION_NONE:
1574 if (worms[i].growing > 0)
1575 buf2 = "Growing";
1576 else {
1577 if (worms[i].alive)
1578 buf2 = "Hungry";
1579 else
1580 buf2 = "Wormed";
1582 break;
1584 case COLLISION_WORM:
1585 buf2 = "Wormed";
1586 break;
1588 case COLLISION_FOOD:
1589 buf2 = "Growing";
1590 break;
1592 case COLLISION_ARGH:
1593 buf2 = "Argh";
1594 break;
1596 case COLLISION_FIELD:
1597 buf2 = "Crashed";
1598 break;
1600 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf);
1601 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2);
1603 if (!worms[i].alive){
1604 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1605 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1606 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1607 rb->lcd_set_drawmode(DRMODE_SOLID);
1609 y += 19;
1611 rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore);
1612 #ifndef DEBUG_WORMLET
1613 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);
1614 #else
1615 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
1616 #endif
1620 * Checks for collisions of the worm and its environment and
1621 * takes appropriate actions like growing the worm or killing it.
1622 * @return bool Returns true if the worm is dead. Returns
1623 * false if the worm is healthy, up and creeping.
1625 static bool process_collisions(struct worm *w)
1627 int index = -1;
1629 w->alive &= !field_collision(w);
1631 if (w->alive) {
1633 /* check if food was eaten */
1634 index = food_collision(w->x[w->head], w->y[w->head]);
1635 if (index != -1){
1636 int i;
1638 clear_food(index);
1639 make_food(index);
1640 draw_food(index);
1642 for (i = 0; i < arghs_per_food; i++) {
1643 argh_count++;
1644 if (argh_count > MAX_ARGH)
1645 argh_count = MAX_ARGH;
1646 make_argh(argh_count - 1);
1647 draw_argh(argh_count - 1);
1650 add_growing(w, worm_food);
1652 draw_worm(w);
1655 /* check if argh was eaten */
1656 else {
1657 index = argh_collision(w->x[w->head], w->y[w->head]);
1658 if (index != -1) {
1659 w->alive = false;
1661 else {
1662 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1663 w->alive = false;
1668 return !w->alive;
1672 * The main loop of the game.
1673 * @return bool Returns true if the game ended
1674 * with a dead worm. Returns false if the user
1675 * aborted the game manually.
1677 static int run(void)
1679 int button = 0;
1680 int wormDead = false;
1681 bool paused = false;
1683 /* ticks are counted to compensate speed variations */
1684 long cycle_start = 0, cycle_end = 0;
1685 #ifdef DEBUG_WORMLET
1686 int ticks_to_max_cycle_reset = 20;
1687 long max_cycle = 0;
1688 char buf[20];
1689 #endif
1691 /* initialize the board and so on */
1692 init_wormlet();
1694 cycle_start = *rb->current_tick;
1695 /* change the direction of the worm */
1696 while (!wormDead)
1698 int i;
1699 long cycle_duration=0;
1701 #ifdef HAS_BUTTON_HOLD
1702 if (rb->button_hold())
1703 paused = true;
1704 #endif
1706 switch (button) {
1707 case BTN_STARTPAUSE:
1708 paused = !paused;
1709 break;
1710 case BTN_STOPRESET:
1711 if (paused)
1712 return 1; /* restart game */
1713 else
1714 paused = true;
1715 break;
1716 #ifdef BTN_RC_QUIT
1717 case BTN_RC_QUIT:
1718 #endif
1719 case BTN_QUIT:
1720 return 2; /* back to menu */
1721 break;
1723 if (!paused)
1725 switch (button) {
1726 case BTN_DIR_UP:
1727 if (players == 1 && !use_remote) {
1728 player1_dir = NORTH;
1730 break;
1732 case BTN_DIR_DOWN:
1733 if (players == 1 && !use_remote) {
1734 player1_dir = SOUTH;
1736 break;
1738 case BTN_DIR_LEFT:
1739 if (players != 1 || use_remote) {
1740 player1_dir = (player1_dir + 3) % 4;
1741 } else {
1742 player1_dir = WEST;
1744 break;
1746 case BTN_DIR_RIGHT:
1747 if (players != 1 || use_remote) {
1748 player1_dir = (player1_dir + 1) % 4;
1749 } else {
1750 player1_dir = EAST;
1752 break;
1754 #ifdef MULTIPLAYER
1755 case BTN_PLAYER2_DIR1:
1756 player2_dir = (player2_dir + 3) % 4;
1757 break;
1759 case BTN_PLAYER2_DIR2:
1760 player2_dir = (player2_dir + 1) % 4;
1761 break;
1762 #endif
1764 #ifdef REMOTE
1765 case BTN_RC_UP:
1766 player3_dir = (player3_dir + 1) % 4;
1767 break;
1769 case BTN_RC_DOWN:
1770 player3_dir = (player3_dir + 3) % 4;
1771 break;
1772 #endif
1776 for (i = 0; i < worm_count; i++) {
1777 worms[i].fetch_worm_direction(&worms[i]);
1780 wormDead = true;
1781 for (i = 0; i < worm_count; i++){
1782 struct worm *w = &worms[i];
1783 move_worm(w);
1784 wormDead &= process_collisions(w);
1785 draw_worm(w);
1787 score_board();
1788 rb->lcd_update();
1789 if (button == BTN_STOPRESET) {
1790 wormDead = true;
1793 /* here the wormlet game cycle ends
1794 thus the current tick is stored
1795 as end time */
1796 cycle_end = *rb->current_tick;
1798 /* The duration of the game cycle */
1799 cycle_duration = cycle_end - cycle_start;
1800 cycle_duration = MAX(0, cycle_duration);
1801 cycle_duration = MIN(speed -1, cycle_duration);
1804 #ifdef DEBUG_WORMLET
1805 ticks_to_max_cycle_reset--;
1806 if (ticks_to_max_cycle_reset <= 0) {
1807 max_cycle = 0;
1810 if (max_cycle < cycle_duration) {
1811 max_cycle = cycle_duration;
1812 ticks_to_max_cycle_reset = 20;
1814 rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle);
1815 set_debug_out(buf);
1816 #endif
1818 /* adjust the number of ticks to wait for a button.
1819 This ensures that a complete game cycle including
1820 user input runs in constant time */
1821 button = rb->button_get_w_tmo(speed - cycle_duration);
1822 cycle_start = *rb->current_tick;
1825 rb->splash(HZ*2, "Game Over!");
1827 return 2; /* back to menu */
1830 #ifdef DEBUG_WORMLET
1833 * Just a test routine that checks that worm_food_collision works
1834 * in some typical situations.
1836 static void test_worm_food_collision(void)
1838 int collision_count = 0;
1839 int i;
1840 rb->lcd_clear_display();
1841 init_worm(&worms[0], 10, 10);
1842 add_growing(&worms[0], 10);
1843 set_worm_dir(&worms[0], EAST);
1844 for (i = 0; i < 10; i++) {
1845 move_worm(&worms[0]);
1846 draw_worm(&worms[0]);
1849 set_worm_dir(&worms[0], SOUTH);
1850 for (i = 0; i < 10; i++) {
1851 move_worm(&worms[0]);
1852 draw_worm(&worms[0]);
1855 foodx[0] = 15;
1856 foody[0] = 12;
1857 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1858 char buf[20];
1859 bool collision;
1860 draw_worm(&worms[0]);
1861 draw_food(0);
1862 collision = worm_food_collision(&worms[0], 0);
1863 if (collision) {
1864 collision_count++;
1866 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1867 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1868 rb->lcd_update();
1870 if (collision_count != food_size) {
1871 rb->button_get(true);
1875 foody[0] = 15;
1876 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1877 char buf[20];
1878 bool collision;
1879 draw_worm(&worms[0]);
1880 draw_food(0);
1881 collision = worm_food_collision(&worms[0], 0);
1882 if (collision) {
1883 collision_count ++;
1885 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1886 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1887 rb->lcd_update();
1889 if (collision_count != food_size * 2) {
1890 rb->button_get(true);
1895 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh)
1897 int x, y;
1898 bool retVal = false;
1899 for (x = rx; x < rx + rw; x++){
1900 for (y = ry; y < ry + rh; y++) {
1901 if (specific_worm_collision(w, x, y) != -1) {
1902 retVal = true;
1906 return retVal;
1909 static void test_worm_argh_collision(void)
1911 int i;
1912 int dir;
1913 int collision_count = 0;
1914 rb->lcd_clear_display();
1915 init_worm(&worms[0], 10, 10);
1916 add_growing(&worms[0], 40);
1917 for (dir = 0; dir < 4; dir++) {
1918 set_worm_dir(&worms[0], (EAST + dir) % 4);
1919 for (i = 0; i < 10; i++) {
1920 move_worm(&worms[0]);
1921 draw_worm(&worms[0]);
1925 arghx[0] = 12;
1926 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1927 char buf[20];
1928 bool collision;
1929 draw_argh(0);
1930 collision = worm_argh_collision(&worms[0], 0);
1931 if (collision) {
1932 collision_count ++;
1934 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1935 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1936 rb->lcd_update();
1938 if (collision_count != argh_size * 2) {
1939 rb->button_get(true);
1942 arghy[0] = 12;
1943 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1944 char buf[20];
1945 bool collision;
1946 draw_argh(0);
1947 collision = worm_argh_collision(&worms[0], 0);
1948 if (collision) {
1949 collision_count ++;
1951 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1952 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1953 rb->lcd_update();
1955 if (collision_count != argh_size * 4) {
1956 rb->button_get(true);
1960 static int testline_in_rect(void)
1962 int testfailed = -1;
1964 int rx = 10;
1965 int ry = 15;
1966 int rw = 20;
1967 int rh = 25;
1969 /* Test 1 */
1970 int x1 = 12;
1971 int y1 = 8;
1972 int x2 = 12;
1973 int y2 = 42;
1975 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1976 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1977 rb->lcd_drawrect(rx, ry, rw, rh);
1978 rb->lcd_drawline(x1, y1, x2, y2);
1979 rb->lcd_update();
1980 rb->lcd_putsxy(0, 0, "failed 1");
1981 rb->button_get(true);
1982 testfailed = 1;
1985 /* test 2 */
1986 y2 = 20;
1987 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1988 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1989 rb->lcd_drawrect(rx, ry, rw, rh);
1990 rb->lcd_drawline(x1, y1, x2, y2);
1991 rb->lcd_putsxy(0, 0, "failed 2");
1992 rb->lcd_update();
1993 rb->button_get(true);
1994 testfailed = 2;
1997 /* test 3 */
1998 y1 = 30;
1999 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2000 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2001 rb->lcd_drawrect(rx, ry, rw, rh);
2002 rb->lcd_drawline(x1, y1, x2, y2);
2003 rb->lcd_putsxy(0, 0, "failed 3");
2004 rb->lcd_update();
2005 rb->button_get(true);
2006 testfailed = 3;
2009 /* test 4 */
2010 y2 = 45;
2011 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2012 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2013 rb->lcd_drawrect(rx, ry, rw, rh);
2014 rb->lcd_drawline(x1, y1, x2, y2);
2015 rb->lcd_putsxy(0, 0, "failed 4");
2016 rb->lcd_update();
2017 rb->button_get(true);
2018 testfailed = 4;
2021 /* test 5 */
2022 y1 = 50;
2023 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2024 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2025 rb->lcd_drawrect(rx, ry, rw, rh);
2026 rb->lcd_drawline(x1, y1, x2, y2);
2027 rb->lcd_putsxy(0, 0, "failed 5");
2028 rb->lcd_update();
2029 rb->button_get(true);
2030 testfailed = 5;
2033 /* test 6 */
2034 y1 = 5;
2035 y2 = 7;
2036 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2037 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2038 rb->lcd_drawrect(rx, ry, rw, rh);
2039 rb->lcd_drawline(x1, y1, x2, y2);
2040 rb->lcd_putsxy(0, 0, "failed 6");
2041 rb->lcd_update();
2042 rb->button_get(true);
2043 testfailed = 6;
2046 /* test 7 */
2047 x1 = 8;
2048 y1 = 20;
2049 x2 = 35;
2050 y2 = 20;
2051 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2052 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2053 rb->lcd_drawrect(rx, ry, rw, rh);
2054 rb->lcd_drawline(x1, y1, x2, y2);
2055 rb->lcd_putsxy(0, 0, "failed 7");
2056 rb->lcd_update();
2057 rb->button_get(true);
2058 testfailed = 7;
2061 /* test 8 */
2062 x2 = 12;
2063 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2064 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2065 rb->lcd_drawrect(rx, ry, rw, rh);
2066 rb->lcd_drawline(x1, y1, x2, y2);
2067 rb->lcd_putsxy(0, 0, "failed 8");
2068 rb->lcd_update();
2069 rb->button_get(true);
2070 testfailed = 8;
2073 /* test 9 */
2074 x1 = 25;
2075 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2076 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2077 rb->lcd_drawrect(rx, ry, rw, rh);
2078 rb->lcd_drawline(x1, y1, x2, y2);
2079 rb->lcd_putsxy(0, 0, "failed 9");
2080 rb->lcd_update();
2081 rb->button_get(true);
2082 testfailed = 9;
2085 /* test 10 */
2086 x2 = 37;
2087 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2088 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2089 rb->lcd_drawrect(rx, ry, rw, rh);
2090 rb->lcd_drawline(x1, y1, x2, y2);
2091 rb->lcd_putsxy(0, 0, "failed 10");
2092 rb->lcd_update();
2093 rb->button_get(true);
2094 testfailed = 10;
2097 /* test 11 */
2098 x1 = 42;
2099 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2100 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2101 rb->lcd_drawrect(rx, ry, rw, rh);
2102 rb->lcd_drawline(x1, y1, x2, y2);
2103 rb->lcd_putsxy(0, 0, "failed 11");
2104 rb->lcd_update();
2105 rb->button_get(true);
2106 testfailed = 11;
2109 /* test 12 */
2110 x1 = 5;
2111 x2 = 7;
2112 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2113 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2114 rb->lcd_drawrect(rx, ry, rw, rh);
2115 rb->lcd_drawline(x1, y1, x2, y2);
2116 rb->lcd_putsxy(0, 0, "failed 12");
2117 rb->lcd_update();
2118 rb->button_get(true);
2119 testfailed = 12;
2122 /* test 13 */
2123 rx = 9;
2124 ry = 15;
2125 rw = food_size;
2126 rh = food_size;
2128 x1 = 10;
2129 y1 = 10;
2130 x2 = 10;
2131 y2 = 20;
2132 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2133 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2134 rb->lcd_drawrect(rx, ry, rw, rh);
2135 rb->lcd_drawline(x1, y1, x2, y2);
2136 rb->lcd_putsxy(0, 0, "failed 13");
2137 rb->lcd_update();
2138 rb->button_get(true);
2139 testfailed = 13;
2142 /* test 14 */
2143 rx = 9;
2144 ry = 15;
2145 rw = 4;
2146 rh = 4;
2148 x1 = 10;
2149 y1 = 10;
2150 x2 = 10;
2151 y2 = 19;
2152 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2153 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2154 rb->lcd_drawline(x1, y1, x2, y2);
2155 rb->lcd_invertrect(rx, ry, rw, rh);
2156 rb->lcd_putsxy(0, 0, "failed 14");
2157 rb->lcd_update();
2158 rb->button_get(true);
2159 testfailed = 14;
2162 rb->lcd_clear_display();
2164 return testfailed;
2168 * Just a test routine to test wether specific_worm_collision might work properly
2170 static int test_specific_worm_collision(void)
2172 int collisions = 0;
2173 int dir;
2174 int x = 0;
2175 int y = 0;
2176 char buf[20];
2177 rb->lcd_clear_display();
2178 init_worm(&worms[0], 10, 20);
2179 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2181 for (dir = EAST; dir < EAST + 4; dir++) {
2182 int i;
2183 set_worm_dir(&worms[0], dir % 4);
2184 for (i = 0; i < 5; i++) {
2185 if (!(dir % 4 == NORTH && i == 9)) {
2186 move_worm(&worms[0]);
2187 draw_worm(&worms[0]);
2192 for (y = 15; y < 30; y ++){
2193 for (x = 5; x < 20; x++) {
2194 if (specific_worm_collision(&worms[0], x, y) != -1) {
2195 collisions ++;
2197 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2198 rb->snprintf(buf, sizeof buf, "collisions %d", collisions);
2199 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2200 rb->lcd_update();
2203 if (collisions != 21) {
2204 rb->button_get(true);
2206 return collisions;
2209 static void test_make_argh(void)
2211 int dir;
2212 int seed = 0;
2213 int hit = 0;
2214 int failures = 0;
2215 int last_failures = 0;
2216 int i, worm_idx;
2217 rb->lcd_clear_display();
2218 worm_count = 3;
2220 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2221 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2222 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2225 for (dir = EAST; dir < EAST + 4; dir++) {
2226 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2227 set_worm_dir(&worms[worm_idx], dir % 4);
2228 for (i = 0; i < 10; i++) {
2229 if (!(dir % 4 == NORTH && i == 9)) {
2230 move_worm(&worms[worm_idx]);
2231 draw_worm(&worms[worm_idx]);
2237 rb->lcd_update();
2239 for (seed = 0; hit < 20; seed += 2) {
2240 char buf[20];
2241 int x, y;
2242 rb->srand(seed);
2243 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2244 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2246 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2247 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2248 int tries = 0;
2249 rb->srand(seed);
2251 tries = make_argh(0);
2252 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2253 failures ++;
2256 rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d",
2257 x, y, failures, tries);
2258 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2259 rb->lcd_update();
2260 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y,
2261 argh_size, argh_size);
2262 rb->lcd_update();
2263 draw_argh(0);
2264 rb->lcd_update();
2265 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y,
2266 argh_size, argh_size);
2267 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y,
2268 argh_size, argh_size);
2270 if (failures > last_failures) {
2271 rb->button_get(true);
2273 last_failures = failures;
2274 hit ++;
2280 static void test_worm_argh_collision_in_moves(void) {
2281 int hit_count = 0;
2282 int i;
2283 rb->lcd_clear_display();
2284 init_worm(&worms[0], 10, 20);
2286 arghx[0] = 20;
2287 arghy[0] = 18;
2288 draw_argh(0);
2290 set_worm_dir(&worms[0], EAST);
2291 for (i = 0; i < 20; i++) {
2292 char buf[20];
2293 move_worm(&worms[0]);
2294 draw_worm(&worms[0]);
2295 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2296 hit_count ++;
2298 rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
2299 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2300 rb->lcd_update();
2302 if (hit_count != argh_size + 5) {
2303 rb->button_get(true);
2306 #endif /* DEBUG_WORMLET */
2309 * Reverts default settings
2311 static void default_settings(void)
2313 arghs_per_food = ARGHS_PER_FOOD;
2314 argh_size = ARGH_SIZE;
2315 food_size = FOOD_SIZE;
2316 speed = SPEED;
2317 worm_food = WORM_PER_FOOD;
2318 players = 1;
2319 worm_count = MAX_WORMS;
2320 use_remote = false;
2321 return;
2325 * Launches the wormlet game
2327 static bool launch_wormlet(void)
2329 int game_result = 1;
2331 rb->lcd_clear_display();
2333 /* Turn off backlight timeout */
2334 backlight_force_on(); /* backlight control in lib/helper.c */
2336 /* start the game */
2337 while (game_result == 1)
2338 game_result = run();
2340 switch (game_result)
2342 case 2:
2343 /* Turn on backlight timeout (revert to settings) */
2344 backlight_use_settings(); /* backlight control in lib/helper.c */
2345 return false;
2346 break;
2348 return false;
2352 * Main entry point
2354 enum plugin_status plugin_start(const void* parameter)
2356 int result;
2357 int menu_quit = 0;
2358 int new_setting;
2360 (void)(parameter);
2362 default_settings();
2363 if (configfile_load(SETTINGS_FILENAME, config,
2364 sizeof(config)/sizeof(*config),
2365 SETTINGS_MIN_VERSION ) < 0)
2367 /* If the loading failed, save a new config file (as the disk is
2368 already spinning) */
2369 configfile_save(SETTINGS_FILENAME, config,
2370 sizeof(config)/sizeof(*config),
2371 SETTINGS_VERSION);
2374 #ifdef HAVE_LCD_COLOR
2375 rb->lcd_set_foreground(COLOR_FG);
2376 rb->lcd_set_background(COLOR_BG);
2377 #endif
2379 #if LCD_DEPTH > 1
2380 rb->lcd_set_backdrop(NULL);
2381 #endif
2383 #ifdef DEBUG_WORMLET
2384 testline_in_rect();
2385 test_worm_argh_collision_in_moves();
2386 test_make_argh();
2387 test_worm_food_collision();
2388 test_worm_argh_collision();
2389 test_specific_worm_collision();
2390 #endif
2392 /* Setup screen */
2394 static const struct opt_items noyes[2] = {
2395 { "No", -1 },
2396 { "Yes", -1 },
2399 static const struct opt_items remoteonly_option[1] = {
2400 { "Remote Control", -1 }
2403 static const struct opt_items key24_option[2] = {
2404 { "4 Key Control", -1 },
2405 { "2 Key Control", -1 }
2408 #ifdef REMOTE
2409 static const struct opt_items remote_option[2] = {
2410 { "Remote Control", -1 },
2411 { "No Rem. Control", -1 }
2413 #else
2414 static const struct opt_items key2_option[1] = {
2415 { "2 Key Control", -1 }
2417 #endif
2419 static const struct opt_items nokey_option[1] = {
2420 { "Out of Control", -1 }
2423 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, "Play Wormlet!",
2424 "Number of Worms", "Number of Players", "Control Style",
2425 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2426 "Argh Size","Food Size","Revert to Default Settings",
2427 "Playback Control", "Quit");
2429 rb->button_clear_queue();
2431 while (!menu_quit) {
2432 switch(rb->do_menu(&menu, &result, NULL, false))
2434 case 0:
2435 rb->lcd_setfont(FONT_SYSFIXED);
2436 launch_wormlet();
2437 break;
2438 case 1:
2439 rb->set_int("Number of Worms", "", UNIT_INT, &worm_count, NULL,
2440 1, 1, 3, NULL);
2441 if (worm_count < players) {
2442 worm_count = players;
2444 break;
2445 case 2:
2446 #ifdef MULTIPLAYER
2447 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2448 1, 0, 4, NULL);
2449 #else
2450 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2451 1, 0, 2, NULL);
2452 #endif
2453 if (players > worm_count) {
2454 worm_count = players;
2456 if (players > 2) {
2457 use_remote = true;
2459 break;
2460 case 3:
2461 switch(players) {
2462 case 0:
2463 rb->set_option("Control Style",&use_remote,INT,
2464 nokey_option, 1, NULL);
2465 break;
2466 case 1:
2467 rb->set_option("Control Style",&use_remote,INT,
2468 key24_option, 2, NULL);
2469 break;
2470 case 2:
2471 #ifdef REMOTE
2472 rb->set_option("Control Style",&use_remote,INT,
2473 remote_option, 2, NULL);
2474 #else
2475 rb->set_option("Control Style",&use_remote,INT,
2476 key2_option, 1, NULL);
2477 #endif
2478 break;
2479 case 3:
2480 rb->set_option("Control Style",&use_remote,INT,
2481 remoteonly_option, 1, NULL);
2482 break;
2484 break;
2485 case 4:
2486 rb->set_int("Worm Growth Per Food", "", UNIT_INT, &worm_food,
2487 NULL, 1, 0, 15, NULL);
2488 break;
2489 case 5:
2490 new_setting = 20 - speed;
2491 rb->set_int("Worm Speed", "", UNIT_INT, &new_setting,
2492 NULL, 1, 0, 20, NULL);
2493 speed = 20 - new_setting;
2494 break;
2495 case 6:
2496 rb->set_int("Arghs Per Food", "", UNIT_INT, &arghs_per_food,
2497 NULL, 1, 0, 8, NULL);
2498 break;
2499 case 7:
2500 rb->set_int("Argh Size", "", UNIT_INT, &argh_size,
2501 NULL, 1, 2, 10, NULL);
2502 break;
2503 case 8:
2504 rb->set_int("Food Size", "", UNIT_INT, &food_size,
2505 NULL, 1, 2, 10, NULL);
2506 break;
2507 case 9:
2508 new_setting = 0;
2509 rb->set_option("Reset Settings?", &new_setting, INT, noyes , 2, NULL);
2510 if (new_setting == 1)
2511 default_settings();
2512 break;
2513 case 10:
2514 playback_control(NULL);
2515 break;
2516 default:
2517 menu_quit=1;
2518 break;
2522 configfile_save(SETTINGS_FILENAME, config,
2523 sizeof(config)/sizeof(*config),
2524 SETTINGS_VERSION);
2526 return PLUGIN_OK;