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"
28 /* size of the field the worm lives in */
29 #define FIELD_RECT_X 1
30 #define FIELD_RECT_Y 1
31 #define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
32 #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
34 /* when the game starts */
35 #define INITIAL_WORM_LENGTH 10
37 /* num of pixel the worm grows per eaten food */
38 #define WORM_PER_FOOD 7
40 /* num of worms creeping in the FIELD */
43 /* minimal distance between a worm and an argh
44 when a new argh is made */
45 #define MIN_ARGH_DIST 5
47 #if (CONFIG_KEYPAD == RECORDER_PAD)
48 #define BTN_DIR_UP BUTTON_UP
49 #define BTN_DIR_DOWN BUTTON_DOWN
50 #define BTN_DIR_LEFT BUTTON_LEFT
51 #define BTN_DIR_RIGHT BUTTON_RIGHT
52 #define BTN_PLAYER2_DIR1 BUTTON_F2
53 #define BTN_PLAYER2_DIR2 BUTTON_F3
54 #define BTN_STARTPAUSE BUTTON_PLAY
55 #define BTN_QUIT BUTTON_OFF
56 #define BTN_STOPRESET BUTTON_ON
57 #define BTN_TOGGLE_KEYS BUTTON_F1
59 #if BUTTON_REMOTE != 0
60 #define BTN_RC_UP BUTTON_RC_VOL_UP
61 #define BTN_RC_DOWN BUTTON_RC_VOL_DOWN
66 #define PLAYERS_TEXT "UP/DN"
67 #define WORMS_TEXT "L/R"
68 #define KEY_CONTROL_TEXT "F1"
70 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
71 #define BTN_DIR_UP BUTTON_UP
72 #define BTN_DIR_DOWN BUTTON_DOWN
73 #define BTN_DIR_LEFT BUTTON_LEFT
74 #define BTN_DIR_RIGHT BUTTON_RIGHT
75 #define BTN_PLAYER2_DIR1 BUTTON_F2
76 #define BTN_PLAYER2_DIR2 BUTTON_F3
77 #define BTN_STARTPAUSE BUTTON_SELECT
78 #define BTN_QUIT BUTTON_OFF
79 #define BTN_STOPRESET BUTTON_ON
80 #define BTN_TOGGLE_KEYS BUTTON_F1
82 #define PLAYERS_TEXT "UP/DN"
83 #define WORMS_TEXT "L/R"
84 #define KEY_CONTROL_TEXT "F1"
86 #elif (CONFIG_KEYPAD == ONDIO_PAD)
87 #define BTN_DIR_UP BUTTON_UP
88 #define BTN_DIR_DOWN BUTTON_DOWN
89 #define BTN_DIR_LEFT BUTTON_LEFT
90 #define BTN_DIR_RIGHT BUTTON_RIGHT
91 #define BTN_STARTPAUSE (BUTTON_MENU|BUTTON_REL)
92 #define BTN_QUIT (BUTTON_OFF|BUTTON_REL)
93 #define BTN_STOPRESET (BUTTON_OFF|BUTTON_MENU)
95 #define PLAYERS_TEXT "UP/DN"
96 #define WORMS_TEXT "L/R"
98 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
99 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
101 #define BTN_DIR_UP BUTTON_MENU
102 #define BTN_DIR_DOWN BUTTON_PLAY
103 #define BTN_DIR_LEFT BUTTON_LEFT
104 #define BTN_DIR_RIGHT BUTTON_RIGHT
105 #define BTN_STARTPAUSE (BUTTON_SELECT|BUTTON_REL)
106 #define BTN_QUIT (BUTTON_SELECT|BUTTON_MENU)
107 #define BTN_STOPRESET (BUTTON_SELECT|BUTTON_PLAY)
109 #define PLAYERS_TEXT "Menu/Play"
110 #define WORMS_TEXT "Left/Right"
112 #elif (CONFIG_KEYPAD == IRIVER_H300_PAD) || (CONFIG_KEYPAD == IRIVER_H100_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_SELECT|BUTTON_REL)
119 #define BTN_QUIT BUTTON_OFF
120 #define BTN_STOPRESET BUTTON_ON
122 #define BTN_RC_QUIT BUTTON_RC_STOP
124 #define PLAYERS_TEXT "Up/Down"
125 #define WORMS_TEXT "Left/Right"
127 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
129 #define BTN_DIR_UP BUTTON_UP
130 #define BTN_DIR_DOWN BUTTON_DOWN
131 #define BTN_DIR_LEFT BUTTON_LEFT
132 #define BTN_DIR_RIGHT BUTTON_RIGHT
133 #define BTN_STARTPAUSE BUTTON_PLAY
134 #define BTN_QUIT BUTTON_POWER
135 #define BTN_STOPRESET BUTTON_REC
137 #define PLAYERS_TEXT "Up/Down"
138 #define WORMS_TEXT "Left/Right"
140 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
142 #define BTN_DIR_UP BUTTON_UP
143 #define BTN_DIR_DOWN BUTTON_DOWN
144 #define BTN_DIR_LEFT BUTTON_LEFT
145 #define BTN_DIR_RIGHT BUTTON_RIGHT
146 #define BTN_STARTPAUSE BUTTON_SELECT
147 #define BTN_QUIT BUTTON_POWER
148 #define BTN_STOPRESET BUTTON_A
150 #define PLAYERS_TEXT "Up/Down"
151 #define WORMS_TEXT "Left/Right"
154 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
155 (CONFIG_KEYPAD == SANSA_C200_PAD)
157 #define BTN_DIR_UP BUTTON_UP
158 #define BTN_DIR_DOWN BUTTON_DOWN
159 #define BTN_DIR_LEFT BUTTON_LEFT
160 #define BTN_DIR_RIGHT BUTTON_RIGHT
161 #define BTN_STARTPAUSE BUTTON_SELECT
162 #define BTN_QUIT BUTTON_POWER
163 #define BTN_STOPRESET BUTTON_REC
165 #define PLAYERS_TEXT "Up/Down"
166 #define WORMS_TEXT "Left/Right"
169 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
170 (CONFIG_KEYPAD == SANSA_FUZE_PAD)
172 #define BTN_DIR_UP BUTTON_UP
173 #define BTN_DIR_DOWN BUTTON_DOWN
174 #define BTN_DIR_LEFT BUTTON_LEFT
175 #define BTN_DIR_RIGHT BUTTON_RIGHT
176 #define BTN_STARTPAUSE BUTTON_SELECT
177 #define BTN_QUIT BUTTON_POWER
178 #define BTN_STOPRESET BUTTON_HOME
180 #define PLAYERS_TEXT "Up/Down"
181 #define WORMS_TEXT "Left/Right"
183 #elif (CONFIG_KEYPAD == SANSA_M200_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 | BUTTON_REL)
190 #define BTN_QUIT BUTTON_POWER
191 #define BTN_STOPRESET (BUTTON_SELECT | BUTTON_UP)
193 #define PLAYERS_TEXT "Up/Down"
194 #define WORMS_TEXT "Left/Right"
196 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
198 #define BTN_DIR_UP BUTTON_SCROLL_UP
199 #define BTN_DIR_DOWN BUTTON_SCROLL_DOWN
200 #define BTN_DIR_LEFT BUTTON_LEFT
201 #define BTN_DIR_RIGHT BUTTON_RIGHT
202 #define BTN_STARTPAUSE BUTTON_PLAY
203 #define BTN_QUIT BUTTON_POWER
204 #define BTN_STOPRESET BUTTON_REW
206 #define PLAYERS_TEXT "Up/Down"
207 #define WORMS_TEXT "Left/Right"
209 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
211 #define BTN_DIR_UP BUTTON_UP
212 #define BTN_DIR_DOWN BUTTON_DOWN
213 #define BTN_DIR_LEFT BUTTON_LEFT
214 #define BTN_DIR_RIGHT BUTTON_RIGHT
215 #define BTN_STARTPAUSE BUTTON_SELECT
216 #define BTN_QUIT BUTTON_BACK
217 #define BTN_STOPRESET BUTTON_MENU
219 #define PLAYERS_TEXT "Up/Down"
220 #define WORMS_TEXT "Left/Right"
222 #elif (CONFIG_KEYPAD == MROBE100_PAD)
224 #define BTN_DIR_UP BUTTON_UP
225 #define BTN_DIR_DOWN BUTTON_DOWN
226 #define BTN_DIR_LEFT BUTTON_LEFT
227 #define BTN_DIR_RIGHT BUTTON_RIGHT
228 #define BTN_STARTPAUSE BUTTON_SELECT
229 #define BTN_QUIT BUTTON_POWER
230 #define BTN_STOPRESET BUTTON_DISPLAY
232 #define PLAYERS_TEXT "Up/Down"
233 #define WORMS_TEXT "Left/Right"
235 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
237 #define BTN_DIR_UP BUTTON_RC_VOL_UP
238 #define BTN_DIR_DOWN BUTTON_RC_VOL_DOWN
239 #define BTN_DIR_LEFT BUTTON_RC_REW
240 #define BTN_DIR_RIGHT BUTTON_RC_FF
241 #define BTN_STARTPAUSE BUTTON_RC_PLAY
242 #define BTN_QUIT BUTTON_RC_REC
243 #define BTN_STOPRESET BUTTON_RC_MODE
245 #define PLAYERS_TEXT "VOL UP/DN"
246 #define WORMS_TEXT "REW/FF"
248 #elif (CONFIG_KEYPAD == COWOND2_PAD)
250 #define BTN_QUIT BUTTON_POWER
252 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
254 #define BTN_DIR_UP BUTTON_UP
255 #define BTN_DIR_DOWN BUTTON_DOWN
256 #define BTN_DIR_LEFT BUTTON_LEFT
257 #define BTN_DIR_RIGHT BUTTON_RIGHT
258 #define BTN_STARTPAUSE BUTTON_PLAY
259 #define BTN_QUIT BUTTON_BACK
260 #define BTN_STOPRESET BUTTON_MENU
262 #define PLAYERS_TEXT "Up/Down"
263 #define WORMS_TEXT "Left/Right"
265 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
267 #define BTN_DIR_UP BUTTON_UP
268 #define BTN_DIR_DOWN BUTTON_DOWN
269 #define BTN_DIR_LEFT BUTTON_LEFT
270 #define BTN_DIR_RIGHT BUTTON_RIGHT
271 #define BTN_STARTPAUSE BUTTON_MENU
272 #define BTN_QUIT BUTTON_POWER
273 #define BTN_STOPRESET BUTTON_VIEW
275 #define PLAYERS_TEXT "Up/Down"
276 #define WORMS_TEXT "Left/Right"
279 #error No keymap defined!
282 #ifdef HAVE_TOUCHSCREEN
284 #define BTN_DIR_UP BUTTON_TOPMIDDLE
287 #define BTN_DIR_DOWN BUTTON_BOTTOMMIDDLE
290 #define BTN_DIR_LEFT BUTTON_MIDLEFT
292 #ifndef BTN_DIR_RIGHT
293 #define BTN_DIR_RIGHT BUTTON_MIDRIGHT
295 #ifndef BTN_STARTPAUSE
296 #define BTN_STARTPAUSE BUTTON_CENTER
299 #define BTN_QUIT BUTTON_TOPLEFT
301 #ifndef BTN_STOPRESET
302 #define BTN_STOPRESET BUTTON_TOPRIGHT
306 #define PLAYERS_TEXT "Up/Down"
309 #define WORMS_TEXT "Left/Right"
314 #if (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
318 #define MAX_WORM_SEGMENTS 128
319 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
323 #define MAX_WORM_SEGMENTS 128
324 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
328 #define MAX_WORM_SEGMENTS 128
329 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 96)
333 #define MAX_WORM_SEGMENTS 128
334 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
338 #define MAX_WORM_SEGMENTS 128
339 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
343 #define MAX_WORM_SEGMENTS 128
344 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
348 #define MAX_WORM_SEGMENTS 256
349 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
353 #define MAX_WORM_SEGMENTS 256
354 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
358 #define MAX_WORM_SEGMENTS 512
359 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
363 #define MAX_WORM_SEGMENTS 512
364 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
368 #define MAX_WORM_SEGMENTS 512
369 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
373 #define MAX_WORM_SEGMENTS 512
376 #ifdef HAVE_LCD_COLOR
377 #define COLOR_WORM LCD_RGBPACK(80, 40, 0)
378 #define COLOR_ARGH LCD_RGBPACK(175, 0, 0)
379 #define COLOR_FOOD LCD_RGBPACK(0, 150, 0)
380 #define COLOR_FG LCD_RGBPACK(0, 0, 0)
381 #define COLOR_BG LCD_RGBPACK(181, 199, 231)
385 * All the properties that a worm has.
388 /* The worm is stored in a ring of xy coordinates */
389 int x
[MAX_WORM_SEGMENTS
];
390 int y
[MAX_WORM_SEGMENTS
];
392 int head
; /* index of the head within the buffer */
393 int tail
; /* index of the tail within the buffer */
394 int growing
; /* number of cyles the worm still keeps growing */
395 bool alive
; /* the worms living state */
397 /* direction vector in which the worm moves */
398 int dirx
; /* only values -1 0 1 allowed */
399 int diry
; /* only values -1 0 1 allowed */
401 /* this method is used to fetch the direction the user
402 has selected. It can be one of the values
403 human_player1, human_player2, remote_player, virtual_player.
404 All these values are fuctions, that can change the direction
406 void (*fetch_worm_direction
)(struct worm
*w
);
409 /* stores the highscore - besides it was scored by a virtual player */
410 static int highscore
;
412 #define MAX_FOOD 5 /* maximal number of food items */
414 /* The arrays store the food coordinates */
415 static int foodx
[MAX_FOOD
];
416 static int foody
[MAX_FOOD
];
418 #define MAX_ARGH 100 /* maximal number of argh items */
419 #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
421 /* The arrays store the argh coordinates */
422 static int arghx
[MAX_ARGH
];
423 static int arghy
[MAX_ARGH
];
425 /* the number of arghs that are currently in use */
426 static int argh_count
;
428 /* the number of arghs per food, settable by user */
429 static int arghs_per_food
= ARGHS_PER_FOOD
;
430 /* the size of the argh, settable by user */
431 static int argh_size
= ARGH_SIZE
;
432 /* the size of the food, settable by user */
433 static int food_size
= FOOD_SIZE
;
434 /* the speed of the worm, settable by user */
435 static int speed
= SPEED
;
436 /* the amount a worm grows by eating a food, settable by user */
437 static int worm_food
= WORM_PER_FOOD
;
439 /* End additional variables */
442 /* just a buffer used for debug output */
443 static char debugout
[15];
446 /* the number of active worms (dead or alive) */
447 static int worm_count
= MAX_WORMS
;
449 /* in multiplayer mode: en- / disables the remote worm control
450 in singleplayer mode: toggles 4 / 2 button worm control */
451 static bool use_remote
= false;
453 /* return values of check_collision */
454 #define COLLISION_NONE 0
455 #define COLLISION_WORM 1
456 #define COLLISION_FOOD 2
457 #define COLLISION_ARGH 3
458 #define COLLISION_FIELD 4
460 /* constants for use as directions.
461 Note that the values are ordered clockwise.
462 Thus increasing / decreasing the values
463 is equivalent to right / left turns. */
469 /* direction of human player 1 */
470 static int player1_dir
= EAST
;
471 /* direction of human player 2 */
472 static int player2_dir
= EAST
;
473 /* direction of human player 3 */
474 static int player3_dir
= EAST
;
476 /* the number of (human) players that currently
478 static int players
= 1;
480 #define SETTINGS_VERSION 1
481 #define SETTINGS_MIN_VERSION 1
482 #define SETTINGS_FILENAME "wormlet.cfg"
484 static struct configdata config
[] =
486 {TYPE_INT
, 0, 1024, { .int_p
= &highscore
}, "highscore", NULL
},
487 {TYPE_INT
, 0, 15, { .int_p
= &arghs_per_food
}, "arghs per food", NULL
},
488 {TYPE_INT
, 0, 15, { .int_p
= &argh_size
}, "argh size", NULL
},
489 {TYPE_INT
, 0, 15, { .int_p
= &food_size
}, "food size", NULL
},
490 {TYPE_INT
, 0, 3, { .int_p
= &players
}, "players", NULL
},
491 {TYPE_INT
, 0, 3, { .int_p
= &worm_count
}, "worms", NULL
},
492 {TYPE_INT
, 0, 20, { .int_p
= &speed
}, "speed", NULL
},
493 {TYPE_INT
, 0, 15, { .int_p
= &worm_food
}, "Worm Growth Per Food", NULL
}
497 static void set_debug_out(char *str
){
498 strcpy(debugout
, str
);
503 * Returns the direction id in which the worm
504 * currently is creeping.
505 * @param struct worm *w The worm that is to be investigated.
506 * w Must not be null.
507 * @return int A value 0 <= value < 4
508 * Note the predefined constants NORTH, SOUTH, EAST, WEST
510 static int get_worm_dir(struct worm
*w
) {
529 * Set the direction of the specified worm with a direction id.
530 * Increasing the value by 1 means to turn the worm direction
531 * to right by 90 degree.
532 * @param struct worm *w The worm that is to be altered. w Must not be null.
533 * @param int dir The new direction in which the worm is to creep.
534 * dir must be 0 <= dir < 4. Use predefined constants
535 * NORTH, SOUTH, EAST, WEST
537 static void set_worm_dir(struct worm
*w
, int dir
) {
559 * Returns the current length of the worm array. This
560 * is also a value for the number of bends that are in the worm.
561 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
563 static int get_worm_array_length(struct worm
*w
) {
564 /* initial simple calculation will be overwritten if wrong. */
565 int retVal
= w
->head
- w
->tail
;
567 /* if the worm 'crosses' the boundaries of the ringbuffer */
569 retVal
= w
->head
+ MAX_WORM_SEGMENTS
- w
->tail
;
576 * Returns the score the specified worm. The score is the length
578 * @param struct worm *w The worm that is to be investigated.
579 * w must not be null.
580 * @return int The length of the worm (>= 0).
582 static int get_score(struct worm
*w
) {
584 int length
= get_worm_array_length(w
);
586 for (i
= 0; i
< length
; i
++) {
588 /* The iteration iterates the length of the worm.
589 Here's the conversion to the true indices within the worm arrays. */
590 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
591 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
592 int startx
= w
->x
[linestart
];
593 int starty
= w
->y
[linestart
];
594 int endx
= w
->x
[lineend
];
595 int endy
= w
->y
[lineend
];
597 int minimum
, maximum
;
599 if (startx
== endx
) {
600 minimum
= MIN(starty
, endy
);
601 maximum
= MAX(starty
, endy
);
603 minimum
= MIN(startx
, endx
);
604 maximum
= MAX(startx
, endx
);
606 retval
+= abs(maximum
- minimum
);
612 * Determines wether the line specified by startx, starty, endx, endy intersects
613 * the rectangle specified by x, y, width, height. Note that the line must be exactly
614 * horizontal or vertical (startx == endx or starty == endy).
615 * @param int startx The x coordinate of the start point of the line.
616 * @param int starty The y coordinate of the start point of the line.
617 * @param int endx The x coordinate of the end point of the line.
618 * @param int endy The y coordinate of the end point of the line.
619 * @param int x The x coordinate of the top left corner of the rectangle.
620 * @param int y The y coordinate of the top left corner of the rectangle.
621 * @param int width The width of the rectangle.
622 * @param int height The height of the rectangle.
623 * @return bool Returns true if the specified line intersects with the recangle.
625 static bool line_in_rect(int startx
, int starty
, int endx
, int endy
, int x
, int y
, int width
, int height
) {
627 int simple
, simplemin
, simplemax
;
628 int compa
, compb
, compmin
, compmax
;
630 if (startx
== endx
) {
633 simplemax
= x
+ width
;
638 compmax
= y
+ height
;
642 simplemax
= y
+ height
;
651 compa
= MIN(compa
, compb
);
652 compb
= MAX(temp
, compb
);
654 if (simplemin
<= simple
&& simple
<= simplemax
) {
655 if ((compmin
<= compa
&& compa
<= compmax
) ||
656 (compmin
<= compb
&& compb
<= compmax
) ||
657 (compa
<= compmin
&& compb
>= compmax
)) {
665 * Tests wether the specified worm intersects with the rect.
666 * @param struct worm *w The worm to be investigated
667 * @param int x The x coordinate of the top left corner of the rect
668 * @param int y The y coordinate of the top left corner of the rect
669 * @param int widht The width of the rect
670 * @param int height The height of the rect
671 * @return bool Returns true if the worm intersects with the rect
673 static bool worm_in_rect(struct worm
*w
, int x
, int y
, int width
, int height
) {
677 /* get_worm_array_length is expensive -> buffer the value */
678 int wormLength
= get_worm_array_length(w
);
681 /* test each entry that is part of the worm */
682 for (i
= 0; i
< wormLength
&& retval
== false; i
++) {
684 /* The iteration iterates the length of the worm.
685 Here's the conversion to the true indices within the worm arrays. */
686 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
687 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
688 int startx
= w
->x
[linestart
];
689 int starty
= w
->y
[linestart
];
690 int endx
= w
->x
[lineend
];
691 int endy
= w
->y
[lineend
];
693 retval
= line_in_rect(startx
, starty
, endx
, endy
, x
, y
, width
, height
);
700 * Checks wether a specific food in the food arrays is at the
701 * specified coordinates.
702 * @param int foodIndex The index of the food in the food arrays
703 * @param int x the x coordinate.
704 * @param int y the y coordinate.
705 * @return Returns true if the coordinate hits the food specified by
708 static bool specific_food_collision(int foodIndex
, int x
, int y
) {
710 if (x
>= foodx
[foodIndex
] &&
711 x
< foodx
[foodIndex
] + food_size
&&
712 y
>= foody
[foodIndex
] &&
713 y
< foody
[foodIndex
] + food_size
) {
721 * Returns the index of the food that is at the
722 * given coordinates. If no food is at the coordinates
724 * @return int -1 <= value < MAX_FOOD
726 static int food_collision(int x
, int y
) {
729 for (i
= 0; i
< MAX_FOOD
; i
++) {
730 if (specific_food_collision(i
, x
, y
)) {
739 * Checks wether a specific argh in the argh arrays is at the
740 * specified coordinates.
741 * @param int arghIndex The index of the argh in the argh arrays
742 * @param int x the x coordinate.
743 * @param int y the y coordinate.
744 * @return Returns true if the coordinate hits the argh specified by
747 static bool specific_argh_collision(int arghIndex
, int x
, int y
) {
749 if ( x
>= arghx
[arghIndex
] &&
750 y
>= arghy
[arghIndex
] &&
751 x
< arghx
[arghIndex
] + argh_size
&&
752 y
< arghy
[arghIndex
] + argh_size
)
761 * Returns the index of the argh that is at the
762 * given coordinates. If no argh is at the coordinates
764 * @param int x The x coordinate.
765 * @param int y The y coordinate.
766 * @return int -1 <= value < argh_count <= MAX_ARGH
768 static int argh_collision(int x
, int y
) {
772 /* search for the argh that has the specified coords */
773 for (i
= 0; i
< argh_count
; i
++) {
774 if (specific_argh_collision(i
, x
, y
)) {
783 * Checks wether the worm collides with the food at the specfied food-arrays.
784 * @param int foodIndex The index of the food in the arrays. Ensure the value is
785 * 0 <= foodIndex <= MAX_FOOD
786 * @return Returns true if the worm collides with the specified food.
788 static bool worm_food_collision(struct worm
*w
, int foodIndex
)
792 retVal
= worm_in_rect(w
, foodx
[foodIndex
], foody
[foodIndex
],
793 food_size
- 1, food_size
- 1);
799 * Returns true if the worm hits the argh within the next moves (unless
800 * the worm changes it's direction).
801 * @param struct worm *w - The worm to investigate
802 * @param int argh_idx - The index of the argh
803 * @param int moves - The number of moves that are considered.
804 * @return Returns false if the specified argh is not hit within the next
807 static bool worm_argh_collision_in_moves(struct worm
*w
, int argh_idx
, int moves
){
813 x2
= w
->x
[w
->head
] + moves
* w
->dirx
;
814 y2
= w
->y
[w
->head
] + moves
* w
->diry
;
816 retVal
= line_in_rect(x1
, y1
, x2
, y2
, arghx
[argh_idx
], arghy
[argh_idx
],
817 argh_size
, argh_size
);
822 * Checks wether the worm collides with the argh at the specfied argh-arrays.
823 * @param int arghIndex The index of the argh in the arrays.
824 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
825 * @return Returns true if the worm collides with the specified argh.
827 static bool worm_argh_collision(struct worm
*w
, int arghIndex
)
831 retVal
= worm_in_rect(w
, arghx
[arghIndex
], arghy
[arghIndex
],
832 argh_size
- 1, argh_size
- 1);
838 * Find new coordinates for the food stored in foodx[index], foody[index]
839 * that don't collide with any other food or argh
841 * Ensure that 0 <= index < MAX_FOOD.
843 static void make_food(int index
) {
847 bool collisionDetected
= false;
851 /* make coordinates for a new food so that
852 the entire food lies within the FIELD */
853 x
= rb
->rand() % (FIELD_RECT_WIDTH
- food_size
);
854 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- food_size
);
856 /* Ensure that the new food doesn't collide with any
857 existing foods or arghs.
858 If one or more corners of the new food hit any existing
859 argh or food a collision is detected.
862 food_collision(x
, y
) >= 0 ||
863 food_collision(x
, y
+ food_size
- 1) >= 0 ||
864 food_collision(x
+ food_size
- 1, y
) >= 0 ||
865 food_collision(x
+ food_size
- 1, y
+ food_size
- 1) >= 0 ||
866 argh_collision(x
, y
) >= 0 ||
867 argh_collision(x
, y
+ food_size
- 1) >= 0 ||
868 argh_collision(x
+ food_size
- 1, y
) >= 0 ||
869 argh_collision(x
+ food_size
- 1, y
+ food_size
- 1) >= 0;
871 /* use coordinates for further testing */
875 /* now test wether we accidently hit the worm with food ;) */
877 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
878 collisionDetected
|= worm_food_collision(&worms
[i
], index
);
881 while (collisionDetected
);
886 * Clears a food from the lcd buffer.
887 * @param int index The index of the food arrays under which
888 * the coordinates of the desired food can be found. Ensure
889 * that the value is 0 <= index <= MAX_FOOD.
891 static void clear_food(int index
)
893 /* remove the old food from the screen */
894 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
895 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
896 foody
[index
] + FIELD_RECT_Y
,
897 food_size
, food_size
);
898 rb
->lcd_set_drawmode(DRMODE_SOLID
);
902 * Draws a food in the lcd buffer.
903 * @param int index The index of the food arrays under which
904 * the coordinates of the desired food can be found. Ensure
905 * that the value is 0 <= index <= MAX_FOOD.
907 static void draw_food(int index
)
909 /* draw the food object */
910 #ifdef HAVE_LCD_COLOR
911 rb
->lcd_set_foreground(COLOR_FOOD
);
913 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
,
914 foody
[index
] + FIELD_RECT_Y
,
915 food_size
, food_size
);
916 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
917 rb
->lcd_fillrect(foodx
[index
] + FIELD_RECT_X
+ 1,
918 foody
[index
] + FIELD_RECT_Y
+ 1,
919 food_size
- 2, food_size
- 2);
920 rb
->lcd_set_drawmode(DRMODE_SOLID
);
921 #ifdef HAVE_LCD_COLOR
922 rb
->lcd_set_foreground(COLOR_FG
);
927 * Find new coordinates for the argh stored in arghx[index], arghy[index]
928 * that don't collide with any other food or argh.
930 * Ensure that 0 <= index < argh_count < MAX_ARGH.
932 static void make_argh(int index
)
936 bool collisionDetected
= false;
940 /* make coordinates for a new argh so that
941 the entire food lies within the FIELD */
942 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
943 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
945 /* Ensure that the new argh doesn't intersect with any
946 existing foods or arghs.
947 If one or more corners of the new argh hit any existing
948 argh or food an intersection is detected.
951 food_collision(x
, y
) >= 0 ||
952 food_collision(x
, y
+ argh_size
- 1) >= 0 ||
953 food_collision(x
+ argh_size
- 1, y
) >= 0 ||
954 food_collision(x
+ argh_size
- 1, y
+ argh_size
- 1) >= 0 ||
955 argh_collision(x
, y
) >= 0 ||
956 argh_collision(x
, y
+ argh_size
- 1) >= 0 ||
957 argh_collision(x
+ argh_size
- 1, y
) >= 0 ||
958 argh_collision(x
+ argh_size
- 1, y
+ argh_size
- 1) >= 0;
960 /* use the candidate coordinates to make a real argh */
964 /* now test wether we accidently hit the worm with argh ;) */
965 for (i
= 0; i
< worm_count
&& !collisionDetected
; i
++) {
966 collisionDetected
|= worm_argh_collision(&worms
[i
], index
);
967 collisionDetected
|= worm_argh_collision_in_moves(&worms
[i
], index
,
971 while (collisionDetected
);
976 * Draws an argh in the lcd buffer.
977 * @param int index The index of the argh arrays under which
978 * the coordinates of the desired argh can be found. Ensure
979 * that the value is 0 <= index < argh_count <= MAX_ARGH.
981 static void draw_argh(int index
)
983 /* draw the new argh */
984 #ifdef HAVE_LCD_COLOR
985 rb
->lcd_set_foreground(COLOR_ARGH
);
987 rb
->lcd_fillrect(arghx
[index
] + FIELD_RECT_X
,
988 arghy
[index
] + FIELD_RECT_Y
,
989 argh_size
, argh_size
);
990 #ifdef HAVE_LCD_COLOR
991 rb
->lcd_set_foreground(COLOR_FG
);
995 static void virtual_player(struct worm
*w
);
997 * Initialzes the specified worm with INITIAL_WORM_LENGTH
998 * and the tail at the specified position. The worm will
999 * be initialized alive and creeping EAST.
1000 * @param struct worm *w The worm that is to be initialized
1001 * @param int x The x coordinate at which the tail of the worm starts.
1002 * x must be 0 <= x < FIELD_RECT_WIDTH.
1003 * @param int y The y coordinate at which the tail of the worm starts
1004 * y must be 0 <= y < FIELD_RECT_WIDTH.
1006 static void init_worm(struct worm
*w
, int x
, int y
){
1007 /* initialize the worm size */
1011 w
->x
[w
->head
] = x
+ 1;
1017 /* set the initial direction the worm creeps to */
1021 w
->growing
= INITIAL_WORM_LENGTH
- 1;
1023 w
->fetch_worm_direction
= virtual_player
;
1027 * Writes the direction that was stored for
1028 * human player 1 into the specified worm. This function
1029 * may be used to be stored in worm.fetch_worm_direction.
1031 * the direction is read from player1_dir.
1032 * @param struct worm *w - The worm of which the direction
1035 static void human_player1(struct worm
*w
) {
1036 set_worm_dir(w
, player1_dir
);
1040 * Writes the direction that was stored for
1041 * human player 2 into the specified worm. This function
1042 * may be used to be stored in worm.fetch_worm_direction.
1044 * the direction is read from player2_dir.
1045 * @param struct worm *w - The worm of which the direction
1048 static void human_player2(struct worm
*w
) {
1049 set_worm_dir(w
, player2_dir
);
1053 * Writes the direction that was stored for
1054 * human player using a remote control
1055 * into the specified worm. This function
1056 * may be used to be stored in worm.fetch_worm_direction.
1058 * the direction is read from player3_dir.
1059 * @param struct worm *w - The worm of which the direction
1062 static void remote_player(struct worm
*w
) {
1063 set_worm_dir(w
, player3_dir
);
1067 * Initializes the worm-, food- and argh-arrays, draws a frame,
1068 * makes some food and argh and display all that stuff.
1070 static void init_wormlet(void)
1074 for (i
= 0; i
< worm_count
; i
++) {
1075 /* Initialize all the worm coordinates to center. */
1076 int x
= (int)(FIELD_RECT_WIDTH
/ 2);
1077 int y
= (int)((FIELD_RECT_HEIGHT
- 20)/ 2) + i
* 10;
1079 init_worm(&worms
[i
], x
, y
);
1087 worms
[0].fetch_worm_direction
= human_player1
;
1092 worms
[1].fetch_worm_direction
= remote_player
;
1094 worms
[1].fetch_worm_direction
= human_player2
;
1099 worms
[2].fetch_worm_direction
= human_player2
;
1102 /* Needed when the game is restarted using BTN_STOPRESET */
1103 rb
->lcd_clear_display();
1105 /* make and display some food and argh */
1106 argh_count
= MAX_FOOD
;
1107 for (i
= 0; i
< MAX_FOOD
; i
++) {
1114 /* draw the game field */
1115 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1116 rb
->lcd_fillrect(0, 0, FIELD_RECT_WIDTH
+ 2, FIELD_RECT_HEIGHT
+ 2);
1117 rb
->lcd_fillrect(1, 1, FIELD_RECT_WIDTH
, FIELD_RECT_HEIGHT
);
1118 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1120 /* make everything visible */
1126 * Move the worm one step further if it is alive.
1127 * The direction in which the worm moves is taken from dirx and diry.
1128 * move_worm decreases growing if > 0. While the worm is growing the tail
1129 * is left untouched.
1130 * @param struct worm *w The worm to move. w must not be NULL.
1132 static void move_worm(struct worm
*w
)
1135 /* determine the head point and its precessor */
1136 int headx
= w
->x
[w
->head
];
1137 int heady
= w
->y
[w
->head
];
1138 int prehead
= (w
->head
+ MAX_WORM_SEGMENTS
- 1) % MAX_WORM_SEGMENTS
;
1139 int preheadx
= w
->x
[prehead
];
1140 int preheady
= w
->y
[prehead
];
1142 /* determine the old direction */
1145 if (headx
== preheadx
) {
1147 olddiry
= (heady
> preheady
) ? 1 : -1;
1150 olddirx
= (headx
> preheadx
) ? 1 : -1;
1154 a change of direction means a new segment
1156 if (olddirx
!= w
->dirx
||
1157 olddiry
!= w
->diry
) {
1158 w
->head
= (w
->head
+ 1) % MAX_WORM_SEGMENTS
;
1161 /* new head position */
1162 w
->x
[w
->head
] = headx
+ w
->dirx
;
1163 w
->y
[w
->head
] = heady
+ w
->diry
;
1166 /* while the worm is growing no tail procession is necessary */
1167 if (w
->growing
> 0) {
1168 /* update the worms grow state */
1172 /* if the worm isn't growing the tail has to be dragged */
1174 /* index of the end of the tail segment */
1175 int tail_segment_end
= (w
->tail
+ 1) % MAX_WORM_SEGMENTS
;
1177 /* drag the end of the tail */
1178 /* only one coordinate has to be altered. Here it is
1179 determined which one */
1180 int dir
= 0; /* specifies wether the coord has to be in- or decreased */
1181 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
]) {
1182 dir
= (w
->y
[w
->tail
] - w
->y
[tail_segment_end
] < 0) ? 1 : -1;
1183 w
->y
[w
->tail
] += dir
;
1185 dir
= (w
->x
[w
->tail
] - w
->x
[tail_segment_end
] < 0) ? 1 : -1;
1186 w
->x
[w
->tail
] += dir
;
1189 /* when the tail has been dragged so far that it meets
1190 the next segment start the tail segment is obsolete and
1192 if (w
->x
[w
->tail
] == w
->x
[tail_segment_end
] &&
1193 w
->y
[w
->tail
] == w
->y
[tail_segment_end
]){
1195 /* drop the last tail point */
1196 w
->tail
= tail_segment_end
;
1203 * Draws the head and clears the tail of the worm in
1204 * the display buffer. lcd_update() is NOT called thus
1205 * the caller has to take care that the buffer is displayed.
1207 static void draw_worm(struct worm
*w
)
1209 #ifdef HAVE_LCD_COLOR
1210 rb
->lcd_set_foreground(COLOR_WORM
);
1212 /* draw the new head */
1213 int x
= w
->x
[w
->head
];
1214 int y
= w
->y
[w
->head
];
1215 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1216 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1219 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1221 /* clear the space behind the worm */
1224 if (x
>= 0 && x
< FIELD_RECT_WIDTH
&& y
>= 0 && y
< FIELD_RECT_HEIGHT
) {
1225 rb
->lcd_drawpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
1227 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1228 #ifdef HAVE_LCD_COLOR
1229 rb
->lcd_set_foreground(COLOR_FG
);
1234 * Checks wether the coordinate is part of the worm. Returns
1235 * true if any part of the worm was hit - including the head.
1236 * @param x int The x coordinate
1237 * @param y int The y coordinate
1238 * @return int The index of the worm arrays that contain x, y.
1239 * Returns -1 if the coordinates are not part of the worm.
1241 static int specific_worm_collision(struct worm
*w
, int x
, int y
)
1245 /* get_worm_array_length is expensive -> buffer the value */
1246 int wormLength
= get_worm_array_length(w
);
1249 /* test each entry that is part of the worm */
1250 for (i
= 0; i
< wormLength
&& retVal
== -1; i
++) {
1252 /* The iteration iterates the length of the worm.
1253 Here's the conversion to the true indices within the worm arrays. */
1254 int linestart
= (w
->tail
+ i
) % MAX_WORM_SEGMENTS
;
1255 int lineend
= (linestart
+ 1) % MAX_WORM_SEGMENTS
;
1256 bool samex
= (w
->x
[linestart
] == x
) && (w
->x
[lineend
] == x
);
1257 bool samey
= (w
->y
[linestart
] == y
) && (w
->y
[lineend
] == y
);
1258 if (samex
|| samey
){
1259 int test
, min
, max
, tmp
;
1262 min
= w
->x
[linestart
];
1263 max
= w
->x
[lineend
];
1266 min
= w
->y
[linestart
];
1267 max
= w
->y
[lineend
];
1272 min
= MIN(min
, max
);
1273 max
= MAX(tmp
, max
);
1275 if (min
<= test
&& test
<= max
) {
1284 * Increases the length of the specified worm by marking
1285 * that it may grow by len pixels. Note that the worm has
1286 * to move to make the growing happen.
1287 * @param worm *w The worm that is to be altered.
1288 * @param int len A positive value specifying the amount of
1289 * pixels the worm may grow.
1291 static void add_growing(struct worm
*w
, int len
) {
1296 * Determins the worm that is at the coordinates x, y. The parameter
1297 * w is a switch parameter that changes the functionality of worm_collision.
1298 * If w is specified and x,y hits the head of w NULL is returned.
1299 * This is a useful way to determine wether the head of w hits
1300 * any worm but including itself but excluding its own head.
1301 * (It hits always its own head ;))
1302 * If w is set to NULL worm_collision returns any worm including all heads
1303 * that is at position of x,y.
1304 * @param struct worm *w The worm of which the head should be excluded in
1305 * the test. w may be set to NULL.
1306 * @param int x The x coordinate that is checked
1307 * @param int y The y coordinate that is checkec
1308 * @return struct worm* The worm that has been hit by x,y. If no worm
1309 * was at the position NULL is returned.
1311 static struct worm
* worm_collision(struct worm
*w
, int x
, int y
){
1312 struct worm
*retVal
= NULL
;
1314 for (i
= 0; (i
< worm_count
) && (retVal
== NULL
); i
++) {
1315 int collision_at
= specific_worm_collision(&worms
[i
], x
, y
);
1316 if (collision_at
!= -1) {
1317 if (!(w
== &worms
[i
] && collision_at
== w
->head
)){
1326 * Returns true if the head of the worm just has
1327 * crossed the field boundaries.
1328 * @return bool true if the worm just has wrapped.
1330 static bool field_collision(struct worm
*w
)
1332 bool retVal
= false;
1333 if ((w
->x
[w
->head
] >= FIELD_RECT_WIDTH
) ||
1334 (w
->y
[w
->head
] >= FIELD_RECT_HEIGHT
) ||
1335 (w
->x
[w
->head
] < 0) ||
1336 (w
->y
[w
->head
] < 0))
1345 * Returns true if the specified coordinates are within the
1346 * field specified by the FIELD_RECT_XXX constants.
1347 * @param int x The x coordinate of the point that is investigated
1348 * @param int y The y coordinate of the point that is investigated
1349 * @return bool Returns false if x,y specifies a point outside the
1352 static bool is_in_field_rect(int x
, int y
) {
1353 bool retVal
= false;
1354 retVal
= (x
>= 0 && x
< FIELD_RECT_WIDTH
&&
1355 y
>= 0 && y
< FIELD_RECT_HEIGHT
);
1360 * Checks and returns wether the head of the w
1361 * is colliding with something currently.
1362 * @return int One of the values:
1369 static int check_collision(struct worm
*w
)
1371 int retVal
= COLLISION_NONE
;
1373 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
)
1374 retVal
= COLLISION_WORM
;
1376 if (food_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1377 retVal
= COLLISION_FOOD
;
1379 if (argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]) >= 0)
1380 retVal
= COLLISION_ARGH
;
1382 if (field_collision(w
))
1383 retVal
= COLLISION_FIELD
;
1389 * Returns the index of the food that is closest to the point
1390 * specified by x, y. This index may be used in the foodx and
1392 * @param int x The x coordinate of the point
1393 * @param int y The y coordinate of the point
1394 * @return int A value usable as index in foodx and foody.
1396 static int get_nearest_food(int x
, int y
){
1397 int nearestfood
= 0;
1398 int olddistance
= FIELD_RECT_WIDTH
+ FIELD_RECT_HEIGHT
;
1402 for (foodindex
= 0; foodindex
< MAX_FOOD
; foodindex
++) {
1404 deltax
= foodx
[foodindex
] - x
;
1405 deltay
= foody
[foodindex
] - y
;
1406 deltax
= deltax
> 0 ? deltax
: deltax
* (-1);
1407 deltay
= deltay
> 0 ? deltay
: deltay
* (-1);
1408 distance
= deltax
+ deltay
;
1410 if (distance
< olddistance
) {
1411 olddistance
= distance
;
1412 nearestfood
= foodindex
;
1419 * Returns wether the specified position is next to the worm
1420 * and in the direction the worm looks. Use this method to
1421 * test wether this position would be hit with the next move of
1422 * the worm unless the worm changes its direction.
1423 * @param struct worm *w - The worm to be investigated
1424 * @param int x - The x coordinate of the position to test.
1425 * @param int y - The y coordinate of the position to test.
1426 * @return Returns true if the worm will hit the position unless
1427 * it change its direction before the next move.
1429 static bool is_in_front_of_worm(struct worm
*w
, int x
, int y
) {
1430 bool infront
= false;
1431 int deltax
= x
- w
->x
[w
->head
];
1432 int deltay
= y
- w
->y
[w
->head
];
1435 infront
= (w
->diry
* deltay
) > 0;
1437 infront
= (w
->dirx
* deltax
) > 0;
1443 * Returns true if the worm will collide with the next move unless
1444 * it changes its direction.
1445 * @param struct worm *w - The worm to be investigated.
1446 * @return Returns true if the worm will collide with the next move
1447 * unless it changes its direction.
1449 static bool will_worm_collide(struct worm
*w
) {
1450 int x
= w
->x
[w
->head
] + w
->dirx
;
1451 int y
= w
->y
[w
->head
] + w
->diry
;
1452 bool retVal
= !is_in_field_rect(x
, y
);
1454 retVal
= (argh_collision(x
, y
) != -1);
1458 retVal
= (worm_collision(w
, x
, y
) != NULL
);
1465 * may be used to be stored in worm.fetch_worm_direction for
1466 * worms that are not controlled by humans but by artificial stupidity.
1467 * A direction is searched that doesn't lead to collision but to the nearest
1468 * food - but not very intelligent. The direction is written to the specified
1470 * @param struct worm *w - The worm of which the direction
1473 static void virtual_player(struct worm
*w
) {
1475 int plana
, planb
, planc
;
1476 /* find the next lunch */
1477 int nearestfood
= get_nearest_food(w
->x
[w
->head
], w
->y
[w
->head
]);
1479 /* determine in which direction it is */
1481 /* in front of me? */
1482 bool infront
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1484 /* left right of me? */
1485 int olddir
= get_worm_dir(w
);
1486 set_worm_dir(w
, (olddir
+ 1) % 4);
1487 isright
= is_in_front_of_worm(w
, foodx
[nearestfood
], foody
[nearestfood
]);
1488 set_worm_dir(w
, olddir
);
1490 /* detect situation, set strategy */
1494 planb
= (olddir
+ 1) % 4;
1495 planc
= (olddir
+ 3) % 4;
1498 planb
= (olddir
+ 3) % 4;
1499 planc
= (olddir
+ 1) % 4;
1503 plana
= (olddir
+ 1) % 4;
1505 planc
= (olddir
+ 3) % 4;
1507 plana
= (olddir
+ 3) % 4;
1509 planc
= (olddir
+ 1) % 4;
1513 /* test for collision */
1514 set_worm_dir(w
, plana
);
1515 if (will_worm_collide(w
)){
1518 set_worm_dir(w
, planb
);
1520 /* test for collision */
1521 if (will_worm_collide(w
)) {
1524 set_worm_dir(w
, planc
);
1530 * prints out the score board with all the status information
1533 static void score_board(void)
1539 rb
->lcd_set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1540 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, 0, LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, LCD_HEIGHT
);
1541 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1542 for (i
= 0; i
< worm_count
; i
++) {
1543 int score
= get_score(&worms
[i
]);
1546 if (worms
[i
].fetch_worm_direction
!= virtual_player
){
1547 if (highscore
< score
) {
1553 rb
->snprintf(buf
, sizeof (buf
),"Len:%d", score
);
1556 switch (check_collision(&worms
[i
])) {
1557 case COLLISION_NONE
:
1558 if (worms
[i
].growing
> 0)
1568 case COLLISION_WORM
:
1572 case COLLISION_FOOD
:
1576 case COLLISION_ARGH
:
1580 case COLLISION_FIELD
:
1584 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, y
, buf
);
1585 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, y
+8, buf2
);
1587 if (!worms
[i
].alive
){
1588 rb
->lcd_set_drawmode(DRMODE_COMPLEMENT
);
1589 rb
->lcd_fillrect(FIELD_RECT_WIDTH
+ 2, y
,
1590 LCD_WIDTH
- FIELD_RECT_WIDTH
- 2, 17);
1591 rb
->lcd_set_drawmode(DRMODE_SOLID
);
1595 rb
->snprintf(buf
, sizeof(buf
), "Hs: %d", highscore
);
1596 #ifndef DEBUG_WORMLET
1597 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, buf
);
1599 rb
->lcd_putsxy(FIELD_RECT_WIDTH
+ 3, LCD_HEIGHT
- 8, debugout
);
1604 * Checks for collisions of the worm and its environment and
1605 * takes appropriate actions like growing the worm or killing it.
1606 * @return bool Returns true if the worm is dead. Returns
1607 * false if the worm is healthy, up and creeping.
1609 static bool process_collisions(struct worm
*w
)
1613 w
->alive
&= !field_collision(w
);
1617 /* check if food was eaten */
1618 index
= food_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1626 for (i
= 0; i
< arghs_per_food
; i
++) {
1628 if (argh_count
> MAX_ARGH
)
1629 argh_count
= MAX_ARGH
;
1630 make_argh(argh_count
- 1);
1631 draw_argh(argh_count
- 1);
1634 add_growing(w
, worm_food
);
1639 /* check if argh was eaten */
1641 index
= argh_collision(w
->x
[w
->head
], w
->y
[w
->head
]);
1646 if (worm_collision(w
, w
->x
[w
->head
], w
->y
[w
->head
]) != NULL
) {
1656 * The main loop of the game.
1657 * @return bool Returns true if the game ended
1658 * with a dead worm. Returns false if the user
1659 * aborted the game manually.
1661 static int run(void)
1664 int wormDead
= false;
1665 bool paused
= false;
1667 /* ticks are counted to compensate speed variations */
1668 long cycle_start
= 0, cycle_end
= 0;
1669 #ifdef DEBUG_WORMLET
1670 int ticks_to_max_cycle_reset
= 20;
1675 /* initialize the board and so on */
1678 cycle_start
= *rb
->current_tick
;
1679 /* change the direction of the worm */
1683 long cycle_duration
=0;
1685 #ifdef HAS_BUTTON_HOLD
1686 if (rb
->button_hold())
1691 case BTN_STARTPAUSE
:
1696 return 1; /* restart game */
1704 return 2; /* back to menu */
1711 if (players
== 1 && !use_remote
) {
1712 player1_dir
= NORTH
;
1717 if (players
== 1 && !use_remote
) {
1718 player1_dir
= SOUTH
;
1723 if (players
!= 1 || use_remote
) {
1724 player1_dir
= (player1_dir
+ 3) % 4;
1731 if (players
!= 1 || use_remote
) {
1732 player1_dir
= (player1_dir
+ 1) % 4;
1739 case BTN_PLAYER2_DIR1
:
1740 player2_dir
= (player2_dir
+ 3) % 4;
1743 case BTN_PLAYER2_DIR2
:
1744 player2_dir
= (player2_dir
+ 1) % 4;
1750 player3_dir
= (player3_dir
+ 1) % 4;
1754 player3_dir
= (player3_dir
+ 3) % 4;
1760 for (i
= 0; i
< worm_count
; i
++) {
1761 worms
[i
].fetch_worm_direction(&worms
[i
]);
1765 for (i
= 0; i
< worm_count
; i
++){
1766 struct worm
*w
= &worms
[i
];
1768 wormDead
&= process_collisions(w
);
1773 if (button
== BTN_STOPRESET
) {
1777 /* here the wormlet game cycle ends
1778 thus the current tick is stored
1780 cycle_end
= *rb
->current_tick
;
1782 /* The duration of the game cycle */
1783 cycle_duration
= cycle_end
- cycle_start
;
1784 cycle_duration
= MAX(0, cycle_duration
);
1785 cycle_duration
= MIN(speed
-1, cycle_duration
);
1788 #ifdef DEBUG_WORMLET
1789 ticks_to_max_cycle_reset
--;
1790 if (ticks_to_max_cycle_reset
<= 0) {
1794 if (max_cycle
< cycle_duration
) {
1795 max_cycle
= cycle_duration
;
1796 ticks_to_max_cycle_reset
= 20;
1798 rb
->snprintf(buf
, sizeof buf
, "ticks %d", max_cycle
);
1802 /* adjust the number of ticks to wait for a button.
1803 This ensures that a complete game cycle including
1804 user input runs in constant time */
1805 button
= rb
->button_get_w_tmo(speed
- cycle_duration
);
1806 cycle_start
= *rb
->current_tick
;
1809 rb
->splash(HZ
*2, "Game Over!");
1811 return 2; /* back to menu */
1814 #ifdef DEBUG_WORMLET
1817 * Just a test routine that checks that worm_food_collision works
1818 * in some typical situations.
1820 static void test_worm_food_collision(void) {
1821 int collision_count
= 0;
1823 rb
->lcd_clear_display();
1824 init_worm(&worms
[0], 10, 10);
1825 add_growing(&worms
[0], 10);
1826 set_worm_dir(&worms
[0], EAST
);
1827 for (i
= 0; i
< 10; i
++) {
1828 move_worm(&worms
[0]);
1829 draw_worm(&worms
[0]);
1832 set_worm_dir(&worms
[0], SOUTH
);
1833 for (i
= 0; i
< 10; i
++) {
1834 move_worm(&worms
[0]);
1835 draw_worm(&worms
[0]);
1840 for (foody
[0] = 20; foody
[0] > 0; foody
[0] --) {
1843 draw_worm(&worms
[0]);
1845 collision
= worm_food_collision(&worms
[0], 0);
1849 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1850 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1853 if (collision_count
!= food_size
) {
1854 rb
->button_get(true);
1859 for (foodx
[0] = 30; foodx
[0] > 0; foodx
[0] --) {
1862 draw_worm(&worms
[0]);
1864 collision
= worm_food_collision(&worms
[0], 0);
1868 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1869 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1872 if (collision_count
!= food_size
* 2) {
1873 rb
->button_get(true);
1878 static bool expensive_worm_in_rect(struct worm
*w
, int rx
, int ry
, int rw
, int rh
){
1880 bool retVal
= false;
1881 for (x
= rx
; x
< rx
+ rw
; x
++){
1882 for (y
= ry
; y
< ry
+ rh
; y
++) {
1883 if (specific_worm_collision(w
, x
, y
) != -1) {
1891 static void test_worm_argh_collision(void) {
1894 int collision_count
= 0;
1895 rb
->lcd_clear_display();
1896 init_worm(&worms
[0], 10, 10);
1897 add_growing(&worms
[0], 40);
1898 for (dir
= 0; dir
< 4; dir
++) {
1899 set_worm_dir(&worms
[0], (EAST
+ dir
) % 4);
1900 for (i
= 0; i
< 10; i
++) {
1901 move_worm(&worms
[0]);
1902 draw_worm(&worms
[0]);
1907 for (arghy
[0] = 0; arghy
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghy
[0]++){
1911 collision
= worm_argh_collision(&worms
[0], 0);
1915 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1916 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1919 if (collision_count
!= argh_size
* 2) {
1920 rb
->button_get(true);
1924 for (arghx
[0] = 0; arghx
[0] < FIELD_RECT_HEIGHT
- argh_size
; arghx
[0]++){
1928 collision
= worm_argh_collision(&worms
[0], 0);
1932 rb
->snprintf(buf
, sizeof buf
, "collisions: %d", collision_count
);
1933 rb
->lcd_putsxy(0, LCD_HEIGHT
-8, buf
);
1936 if (collision_count
!= argh_size
* 4) {
1937 rb
->button_get(true);
1941 static int testline_in_rect(void) {
1942 int testfailed
= -1;
1955 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1956 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1957 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1958 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1960 rb
->lcd_putsxy(0, 0, "failed 1");
1961 rb
->button_get(true);
1967 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1968 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1969 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1970 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1971 rb
->lcd_putsxy(0, 0, "failed 2");
1973 rb
->button_get(true);
1979 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1980 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1981 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1982 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1983 rb
->lcd_putsxy(0, 0, "failed 3");
1985 rb
->button_get(true);
1991 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
1992 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
1993 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
1994 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
1995 rb
->lcd_putsxy(0, 0, "failed 4");
1997 rb
->button_get(true);
2003 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2004 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2005 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2006 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2007 rb
->lcd_putsxy(0, 0, "failed 5");
2009 rb
->button_get(true);
2016 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2017 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2018 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2019 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2020 rb
->lcd_putsxy(0, 0, "failed 6");
2022 rb
->button_get(true);
2031 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2032 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2033 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2034 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2035 rb
->lcd_putsxy(0, 0, "failed 7");
2037 rb
->button_get(true);
2043 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2044 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2045 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2046 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2047 rb
->lcd_putsxy(0, 0, "failed 8");
2049 rb
->button_get(true);
2055 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2056 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2057 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2058 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2059 rb
->lcd_putsxy(0, 0, "failed 9");
2061 rb
->button_get(true);
2067 if (!line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2068 !line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2069 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2070 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2071 rb
->lcd_putsxy(0, 0, "failed 10");
2073 rb
->button_get(true);
2079 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2080 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2081 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2082 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2083 rb
->lcd_putsxy(0, 0, "failed 11");
2085 rb
->button_get(true);
2092 if (line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) ||
2093 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
)) {
2094 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2095 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2096 rb
->lcd_putsxy(0, 0, "failed 12");
2098 rb
->button_get(true);
2112 if (!(line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2113 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
))) {
2114 rb
->lcd_drawrect(rx
, ry
, rw
, rh
);
2115 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2116 rb
->lcd_putsxy(0, 0, "failed 13");
2118 rb
->button_get(true);
2132 if (!(line_in_rect(x1
, y1
, x2
, y2
, rx
, ry
, rw
, rh
) &&
2133 line_in_rect(x2
, y2
, x1
, y1
, rx
, ry
, rw
, rh
))) {
2134 rb
->lcd_drawline(x1
, y1
, x2
, y2
);
2135 rb
->lcd_invertrect(rx
, ry
, rw
, rh
);
2136 rb
->lcd_putsxy(0, 0, "failed 14");
2138 rb
->button_get(true);
2142 rb
->lcd_clear_display();
2148 * Just a test routine to test wether specific_worm_collision might work properly
2150 static int test_specific_worm_collision(void) {
2156 rb
->lcd_clear_display();
2157 init_worm(&worms
[0], 10, 20);
2158 add_growing(&worms
[0], 20 - INITIAL_WORM_LENGTH
);
2160 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2162 set_worm_dir(&worms
[0], dir
% 4);
2163 for (i
= 0; i
< 5; i
++) {
2164 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2165 move_worm(&worms
[0]);
2166 draw_worm(&worms
[0]);
2171 for (y
= 15; y
< 30; y
++){
2172 for (x
= 5; x
< 20; x
++) {
2173 if (specific_worm_collision(&worms
[0], x
, y
) != -1) {
2176 rb
->lcd_invertpixel(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
);
2177 rb
->snprintf(buf
, sizeof buf
, "collisions %d", collisions
);
2178 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2182 if (collisions
!= 21) {
2183 rb
->button_get(true);
2188 static void test_make_argh(void){
2193 int last_failures
= 0;
2195 rb
->lcd_clear_display();
2198 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2199 init_worm(&worms
[worm_idx
], 10 + worm_idx
* 20, 20);
2200 add_growing(&worms
[worm_idx
], 40 - INITIAL_WORM_LENGTH
);
2203 for (dir
= EAST
; dir
< EAST
+ 4; dir
++) {
2204 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++) {
2205 set_worm_dir(&worms
[worm_idx
], dir
% 4);
2206 for (i
= 0; i
< 10; i
++) {
2207 if (!(dir
% 4 == NORTH
&& i
== 9)) {
2208 move_worm(&worms
[worm_idx
]);
2209 draw_worm(&worms
[worm_idx
]);
2217 for (seed
= 0; hit
< 20; seed
+= 2) {
2221 x
= rb
->rand() % (FIELD_RECT_WIDTH
- argh_size
);
2222 y
= rb
->rand() % (FIELD_RECT_HEIGHT
- argh_size
);
2224 for (worm_idx
= 0; worm_idx
< worm_count
; worm_idx
++){
2225 if (expensive_worm_in_rect(&worms
[worm_idx
], x
, y
, argh_size
, argh_size
)) {
2229 tries
= make_argh(0);
2230 if ((x
== arghx
[0] && y
== arghy
[0]) || tries
< 2) {
2234 rb
->snprintf(buf
, sizeof buf
, "(%d;%d) fail%d try%d", x
, y
, failures
, tries
);
2235 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2237 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
, argh_size
, argh_size
);
2241 rb
->lcd_invertrect(x
+ FIELD_RECT_X
, y
+ FIELD_RECT_Y
, argh_size
, argh_size
);
2242 rb
->lcd_clearrect(arghx
[0] + FIELD_RECT_X
, arghy
[0] + FIELD_RECT_Y
, argh_size
, argh_size
);
2244 if (failures
> last_failures
) {
2245 rb
->button_get(true);
2247 last_failures
= failures
;
2254 static void test_worm_argh_collision_in_moves(void) {
2257 rb
->lcd_clear_display();
2258 init_worm(&worms
[0], 10, 20);
2264 set_worm_dir(&worms
[0], EAST
);
2265 for (i
= 0; i
< 20; i
++) {
2267 move_worm(&worms
[0]);
2268 draw_worm(&worms
[0]);
2269 if (worm_argh_collision_in_moves(&worms
[0], 0, 5)){
2272 rb
->snprintf(buf
, sizeof buf
, "in 5 moves hits: %d", hit_count
);
2273 rb
->lcd_putsxy(0, LCD_HEIGHT
- 8, buf
);
2276 if (hit_count
!= argh_size
+ 5) {
2277 rb
->button_get(true);
2280 #endif /* DEBUG_WORMLET */
2282 extern bool use_old_rect
;
2285 * These are additional functions required to set various wormlet settings
2286 * and to enable saving of settings and high score
2290 Sets the total number of worms, both human and machine
2292 bool set_worm_num_worms(void)
2295 static const struct opt_items num_worms_option
[3] = {
2301 ret
= rb
->set_option("Number Of Worms", &worm_count
,INT
, num_worms_option
, 3, NULL
);
2302 if (worm_count
< players
) {
2309 Sets the number of human players
2311 bool set_worm_num_players(void)
2314 static const struct opt_items num_players_option
[4] = {
2321 ret
= rb
->set_option("Number of Players", &players
, INT
,num_players_option
, 4, NULL
);
2322 if (players
> worm_count
) {
2323 worm_count
= players
;
2332 Sets the size of each argh block created
2334 bool set_worm_argh_size(void)
2336 static const struct opt_items argh_size_option
[11] = {
2350 return rb
->set_option("Argh Size", &argh_size
,INT
,argh_size_option
, 11, NULL
);
2354 Sets the amount a worm grows per food
2356 bool set_worm_food(void)
2358 static const struct opt_items worm_food_option
[11] = {
2371 return rb
->set_option("Worm Growth Per Food", &worm_food
,INT
,worm_food_option
, 11, NULL
);
2375 Sets the number of arghs created per food
2377 bool set_argh_per_food(void)
2379 static const struct opt_items argh_food_option
[5] = {
2386 return rb
->set_option("Arghs Per Food", &arghs_per_food
,INT
,argh_food_option
, 5, NULL
);
2390 Sets the number of ticks per move
2392 bool set_worm_speed(void)
2395 static const struct opt_items speed_option
[19] = {
2419 ret
= rb
->set_option("Worm Speed", &speed
,INT
,speed_option
, 19, NULL
);
2425 Sets the control style, which depends on the number of players,
2426 remote control existing, etc
2427 bool set_bool_options(char* string, bool* variable,
2428 char* yes_str, char* no_str, void (*function)(bool))
2430 bool set_worm_control_style(void)
2432 static const struct opt_items key1_option
[2] = {
2433 { "Remote Control", -1 },
2434 { "No Rem. Control", -1 },
2437 static const struct opt_items key2_option
[2] = {
2438 { "2 Key Control", -1 },
2439 { "4 Key COntrol", -1 },
2442 static const struct opt_items key3_option
[1] = {
2443 { "Out of Control", -1 },
2447 rb
->set_option("Control Style",&use_remote
,INT
, key1_option
, 2, NULL
);
2450 rb
->set_option("Control Style",&use_remote
,INT
, key2_option
, 2, NULL
);
2453 rb
->set_option("Control Style",&use_remote
,INT
, key3_option
, 1, NULL
);
2459 void default_settings(void)
2461 arghs_per_food
= ARGHS_PER_FOOD
;
2462 argh_size
= ARGH_SIZE
;
2463 food_size
= FOOD_SIZE
;
2465 worm_food
= WORM_PER_FOOD
;
2467 worm_count
= MAX_WORMS
;
2473 Launches the wormlet game
2475 bool launch_wormlet(void)
2477 int game_result
= 1;
2479 rb
->lcd_clear_display();
2481 /* Turn off backlight timeout */
2482 backlight_force_on(); /* backlight control in lib/helper.c */
2484 /* start the game */
2485 while (game_result
== 1)
2486 game_result
= run();
2488 switch (game_result
)
2491 /* Turn on backlight timeout (revert to settings) */
2492 backlight_use_settings(); /* backlight control in lib/helper.c */
2499 /* End of settings/changes etc */
2504 enum plugin_status
plugin_start(const void* parameter
)
2513 if (configfile_load(SETTINGS_FILENAME
, config
,
2514 sizeof(config
)/sizeof(*config
),
2515 SETTINGS_MIN_VERSION
) < 0)
2517 /* If the loading failed, save a new config file (as the disk is
2518 already spinning) */
2519 configfile_save(SETTINGS_FILENAME
, config
,
2520 sizeof(config
)/sizeof(*config
),
2524 #ifdef HAVE_LCD_COLOR
2525 rb
->lcd_set_foreground(COLOR_FG
);
2526 rb
->lcd_set_background(COLOR_BG
);
2530 rb
->lcd_set_backdrop(NULL
);
2533 #ifdef DEBUG_WORMLET
2535 test_worm_argh_collision_in_moves();
2537 test_worm_food_collision();
2538 test_worm_argh_collision();
2539 test_specific_worm_collision();
2544 static const struct opt_items noyes
[2] = {
2549 static const struct opt_items num_worms_option
[3] = {
2556 static const struct opt_items num_players_option
[4] = {
2558 static const struct opt_items num_players_option
[2] = {
2568 static const struct opt_items argh_size_option
[9] = {
2580 static const struct opt_items food_size_option
[9] = {
2592 static const struct opt_items worm_food_option
[16] = {
2611 static const struct opt_items argh_food_option
[9] = {
2623 static const struct opt_items speed_option
[21] = {
2647 static const struct opt_items remoteonly_option
[1] = {
2648 { "Remote Control", -1 }
2651 static const struct opt_items key24_option
[2] = {
2652 { "4 Key Control", -1 },
2653 { "2 Key Control", -1 }
2657 static const struct opt_items remote_option
[2] = {
2658 { "Remote Control", -1 },
2659 { "No Rem. Control", -1 }
2662 static const struct opt_items key2_option
[1] = {
2663 { "2 Key Control", -1 }
2667 static const struct opt_items nokey_option
[1] = {
2668 { "Out of Control", -1 }
2671 MENUITEM_STRINGLIST(menu
, "Wormlet Menu", NULL
, "Play Wormlet!",
2672 "Number of Worms", "Number of Players", "Control Style",
2673 "Worm Growth Per Food","Worm Speed","Arghs Per Food",
2674 "Argh Size","Food Size","Revert to Default Settings",
2675 "Playback Control", "Quit");
2677 rb
->button_clear_queue();
2679 while (!menu_quit
) {
2680 switch(rb
->do_menu(&menu
, &result
, NULL
, false))
2683 rb
->lcd_setfont(FONT_SYSFIXED
);
2687 new_setting
= worm_count
- 1;
2688 rb
->set_option("Number of Worms", &new_setting
, INT
, num_worms_option
, 3, NULL
);
2689 if (new_setting
!= worm_count
)
2690 worm_count
= new_setting
+ 1;
2691 if (worm_count
< players
) {
2692 worm_count
= players
;
2696 new_setting
= players
;
2698 rb
->set_option("Number of Players", &new_setting
, INT
, num_players_option
, 4, NULL
);
2700 rb
->set_option("Number of Players", &new_setting
, INT
, num_players_option
, 2, NULL
);
2702 if (new_setting
!= players
)
2703 players
= new_setting
;
2704 if (players
> worm_count
) {
2705 worm_count
= players
;
2712 new_setting
= use_remote
;
2715 rb
->set_option("Control Style",&new_setting
,INT
, nokey_option
, 1, NULL
);
2718 rb
->set_option("Control Style",&new_setting
,INT
, key24_option
, 2, NULL
);
2722 rb
->set_option("Control Style",&new_setting
,INT
, remote_option
, 2, NULL
);
2724 rb
->set_option("Control Style",&new_setting
,INT
, key2_option
, 1, NULL
);
2728 rb
->set_option("Control Style",&new_setting
,INT
, remoteonly_option
, 1, NULL
);
2731 if (new_setting
!= use_remote
)
2732 use_remote
= new_setting
;
2735 new_setting
= worm_food
;
2736 rb
->set_option("Worm Growth Per Food", &new_setting
,INT
,worm_food_option
, 16, NULL
);
2737 if (new_setting
!= worm_food
)
2738 worm_food
= new_setting
;
2741 new_setting
= speed
;
2742 new_setting
= 20 - new_setting
;
2743 rb
->set_option("Worm Speed", &new_setting
,INT
,speed_option
, 21, NULL
);
2744 new_setting
= 20 - new_setting
;
2745 if (new_setting
!= speed
)
2746 speed
= new_setting
;
2749 new_setting
= arghs_per_food
;
2750 rb
->set_option("Arghs Per Food", &new_setting
,INT
,argh_food_option
, 9, NULL
);
2751 if (new_setting
!= arghs_per_food
)
2752 arghs_per_food
= new_setting
;
2755 new_setting
= argh_size
-2;
2756 rb
->set_option("Argh Size", &new_setting
,INT
,argh_size_option
, 9, NULL
);
2757 if (new_setting
!= argh_size
)
2758 argh_size
= new_setting
+2;
2761 new_setting
= food_size
-2;
2762 rb
->set_option("Food Size", &new_setting
,INT
,food_size_option
, 9, NULL
);
2763 if (new_setting
!= food_size
)
2764 food_size
= new_setting
+2;
2768 rb
->set_option("Reset Settings?", &new_setting
,INT
, noyes
, 2, NULL
);
2769 if (new_setting
== 1)
2773 playback_control(NULL
);
2781 configfile_save(SETTINGS_FILENAME
, config
,
2782 sizeof(config
)/sizeof(*config
),