FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / apps / plugins / wormlet.c
blobeb76e922d920855890d6941caae43f9ca9147088
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 == COWOND2_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 == ONDAVX747_PAD) || CONFIG_KEYPAD == MROBE500_PAD
237 #define BTN_QUIT BUTTON_POWER
239 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
241 #define BTN_DIR_UP BUTTON_UP
242 #define BTN_DIR_DOWN BUTTON_DOWN
243 #define BTN_DIR_LEFT BUTTON_LEFT
244 #define BTN_DIR_RIGHT BUTTON_RIGHT
245 #define BTN_STARTPAUSE BUTTON_PLAY
246 #define BTN_QUIT BUTTON_FFWD
247 #define BTN_STOPRESET BUTTON_REW
249 #else
250 #error No keymap defined!
251 #endif
253 #ifdef HAVE_TOUCHSCREEN
254 #ifndef BTN_DIR_UP
255 #define BTN_DIR_UP BUTTON_TOPMIDDLE
256 #endif
257 #ifndef BTN_DIR_DOWN
258 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
259 #endif
260 #ifndef BTN_DIR_LEFT
261 #define BTN_DIR_LEFT BUTTON_MIDLEFT
262 #endif
263 #ifndef BTN_DIR_RIGHT
264 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
265 #endif
266 #ifndef BTN_STARTPAUSE
267 #define BTN_STARTPAUSE BUTTON_CENTER
268 #endif
269 #ifndef BTN_QUIT
270 #define BTN_QUIT BUTTON_TOPLEFT
271 #endif
272 #ifndef BTN_STOPRESET
273 #define BTN_STOPRESET BUTTON_TOPRIGHT
274 #endif
275 #endif
277 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
278 #define FOOD_SIZE 3
279 #define ARGH_SIZE 4
280 #define SPEED 14
281 #define MAX_WORM_SEGMENTS 128
282 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
283 #define FOOD_SIZE 3
284 #define ARGH_SIZE 4
285 #define SPEED 14
286 #define MAX_WORM_SEGMENTS 128
287 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
288 #define FOOD_SIZE 3
289 #define ARGH_SIZE 4
290 #define SPEED 14
291 #define MAX_WORM_SEGMENTS 128
292 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
293 #define FOOD_SIZE 3
294 #define ARGH_SIZE 4
295 #define SPEED 12
296 #define MAX_WORM_SEGMENTS 128
297 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
298 #define FOOD_SIZE 4
299 #define ARGH_SIZE 5
300 #define SPEED 10
301 #define MAX_WORM_SEGMENTS 128
302 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
303 #define FOOD_SIZE 4
304 #define ARGH_SIZE 5
305 #define SPEED 9
306 #define MAX_WORM_SEGMENTS 128
307 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
308 #define FOOD_SIZE 4
309 #define ARGH_SIZE 5
310 #define SPEED 8
311 #define MAX_WORM_SEGMENTS 256
312 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
313 #define FOOD_SIZE 4
314 #define ARGH_SIZE 5
315 #define SPEED 6
316 #define MAX_WORM_SEGMENTS 256
317 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
318 #define FOOD_SIZE 5
319 #define ARGH_SIZE 6
320 #define SPEED 4
321 #define MAX_WORM_SEGMENTS 512
322 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
323 #define FOOD_SIZE 5
324 #define ARGH_SIZE 6
325 #define SPEED 4
326 #define MAX_WORM_SEGMENTS 512
327 #elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
328 ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
329 #define FOOD_SIZE 7
330 #define ARGH_SIZE 8
331 #define SPEED 4
332 #define MAX_WORM_SEGMENTS 512
333 #elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
334 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
335 #define FOOD_SIZE 14
336 #define ARGH_SIZE 16
337 #define SPEED 4
338 #define MAX_WORM_SEGMENTS 512
339 #endif
341 #ifdef HAVE_LCD_COLOR
342 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
343 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
344 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
345 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
346 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
347 #endif
350 * All the properties that a worm has.
352 static struct worm {
353 /* The worm is stored in a ring of xy coordinates */
354 int x[MAX_WORM_SEGMENTS];
355 int y[MAX_WORM_SEGMENTS];
357 int head; /* index of the head within the buffer */
358 int tail; /* index of the tail within the buffer */
359 int growing; /* number of cyles the worm still keeps growing */
360 bool alive; /* the worms living state */
362 /* direction vector in which the worm moves */
363 int dirx; /* only values -1 0 1 allowed */
364 int diry; /* only values -1 0 1 allowed */
366 /* this method is used to fetch the direction the user
367 has selected. It can be one of the values
368 human_player1, human_player2, remote_player, virtual_player.
369 All these values are fuctions, that can change the direction
370 of the worm */
371 void (*fetch_worm_direction)(struct worm *w);
372 } worms[MAX_WORMS];
374 /* stores the highscore - besides it was scored by a virtual player */
375 static int highscore;
377 #define MAX_FOOD 5 /* maximal number of food items */
379 /* The arrays store the food coordinates */
380 static int foodx[MAX_FOOD];
381 static int foody[MAX_FOOD];
383 #define MAX_ARGH 100 /* maximal number of argh items */
384 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
386 /* The arrays store the argh coordinates */
387 static int arghx[MAX_ARGH];
388 static int arghy[MAX_ARGH];
390 /* the number of arghs that are currently in use */
391 static int argh_count;
393 /* the number of arghs per food, settable by user */
394 static int arghs_per_food = ARGHS_PER_FOOD;
395 /* the size of the argh, settable by user */
396 static int argh_size = ARGH_SIZE;
397 /* the size of the food, settable by user */
398 static int food_size = FOOD_SIZE;
399 /* the speed of the worm, settable by user */
400 static int speed = SPEED;
401 /* the amount a worm grows by eating a food, settable by user */
402 static int worm_food = WORM_PER_FOOD;
404 /* End additional variables */
406 #ifdef DEBUG_WORMLET
407 /* just a buffer used for debug output */
408 static char debugout[15];
409 #endif
411 /* the number of active worms (dead or alive) */
412 static int worm_count = MAX_WORMS;
414 /* in multiplayer mode: en- / disables the remote worm control
415 in singleplayer mode: toggles 4 / 2 button worm control */
416 static bool use_remote = false;
418 /* return values of check_collision */
419 #define COLLISION_NONE 0
420 #define COLLISION_WORM 1
421 #define COLLISION_FOOD 2
422 #define COLLISION_ARGH 3
423 #define COLLISION_FIELD 4
425 /* constants for use as directions.
426 Note that the values are ordered clockwise.
427 Thus increasing / decreasing the values
428 is equivalent to right / left turns. */
429 #define WEST 0
430 #define NORTH 1
431 #define EAST 2
432 #define SOUTH 3
434 /* direction of human player 1 */
435 static int player1_dir = EAST;
436 /* direction of human player 2 */
437 static int player2_dir = EAST;
438 /* direction of human player 3 */
439 static int player3_dir = EAST;
441 /* the number of (human) players that currently
442 control a worm */
443 static int players = 1;
445 #define SETTINGS_VERSION 1
446 #define SETTINGS_MIN_VERSION 1
447 #define SETTINGS_FILENAME "wormlet.cfg"
449 static struct configdata config[] =
451 {TYPE_INT, 0, 1024, { .int_p = &highscore }, "highscore", NULL},
452 {TYPE_INT, 0, 15, { .int_p = &arghs_per_food }, "arghs per food", NULL},
453 {TYPE_INT, 0, 15, { .int_p = &argh_size }, "argh size", NULL},
454 {TYPE_INT, 0, 15, { .int_p = &food_size }, "food size", NULL},
455 {TYPE_INT, 0, 3, { .int_p = &players }, "players", NULL},
456 {TYPE_INT, 0, 3, { .int_p = &worm_count }, "worms", NULL},
457 {TYPE_INT, 0, 20, { .int_p = &speed }, "speed", NULL},
458 {TYPE_INT, 0, 15, { .int_p = &worm_food }, "Worm Growth Per Food", NULL}
461 #ifdef DEBUG_WORMLET
462 static void set_debug_out(char *str){
463 strcpy(debugout, str);
465 #endif
468 * Returns the direction id in which the worm
469 * currently is creeping.
470 * @param struct worm *w The worm that is to be investigated.
471 * w Must not be null.
472 * @return int A value 0 <= value < 4
473 * Note the predefined constants NORTH, SOUTH, EAST, WEST
475 static int get_worm_dir(struct worm *w)
477 int retVal ;
478 if (w->dirx == 0) {
479 if (w->diry == 1) {
480 retVal = SOUTH;
481 } else {
482 retVal = NORTH;
484 } else {
485 if (w->dirx == 1) {
486 retVal = EAST;
487 } else {
488 retVal = WEST;
491 return retVal;
495 * Set the direction of the specified worm with a direction id.
496 * Increasing the value by 1 means to turn the worm direction
497 * to right by 90 degree.
498 * @param struct worm *w The worm that is to be altered. w Must not be null.
499 * @param int dir The new direction in which the worm is to creep.
500 * dir must be 0 <= dir < 4. Use predefined constants
501 * NORTH, SOUTH, EAST, WEST
503 static void set_worm_dir(struct worm *w, int dir)
505 switch (dir) {
506 case WEST:
507 w->dirx = -1;
508 w->diry = 0;
509 break;
510 case NORTH:
511 w->dirx = 0;
512 w->diry = - 1;
513 break;
514 case EAST:
515 w->dirx = 1;
516 w->diry = 0;
517 break;
518 case SOUTH:
519 w->dirx = 0;
520 w->diry = 1;
521 break;
526 * Returns the current length of the worm array. This
527 * is also a value for the number of bends that are in the worm.
528 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
530 static int get_worm_array_length(struct worm *w)
532 /* initial simple calculation will be overwritten if wrong. */
533 int retVal = w->head - w->tail;
535 /* if the worm 'crosses' the boundaries of the ringbuffer */
536 if (retVal < 0) {
537 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
540 return retVal;
544 * Returns the score the specified worm. The score is the length
545 * of the worm.
546 * @param struct worm *w The worm that is to be investigated.
547 * w must not be null.
548 * @return int The length of the worm (>= 0).
550 static int get_score(struct worm *w)
552 int retval = 0;
553 int length = get_worm_array_length(w);
554 int i;
555 for (i = 0; i < length; i++) {
557 /* The iteration iterates the length of the worm.
558 Here's the conversion to the true indices within the worm arrays. */
559 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
560 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
561 int startx = w->x[linestart];
562 int starty = w->y[linestart];
563 int endx = w->x[lineend];
564 int endy = w->y[lineend];
566 int minimum, maximum;
568 if (startx == endx) {
569 minimum = MIN(starty, endy);
570 maximum = MAX(starty, endy);
571 } else {
572 minimum = MIN(startx, endx);
573 maximum = MAX(startx, endx);
575 retval += abs(maximum - minimum);
577 return retval;
581 * Determines wether the line specified by startx, starty, endx, endy intersects
582 * the rectangle specified by x, y, width, height. Note that the line must be exactly
583 * horizontal or vertical (startx == endx or starty == endy).
584 * @param int startx The x coordinate of the start point of the line.
585 * @param int starty The y coordinate of the start point of the line.
586 * @param int endx The x coordinate of the end point of the line.
587 * @param int endy The y coordinate of the end point of the line.
588 * @param int x The x coordinate of the top left corner of the rectangle.
589 * @param int y The y coordinate of the top left corner of the rectangle.
590 * @param int width The width of the rectangle.
591 * @param int height The height of the rectangle.
592 * @return bool Returns true if the specified line intersects with the recangle.
594 static bool line_in_rect(int startx, int starty, int endx, int endy,
595 int x, int y, int width, int height)
597 bool retval = false;
598 int simple, simplemin, simplemax;
599 int compa, compb, compmin, compmax;
600 int temp;
601 if (startx == endx) {
602 simple = startx;
603 simplemin = x;
604 simplemax = x + width;
606 compa = starty;
607 compb = endy;
608 compmin = y;
609 compmax = y + height;
610 } else {
611 simple = starty;
612 simplemin = y;
613 simplemax = y + height;
615 compa = startx;
616 compb = endx;
617 compmin = x;
618 compmax = x + width;
621 temp = compa;
622 compa = MIN(compa, compb);
623 compb = MAX(temp, compb);
625 if (simplemin <= simple && simple <= simplemax) {
626 if ((compmin <= compa && compa <= compmax) ||
627 (compmin <= compb && compb <= compmax) ||
628 (compa <= compmin && compb >= compmax)) {
629 retval = true;
632 return retval;
636 * Tests wether the specified worm intersects with the rect.
637 * @param struct worm *w The worm to be investigated
638 * @param int x The x coordinate of the top left corner of the rect
639 * @param int y The y coordinate of the top left corner of the rect
640 * @param int widht The width of the rect
641 * @param int height The height of the rect
642 * @return bool Returns true if the worm intersects with the rect
644 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height)
646 bool retval = false;
649 /* get_worm_array_length is expensive -> buffer the value */
650 int wormLength = get_worm_array_length(w);
651 int i;
653 /* test each entry that is part of the worm */
654 for (i = 0; i < wormLength && retval == false; i++) {
656 /* The iteration iterates the length of the worm.
657 Here's the conversion to the true indices within the worm arrays. */
658 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
659 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
660 int startx = w->x[linestart];
661 int starty = w->y[linestart];
662 int endx = w->x[lineend];
663 int endy = w->y[lineend];
665 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
668 return retval;
672 * Checks wether a specific food in the food arrays is at the
673 * specified coordinates.
674 * @param int foodIndex The index of the food in the food arrays
675 * @param int x the x coordinate.
676 * @param int y the y coordinate.
677 * @return Returns true if the coordinate hits the food specified by
678 * foodIndex.
680 static bool specific_food_collision(int foodIndex, int x, int y)
682 bool retVal = false;
683 if (x >= foodx[foodIndex] &&
684 x < foodx[foodIndex] + food_size &&
685 y >= foody[foodIndex] &&
686 y < foody[foodIndex] + food_size) {
688 retVal = true;
690 return retVal;
694 * Returns the index of the food that is at the
695 * given coordinates. If no food is at the coordinates
696 * -1 is returned.
697 * @return int -1 <= value < MAX_FOOD
699 static int food_collision(int x, int y)
701 int i = 0;
702 int retVal = -1;
703 for (i = 0; i < MAX_FOOD; i++) {
704 if (specific_food_collision(i, x, y)) {
705 retVal = i;
706 break;
709 return retVal;
713 * Checks wether a specific argh in the argh arrays is at the
714 * specified coordinates.
715 * @param int arghIndex The index of the argh in the argh arrays
716 * @param int x the x coordinate.
717 * @param int y the y coordinate.
718 * @return Returns true if the coordinate hits the argh specified by
719 * arghIndex.
721 static bool specific_argh_collision(int arghIndex, int x, int y)
723 if ( x >= arghx[arghIndex] &&
724 y >= arghy[arghIndex] &&
725 x < arghx[arghIndex] + argh_size &&
726 y < arghy[arghIndex] + argh_size )
728 return true;
731 return false;
735 * Returns the index of the argh that is at the
736 * given coordinates. If no argh is at the coordinates
737 * -1 is returned.
738 * @param int x The x coordinate.
739 * @param int y The y coordinate.
740 * @return int -1 <= value < argh_count <= MAX_ARGH
742 static int argh_collision(int x, int y)
744 int i = 0;
745 int retVal = -1;
747 /* search for the argh that has the specified coords */
748 for (i = 0; i < argh_count; i++) {
749 if (specific_argh_collision(i, x, y)) {
750 retVal = i;
751 break;
754 return retVal;
758 * Checks wether the worm collides with the food at the specfied food-arrays.
759 * @param int foodIndex The index of the food in the arrays. Ensure the value is
760 * 0 <= foodIndex <= MAX_FOOD
761 * @return Returns true if the worm collides with the specified food.
763 static bool worm_food_collision(struct worm *w, int foodIndex)
765 bool retVal = false;
767 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
768 food_size - 1, food_size - 1);
770 return retVal;
774 * Returns true if the worm hits the argh within the next moves (unless
775 * the worm changes it's direction).
776 * @param struct worm *w - The worm to investigate
777 * @param int argh_idx - The index of the argh
778 * @param int moves - The number of moves that are considered.
779 * @return Returns false if the specified argh is not hit within the next
780 * moves.
782 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves)
784 bool retVal = false;
785 int x1, y1, x2, y2;
786 x1 = w->x[w->head];
787 y1 = w->y[w->head];
789 x2 = w->x[w->head] + moves * w->dirx;
790 y2 = w->y[w->head] + moves * w->diry;
792 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
793 argh_size, argh_size);
794 return retVal;
798 * Checks wether the worm collides with the argh at the specfied argh-arrays.
799 * @param int arghIndex The index of the argh in the arrays.
800 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
801 * @return Returns true if the worm collides with the specified argh.
803 static bool worm_argh_collision(struct worm *w, int arghIndex)
805 bool retVal = false;
807 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
808 argh_size - 1, argh_size - 1);
810 return retVal;
814 * Find new coordinates for the food stored in foodx[index], foody[index]
815 * that don't collide with any other food or argh
816 * @param int index
817 * Ensure that 0 <= index < MAX_FOOD.
819 static void make_food(int index)
821 int x = 0;
822 int y = 0;
823 bool collisionDetected = false;
824 int i;
826 do {
827 /* make coordinates for a new food so that
828 the entire food lies within the FIELD */
829 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
830 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
832 /* Ensure that the new food doesn't collide with any
833 existing foods or arghs.
834 If one or more corners of the new food hit any existing
835 argh or food a collision is detected.
837 collisionDetected =
838 food_collision(x , y ) >= 0 ||
839 food_collision(x , y + food_size - 1) >= 0 ||
840 food_collision(x + food_size - 1, y ) >= 0 ||
841 food_collision(x + food_size - 1, y + food_size - 1) >= 0 ||
842 argh_collision(x , y ) >= 0 ||
843 argh_collision(x , y + food_size - 1) >= 0 ||
844 argh_collision(x + food_size - 1, y ) >= 0 ||
845 argh_collision(x + food_size - 1, y + food_size - 1) >= 0;
847 /* use coordinates for further testing */
848 foodx[index] = x;
849 foody[index] = y;
851 /* now test wether we accidently hit the worm with food ;) */
852 i = 0;
853 for (i = 0; i < worm_count && !collisionDetected; i++) {
854 collisionDetected |= worm_food_collision(&worms[i], index);
857 while (collisionDetected);
858 return;
862 * Clears a food from the lcd buffer.
863 * @param int index The index of the food arrays under which
864 * the coordinates of the desired food can be found. Ensure
865 * that the value is 0 <= index <= MAX_FOOD.
867 static void clear_food(int index)
869 /* remove the old food from the screen */
870 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
871 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
872 foody[index] + FIELD_RECT_Y,
873 food_size, food_size);
874 rb->lcd_set_drawmode(DRMODE_SOLID);
878 * Draws a food in the lcd buffer.
879 * @param int index The index of the food arrays under which
880 * the coordinates of the desired food can be found. Ensure
881 * that the value is 0 <= index <= MAX_FOOD.
883 static void draw_food(int index)
885 /* draw the food object */
886 #ifdef HAVE_LCD_COLOR
887 rb->lcd_set_foreground(COLOR_FOOD);
888 #endif
889 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
890 foody[index] + FIELD_RECT_Y,
891 food_size, food_size);
892 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
893 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
894 foody[index] + FIELD_RECT_Y + 1,
895 food_size - 2, food_size - 2);
896 rb->lcd_set_drawmode(DRMODE_SOLID);
897 #ifdef HAVE_LCD_COLOR
898 rb->lcd_set_foreground(COLOR_FG);
899 #endif
903 * Find new coordinates for the argh stored in arghx[index], arghy[index]
904 * that don't collide with any other food or argh.
905 * @param int index
906 * Ensure that 0 <= index < argh_count < MAX_ARGH.
908 static void make_argh(int index)
910 int x = -1;
911 int y = -1;
912 bool collisionDetected = false;
913 int i;
915 do {
916 /* make coordinates for a new argh so that
917 the entire food lies within the FIELD */
918 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
919 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
921 /* Ensure that the new argh doesn't intersect with any
922 existing foods or arghs.
923 If one or more corners of the new argh hit any existing
924 argh or food an intersection is detected.
926 collisionDetected =
927 food_collision(x , y ) >= 0 ||
928 food_collision(x , y + argh_size - 1) >= 0 ||
929 food_collision(x + argh_size - 1, y ) >= 0 ||
930 food_collision(x + argh_size - 1, y + argh_size - 1) >= 0 ||
931 argh_collision(x , y ) >= 0 ||
932 argh_collision(x , y + argh_size - 1) >= 0 ||
933 argh_collision(x + argh_size - 1, y ) >= 0 ||
934 argh_collision(x + argh_size - 1, y + argh_size - 1) >= 0;
936 /* use the candidate coordinates to make a real argh */
937 arghx[index] = x;
938 arghy[index] = y;
940 /* now test wether we accidently hit the worm with argh ;) */
941 for (i = 0; i < worm_count && !collisionDetected; i++) {
942 collisionDetected |= worm_argh_collision(&worms[i], index);
943 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
944 MIN_ARGH_DIST);
947 while (collisionDetected);
948 return;
952 * Draws an argh in the lcd buffer.
953 * @param int index The index of the argh arrays under which
954 * the coordinates of the desired argh can be found. Ensure
955 * that the value is 0 <= index < argh_count <= MAX_ARGH.
957 static void draw_argh(int index)
959 /* draw the new argh */
960 #ifdef HAVE_LCD_COLOR
961 rb->lcd_set_foreground(COLOR_ARGH);
962 #endif
963 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
964 arghy[index] + FIELD_RECT_Y,
965 argh_size, argh_size);
966 #ifdef HAVE_LCD_COLOR
967 rb->lcd_set_foreground(COLOR_FG);
968 #endif
971 static void virtual_player(struct worm *w);
973 * Initialzes the specified worm with INITIAL_WORM_LENGTH
974 * and the tail at the specified position. The worm will
975 * be initialized alive and creeping EAST.
976 * @param struct worm *w The worm that is to be initialized
977 * @param int x The x coordinate at which the tail of the worm starts.
978 * x must be 0 <= x < FIELD_RECT_WIDTH.
979 * @param int y The y coordinate at which the tail of the worm starts
980 * y must be 0 <= y < FIELD_RECT_WIDTH.
982 static void init_worm(struct worm *w, int x, int y)
984 /* initialize the worm size */
985 w->head = 1;
986 w->tail = 0;
988 w->x[w->head] = x + 1;
989 w->y[w->head] = y;
991 w->x[w->tail] = x;
992 w->y[w->tail] = y;
994 /* set the initial direction the worm creeps to */
995 w->dirx = 1;
996 w->diry = 0;
998 w->growing = INITIAL_WORM_LENGTH - 1;
999 w->alive = true;
1000 w->fetch_worm_direction = virtual_player;
1004 * Writes the direction that was stored for
1005 * human player 1 into the specified worm. This function
1006 * may be used to be stored in worm.fetch_worm_direction.
1007 * The value of
1008 * the direction is read from player1_dir.
1009 * @param struct worm *w - The worm of which the direction
1010 * is altered.
1012 static void human_player1(struct worm *w) {
1013 set_worm_dir(w, player1_dir);
1017 * Writes the direction that was stored for
1018 * human player 2 into the specified worm. This function
1019 * may be used to be stored in worm.fetch_worm_direction.
1020 * The value of
1021 * the direction is read from player2_dir.
1022 * @param struct worm *w - The worm of which the direction
1023 * is altered.
1025 static void human_player2(struct worm *w) {
1026 set_worm_dir(w, player2_dir);
1030 * Writes the direction that was stored for
1031 * human player using a remote control
1032 * into the specified worm. This function
1033 * may be used to be stored in worm.fetch_worm_direction.
1034 * The value of
1035 * the direction is read from player3_dir.
1036 * @param struct worm *w - The worm of which the direction
1037 * is altered.
1039 static void remote_player(struct worm *w) {
1040 set_worm_dir(w, player3_dir);
1044 * Initializes the worm-, food- and argh-arrays, draws a frame,
1045 * makes some food and argh and display all that stuff.
1047 static void init_wormlet(void)
1049 int i;
1051 for (i = 0; i< worm_count; i++) {
1052 /* Initialize all the worm coordinates to center. */
1053 int x = (int)(FIELD_RECT_WIDTH / 2);
1054 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
1056 init_worm(&worms[i], x, y);
1059 player1_dir = EAST;
1060 player2_dir = EAST;
1061 player3_dir = EAST;
1063 if (players > 0) {
1064 worms[0].fetch_worm_direction = human_player1;
1067 if (players > 1) {
1068 if (use_remote) {
1069 worms[1].fetch_worm_direction = remote_player;
1070 } else {
1071 worms[1].fetch_worm_direction = human_player2;
1075 if (players > 2) {
1076 worms[2].fetch_worm_direction = human_player2;
1079 /* Needed when the game is restarted using BTN_STOPRESET */
1080 rb->lcd_clear_display();
1082 /* make and display some food and argh */
1083 argh_count = MAX_FOOD;
1084 for (i = 0; i < MAX_FOOD; i++) {
1085 make_food(i);
1086 draw_food(i);
1087 make_argh(i);
1088 draw_argh(i);
1091 /* draw the game field */
1092 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1093 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
1094 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
1095 rb->lcd_set_drawmode(DRMODE_SOLID);
1097 /* make everything visible */
1098 rb->lcd_update();
1103 * Move the worm one step further if it is alive.
1104 * The direction in which the worm moves is taken from dirx and diry.
1105 * move_worm decreases growing if > 0. While the worm is growing the tail
1106 * is left untouched.
1107 * @param struct worm *w The worm to move. w must not be NULL.
1109 static void move_worm(struct worm *w)
1111 if (w->alive) {
1112 /* determine the head point and its precessor */
1113 int headx = w->x[w->head];
1114 int heady = w->y[w->head];
1115 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
1116 int preheadx = w->x[prehead];
1117 int preheady = w->y[prehead];
1119 /* determine the old direction */
1120 int olddirx;
1121 int olddiry;
1122 if (headx == preheadx) {
1123 olddirx = 0;
1124 olddiry = (heady > preheady) ? 1 : -1;
1125 } else {
1126 olddiry = 0;
1127 olddirx = (headx > preheadx) ? 1 : -1;
1130 /* olddir == dir?
1131 a change of direction means a new segment
1132 has been opened */
1133 if (olddirx != w->dirx ||
1134 olddiry != w->diry) {
1135 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1138 /* new head position */
1139 w->x[w->head] = headx + w->dirx;
1140 w->y[w->head] = heady + w->diry;
1143 /* while the worm is growing no tail procession is necessary */
1144 if (w->growing > 0) {
1145 /* update the worms grow state */
1146 w->growing--;
1149 /* if the worm isn't growing the tail has to be dragged */
1150 else {
1151 /* index of the end of the tail segment */
1152 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1154 /* drag the end of the tail */
1155 /* only one coordinate has to be altered. Here it is
1156 determined which one */
1157 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1158 if (w->x[w->tail] == w->x[tail_segment_end]) {
1159 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1160 w->y[w->tail] += dir;
1161 } else {
1162 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1163 w->x[w->tail] += dir;
1166 /* when the tail has been dragged so far that it meets
1167 the next segment start the tail segment is obsolete and
1168 must be freed */
1169 if (w->x[w->tail] == w->x[tail_segment_end] &&
1170 w->y[w->tail] == w->y[tail_segment_end]){
1172 /* drop the last tail point */
1173 w->tail = tail_segment_end;
1180 * Draws the head and clears the tail of the worm in
1181 * the display buffer. lcd_update() is NOT called thus
1182 * the caller has to take care that the buffer is displayed.
1184 static void draw_worm(struct worm *w)
1186 /* draw the new head */
1187 int x = w->x[w->head];
1188 int y = w->y[w->head];
1189 #ifdef HAVE_LCD_COLOR
1190 rb->lcd_set_foreground(COLOR_WORM);
1191 #endif
1192 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1193 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1196 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1198 /* clear the space behind the worm */
1199 x = w->x[w->tail] ;
1200 y = w->y[w->tail] ;
1201 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1202 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1204 rb->lcd_set_drawmode(DRMODE_SOLID);
1205 #ifdef HAVE_LCD_COLOR
1206 rb->lcd_set_foreground(COLOR_FG);
1207 #endif
1211 * Checks wether the coordinate is part of the worm. Returns
1212 * true if any part of the worm was hit - including the head.
1213 * @param x int The x coordinate
1214 * @param y int The y coordinate
1215 * @return int The index of the worm arrays that contain x, y.
1216 * Returns -1 if the coordinates are not part of the worm.
1218 static int specific_worm_collision(struct worm *w, int x, int y)
1220 int retVal = -1;
1222 /* get_worm_array_length is expensive -> buffer the value */
1223 int wormLength = get_worm_array_length(w);
1224 int i;
1226 /* test each entry that is part of the worm */
1227 for (i = 0; i < wormLength && retVal == -1; i++) {
1229 /* The iteration iterates the length of the worm.
1230 Here's the conversion to the true indices within the worm arrays. */
1231 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1232 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1233 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1234 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1235 if (samex || samey){
1236 int test, min, max, tmp;
1238 if (samey) {
1239 min = w->x[linestart];
1240 max = w->x[lineend];
1241 test = x;
1242 } else {
1243 min = w->y[linestart];
1244 max = w->y[lineend];
1245 test = y;
1248 tmp = min;
1249 min = MIN(min, max);
1250 max = MAX(tmp, max);
1252 if (min <= test && test <= max) {
1253 retVal = lineend;
1257 return retVal;
1261 * Increases the length of the specified worm by marking
1262 * that it may grow by len pixels. Note that the worm has
1263 * to move to make the growing happen.
1264 * @param worm *w The worm that is to be altered.
1265 * @param int len A positive value specifying the amount of
1266 * pixels the worm may grow.
1268 static void add_growing(struct worm *w, int len) {
1269 w->growing += len;
1273 * Determins the worm that is at the coordinates x, y. The parameter
1274 * w is a switch parameter that changes the functionality of worm_collision.
1275 * If w is specified and x,y hits the head of w NULL is returned.
1276 * This is a useful way to determine wether the head of w hits
1277 * any worm but including itself but excluding its own head.
1278 * (It hits always its own head ;))
1279 * If w is set to NULL worm_collision returns any worm including all heads
1280 * that is at position of x,y.
1281 * @param struct worm *w The worm of which the head should be excluded in
1282 * the test. w may be set to NULL.
1283 * @param int x The x coordinate that is checked
1284 * @param int y The y coordinate that is checkec
1285 * @return struct worm* The worm that has been hit by x,y. If no worm
1286 * was at the position NULL is returned.
1288 static struct worm* worm_collision(struct worm *w, int x, int y)
1290 struct worm *retVal = NULL;
1291 int i;
1292 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1293 int collision_at = specific_worm_collision(&worms[i], x, y);
1294 if (collision_at != -1) {
1295 if (!(w == &worms[i] && collision_at == w->head)){
1296 retVal = &worms[i];
1300 return retVal;
1304 * Returns true if the head of the worm just has
1305 * crossed the field boundaries.
1306 * @return bool true if the worm just has wrapped.
1308 static bool field_collision(struct worm *w)
1310 bool retVal = false;
1311 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1312 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1313 (w->x[w->head] < 0) ||
1314 (w->y[w->head] < 0))
1316 retVal = true;
1318 return retVal;
1323 * Returns true if the specified coordinates are within the
1324 * field specified by the FIELD_RECT_XXX constants.
1325 * @param int x The x coordinate of the point that is investigated
1326 * @param int y The y coordinate of the point that is investigated
1327 * @return bool Returns false if x,y specifies a point outside the
1328 * field of worms.
1330 static bool is_in_field_rect(int x, int y)
1332 bool retVal = false;
1333 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1334 y >= 0 && y < FIELD_RECT_HEIGHT);
1335 return retVal;
1339 * Checks and returns wether the head of the w
1340 * is colliding with something currently.
1341 * @return int One of the values:
1342 * COLLISION_NONE
1343 * COLLISION_w
1344 * COLLISION_FOOD
1345 * COLLISION_ARGH
1346 * COLLISION_FIELD
1348 static int check_collision(struct worm *w)
1350 int retVal = COLLISION_NONE;
1352 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1353 retVal = COLLISION_WORM;
1355 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1356 retVal = COLLISION_FOOD;
1358 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1359 retVal = COLLISION_ARGH;
1361 if (field_collision(w))
1362 retVal = COLLISION_FIELD;
1364 return retVal;
1368 * Returns the index of the food that is closest to the point
1369 * specified by x, y. This index may be used in the foodx and
1370 * foody arrays.
1371 * @param int x The x coordinate of the point
1372 * @param int y The y coordinate of the point
1373 * @return int A value usable as index in foodx and foody.
1375 static int get_nearest_food(int x, int y)
1377 int nearestfood = 0;
1378 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1379 int deltax = 0;
1380 int deltay = 0;
1381 int foodindex;
1382 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1383 int distance;
1384 deltax = foodx[foodindex] - x;
1385 deltay = foody[foodindex] - y;
1386 deltax = deltax > 0 ? deltax : deltax * (-1);
1387 deltay = deltay > 0 ? deltay : deltay * (-1);
1388 distance = deltax + deltay;
1390 if (distance < olddistance) {
1391 olddistance = distance;
1392 nearestfood = foodindex;
1395 return nearestfood;
1399 * Returns wether the specified position is next to the worm
1400 * and in the direction the worm looks. Use this method to
1401 * test wether this position would be hit with the next move of
1402 * the worm unless the worm changes its direction.
1403 * @param struct worm *w - The worm to be investigated
1404 * @param int x - The x coordinate of the position to test.
1405 * @param int y - The y coordinate of the position to test.
1406 * @return Returns true if the worm will hit the position unless
1407 * it change its direction before the next move.
1409 static bool is_in_front_of_worm(struct worm *w, int x, int y)
1411 bool infront = false;
1412 int deltax = x - w->x[w->head];
1413 int deltay = y - w->y[w->head];
1415 if (w->dirx == 0) {
1416 infront = (w->diry * deltay) > 0;
1417 } else {
1418 infront = (w->dirx * deltax) > 0;
1420 return infront;
1424 * Returns true if the worm will collide with the next move unless
1425 * it changes its direction.
1426 * @param struct worm *w - The worm to be investigated.
1427 * @return Returns true if the worm will collide with the next move
1428 * unless it changes its direction.
1430 static bool will_worm_collide(struct worm *w)
1432 int x = w->x[w->head] + w->dirx;
1433 int y = w->y[w->head] + w->diry;
1434 bool retVal = !is_in_field_rect(x, y);
1435 if (!retVal) {
1436 retVal = (argh_collision(x, y) != -1);
1439 if (!retVal) {
1440 retVal = (worm_collision(w, x, y) != NULL);
1442 return retVal;
1446 * This function
1447 * may be used to be stored in worm.fetch_worm_direction for
1448 * worms that are not controlled by humans but by artificial stupidity.
1449 * A direction is searched that doesn't lead to collision but to the nearest
1450 * food - but not very intelligent. The direction is written to the specified
1451 * worm.
1452 * @param struct worm *w - The worm of which the direction
1453 * is altered.
1455 static void virtual_player(struct worm *w)
1457 bool isright;
1458 int plana, planb, planc;
1459 /* find the next lunch */
1460 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1462 /* determine in which direction it is */
1464 /* in front of me? */
1465 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1467 /* left right of me? */
1468 int olddir = get_worm_dir(w);
1469 set_worm_dir(w, (olddir + 1) % 4);
1470 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1471 set_worm_dir(w, olddir);
1473 /* detect situation, set strategy */
1474 if (infront) {
1475 if (isright) {
1476 plana = olddir;
1477 planb = (olddir + 1) % 4;
1478 planc = (olddir + 3) % 4;
1479 } else {
1480 plana = olddir;
1481 planb = (olddir + 3) % 4;
1482 planc = (olddir + 1) % 4;
1484 } else {
1485 if (isright) {
1486 plana = (olddir + 1) % 4;
1487 planb = olddir;
1488 planc = (olddir + 3) % 4;
1489 } else {
1490 plana = (olddir + 3) % 4;
1491 planb = olddir;
1492 planc = (olddir + 1) % 4;
1496 /* test for collision */
1497 set_worm_dir(w, plana);
1498 if (will_worm_collide(w)){
1500 /* plan b */
1501 set_worm_dir(w, planb);
1503 /* test for collision */
1504 if (will_worm_collide(w)) {
1506 /* plan c */
1507 set_worm_dir(w, planc);
1513 * prints out the score board with all the status information
1514 * about the game.
1516 static void score_board(void)
1518 char buf[15];
1519 char* buf2 = NULL;
1520 int i;
1521 int y = 0;
1522 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1523 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0,
1524 LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1525 rb->lcd_set_drawmode(DRMODE_SOLID);
1526 for (i = 0; i < worm_count; i++) {
1527 int score = get_score(&worms[i]);
1529 /* high score */
1530 if (worms[i].fetch_worm_direction != virtual_player){
1531 if (highscore < score) {
1532 highscore = score;
1536 /* length */
1537 rb->snprintf(buf, sizeof (buf),"Len:%d", score);
1539 /* worm state */
1540 switch (check_collision(&worms[i])) {
1541 case COLLISION_NONE:
1542 if (worms[i].growing > 0)
1543 buf2 = "Growing";
1544 else {
1545 if (worms[i].alive)
1546 buf2 = "Hungry";
1547 else
1548 buf2 = "Wormed";
1550 break;
1552 case COLLISION_WORM:
1553 buf2 = "Wormed";
1554 break;
1556 case COLLISION_FOOD:
1557 buf2 = "Growing";
1558 break;
1560 case COLLISION_ARGH:
1561 buf2 = "Argh";
1562 break;
1564 case COLLISION_FIELD:
1565 buf2 = "Crashed";
1566 break;
1568 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf);
1569 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2);
1571 if (!worms[i].alive){
1572 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1573 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1574 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1575 rb->lcd_set_drawmode(DRMODE_SOLID);
1577 y += 19;
1579 rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore);
1580 #ifndef DEBUG_WORMLET
1581 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);
1582 #else
1583 rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
1584 #endif
1588 * Checks for collisions of the worm and its environment and
1589 * takes appropriate actions like growing the worm or killing it.
1590 * @return bool Returns true if the worm is dead. Returns
1591 * false if the worm is healthy, up and creeping.
1593 static bool process_collisions(struct worm *w)
1595 int index = -1;
1597 w->alive &= !field_collision(w);
1599 if (w->alive) {
1601 /* check if food was eaten */
1602 index = food_collision(w->x[w->head], w->y[w->head]);
1603 if (index != -1){
1604 int i;
1606 clear_food(index);
1607 make_food(index);
1608 draw_food(index);
1610 for (i = 0; i < arghs_per_food; i++) {
1611 argh_count++;
1612 if (argh_count > MAX_ARGH)
1613 argh_count = MAX_ARGH;
1614 make_argh(argh_count - 1);
1615 draw_argh(argh_count - 1);
1618 add_growing(w, worm_food);
1620 draw_worm(w);
1623 /* check if argh was eaten */
1624 else {
1625 index = argh_collision(w->x[w->head], w->y[w->head]);
1626 if (index != -1) {
1627 w->alive = false;
1629 else {
1630 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1631 w->alive = false;
1636 return !w->alive;
1640 * The main loop of the game.
1641 * @return bool Returns true if the game ended
1642 * with a dead worm. Returns false if the user
1643 * aborted the game manually.
1645 static int run(void)
1647 int button = 0;
1648 int wormDead = false;
1649 bool paused = false;
1651 /* ticks are counted to compensate speed variations */
1652 long cycle_start = 0, cycle_end = 0;
1653 #ifdef DEBUG_WORMLET
1654 int ticks_to_max_cycle_reset = 20;
1655 long max_cycle = 0;
1656 char buf[20];
1657 #endif
1659 /* initialize the board and so on */
1660 init_wormlet();
1662 cycle_start = *rb->current_tick;
1663 /* change the direction of the worm */
1664 while (!wormDead)
1666 int i;
1667 long cycle_duration=0;
1669 #ifdef HAS_BUTTON_HOLD
1670 if (rb->button_hold())
1671 paused = true;
1672 #endif
1674 switch (button) {
1675 case BTN_STARTPAUSE:
1676 paused = !paused;
1677 break;
1678 case BTN_STOPRESET:
1679 if (paused)
1680 return 1; /* restart game */
1681 else
1682 paused = true;
1683 break;
1684 #ifdef BTN_RC_QUIT
1685 case BTN_RC_QUIT:
1686 #endif
1687 case BTN_QUIT:
1688 return 2; /* back to menu */
1689 break;
1691 if (!paused)
1693 switch (button) {
1694 case BTN_DIR_UP:
1695 if (players == 1 && !use_remote) {
1696 player1_dir = NORTH;
1698 break;
1700 case BTN_DIR_DOWN:
1701 if (players == 1 && !use_remote) {
1702 player1_dir = SOUTH;
1704 break;
1706 case BTN_DIR_LEFT:
1707 if (players != 1 || use_remote) {
1708 player1_dir = (player1_dir + 3) % 4;
1709 } else {
1710 player1_dir = WEST;
1712 break;
1714 case BTN_DIR_RIGHT:
1715 if (players != 1 || use_remote) {
1716 player1_dir = (player1_dir + 1) % 4;
1717 } else {
1718 player1_dir = EAST;
1720 break;
1722 #ifdef MULTIPLAYER
1723 case BTN_PLAYER2_DIR1:
1724 player2_dir = (player2_dir + 3) % 4;
1725 break;
1727 case BTN_PLAYER2_DIR2:
1728 player2_dir = (player2_dir + 1) % 4;
1729 break;
1730 #endif
1732 #ifdef REMOTE
1733 case BTN_RC_UP:
1734 player3_dir = (player3_dir + 1) % 4;
1735 break;
1737 case BTN_RC_DOWN:
1738 player3_dir = (player3_dir + 3) % 4;
1739 break;
1740 #endif
1744 for (i = 0; i < worm_count; i++) {
1745 worms[i].fetch_worm_direction(&worms[i]);
1748 wormDead = true;
1749 for (i = 0; i < worm_count; i++){
1750 struct worm *w = &worms[i];
1751 move_worm(w);
1752 wormDead &= process_collisions(w);
1753 draw_worm(w);
1755 score_board();
1756 rb->lcd_update();
1757 if (button == BTN_STOPRESET) {
1758 wormDead = true;
1761 /* here the wormlet game cycle ends
1762 thus the current tick is stored
1763 as end time */
1764 cycle_end = *rb->current_tick;
1766 /* The duration of the game cycle */
1767 cycle_duration = cycle_end - cycle_start;
1768 cycle_duration = MAX(0, cycle_duration);
1769 cycle_duration = MIN(speed -1, cycle_duration);
1772 #ifdef DEBUG_WORMLET
1773 ticks_to_max_cycle_reset--;
1774 if (ticks_to_max_cycle_reset <= 0) {
1775 max_cycle = 0;
1778 if (max_cycle < cycle_duration) {
1779 max_cycle = cycle_duration;
1780 ticks_to_max_cycle_reset = 20;
1782 rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle);
1783 set_debug_out(buf);
1784 #endif
1786 /* adjust the number of ticks to wait for a button.
1787 This ensures that a complete game cycle including
1788 user input runs in constant time */
1789 button = rb->button_get_w_tmo(speed - cycle_duration);
1790 cycle_start = *rb->current_tick;
1793 rb->splash(HZ*2, "Game Over!");
1795 return 2; /* back to menu */
1798 #ifdef DEBUG_WORMLET
1801 * Just a test routine that checks that worm_food_collision works
1802 * in some typical situations.
1804 static void test_worm_food_collision(void)
1806 int collision_count = 0;
1807 int i;
1808 rb->lcd_clear_display();
1809 init_worm(&worms[0], 10, 10);
1810 add_growing(&worms[0], 10);
1811 set_worm_dir(&worms[0], EAST);
1812 for (i = 0; i < 10; i++) {
1813 move_worm(&worms[0]);
1814 draw_worm(&worms[0]);
1817 set_worm_dir(&worms[0], SOUTH);
1818 for (i = 0; i < 10; i++) {
1819 move_worm(&worms[0]);
1820 draw_worm(&worms[0]);
1823 foodx[0] = 15;
1824 foody[0] = 12;
1825 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1826 char buf[20];
1827 bool collision;
1828 draw_worm(&worms[0]);
1829 draw_food(0);
1830 collision = worm_food_collision(&worms[0], 0);
1831 if (collision) {
1832 collision_count++;
1834 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1835 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1836 rb->lcd_update();
1838 if (collision_count != food_size) {
1839 rb->button_get(true);
1843 foody[0] = 15;
1844 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1845 char buf[20];
1846 bool collision;
1847 draw_worm(&worms[0]);
1848 draw_food(0);
1849 collision = worm_food_collision(&worms[0], 0);
1850 if (collision) {
1851 collision_count ++;
1853 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1854 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1855 rb->lcd_update();
1857 if (collision_count != food_size * 2) {
1858 rb->button_get(true);
1863 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh)
1865 int x, y;
1866 bool retVal = false;
1867 for (x = rx; x < rx + rw; x++){
1868 for (y = ry; y < ry + rh; y++) {
1869 if (specific_worm_collision(w, x, y) != -1) {
1870 retVal = true;
1874 return retVal;
1877 static void test_worm_argh_collision(void)
1879 int i;
1880 int dir;
1881 int collision_count = 0;
1882 rb->lcd_clear_display();
1883 init_worm(&worms[0], 10, 10);
1884 add_growing(&worms[0], 40);
1885 for (dir = 0; dir < 4; dir++) {
1886 set_worm_dir(&worms[0], (EAST + dir) % 4);
1887 for (i = 0; i < 10; i++) {
1888 move_worm(&worms[0]);
1889 draw_worm(&worms[0]);
1893 arghx[0] = 12;
1894 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1895 char buf[20];
1896 bool collision;
1897 draw_argh(0);
1898 collision = worm_argh_collision(&worms[0], 0);
1899 if (collision) {
1900 collision_count ++;
1902 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1903 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1904 rb->lcd_update();
1906 if (collision_count != argh_size * 2) {
1907 rb->button_get(true);
1910 arghy[0] = 12;
1911 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1912 char buf[20];
1913 bool collision;
1914 draw_argh(0);
1915 collision = worm_argh_collision(&worms[0], 0);
1916 if (collision) {
1917 collision_count ++;
1919 rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count);
1920 rb->lcd_putsxy(0, LCD_HEIGHT -8, buf);
1921 rb->lcd_update();
1923 if (collision_count != argh_size * 4) {
1924 rb->button_get(true);
1928 static int testline_in_rect(void)
1930 int testfailed = -1;
1932 int rx = 10;
1933 int ry = 15;
1934 int rw = 20;
1935 int rh = 25;
1937 /* Test 1 */
1938 int x1 = 12;
1939 int y1 = 8;
1940 int x2 = 12;
1941 int y2 = 42;
1943 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1944 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1945 rb->lcd_drawrect(rx, ry, rw, rh);
1946 rb->lcd_drawline(x1, y1, x2, y2);
1947 rb->lcd_update();
1948 rb->lcd_putsxy(0, 0, "failed 1");
1949 rb->button_get(true);
1950 testfailed = 1;
1953 /* test 2 */
1954 y2 = 20;
1955 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1956 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1957 rb->lcd_drawrect(rx, ry, rw, rh);
1958 rb->lcd_drawline(x1, y1, x2, y2);
1959 rb->lcd_putsxy(0, 0, "failed 2");
1960 rb->lcd_update();
1961 rb->button_get(true);
1962 testfailed = 2;
1965 /* test 3 */
1966 y1 = 30;
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 3");
1972 rb->lcd_update();
1973 rb->button_get(true);
1974 testfailed = 3;
1977 /* test 4 */
1978 y2 = 45;
1979 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1980 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1981 rb->lcd_drawrect(rx, ry, rw, rh);
1982 rb->lcd_drawline(x1, y1, x2, y2);
1983 rb->lcd_putsxy(0, 0, "failed 4");
1984 rb->lcd_update();
1985 rb->button_get(true);
1986 testfailed = 4;
1989 /* test 5 */
1990 y1 = 50;
1991 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1992 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1993 rb->lcd_drawrect(rx, ry, rw, rh);
1994 rb->lcd_drawline(x1, y1, x2, y2);
1995 rb->lcd_putsxy(0, 0, "failed 5");
1996 rb->lcd_update();
1997 rb->button_get(true);
1998 testfailed = 5;
2001 /* test 6 */
2002 y1 = 5;
2003 y2 = 7;
2004 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2005 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2006 rb->lcd_drawrect(rx, ry, rw, rh);
2007 rb->lcd_drawline(x1, y1, x2, y2);
2008 rb->lcd_putsxy(0, 0, "failed 6");
2009 rb->lcd_update();
2010 rb->button_get(true);
2011 testfailed = 6;
2014 /* test 7 */
2015 x1 = 8;
2016 y1 = 20;
2017 x2 = 35;
2018 y2 = 20;
2019 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2020 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2021 rb->lcd_drawrect(rx, ry, rw, rh);
2022 rb->lcd_drawline(x1, y1, x2, y2);
2023 rb->lcd_putsxy(0, 0, "failed 7");
2024 rb->lcd_update();
2025 rb->button_get(true);
2026 testfailed = 7;
2029 /* test 8 */
2030 x2 = 12;
2031 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2032 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2033 rb->lcd_drawrect(rx, ry, rw, rh);
2034 rb->lcd_drawline(x1, y1, x2, y2);
2035 rb->lcd_putsxy(0, 0, "failed 8");
2036 rb->lcd_update();
2037 rb->button_get(true);
2038 testfailed = 8;
2041 /* test 9 */
2042 x1 = 25;
2043 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2044 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2045 rb->lcd_drawrect(rx, ry, rw, rh);
2046 rb->lcd_drawline(x1, y1, x2, y2);
2047 rb->lcd_putsxy(0, 0, "failed 9");
2048 rb->lcd_update();
2049 rb->button_get(true);
2050 testfailed = 9;
2053 /* test 10 */
2054 x2 = 37;
2055 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2056 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2057 rb->lcd_drawrect(rx, ry, rw, rh);
2058 rb->lcd_drawline(x1, y1, x2, y2);
2059 rb->lcd_putsxy(0, 0, "failed 10");
2060 rb->lcd_update();
2061 rb->button_get(true);
2062 testfailed = 10;
2065 /* test 11 */
2066 x1 = 42;
2067 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2068 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2069 rb->lcd_drawrect(rx, ry, rw, rh);
2070 rb->lcd_drawline(x1, y1, x2, y2);
2071 rb->lcd_putsxy(0, 0, "failed 11");
2072 rb->lcd_update();
2073 rb->button_get(true);
2074 testfailed = 11;
2077 /* test 12 */
2078 x1 = 5;
2079 x2 = 7;
2080 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2081 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2082 rb->lcd_drawrect(rx, ry, rw, rh);
2083 rb->lcd_drawline(x1, y1, x2, y2);
2084 rb->lcd_putsxy(0, 0, "failed 12");
2085 rb->lcd_update();
2086 rb->button_get(true);
2087 testfailed = 12;
2090 /* test 13 */
2091 rx = 9;
2092 ry = 15;
2093 rw = food_size;
2094 rh = food_size;
2096 x1 = 10;
2097 y1 = 10;
2098 x2 = 10;
2099 y2 = 20;
2100 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2101 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2102 rb->lcd_drawrect(rx, ry, rw, rh);
2103 rb->lcd_drawline(x1, y1, x2, y2);
2104 rb->lcd_putsxy(0, 0, "failed 13");
2105 rb->lcd_update();
2106 rb->button_get(true);
2107 testfailed = 13;
2110 /* test 14 */
2111 rx = 9;
2112 ry = 15;
2113 rw = 4;
2114 rh = 4;
2116 x1 = 10;
2117 y1 = 10;
2118 x2 = 10;
2119 y2 = 19;
2120 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2121 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2122 rb->lcd_drawline(x1, y1, x2, y2);
2123 rb->lcd_invertrect(rx, ry, rw, rh);
2124 rb->lcd_putsxy(0, 0, "failed 14");
2125 rb->lcd_update();
2126 rb->button_get(true);
2127 testfailed = 14;
2130 rb->lcd_clear_display();
2132 return testfailed;
2136 * Just a test routine to test wether specific_worm_collision might work properly
2138 static int test_specific_worm_collision(void)
2140 int collisions = 0;
2141 int dir;
2142 int x = 0;
2143 int y = 0;
2144 char buf[20];
2145 rb->lcd_clear_display();
2146 init_worm(&worms[0], 10, 20);
2147 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2149 for (dir = EAST; dir < EAST + 4; dir++) {
2150 int i;
2151 set_worm_dir(&worms[0], dir % 4);
2152 for (i = 0; i < 5; i++) {
2153 if (!(dir % 4 == NORTH && i == 9)) {
2154 move_worm(&worms[0]);
2155 draw_worm(&worms[0]);
2160 for (y = 15; y < 30; y ++){
2161 for (x = 5; x < 20; x++) {
2162 if (specific_worm_collision(&worms[0], x, y) != -1) {
2163 collisions ++;
2165 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2166 rb->snprintf(buf, sizeof buf, "collisions %d", collisions);
2167 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2168 rb->lcd_update();
2171 if (collisions != 21) {
2172 rb->button_get(true);
2174 return collisions;
2177 static void test_make_argh(void)
2179 int dir;
2180 int seed = 0;
2181 int hit = 0;
2182 int failures = 0;
2183 int last_failures = 0;
2184 int i, worm_idx;
2185 rb->lcd_clear_display();
2186 worm_count = 3;
2188 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2189 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2190 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2193 for (dir = EAST; dir < EAST + 4; dir++) {
2194 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2195 set_worm_dir(&worms[worm_idx], dir % 4);
2196 for (i = 0; i < 10; i++) {
2197 if (!(dir % 4 == NORTH && i == 9)) {
2198 move_worm(&worms[worm_idx]);
2199 draw_worm(&worms[worm_idx]);
2205 rb->lcd_update();
2207 for (seed = 0; hit < 20; seed += 2) {
2208 char buf[20];
2209 int x, y;
2210 rb->srand(seed);
2211 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2212 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2214 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2215 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2216 int tries = 0;
2217 rb->srand(seed);
2219 tries = make_argh(0);
2220 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2221 failures ++;
2224 rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d",
2225 x, y, failures, tries);
2226 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2227 rb->lcd_update();
2228 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y,
2229 argh_size, argh_size);
2230 rb->lcd_update();
2231 draw_argh(0);
2232 rb->lcd_update();
2233 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y,
2234 argh_size, argh_size);
2235 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y,
2236 argh_size, argh_size);
2238 if (failures > last_failures) {
2239 rb->button_get(true);
2241 last_failures = failures;
2242 hit ++;
2248 static void test_worm_argh_collision_in_moves(void) {
2249 int hit_count = 0;
2250 int i;
2251 rb->lcd_clear_display();
2252 init_worm(&worms[0], 10, 20);
2254 arghx[0] = 20;
2255 arghy[0] = 18;
2256 draw_argh(0);
2258 set_worm_dir(&worms[0], EAST);
2259 for (i = 0; i < 20; i++) {
2260 char buf[20];
2261 move_worm(&worms[0]);
2262 draw_worm(&worms[0]);
2263 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2264 hit_count ++;
2266 rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
2267 rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf);
2268 rb->lcd_update();
2270 if (hit_count != argh_size + 5) {
2271 rb->button_get(true);
2274 #endif /* DEBUG_WORMLET */
2277 * Reverts default settings
2279 static void default_settings(void)
2281 arghs_per_food = ARGHS_PER_FOOD;
2282 argh_size = ARGH_SIZE;
2283 food_size = FOOD_SIZE;
2284 speed = SPEED;
2285 worm_food = WORM_PER_FOOD;
2286 players = 1;
2287 worm_count = MAX_WORMS;
2288 use_remote = false;
2289 return;
2293 * Launches the wormlet game
2295 static bool launch_wormlet(void)
2297 int game_result = 1;
2299 rb->lcd_clear_display();
2301 /* Turn off backlight timeout */
2302 backlight_force_on(); /* backlight control in lib/helper.c */
2304 /* start the game */
2305 while (game_result == 1)
2306 game_result = run();
2308 switch (game_result)
2310 case 2:
2311 /* Turn on backlight timeout (revert to settings) */
2312 backlight_use_settings(); /* backlight control in lib/helper.c */
2313 return false;
2314 break;
2316 return false;
2320 * Main entry point
2322 enum plugin_status plugin_start(const void* parameter)
2324 int result;
2325 int menu_quit = 0;
2326 int new_setting;
2328 (void)(parameter);
2330 default_settings();
2331 if (configfile_load(SETTINGS_FILENAME, config,
2332 sizeof(config)/sizeof(*config),
2333 SETTINGS_MIN_VERSION ) < 0)
2335 /* If the loading failed, save a new config file (as the disk is
2336 already spinning) */
2337 configfile_save(SETTINGS_FILENAME, config,
2338 sizeof(config)/sizeof(*config),
2339 SETTINGS_VERSION);
2342 #ifdef HAVE_LCD_COLOR
2343 rb->lcd_set_foreground(COLOR_FG);
2344 rb->lcd_set_background(COLOR_BG);
2345 #endif
2347 #if LCD_DEPTH > 1
2348 rb->lcd_set_backdrop(NULL);
2349 #endif
2351 #ifdef DEBUG_WORMLET
2352 testline_in_rect();
2353 test_worm_argh_collision_in_moves();
2354 test_make_argh();
2355 test_worm_food_collision();
2356 test_worm_argh_collision();
2357 test_specific_worm_collision();
2358 #endif
2360 /* Setup screen */
2362 static const struct opt_items noyes[2] = {
2363 { "No", -1 },
2364 { "Yes", -1 },
2367 static const struct opt_items remoteonly_option[1] = {
2368 { "Remote Control", -1 }
2371 static const struct opt_items key24_option[2] = {
2372 { "4 Key Control", -1 },
2373 { "2 Key Control", -1 }
2376 #ifdef REMOTE
2377 static const struct opt_items remote_option[2] = {
2378 { "Remote Control", -1 },
2379 { "No Rem. Control", -1 }
2381 #else
2382 static const struct opt_items key2_option[1] = {
2383 { "2 Key Control", -1 }
2385 #endif
2387 static const struct opt_items nokey_option[1] = {
2388 { "Out of Control", -1 }
2391 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, "Play Wormlet!",
2392 "Number of Worms", "Number of Players", "Control Style",
2393 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2394 "Argh Size","Food Size","Revert to Default Settings",
2395 "Playback Control", "Quit");
2397 rb->button_clear_queue();
2399 while (!menu_quit) {
2400 switch(rb->do_menu(&menu, &result, NULL, false))
2402 case 0:
2403 rb->lcd_setfont(FONT_SYSFIXED);
2404 launch_wormlet();
2405 break;
2406 case 1:
2407 rb->set_int("Number of Worms", "", UNIT_INT, &worm_count, NULL,
2408 1, 1, 3, NULL);
2409 if (worm_count < players) {
2410 worm_count = players;
2412 break;
2413 case 2:
2414 #ifdef MULTIPLAYER
2415 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2416 1, 0, 4, NULL);
2417 #else
2418 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2419 1, 0, 2, NULL);
2420 #endif
2421 if (players > worm_count) {
2422 worm_count = players;
2424 if (players > 2) {
2425 use_remote = true;
2427 break;
2428 case 3:
2429 switch(players) {
2430 case 0:
2431 rb->set_option("Control Style",&use_remote,INT,
2432 nokey_option, 1, NULL);
2433 break;
2434 case 1:
2435 rb->set_option("Control Style",&use_remote,INT,
2436 key24_option, 2, NULL);
2437 break;
2438 case 2:
2439 #ifdef REMOTE
2440 rb->set_option("Control Style",&use_remote,INT,
2441 remote_option, 2, NULL);
2442 #else
2443 rb->set_option("Control Style",&use_remote,INT,
2444 key2_option, 1, NULL);
2445 #endif
2446 break;
2447 case 3:
2448 rb->set_option("Control Style",&use_remote,INT,
2449 remoteonly_option, 1, NULL);
2450 break;
2452 break;
2453 case 4:
2454 rb->set_int("Worm Growth Per Food", "", UNIT_INT, &worm_food,
2455 NULL, 1, 0, 15, NULL);
2456 break;
2457 case 5:
2458 new_setting = 20 - speed;
2459 rb->set_int("Worm Speed", "", UNIT_INT, &new_setting,
2460 NULL, 1, 0, 20, NULL);
2461 speed = 20 - new_setting;
2462 break;
2463 case 6:
2464 rb->set_int("Arghs Per Food", "", UNIT_INT, &arghs_per_food,
2465 NULL, 1, 0, 8, NULL);
2466 break;
2467 case 7:
2468 rb->set_int("Argh Size", "", UNIT_INT, &argh_size,
2469 NULL, 1, 2, 10, NULL);
2470 break;
2471 case 8:
2472 rb->set_int("Food Size", "", UNIT_INT, &food_size,
2473 NULL, 1, 2, 10, NULL);
2474 break;
2475 case 9:
2476 new_setting = 0;
2477 rb->set_option("Reset Settings?", &new_setting, INT, noyes , 2, NULL);
2478 if (new_setting == 1)
2479 default_settings();
2480 break;
2481 case 10:
2482 playback_control(NULL);
2483 break;
2484 default:
2485 menu_quit=1;
2486 break;
2490 configfile_save(SETTINGS_FILENAME, config,
2491 sizeof(config)/sizeof(*config),
2492 SETTINGS_VERSION);
2494 return PLUGIN_OK;