Bump version numbers for 3.13
[maemo-rb.git] / apps / plugins / wormlet.c
blob2ad5d538a3fcae3c7a5c0dedf1e8817420e92e96
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 #ifdef DEBUG_WORMLET
27 static long max_cycle;
28 #endif
30 /* size of the field the worm lives in */
31 #define FIELD_RECT_X 1
32 #define FIELD_RECT_Y 1
33 #define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
34 #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
36 /* when the game starts */
37 #define INITIAL_WORM_LENGTH 10
39 /* num of pixel the worm grows per eaten food */
40 #define WORM_PER_FOOD 7
42 /* num of worms creeping in the FIELD */
43 #define MAX_WORMS 3
45 /* minimal distance between a worm and an argh
46 when a new argh is made */
47 #define MIN_ARGH_DIST 5
49 #if (CONFIG_KEYPAD == RECORDER_PAD)
50 #define BTN_DIR_UP BUTTON_UP
51 #define BTN_DIR_DOWN BUTTON_DOWN
52 #define BTN_DIR_LEFT BUTTON_LEFT
53 #define BTN_DIR_RIGHT BUTTON_RIGHT
54 #define BTN_PLAYER2_DIR1 BUTTON_F2
55 #define BTN_PLAYER2_DIR2 BUTTON_F3
56 #define BTN_STARTPAUSE BUTTON_PLAY
57 #define BTN_QUIT BUTTON_OFF
58 #define BTN_STOPRESET BUTTON_ON
59 #define BTN_TOGGLE_KEYS BUTTON_F1
61 #if BUTTON_REMOTE != 0
62 #define BTN_RC_UP BUTTON_RC_VOL_UP
63 #define BTN_RC_DOWN BUTTON_RC_VOL_DOWN
64 #define REMOTE
65 #define MULTIPLAYER
66 #endif
68 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
69 #define BTN_DIR_UP BUTTON_UP
70 #define BTN_DIR_DOWN BUTTON_DOWN
71 #define BTN_DIR_LEFT BUTTON_LEFT
72 #define BTN_DIR_RIGHT BUTTON_RIGHT
73 #define BTN_PLAYER2_DIR1 BUTTON_F2
74 #define BTN_PLAYER2_DIR2 BUTTON_F3
75 #define BTN_STARTPAUSE BUTTON_SELECT
76 #define BTN_QUIT BUTTON_OFF
77 #define BTN_STOPRESET BUTTON_ON
78 #define BTN_TOGGLE_KEYS BUTTON_F1
80 #elif (CONFIG_KEYPAD == ONDIO_PAD)
81 #define BTN_DIR_UP BUTTON_UP
82 #define BTN_DIR_DOWN BUTTON_DOWN
83 #define BTN_DIR_LEFT BUTTON_LEFT
84 #define BTN_DIR_RIGHT BUTTON_RIGHT
85 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
86 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
87 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
89 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
90 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
92 #define BTN_DIR_UP BUTTON_MENU
93 #define BTN_DIR_DOWN BUTTON_PLAY
94 #define BTN_DIR_LEFT BUTTON_LEFT
95 #define BTN_DIR_RIGHT BUTTON_RIGHT
96 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
97 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
98 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
100 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_PAD)
102 #define BTN_DIR_UP BUTTON_UP
103 #define BTN_DIR_DOWN BUTTON_DOWN
104 #define BTN_DIR_LEFT BUTTON_LEFT
105 #define BTN_DIR_RIGHT BUTTON_RIGHT
106 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
107 #define BTN_QUIT BUTTON_OFF
108 #define BTN_STOPRESET BUTTON_ON
110 #define BTN_RC_QUIT BUTTON_RC_STOP
112 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
114 #define BTN_DIR_UP BUTTON_UP
115 #define BTN_DIR_DOWN BUTTON_DOWN
116 #define BTN_DIR_LEFT BUTTON_LEFT
117 #define BTN_DIR_RIGHT BUTTON_RIGHT
118 #define BTN_STARTPAUSE BUTTON_PLAY
119 #define BTN_QUIT BUTTON_POWER
120 #define BTN_STOPRESET BUTTON_REC
122 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
124 #define BTN_DIR_UP BUTTON_UP
125 #define BTN_DIR_DOWN BUTTON_DOWN
126 #define BTN_DIR_LEFT BUTTON_LEFT
127 #define BTN_DIR_RIGHT BUTTON_RIGHT
128 #define BTN_STARTPAUSE BUTTON_SELECT
129 #define BTN_QUIT BUTTON_POWER
130 #define BTN_STOPRESET BUTTON_A
132 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
133 (CONFIG_KEYPAD == SANSA_C200_PAD)
135 #define BTN_DIR_UP BUTTON_UP
136 #define BTN_DIR_DOWN BUTTON_DOWN
137 #define BTN_DIR_LEFT BUTTON_LEFT
138 #define BTN_DIR_RIGHT BUTTON_RIGHT
139 #define BTN_STARTPAUSE BUTTON_SELECT
140 #define BTN_QUIT BUTTON_POWER
141 #define BTN_STOPRESET BUTTON_REC
143 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
145 #define BTN_DIR_UP BUTTON_UP
146 #define BTN_DIR_DOWN BUTTON_DOWN
147 #define BTN_DIR_LEFT BUTTON_LEFT
148 #define BTN_DIR_RIGHT BUTTON_RIGHT
149 #define BTN_STARTPAUSE BUTTON_SELECT
150 #define BTN_QUIT BUTTON_POWER
151 #define BTN_STOPRESET BUTTON_HOME
153 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
155 #define BTN_DIR_UP BUTTON_UP
156 #define BTN_DIR_DOWN BUTTON_DOWN
157 #define BTN_DIR_LEFT BUTTON_LEFT
158 #define BTN_DIR_RIGHT BUTTON_RIGHT
159 #define BTN_STARTPAUSE BUTTON_SELECT
160 #define BTN_QUIT (BUTTON_HOME|BUTTON_REPEAT)
161 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
163 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
165 #define BTN_DIR_UP BUTTON_UP
166 #define BTN_DIR_DOWN BUTTON_DOWN
167 #define BTN_DIR_LEFT BUTTON_LEFT
168 #define BTN_DIR_RIGHT BUTTON_RIGHT
169 #define BTN_STARTPAUSE (BUTTON_SELECT | BUTTON_REL)
170 #define BTN_QUIT BUTTON_POWER
171 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
173 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
175 #define BTN_DIR_UP BUTTON_SCROLL_UP
176 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
177 #define BTN_DIR_LEFT BUTTON_LEFT
178 #define BTN_DIR_RIGHT BUTTON_RIGHT
179 #define BTN_STARTPAUSE BUTTON_PLAY
180 #define BTN_QUIT BUTTON_POWER
181 #define BTN_STOPRESET BUTTON_REW
183 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD) || \
184 (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
186 #define BTN_DIR_UP BUTTON_UP
187 #define BTN_DIR_DOWN BUTTON_DOWN
188 #define BTN_DIR_LEFT BUTTON_LEFT
189 #define BTN_DIR_RIGHT BUTTON_RIGHT
190 #define BTN_STARTPAUSE BUTTON_SELECT
191 #define BTN_QUIT BUTTON_BACK
192 #define BTN_STOPRESET BUTTON_MENU
194 #elif (CONFIG_KEYPAD == MROBE100_PAD)
196 #define BTN_DIR_UP BUTTON_UP
197 #define BTN_DIR_DOWN BUTTON_DOWN
198 #define BTN_DIR_LEFT BUTTON_LEFT
199 #define BTN_DIR_RIGHT BUTTON_RIGHT
200 #define BTN_STARTPAUSE BUTTON_SELECT
201 #define BTN_QUIT BUTTON_POWER
202 #define BTN_STOPRESET BUTTON_DISPLAY
204 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
206 #define BTN_DIR_UP BUTTON_RC_VOL_UP
207 #define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN
208 #define BTN_DIR_LEFT BUTTON_RC_REW
209 #define BTN_DIR_RIGHT BUTTON_RC_FF
210 #define BTN_STARTPAUSE BUTTON_RC_PLAY
211 #define BTN_QUIT BUTTON_RC_REC
212 #define BTN_STOPRESET BUTTON_RC_MODE
214 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
216 #define BTN_QUIT BUTTON_POWER
218 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
220 #define BTN_DIR_UP BUTTON_UP
221 #define BTN_DIR_DOWN BUTTON_DOWN
222 #define BTN_DIR_LEFT BUTTON_LEFT
223 #define BTN_DIR_RIGHT BUTTON_RIGHT
224 #define BTN_STARTPAUSE BUTTON_PLAY
225 #define BTN_QUIT BUTTON_BACK
226 #define BTN_STOPRESET BUTTON_MENU
228 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
230 #define BTN_DIR_UP BUTTON_UP
231 #define BTN_DIR_DOWN BUTTON_DOWN
232 #define BTN_DIR_LEFT BUTTON_LEFT
233 #define BTN_DIR_RIGHT BUTTON_RIGHT
234 #define BTN_STARTPAUSE BUTTON_MENU
235 #define BTN_QUIT BUTTON_POWER
236 #define BTN_STOPRESET BUTTON_VIEW
238 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
240 #define BTN_DIR_UP BUTTON_UP
241 #define BTN_DIR_DOWN BUTTON_DOWN
242 #define BTN_DIR_LEFT BUTTON_PREV
243 #define BTN_DIR_RIGHT BUTTON_NEXT
244 #define BTN_STARTPAUSE BUTTON_MENU
245 #define BTN_QUIT BUTTON_POWER
246 #define BTN_STOPRESET BUTTON_RIGHT
248 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
250 #define BTN_DIR_UP BUTTON_UP
251 #define BTN_DIR_DOWN BUTTON_DOWN
252 #define BTN_DIR_LEFT BUTTON_PREV
253 #define BTN_DIR_RIGHT BUTTON_RIGHT
254 #define BTN_STARTPAUSE BUTTON_MENU
255 #define BTN_QUIT BUTTON_POWER
256 #define BTN_STOPRESET BUTTON_RIGHT
258 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
259 (CONFIG_KEYPAD == ONDAVX777_PAD) || \
260 CONFIG_KEYPAD == MROBE500_PAD
262 #define BTN_QUIT BUTTON_POWER
264 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
266 #define BTN_DIR_UP BUTTON_UP
267 #define BTN_DIR_DOWN BUTTON_DOWN
268 #define BTN_DIR_LEFT BUTTON_LEFT
269 #define BTN_DIR_RIGHT BUTTON_RIGHT
270 #define BTN_STARTPAUSE BUTTON_PLAY
271 #define BTN_QUIT BUTTON_FFWD
272 #define BTN_STOPRESET BUTTON_REW
274 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
276 #define BTN_DIR_UP BUTTON_UP
277 #define BTN_DIR_DOWN BUTTON_DOWN
278 #define BTN_DIR_LEFT BUTTON_PREV
279 #define BTN_DIR_RIGHT BUTTON_NEXT
280 #define BTN_STARTPAUSE BUTTON_PLAY
281 #define BTN_QUIT BUTTON_REC
282 #define BTN_STOPRESET BUTTON_CANCEL
284 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
286 #define BTN_DIR_UP BUTTON_REW
287 #define BTN_DIR_DOWN BUTTON_FF
288 #define BTN_DIR_LEFT BUTTON_VOL_DOWN
289 #define BTN_DIR_RIGHT BUTTON_VOL_UP
290 #define BTN_STARTPAUSE BUTTON_FUNC
291 #define BTN_QUIT (BUTTON_REC|BUTTON_PLAY)
292 #define BTN_STOPRESET (BUTTON_FUNC|BUTTON_REPEAT)
294 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
296 #define BTN_DIR_UP BUTTON_UP
297 #define BTN_DIR_DOWN BUTTON_DOWN
298 #define BTN_DIR_LEFT BUTTON_REW
299 #define BTN_DIR_RIGHT BUTTON_FF
300 #define BTN_STARTPAUSE BUTTON_PLAY
301 #define BTN_QUIT (BUTTON_MENU | BUTTON_REPEAT)
302 #define BTN_STOPRESET (BUTTON_PLAY | BUTTON_REPEAT)
304 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
306 #define BTN_DIR_UP BUTTON_UP
307 #define BTN_DIR_DOWN BUTTON_DOWN
308 #define BTN_DIR_LEFT BUTTON_LEFT
309 #define BTN_DIR_RIGHT BUTTON_RIGHT
310 #define BTN_STARTPAUSE BUTTON_PLAYPAUSE
311 #define BTN_QUIT BUTTON_POWER
312 #define BTN_STOPRESET BUTTON_BACK
314 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
316 #define BTN_DIR_UP BUTTON_UP
317 #define BTN_DIR_DOWN BUTTON_DOWN
318 #define BTN_DIR_LEFT BUTTON_LEFT
319 #define BTN_DIR_RIGHT BUTTON_RIGHT
320 #define BTN_STARTPAUSE BUTTON_SELECT
321 #define BTN_QUIT BUTTON_POWER
322 #define BTN_STOPRESET BUTTON_VOL_DOWN
324 #elif CONFIG_KEYPAD == HM60X_PAD
326 #define BTN_DIR_UP BUTTON_UP
327 #define BTN_DIR_DOWN BUTTON_DOWN
328 #define BTN_DIR_LEFT BUTTON_LEFT
329 #define BTN_DIR_RIGHT BUTTON_RIGHT
330 #define BTN_STARTPAUSE BUTTON_SELECT
331 #define BTN_QUIT BUTTON_POWER
332 #define BTN_STOPRESET (BUTTON_POWER|BUTTON_SELECT)
334 #elif CONFIG_KEYPAD == HM801_PAD
336 #define BTN_DIR_UP BUTTON_UP
337 #define BTN_DIR_DOWN BUTTON_DOWN
338 #define BTN_DIR_LEFT BUTTON_LEFT
339 #define BTN_DIR_RIGHT BUTTON_RIGHT
340 #define BTN_STARTPAUSE BUTTON_SELECT
341 #define BTN_QUIT BUTTON_POWER
342 #define BTN_STOPRESET BUTTON_PLAY
344 #else
345 #error No keymap defined!
346 #endif
348 #ifdef HAVE_TOUCHSCREEN
349 #ifndef BTN_DIR_UP
350 #define BTN_DIR_UP BUTTON_TOPMIDDLE
351 #endif
352 #ifndef BTN_DIR_DOWN
353 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
354 #endif
355 #ifndef BTN_DIR_LEFT
356 #define BTN_DIR_LEFT BUTTON_MIDLEFT
357 #endif
358 #ifndef BTN_DIR_RIGHT
359 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
360 #endif
361 #ifndef BTN_STARTPAUSE
362 #define BTN_STARTPAUSE BUTTON_CENTER
363 #endif
364 #ifndef BTN_QUIT
365 #define BTN_QUIT BUTTON_TOPLEFT
366 #endif
367 #ifndef BTN_STOPRESET
368 #define BTN_STOPRESET BUTTON_TOPRIGHT
369 #endif
370 #endif
372 #if (LCD_WIDTH == 96) && (LCD_HEIGHT == 96)
373 #define FOOD_SIZE 3
374 #define ARGH_SIZE 4
375 #define SPEED 14
376 #define MAX_WORM_SEGMENTS 128
377 #elif (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
378 #define FOOD_SIZE 3
379 #define ARGH_SIZE 4
380 #define SPEED 14
381 #define MAX_WORM_SEGMENTS 128
382 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
383 #define FOOD_SIZE 3
384 #define ARGH_SIZE 4
385 #define SPEED 14
386 #define MAX_WORM_SEGMENTS 128
387 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
388 #define FOOD_SIZE 3
389 #define ARGH_SIZE 4
390 #define SPEED 14
391 #define MAX_WORM_SEGMENTS 128
392 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
393 #define FOOD_SIZE 3
394 #define ARGH_SIZE 4
395 #define SPEED 12
396 #define MAX_WORM_SEGMENTS 128
397 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
398 #define FOOD_SIZE 4
399 #define ARGH_SIZE 5
400 #define SPEED 10
401 #define MAX_WORM_SEGMENTS 128
402 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
403 #define FOOD_SIZE 4
404 #define ARGH_SIZE 5
405 #define SPEED 9
406 #define MAX_WORM_SEGMENTS 128
407 #elif ((LCD_WIDTH == 160) && (LCD_HEIGHT == 128)) || \
408 ((LCD_WIDTH == 128) && (LCD_HEIGHT == 160))
409 #define FOOD_SIZE 4
410 #define ARGH_SIZE 5
411 #define SPEED 8
412 #define MAX_WORM_SEGMENTS 256
413 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
414 #define FOOD_SIZE 4
415 #define ARGH_SIZE 5
416 #define SPEED 6
417 #define MAX_WORM_SEGMENTS 256
418 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
419 #define FOOD_SIZE 5
420 #define ARGH_SIZE 6
421 #define SPEED 4
422 #define MAX_WORM_SEGMENTS 512
423 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
424 #define FOOD_SIZE 5
425 #define ARGH_SIZE 6
426 #define SPEED 4
427 #define MAX_WORM_SEGMENTS 512
428 #elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
429 ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
430 #define FOOD_SIZE 7
431 #define ARGH_SIZE 8
432 #define SPEED 4
433 #define MAX_WORM_SEGMENTS 512
434 #elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
435 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
436 #define FOOD_SIZE 14
437 #define ARGH_SIZE 16
438 #define SPEED 4
439 #define MAX_WORM_SEGMENTS 512
440 #endif
442 #ifdef HAVE_LCD_COLOR
443 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
444 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
445 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
446 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
447 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
448 #endif
450 #define CHECK_SQUARE_COLLISION(x1,y1,s1,x2,y2,s2) (x1+s1>x2)&&(x2+s2>x1)&&(y1+s1>y2)&&(y2+s2>y1)
453 * All the properties that a worm has.
455 static struct worm {
456 /* The worm is stored in a ring of xy coordinates */
457 int x[MAX_WORM_SEGMENTS];
458 int y[MAX_WORM_SEGMENTS];
460 int head; /* index of the head within the buffer */
461 int tail; /* index of the tail within the buffer */
462 int growing; /* number of cyles the worm still keeps growing */
463 bool alive; /* the worms living state */
465 /* direction vector in which the worm moves */
466 int dirx; /* only values -1 0 1 allowed */
467 int diry; /* only values -1 0 1 allowed */
469 /* this method is used to fetch the direction the user
470 has selected. It can be one of the values
471 human_player1, human_player2, remote_player, virtual_player.
472 All these values are fuctions, that can change the direction
473 of the worm */
474 void (*fetch_worm_direction)(struct worm *w);
475 } worms[MAX_WORMS];
477 /* stores the highscore - besides it was scored by a virtual player */
478 static int highscore;
480 #define MAX_FOOD 5 /* maximal number of food items */
482 /* The arrays store the food coordinates */
483 static int foodx[MAX_FOOD];
484 static int foody[MAX_FOOD];
486 #define MAX_ARGH 100 /* maximal number of argh items */
487 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
489 /* The arrays store the argh coordinates */
490 static int arghx[MAX_ARGH];
491 static int arghy[MAX_ARGH];
493 /* the number of arghs that are currently in use */
494 static int argh_count;
496 /* the number of arghs per food, settable by user */
497 static int arghs_per_food = ARGHS_PER_FOOD;
498 /* the size of the argh, settable by user */
499 static int argh_size = ARGH_SIZE;
500 /* the size of the food, settable by user */
501 static int food_size = FOOD_SIZE;
502 /* the speed of the worm, settable by user */
503 static int speed = SPEED;
504 /* the amount a worm grows by eating a food, settable by user */
505 static int worm_food = WORM_PER_FOOD;
507 /* End additional variables */
509 /* the number of active worms (dead or alive) */
510 static int worm_count = MAX_WORMS;
512 /* in multiplayer mode: en- / disables the remote worm control
513 in singleplayer mode: toggles 4 / 2 button worm control */
514 static bool use_remote = false;
516 /* return values of check_collision */
517 #define COLLISION_NONE 0
518 #define COLLISION_WORM 1
519 #define COLLISION_FOOD 2
520 #define COLLISION_ARGH 3
521 #define COLLISION_FIELD 4
523 static const char *const state_desc[] = {
524 [COLLISION_NONE] = NULL,
525 [COLLISION_WORM] = "Wormed",
526 [COLLISION_FOOD] = "Growing",
527 [COLLISION_ARGH] = "Argh",
528 [COLLISION_FIELD] = "Crashed",
531 /* constants for use as directions.
532 Note that the values are ordered clockwise.
533 Thus increasing / decreasing the values
534 is equivalent to right / left turns. */
535 #define WEST 0
536 #define NORTH 1
537 #define EAST 2
538 #define SOUTH 3
540 /* direction of human player 1 */
541 static int player1_dir = EAST;
542 /* direction of human player 2 */
543 static int player2_dir = EAST;
544 /* direction of human player 3 */
545 static int player3_dir = EAST;
547 /* the number of (human) players that currently
548 control a worm */
549 static int players = 1;
551 #define SETTINGS_VERSION 1
552 #define SETTINGS_MIN_VERSION 1
553 #define SETTINGS_FILENAME "wormlet.cfg"
555 static struct configdata config[] =
557 {TYPE_INT, 0, 1024, { .int_p = &highscore }, "highscore", NULL},
558 {TYPE_INT, 0, 15, { .int_p = &arghs_per_food }, "arghs per food", NULL},
559 {TYPE_INT, 0, 15, { .int_p = &argh_size }, "argh size", NULL},
560 {TYPE_INT, 0, 15, { .int_p = &food_size }, "food size", NULL},
561 {TYPE_INT, 0, 3, { .int_p = &players }, "players", NULL},
562 {TYPE_INT, 0, 3, { .int_p = &worm_count }, "worms", NULL},
563 {TYPE_INT, 0, 20, { .int_p = &speed }, "speed", NULL},
564 {TYPE_INT, 0, 15, { .int_p = &worm_food }, "Worm Growth Per Food", NULL}
568 * Returns the direction id in which the worm
569 * currently is creeping.
570 * @param struct worm *w The worm that is to be investigated.
571 * w Must not be null.
572 * @return int A value 0 <= value < 4
573 * Note the predefined constants NORTH, SOUTH, EAST, WEST
575 static int get_worm_dir(struct worm *w)
577 int retVal ;
578 if (w->dirx == 0) {
579 if (w->diry == 1) {
580 retVal = SOUTH;
581 } else {
582 retVal = NORTH;
584 } else {
585 if (w->dirx == 1) {
586 retVal = EAST;
587 } else {
588 retVal = WEST;
591 return retVal;
595 * Set the direction of the specified worm with a direction id.
596 * Increasing the value by 1 means to turn the worm direction
597 * to right by 90 degree.
598 * @param struct worm *w The worm that is to be altered. w Must not be null.
599 * @param int dir The new direction in which the worm is to creep.
600 * dir must be 0 <= dir < 4. Use predefined constants
601 * NORTH, SOUTH, EAST, WEST
603 static void set_worm_dir(struct worm *w, int dir)
605 switch (dir) {
606 case WEST:
607 w->dirx = -1;
608 w->diry = 0;
609 break;
610 case NORTH:
611 w->dirx = 0;
612 w->diry = - 1;
613 break;
614 case EAST:
615 w->dirx = 1;
616 w->diry = 0;
617 break;
618 case SOUTH:
619 w->dirx = 0;
620 w->diry = 1;
621 break;
626 * Returns the current length of the worm array. This
627 * is also a value for the number of bends that are in the worm.
628 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
630 static int get_worm_array_length(struct worm *w)
632 /* initial simple calculation will be overwritten if wrong. */
633 int retVal = w->head - w->tail;
635 /* if the worm 'crosses' the boundaries of the ringbuffer */
636 if (retVal < 0) {
637 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
640 return retVal;
644 * Returns the score the specified worm. The score is the length
645 * of the worm.
646 * @param struct worm *w The worm that is to be investigated.
647 * w must not be null.
648 * @return int The length of the worm (>= 0).
650 static int get_score(struct worm *w)
652 int retval = 0;
653 int length = get_worm_array_length(w);
654 int i;
655 for (i = 0; i < length; i++) {
657 /* The iteration iterates the length of the worm.
658 Here's the conversion to the true indices within the worm arrays. */
659 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
660 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
661 int startx = w->x[linestart];
662 int starty = w->y[linestart];
663 int endx = w->x[lineend];
664 int endy = w->y[lineend];
666 int minimum, maximum;
668 if (startx == endx) {
669 minimum = MIN(starty, endy);
670 maximum = MAX(starty, endy);
671 } else {
672 minimum = MIN(startx, endx);
673 maximum = MAX(startx, endx);
675 retval += abs(maximum - minimum);
677 return retval;
681 * Determines wether the line specified by startx, starty, endx, endy intersects
682 * the rectangle specified by x, y, width, height. Note that the line must be exactly
683 * horizontal or vertical (startx == endx or starty == endy).
684 * @param int startx The x coordinate of the start point of the line.
685 * @param int starty The y coordinate of the start point of the line.
686 * @param int endx The x coordinate of the end point of the line.
687 * @param int endy The y coordinate of the end point of the line.
688 * @param int x The x coordinate of the top left corner of the rectangle.
689 * @param int y The y coordinate of the top left corner of the rectangle.
690 * @param int width The width of the rectangle.
691 * @param int height The height of the rectangle.
692 * @return bool Returns true if the specified line intersects with the recangle.
694 static bool line_in_rect(int startx, int starty, int endx, int endy,
695 int x, int y, int width, int height)
697 bool retval = false;
698 int simple, simplemin, simplemax;
699 int compa, compb, compmin, compmax;
700 int temp;
701 if (startx == endx) {
702 simple = startx;
703 simplemin = x;
704 simplemax = x + width;
706 compa = starty;
707 compb = endy;
708 compmin = y;
709 compmax = y + height;
710 } else {
711 simple = starty;
712 simplemin = y;
713 simplemax = y + height;
715 compa = startx;
716 compb = endx;
717 compmin = x;
718 compmax = x + width;
721 temp = compa;
722 compa = MIN(compa, compb);
723 compb = MAX(temp, compb);
725 if (simplemin <= simple && simple <= simplemax) {
726 if ((compmin <= compa && compa <= compmax) ||
727 (compmin <= compb && compb <= compmax) ||
728 (compa <= compmin && compb >= compmax)) {
729 retval = true;
732 return retval;
736 * Tests wether the specified worm intersects with the rect.
737 * @param struct worm *w The worm to be investigated
738 * @param int x The x coordinate of the top left corner of the rect
739 * @param int y The y coordinate of the top left corner of the rect
740 * @param int widht The width of the rect
741 * @param int height The height of the rect
742 * @return bool Returns true if the worm intersects with the rect
744 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height)
746 bool retval = false;
749 /* get_worm_array_length is expensive -> buffer the value */
750 int wormLength = get_worm_array_length(w);
751 int i;
753 /* test each entry that is part of the worm */
754 for (i = 0; i < wormLength && retval == false; i++) {
756 /* The iteration iterates the length of the worm.
757 Here's the conversion to the true indices within the worm arrays. */
758 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
759 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
760 int startx = w->x[linestart];
761 int starty = w->y[linestart];
762 int endx = w->x[lineend];
763 int endy = w->y[lineend];
765 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
768 return retval;
772 * Checks wether a specific food in the food arrays is at the
773 * specified coordinates.
774 * @param int foodIndex The index of the food in the food arrays
775 * @param int x the x coordinate.
776 * @param int y the y coordinate.
777 * @return Returns true if the coordinate hits the food specified by
778 * foodIndex.
780 static bool specific_food_collision(int foodIndex, int x, int y)
782 bool retVal = false;
783 if (x >= foodx[foodIndex] &&
784 x < foodx[foodIndex] + food_size &&
785 y >= foody[foodIndex] &&
786 y < foody[foodIndex] + food_size) {
788 retVal = true;
790 return retVal;
794 * Returns the index of the food that is at the
795 * given coordinates. If no food is at the coordinates
796 * -1 is returned.
797 * @return int -1 <= value < MAX_FOOD
799 static int food_collision(int x, int y)
801 int i = 0;
802 int retVal = -1;
803 for (i = 0; i < MAX_FOOD; i++) {
804 if (specific_food_collision(i, x, y)) {
805 retVal = i;
806 break;
809 return retVal;
813 * Checks wether a specific argh in the argh arrays is at the
814 * specified coordinates.
815 * @param int arghIndex The index of the argh in the argh arrays
816 * @param int x the x coordinate.
817 * @param int y the y coordinate.
818 * @return Returns true if the coordinate hits the argh specified by
819 * arghIndex.
821 static bool specific_argh_collision(int arghIndex, int x, int y)
823 if ( x >= arghx[arghIndex] &&
824 y >= arghy[arghIndex] &&
825 x < arghx[arghIndex] + argh_size &&
826 y < arghy[arghIndex] + argh_size )
828 return true;
831 return false;
835 * Returns the index of the argh that is at the
836 * given coordinates. If no argh is at the coordinates
837 * -1 is returned.
838 * @param int x The x coordinate.
839 * @param int y The y coordinate.
840 * @return int -1 <= value < argh_count <= MAX_ARGH
842 static int argh_collision(int x, int y)
844 int i = 0;
845 int retVal = -1;
847 /* search for the argh that has the specified coords */
848 for (i = 0; i < argh_count; i++) {
849 if (specific_argh_collision(i, x, y)) {
850 retVal = i;
851 break;
854 return retVal;
858 * Checks wether the worm collides with the food at the specfied food-arrays.
859 * @param int foodIndex The index of the food in the arrays. Ensure the value is
860 * 0 <= foodIndex <= MAX_FOOD
861 * @return Returns true if the worm collides with the specified food.
863 static bool worm_food_collision(struct worm *w, int foodIndex)
865 bool retVal = false;
867 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
868 food_size - 1, food_size - 1);
870 return retVal;
874 * Returns true if the worm hits the argh within the next moves (unless
875 * the worm changes it's direction).
876 * @param struct worm *w - The worm to investigate
877 * @param int argh_idx - The index of the argh
878 * @param int moves - The number of moves that are considered.
879 * @return Returns false if the specified argh is not hit within the next
880 * moves.
882 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves)
884 bool retVal = false;
885 int x1, y1, x2, y2;
886 x1 = w->x[w->head];
887 y1 = w->y[w->head];
889 x2 = w->x[w->head] + moves * w->dirx;
890 y2 = w->y[w->head] + moves * w->diry;
892 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
893 argh_size, argh_size);
894 return retVal;
898 * Checks wether the worm collides with the argh at the specfied argh-arrays.
899 * @param int arghIndex The index of the argh in the arrays.
900 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
901 * @return Returns true if the worm collides with the specified argh.
903 static bool worm_argh_collision(struct worm *w, int arghIndex)
905 bool retVal = false;
907 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
908 argh_size - 1, argh_size - 1);
910 return retVal;
914 * Find new coordinates for the food stored in foodx[index], foody[index]
915 * that don't collide with any other food or argh
916 * @param int index
917 * Ensure that 0 <= index < MAX_FOOD.
919 static void make_food(int index)
921 int x = 0;
922 int y = 0;
923 bool collisionDetected = false;
924 int i;
926 do {
927 /* make coordinates for a new food so that
928 the entire food lies within the FIELD */
929 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
930 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
931 collisionDetected = false;
932 /* Ensure that the new food doesn't collide with any
933 existing foods or arghs.
934 If the new food hit any existing
935 argh or food a collision is detected.
938 for (i=0; i<MAX_FOOD && !collisionDetected; i++) {
939 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,foodx[i],foody[i],food_size);
941 for (i=0; i<argh_count && !collisionDetected; i++) {
942 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,arghx[i],arghy[i],argh_size);
945 /* use coordinates for further testing */
946 foodx[index] = x;
947 foody[index] = y;
949 /* now test wether we accidently hit the worm with food ;) */
950 i = 0;
951 for (i = 0; i < worm_count && !collisionDetected; i++) {
953 collisionDetected = worm_food_collision(&worms[i], index);
957 while (collisionDetected);
958 return;
962 * Clears a food from the lcd buffer.
963 * @param int index The index of the food arrays under which
964 * the coordinates of the desired food can be found. Ensure
965 * that the value is 0 <= index <= MAX_FOOD.
967 static void clear_food(int index)
969 /* remove the old food from the screen */
970 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
971 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
972 foody[index] + FIELD_RECT_Y,
973 food_size, food_size);
974 rb->lcd_set_drawmode(DRMODE_SOLID);
978 * Draws a food in the lcd buffer.
979 * @param int index The index of the food arrays under which
980 * the coordinates of the desired food can be found. Ensure
981 * that the value is 0 <= index <= MAX_FOOD.
983 static void draw_food(int index)
985 /* draw the food object */
986 #ifdef HAVE_LCD_COLOR
987 rb->lcd_set_foreground(COLOR_FOOD);
988 #endif
989 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
990 foody[index] + FIELD_RECT_Y,
991 food_size, food_size);
992 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
993 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
994 foody[index] + FIELD_RECT_Y + 1,
995 food_size - 2, food_size - 2);
996 rb->lcd_set_drawmode(DRMODE_SOLID);
997 #ifdef HAVE_LCD_COLOR
998 rb->lcd_set_foreground(COLOR_FG);
999 #endif
1003 * Find new coordinates for the argh stored in arghx[index], arghy[index]
1004 * that don't collide with any other food or argh.
1005 * @param int index
1006 * Ensure that 0 <= index < argh_count < MAX_ARGH.
1008 static void make_argh(int index)
1010 int x = -1;
1011 int y = -1;
1012 bool collisionDetected = false;
1013 int i;
1015 do {
1016 /* make coordinates for a new argh so that
1017 the entire food lies within the FIELD */
1018 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
1019 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
1020 collisionDetected = false;
1021 /* Ensure that the new argh doesn't intersect with any
1022 existing foods or arghs.
1023 If the new argh hit any existing
1024 argh or food an intersection is detected.
1027 for (i=0; i<MAX_FOOD && !collisionDetected; i++) {
1028 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,foodx[i],foody[i],food_size);
1030 for (i=0; i<argh_count && !collisionDetected; i++) {
1031 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,arghx[i],arghy[i],argh_size);
1034 /* use the candidate coordinates to make a real argh */
1035 arghx[index] = x;
1036 arghy[index] = y;
1038 /* now test wether we accidently hit the worm with argh ;) */
1039 for (i = 0; i < worm_count && !collisionDetected; i++) {
1040 collisionDetected |= worm_argh_collision(&worms[i], index);
1041 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
1042 MIN_ARGH_DIST);
1045 while (collisionDetected);
1046 return;
1050 * Draws an argh in the lcd buffer.
1051 * @param int index The index of the argh arrays under which
1052 * the coordinates of the desired argh can be found. Ensure
1053 * that the value is 0 <= index < argh_count <= MAX_ARGH.
1055 static void draw_argh(int index)
1057 /* draw the new argh */
1058 #ifdef HAVE_LCD_COLOR
1059 rb->lcd_set_foreground(COLOR_ARGH);
1060 #endif
1061 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
1062 arghy[index] + FIELD_RECT_Y,
1063 argh_size, argh_size);
1064 #ifdef HAVE_LCD_COLOR
1065 rb->lcd_set_foreground(COLOR_FG);
1066 #endif
1069 static void virtual_player(struct worm *w);
1071 * Initialzes the specified worm with INITIAL_WORM_LENGTH
1072 * and the tail at the specified position. The worm will
1073 * be initialized alive and creeping EAST.
1074 * @param struct worm *w The worm that is to be initialized
1075 * @param int x The x coordinate at which the tail of the worm starts.
1076 * x must be 0 <= x < FIELD_RECT_WIDTH.
1077 * @param int y The y coordinate at which the tail of the worm starts
1078 * y must be 0 <= y < FIELD_RECT_WIDTH.
1080 static void init_worm(struct worm *w, int x, int y)
1082 /* initialize the worm size */
1083 w->head = 1;
1084 w->tail = 0;
1086 w->x[w->head] = x + 1;
1087 w->y[w->head] = y;
1089 w->x[w->tail] = x;
1090 w->y[w->tail] = y;
1092 /* set the initial direction the worm creeps to */
1093 w->dirx = 1;
1094 w->diry = 0;
1096 w->growing = INITIAL_WORM_LENGTH - 1;
1097 w->alive = true;
1098 w->fetch_worm_direction = virtual_player;
1102 * Writes the direction that was stored for
1103 * human player 1 into the specified worm. This function
1104 * may be used to be stored in worm.fetch_worm_direction.
1105 * The value of
1106 * the direction is read from player1_dir.
1107 * @param struct worm *w - The worm of which the direction
1108 * is altered.
1110 static void human_player1(struct worm *w) {
1111 set_worm_dir(w, player1_dir);
1115 * Writes the direction that was stored for
1116 * human player 2 into the specified worm. This function
1117 * may be used to be stored in worm.fetch_worm_direction.
1118 * The value of
1119 * the direction is read from player2_dir.
1120 * @param struct worm *w - The worm of which the direction
1121 * is altered.
1123 static void human_player2(struct worm *w) {
1124 set_worm_dir(w, player2_dir);
1128 * Writes the direction that was stored for
1129 * human player using a remote control
1130 * into the specified worm. This function
1131 * may be used to be stored in worm.fetch_worm_direction.
1132 * The value of
1133 * the direction is read from player3_dir.
1134 * @param struct worm *w - The worm of which the direction
1135 * is altered.
1137 static void remote_player(struct worm *w) {
1138 set_worm_dir(w, player3_dir);
1142 * Initializes the worm-, food- and argh-arrays, draws a frame,
1143 * makes some food and argh and display all that stuff.
1145 static void init_wormlet(void)
1147 int i;
1149 for (i = 0; i< worm_count; i++) {
1150 /* Initialize all the worm coordinates to center. */
1151 int x = (int)(FIELD_RECT_WIDTH / 2);
1152 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
1154 init_worm(&worms[i], x, y);
1157 player1_dir = EAST;
1158 player2_dir = EAST;
1159 player3_dir = EAST;
1161 if (players > 0) {
1162 worms[0].fetch_worm_direction = human_player1;
1165 if (players > 1) {
1166 if (use_remote) {
1167 worms[1].fetch_worm_direction = remote_player;
1168 } else {
1169 worms[1].fetch_worm_direction = human_player2;
1173 if (players > 2) {
1174 worms[2].fetch_worm_direction = human_player2;
1177 /* Needed when the game is restarted using BTN_STOPRESET */
1178 rb->lcd_clear_display();
1180 /* make and display some food and argh */
1181 argh_count = MAX_FOOD;
1182 for (i = 0; i < MAX_FOOD; i++) {
1183 make_food(i);
1184 draw_food(i);
1185 make_argh(i);
1186 draw_argh(i);
1189 /* draw the game field */
1190 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1191 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
1192 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
1193 rb->lcd_set_drawmode(DRMODE_SOLID);
1195 /* make everything visible */
1196 rb->lcd_update();
1201 * Move the worm one step further if it is alive.
1202 * The direction in which the worm moves is taken from dirx and diry.
1203 * move_worm decreases growing if > 0. While the worm is growing the tail
1204 * is left untouched.
1205 * @param struct worm *w The worm to move. w must not be NULL.
1207 static void move_worm(struct worm *w)
1209 if (w->alive) {
1210 /* determine the head point and its precessor */
1211 int headx = w->x[w->head];
1212 int heady = w->y[w->head];
1213 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
1214 int preheadx = w->x[prehead];
1215 int preheady = w->y[prehead];
1217 /* determine the old direction */
1218 int olddirx;
1219 int olddiry;
1220 if (headx == preheadx) {
1221 olddirx = 0;
1222 olddiry = (heady > preheady) ? 1 : -1;
1223 } else {
1224 olddiry = 0;
1225 olddirx = (headx > preheadx) ? 1 : -1;
1228 /* olddir == dir?
1229 a change of direction means a new segment
1230 has been opened */
1231 if (olddirx != w->dirx ||
1232 olddiry != w->diry) {
1233 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1236 /* new head position */
1237 w->x[w->head] = headx + w->dirx;
1238 w->y[w->head] = heady + w->diry;
1241 /* while the worm is growing no tail procession is necessary */
1242 if (w->growing > 0) {
1243 /* update the worms grow state */
1244 w->growing--;
1247 /* if the worm isn't growing the tail has to be dragged */
1248 else {
1249 /* index of the end of the tail segment */
1250 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1252 /* drag the end of the tail */
1253 /* only one coordinate has to be altered. Here it is
1254 determined which one */
1255 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1256 if (w->x[w->tail] == w->x[tail_segment_end]) {
1257 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1258 w->y[w->tail] += dir;
1259 } else {
1260 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1261 w->x[w->tail] += dir;
1264 /* when the tail has been dragged so far that it meets
1265 the next segment start the tail segment is obsolete and
1266 must be freed */
1267 if (w->x[w->tail] == w->x[tail_segment_end] &&
1268 w->y[w->tail] == w->y[tail_segment_end]){
1270 /* drop the last tail point */
1271 w->tail = tail_segment_end;
1278 * Draws the head and clears the tail of the worm in
1279 * the display buffer. lcd_update() is NOT called thus
1280 * the caller has to take care that the buffer is displayed.
1282 static void draw_worm(struct worm *w)
1284 /* draw the new head */
1285 int x = w->x[w->head];
1286 int y = w->y[w->head];
1287 #ifdef HAVE_LCD_COLOR
1288 rb->lcd_set_foreground(COLOR_WORM);
1289 #endif
1290 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1291 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1294 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1296 /* clear the space behind the worm */
1297 x = w->x[w->tail] ;
1298 y = w->y[w->tail] ;
1299 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1300 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1302 rb->lcd_set_drawmode(DRMODE_SOLID);
1303 #ifdef HAVE_LCD_COLOR
1304 rb->lcd_set_foreground(COLOR_FG);
1305 #endif
1309 * Checks wether the coordinate is part of the worm. Returns
1310 * true if any part of the worm was hit - including the head.
1311 * @param x int The x coordinate
1312 * @param y int The y coordinate
1313 * @return int The index of the worm arrays that contain x, y.
1314 * Returns -1 if the coordinates are not part of the worm.
1316 static int specific_worm_collision(struct worm *w, int x, int y)
1318 int retVal = -1;
1320 /* get_worm_array_length is expensive -> buffer the value */
1321 int wormLength = get_worm_array_length(w);
1322 int i;
1324 /* test each entry that is part of the worm */
1325 for (i = 0; i < wormLength && retVal == -1; i++) {
1327 /* The iteration iterates the length of the worm.
1328 Here's the conversion to the true indices within the worm arrays. */
1329 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1330 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1331 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1332 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1333 if (samex || samey){
1334 int test, min, max, tmp;
1336 if (samey) {
1337 min = w->x[linestart];
1338 max = w->x[lineend];
1339 test = x;
1340 } else {
1341 min = w->y[linestart];
1342 max = w->y[lineend];
1343 test = y;
1346 tmp = min;
1347 min = MIN(min, max);
1348 max = MAX(tmp, max);
1350 if (min <= test && test <= max) {
1351 retVal = lineend;
1355 return retVal;
1359 * Increases the length of the specified worm by marking
1360 * that it may grow by len pixels. Note that the worm has
1361 * to move to make the growing happen.
1362 * @param worm *w The worm that is to be altered.
1363 * @param int len A positive value specifying the amount of
1364 * pixels the worm may grow.
1366 static void add_growing(struct worm *w, int len) {
1367 w->growing += len;
1371 * Determins the worm that is at the coordinates x, y. The parameter
1372 * w is a switch parameter that changes the functionality of worm_collision.
1373 * If w is specified and x,y hits the head of w NULL is returned.
1374 * This is a useful way to determine wether the head of w hits
1375 * any worm but including itself but excluding its own head.
1376 * (It hits always its own head ;))
1377 * If w is set to NULL worm_collision returns any worm including all heads
1378 * that is at position of x,y.
1379 * @param struct worm *w The worm of which the head should be excluded in
1380 * the test. w may be set to NULL.
1381 * @param int x The x coordinate that is checked
1382 * @param int y The y coordinate that is checkec
1383 * @return struct worm* The worm that has been hit by x,y. If no worm
1384 * was at the position NULL is returned.
1386 static struct worm* worm_collision(struct worm *w, int x, int y)
1388 struct worm *retVal = NULL;
1389 int i;
1390 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1391 int collision_at = specific_worm_collision(&worms[i], x, y);
1392 if (collision_at != -1) {
1393 if (!(w == &worms[i] && collision_at == w->head)){
1394 retVal = &worms[i];
1398 return retVal;
1402 * Returns true if the head of the worm just has
1403 * crossed the field boundaries.
1404 * @return bool true if the worm just has wrapped.
1406 static bool field_collision(struct worm *w)
1408 bool retVal = false;
1409 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1410 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1411 (w->x[w->head] < 0) ||
1412 (w->y[w->head] < 0))
1414 retVal = true;
1416 return retVal;
1421 * Returns true if the specified coordinates are within the
1422 * field specified by the FIELD_RECT_XXX constants.
1423 * @param int x The x coordinate of the point that is investigated
1424 * @param int y The y coordinate of the point that is investigated
1425 * @return bool Returns false if x,y specifies a point outside the
1426 * field of worms.
1428 static bool is_in_field_rect(int x, int y)
1430 bool retVal = false;
1431 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1432 y >= 0 && y < FIELD_RECT_HEIGHT);
1433 return retVal;
1437 * Checks and returns wether the head of the w
1438 * is colliding with something currently.
1439 * @return int One of the values:
1440 * COLLISION_NONE
1441 * COLLISION_w
1442 * COLLISION_FOOD
1443 * COLLISION_ARGH
1444 * COLLISION_FIELD
1446 static int check_collision(struct worm *w)
1448 int retVal = COLLISION_NONE;
1450 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1451 retVal = COLLISION_WORM;
1453 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1454 retVal = COLLISION_FOOD;
1456 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1457 retVal = COLLISION_ARGH;
1459 if (field_collision(w))
1460 retVal = COLLISION_FIELD;
1462 return retVal;
1466 * Returns the index of the food that is closest to the point
1467 * specified by x, y. This index may be used in the foodx and
1468 * foody arrays.
1469 * @param int x The x coordinate of the point
1470 * @param int y The y coordinate of the point
1471 * @return int A value usable as index in foodx and foody.
1473 static int get_nearest_food(int x, int y)
1475 int nearestfood = 0;
1476 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1477 int deltax = 0;
1478 int deltay = 0;
1479 int foodindex;
1480 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1481 int distance;
1482 deltax = foodx[foodindex] - x;
1483 deltay = foody[foodindex] - y;
1484 deltax = deltax > 0 ? deltax : deltax * (-1);
1485 deltay = deltay > 0 ? deltay : deltay * (-1);
1486 distance = deltax + deltay;
1488 if (distance < olddistance) {
1489 olddistance = distance;
1490 nearestfood = foodindex;
1493 return nearestfood;
1497 * Returns wether the specified position is next to the worm
1498 * and in the direction the worm looks. Use this method to
1499 * test wether this position would be hit with the next move of
1500 * the worm unless the worm changes its direction.
1501 * @param struct worm *w - The worm to be investigated
1502 * @param int x - The x coordinate of the position to test.
1503 * @param int y - The y coordinate of the position to test.
1504 * @return Returns true if the worm will hit the position unless
1505 * it change its direction before the next move.
1507 static bool is_in_front_of_worm(struct worm *w, int x, int y)
1509 bool infront = false;
1510 int deltax = x - w->x[w->head];
1511 int deltay = y - w->y[w->head];
1513 if (w->dirx == 0) {
1514 infront = (w->diry * deltay) > 0;
1515 } else {
1516 infront = (w->dirx * deltax) > 0;
1518 return infront;
1522 * Returns true if the worm will collide with the next move unless
1523 * it changes its direction.
1524 * @param struct worm *w - The worm to be investigated.
1525 * @return Returns true if the worm will collide with the next move
1526 * unless it changes its direction.
1528 static bool will_worm_collide(struct worm *w)
1530 int x = w->x[w->head] + w->dirx;
1531 int y = w->y[w->head] + w->diry;
1532 bool retVal = !is_in_field_rect(x, y);
1533 if (!retVal) {
1534 retVal = (argh_collision(x, y) != -1);
1537 if (!retVal) {
1538 retVal = (worm_collision(w, x, y) != NULL);
1540 return retVal;
1544 * This function
1545 * may be used to be stored in worm.fetch_worm_direction for
1546 * worms that are not controlled by humans but by artificial stupidity.
1547 * A direction is searched that doesn't lead to collision but to the nearest
1548 * food - but not very intelligent. The direction is written to the specified
1549 * worm.
1550 * @param struct worm *w - The worm of which the direction
1551 * is altered.
1553 static void virtual_player(struct worm *w)
1555 bool isright;
1556 int plana, planb, planc;
1557 /* find the next lunch */
1558 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1560 /* determine in which direction it is */
1562 /* in front of me? */
1563 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1565 /* left right of me? */
1566 int olddir = get_worm_dir(w);
1567 set_worm_dir(w, (olddir + 1) % 4);
1568 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1569 set_worm_dir(w, olddir);
1571 /* detect situation, set strategy */
1572 if (infront) {
1573 if (isright) {
1574 plana = olddir;
1575 planb = (olddir + 1) % 4;
1576 planc = (olddir + 3) % 4;
1577 } else {
1578 plana = olddir;
1579 planb = (olddir + 3) % 4;
1580 planc = (olddir + 1) % 4;
1582 } else {
1583 if (isright) {
1584 plana = (olddir + 1) % 4;
1585 planb = olddir;
1586 planc = (olddir + 3) % 4;
1587 } else {
1588 plana = (olddir + 3) % 4;
1589 planb = olddir;
1590 planc = (olddir + 1) % 4;
1594 /* test for collision */
1595 set_worm_dir(w, plana);
1596 if (will_worm_collide(w)){
1598 /* plan b */
1599 set_worm_dir(w, planb);
1601 /* test for collision */
1602 if (will_worm_collide(w)) {
1604 /* plan c */
1605 set_worm_dir(w, planc);
1611 * prints out the score board with all the status information
1612 * about the game.
1614 static void score_board(void)
1616 int i;
1617 int y = 0;
1618 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1619 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0,
1620 LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1621 rb->lcd_set_drawmode(DRMODE_SOLID);
1622 for (i = 0; i < worm_count; i++) {
1623 int score = get_score(&worms[i]);
1624 int collision = check_collision(&worms[i]);
1625 const char *state_str;
1627 /* high score */
1628 if (worms[i].fetch_worm_direction != virtual_player){
1629 if (highscore < score) {
1630 highscore = score;
1634 /* worm state */
1635 if (collision == COLLISION_NONE) {
1636 if (worms[i].growing > 0)
1637 state_str = "Growing";
1638 else {
1639 state_str = worms[i].alive ? "Hungry" : "Wormed";
1641 } else {
1642 state_str = state_desc[collision];
1645 /* length */
1646 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, y , "Len:%d", score);
1647 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, y+8, state_str);
1649 if (!worms[i].alive){
1650 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1651 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1652 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1653 rb->lcd_set_drawmode(DRMODE_SOLID);
1655 y += 19;
1657 #ifdef DEBUG_WORMLET
1658 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, "ticks %d", max_cycle);
1659 #else
1660 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, "Hs: %d", highscore);
1661 #endif
1665 * Checks for collisions of the worm and its environment and
1666 * takes appropriate actions like growing the worm or killing it.
1667 * @return bool Returns true if the worm is dead. Returns
1668 * false if the worm is healthy, up and creeping.
1670 static bool process_collisions(struct worm *w)
1672 int index = -1;
1674 w->alive &= !field_collision(w);
1676 if (w->alive) {
1678 /* check if food was eaten */
1679 index = food_collision(w->x[w->head], w->y[w->head]);
1680 if (index != -1){
1681 int i;
1683 clear_food(index);
1684 make_food(index);
1685 draw_food(index);
1687 for (i = 0; i < arghs_per_food; i++) {
1688 argh_count++;
1689 if (argh_count > MAX_ARGH)
1690 argh_count = MAX_ARGH;
1691 make_argh(argh_count - 1);
1692 draw_argh(argh_count - 1);
1695 add_growing(w, worm_food);
1697 draw_worm(w);
1700 /* check if argh was eaten */
1701 else {
1702 index = argh_collision(w->x[w->head], w->y[w->head]);
1703 if (index != -1) {
1704 w->alive = false;
1706 else {
1707 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1708 w->alive = false;
1713 return !w->alive;
1717 * The main loop of the game.
1718 * @return bool Returns true if the game ended
1719 * with a dead worm. Returns false if the user
1720 * aborted the game manually.
1722 static int run(void)
1724 int button = 0;
1725 int wormDead = false;
1726 bool paused = false;
1728 /* ticks are counted to compensate speed variations */
1729 long cycle_start = 0, cycle_end = 0;
1730 #ifdef DEBUG_WORMLET
1731 int ticks_to_max_cycle_reset = 20;
1732 max_cycle = 0;
1733 #endif
1735 /* initialize the board and so on */
1736 init_wormlet();
1738 cycle_start = *rb->current_tick;
1739 /* change the direction of the worm */
1740 while (!wormDead)
1742 int i;
1743 long cycle_duration=0;
1745 #ifdef HAS_BUTTON_HOLD
1746 if (rb->button_hold())
1747 paused = true;
1748 #endif
1750 switch (button) {
1751 case BTN_STARTPAUSE:
1752 paused = !paused;
1753 break;
1754 case BTN_STOPRESET:
1755 if (paused)
1756 return 1; /* restart game */
1757 else
1758 paused = true;
1759 break;
1760 #ifdef BTN_RC_QUIT
1761 case BTN_RC_QUIT:
1762 #endif
1763 case BTN_QUIT:
1764 return 2; /* back to menu */
1765 break;
1767 if (!paused)
1769 switch (button) {
1770 case BTN_DIR_UP:
1771 if (players == 1 && !use_remote) {
1772 player1_dir = NORTH;
1774 break;
1776 case BTN_DIR_DOWN:
1777 if (players == 1 && !use_remote) {
1778 player1_dir = SOUTH;
1780 break;
1782 case BTN_DIR_LEFT:
1783 if (players != 1 || use_remote) {
1784 player1_dir = (player1_dir + 3) % 4;
1785 } else {
1786 player1_dir = WEST;
1788 break;
1790 case BTN_DIR_RIGHT:
1791 if (players != 1 || use_remote) {
1792 player1_dir = (player1_dir + 1) % 4;
1793 } else {
1794 player1_dir = EAST;
1796 break;
1798 #ifdef MULTIPLAYER
1799 case BTN_PLAYER2_DIR1:
1800 player2_dir = (player2_dir + 3) % 4;
1801 break;
1803 case BTN_PLAYER2_DIR2:
1804 player2_dir = (player2_dir + 1) % 4;
1805 break;
1806 #endif
1808 #ifdef REMOTE
1809 case BTN_RC_UP:
1810 player3_dir = (player3_dir + 1) % 4;
1811 break;
1813 case BTN_RC_DOWN:
1814 player3_dir = (player3_dir + 3) % 4;
1815 break;
1816 #endif
1820 for (i = 0; i < worm_count; i++) {
1821 worms[i].fetch_worm_direction(&worms[i]);
1824 wormDead = true;
1825 for (i = 0; i < worm_count; i++){
1826 struct worm *w = &worms[i];
1827 move_worm(w);
1828 wormDead &= process_collisions(w);
1829 draw_worm(w);
1831 score_board();
1832 rb->lcd_update();
1833 if (button == BTN_STOPRESET) {
1834 wormDead = true;
1837 /* here the wormlet game cycle ends
1838 thus the current tick is stored
1839 as end time */
1840 cycle_end = *rb->current_tick;
1842 /* The duration of the game cycle */
1843 cycle_duration = cycle_end - cycle_start;
1844 cycle_duration = MAX(0, cycle_duration);
1845 cycle_duration = MIN(speed -1, cycle_duration);
1848 #ifdef DEBUG_WORMLET
1849 ticks_to_max_cycle_reset--;
1850 if (ticks_to_max_cycle_reset <= 0) {
1851 max_cycle = 0;
1854 if (max_cycle < cycle_duration) {
1855 max_cycle = cycle_duration;
1856 ticks_to_max_cycle_reset = 20;
1858 #endif
1860 /* adjust the number of ticks to wait for a button.
1861 This ensures that a complete game cycle including
1862 user input runs in constant time */
1863 button = rb->button_get_w_tmo(speed - cycle_duration);
1864 cycle_start = *rb->current_tick;
1867 rb->splash(HZ*2, "Game Over!");
1869 return 2; /* back to menu */
1872 #ifdef DEBUG_WORMLET
1875 * Just a test routine that checks that worm_food_collision works
1876 * in some typical situations.
1878 static void test_worm_food_collision(void)
1880 int collision_count = 0;
1881 int i;
1882 rb->lcd_clear_display();
1883 init_worm(&worms[0], 10, 10);
1884 add_growing(&worms[0], 10);
1885 set_worm_dir(&worms[0], EAST);
1886 for (i = 0; i < 10; i++) {
1887 move_worm(&worms[0]);
1888 draw_worm(&worms[0]);
1891 set_worm_dir(&worms[0], SOUTH);
1892 for (i = 0; i < 10; i++) {
1893 move_worm(&worms[0]);
1894 draw_worm(&worms[0]);
1897 foodx[0] = 15;
1898 foody[0] = 12;
1899 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1900 bool collision;
1901 draw_worm(&worms[0]);
1902 draw_food(0);
1903 collision = worm_food_collision(&worms[0], 0);
1904 if (collision) {
1905 collision_count++;
1907 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count);
1908 rb->lcd_update();
1910 if (collision_count != food_size) {
1911 rb->button_get(true);
1915 foody[0] = 15;
1916 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1917 bool collision;
1918 draw_worm(&worms[0]);
1919 draw_food(0);
1920 collision = worm_food_collision(&worms[0], 0);
1921 if (collision) {
1922 collision_count ++;
1924 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count);
1925 rb->lcd_update();
1927 if (collision_count != food_size * 2) {
1928 rb->button_get(true);
1933 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh)
1935 int x, y;
1936 bool retVal = false;
1937 for (x = rx; x < rx + rw; x++){
1938 for (y = ry; y < ry + rh; y++) {
1939 if (specific_worm_collision(w, x, y) != -1) {
1940 retVal = true;
1944 return retVal;
1947 static void test_worm_argh_collision(void)
1949 int i;
1950 int dir;
1951 int collision_count = 0;
1952 rb->lcd_clear_display();
1953 init_worm(&worms[0], 10, 10);
1954 add_growing(&worms[0], 40);
1955 for (dir = 0; dir < 4; dir++) {
1956 set_worm_dir(&worms[0], (EAST + dir) % 4);
1957 for (i = 0; i < 10; i++) {
1958 move_worm(&worms[0]);
1959 draw_worm(&worms[0]);
1963 arghx[0] = 12;
1964 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1965 bool collision;
1966 draw_argh(0);
1967 collision = worm_argh_collision(&worms[0], 0);
1968 if (collision) {
1969 collision_count ++;
1971 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count);
1972 rb->lcd_update();
1974 if (collision_count != argh_size * 2) {
1975 rb->button_get(true);
1978 arghy[0] = 12;
1979 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1980 bool collision;
1981 draw_argh(0);
1982 collision = worm_argh_collision(&worms[0], 0);
1983 if (collision) {
1984 collision_count ++;
1986 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count);
1987 rb->lcd_update();
1989 if (collision_count != argh_size * 4) {
1990 rb->button_get(true);
1994 static int testline_in_rect(void)
1996 int testfailed = -1;
1998 int rx = 10;
1999 int ry = 15;
2000 int rw = 20;
2001 int rh = 25;
2003 /* Test 1 */
2004 int x1 = 12;
2005 int y1 = 8;
2006 int x2 = 12;
2007 int y2 = 42;
2009 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2010 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2011 rb->lcd_drawrect(rx, ry, rw, rh);
2012 rb->lcd_drawline(x1, y1, x2, y2);
2013 rb->lcd_update();
2014 rb->lcd_putsxy(0, 0, "failed 1");
2015 rb->button_get(true);
2016 testfailed = 1;
2019 /* test 2 */
2020 y2 = 20;
2021 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2022 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2023 rb->lcd_drawrect(rx, ry, rw, rh);
2024 rb->lcd_drawline(x1, y1, x2, y2);
2025 rb->lcd_putsxy(0, 0, "failed 2");
2026 rb->lcd_update();
2027 rb->button_get(true);
2028 testfailed = 2;
2031 /* test 3 */
2032 y1 = 30;
2033 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2034 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2035 rb->lcd_drawrect(rx, ry, rw, rh);
2036 rb->lcd_drawline(x1, y1, x2, y2);
2037 rb->lcd_putsxy(0, 0, "failed 3");
2038 rb->lcd_update();
2039 rb->button_get(true);
2040 testfailed = 3;
2043 /* test 4 */
2044 y2 = 45;
2045 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2046 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2047 rb->lcd_drawrect(rx, ry, rw, rh);
2048 rb->lcd_drawline(x1, y1, x2, y2);
2049 rb->lcd_putsxy(0, 0, "failed 4");
2050 rb->lcd_update();
2051 rb->button_get(true);
2052 testfailed = 4;
2055 /* test 5 */
2056 y1 = 50;
2057 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2058 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2059 rb->lcd_drawrect(rx, ry, rw, rh);
2060 rb->lcd_drawline(x1, y1, x2, y2);
2061 rb->lcd_putsxy(0, 0, "failed 5");
2062 rb->lcd_update();
2063 rb->button_get(true);
2064 testfailed = 5;
2067 /* test 6 */
2068 y1 = 5;
2069 y2 = 7;
2070 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2071 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2072 rb->lcd_drawrect(rx, ry, rw, rh);
2073 rb->lcd_drawline(x1, y1, x2, y2);
2074 rb->lcd_putsxy(0, 0, "failed 6");
2075 rb->lcd_update();
2076 rb->button_get(true);
2077 testfailed = 6;
2080 /* test 7 */
2081 x1 = 8;
2082 y1 = 20;
2083 x2 = 35;
2084 y2 = 20;
2085 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2086 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2087 rb->lcd_drawrect(rx, ry, rw, rh);
2088 rb->lcd_drawline(x1, y1, x2, y2);
2089 rb->lcd_putsxy(0, 0, "failed 7");
2090 rb->lcd_update();
2091 rb->button_get(true);
2092 testfailed = 7;
2095 /* test 8 */
2096 x2 = 12;
2097 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2098 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2099 rb->lcd_drawrect(rx, ry, rw, rh);
2100 rb->lcd_drawline(x1, y1, x2, y2);
2101 rb->lcd_putsxy(0, 0, "failed 8");
2102 rb->lcd_update();
2103 rb->button_get(true);
2104 testfailed = 8;
2107 /* test 9 */
2108 x1 = 25;
2109 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2110 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2111 rb->lcd_drawrect(rx, ry, rw, rh);
2112 rb->lcd_drawline(x1, y1, x2, y2);
2113 rb->lcd_putsxy(0, 0, "failed 9");
2114 rb->lcd_update();
2115 rb->button_get(true);
2116 testfailed = 9;
2119 /* test 10 */
2120 x2 = 37;
2121 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2122 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2123 rb->lcd_drawrect(rx, ry, rw, rh);
2124 rb->lcd_drawline(x1, y1, x2, y2);
2125 rb->lcd_putsxy(0, 0, "failed 10");
2126 rb->lcd_update();
2127 rb->button_get(true);
2128 testfailed = 10;
2131 /* test 11 */
2132 x1 = 42;
2133 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2134 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2135 rb->lcd_drawrect(rx, ry, rw, rh);
2136 rb->lcd_drawline(x1, y1, x2, y2);
2137 rb->lcd_putsxy(0, 0, "failed 11");
2138 rb->lcd_update();
2139 rb->button_get(true);
2140 testfailed = 11;
2143 /* test 12 */
2144 x1 = 5;
2145 x2 = 7;
2146 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2147 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2148 rb->lcd_drawrect(rx, ry, rw, rh);
2149 rb->lcd_drawline(x1, y1, x2, y2);
2150 rb->lcd_putsxy(0, 0, "failed 12");
2151 rb->lcd_update();
2152 rb->button_get(true);
2153 testfailed = 12;
2156 /* test 13 */
2157 rx = 9;
2158 ry = 15;
2159 rw = food_size;
2160 rh = food_size;
2162 x1 = 10;
2163 y1 = 10;
2164 x2 = 10;
2165 y2 = 20;
2166 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2167 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2168 rb->lcd_drawrect(rx, ry, rw, rh);
2169 rb->lcd_drawline(x1, y1, x2, y2);
2170 rb->lcd_putsxy(0, 0, "failed 13");
2171 rb->lcd_update();
2172 rb->button_get(true);
2173 testfailed = 13;
2176 /* test 14 */
2177 rx = 9;
2178 ry = 15;
2179 rw = 4;
2180 rh = 4;
2182 x1 = 10;
2183 y1 = 10;
2184 x2 = 10;
2185 y2 = 19;
2186 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2187 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2188 rb->lcd_drawline(x1, y1, x2, y2);
2189 rb->lcd_invertrect(rx, ry, rw, rh);
2190 rb->lcd_putsxy(0, 0, "failed 14");
2191 rb->lcd_update();
2192 rb->button_get(true);
2193 testfailed = 14;
2196 rb->lcd_clear_display();
2198 return testfailed;
2202 * Just a test routine to test wether specific_worm_collision might work properly
2204 static int test_specific_worm_collision(void)
2206 int collisions = 0;
2207 int dir;
2208 int x = 0;
2209 int y = 0;
2210 rb->lcd_clear_display();
2211 init_worm(&worms[0], 10, 20);
2212 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2214 for (dir = EAST; dir < EAST + 4; dir++) {
2215 int i;
2216 set_worm_dir(&worms[0], dir % 4);
2217 for (i = 0; i < 5; i++) {
2218 if (!(dir % 4 == NORTH && i == 9)) {
2219 move_worm(&worms[0]);
2220 draw_worm(&worms[0]);
2225 for (y = 15; y < 30; y ++){
2226 for (x = 5; x < 20; x++) {
2227 if (specific_worm_collision(&worms[0], x, y) != -1) {
2228 collisions ++;
2230 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2231 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "collisions %d", collisions);
2232 rb->lcd_update();
2235 if (collisions != 21) {
2236 rb->button_get(true);
2238 return collisions;
2241 static void test_make_argh(void)
2243 int dir;
2244 int seed = 0;
2245 int hit = 0;
2246 int failures = 0;
2247 int last_failures = 0;
2248 int i, worm_idx;
2249 rb->lcd_clear_display();
2250 worm_count = 3;
2252 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2253 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2254 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2257 for (dir = EAST; dir < EAST + 4; dir++) {
2258 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2259 set_worm_dir(&worms[worm_idx], dir % 4);
2260 for (i = 0; i < 10; i++) {
2261 if (!(dir % 4 == NORTH && i == 9)) {
2262 move_worm(&worms[worm_idx]);
2263 draw_worm(&worms[worm_idx]);
2269 rb->lcd_update();
2271 for (seed = 0; hit < 20; seed += 2) {
2272 int x, y;
2273 rb->srand(seed);
2274 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2275 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2277 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2278 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2279 int tries = 0;
2280 rb->srand(seed);
2282 tries = make_argh(0);
2283 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2284 failures ++;
2287 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "(%d;%d) fail%d try%d",
2288 x, y, failures, tries);
2289 rb->lcd_update();
2290 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y,
2291 argh_size, argh_size);
2292 rb->lcd_update();
2293 draw_argh(0);
2294 rb->lcd_update();
2295 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y,
2296 argh_size, argh_size);
2297 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y,
2298 argh_size, argh_size);
2300 if (failures > last_failures) {
2301 rb->button_get(true);
2303 last_failures = failures;
2304 hit ++;
2310 static void test_worm_argh_collision_in_moves(void) {
2311 int hit_count = 0;
2312 int i;
2313 rb->lcd_clear_display();
2314 init_worm(&worms[0], 10, 20);
2316 arghx[0] = 20;
2317 arghy[0] = 18;
2318 draw_argh(0);
2320 set_worm_dir(&worms[0], EAST);
2321 for (i = 0; i < 20; i++) {
2322 move_worm(&worms[0]);
2323 draw_worm(&worms[0]);
2324 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2325 hit_count ++;
2327 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "in 5 moves hits: %d", hit_count);
2328 rb->lcd_update();
2330 if (hit_count != argh_size + 5) {
2331 rb->button_get(true);
2334 #endif /* DEBUG_WORMLET */
2337 * Reverts default settings
2339 static void default_settings(void)
2341 arghs_per_food = ARGHS_PER_FOOD;
2342 argh_size = ARGH_SIZE;
2343 food_size = FOOD_SIZE;
2344 speed = SPEED;
2345 worm_food = WORM_PER_FOOD;
2346 players = 1;
2347 worm_count = MAX_WORMS;
2348 use_remote = false;
2349 return;
2353 * Launches the wormlet game
2355 static bool launch_wormlet(void)
2357 int game_result = 1;
2359 rb->lcd_clear_display();
2361 /* Turn off backlight timeout */
2362 backlight_ignore_timeout();
2364 /* start the game */
2365 while (game_result == 1)
2366 game_result = run();
2368 switch (game_result)
2370 case 2:
2371 /* Turn on backlight timeout (revert to settings) */
2372 backlight_use_settings();
2373 return false;
2374 break;
2376 return false;
2380 * Main entry point
2382 enum plugin_status plugin_start(const void* parameter)
2384 int result;
2385 int menu_quit = 0;
2386 int new_setting;
2388 (void)(parameter);
2390 default_settings();
2391 if (configfile_load(SETTINGS_FILENAME, config,
2392 sizeof(config)/sizeof(*config),
2393 SETTINGS_MIN_VERSION ) < 0)
2395 /* If the loading failed, save a new config file (as the disk is
2396 already spinning) */
2397 configfile_save(SETTINGS_FILENAME, config,
2398 sizeof(config)/sizeof(*config),
2399 SETTINGS_VERSION);
2402 #ifdef HAVE_LCD_COLOR
2403 rb->lcd_set_foreground(COLOR_FG);
2404 rb->lcd_set_background(COLOR_BG);
2405 #endif
2407 #if LCD_DEPTH > 1
2408 rb->lcd_set_backdrop(NULL);
2409 #endif
2411 #ifdef DEBUG_WORMLET
2412 testline_in_rect();
2413 test_worm_argh_collision_in_moves();
2414 test_make_argh();
2415 test_worm_food_collision();
2416 test_worm_argh_collision();
2417 test_specific_worm_collision();
2418 #endif
2420 /* Setup screen */
2422 static const struct opt_items noyes[2] = {
2423 { "No", -1 },
2424 { "Yes", -1 },
2427 static const struct opt_items remoteonly_option[1] = {
2428 { "Remote Control", -1 }
2431 static const struct opt_items key24_option[2] = {
2432 { "4 Key Control", -1 },
2433 { "2 Key Control", -1 }
2436 #ifdef REMOTE
2437 static const struct opt_items remote_option[2] = {
2438 { "Remote Control", -1 },
2439 { "No Rem. Control", -1 }
2441 #else
2442 static const struct opt_items key2_option[1] = {
2443 { "2 Key Control", -1 }
2445 #endif
2447 static const struct opt_items nokey_option[1] = {
2448 { "Out of Control", -1 }
2451 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, "Play Wormlet!",
2452 "Number of Worms", "Number of Players", "Control Style",
2453 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2454 "Argh Size","Food Size","Revert to Default Settings",
2455 "Playback Control", "Quit");
2457 rb->button_clear_queue();
2459 while (!menu_quit) {
2460 switch(rb->do_menu(&menu, &result, NULL, false))
2462 case 0:
2463 rb->lcd_setfont(FONT_SYSFIXED);
2464 launch_wormlet();
2465 break;
2466 case 1:
2467 rb->set_int("Number of Worms", "", UNIT_INT, &worm_count, NULL,
2468 1, 1, 3, NULL);
2469 if (worm_count < players) {
2470 worm_count = players;
2472 break;
2473 case 2:
2474 #ifdef MULTIPLAYER
2475 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2476 1, 0, 4, NULL);
2477 #else
2478 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2479 1, 0, 2, NULL);
2480 #endif
2481 if (players > worm_count) {
2482 worm_count = players;
2484 if (players > 2) {
2485 use_remote = true;
2487 break;
2488 case 3:
2489 switch(players) {
2490 case 0:
2491 rb->set_option("Control Style",&use_remote,INT,
2492 nokey_option, 1, NULL);
2493 break;
2494 case 1:
2495 rb->set_option("Control Style",&use_remote,INT,
2496 key24_option, 2, NULL);
2497 break;
2498 case 2:
2499 #ifdef REMOTE
2500 rb->set_option("Control Style",&use_remote,INT,
2501 remote_option, 2, NULL);
2502 #else
2503 rb->set_option("Control Style",&use_remote,INT,
2504 key2_option, 1, NULL);
2505 #endif
2506 break;
2507 case 3:
2508 rb->set_option("Control Style",&use_remote,INT,
2509 remoteonly_option, 1, NULL);
2510 break;
2512 break;
2513 case 4:
2514 rb->set_int("Worm Growth Per Food", "", UNIT_INT, &worm_food,
2515 NULL, 1, 0, 15, NULL);
2516 break;
2517 case 5:
2518 new_setting = 20 - speed;
2519 rb->set_int("Worm Speed", "", UNIT_INT, &new_setting,
2520 NULL, 1, 0, 20, NULL);
2521 speed = 20 - new_setting;
2522 break;
2523 case 6:
2524 rb->set_int("Arghs Per Food", "", UNIT_INT, &arghs_per_food,
2525 NULL, 1, 0, 8, NULL);
2526 break;
2527 case 7:
2528 rb->set_int("Argh Size", "", UNIT_INT, &argh_size,
2529 NULL, 1, 2, 10, NULL);
2530 break;
2531 case 8:
2532 rb->set_int("Food Size", "", UNIT_INT, &food_size,
2533 NULL, 1, 2, 10, NULL);
2534 break;
2535 case 9:
2536 new_setting = 0;
2537 rb->set_option("Reset Settings?", &new_setting, INT, noyes , 2, NULL);
2538 if (new_setting == 1)
2539 default_settings();
2540 break;
2541 case 10:
2542 playback_control(NULL);
2543 break;
2544 default:
2545 menu_quit=1;
2546 break;
2550 configfile_save(SETTINGS_FILENAME, config,
2551 sizeof(config)/sizeof(*config),
2552 SETTINGS_VERSION);
2554 return PLUGIN_OK;