HD300 - plugins keymaps
[kugel-rb.git] / apps / plugins / wormlet.c
blob3d24f7fa19af20b53c2a2a121321ae7547f05a7a
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)
185 #define BTN_DIR_UP BUTTON_UP
186 #define BTN_DIR_DOWN BUTTON_DOWN
187 #define BTN_DIR_LEFT BUTTON_LEFT
188 #define BTN_DIR_RIGHT BUTTON_RIGHT
189 #define BTN_STARTPAUSE BUTTON_SELECT
190 #define BTN_QUIT BUTTON_BACK
191 #define BTN_STOPRESET BUTTON_MENU
193 #elif (CONFIG_KEYPAD == MROBE100_PAD)
195 #define BTN_DIR_UP BUTTON_UP
196 #define BTN_DIR_DOWN BUTTON_DOWN
197 #define BTN_DIR_LEFT BUTTON_LEFT
198 #define BTN_DIR_RIGHT BUTTON_RIGHT
199 #define BTN_STARTPAUSE BUTTON_SELECT
200 #define BTN_QUIT BUTTON_POWER
201 #define BTN_STOPRESET BUTTON_DISPLAY
203 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
205 #define BTN_DIR_UP BUTTON_RC_VOL_UP
206 #define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN
207 #define BTN_DIR_LEFT BUTTON_RC_REW
208 #define BTN_DIR_RIGHT BUTTON_RC_FF
209 #define BTN_STARTPAUSE BUTTON_RC_PLAY
210 #define BTN_QUIT BUTTON_RC_REC
211 #define BTN_STOPRESET BUTTON_RC_MODE
213 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
215 #define BTN_QUIT BUTTON_POWER
217 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
219 #define BTN_DIR_UP BUTTON_UP
220 #define BTN_DIR_DOWN BUTTON_DOWN
221 #define BTN_DIR_LEFT BUTTON_LEFT
222 #define BTN_DIR_RIGHT BUTTON_RIGHT
223 #define BTN_STARTPAUSE BUTTON_PLAY
224 #define BTN_QUIT BUTTON_BACK
225 #define BTN_STOPRESET BUTTON_MENU
227 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
229 #define BTN_DIR_UP BUTTON_UP
230 #define BTN_DIR_DOWN BUTTON_DOWN
231 #define BTN_DIR_LEFT BUTTON_LEFT
232 #define BTN_DIR_RIGHT BUTTON_RIGHT
233 #define BTN_STARTPAUSE BUTTON_MENU
234 #define BTN_QUIT BUTTON_POWER
235 #define BTN_STOPRESET BUTTON_VIEW
237 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
239 #define BTN_DIR_UP BUTTON_UP
240 #define BTN_DIR_DOWN BUTTON_DOWN
241 #define BTN_DIR_LEFT BUTTON_PREV
242 #define BTN_DIR_RIGHT BUTTON_NEXT
243 #define BTN_STARTPAUSE BUTTON_MENU
244 #define BTN_QUIT BUTTON_POWER
245 #define BTN_STOPRESET BUTTON_RIGHT
247 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
249 #define BTN_DIR_UP BUTTON_UP
250 #define BTN_DIR_DOWN BUTTON_DOWN
251 #define BTN_DIR_LEFT BUTTON_PREV
252 #define BTN_DIR_RIGHT BUTTON_RIGHT
253 #define BTN_STARTPAUSE BUTTON_MENU
254 #define BTN_QUIT BUTTON_POWER
255 #define BTN_STOPRESET BUTTON_RIGHT
257 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
258 (CONFIG_KEYPAD == ONDAVX777_PAD) || \
259 CONFIG_KEYPAD == MROBE500_PAD
261 #define BTN_QUIT BUTTON_POWER
263 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
265 #define BTN_DIR_UP BUTTON_UP
266 #define BTN_DIR_DOWN BUTTON_DOWN
267 #define BTN_DIR_LEFT BUTTON_LEFT
268 #define BTN_DIR_RIGHT BUTTON_RIGHT
269 #define BTN_STARTPAUSE BUTTON_PLAY
270 #define BTN_QUIT BUTTON_FFWD
271 #define BTN_STOPRESET BUTTON_REW
273 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
275 #define BTN_DIR_UP BUTTON_UP
276 #define BTN_DIR_DOWN BUTTON_DOWN
277 #define BTN_DIR_LEFT BUTTON_PREV
278 #define BTN_DIR_RIGHT BUTTON_NEXT
279 #define BTN_STARTPAUSE BUTTON_PLAY
280 #define BTN_QUIT BUTTON_REC
281 #define BTN_STOPRESET BUTTON_CANCEL
283 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
285 #define BTN_DIR_UP BUTTON_REC
286 #define BTN_DIR_DOWN BUTTON_PLAY
287 #define BTN_DIR_LEFT BUTTON_REW
288 #define BTN_DIR_RIGHT BUTTON_FF
289 #define BTN_STARTPAUSE BUTTON_FUNC
290 #define BTN_QUIT (BUTTON_REC|BUTTON_PLAY)
291 #define BTN_STOPRESET (BUTTON_FUNC|BUTTON_REPEAT)
293 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
295 #define BTN_DIR_UP BUTTON_UP
296 #define BTN_DIR_DOWN BUTTON_DOWN
297 #define BTN_DIR_LEFT BUTTON_MENU
298 #define BTN_DIR_RIGHT BUTTON_ENTER
299 #define BTN_STARTPAUSE BUTTON_PLAY
300 #define BTN_QUIT BUTTON_REC
301 #define BTN_STOPRESET (BUTTON_MENU | BUTTON_REPEAT)
303 #else
304 #error No keymap defined!
305 #endif
307 #ifdef HAVE_TOUCHSCREEN
308 #ifndef BTN_DIR_UP
309 #define BTN_DIR_UP BUTTON_TOPMIDDLE
310 #endif
311 #ifndef BTN_DIR_DOWN
312 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
313 #endif
314 #ifndef BTN_DIR_LEFT
315 #define BTN_DIR_LEFT BUTTON_MIDLEFT
316 #endif
317 #ifndef BTN_DIR_RIGHT
318 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
319 #endif
320 #ifndef BTN_STARTPAUSE
321 #define BTN_STARTPAUSE BUTTON_CENTER
322 #endif
323 #ifndef BTN_QUIT
324 #define BTN_QUIT BUTTON_TOPLEFT
325 #endif
326 #ifndef BTN_STOPRESET
327 #define BTN_STOPRESET BUTTON_TOPRIGHT
328 #endif
329 #endif
331 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
332 #define FOOD_SIZE 3
333 #define ARGH_SIZE 4
334 #define SPEED 14
335 #define MAX_WORM_SEGMENTS 128
336 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
337 #define FOOD_SIZE 3
338 #define ARGH_SIZE 4
339 #define SPEED 14
340 #define MAX_WORM_SEGMENTS 128
341 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
342 #define FOOD_SIZE 3
343 #define ARGH_SIZE 4
344 #define SPEED 14
345 #define MAX_WORM_SEGMENTS 128
346 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
347 #define FOOD_SIZE 3
348 #define ARGH_SIZE 4
349 #define SPEED 12
350 #define MAX_WORM_SEGMENTS 128
351 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
352 #define FOOD_SIZE 4
353 #define ARGH_SIZE 5
354 #define SPEED 10
355 #define MAX_WORM_SEGMENTS 128
356 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
357 #define FOOD_SIZE 4
358 #define ARGH_SIZE 5
359 #define SPEED 9
360 #define MAX_WORM_SEGMENTS 128
361 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
362 #define FOOD_SIZE 4
363 #define ARGH_SIZE 5
364 #define SPEED 8
365 #define MAX_WORM_SEGMENTS 256
366 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
367 #define FOOD_SIZE 4
368 #define ARGH_SIZE 5
369 #define SPEED 6
370 #define MAX_WORM_SEGMENTS 256
371 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
372 #define FOOD_SIZE 5
373 #define ARGH_SIZE 6
374 #define SPEED 4
375 #define MAX_WORM_SEGMENTS 512
376 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
377 #define FOOD_SIZE 5
378 #define ARGH_SIZE 6
379 #define SPEED 4
380 #define MAX_WORM_SEGMENTS 512
381 #elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
382 ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
383 #define FOOD_SIZE 7
384 #define ARGH_SIZE 8
385 #define SPEED 4
386 #define MAX_WORM_SEGMENTS 512
387 #elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
388 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
389 #define FOOD_SIZE 14
390 #define ARGH_SIZE 16
391 #define SPEED 4
392 #define MAX_WORM_SEGMENTS 512
393 #endif
395 #ifdef HAVE_LCD_COLOR
396 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
397 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
398 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
399 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
400 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
401 #endif
403 #define CHECK_SQUARE_COLLISION(x1,y1,s1,x2,y2,s2) (x1+s1>x2)&&(x2+s2>x1)&&(y1+s1>y2)&&(y2+s2>y1)
406 * All the properties that a worm has.
408 static struct worm {
409 /* The worm is stored in a ring of xy coordinates */
410 int x[MAX_WORM_SEGMENTS];
411 int y[MAX_WORM_SEGMENTS];
413 int head; /* index of the head within the buffer */
414 int tail; /* index of the tail within the buffer */
415 int growing; /* number of cyles the worm still keeps growing */
416 bool alive; /* the worms living state */
418 /* direction vector in which the worm moves */
419 int dirx; /* only values -1 0 1 allowed */
420 int diry; /* only values -1 0 1 allowed */
422 /* this method is used to fetch the direction the user
423 has selected. It can be one of the values
424 human_player1, human_player2, remote_player, virtual_player.
425 All these values are fuctions, that can change the direction
426 of the worm */
427 void (*fetch_worm_direction)(struct worm *w);
428 } worms[MAX_WORMS];
430 /* stores the highscore - besides it was scored by a virtual player */
431 static int highscore;
433 #define MAX_FOOD 5 /* maximal number of food items */
435 /* The arrays store the food coordinates */
436 static int foodx[MAX_FOOD];
437 static int foody[MAX_FOOD];
439 #define MAX_ARGH 100 /* maximal number of argh items */
440 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
442 /* The arrays store the argh coordinates */
443 static int arghx[MAX_ARGH];
444 static int arghy[MAX_ARGH];
446 /* the number of arghs that are currently in use */
447 static int argh_count;
449 /* the number of arghs per food, settable by user */
450 static int arghs_per_food = ARGHS_PER_FOOD;
451 /* the size of the argh, settable by user */
452 static int argh_size = ARGH_SIZE;
453 /* the size of the food, settable by user */
454 static int food_size = FOOD_SIZE;
455 /* the speed of the worm, settable by user */
456 static int speed = SPEED;
457 /* the amount a worm grows by eating a food, settable by user */
458 static int worm_food = WORM_PER_FOOD;
460 /* End additional variables */
462 /* the number of active worms (dead or alive) */
463 static int worm_count = MAX_WORMS;
465 /* in multiplayer mode: en- / disables the remote worm control
466 in singleplayer mode: toggles 4 / 2 button worm control */
467 static bool use_remote = false;
469 /* return values of check_collision */
470 #define COLLISION_NONE 0
471 #define COLLISION_WORM 1
472 #define COLLISION_FOOD 2
473 #define COLLISION_ARGH 3
474 #define COLLISION_FIELD 4
476 static const char *const state_desc[] = {
477 [COLLISION_NONE] = NULL,
478 [COLLISION_WORM] = "Wormed",
479 [COLLISION_FOOD] = "Growing",
480 [COLLISION_ARGH] = "Argh",
481 [COLLISION_FIELD] = "Crashed",
484 /* constants for use as directions.
485 Note that the values are ordered clockwise.
486 Thus increasing / decreasing the values
487 is equivalent to right / left turns. */
488 #define WEST 0
489 #define NORTH 1
490 #define EAST 2
491 #define SOUTH 3
493 /* direction of human player 1 */
494 static int player1_dir = EAST;
495 /* direction of human player 2 */
496 static int player2_dir = EAST;
497 /* direction of human player 3 */
498 static int player3_dir = EAST;
500 /* the number of (human) players that currently
501 control a worm */
502 static int players = 1;
504 #define SETTINGS_VERSION 1
505 #define SETTINGS_MIN_VERSION 1
506 #define SETTINGS_FILENAME "wormlet.cfg"
508 static struct configdata config[] =
510 {TYPE_INT, 0, 1024, { .int_p = &highscore }, "highscore", NULL},
511 {TYPE_INT, 0, 15, { .int_p = &arghs_per_food }, "arghs per food", NULL},
512 {TYPE_INT, 0, 15, { .int_p = &argh_size }, "argh size", NULL},
513 {TYPE_INT, 0, 15, { .int_p = &food_size }, "food size", NULL},
514 {TYPE_INT, 0, 3, { .int_p = &players }, "players", NULL},
515 {TYPE_INT, 0, 3, { .int_p = &worm_count }, "worms", NULL},
516 {TYPE_INT, 0, 20, { .int_p = &speed }, "speed", NULL},
517 {TYPE_INT, 0, 15, { .int_p = &worm_food }, "Worm Growth Per Food", NULL}
521 * Returns the direction id in which the worm
522 * currently is creeping.
523 * @param struct worm *w The worm that is to be investigated.
524 * w Must not be null.
525 * @return int A value 0 <= value < 4
526 * Note the predefined constants NORTH, SOUTH, EAST, WEST
528 static int get_worm_dir(struct worm *w)
530 int retVal ;
531 if (w->dirx == 0) {
532 if (w->diry == 1) {
533 retVal = SOUTH;
534 } else {
535 retVal = NORTH;
537 } else {
538 if (w->dirx == 1) {
539 retVal = EAST;
540 } else {
541 retVal = WEST;
544 return retVal;
548 * Set the direction of the specified worm with a direction id.
549 * Increasing the value by 1 means to turn the worm direction
550 * to right by 90 degree.
551 * @param struct worm *w The worm that is to be altered. w Must not be null.
552 * @param int dir The new direction in which the worm is to creep.
553 * dir must be 0 <= dir < 4. Use predefined constants
554 * NORTH, SOUTH, EAST, WEST
556 static void set_worm_dir(struct worm *w, int dir)
558 switch (dir) {
559 case WEST:
560 w->dirx = -1;
561 w->diry = 0;
562 break;
563 case NORTH:
564 w->dirx = 0;
565 w->diry = - 1;
566 break;
567 case EAST:
568 w->dirx = 1;
569 w->diry = 0;
570 break;
571 case SOUTH:
572 w->dirx = 0;
573 w->diry = 1;
574 break;
579 * Returns the current length of the worm array. This
580 * is also a value for the number of bends that are in the worm.
581 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
583 static int get_worm_array_length(struct worm *w)
585 /* initial simple calculation will be overwritten if wrong. */
586 int retVal = w->head - w->tail;
588 /* if the worm 'crosses' the boundaries of the ringbuffer */
589 if (retVal < 0) {
590 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
593 return retVal;
597 * Returns the score the specified worm. The score is the length
598 * of the worm.
599 * @param struct worm *w The worm that is to be investigated.
600 * w must not be null.
601 * @return int The length of the worm (>= 0).
603 static int get_score(struct worm *w)
605 int retval = 0;
606 int length = get_worm_array_length(w);
607 int i;
608 for (i = 0; i < length; i++) {
610 /* The iteration iterates the length of the worm.
611 Here's the conversion to the true indices within the worm arrays. */
612 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
613 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
614 int startx = w->x[linestart];
615 int starty = w->y[linestart];
616 int endx = w->x[lineend];
617 int endy = w->y[lineend];
619 int minimum, maximum;
621 if (startx == endx) {
622 minimum = MIN(starty, endy);
623 maximum = MAX(starty, endy);
624 } else {
625 minimum = MIN(startx, endx);
626 maximum = MAX(startx, endx);
628 retval += abs(maximum - minimum);
630 return retval;
634 * Determines wether the line specified by startx, starty, endx, endy intersects
635 * the rectangle specified by x, y, width, height. Note that the line must be exactly
636 * horizontal or vertical (startx == endx or starty == endy).
637 * @param int startx The x coordinate of the start point of the line.
638 * @param int starty The y coordinate of the start point of the line.
639 * @param int endx The x coordinate of the end point of the line.
640 * @param int endy The y coordinate of the end point of the line.
641 * @param int x The x coordinate of the top left corner of the rectangle.
642 * @param int y The y coordinate of the top left corner of the rectangle.
643 * @param int width The width of the rectangle.
644 * @param int height The height of the rectangle.
645 * @return bool Returns true if the specified line intersects with the recangle.
647 static bool line_in_rect(int startx, int starty, int endx, int endy,
648 int x, int y, int width, int height)
650 bool retval = false;
651 int simple, simplemin, simplemax;
652 int compa, compb, compmin, compmax;
653 int temp;
654 if (startx == endx) {
655 simple = startx;
656 simplemin = x;
657 simplemax = x + width;
659 compa = starty;
660 compb = endy;
661 compmin = y;
662 compmax = y + height;
663 } else {
664 simple = starty;
665 simplemin = y;
666 simplemax = y + height;
668 compa = startx;
669 compb = endx;
670 compmin = x;
671 compmax = x + width;
674 temp = compa;
675 compa = MIN(compa, compb);
676 compb = MAX(temp, compb);
678 if (simplemin <= simple && simple <= simplemax) {
679 if ((compmin <= compa && compa <= compmax) ||
680 (compmin <= compb && compb <= compmax) ||
681 (compa <= compmin && compb >= compmax)) {
682 retval = true;
685 return retval;
689 * Tests wether the specified worm intersects with the rect.
690 * @param struct worm *w The worm to be investigated
691 * @param int x The x coordinate of the top left corner of the rect
692 * @param int y The y coordinate of the top left corner of the rect
693 * @param int widht The width of the rect
694 * @param int height The height of the rect
695 * @return bool Returns true if the worm intersects with the rect
697 static bool worm_in_rect(struct worm *w, int x, int y, int width, int height)
699 bool retval = false;
702 /* get_worm_array_length is expensive -> buffer the value */
703 int wormLength = get_worm_array_length(w);
704 int i;
706 /* test each entry that is part of the worm */
707 for (i = 0; i < wormLength && retval == false; i++) {
709 /* The iteration iterates the length of the worm.
710 Here's the conversion to the true indices within the worm arrays. */
711 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
712 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
713 int startx = w->x[linestart];
714 int starty = w->y[linestart];
715 int endx = w->x[lineend];
716 int endy = w->y[lineend];
718 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
721 return retval;
725 * Checks wether a specific food in the food arrays is at the
726 * specified coordinates.
727 * @param int foodIndex The index of the food in the food arrays
728 * @param int x the x coordinate.
729 * @param int y the y coordinate.
730 * @return Returns true if the coordinate hits the food specified by
731 * foodIndex.
733 static bool specific_food_collision(int foodIndex, int x, int y)
735 bool retVal = false;
736 if (x >= foodx[foodIndex] &&
737 x < foodx[foodIndex] + food_size &&
738 y >= foody[foodIndex] &&
739 y < foody[foodIndex] + food_size) {
741 retVal = true;
743 return retVal;
747 * Returns the index of the food that is at the
748 * given coordinates. If no food is at the coordinates
749 * -1 is returned.
750 * @return int -1 <= value < MAX_FOOD
752 static int food_collision(int x, int y)
754 int i = 0;
755 int retVal = -1;
756 for (i = 0; i < MAX_FOOD; i++) {
757 if (specific_food_collision(i, x, y)) {
758 retVal = i;
759 break;
762 return retVal;
766 * Checks wether a specific argh in the argh arrays is at the
767 * specified coordinates.
768 * @param int arghIndex The index of the argh in the argh arrays
769 * @param int x the x coordinate.
770 * @param int y the y coordinate.
771 * @return Returns true if the coordinate hits the argh specified by
772 * arghIndex.
774 static bool specific_argh_collision(int arghIndex, int x, int y)
776 if ( x >= arghx[arghIndex] &&
777 y >= arghy[arghIndex] &&
778 x < arghx[arghIndex] + argh_size &&
779 y < arghy[arghIndex] + argh_size )
781 return true;
784 return false;
788 * Returns the index of the argh that is at the
789 * given coordinates. If no argh is at the coordinates
790 * -1 is returned.
791 * @param int x The x coordinate.
792 * @param int y The y coordinate.
793 * @return int -1 <= value < argh_count <= MAX_ARGH
795 static int argh_collision(int x, int y)
797 int i = 0;
798 int retVal = -1;
800 /* search for the argh that has the specified coords */
801 for (i = 0; i < argh_count; i++) {
802 if (specific_argh_collision(i, x, y)) {
803 retVal = i;
804 break;
807 return retVal;
811 * Checks wether the worm collides with the food at the specfied food-arrays.
812 * @param int foodIndex The index of the food in the arrays. Ensure the value is
813 * 0 <= foodIndex <= MAX_FOOD
814 * @return Returns true if the worm collides with the specified food.
816 static bool worm_food_collision(struct worm *w, int foodIndex)
818 bool retVal = false;
820 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
821 food_size - 1, food_size - 1);
823 return retVal;
827 * Returns true if the worm hits the argh within the next moves (unless
828 * the worm changes it's direction).
829 * @param struct worm *w - The worm to investigate
830 * @param int argh_idx - The index of the argh
831 * @param int moves - The number of moves that are considered.
832 * @return Returns false if the specified argh is not hit within the next
833 * moves.
835 static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves)
837 bool retVal = false;
838 int x1, y1, x2, y2;
839 x1 = w->x[w->head];
840 y1 = w->y[w->head];
842 x2 = w->x[w->head] + moves * w->dirx;
843 y2 = w->y[w->head] + moves * w->diry;
845 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
846 argh_size, argh_size);
847 return retVal;
851 * Checks wether the worm collides with the argh at the specfied argh-arrays.
852 * @param int arghIndex The index of the argh in the arrays.
853 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
854 * @return Returns true if the worm collides with the specified argh.
856 static bool worm_argh_collision(struct worm *w, int arghIndex)
858 bool retVal = false;
860 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
861 argh_size - 1, argh_size - 1);
863 return retVal;
867 * Find new coordinates for the food stored in foodx[index], foody[index]
868 * that don't collide with any other food or argh
869 * @param int index
870 * Ensure that 0 <= index < MAX_FOOD.
872 static void make_food(int index)
874 int x = 0;
875 int y = 0;
876 bool collisionDetected = false;
877 int i;
879 do {
880 /* make coordinates for a new food so that
881 the entire food lies within the FIELD */
882 x = rb->rand() % (FIELD_RECT_WIDTH - food_size);
883 y = rb->rand() % (FIELD_RECT_HEIGHT - food_size);
884 collisionDetected = false;
885 /* Ensure that the new food doesn't collide with any
886 existing foods or arghs.
887 If the new food hit any existing
888 argh or food a collision is detected.
891 for (i=0; i<MAX_FOOD && !collisionDetected; i++) {
892 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,foodx[i],foody[i],food_size);
894 for (i=0; i<argh_count && !collisionDetected; i++) {
895 collisionDetected = CHECK_SQUARE_COLLISION(x,y,food_size,arghx[i],arghy[i],argh_size);
898 /* use coordinates for further testing */
899 foodx[index] = x;
900 foody[index] = y;
902 /* now test wether we accidently hit the worm with food ;) */
903 i = 0;
904 for (i = 0; i < worm_count && !collisionDetected; i++) {
906 collisionDetected = worm_food_collision(&worms[i], index);
910 while (collisionDetected);
911 return;
915 * Clears a food from the lcd buffer.
916 * @param int index The index of the food arrays under which
917 * the coordinates of the desired food can be found. Ensure
918 * that the value is 0 <= index <= MAX_FOOD.
920 static void clear_food(int index)
922 /* remove the old food from the screen */
923 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
924 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
925 foody[index] + FIELD_RECT_Y,
926 food_size, food_size);
927 rb->lcd_set_drawmode(DRMODE_SOLID);
931 * Draws a food in the lcd buffer.
932 * @param int index The index of the food arrays under which
933 * the coordinates of the desired food can be found. Ensure
934 * that the value is 0 <= index <= MAX_FOOD.
936 static void draw_food(int index)
938 /* draw the food object */
939 #ifdef HAVE_LCD_COLOR
940 rb->lcd_set_foreground(COLOR_FOOD);
941 #endif
942 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X,
943 foody[index] + FIELD_RECT_Y,
944 food_size, food_size);
945 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
946 rb->lcd_fillrect(foodx[index] + FIELD_RECT_X + 1,
947 foody[index] + FIELD_RECT_Y + 1,
948 food_size - 2, food_size - 2);
949 rb->lcd_set_drawmode(DRMODE_SOLID);
950 #ifdef HAVE_LCD_COLOR
951 rb->lcd_set_foreground(COLOR_FG);
952 #endif
956 * Find new coordinates for the argh stored in arghx[index], arghy[index]
957 * that don't collide with any other food or argh.
958 * @param int index
959 * Ensure that 0 <= index < argh_count < MAX_ARGH.
961 static void make_argh(int index)
963 int x = -1;
964 int y = -1;
965 bool collisionDetected = false;
966 int i;
968 do {
969 /* make coordinates for a new argh so that
970 the entire food lies within the FIELD */
971 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
972 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
973 collisionDetected = false;
974 /* Ensure that the new argh doesn't intersect with any
975 existing foods or arghs.
976 If the new argh hit any existing
977 argh or food an intersection is detected.
980 for (i=0; i<MAX_FOOD && !collisionDetected; i++) {
981 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,foodx[i],foody[i],food_size);
983 for (i=0; i<argh_count && !collisionDetected; i++) {
984 collisionDetected = CHECK_SQUARE_COLLISION(x,y,argh_size,arghx[i],arghy[i],argh_size);
987 /* use the candidate coordinates to make a real argh */
988 arghx[index] = x;
989 arghy[index] = y;
991 /* now test wether we accidently hit the worm with argh ;) */
992 for (i = 0; i < worm_count && !collisionDetected; i++) {
993 collisionDetected |= worm_argh_collision(&worms[i], index);
994 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
995 MIN_ARGH_DIST);
998 while (collisionDetected);
999 return;
1003 * Draws an argh in the lcd buffer.
1004 * @param int index The index of the argh arrays under which
1005 * the coordinates of the desired argh can be found. Ensure
1006 * that the value is 0 <= index < argh_count <= MAX_ARGH.
1008 static void draw_argh(int index)
1010 /* draw the new argh */
1011 #ifdef HAVE_LCD_COLOR
1012 rb->lcd_set_foreground(COLOR_ARGH);
1013 #endif
1014 rb->lcd_fillrect(arghx[index] + FIELD_RECT_X,
1015 arghy[index] + FIELD_RECT_Y,
1016 argh_size, argh_size);
1017 #ifdef HAVE_LCD_COLOR
1018 rb->lcd_set_foreground(COLOR_FG);
1019 #endif
1022 static void virtual_player(struct worm *w);
1024 * Initialzes the specified worm with INITIAL_WORM_LENGTH
1025 * and the tail at the specified position. The worm will
1026 * be initialized alive and creeping EAST.
1027 * @param struct worm *w The worm that is to be initialized
1028 * @param int x The x coordinate at which the tail of the worm starts.
1029 * x must be 0 <= x < FIELD_RECT_WIDTH.
1030 * @param int y The y coordinate at which the tail of the worm starts
1031 * y must be 0 <= y < FIELD_RECT_WIDTH.
1033 static void init_worm(struct worm *w, int x, int y)
1035 /* initialize the worm size */
1036 w->head = 1;
1037 w->tail = 0;
1039 w->x[w->head] = x + 1;
1040 w->y[w->head] = y;
1042 w->x[w->tail] = x;
1043 w->y[w->tail] = y;
1045 /* set the initial direction the worm creeps to */
1046 w->dirx = 1;
1047 w->diry = 0;
1049 w->growing = INITIAL_WORM_LENGTH - 1;
1050 w->alive = true;
1051 w->fetch_worm_direction = virtual_player;
1055 * Writes the direction that was stored for
1056 * human player 1 into the specified worm. This function
1057 * may be used to be stored in worm.fetch_worm_direction.
1058 * The value of
1059 * the direction is read from player1_dir.
1060 * @param struct worm *w - The worm of which the direction
1061 * is altered.
1063 static void human_player1(struct worm *w) {
1064 set_worm_dir(w, player1_dir);
1068 * Writes the direction that was stored for
1069 * human player 2 into the specified worm. This function
1070 * may be used to be stored in worm.fetch_worm_direction.
1071 * The value of
1072 * the direction is read from player2_dir.
1073 * @param struct worm *w - The worm of which the direction
1074 * is altered.
1076 static void human_player2(struct worm *w) {
1077 set_worm_dir(w, player2_dir);
1081 * Writes the direction that was stored for
1082 * human player using a remote control
1083 * into the specified worm. This function
1084 * may be used to be stored in worm.fetch_worm_direction.
1085 * The value of
1086 * the direction is read from player3_dir.
1087 * @param struct worm *w - The worm of which the direction
1088 * is altered.
1090 static void remote_player(struct worm *w) {
1091 set_worm_dir(w, player3_dir);
1095 * Initializes the worm-, food- and argh-arrays, draws a frame,
1096 * makes some food and argh and display all that stuff.
1098 static void init_wormlet(void)
1100 int i;
1102 for (i = 0; i< worm_count; i++) {
1103 /* Initialize all the worm coordinates to center. */
1104 int x = (int)(FIELD_RECT_WIDTH / 2);
1105 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
1107 init_worm(&worms[i], x, y);
1110 player1_dir = EAST;
1111 player2_dir = EAST;
1112 player3_dir = EAST;
1114 if (players > 0) {
1115 worms[0].fetch_worm_direction = human_player1;
1118 if (players > 1) {
1119 if (use_remote) {
1120 worms[1].fetch_worm_direction = remote_player;
1121 } else {
1122 worms[1].fetch_worm_direction = human_player2;
1126 if (players > 2) {
1127 worms[2].fetch_worm_direction = human_player2;
1130 /* Needed when the game is restarted using BTN_STOPRESET */
1131 rb->lcd_clear_display();
1133 /* make and display some food and argh */
1134 argh_count = MAX_FOOD;
1135 for (i = 0; i < MAX_FOOD; i++) {
1136 make_food(i);
1137 draw_food(i);
1138 make_argh(i);
1139 draw_argh(i);
1142 /* draw the game field */
1143 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1144 rb->lcd_fillrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
1145 rb->lcd_fillrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
1146 rb->lcd_set_drawmode(DRMODE_SOLID);
1148 /* make everything visible */
1149 rb->lcd_update();
1154 * Move the worm one step further if it is alive.
1155 * The direction in which the worm moves is taken from dirx and diry.
1156 * move_worm decreases growing if > 0. While the worm is growing the tail
1157 * is left untouched.
1158 * @param struct worm *w The worm to move. w must not be NULL.
1160 static void move_worm(struct worm *w)
1162 if (w->alive) {
1163 /* determine the head point and its precessor */
1164 int headx = w->x[w->head];
1165 int heady = w->y[w->head];
1166 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
1167 int preheadx = w->x[prehead];
1168 int preheady = w->y[prehead];
1170 /* determine the old direction */
1171 int olddirx;
1172 int olddiry;
1173 if (headx == preheadx) {
1174 olddirx = 0;
1175 olddiry = (heady > preheady) ? 1 : -1;
1176 } else {
1177 olddiry = 0;
1178 olddirx = (headx > preheadx) ? 1 : -1;
1181 /* olddir == dir?
1182 a change of direction means a new segment
1183 has been opened */
1184 if (olddirx != w->dirx ||
1185 olddiry != w->diry) {
1186 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
1189 /* new head position */
1190 w->x[w->head] = headx + w->dirx;
1191 w->y[w->head] = heady + w->diry;
1194 /* while the worm is growing no tail procession is necessary */
1195 if (w->growing > 0) {
1196 /* update the worms grow state */
1197 w->growing--;
1200 /* if the worm isn't growing the tail has to be dragged */
1201 else {
1202 /* index of the end of the tail segment */
1203 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
1205 /* drag the end of the tail */
1206 /* only one coordinate has to be altered. Here it is
1207 determined which one */
1208 int dir = 0; /* specifies wether the coord has to be in- or decreased */
1209 if (w->x[w->tail] == w->x[tail_segment_end]) {
1210 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
1211 w->y[w->tail] += dir;
1212 } else {
1213 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
1214 w->x[w->tail] += dir;
1217 /* when the tail has been dragged so far that it meets
1218 the next segment start the tail segment is obsolete and
1219 must be freed */
1220 if (w->x[w->tail] == w->x[tail_segment_end] &&
1221 w->y[w->tail] == w->y[tail_segment_end]){
1223 /* drop the last tail point */
1224 w->tail = tail_segment_end;
1231 * Draws the head and clears the tail of the worm in
1232 * the display buffer. lcd_update() is NOT called thus
1233 * the caller has to take care that the buffer is displayed.
1235 static void draw_worm(struct worm *w)
1237 /* draw the new head */
1238 int x = w->x[w->head];
1239 int y = w->y[w->head];
1240 #ifdef HAVE_LCD_COLOR
1241 rb->lcd_set_foreground(COLOR_WORM);
1242 #endif
1243 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1244 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1247 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1249 /* clear the space behind the worm */
1250 x = w->x[w->tail] ;
1251 y = w->y[w->tail] ;
1252 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
1253 rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1255 rb->lcd_set_drawmode(DRMODE_SOLID);
1256 #ifdef HAVE_LCD_COLOR
1257 rb->lcd_set_foreground(COLOR_FG);
1258 #endif
1262 * Checks wether the coordinate is part of the worm. Returns
1263 * true if any part of the worm was hit - including the head.
1264 * @param x int The x coordinate
1265 * @param y int The y coordinate
1266 * @return int The index of the worm arrays that contain x, y.
1267 * Returns -1 if the coordinates are not part of the worm.
1269 static int specific_worm_collision(struct worm *w, int x, int y)
1271 int retVal = -1;
1273 /* get_worm_array_length is expensive -> buffer the value */
1274 int wormLength = get_worm_array_length(w);
1275 int i;
1277 /* test each entry that is part of the worm */
1278 for (i = 0; i < wormLength && retVal == -1; i++) {
1280 /* The iteration iterates the length of the worm.
1281 Here's the conversion to the true indices within the worm arrays. */
1282 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
1283 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
1284 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
1285 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
1286 if (samex || samey){
1287 int test, min, max, tmp;
1289 if (samey) {
1290 min = w->x[linestart];
1291 max = w->x[lineend];
1292 test = x;
1293 } else {
1294 min = w->y[linestart];
1295 max = w->y[lineend];
1296 test = y;
1299 tmp = min;
1300 min = MIN(min, max);
1301 max = MAX(tmp, max);
1303 if (min <= test && test <= max) {
1304 retVal = lineend;
1308 return retVal;
1312 * Increases the length of the specified worm by marking
1313 * that it may grow by len pixels. Note that the worm has
1314 * to move to make the growing happen.
1315 * @param worm *w The worm that is to be altered.
1316 * @param int len A positive value specifying the amount of
1317 * pixels the worm may grow.
1319 static void add_growing(struct worm *w, int len) {
1320 w->growing += len;
1324 * Determins the worm that is at the coordinates x, y. The parameter
1325 * w is a switch parameter that changes the functionality of worm_collision.
1326 * If w is specified and x,y hits the head of w NULL is returned.
1327 * This is a useful way to determine wether the head of w hits
1328 * any worm but including itself but excluding its own head.
1329 * (It hits always its own head ;))
1330 * If w is set to NULL worm_collision returns any worm including all heads
1331 * that is at position of x,y.
1332 * @param struct worm *w The worm of which the head should be excluded in
1333 * the test. w may be set to NULL.
1334 * @param int x The x coordinate that is checked
1335 * @param int y The y coordinate that is checkec
1336 * @return struct worm* The worm that has been hit by x,y. If no worm
1337 * was at the position NULL is returned.
1339 static struct worm* worm_collision(struct worm *w, int x, int y)
1341 struct worm *retVal = NULL;
1342 int i;
1343 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
1344 int collision_at = specific_worm_collision(&worms[i], x, y);
1345 if (collision_at != -1) {
1346 if (!(w == &worms[i] && collision_at == w->head)){
1347 retVal = &worms[i];
1351 return retVal;
1355 * Returns true if the head of the worm just has
1356 * crossed the field boundaries.
1357 * @return bool true if the worm just has wrapped.
1359 static bool field_collision(struct worm *w)
1361 bool retVal = false;
1362 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
1363 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
1364 (w->x[w->head] < 0) ||
1365 (w->y[w->head] < 0))
1367 retVal = true;
1369 return retVal;
1374 * Returns true if the specified coordinates are within the
1375 * field specified by the FIELD_RECT_XXX constants.
1376 * @param int x The x coordinate of the point that is investigated
1377 * @param int y The y coordinate of the point that is investigated
1378 * @return bool Returns false if x,y specifies a point outside the
1379 * field of worms.
1381 static bool is_in_field_rect(int x, int y)
1383 bool retVal = false;
1384 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
1385 y >= 0 && y < FIELD_RECT_HEIGHT);
1386 return retVal;
1390 * Checks and returns wether the head of the w
1391 * is colliding with something currently.
1392 * @return int One of the values:
1393 * COLLISION_NONE
1394 * COLLISION_w
1395 * COLLISION_FOOD
1396 * COLLISION_ARGH
1397 * COLLISION_FIELD
1399 static int check_collision(struct worm *w)
1401 int retVal = COLLISION_NONE;
1403 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
1404 retVal = COLLISION_WORM;
1406 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
1407 retVal = COLLISION_FOOD;
1409 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
1410 retVal = COLLISION_ARGH;
1412 if (field_collision(w))
1413 retVal = COLLISION_FIELD;
1415 return retVal;
1419 * Returns the index of the food that is closest to the point
1420 * specified by x, y. This index may be used in the foodx and
1421 * foody arrays.
1422 * @param int x The x coordinate of the point
1423 * @param int y The y coordinate of the point
1424 * @return int A value usable as index in foodx and foody.
1426 static int get_nearest_food(int x, int y)
1428 int nearestfood = 0;
1429 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1430 int deltax = 0;
1431 int deltay = 0;
1432 int foodindex;
1433 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1434 int distance;
1435 deltax = foodx[foodindex] - x;
1436 deltay = foody[foodindex] - y;
1437 deltax = deltax > 0 ? deltax : deltax * (-1);
1438 deltay = deltay > 0 ? deltay : deltay * (-1);
1439 distance = deltax + deltay;
1441 if (distance < olddistance) {
1442 olddistance = distance;
1443 nearestfood = foodindex;
1446 return nearestfood;
1450 * Returns wether the specified position is next to the worm
1451 * and in the direction the worm looks. Use this method to
1452 * test wether this position would be hit with the next move of
1453 * the worm unless the worm changes its direction.
1454 * @param struct worm *w - The worm to be investigated
1455 * @param int x - The x coordinate of the position to test.
1456 * @param int y - The y coordinate of the position to test.
1457 * @return Returns true if the worm will hit the position unless
1458 * it change its direction before the next move.
1460 static bool is_in_front_of_worm(struct worm *w, int x, int y)
1462 bool infront = false;
1463 int deltax = x - w->x[w->head];
1464 int deltay = y - w->y[w->head];
1466 if (w->dirx == 0) {
1467 infront = (w->diry * deltay) > 0;
1468 } else {
1469 infront = (w->dirx * deltax) > 0;
1471 return infront;
1475 * Returns true if the worm will collide with the next move unless
1476 * it changes its direction.
1477 * @param struct worm *w - The worm to be investigated.
1478 * @return Returns true if the worm will collide with the next move
1479 * unless it changes its direction.
1481 static bool will_worm_collide(struct worm *w)
1483 int x = w->x[w->head] + w->dirx;
1484 int y = w->y[w->head] + w->diry;
1485 bool retVal = !is_in_field_rect(x, y);
1486 if (!retVal) {
1487 retVal = (argh_collision(x, y) != -1);
1490 if (!retVal) {
1491 retVal = (worm_collision(w, x, y) != NULL);
1493 return retVal;
1497 * This function
1498 * may be used to be stored in worm.fetch_worm_direction for
1499 * worms that are not controlled by humans but by artificial stupidity.
1500 * A direction is searched that doesn't lead to collision but to the nearest
1501 * food - but not very intelligent. The direction is written to the specified
1502 * worm.
1503 * @param struct worm *w - The worm of which the direction
1504 * is altered.
1506 static void virtual_player(struct worm *w)
1508 bool isright;
1509 int plana, planb, planc;
1510 /* find the next lunch */
1511 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1513 /* determine in which direction it is */
1515 /* in front of me? */
1516 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1518 /* left right of me? */
1519 int olddir = get_worm_dir(w);
1520 set_worm_dir(w, (olddir + 1) % 4);
1521 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1522 set_worm_dir(w, olddir);
1524 /* detect situation, set strategy */
1525 if (infront) {
1526 if (isright) {
1527 plana = olddir;
1528 planb = (olddir + 1) % 4;
1529 planc = (olddir + 3) % 4;
1530 } else {
1531 plana = olddir;
1532 planb = (olddir + 3) % 4;
1533 planc = (olddir + 1) % 4;
1535 } else {
1536 if (isright) {
1537 plana = (olddir + 1) % 4;
1538 planb = olddir;
1539 planc = (olddir + 3) % 4;
1540 } else {
1541 plana = (olddir + 3) % 4;
1542 planb = olddir;
1543 planc = (olddir + 1) % 4;
1547 /* test for collision */
1548 set_worm_dir(w, plana);
1549 if (will_worm_collide(w)){
1551 /* plan b */
1552 set_worm_dir(w, planb);
1554 /* test for collision */
1555 if (will_worm_collide(w)) {
1557 /* plan c */
1558 set_worm_dir(w, planc);
1564 * prints out the score board with all the status information
1565 * about the game.
1567 static void score_board(void)
1569 int i;
1570 int y = 0;
1571 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1572 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, 0,
1573 LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1574 rb->lcd_set_drawmode(DRMODE_SOLID);
1575 for (i = 0; i < worm_count; i++) {
1576 int score = get_score(&worms[i]);
1577 int collision = check_collision(&worms[i]);
1578 const char *state_str;
1580 /* high score */
1581 if (worms[i].fetch_worm_direction != virtual_player){
1582 if (highscore < score) {
1583 highscore = score;
1587 /* worm state */
1588 if (collision == COLLISION_NONE) {
1589 if (worms[i].growing > 0)
1590 state_str = "Growing";
1591 else {
1592 state_str = worms[i].alive ? "Hungry" : "Wormed";
1594 } else {
1595 state_str = state_desc[collision];
1598 /* length */
1599 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, y , "Len:%d", score);
1600 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, y+8, state_str);
1602 if (!worms[i].alive){
1603 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1604 rb->lcd_fillrect(FIELD_RECT_WIDTH + 2, y,
1605 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1606 rb->lcd_set_drawmode(DRMODE_SOLID);
1608 y += 19;
1610 #ifdef DEBUG_WORMLET
1611 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, "ticks %d", max_cycle);
1612 #else
1613 rb->lcd_putsxyf(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, "Hs: %d", highscore);
1614 #endif
1618 * Checks for collisions of the worm and its environment and
1619 * takes appropriate actions like growing the worm or killing it.
1620 * @return bool Returns true if the worm is dead. Returns
1621 * false if the worm is healthy, up and creeping.
1623 static bool process_collisions(struct worm *w)
1625 int index = -1;
1627 w->alive &= !field_collision(w);
1629 if (w->alive) {
1631 /* check if food was eaten */
1632 index = food_collision(w->x[w->head], w->y[w->head]);
1633 if (index != -1){
1634 int i;
1636 clear_food(index);
1637 make_food(index);
1638 draw_food(index);
1640 for (i = 0; i < arghs_per_food; i++) {
1641 argh_count++;
1642 if (argh_count > MAX_ARGH)
1643 argh_count = MAX_ARGH;
1644 make_argh(argh_count - 1);
1645 draw_argh(argh_count - 1);
1648 add_growing(w, worm_food);
1650 draw_worm(w);
1653 /* check if argh was eaten */
1654 else {
1655 index = argh_collision(w->x[w->head], w->y[w->head]);
1656 if (index != -1) {
1657 w->alive = false;
1659 else {
1660 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1661 w->alive = false;
1666 return !w->alive;
1670 * The main loop of the game.
1671 * @return bool Returns true if the game ended
1672 * with a dead worm. Returns false if the user
1673 * aborted the game manually.
1675 static int run(void)
1677 int button = 0;
1678 int wormDead = false;
1679 bool paused = false;
1681 /* ticks are counted to compensate speed variations */
1682 long cycle_start = 0, cycle_end = 0;
1683 #ifdef DEBUG_WORMLET
1684 int ticks_to_max_cycle_reset = 20;
1685 max_cycle = 0;
1686 #endif
1688 /* initialize the board and so on */
1689 init_wormlet();
1691 cycle_start = *rb->current_tick;
1692 /* change the direction of the worm */
1693 while (!wormDead)
1695 int i;
1696 long cycle_duration=0;
1698 #ifdef HAS_BUTTON_HOLD
1699 if (rb->button_hold())
1700 paused = true;
1701 #endif
1703 switch (button) {
1704 case BTN_STARTPAUSE:
1705 paused = !paused;
1706 break;
1707 case BTN_STOPRESET:
1708 if (paused)
1709 return 1; /* restart game */
1710 else
1711 paused = true;
1712 break;
1713 #ifdef BTN_RC_QUIT
1714 case BTN_RC_QUIT:
1715 #endif
1716 case BTN_QUIT:
1717 return 2; /* back to menu */
1718 break;
1720 if (!paused)
1722 switch (button) {
1723 case BTN_DIR_UP:
1724 if (players == 1 && !use_remote) {
1725 player1_dir = NORTH;
1727 break;
1729 case BTN_DIR_DOWN:
1730 if (players == 1 && !use_remote) {
1731 player1_dir = SOUTH;
1733 break;
1735 case BTN_DIR_LEFT:
1736 if (players != 1 || use_remote) {
1737 player1_dir = (player1_dir + 3) % 4;
1738 } else {
1739 player1_dir = WEST;
1741 break;
1743 case BTN_DIR_RIGHT:
1744 if (players != 1 || use_remote) {
1745 player1_dir = (player1_dir + 1) % 4;
1746 } else {
1747 player1_dir = EAST;
1749 break;
1751 #ifdef MULTIPLAYER
1752 case BTN_PLAYER2_DIR1:
1753 player2_dir = (player2_dir + 3) % 4;
1754 break;
1756 case BTN_PLAYER2_DIR2:
1757 player2_dir = (player2_dir + 1) % 4;
1758 break;
1759 #endif
1761 #ifdef REMOTE
1762 case BTN_RC_UP:
1763 player3_dir = (player3_dir + 1) % 4;
1764 break;
1766 case BTN_RC_DOWN:
1767 player3_dir = (player3_dir + 3) % 4;
1768 break;
1769 #endif
1773 for (i = 0; i < worm_count; i++) {
1774 worms[i].fetch_worm_direction(&worms[i]);
1777 wormDead = true;
1778 for (i = 0; i < worm_count; i++){
1779 struct worm *w = &worms[i];
1780 move_worm(w);
1781 wormDead &= process_collisions(w);
1782 draw_worm(w);
1784 score_board();
1785 rb->lcd_update();
1786 if (button == BTN_STOPRESET) {
1787 wormDead = true;
1790 /* here the wormlet game cycle ends
1791 thus the current tick is stored
1792 as end time */
1793 cycle_end = *rb->current_tick;
1795 /* The duration of the game cycle */
1796 cycle_duration = cycle_end - cycle_start;
1797 cycle_duration = MAX(0, cycle_duration);
1798 cycle_duration = MIN(speed -1, cycle_duration);
1801 #ifdef DEBUG_WORMLET
1802 ticks_to_max_cycle_reset--;
1803 if (ticks_to_max_cycle_reset <= 0) {
1804 max_cycle = 0;
1807 if (max_cycle < cycle_duration) {
1808 max_cycle = cycle_duration;
1809 ticks_to_max_cycle_reset = 20;
1811 #endif
1813 /* adjust the number of ticks to wait for a button.
1814 This ensures that a complete game cycle including
1815 user input runs in constant time */
1816 button = rb->button_get_w_tmo(speed - cycle_duration);
1817 cycle_start = *rb->current_tick;
1820 rb->splash(HZ*2, "Game Over!");
1822 return 2; /* back to menu */
1825 #ifdef DEBUG_WORMLET
1828 * Just a test routine that checks that worm_food_collision works
1829 * in some typical situations.
1831 static void test_worm_food_collision(void)
1833 int collision_count = 0;
1834 int i;
1835 rb->lcd_clear_display();
1836 init_worm(&worms[0], 10, 10);
1837 add_growing(&worms[0], 10);
1838 set_worm_dir(&worms[0], EAST);
1839 for (i = 0; i < 10; i++) {
1840 move_worm(&worms[0]);
1841 draw_worm(&worms[0]);
1844 set_worm_dir(&worms[0], SOUTH);
1845 for (i = 0; i < 10; i++) {
1846 move_worm(&worms[0]);
1847 draw_worm(&worms[0]);
1850 foodx[0] = 15;
1851 foody[0] = 12;
1852 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1853 bool collision;
1854 draw_worm(&worms[0]);
1855 draw_food(0);
1856 collision = worm_food_collision(&worms[0], 0);
1857 if (collision) {
1858 collision_count++;
1860 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count);
1861 rb->lcd_update();
1863 if (collision_count != food_size) {
1864 rb->button_get(true);
1868 foody[0] = 15;
1869 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1870 bool collision;
1871 draw_worm(&worms[0]);
1872 draw_food(0);
1873 collision = worm_food_collision(&worms[0], 0);
1874 if (collision) {
1875 collision_count ++;
1877 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count);
1878 rb->lcd_update();
1880 if (collision_count != food_size * 2) {
1881 rb->button_get(true);
1886 static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh)
1888 int x, y;
1889 bool retVal = false;
1890 for (x = rx; x < rx + rw; x++){
1891 for (y = ry; y < ry + rh; y++) {
1892 if (specific_worm_collision(w, x, y) != -1) {
1893 retVal = true;
1897 return retVal;
1900 static void test_worm_argh_collision(void)
1902 int i;
1903 int dir;
1904 int collision_count = 0;
1905 rb->lcd_clear_display();
1906 init_worm(&worms[0], 10, 10);
1907 add_growing(&worms[0], 40);
1908 for (dir = 0; dir < 4; dir++) {
1909 set_worm_dir(&worms[0], (EAST + dir) % 4);
1910 for (i = 0; i < 10; i++) {
1911 move_worm(&worms[0]);
1912 draw_worm(&worms[0]);
1916 arghx[0] = 12;
1917 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - argh_size; arghy[0]++){
1918 bool collision;
1919 draw_argh(0);
1920 collision = worm_argh_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 != argh_size * 2) {
1928 rb->button_get(true);
1931 arghy[0] = 12;
1932 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - argh_size; arghx[0]++){
1933 bool collision;
1934 draw_argh(0);
1935 collision = worm_argh_collision(&worms[0], 0);
1936 if (collision) {
1937 collision_count ++;
1939 rb->lcd_putsxyf(0, LCD_HEIGHT -8, "collisions: %d", collision_count);
1940 rb->lcd_update();
1942 if (collision_count != argh_size * 4) {
1943 rb->button_get(true);
1947 static int testline_in_rect(void)
1949 int testfailed = -1;
1951 int rx = 10;
1952 int ry = 15;
1953 int rw = 20;
1954 int rh = 25;
1956 /* Test 1 */
1957 int x1 = 12;
1958 int y1 = 8;
1959 int x2 = 12;
1960 int y2 = 42;
1962 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1963 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1964 rb->lcd_drawrect(rx, ry, rw, rh);
1965 rb->lcd_drawline(x1, y1, x2, y2);
1966 rb->lcd_update();
1967 rb->lcd_putsxy(0, 0, "failed 1");
1968 rb->button_get(true);
1969 testfailed = 1;
1972 /* test 2 */
1973 y2 = 20;
1974 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1975 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1976 rb->lcd_drawrect(rx, ry, rw, rh);
1977 rb->lcd_drawline(x1, y1, x2, y2);
1978 rb->lcd_putsxy(0, 0, "failed 2");
1979 rb->lcd_update();
1980 rb->button_get(true);
1981 testfailed = 2;
1984 /* test 3 */
1985 y1 = 30;
1986 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1987 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1988 rb->lcd_drawrect(rx, ry, rw, rh);
1989 rb->lcd_drawline(x1, y1, x2, y2);
1990 rb->lcd_putsxy(0, 0, "failed 3");
1991 rb->lcd_update();
1992 rb->button_get(true);
1993 testfailed = 3;
1996 /* test 4 */
1997 y2 = 45;
1998 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1999 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2000 rb->lcd_drawrect(rx, ry, rw, rh);
2001 rb->lcd_drawline(x1, y1, x2, y2);
2002 rb->lcd_putsxy(0, 0, "failed 4");
2003 rb->lcd_update();
2004 rb->button_get(true);
2005 testfailed = 4;
2008 /* test 5 */
2009 y1 = 50;
2010 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2011 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2012 rb->lcd_drawrect(rx, ry, rw, rh);
2013 rb->lcd_drawline(x1, y1, x2, y2);
2014 rb->lcd_putsxy(0, 0, "failed 5");
2015 rb->lcd_update();
2016 rb->button_get(true);
2017 testfailed = 5;
2020 /* test 6 */
2021 y1 = 5;
2022 y2 = 7;
2023 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2024 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2025 rb->lcd_drawrect(rx, ry, rw, rh);
2026 rb->lcd_drawline(x1, y1, x2, y2);
2027 rb->lcd_putsxy(0, 0, "failed 6");
2028 rb->lcd_update();
2029 rb->button_get(true);
2030 testfailed = 6;
2033 /* test 7 */
2034 x1 = 8;
2035 y1 = 20;
2036 x2 = 35;
2037 y2 = 20;
2038 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2039 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2040 rb->lcd_drawrect(rx, ry, rw, rh);
2041 rb->lcd_drawline(x1, y1, x2, y2);
2042 rb->lcd_putsxy(0, 0, "failed 7");
2043 rb->lcd_update();
2044 rb->button_get(true);
2045 testfailed = 7;
2048 /* test 8 */
2049 x2 = 12;
2050 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2051 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2052 rb->lcd_drawrect(rx, ry, rw, rh);
2053 rb->lcd_drawline(x1, y1, x2, y2);
2054 rb->lcd_putsxy(0, 0, "failed 8");
2055 rb->lcd_update();
2056 rb->button_get(true);
2057 testfailed = 8;
2060 /* test 9 */
2061 x1 = 25;
2062 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2063 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2064 rb->lcd_drawrect(rx, ry, rw, rh);
2065 rb->lcd_drawline(x1, y1, x2, y2);
2066 rb->lcd_putsxy(0, 0, "failed 9");
2067 rb->lcd_update();
2068 rb->button_get(true);
2069 testfailed = 9;
2072 /* test 10 */
2073 x2 = 37;
2074 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2075 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2076 rb->lcd_drawrect(rx, ry, rw, rh);
2077 rb->lcd_drawline(x1, y1, x2, y2);
2078 rb->lcd_putsxy(0, 0, "failed 10");
2079 rb->lcd_update();
2080 rb->button_get(true);
2081 testfailed = 10;
2084 /* test 11 */
2085 x1 = 42;
2086 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2087 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2088 rb->lcd_drawrect(rx, ry, rw, rh);
2089 rb->lcd_drawline(x1, y1, x2, y2);
2090 rb->lcd_putsxy(0, 0, "failed 11");
2091 rb->lcd_update();
2092 rb->button_get(true);
2093 testfailed = 11;
2096 /* test 12 */
2097 x1 = 5;
2098 x2 = 7;
2099 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
2100 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
2101 rb->lcd_drawrect(rx, ry, rw, rh);
2102 rb->lcd_drawline(x1, y1, x2, y2);
2103 rb->lcd_putsxy(0, 0, "failed 12");
2104 rb->lcd_update();
2105 rb->button_get(true);
2106 testfailed = 12;
2109 /* test 13 */
2110 rx = 9;
2111 ry = 15;
2112 rw = food_size;
2113 rh = food_size;
2115 x1 = 10;
2116 y1 = 10;
2117 x2 = 10;
2118 y2 = 20;
2119 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2120 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2121 rb->lcd_drawrect(rx, ry, rw, rh);
2122 rb->lcd_drawline(x1, y1, x2, y2);
2123 rb->lcd_putsxy(0, 0, "failed 13");
2124 rb->lcd_update();
2125 rb->button_get(true);
2126 testfailed = 13;
2129 /* test 14 */
2130 rx = 9;
2131 ry = 15;
2132 rw = 4;
2133 rh = 4;
2135 x1 = 10;
2136 y1 = 10;
2137 x2 = 10;
2138 y2 = 19;
2139 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
2140 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
2141 rb->lcd_drawline(x1, y1, x2, y2);
2142 rb->lcd_invertrect(rx, ry, rw, rh);
2143 rb->lcd_putsxy(0, 0, "failed 14");
2144 rb->lcd_update();
2145 rb->button_get(true);
2146 testfailed = 14;
2149 rb->lcd_clear_display();
2151 return testfailed;
2155 * Just a test routine to test wether specific_worm_collision might work properly
2157 static int test_specific_worm_collision(void)
2159 int collisions = 0;
2160 int dir;
2161 int x = 0;
2162 int y = 0;
2163 rb->lcd_clear_display();
2164 init_worm(&worms[0], 10, 20);
2165 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
2167 for (dir = EAST; dir < EAST + 4; dir++) {
2168 int i;
2169 set_worm_dir(&worms[0], dir % 4);
2170 for (i = 0; i < 5; i++) {
2171 if (!(dir % 4 == NORTH && i == 9)) {
2172 move_worm(&worms[0]);
2173 draw_worm(&worms[0]);
2178 for (y = 15; y < 30; y ++){
2179 for (x = 5; x < 20; x++) {
2180 if (specific_worm_collision(&worms[0], x, y) != -1) {
2181 collisions ++;
2183 rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
2184 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "collisions %d", collisions);
2185 rb->lcd_update();
2188 if (collisions != 21) {
2189 rb->button_get(true);
2191 return collisions;
2194 static void test_make_argh(void)
2196 int dir;
2197 int seed = 0;
2198 int hit = 0;
2199 int failures = 0;
2200 int last_failures = 0;
2201 int i, worm_idx;
2202 rb->lcd_clear_display();
2203 worm_count = 3;
2205 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2206 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
2207 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
2210 for (dir = EAST; dir < EAST + 4; dir++) {
2211 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
2212 set_worm_dir(&worms[worm_idx], dir % 4);
2213 for (i = 0; i < 10; i++) {
2214 if (!(dir % 4 == NORTH && i == 9)) {
2215 move_worm(&worms[worm_idx]);
2216 draw_worm(&worms[worm_idx]);
2222 rb->lcd_update();
2224 for (seed = 0; hit < 20; seed += 2) {
2225 int x, y;
2226 rb->srand(seed);
2227 x = rb->rand() % (FIELD_RECT_WIDTH - argh_size);
2228 y = rb->rand() % (FIELD_RECT_HEIGHT - argh_size);
2230 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
2231 if (expensive_worm_in_rect(&worms[worm_idx], x, y, argh_size, argh_size)) {
2232 int tries = 0;
2233 rb->srand(seed);
2235 tries = make_argh(0);
2236 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
2237 failures ++;
2240 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "(%d;%d) fail%d try%d",
2241 x, y, failures, tries);
2242 rb->lcd_update();
2243 rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y,
2244 argh_size, argh_size);
2245 rb->lcd_update();
2246 draw_argh(0);
2247 rb->lcd_update();
2248 rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y,
2249 argh_size, argh_size);
2250 rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y,
2251 argh_size, argh_size);
2253 if (failures > last_failures) {
2254 rb->button_get(true);
2256 last_failures = failures;
2257 hit ++;
2263 static void test_worm_argh_collision_in_moves(void) {
2264 int hit_count = 0;
2265 int i;
2266 rb->lcd_clear_display();
2267 init_worm(&worms[0], 10, 20);
2269 arghx[0] = 20;
2270 arghy[0] = 18;
2271 draw_argh(0);
2273 set_worm_dir(&worms[0], EAST);
2274 for (i = 0; i < 20; i++) {
2275 move_worm(&worms[0]);
2276 draw_worm(&worms[0]);
2277 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
2278 hit_count ++;
2280 rb->lcd_putsxyf(0, LCD_HEIGHT - 8, "in 5 moves hits: %d", hit_count);
2281 rb->lcd_update();
2283 if (hit_count != argh_size + 5) {
2284 rb->button_get(true);
2287 #endif /* DEBUG_WORMLET */
2290 * Reverts default settings
2292 static void default_settings(void)
2294 arghs_per_food = ARGHS_PER_FOOD;
2295 argh_size = ARGH_SIZE;
2296 food_size = FOOD_SIZE;
2297 speed = SPEED;
2298 worm_food = WORM_PER_FOOD;
2299 players = 1;
2300 worm_count = MAX_WORMS;
2301 use_remote = false;
2302 return;
2306 * Launches the wormlet game
2308 static bool launch_wormlet(void)
2310 int game_result = 1;
2312 rb->lcd_clear_display();
2314 /* Turn off backlight timeout */
2315 backlight_force_on(); /* backlight control in lib/helper.c */
2317 /* start the game */
2318 while (game_result == 1)
2319 game_result = run();
2321 switch (game_result)
2323 case 2:
2324 /* Turn on backlight timeout (revert to settings) */
2325 backlight_use_settings(); /* backlight control in lib/helper.c */
2326 return false;
2327 break;
2329 return false;
2333 * Main entry point
2335 enum plugin_status plugin_start(const void* parameter)
2337 int result;
2338 int menu_quit = 0;
2339 int new_setting;
2341 (void)(parameter);
2343 default_settings();
2344 if (configfile_load(SETTINGS_FILENAME, config,
2345 sizeof(config)/sizeof(*config),
2346 SETTINGS_MIN_VERSION ) < 0)
2348 /* If the loading failed, save a new config file (as the disk is
2349 already spinning) */
2350 configfile_save(SETTINGS_FILENAME, config,
2351 sizeof(config)/sizeof(*config),
2352 SETTINGS_VERSION);
2355 #ifdef HAVE_LCD_COLOR
2356 rb->lcd_set_foreground(COLOR_FG);
2357 rb->lcd_set_background(COLOR_BG);
2358 #endif
2360 #if LCD_DEPTH > 1
2361 rb->lcd_set_backdrop(NULL);
2362 #endif
2364 #ifdef DEBUG_WORMLET
2365 testline_in_rect();
2366 test_worm_argh_collision_in_moves();
2367 test_make_argh();
2368 test_worm_food_collision();
2369 test_worm_argh_collision();
2370 test_specific_worm_collision();
2371 #endif
2373 /* Setup screen */
2375 static const struct opt_items noyes[2] = {
2376 { "No", -1 },
2377 { "Yes", -1 },
2380 static const struct opt_items remoteonly_option[1] = {
2381 { "Remote Control", -1 }
2384 static const struct opt_items key24_option[2] = {
2385 { "4 Key Control", -1 },
2386 { "2 Key Control", -1 }
2389 #ifdef REMOTE
2390 static const struct opt_items remote_option[2] = {
2391 { "Remote Control", -1 },
2392 { "No Rem. Control", -1 }
2394 #else
2395 static const struct opt_items key2_option[1] = {
2396 { "2 Key Control", -1 }
2398 #endif
2400 static const struct opt_items nokey_option[1] = {
2401 { "Out of Control", -1 }
2404 MENUITEM_STRINGLIST(menu, "Wormlet Menu", NULL, "Play Wormlet!",
2405 "Number of Worms", "Number of Players", "Control Style",
2406 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2407 "Argh Size","Food Size","Revert to Default Settings",
2408 "Playback Control", "Quit");
2410 rb->button_clear_queue();
2412 while (!menu_quit) {
2413 switch(rb->do_menu(&menu, &result, NULL, false))
2415 case 0:
2416 rb->lcd_setfont(FONT_SYSFIXED);
2417 launch_wormlet();
2418 break;
2419 case 1:
2420 rb->set_int("Number of Worms", "", UNIT_INT, &worm_count, NULL,
2421 1, 1, 3, NULL);
2422 if (worm_count < players) {
2423 worm_count = players;
2425 break;
2426 case 2:
2427 #ifdef MULTIPLAYER
2428 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2429 1, 0, 4, NULL);
2430 #else
2431 rb->set_int("Number of Players", "", UNIT_INT, &players, NULL,
2432 1, 0, 2, NULL);
2433 #endif
2434 if (players > worm_count) {
2435 worm_count = players;
2437 if (players > 2) {
2438 use_remote = true;
2440 break;
2441 case 3:
2442 switch(players) {
2443 case 0:
2444 rb->set_option("Control Style",&use_remote,INT,
2445 nokey_option, 1, NULL);
2446 break;
2447 case 1:
2448 rb->set_option("Control Style",&use_remote,INT,
2449 key24_option, 2, NULL);
2450 break;
2451 case 2:
2452 #ifdef REMOTE
2453 rb->set_option("Control Style",&use_remote,INT,
2454 remote_option, 2, NULL);
2455 #else
2456 rb->set_option("Control Style",&use_remote,INT,
2457 key2_option, 1, NULL);
2458 #endif
2459 break;
2460 case 3:
2461 rb->set_option("Control Style",&use_remote,INT,
2462 remoteonly_option, 1, NULL);
2463 break;
2465 break;
2466 case 4:
2467 rb->set_int("Worm Growth Per Food", "", UNIT_INT, &worm_food,
2468 NULL, 1, 0, 15, NULL);
2469 break;
2470 case 5:
2471 new_setting = 20 - speed;
2472 rb->set_int("Worm Speed", "", UNIT_INT, &new_setting,
2473 NULL, 1, 0, 20, NULL);
2474 speed = 20 - new_setting;
2475 break;
2476 case 6:
2477 rb->set_int("Arghs Per Food", "", UNIT_INT, &arghs_per_food,
2478 NULL, 1, 0, 8, NULL);
2479 break;
2480 case 7:
2481 rb->set_int("Argh Size", "", UNIT_INT, &argh_size,
2482 NULL, 1, 2, 10, NULL);
2483 break;
2484 case 8:
2485 rb->set_int("Food Size", "", UNIT_INT, &food_size,
2486 NULL, 1, 2, 10, NULL);
2487 break;
2488 case 9:
2489 new_setting = 0;
2490 rb->set_option("Reset Settings?", &new_setting, INT, noyes , 2, NULL);
2491 if (new_setting == 1)
2492 default_settings();
2493 break;
2494 case 10:
2495 playback_control(NULL);
2496 break;
2497 default:
2498 menu_quit=1;
2499 break;
2503 configfile_save(SETTINGS_FILENAME, config,
2504 sizeof(config)/sizeof(*config),
2505 SETTINGS_VERSION);
2507 return PLUGIN_OK;