1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
22 #include "lib/configfile.h"
23 #include "lib/helper.h"
24 #include "lib/playback_control.h"
27 static long max_cycle
;
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 */
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
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_REC
287 #define BTN_DIR_DOWN BUTTON_PLAY
288 #define BTN_DIR_LEFT BUTTON_REW
289 #define BTN_DIR_RIGHT BUTTON_FF
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_MENU
299 #define BTN_DIR_RIGHT BUTTON_ENTER
300 #define BTN_STARTPAUSE BUTTON_PLAY
301 #define BTN_QUIT BUTTON_REC
302 #define BTN_STOPRESET (BUTTON_MENU | 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
325 #error No keymap defined!
328 #ifdef HAVE_TOUCHSCREEN
330 #define BTN_DIR_UP BUTTON_TOPMIDDLE
333 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
336 #define BTN_DIR_LEFT BUTTON_MIDLEFT
338 #ifndef BTN_DIR_RIGHT
339 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
341 #ifndef BTN_STARTPAUSE
342 #define BTN_STARTPAUSE BUTTON_CENTER
345 #define BTN_QUIT BUTTON_TOPLEFT
347 #ifndef BTN_STOPRESET
348 #define BTN_STOPRESET BUTTON_TOPRIGHT
352 #if (LCD_WIDTH == 96) && (LCD_HEIGHT == 96)
356 #define MAX_WORM_SEGMENTS 128
357 #elif (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
361 #define MAX_WORM_SEGMENTS 128
362 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
366 #define MAX_WORM_SEGMENTS 128
367 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
371 #define MAX_WORM_SEGMENTS 128
372 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
376 #define MAX_WORM_SEGMENTS 128
377 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
381 #define MAX_WORM_SEGMENTS 128
382 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
386 #define MAX_WORM_SEGMENTS 128
387 #elif ((LCD_WIDTH == 160) && (LCD_HEIGHT == 128)) || \
388 ((LCD_WIDTH == 128) && (LCD_HEIGHT == 160))
392 #define MAX_WORM_SEGMENTS 256
393 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
397 #define MAX_WORM_SEGMENTS 256
398 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
402 #define MAX_WORM_SEGMENTS 512
403 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
407 #define MAX_WORM_SEGMENTS 512
408 #elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
409 ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
413 #define MAX_WORM_SEGMENTS 512
414 #elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
415 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
419 #define MAX_WORM_SEGMENTS 512
422 #ifdef HAVE_LCD_COLOR
423 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
424 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
425 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
426 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
427 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
430 #define CHECK_SQUARE_COLLISION(x1,y1,s1,x2,y2,s2) (x1+s1>x2)&&(x2+s2>x1)&&(y1+s1>y2)&&(y2+s2>y1)
433 * All the properties that a worm has.
436 /* The worm is stored in a ring of xy coordinates */
437 int x
[MAX_WORM_SEGMENTS
];
438 int y
[MAX_WORM_SEGMENTS
];
440 int head
; /* index of the head within the buffer */
441 int tail
; /* index of the tail within the buffer */
442 int growing
; /* number of cyles the worm still keeps growing */
443 bool alive
; /* the worms living state */
445 /* direction vector in which the worm moves */
446 int dirx
; /* only values -1 0 1 allowed */
447 int diry
; /* only values -1 0 1 allowed */
449 /* this method is used to fetch the direction the user
450 has selected. It can be one of the values
451 human_player1, human_player2, remote_player, virtual_player.
452 All these values are fuctions, that can change the direction
454 void (*fetch_worm_direction
)(struct worm
*w
);
457 /* stores the highscore - besides it was scored by a virtual player */
458 static int highscore
;
460 #define MAX_FOOD 5 /* maximal number of food items */
462 /* The arrays store the food coordinates */
463 static int foodx
[MAX_FOOD
];
464 static int foody
[MAX_FOOD
];
466 #define MAX_ARGH 100 /* maximal number of argh items */
467 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
469 /* The arrays store the argh coordinates */
470 static int arghx
[MAX_ARGH
];
471 static int arghy
[MAX_ARGH
];
473 /* the number of arghs that are currently in use */
474 static int argh_count
;
476 /* the number of arghs per food, settable by user */
477 static int arghs_per_food
= ARGHS_PER_FOOD
;
478 /* the size of the argh, settable by user */
479 static int argh_size
= ARGH_SIZE
;
480 /* the size of the food, settable by user */
481 static int food_size
= FOOD_SIZE
;
482 /* the speed of the worm, settable by user */
483 static int speed
= SPEED
;
484 /* the amount a worm grows by eating a food, settable by user */
485 static int worm_food
= WORM_PER_FOOD
;
487 /* End additional variables */
489 /* the number of active worms (dead or alive) */
490 static int worm_count
= MAX_WORMS
;
492 /* in multiplayer mode: en- / disables the remote worm control
493 in singleplayer mode: toggles 4 / 2 button worm control */
494 static bool use_remote
= false;
496 /* return values of check_collision */
497 #define COLLISION_NONE 0
498 #define COLLISION_WORM 1
499 #define COLLISION_FOOD 2
500 #define COLLISION_ARGH 3
501 #define COLLISION_FIELD 4
503 static const char *const state_desc
[] = {
504 [COLLISION_NONE
] = NULL
,
505 [COLLISION_WORM
] = "Wormed",
506 [COLLISION_FOOD
] = "Growing",
507 [COLLISION_ARGH
] = "Argh",
508 [COLLISION_FIELD
] = "Crashed",
511 /* constants for use as directions.
512 Note that the values are ordered clockwise.
513 Thus increasing / decreasing the values
514 is equivalent to right / left turns. */
520 /* direction of human player 1 */
521 static int player1_dir
= EAST
;
522 /* direction of human player 2 */
523 static int player2_dir
= EAST
;
524 /* direction of human player 3 */
525 static int player3_dir
= EAST
;
527 /* the number of (human) players that currently
529 static int players
= 1;
531 #define SETTINGS_VERSION 1
532 #define SETTINGS_MIN_VERSION 1
533 #define SETTINGS_FILENAME "wormlet.cfg"
535 static struct configdata config
[] =
537 {TYPE_INT
, 0, 1024, { .int_p
= &highscore
}, "highscore", NULL
},
538 {TYPE_INT
, 0, 15, { .int_p
= &arghs_per_food
}, "arghs per food", NULL
},
539 {TYPE_INT
, 0, 15, { .int_p
= &argh_size
}, "argh size", NULL
},
540 {TYPE_INT
, 0, 15, { .int_p
= &food_size
}, "food size", NULL
},
541 {TYPE_INT
, 0, 3, { .int_p
= &players
}, "players", NULL
},
542 {TYPE_INT
, 0, 3, { .int_p
= &worm_count
}, "worms", NULL
},
543 {TYPE_INT
, 0, 20, { .int_p
= &speed
}, "speed", NULL
},
544 {TYPE_INT
, 0, 15, { .int_p
= &worm_food
}, "Worm Growth Per Food", NULL
}
548 * Returns the direction id in which the worm
549 * currently is creeping.
550 * @param struct worm *w The worm that is to be investigated.
551 * w Must not be null.
552 * @return int A value 0 <= value < 4
553 * Note the predefined constants NORTH, SOUTH, EAST, WEST
555 static int get_worm_dir(struct worm
*w
)
575 * Set the direction of the specified worm with a direction id.
576 * Increasing the value by 1 means to turn the worm direction
577 * to right by 90 degree.
578 * @param struct worm *w The worm that is to be altered. w Must not be null.
579 * @param int dir The new direction in which the worm is to creep.
580 * dir must be 0 <= dir < 4. Use predefined constants
581 * NORTH, SOUTH, EAST, WEST
583 static void set_worm_dir(struct worm
*w
, int dir
)
606 * Returns the current length of the worm array. This
607 * is also a value for the number of bends that are in the worm.
608 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
610 static int get_worm_array_length(struct worm
*w
)
612 /* initial simple calculation will be overwritten if wrong. */
613 int retVal
= w
->head
- w
->tail
;
615 /* if the worm 'crosses' the boundaries of the ringbuffer */
617 retVal
= w
->head
+ MAX_WORM_SEGMENTS
- w
->tail
;
624 * Returns the score the specified worm. The score is the length
626 * @param struct worm *w The worm that is to be investigated.
627 * w must not be null.
628 * @return int The length of the worm (>= 0).
630 static int get_score(struct worm
*w
)
633 int length
= get_worm_array_length(w
);
635 for (i
= 0; i
< length
; i
++) {
637 /* The iteration iterates the length of the worm.
638 Here's the conversion to the true indices within the worm arrays. */
639 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
640 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
641 int startx
= w
->x
[linestart
];
642 int starty
= w
->y
[linestart
];
643 int endx
= w
->x
[lineend
];
644 int endy
= w
->y
[lineend
];
646 int minimum
, maximum
;
648 if (startx
== endx
) {
649 minimum
= MIN(starty
, endy
);
650 maximum
= MAX(starty
, endy
);
652 minimum
= MIN(startx
, endx
);
653 maximum
= MAX(startx
, endx
);
655 retval
+= abs(maximum
- minimum
);
661 * Determines wether the line specified by startx, starty, endx, endy intersects
662 * the rectangle specified by x, y, width, height. Note that the line must be exactly
663 * horizontal or vertical (startx == endx or starty == endy).
664 * @param int startx The x coordinate of the start point of the line.
665 * @param int starty The y coordinate of the start point of the line.
666 * @param int endx The x coordinate of the end point of the line.
667 * @param int endy The y coordinate of the end point of the line.
668 * @param int x The x coordinate of the top left corner of the rectangle.
669 * @param int y The y coordinate of the top left corner of the rectangle.
670 * @param int width The width of the rectangle.
671 * @param int height The height of the rectangle.
672 * @return bool Returns true if the specified line intersects with the recangle.
674 static bool line_in_rect(int startx
, int starty
, int endx
, int endy
,
675 int x
, int y
, int width
, int height
)
678 int simple
, simplemin
, simplemax
;
679 int compa
, compb
, compmin
, compmax
;
681 if (startx
== endx
) {
684 simplemax
= x
+ width
;
689 compmax
= y
+ height
;
693 simplemax
= y
+ height
;
702 compa
= MIN(compa
, compb
);
703 compb
= MAX(temp
, compb
);
705 if (simplemin
<= simple
&& simple
<= simplemax
) {
706 if ((compmin
<= compa
&& compa
<= compmax
) ||
707 (compmin
<= compb
&& compb
<= compmax
) ||
708 (compa
<= compmin
&& compb
>= compmax
)) {
716 * Tests wether the specified worm intersects with the rect.
717 * @param struct worm *w The worm to be investigated
718 * @param int x The x coordinate of the top left corner of the rect
719 * @param int y The y coordinate of the top left corner of the rect
720 * @param int widht The width of the rect
721 * @param int height The height of the rect
722 * @return bool Returns true if the worm intersects with the rect
724 static bool worm_in_rect(struct worm
*w
, int x
, int y
, int width
, int height
)
729 /* get_worm_array_length is expensive -> buffer the value */
730 int wormLength
= get_worm_array_length(w
);
733 /* test each entry that is part of the worm */
734 for (i
= 0; i
< wormLength
&& retval
== false; i
++) {
736 /* The iteration iterates the length of the worm.
737 Here's the conversion to the true indices within the worm arrays. */
738 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
739 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
740 int startx
= w
->x
[linestart
];
741 int starty
= w
->y
[linestart
];
742 int endx
= w
->x
[lineend
];
743 int endy
= w
->y
[lineend
];
745 retval
= line_in_rect(startx
, starty
, endx
, endy
, x
, y
, width
, height
);
752 * Checks wether a specific food in the food arrays is at the
753 * specified coordinates.
754 * @param int foodIndex The index of the food in the food arrays
755 * @param int x the x coordinate.
756 * @param int y the y coordinate.
757 * @return Returns true if the coordinate hits the food specified by
760 static bool specific_food_collision(int foodIndex
, int x
, int y
)
763 if (x
>= foodx
[foodIndex
] &&
764 x
< foodx
[foodIndex
] + food_size
&&
765 y
>= foody
[foodIndex
] &&
766 y
< foody
[foodIndex
] + food_size
) {
774 * Returns the index of the food that is at the
775 * given coordinates. If no food is at the coordinates
777 * @return int -1 <= value < MAX_FOOD
779 static int food_collision(int x
, int y
)
783 for (i
= 0; i
< MAX_FOOD
; i
++) {
784 if (specific_food_collision(i
, x
, y
)) {
793 * Checks wether a specific argh in the argh arrays is at the
794 * specified coordinates.
795 * @param int arghIndex The index of the argh in the argh arrays
796 * @param int x the x coordinate.
797 * @param int y the y coordinate.
798 * @return Returns true if the coordinate hits the argh specified by
801 static bool specific_argh_collision(int arghIndex
, int x
, int y
)
803 if ( x
>= arghx
[arghIndex
] &&
804 y
>= arghy
[arghIndex
] &&
805 x
< arghx
[arghIndex
] + argh_size
&&
806 y
< arghy
[arghIndex
] + argh_size
)
815 * Returns the index of the argh that is at the
816 * given coordinates. If no argh is at the coordinates
818 * @param int x The x coordinate.
819 * @param int y The y coordinate.
820 * @return int -1 <= value < argh_count <= MAX_ARGH
822 static int argh_collision(int x
, int y
)
827 /* search for the argh that has the specified coords */
828 for (i
= 0; i
< argh_count
; i
++) {
829 if (specific_argh_collision(i
, x
, y
)) {
838 * Checks wether the worm collides with the food at the specfied food-arrays.
839 * @param int foodIndex The index of the food in the arrays. Ensure the value is
840 * 0 <= foodIndex <= MAX_FOOD
841 * @return Returns true if the worm collides with the specified food.
843 static bool worm_food_collision(struct worm
*w
, int foodIndex
)
847 retVal
= worm_in_rect(w
, foodx
[foodIndex
], foody
[foodIndex
],
848 food_size
- 1, food_size
- 1);
854 * Returns true if the worm hits the argh within the next moves (unless
855 * the worm changes it's direction).
856 * @param struct worm *w - The worm to investigate
857 * @param int argh_idx - The index of the argh
858 * @param int moves - The number of moves that are considered.
859 * @return Returns false if the specified argh is not hit within the next
862 static bool worm_argh_collision_in_moves(struct worm
*w
, int argh_idx
, int moves
)
869 x2
= w
->x
[w
->head
] + moves
* w
->dirx
;
870 y2
= w
->y
[w
->head
] + moves
* w
->diry
;
872 retVal
= line_in_rect(x1
, y1
, x2
, y2
, arghx
[argh_idx
], arghy
[argh_idx
],
873 argh_size
, argh_size
);
878 * Checks wether the worm collides with the argh at the specfied argh-arrays.
879 * @param int arghIndex The index of the argh in the arrays.
880 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
881 * @return Returns true if the worm collides with the specified argh.
883 static bool worm_argh_collision(struct worm
*w
, int arghIndex
)
887 retVal
= worm_in_rect(w
, arghx
[arghIndex
], arghy
[arghIndex
],
888 argh_size
- 1, argh_size
- 1);
894 * Find new coordinates for the food stored in foodx[index], foody[index]
895 * that don't collide with any other food or argh
897 * Ensure that 0 <= index < MAX_FOOD.
899 static void make_food(int index
)
903 bool collisionDetected
= false;
907 /* make coordinates for a new food so that
908 the entire food lies within the FIELD */
909 x
= rb
->rand() % (FIELD_RECT_WIDTH
- food_size
);
910 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- food_size
);
911 collisionDetected
= false;
912 /* Ensure that the new food doesn't collide with any
913 existing foods or arghs.
914 If the new food hit any existing
915 argh or food a collision is detected.
918 for (i
=0; i
<MAX_FOOD
&& !collisionDetected
; i
++) {
919 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,food_size
,foodx
[i
],foody
[i
],food_size
);
921 for (i
=0; i
<argh_count
&& !collisionDetected
; i
++) {
922 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,food_size
,arghx
[i
],arghy
[i
],argh_size
);
925 /* use coordinates for further testing */
929 /* now test wether we accidently hit the worm with food ;) */
931 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
933 collisionDetected
= worm_food_collision(&worms
[i
], index
);
937 while (collisionDetected
);
942 * Clears a food from the lcd buffer.
943 * @param int index The index of the food arrays under which
944 * the coordinates of the desired food can be found. Ensure
945 * that the value is 0 <= index <= MAX_FOOD.
947 static void clear_food(int index
)
949 /* remove the old food from the screen */
950 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
951 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
952 foody
[index
] + FIELD_RECT_Y
,
953 food_size
, food_size
);
954 rb
->lcd_set_drawmode(DRMODE_SOLID
);
958 * Draws a food in the lcd buffer.
959 * @param int index The index of the food arrays under which
960 * the coordinates of the desired food can be found. Ensure
961 * that the value is 0 <= index <= MAX_FOOD.
963 static void draw_food(int index
)
965 /* draw the food object */
966 #ifdef HAVE_LCD_COLOR
967 rb
->lcd_set_foreground(COLOR_FOOD
);
969 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
970 foody
[index
] + FIELD_RECT_Y
,
971 food_size
, food_size
);
972 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
973 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
+ 1,
974 foody
[index
] + FIELD_RECT_Y
+ 1,
975 food_size
- 2, food_size
- 2);
976 rb
->lcd_set_drawmode(DRMODE_SOLID
);
977 #ifdef HAVE_LCD_COLOR
978 rb
->lcd_set_foreground(COLOR_FG
);
983 * Find new coordinates for the argh stored in arghx[index], arghy[index]
984 * that don't collide with any other food or argh.
986 * Ensure that 0 <= index < argh_count < MAX_ARGH.
988 static void make_argh(int index
)
992 bool collisionDetected
= false;
996 /* make coordinates for a new argh so that
997 the entire food lies within the FIELD */
998 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
999 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
1000 collisionDetected
= false;
1001 /* Ensure that the new argh doesn't intersect with any
1002 existing foods or arghs.
1003 If the new argh hit any existing
1004 argh or food an intersection is detected.
1007 for (i
=0; i
<MAX_FOOD
&& !collisionDetected
; i
++) {
1008 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,argh_size
,foodx
[i
],foody
[i
],food_size
);
1010 for (i
=0; i
<argh_count
&& !collisionDetected
; i
++) {
1011 collisionDetected
= CHECK_SQUARE_COLLISION(x
,y
,argh_size
,arghx
[i
],arghy
[i
],argh_size
);
1014 /* use the candidate coordinates to make a real argh */
1018 /* now test wether we accidently hit the worm with argh ;) */
1019 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
1020 collisionDetected
|= worm_argh_collision(&worms
[i
], index
);
1021 collisionDetected
|= worm_argh_collision_in_moves(&worms
[i
], index
,
1025 while (collisionDetected
);
1030 * Draws an argh in the lcd buffer.
1031 * @param int index The index of the argh arrays under which
1032 * the coordinates of the desired argh can be found. Ensure
1033 * that the value is 0 <= index < argh_count <= MAX_ARGH.
1035 static void draw_argh(int index
)
1037 /* draw the new argh */
1038 #ifdef HAVE_LCD_COLOR
1039 rb
->lcd_set_foreground(COLOR_ARGH
);
1041 rb
->lcd_fillrect(arghx
[index
] + FIELD_RECT_X
,
1042 arghy
[index
] + FIELD_RECT_Y
,
1043 argh_size
, argh_size
);
1044 #ifdef HAVE_LCD_COLOR
1045 rb
->lcd_set_foreground(COLOR_FG
);
1049 static void virtual_player(struct worm
*w
);
1051 * Initialzes the specified worm with INITIAL_WORM_LENGTH
1052 * and the tail at the specified position. The worm will
1053 * be initialized alive and creeping EAST.
1054 * @param struct worm *w The worm that is to be initialized
1055 * @param int x The x coordinate at which the tail of the worm starts.
1056 * x must be 0 <= x < FIELD_RECT_WIDTH.
1057 * @param int y The y coordinate at which the tail of the worm starts
1058 * y must be 0 <= y < FIELD_RECT_WIDTH.
1060 static void init_worm(struct worm
*w
, int x
, int y
)
1062 /* initialize the worm size */
1066 w
->x
[w
->head
] = x
+ 1;
1072 /* set the initial direction the worm creeps to */
1076 w
->growing
= INITIAL_WORM_LENGTH
- 1;
1078 w
->fetch_worm_direction
= virtual_player
;
1082 * Writes the direction that was stored for
1083 * human player 1 into the specified worm. This function
1084 * may be used to be stored in worm.fetch_worm_direction.
1086 * the direction is read from player1_dir.
1087 * @param struct worm *w - The worm of which the direction
1090 static void human_player1(struct worm
*w
) {
1091 set_worm_dir(w
, player1_dir
);
1095 * Writes the direction that was stored for
1096 * human player 2 into the specified worm. This function
1097 * may be used to be stored in worm.fetch_worm_direction.
1099 * the direction is read from player2_dir.
1100 * @param struct worm *w - The worm of which the direction
1103 static void human_player2(struct worm
*w
) {
1104 set_worm_dir(w
, player2_dir
);
1108 * Writes the direction that was stored for
1109 * human player using a remote control
1110 * into the specified worm. This function
1111 * may be used to be stored in worm.fetch_worm_direction.
1113 * the direction is read from player3_dir.
1114 * @param struct worm *w - The worm of which the direction
1117 static void remote_player(struct worm
*w
) {
1118 set_worm_dir(w
, player3_dir
);
1122 * Initializes the worm-, food- and argh-arrays, draws a frame,
1123 * makes some food and argh and display all that stuff.
1125 static void init_wormlet(void)
1129 for (i
= 0; i
< worm_count
; i
++) {
1130 /* Initialize all the worm coordinates to center. */
1131 int x
= (int)(FIELD_RECT_WIDTH
/ 2);
1132 int y
= (int)((FIELD_RECT_HEIGHT
- 20)/ 2) + i
* 10;
1134 init_worm(&worms
[i
], x
, y
);
1142 worms
[0].fetch_worm_direction
= human_player1
;
1147 worms
[1].fetch_worm_direction
= remote_player
;
1149 worms
[1].fetch_worm_direction
= human_player2
;
1154 worms
[2].fetch_worm_direction
= human_player2
;
1157 /* Needed when the game is restarted using BTN_STOPRESET */
1158 rb
->lcd_clear_display();
1160 /* make and display some food and argh */
1161 argh_count
= MAX_FOOD
;
1162 for (i
= 0; i
< MAX_FOOD
; i
++) {
1169 /* draw the game field */
1170 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1171 rb
->lcd_fillrect(0, 0, FIELD_RECT_WIDTH
+ 2, FIELD_RECT_HEIGHT
+ 2);
1172 rb
->lcd_fillrect(1, 1, FIELD_RECT_WIDTH
, FIELD_RECT_HEIGHT
);
1173 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1175 /* make everything visible */
1181 * Move the worm one step further if it is alive.
1182 * The direction in which the worm moves is taken from dirx and diry.
1183 * move_worm decreases growing if > 0. While the worm is growing the tail
1184 * is left untouched.
1185 * @param struct worm *w The worm to move. w must not be NULL.
1187 static void move_worm(struct worm
*w
)
1190 /* determine the head point and its precessor */
1191 int headx
= w
->x
[w
->head
];
1192 int heady
= w
->y
[w
->head
];
1193 int prehead
= (w
->head
+ MAX_WORM_SEGMENTS
- 1) % MAX_WORM_SEGMENTS
;
1194 int preheadx
= w
->x
[prehead
];
1195 int preheady
= w
->y
[prehead
];
1197 /* determine the old direction */
1200 if (headx
== preheadx
) {
1202 olddiry
= (heady
> preheady
) ? 1 : -1;
1205 olddirx
= (headx
> preheadx
) ? 1 : -1;
1209 a change of direction means a new segment
1211 if (olddirx
!= w
->dirx
||
1212 olddiry
!= w
->diry
) {
1213 w
->head
= (w
->head
+ 1) % MAX_WORM_SEGMENTS
;
1216 /* new head position */
1217 w
->x
[w
->head
] = headx
+ w
->dirx
;
1218 w
->y
[w
->head
] = heady
+ w
->diry
;
1221 /* while the worm is growing no tail procession is necessary */
1222 if (w
->growing
> 0) {
1223 /* update the worms grow state */
1227 /* if the worm isn't growing the tail has to be dragged */
1229 /* index of the end of the tail segment */
1230 int tail_segment_end
= (w
->tail
+ 1) % MAX_WORM_SEGMENTS
;
1232 /* drag the end of the tail */
1233 /* only one coordinate has to be altered. Here it is
1234 determined which one */
1235 int dir
= 0; /* specifies wether the coord has to be in- or decreased */
1236 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
]) {
1237 dir
= (w
->y
[w
->tail
] - w
->y
[tail_segment_end
] < 0) ? 1 : -1;
1238 w
->y
[w
->tail
] += dir
;
1240 dir
= (w
->x
[w
->tail
] - w
->x
[tail_segment_end
] < 0) ? 1 : -1;
1241 w
->x
[w
->tail
] += dir
;
1244 /* when the tail has been dragged so far that it meets
1245 the next segment start the tail segment is obsolete and
1247 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
] &&
1248 w
->y
[w
->tail
] == w
->y
[tail_segment_end
]){
1250 /* drop the last tail point */
1251 w
->tail
= tail_segment_end
;
1258 * Draws the head and clears the tail of the worm in
1259 * the display buffer. lcd_update() is NOT called thus
1260 * the caller has to take care that the buffer is displayed.
1262 static void draw_worm(struct worm
*w
)
1264 /* draw the new head */
1265 int x
= w
->x
[w
->head
];
1266 int y
= w
->y
[w
->head
];
1267 #ifdef HAVE_LCD_COLOR
1268 rb
->lcd_set_foreground(COLOR_WORM
);
1270 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1271 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1274 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1276 /* clear the space behind the worm */
1279 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1280 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1282 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1283 #ifdef HAVE_LCD_COLOR
1284 rb
->lcd_set_foreground(COLOR_FG
);
1289 * Checks wether the coordinate is part of the worm. Returns
1290 * true if any part of the worm was hit - including the head.
1291 * @param x int The x coordinate
1292 * @param y int The y coordinate
1293 * @return int The index of the worm arrays that contain x, y.
1294 * Returns -1 if the coordinates are not part of the worm.
1296 static int specific_worm_collision(struct worm
*w
, int x
, int y
)
1300 /* get_worm_array_length is expensive -> buffer the value */
1301 int wormLength
= get_worm_array_length(w
);
1304 /* test each entry that is part of the worm */
1305 for (i
= 0; i
< wormLength
&& retVal
== -1; i
++) {
1307 /* The iteration iterates the length of the worm.
1308 Here's the conversion to the true indices within the worm arrays. */
1309 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
1310 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
1311 bool samex
= (w
->x
[linestart
] == x
) && (w
->x
[lineend
] == x
);
1312 bool samey
= (w
->y
[linestart
] == y
) && (w
->y
[lineend
] == y
);
1313 if (samex
|| samey
){
1314 int test
, min
, max
, tmp
;
1317 min
= w
->x
[linestart
];
1318 max
= w
->x
[lineend
];
1321 min
= w
->y
[linestart
];
1322 max
= w
->y
[lineend
];
1327 min
= MIN(min
, max
);
1328 max
= MAX(tmp
, max
);
1330 if (min
<= test
&& test
<= max
) {
1339 * Increases the length of the specified worm by marking
1340 * that it may grow by len pixels. Note that the worm has
1341 * to move to make the growing happen.
1342 * @param worm *w The worm that is to be altered.
1343 * @param int len A positive value specifying the amount of
1344 * pixels the worm may grow.
1346 static void add_growing(struct worm
*w
, int len
) {
1351 * Determins the worm that is at the coordinates x, y. The parameter
1352 * w is a switch parameter that changes the functionality of worm_collision.
1353 * If w is specified and x,y hits the head of w NULL is returned.
1354 * This is a useful way to determine wether the head of w hits
1355 * any worm but including itself but excluding its own head.
1356 * (It hits always its own head ;))
1357 * If w is set to NULL worm_collision returns any worm including all heads
1358 * that is at position of x,y.
1359 * @param struct worm *w The worm of which the head should be excluded in
1360 * the test. w may be set to NULL.
1361 * @param int x The x coordinate that is checked
1362 * @param int y The y coordinate that is checkec
1363 * @return struct worm* The worm that has been hit by x,y. If no worm
1364 * was at the position NULL is returned.
1366 static struct worm
* worm_collision(struct worm
*w
, int x
, int y
)
1368 struct worm
*retVal
= NULL
;
1370 for (i
= 0; (i
< worm_count
) && (retVal
== NULL
); i
++) {
1371 int collision_at
= specific_worm_collision(&worms
[i
], x
, y
);
1372 if (collision_at
!= -1) {
1373 if (!(w
== &worms
[i
] && collision_at
== w
->head
)){
1382 * Returns true if the head of the worm just has
1383 * crossed the field boundaries.
1384 * @return bool true if the worm just has wrapped.
1386 static bool field_collision(struct worm
*w
)
1388 bool retVal
= false;
1389 if ((w
->x
[w
->head
] >= FIELD_RECT_WIDTH
) ||
1390 (w
->y
[w
->head
] >= FIELD_RECT_HEIGHT
) ||
1391 (w
->x
[w
->head
] < 0) ||
1392 (w
->y
[w
->head
] < 0))
1401 * Returns true if the specified coordinates are within the
1402 * field specified by the FIELD_RECT_XXX constants.
1403 * @param int x The x coordinate of the point that is investigated
1404 * @param int y The y coordinate of the point that is investigated
1405 * @return bool Returns false if x,y specifies a point outside the
1408 static bool is_in_field_rect(int x
, int y
)
1410 bool retVal
= false;
1411 retVal
= (x
>= 0 && x
< FIELD_RECT_WIDTH
&&
1412 y
>= 0 && y
< FIELD_RECT_HEIGHT
);
1417 * Checks and returns wether the head of the w
1418 * is colliding with something currently.
1419 * @return int One of the values:
1426 static int check_collision(struct worm
*w
)
1428 int retVal
= COLLISION_NONE
;
1430 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
)
1431 retVal
= COLLISION_WORM
;
1433 if (food_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1434 retVal
= COLLISION_FOOD
;
1436 if (argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1437 retVal
= COLLISION_ARGH
;
1439 if (field_collision(w
))
1440 retVal
= COLLISION_FIELD
;
1446 * Returns the index of the food that is closest to the point
1447 * specified by x, y. This index may be used in the foodx and
1449 * @param int x The x coordinate of the point
1450 * @param int y The y coordinate of the point
1451 * @return int A value usable as index in foodx and foody.
1453 static int get_nearest_food(int x
, int y
)
1455 int nearestfood
= 0;
1456 int olddistance
= FIELD_RECT_WIDTH
+ FIELD_RECT_HEIGHT
;
1460 for (foodindex
= 0; foodindex
< MAX_FOOD
; foodindex
++) {
1462 deltax
= foodx
[foodindex
] - x
;
1463 deltay
= foody
[foodindex
] - y
;
1464 deltax
= deltax
> 0 ? deltax
: deltax
* (-1);
1465 deltay
= deltay
> 0 ? deltay
: deltay
* (-1);
1466 distance
= deltax
+ deltay
;
1468 if (distance
< olddistance
) {
1469 olddistance
= distance
;
1470 nearestfood
= foodindex
;
1477 * Returns wether the specified position is next to the worm
1478 * and in the direction the worm looks. Use this method to
1479 * test wether this position would be hit with the next move of
1480 * the worm unless the worm changes its direction.
1481 * @param struct worm *w - The worm to be investigated
1482 * @param int x - The x coordinate of the position to test.
1483 * @param int y - The y coordinate of the position to test.
1484 * @return Returns true if the worm will hit the position unless
1485 * it change its direction before the next move.
1487 static bool is_in_front_of_worm(struct worm
*w
, int x
, int y
)
1489 bool infront
= false;
1490 int deltax
= x
- w
->x
[w
->head
];
1491 int deltay
= y
- w
->y
[w
->head
];
1494 infront
= (w
->diry
* deltay
) > 0;
1496 infront
= (w
->dirx
* deltax
) > 0;
1502 * Returns true if the worm will collide with the next move unless
1503 * it changes its direction.
1504 * @param struct worm *w - The worm to be investigated.
1505 * @return Returns true if the worm will collide with the next move
1506 * unless it changes its direction.
1508 static bool will_worm_collide(struct worm
*w
)
1510 int x
= w
->x
[w
->head
] + w
->dirx
;
1511 int y
= w
->y
[w
->head
] + w
->diry
;
1512 bool retVal
= !is_in_field_rect(x
, y
);
1514 retVal
= (argh_collision(x
, y
) != -1);
1518 retVal
= (worm_collision(w
, x
, y
) != NULL
);
1525 * may be used to be stored in worm.fetch_worm_direction for
1526 * worms that are not controlled by humans but by artificial stupidity.
1527 * A direction is searched that doesn't lead to collision but to the nearest
1528 * food - but not very intelligent. The direction is written to the specified
1530 * @param struct worm *w - The worm of which the direction
1533 static void virtual_player(struct worm
*w
)
1536 int plana
, planb
, planc
;
1537 /* find the next lunch */
1538 int nearestfood
= get_nearest_food(w
->x
[w
->head
], w
->y
[w
->head
]);
1540 /* determine in which direction it is */
1542 /* in front of me? */
1543 bool infront
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1545 /* left right of me? */
1546 int olddir
= get_worm_dir(w
);
1547 set_worm_dir(w
, (olddir
+ 1) % 4);
1548 isright
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1549 set_worm_dir(w
, olddir
);
1551 /* detect situation, set strategy */
1555 planb
= (olddir
+ 1) % 4;
1556 planc
= (olddir
+ 3) % 4;
1559 planb
= (olddir
+ 3) % 4;
1560 planc
= (olddir
+ 1) % 4;
1564 plana
= (olddir
+ 1) % 4;
1566 planc
= (olddir
+ 3) % 4;
1568 plana
= (olddir
+ 3) % 4;
1570 planc
= (olddir
+ 1) % 4;
1574 /* test for collision */
1575 set_worm_dir(w
, plana
);
1576 if (will_worm_collide(w
)){
1579 set_worm_dir(w
, planb
);
1581 /* test for collision */
1582 if (will_worm_collide(w
)) {
1585 set_worm_dir(w
, planc
);
1591 * prints out the score board with all the status information
1594 static void score_board(void)
1598 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1599 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, 0,
1600 LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, LCD_HEIGHT
);
1601 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1602 for (i
= 0; i
< worm_count
; i
++) {
1603 int score
= get_score(&worms
[i
]);
1604 int collision
= check_collision(&worms
[i
]);
1605 const char *state_str
;
1608 if (worms
[i
].fetch_worm_direction
!= virtual_player
){
1609 if (highscore
< score
) {
1615 if (collision
== COLLISION_NONE
) {
1616 if (worms
[i
].growing
> 0)
1617 state_str
= "Growing";
1619 state_str
= worms
[i
].alive
? "Hungry" : "Wormed";
1622 state_str
= state_desc
[collision
];
1626 rb
->lcd_putsxyf(FIELD_RECT_WIDTH
+ 3, y
, "Len:%d", score
);
1627 rb
->lcd_putsxyf(FIELD_RECT_WIDTH
+ 3, y
+8, state_str
);
1629 if (!worms
[i
].alive
){
1630 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1631 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, y
,
1632 LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, 17);
1633 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1637 #ifdef DEBUG_WORMLET
1638 rb
->lcd_putsxyf(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, "ticks %d", max_cycle
);
1640 rb
->lcd_putsxyf(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, "Hs: %d", highscore
);
1645 * Checks for collisions of the worm and its environment and
1646 * takes appropriate actions like growing the worm or killing it.
1647 * @return bool Returns true if the worm is dead. Returns
1648 * false if the worm is healthy, up and creeping.
1650 static bool process_collisions(struct worm
*w
)
1654 w
->alive
&= !field_collision(w
);
1658 /* check if food was eaten */
1659 index
= food_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1667 for (i
= 0; i
< arghs_per_food
; i
++) {
1669 if (argh_count
> MAX_ARGH
)
1670 argh_count
= MAX_ARGH
;
1671 make_argh(argh_count
- 1);
1672 draw_argh(argh_count
- 1);
1675 add_growing(w
, worm_food
);
1680 /* check if argh was eaten */
1682 index
= argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1687 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
) {
1697 * The main loop of the game.
1698 * @return bool Returns true if the game ended
1699 * with a dead worm. Returns false if the user
1700 * aborted the game manually.
1702 static int run(void)
1705 int wormDead
= false;
1706 bool paused
= false;
1708 /* ticks are counted to compensate speed variations */
1709 long cycle_start
= 0, cycle_end
= 0;
1710 #ifdef DEBUG_WORMLET
1711 int ticks_to_max_cycle_reset
= 20;
1715 /* initialize the board and so on */
1718 cycle_start
= *rb
->current_tick
;
1719 /* change the direction of the worm */
1723 long cycle_duration
=0;
1725 #ifdef HAS_BUTTON_HOLD
1726 if (rb
->button_hold())
1731 case BTN_STARTPAUSE
:
1736 return 1; /* restart game */
1744 return 2; /* back to menu */
1751 if (players
== 1 && !use_remote
) {
1752 player1_dir
= NORTH
;
1757 if (players
== 1 && !use_remote
) {
1758 player1_dir
= SOUTH
;
1763 if (players
!= 1 || use_remote
) {
1764 player1_dir
= (player1_dir
+ 3) % 4;
1771 if (players
!= 1 || use_remote
) {
1772 player1_dir
= (player1_dir
+ 1) % 4;
1779 case BTN_PLAYER2_DIR1
:
1780 player2_dir
= (player2_dir
+ 3) % 4;
1783 case BTN_PLAYER2_DIR2
:
1784 player2_dir
= (player2_dir
+ 1) % 4;
1790 player3_dir
= (player3_dir
+ 1) % 4;
1794 player3_dir
= (player3_dir
+ 3) % 4;
1800 for (i
= 0; i
< worm_count
; i
++) {
1801 worms
[i
].fetch_worm_direction(&worms
[i
]);
1805 for (i
= 0; i
< worm_count
; i
++){
1806 struct worm
*w
= &worms
[i
];
1808 wormDead
&= process_collisions(w
);
1813 if (button
== BTN_STOPRESET
) {
1817 /* here the wormlet game cycle ends
1818 thus the current tick is stored
1820 cycle_end
= *rb
->current_tick
;
1822 /* The duration of the game cycle */
1823 cycle_duration
= cycle_end
- cycle_start
;
1824 cycle_duration
= MAX(0, cycle_duration
);
1825 cycle_duration
= MIN(speed
-1, cycle_duration
);
1828 #ifdef DEBUG_WORMLET
1829 ticks_to_max_cycle_reset
--;
1830 if (ticks_to_max_cycle_reset
<= 0) {
1834 if (max_cycle
< cycle_duration
) {
1835 max_cycle
= cycle_duration
;
1836 ticks_to_max_cycle_reset
= 20;
1840 /* adjust the number of ticks to wait for a button.
1841 This ensures that a complete game cycle including
1842 user input runs in constant time */
1843 button
= rb
->button_get_w_tmo(speed
- cycle_duration
);
1844 cycle_start
= *rb
->current_tick
;
1847 rb
->splash(HZ
*2, "Game Over!");
1849 return 2; /* back to menu */
1852 #ifdef DEBUG_WORMLET
1855 * Just a test routine that checks that worm_food_collision works
1856 * in some typical situations.
1858 static void test_worm_food_collision(void)
1860 int collision_count
= 0;
1862 rb
->lcd_clear_display();
1863 init_worm(&worms
[0], 10, 10);
1864 add_growing(&worms
[0], 10);
1865 set_worm_dir(&worms
[0], EAST
);
1866 for (i
= 0; i
< 10; i
++) {
1867 move_worm(&worms
[0]);
1868 draw_worm(&worms
[0]);
1871 set_worm_dir(&worms
[0], SOUTH
);
1872 for (i
= 0; i
< 10; i
++) {
1873 move_worm(&worms
[0]);
1874 draw_worm(&worms
[0]);
1879 for (foody
[0] = 20; foody
[0] > 0; foody
[0] --) {
1881 draw_worm(&worms
[0]);
1883 collision
= worm_food_collision(&worms
[0], 0);
1887 rb
->lcd_putsxyf(0, LCD_HEIGHT
-8, "collisions: %d", collision_count
);
1890 if (collision_count
!= food_size
) {
1891 rb
->button_get(true);
1896 for (foodx
[0] = 30; foodx
[0] > 0; foodx
[0] --) {
1898 draw_worm(&worms
[0]);
1900 collision
= worm_food_collision(&worms
[0], 0);
1904 rb
->lcd_putsxyf(0, LCD_HEIGHT
-8, "collisions: %d", collision_count
);
1907 if (collision_count
!= food_size
* 2) {
1908 rb
->button_get(true);
1913 static bool expensive_worm_in_rect(struct worm
*w
, int rx
, int ry
, int rw
, int rh
)
1916 bool retVal
= false;
1917 for (x
= rx
; x
< rx
+ rw
; x
++){
1918 for (y
= ry
; y
< ry
+ rh
; y
++) {
1919 if (specific_worm_collision(w
, x
, y
) != -1) {
1927 static void test_worm_argh_collision(void)
1931 int collision_count
= 0;
1932 rb
->lcd_clear_display();
1933 init_worm(&worms
[0], 10, 10);
1934 add_growing(&worms
[0], 40);
1935 for (dir
= 0; dir
< 4; dir
++) {
1936 set_worm_dir(&worms
[0], (EAST
+ dir
) % 4);
1937 for (i
= 0; i
< 10; i
++) {
1938 move_worm(&worms
[0]);
1939 draw_worm(&worms
[0]);
1944 for (arghy
[0] = 0; arghy
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghy
[0]++){
1947 collision
= worm_argh_collision(&worms
[0], 0);
1951 rb
->lcd_putsxyf(0, LCD_HEIGHT
-8, "collisions: %d", collision_count
);
1954 if (collision_count
!= argh_size
* 2) {
1955 rb
->button_get(true);
1959 for (arghx
[0] = 0; arghx
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghx
[0]++){
1962 collision
= worm_argh_collision(&worms
[0], 0);
1966 rb
->lcd_putsxyf(0, LCD_HEIGHT
-8, "collisions: %d", collision_count
);
1969 if (collision_count
!= argh_size
* 4) {
1970 rb
->button_get(true);
1974 static int testline_in_rect(void)
1976 int testfailed
= -1;
1989 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1990 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1991 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1992 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1994 rb
->lcd_putsxy(0, 0, "failed 1");
1995 rb
->button_get(true);
2001 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2002 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2003 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2004 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2005 rb
->lcd_putsxy(0, 0, "failed 2");
2007 rb
->button_get(true);
2013 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2014 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2015 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2016 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2017 rb
->lcd_putsxy(0, 0, "failed 3");
2019 rb
->button_get(true);
2025 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2026 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2027 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2028 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2029 rb
->lcd_putsxy(0, 0, "failed 4");
2031 rb
->button_get(true);
2037 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2038 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2039 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2040 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2041 rb
->lcd_putsxy(0, 0, "failed 5");
2043 rb
->button_get(true);
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 6");
2056 rb
->button_get(true);
2065 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2066 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2067 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2068 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2069 rb
->lcd_putsxy(0, 0, "failed 7");
2071 rb
->button_get(true);
2077 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2078 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2079 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2080 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2081 rb
->lcd_putsxy(0, 0, "failed 8");
2083 rb
->button_get(true);
2089 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2090 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2091 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2092 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2093 rb
->lcd_putsxy(0, 0, "failed 9");
2095 rb
->button_get(true);
2101 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2102 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2103 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2104 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2105 rb
->lcd_putsxy(0, 0, "failed 10");
2107 rb
->button_get(true);
2113 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2114 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2115 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2116 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2117 rb
->lcd_putsxy(0, 0, "failed 11");
2119 rb
->button_get(true);
2126 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2127 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2128 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2129 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2130 rb
->lcd_putsxy(0, 0, "failed 12");
2132 rb
->button_get(true);
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 13");
2152 rb
->button_get(true);
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_drawline(x1
, y1
, x2
, y2
);
2169 rb
->lcd_invertrect(rx
, ry
, rw
, rh
);
2170 rb
->lcd_putsxy(0, 0, "failed 14");
2172 rb
->button_get(true);
2176 rb
->lcd_clear_display();
2182 * Just a test routine to test wether specific_worm_collision might work properly
2184 static int test_specific_worm_collision(void)
2190 rb
->lcd_clear_display();
2191 init_worm(&worms
[0], 10, 20);
2192 add_growing(&worms
[0], 20 - INITIAL_WORM_LENGTH
);
2194 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2196 set_worm_dir(&worms
[0], dir
% 4);
2197 for (i
= 0; i
< 5; i
++) {
2198 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2199 move_worm(&worms
[0]);
2200 draw_worm(&worms
[0]);
2205 for (y
= 15; y
< 30; y
++){
2206 for (x
= 5; x
< 20; x
++) {
2207 if (specific_worm_collision(&worms
[0], x
, y
) != -1) {
2210 rb
->lcd_invertpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
2211 rb
->lcd_putsxyf(0, LCD_HEIGHT
- 8, "collisions %d", collisions
);
2215 if (collisions
!= 21) {
2216 rb
->button_get(true);
2221 static void test_make_argh(void)
2227 int last_failures
= 0;
2229 rb
->lcd_clear_display();
2232 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2233 init_worm(&worms
[worm_idx
], 10 + worm_idx
* 20, 20);
2234 add_growing(&worms
[worm_idx
], 40 - INITIAL_WORM_LENGTH
);
2237 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2238 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2239 set_worm_dir(&worms
[worm_idx
], dir
% 4);
2240 for (i
= 0; i
< 10; i
++) {
2241 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2242 move_worm(&worms
[worm_idx
]);
2243 draw_worm(&worms
[worm_idx
]);
2251 for (seed
= 0; hit
< 20; seed
+= 2) {
2254 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
2255 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
2257 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++){
2258 if (expensive_worm_in_rect(&worms
[worm_idx
], x
, y
, argh_size
, argh_size
)) {
2262 tries
= make_argh(0);
2263 if ((x
== arghx
[0] && y
== arghy
[0]) || tries
< 2) {
2267 rb
->lcd_putsxyf(0, LCD_HEIGHT
- 8, "(%d;%d) fail%d try%d",
2268 x
, y
, failures
, tries
);
2270 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
,
2271 argh_size
, argh_size
);
2275 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
,
2276 argh_size
, argh_size
);
2277 rb
->lcd_clearrect(arghx
[0] + FIELD_RECT_X
, arghy
[0] + FIELD_RECT_Y
,
2278 argh_size
, argh_size
);
2280 if (failures
> last_failures
) {
2281 rb
->button_get(true);
2283 last_failures
= failures
;
2290 static void test_worm_argh_collision_in_moves(void) {
2293 rb
->lcd_clear_display();
2294 init_worm(&worms
[0], 10, 20);
2300 set_worm_dir(&worms
[0], EAST
);
2301 for (i
= 0; i
< 20; i
++) {
2302 move_worm(&worms
[0]);
2303 draw_worm(&worms
[0]);
2304 if (worm_argh_collision_in_moves(&worms
[0], 0, 5)){
2307 rb
->lcd_putsxyf(0, LCD_HEIGHT
- 8, "in 5 moves hits: %d", hit_count
);
2310 if (hit_count
!= argh_size
+ 5) {
2311 rb
->button_get(true);
2314 #endif /* DEBUG_WORMLET */
2317 * Reverts default settings
2319 static void default_settings(void)
2321 arghs_per_food
= ARGHS_PER_FOOD
;
2322 argh_size
= ARGH_SIZE
;
2323 food_size
= FOOD_SIZE
;
2325 worm_food
= WORM_PER_FOOD
;
2327 worm_count
= MAX_WORMS
;
2333 * Launches the wormlet game
2335 static bool launch_wormlet(void)
2337 int game_result
= 1;
2339 rb
->lcd_clear_display();
2341 /* Turn off backlight timeout */
2342 backlight_ignore_timeout();
2344 /* start the game */
2345 while (game_result
== 1)
2346 game_result
= run();
2348 switch (game_result
)
2351 /* Turn on backlight timeout (revert to settings) */
2352 backlight_use_settings();
2362 enum plugin_status
plugin_start(const void* parameter
)
2371 if (configfile_load(SETTINGS_FILENAME
, config
,
2372 sizeof(config
)/sizeof(*config
),
2373 SETTINGS_MIN_VERSION
) < 0)
2375 /* If the loading failed, save a new config file (as the disk is
2376 already spinning) */
2377 configfile_save(SETTINGS_FILENAME
, config
,
2378 sizeof(config
)/sizeof(*config
),
2382 #ifdef HAVE_LCD_COLOR
2383 rb
->lcd_set_foreground(COLOR_FG
);
2384 rb
->lcd_set_background(COLOR_BG
);
2388 rb
->lcd_set_backdrop(NULL
);
2391 #ifdef DEBUG_WORMLET
2393 test_worm_argh_collision_in_moves();
2395 test_worm_food_collision();
2396 test_worm_argh_collision();
2397 test_specific_worm_collision();
2402 static const struct opt_items noyes
[2] = {
2407 static const struct opt_items remoteonly_option
[1] = {
2408 { "Remote Control", -1 }
2411 static const struct opt_items key24_option
[2] = {
2412 { "4 Key Control", -1 },
2413 { "2 Key Control", -1 }
2417 static const struct opt_items remote_option
[2] = {
2418 { "Remote Control", -1 },
2419 { "No Rem. Control", -1 }
2422 static const struct opt_items key2_option
[1] = {
2423 { "2 Key Control", -1 }
2427 static const struct opt_items nokey_option
[1] = {
2428 { "Out of Control", -1 }
2431 MENUITEM_STRINGLIST(menu
, "Wormlet Menu", NULL
, "Play Wormlet!",
2432 "Number of Worms", "Number of Players", "Control Style",
2433 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2434 "Argh Size","Food Size","Revert to Default Settings",
2435 "Playback Control", "Quit");
2437 rb
->button_clear_queue();
2439 while (!menu_quit
) {
2440 switch(rb
->do_menu(&menu
, &result
, NULL
, false))
2443 rb
->lcd_setfont(FONT_SYSFIXED
);
2447 rb
->set_int("Number of Worms", "", UNIT_INT
, &worm_count
, NULL
,
2449 if (worm_count
< players
) {
2450 worm_count
= players
;
2455 rb
->set_int("Number of Players", "", UNIT_INT
, &players
, NULL
,
2458 rb
->set_int("Number of Players", "", UNIT_INT
, &players
, NULL
,
2461 if (players
> worm_count
) {
2462 worm_count
= players
;
2471 rb
->set_option("Control Style",&use_remote
,INT
,
2472 nokey_option
, 1, NULL
);
2475 rb
->set_option("Control Style",&use_remote
,INT
,
2476 key24_option
, 2, NULL
);
2480 rb
->set_option("Control Style",&use_remote
,INT
,
2481 remote_option
, 2, NULL
);
2483 rb
->set_option("Control Style",&use_remote
,INT
,
2484 key2_option
, 1, NULL
);
2488 rb
->set_option("Control Style",&use_remote
,INT
,
2489 remoteonly_option
, 1, NULL
);
2494 rb
->set_int("Worm Growth Per Food", "", UNIT_INT
, &worm_food
,
2495 NULL
, 1, 0, 15, NULL
);
2498 new_setting
= 20 - speed
;
2499 rb
->set_int("Worm Speed", "", UNIT_INT
, &new_setting
,
2500 NULL
, 1, 0, 20, NULL
);
2501 speed
= 20 - new_setting
;
2504 rb
->set_int("Arghs Per Food", "", UNIT_INT
, &arghs_per_food
,
2505 NULL
, 1, 0, 8, NULL
);
2508 rb
->set_int("Argh Size", "", UNIT_INT
, &argh_size
,
2509 NULL
, 1, 2, 10, NULL
);
2512 rb
->set_int("Food Size", "", UNIT_INT
, &food_size
,
2513 NULL
, 1, 2, 10, NULL
);
2517 rb
->set_option("Reset Settings?", &new_setting
, INT
, noyes
, 2, NULL
);
2518 if (new_setting
== 1)
2522 playback_control(NULL
);
2530 configfile_save(SETTINGS_FILENAME
, config
,
2531 sizeof(config
)/sizeof(*config
),