diff options
author | Björn Stenberg <bjorn@haxx.se> | 2003-06-29 16:33:04 +0000 |
---|---|---|
committer | Björn Stenberg <bjorn@haxx.se> | 2003-06-29 16:33:04 +0000 |
commit | ba371fb595affd68c823926b85718d1d613dc7d3 (patch) | |
tree | cfda303d0603d623cdb12f3928905d3ae02f1d87 /apps/plugins/wormlet.c | |
parent | 9bcbe3fd723d23a709873a0855f27b86bc5c96f1 (diff) | |
download | rockbox-ba371fb595affd68c823926b85718d1d613dc7d3.tar.gz rockbox-ba371fb595affd68c823926b85718d1d613dc7d3.zip |
Added plugin loader. Moved games, demos and the text viewer to loadable plugins. Copy your *.rock files to /.rockbox/rocks/
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3769 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/wormlet.c')
-rw-r--r-- | apps/plugins/wormlet.c | 2009 |
1 files changed, 2009 insertions, 0 deletions
diff --git a/apps/plugins/wormlet.c b/apps/plugins/wormlet.c new file mode 100644 index 0000000000..be089cdb7c --- /dev/null +++ b/apps/plugins/wormlet.c | |||
@@ -0,0 +1,2009 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Philipp Pertermann | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "plugin.h" | ||
20 | |||
21 | #ifdef HAVE_LCD_BITMAP | ||
22 | |||
23 | /* size of the field the worm lives in */ | ||
24 | #define FIELD_RECT_X 1 | ||
25 | #define FIELD_RECT_Y 1 | ||
26 | #define FIELD_RECT_WIDTH (LCD_WIDTH - 45) | ||
27 | #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2) | ||
28 | |||
29 | /* size of the ring of the worm | ||
30 | choos a value that is a power of 2 to help | ||
31 | the compiler optimize modul operations*/ | ||
32 | #define MAX_WORM_SEGMENTS 64 | ||
33 | |||
34 | /* when the game starts */ | ||
35 | #define INITIAL_WORM_LENGTH 10 | ||
36 | |||
37 | /* num of pixel the worm grows per eaten food */ | ||
38 | #define WORM_PER_FOOD 7 | ||
39 | |||
40 | /* num of worms creeping in the FIELD */ | ||
41 | #define MAX_WORMS 3 | ||
42 | |||
43 | /* minimal distance between a worm and an argh | ||
44 | when a new argh is made */ | ||
45 | #define MIN_ARGH_DIST 5 | ||
46 | |||
47 | /** | ||
48 | * All the properties that a worm has. | ||
49 | */ | ||
50 | static struct worm { | ||
51 | /* The worm is stored in a ring of xy coordinates */ | ||
52 | char x[MAX_WORM_SEGMENTS]; | ||
53 | char y[MAX_WORM_SEGMENTS]; | ||
54 | |||
55 | int head; /* index of the head within the buffer */ | ||
56 | int tail; /* index of the tail within the buffer */ | ||
57 | int growing; /* number of cyles the worm still keeps growing */ | ||
58 | bool alive; /* the worms living state */ | ||
59 | |||
60 | /* direction vector in which the worm moves */ | ||
61 | int dirx; /* only values -1 0 1 allowed */ | ||
62 | int diry; /* only values -1 0 1 allowed */ | ||
63 | |||
64 | /* this method is used to fetch the direction the user | ||
65 | has selected. It can be one of the values | ||
66 | human_player1, human_player2, remote_player, virtual_player. | ||
67 | All these values are fuctions, that can change the direction | ||
68 | of the worm */ | ||
69 | void (*fetch_worm_direction)(struct worm *w); | ||
70 | } worms[MAX_WORMS]; | ||
71 | |||
72 | /* stores the highscore - besides it was scored by a virtual player */ | ||
73 | static int highscore; | ||
74 | |||
75 | #define MAX_FOOD 5 /* maximal number of food items */ | ||
76 | #define FOOD_SIZE 3 /* the width and height of a food */ | ||
77 | |||
78 | /* The arrays store the food coordinates */ | ||
79 | static char foodx[MAX_FOOD]; | ||
80 | static char foody[MAX_FOOD]; | ||
81 | |||
82 | #define MAX_ARGH 100 /* maximal number of argh items */ | ||
83 | #define ARGH_SIZE 4 /* the width and height of a argh */ | ||
84 | #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */ | ||
85 | |||
86 | /* The arrays store the argh coordinates */ | ||
87 | static char arghx[MAX_ARGH]; | ||
88 | static char arghy[MAX_ARGH]; | ||
89 | |||
90 | /* the number of arghs that are currently in use */ | ||
91 | static int argh_count; | ||
92 | |||
93 | #ifdef DEBUG_WORMLET | ||
94 | /* just a buffer used for debug output */ | ||
95 | static char debugout[15]; | ||
96 | #endif | ||
97 | |||
98 | /* the number of ticks each game cycle should take */ | ||
99 | #define SPEED 14 | ||
100 | |||
101 | /* the number of active worms (dead or alive) */ | ||
102 | static int worm_count = MAX_WORMS; | ||
103 | |||
104 | /* in multiplayer mode: en- / disables the remote worm control | ||
105 | in singleplayer mode: toggles 4 / 2 button worm control */ | ||
106 | static bool use_remote = false; | ||
107 | |||
108 | /* return values of check_collision */ | ||
109 | #define COLLISION_NONE 0 | ||
110 | #define COLLISION_WORM 1 | ||
111 | #define COLLISION_FOOD 2 | ||
112 | #define COLLISION_ARGH 3 | ||
113 | #define COLLISION_FIELD 4 | ||
114 | |||
115 | /* constants for use as directions. | ||
116 | Note that the values are ordered clockwise. | ||
117 | Thus increasing / decreasing the values | ||
118 | is equivalent to right / left turns. */ | ||
119 | #define WEST 0 | ||
120 | #define NORTH 1 | ||
121 | #define EAST 2 | ||
122 | #define SOUTH 3 | ||
123 | |||
124 | /* direction of human player 1 */ | ||
125 | static int player1_dir = EAST; | ||
126 | /* direction of human player 2 */ | ||
127 | static int player2_dir = EAST; | ||
128 | /* direction of human player 3 */ | ||
129 | static int player3_dir = EAST; | ||
130 | |||
131 | /* the number of (human) players that currently | ||
132 | control a worm */ | ||
133 | static int players = 1; | ||
134 | |||
135 | /* the rockbox plugin api */ | ||
136 | static struct plugin_api* rb; | ||
137 | |||
138 | #ifdef DEBUG_WORMLET | ||
139 | static void set_debug_out(char *str){ | ||
140 | strcpy(debugout, str); | ||
141 | } | ||
142 | #endif | ||
143 | |||
144 | /** | ||
145 | * Returns the direction id in which the worm | ||
146 | * currently is creeping. | ||
147 | * @param struct worm *w The worm that is to be investigated. | ||
148 | * w Must not be null. | ||
149 | * @return int A value 0 <= value < 4 | ||
150 | * Note the predefined constants NORTH, SOUTH, EAST, WEST | ||
151 | */ | ||
152 | static int get_worm_dir(struct worm *w) { | ||
153 | int retVal ; | ||
154 | if (w->dirx == 0) { | ||
155 | if (w->diry == 1) { | ||
156 | retVal = SOUTH; | ||
157 | } else { | ||
158 | retVal = NORTH; | ||
159 | } | ||
160 | } else { | ||
161 | if (w->dirx == 1) { | ||
162 | retVal = EAST; | ||
163 | } else { | ||
164 | retVal = WEST; | ||
165 | } | ||
166 | } | ||
167 | return retVal; | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Set the direction of the specified worm with a direction id. | ||
172 | * Increasing the value by 1 means to turn the worm direction | ||
173 | * to right by 90 degree. | ||
174 | * @param struct worm *w The worm that is to be altered. w Must not be null. | ||
175 | * @param int dir The new direction in which the worm is to creep. | ||
176 | * dir must be 0 <= dir < 4. Use predefined constants | ||
177 | * NORTH, SOUTH, EAST, WEST | ||
178 | */ | ||
179 | static void set_worm_dir(struct worm *w, int dir) { | ||
180 | switch (dir) { | ||
181 | case WEST: | ||
182 | w->dirx = -1; | ||
183 | w->diry = 0; | ||
184 | break; | ||
185 | case NORTH: | ||
186 | w->dirx = 0; | ||
187 | w->diry = - 1; | ||
188 | break; | ||
189 | case EAST: | ||
190 | w->dirx = 1; | ||
191 | w->diry = 0; | ||
192 | break; | ||
193 | case SOUTH: | ||
194 | w->dirx = 0; | ||
195 | w->diry = 1; | ||
196 | break; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * Returns the current length of the worm array. This | ||
202 | * is also a value for the number of bends that are in the worm. | ||
203 | * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS | ||
204 | */ | ||
205 | static int get_worm_array_length(struct worm *w) { | ||
206 | /* initial simple calculation will be overwritten if wrong. */ | ||
207 | int retVal = w->head - w->tail; | ||
208 | |||
209 | /* if the worm 'crosses' the boundaries of the ringbuffer */ | ||
210 | if (retVal < 0) { | ||
211 | retVal = w->head + MAX_WORM_SEGMENTS - w->tail; | ||
212 | } | ||
213 | |||
214 | return retVal; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * Returns the score the specified worm. The score is the length | ||
219 | * of the worm. | ||
220 | * @param struct worm *w The worm that is to be investigated. | ||
221 | * w must not be null. | ||
222 | * @return int The length of the worm (>= 0). | ||
223 | */ | ||
224 | static int get_score(struct worm *w) { | ||
225 | int retval = 0; | ||
226 | int length = get_worm_array_length(w); | ||
227 | int i; | ||
228 | for (i = 0; i < length; i++) { | ||
229 | |||
230 | /* The iteration iterates the length of the worm. | ||
231 | Here's the conversion to the true indices within the worm arrays. */ | ||
232 | int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; | ||
233 | int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; | ||
234 | int startx = w->x[linestart]; | ||
235 | int starty = w->y[linestart]; | ||
236 | int endx = w->x[lineend]; | ||
237 | int endy = w->y[lineend]; | ||
238 | |||
239 | int minimum, maximum; | ||
240 | |||
241 | if (startx == endx) { | ||
242 | minimum = MIN(starty, endy); | ||
243 | maximum = MAX(starty, endy); | ||
244 | } else { | ||
245 | minimum = MIN(startx, endx); | ||
246 | maximum = MAX(startx, endx); | ||
247 | } | ||
248 | retval += abs(maximum - minimum); | ||
249 | } | ||
250 | return retval; | ||
251 | } | ||
252 | |||
253 | /** | ||
254 | * Determines wether the line specified by startx, starty, endx, endy intersects | ||
255 | * the rectangle specified by x, y, width, height. Note that the line must be exactly | ||
256 | * horizontal or vertical (startx == endx or starty == endy). | ||
257 | * @param int startx The x coordinate of the start point of the line. | ||
258 | * @param int starty The y coordinate of the start point of the line. | ||
259 | * @param int endx The x coordinate of the end point of the line. | ||
260 | * @param int endy The y coordinate of the end point of the line. | ||
261 | * @param int x The x coordinate of the top left corner of the rectangle. | ||
262 | * @param int y The y coordinate of the top left corner of the rectangle. | ||
263 | * @param int width The width of the rectangle. | ||
264 | * @param int height The height of the rectangle. | ||
265 | * @return bool Returns true if the specified line intersects with the recangle. | ||
266 | */ | ||
267 | static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) { | ||
268 | bool retval = false; | ||
269 | int simple, simplemin, simplemax; | ||
270 | int compa, compb, compmin, compmax; | ||
271 | int temp; | ||
272 | if (startx == endx) { | ||
273 | simple = startx; | ||
274 | simplemin = x; | ||
275 | simplemax = x + width; | ||
276 | |||
277 | compa = starty; | ||
278 | compb = endy; | ||
279 | compmin = y; | ||
280 | compmax = y + height; | ||
281 | } else { | ||
282 | simple = starty; | ||
283 | simplemin = y; | ||
284 | simplemax = y + height; | ||
285 | |||
286 | compa = startx; | ||
287 | compb = endx; | ||
288 | compmin = x; | ||
289 | compmax = x + width; | ||
290 | }; | ||
291 | |||
292 | temp = compa; | ||
293 | compa = MIN(compa, compb); | ||
294 | compb = MAX(temp, compb); | ||
295 | |||
296 | if (simplemin <= simple && simple <= simplemax) { | ||
297 | if ((compmin <= compa && compa <= compmax) || | ||
298 | (compmin <= compb && compb <= compmax) || | ||
299 | (compa <= compmin && compb >= compmax)) { | ||
300 | retval = true; | ||
301 | } | ||
302 | } | ||
303 | return retval; | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * Tests wether the specified worm intersects with the rect. | ||
308 | * @param struct worm *w The worm to be investigated | ||
309 | * @param int x The x coordinate of the top left corner of the rect | ||
310 | * @param int y The y coordinate of the top left corner of the rect | ||
311 | * @param int widht The width of the rect | ||
312 | * @param int height The height of the rect | ||
313 | * @return bool Returns true if the worm intersects with the rect | ||
314 | */ | ||
315 | static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) { | ||
316 | bool retval = false; | ||
317 | |||
318 | |||
319 | /* get_worm_array_length is expensive -> buffer the value */ | ||
320 | int wormLength = get_worm_array_length(w); | ||
321 | int i; | ||
322 | |||
323 | /* test each entry that is part of the worm */ | ||
324 | for (i = 0; i < wormLength && retval == false; i++) { | ||
325 | |||
326 | /* The iteration iterates the length of the worm. | ||
327 | Here's the conversion to the true indices within the worm arrays. */ | ||
328 | int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; | ||
329 | int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; | ||
330 | int startx = w->x[linestart]; | ||
331 | int starty = w->y[linestart]; | ||
332 | int endx = w->x[lineend]; | ||
333 | int endy = w->y[lineend]; | ||
334 | |||
335 | retval = line_in_rect(startx, starty, endx, endy, x, y, width, height); | ||
336 | } | ||
337 | |||
338 | return retval; | ||
339 | } | ||
340 | |||
341 | /** | ||
342 | * Checks wether a specific food in the food arrays is at the | ||
343 | * specified coordinates. | ||
344 | * @param int foodIndex The index of the food in the food arrays | ||
345 | * @param int x the x coordinate. | ||
346 | * @param int y the y coordinate. | ||
347 | * @return Returns true if the coordinate hits the food specified by | ||
348 | * foodIndex. | ||
349 | */ | ||
350 | static bool specific_food_collision(int foodIndex, int x, int y) { | ||
351 | bool retVal = false; | ||
352 | if (x >= foodx[foodIndex] && | ||
353 | x < foodx[foodIndex] + FOOD_SIZE && | ||
354 | y >= foody[foodIndex] && | ||
355 | y < foody[foodIndex] + FOOD_SIZE) { | ||
356 | |||
357 | retVal = true; | ||
358 | } | ||
359 | return retVal; | ||
360 | } | ||
361 | |||
362 | /** | ||
363 | * Returns the index of the food that is at the | ||
364 | * given coordinates. If no food is at the coordinates | ||
365 | * -1 is returned. | ||
366 | * @return int -1 <= value < MAX_FOOD | ||
367 | */ | ||
368 | static int food_collision(int x, int y) { | ||
369 | int i = 0; | ||
370 | int retVal = -1; | ||
371 | for (i = 0; i < MAX_FOOD; i++) { | ||
372 | if (specific_food_collision(i, x, y)) { | ||
373 | retVal = i; | ||
374 | break; | ||
375 | } | ||
376 | } | ||
377 | return retVal; | ||
378 | } | ||
379 | |||
380 | /** | ||
381 | * Checks wether a specific argh in the argh arrays is at the | ||
382 | * specified coordinates. | ||
383 | * @param int arghIndex The index of the argh in the argh arrays | ||
384 | * @param int x the x coordinate. | ||
385 | * @param int y the y coordinate. | ||
386 | * @return Returns true if the coordinate hits the argh specified by | ||
387 | * arghIndex. | ||
388 | */ | ||
389 | static bool specific_argh_collision(int arghIndex, int x, int y) { | ||
390 | |||
391 | if ( x >= arghx[arghIndex] && | ||
392 | y >= arghy[arghIndex] && | ||
393 | x < arghx[arghIndex] + ARGH_SIZE && | ||
394 | y < arghy[arghIndex] + ARGH_SIZE ) | ||
395 | { | ||
396 | return true; | ||
397 | } | ||
398 | |||
399 | return false; | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * Returns the index of the argh that is at the | ||
404 | * given coordinates. If no argh is at the coordinates | ||
405 | * -1 is returned. | ||
406 | * @param int x The x coordinate. | ||
407 | * @param int y The y coordinate. | ||
408 | * @return int -1 <= value < argh_count <= MAX_ARGH | ||
409 | */ | ||
410 | static int argh_collision(int x, int y) { | ||
411 | int i = 0; | ||
412 | int retVal = -1; | ||
413 | |||
414 | /* search for the argh that has the specified coords */ | ||
415 | for (i = 0; i < argh_count; i++) { | ||
416 | if (specific_argh_collision(i, x, y)) { | ||
417 | retVal = i; | ||
418 | break; | ||
419 | } | ||
420 | } | ||
421 | return retVal; | ||
422 | } | ||
423 | |||
424 | /** | ||
425 | * Checks wether the worm collides with the food at the specfied food-arrays. | ||
426 | * @param int foodIndex The index of the food in the arrays. Ensure the value is | ||
427 | * 0 <= foodIndex <= MAX_FOOD | ||
428 | * @return Returns true if the worm collides with the specified food. | ||
429 | */ | ||
430 | static bool worm_food_collision(struct worm *w, int foodIndex) | ||
431 | { | ||
432 | bool retVal = false; | ||
433 | |||
434 | retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex], | ||
435 | FOOD_SIZE - 1, FOOD_SIZE - 1); | ||
436 | |||
437 | return retVal; | ||
438 | } | ||
439 | |||
440 | /** | ||
441 | * Returns true if the worm hits the argh within the next moves (unless | ||
442 | * the worm changes it's direction). | ||
443 | * @param struct worm *w - The worm to investigate | ||
444 | * @param int argh_idx - The index of the argh | ||
445 | * @param int moves - The number of moves that are considered. | ||
446 | * @return Returns false if the specified argh is not hit within the next | ||
447 | * moves. | ||
448 | */ | ||
449 | static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){ | ||
450 | bool retVal = false; | ||
451 | int x1, y1, x2, y2; | ||
452 | x1 = w->x[w->head]; | ||
453 | y1 = w->y[w->head]; | ||
454 | |||
455 | x2 = w->x[w->head] + moves * w->dirx; | ||
456 | y2 = w->y[w->head] + moves * w->diry; | ||
457 | |||
458 | retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx], | ||
459 | ARGH_SIZE, ARGH_SIZE); | ||
460 | return retVal; | ||
461 | } | ||
462 | |||
463 | /** | ||
464 | * Checks wether the worm collides with the argh at the specfied argh-arrays. | ||
465 | * @param int arghIndex The index of the argh in the arrays. | ||
466 | * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH | ||
467 | * @return Returns true if the worm collides with the specified argh. | ||
468 | */ | ||
469 | static bool worm_argh_collision(struct worm *w, int arghIndex) | ||
470 | { | ||
471 | bool retVal = false; | ||
472 | |||
473 | retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex], | ||
474 | ARGH_SIZE - 1, ARGH_SIZE - 1); | ||
475 | |||
476 | return retVal; | ||
477 | } | ||
478 | |||
479 | /** | ||
480 | * Find new coordinates for the food stored in foodx[index], foody[index] | ||
481 | * that don't collide with any other food or argh | ||
482 | * @param int index | ||
483 | * Ensure that 0 <= index < MAX_FOOD. | ||
484 | */ | ||
485 | static int make_food(int index) { | ||
486 | |||
487 | int x = 0; | ||
488 | int y = 0; | ||
489 | bool collisionDetected = false; | ||
490 | int tries = 0; | ||
491 | int i; | ||
492 | |||
493 | do { | ||
494 | /* make coordinates for a new food so that | ||
495 | the entire food lies within the FIELD */ | ||
496 | x = rb->rand() % (FIELD_RECT_WIDTH - FOOD_SIZE); | ||
497 | y = rb->rand() % (FIELD_RECT_HEIGHT - FOOD_SIZE); | ||
498 | tries ++; | ||
499 | |||
500 | /* Ensure that the new food doesn't collide with any | ||
501 | existing foods or arghs. | ||
502 | If one or more corners of the new food hit any existing | ||
503 | argh or food a collision is detected. | ||
504 | */ | ||
505 | collisionDetected = | ||
506 | food_collision(x , y ) >= 0 || | ||
507 | food_collision(x , y + FOOD_SIZE - 1) >= 0 || | ||
508 | food_collision(x + FOOD_SIZE - 1, y ) >= 0 || | ||
509 | food_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0 || | ||
510 | argh_collision(x , y ) >= 0 || | ||
511 | argh_collision(x , y + FOOD_SIZE - 1) >= 0 || | ||
512 | argh_collision(x + FOOD_SIZE - 1, y ) >= 0 || | ||
513 | argh_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0; | ||
514 | |||
515 | /* use coordinates for further testing */ | ||
516 | foodx[index] = x; | ||
517 | foody[index] = y; | ||
518 | |||
519 | /* now test wether we accidently hit the worm with food ;) */ | ||
520 | i = 0; | ||
521 | for (i = 0; i < worm_count && !collisionDetected; i++) { | ||
522 | collisionDetected |= worm_food_collision(&worms[i], index); | ||
523 | } | ||
524 | } | ||
525 | while (collisionDetected); | ||
526 | return tries; | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * Clears a food from the lcd buffer. | ||
531 | * @param int index The index of the food arrays under which | ||
532 | * the coordinates of the desired food can be found. Ensure | ||
533 | * that the value is 0 <= index <= MAX_FOOD. | ||
534 | */ | ||
535 | static void clear_food(int index) | ||
536 | { | ||
537 | /* remove the old food from the screen */ | ||
538 | rb->lcd_clearrect(foodx[index] + FIELD_RECT_X, | ||
539 | foody[index] + FIELD_RECT_Y, | ||
540 | FOOD_SIZE, FOOD_SIZE); | ||
541 | } | ||
542 | |||
543 | /** | ||
544 | * Draws a food in the lcd buffer. | ||
545 | * @param int index The index of the food arrays under which | ||
546 | * the coordinates of the desired food can be found. Ensure | ||
547 | * that the value is 0 <= index <= MAX_FOOD. | ||
548 | */ | ||
549 | static void draw_food(int index) | ||
550 | { | ||
551 | /* draw the food object */ | ||
552 | rb->lcd_fillrect(foodx[index] + FIELD_RECT_X, | ||
553 | foody[index] + FIELD_RECT_Y, | ||
554 | FOOD_SIZE, FOOD_SIZE); | ||
555 | rb->lcd_clearrect(foodx[index] + FIELD_RECT_X + 1, | ||
556 | foody[index] + FIELD_RECT_Y + 1, | ||
557 | FOOD_SIZE - 2, FOOD_SIZE - 2); | ||
558 | } | ||
559 | |||
560 | /** | ||
561 | * Find new coordinates for the argh stored in arghx[index], arghy[index] | ||
562 | * that don't collide with any other food or argh. | ||
563 | * @param int index | ||
564 | * Ensure that 0 <= index < argh_count < MAX_ARGH. | ||
565 | */ | ||
566 | static int make_argh(int index) | ||
567 | { | ||
568 | int x = -1; | ||
569 | int y = -1; | ||
570 | bool collisionDetected = false; | ||
571 | int tries = 0; | ||
572 | int i; | ||
573 | |||
574 | do { | ||
575 | /* make coordinates for a new argh so that | ||
576 | the entire food lies within the FIELD */ | ||
577 | x = rb->rand() % (FIELD_RECT_WIDTH - ARGH_SIZE); | ||
578 | y = rb->rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE); | ||
579 | tries ++; | ||
580 | |||
581 | /* Ensure that the new argh doesn't intersect with any | ||
582 | existing foods or arghs. | ||
583 | If one or more corners of the new argh hit any existing | ||
584 | argh or food an intersection is detected. | ||
585 | */ | ||
586 | collisionDetected = | ||
587 | food_collision(x , y ) >= 0 || | ||
588 | food_collision(x , y + ARGH_SIZE - 1) >= 0 || | ||
589 | food_collision(x + ARGH_SIZE - 1, y ) >= 0 || | ||
590 | food_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0 || | ||
591 | argh_collision(x , y ) >= 0 || | ||
592 | argh_collision(x , y + ARGH_SIZE - 1) >= 0 || | ||
593 | argh_collision(x + ARGH_SIZE - 1, y ) >= 0 || | ||
594 | argh_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0; | ||
595 | |||
596 | /* use the candidate coordinates to make a real argh */ | ||
597 | arghx[index] = x; | ||
598 | arghy[index] = y; | ||
599 | |||
600 | /* now test wether we accidently hit the worm with argh ;) */ | ||
601 | for (i = 0; i < worm_count && !collisionDetected; i++) { | ||
602 | collisionDetected |= worm_argh_collision(&worms[i], index); | ||
603 | collisionDetected |= worm_argh_collision_in_moves(&worms[i], index, | ||
604 | MIN_ARGH_DIST); | ||
605 | } | ||
606 | } | ||
607 | while (collisionDetected); | ||
608 | return tries; | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * Draws an argh in the lcd buffer. | ||
613 | * @param int index The index of the argh arrays under which | ||
614 | * the coordinates of the desired argh can be found. Ensure | ||
615 | * that the value is 0 <= index < argh_count <= MAX_ARGH. | ||
616 | */ | ||
617 | static void draw_argh(int index) | ||
618 | { | ||
619 | /* draw the new argh */ | ||
620 | rb->lcd_fillrect(arghx[index] + FIELD_RECT_X, | ||
621 | arghy[index] + FIELD_RECT_Y, | ||
622 | ARGH_SIZE, ARGH_SIZE); | ||
623 | } | ||
624 | |||
625 | static void virtual_player(struct worm *w); | ||
626 | /** | ||
627 | * Initialzes the specified worm with INITIAL_WORM_LENGTH | ||
628 | * and the tail at the specified position. The worm will | ||
629 | * be initialized alive and creeping EAST. | ||
630 | * @param struct worm *w The worm that is to be initialized | ||
631 | * @param int x The x coordinate at which the tail of the worm starts. | ||
632 | * x must be 0 <= x < FIELD_RECT_WIDTH. | ||
633 | * @param int y The y coordinate at which the tail of the worm starts | ||
634 | * y must be 0 <= y < FIELD_RECT_WIDTH. | ||
635 | */ | ||
636 | static void init_worm(struct worm *w, int x, int y){ | ||
637 | /* initialize the worm size */ | ||
638 | w->head = 1; | ||
639 | w->tail = 0; | ||
640 | |||
641 | w->x[w->head] = x + 1; | ||
642 | w->y[w->head] = y; | ||
643 | |||
644 | w->x[w->tail] = x; | ||
645 | w->y[w->tail] = y; | ||
646 | |||
647 | /* set the initial direction the worm creeps to */ | ||
648 | w->dirx = 1; | ||
649 | w->diry = 0; | ||
650 | |||
651 | w->growing = INITIAL_WORM_LENGTH - 1; | ||
652 | w->alive = true; | ||
653 | w->fetch_worm_direction = virtual_player; | ||
654 | } | ||
655 | |||
656 | /** | ||
657 | * Writes the direction that was stored for | ||
658 | * human player 1 into the specified worm. This function | ||
659 | * may be used to be stored in worm.fetch_worm_direction. | ||
660 | * The value of | ||
661 | * the direction is read from player1_dir. | ||
662 | * @param struct worm *w - The worm of which the direction | ||
663 | * is altered. | ||
664 | */ | ||
665 | static void human_player1(struct worm *w) { | ||
666 | set_worm_dir(w, player1_dir); | ||
667 | } | ||
668 | |||
669 | /** | ||
670 | * Writes the direction that was stored for | ||
671 | * human player 2 into the specified worm. This function | ||
672 | * may be used to be stored in worm.fetch_worm_direction. | ||
673 | * The value of | ||
674 | * the direction is read from player2_dir. | ||
675 | * @param struct worm *w - The worm of which the direction | ||
676 | * is altered. | ||
677 | */ | ||
678 | static void human_player2(struct worm *w) { | ||
679 | set_worm_dir(w, player2_dir); | ||
680 | } | ||
681 | |||
682 | /** | ||
683 | * Writes the direction that was stored for | ||
684 | * human player using a remote control | ||
685 | * into the specified worm. This function | ||
686 | * may be used to be stored in worm.fetch_worm_direction. | ||
687 | * The value of | ||
688 | * the direction is read from player3_dir. | ||
689 | * @param struct worm *w - The worm of which the direction | ||
690 | * is altered. | ||
691 | */ | ||
692 | static void remote_player(struct worm *w) { | ||
693 | set_worm_dir(w, player3_dir); | ||
694 | } | ||
695 | |||
696 | /** | ||
697 | * Initializes the worm-, food- and argh-arrays, draws a frame, | ||
698 | * makes some food and argh and display all that stuff. | ||
699 | */ | ||
700 | static void init_wormlet(void) | ||
701 | { | ||
702 | int i; | ||
703 | |||
704 | for (i = 0; i< worm_count; i++) { | ||
705 | /* Initialize all the worm coordinates to center. */ | ||
706 | int x = (int)(FIELD_RECT_WIDTH / 2); | ||
707 | int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10; | ||
708 | |||
709 | init_worm(&worms[i], x, y); | ||
710 | } | ||
711 | |||
712 | player1_dir = EAST; | ||
713 | player2_dir = EAST; | ||
714 | player3_dir = EAST; | ||
715 | |||
716 | if (players > 0) { | ||
717 | worms[0].fetch_worm_direction = human_player1; | ||
718 | } | ||
719 | |||
720 | if (players > 1) { | ||
721 | if (use_remote) { | ||
722 | worms[1].fetch_worm_direction = remote_player; | ||
723 | } else { | ||
724 | worms[1].fetch_worm_direction = human_player2; | ||
725 | } | ||
726 | } | ||
727 | |||
728 | if (players > 2) { | ||
729 | worms[2].fetch_worm_direction = human_player2; | ||
730 | } | ||
731 | |||
732 | /* Needed when the game is restarted using BUTTON_ON */ | ||
733 | rb->lcd_clear_display(); | ||
734 | |||
735 | /* make and display some food and argh */ | ||
736 | argh_count = MAX_FOOD; | ||
737 | for (i = 0; i < MAX_FOOD; i++) { | ||
738 | make_food(i); | ||
739 | draw_food(i); | ||
740 | make_argh(i); | ||
741 | draw_argh(i); | ||
742 | } | ||
743 | |||
744 | /* draw the game field */ | ||
745 | rb->lcd_invertrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2); | ||
746 | rb->lcd_invertrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT); | ||
747 | |||
748 | /* make everything visible */ | ||
749 | rb->lcd_update(); | ||
750 | } | ||
751 | |||
752 | |||
753 | /** | ||
754 | * Move the worm one step further if it is alive. | ||
755 | * The direction in which the worm moves is taken from dirx and diry. | ||
756 | * move_worm decreases growing if > 0. While the worm is growing the tail | ||
757 | * is left untouched. | ||
758 | * @param struct worm *w The worm to move. w must not be NULL. | ||
759 | */ | ||
760 | static void move_worm(struct worm *w) | ||
761 | { | ||
762 | if (w->alive) { | ||
763 | /* determine the head point and its precessor */ | ||
764 | int headx = w->x[w->head]; | ||
765 | int heady = w->y[w->head]; | ||
766 | int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS; | ||
767 | int preheadx = w->x[prehead]; | ||
768 | int preheady = w->y[prehead]; | ||
769 | |||
770 | /* determine the old direction */ | ||
771 | int olddirx; | ||
772 | int olddiry; | ||
773 | if (headx == preheadx) { | ||
774 | olddirx = 0; | ||
775 | olddiry = (heady > preheady) ? 1 : -1; | ||
776 | } else { | ||
777 | olddiry = 0; | ||
778 | olddirx = (headx > preheadx) ? 1 : -1; | ||
779 | } | ||
780 | |||
781 | /* olddir == dir? | ||
782 | a change of direction means a new segment | ||
783 | has been opened */ | ||
784 | if (olddirx != w->dirx || | ||
785 | olddiry != w->diry) { | ||
786 | w->head = (w->head + 1) % MAX_WORM_SEGMENTS; | ||
787 | } | ||
788 | |||
789 | /* new head position */ | ||
790 | w->x[w->head] = headx + w->dirx; | ||
791 | w->y[w->head] = heady + w->diry; | ||
792 | |||
793 | |||
794 | /* while the worm is growing no tail procession is necessary */ | ||
795 | if (w->growing > 0) { | ||
796 | /* update the worms grow state */ | ||
797 | w->growing--; | ||
798 | } | ||
799 | |||
800 | /* if the worm isn't growing the tail has to be dragged */ | ||
801 | else { | ||
802 | /* index of the end of the tail segment */ | ||
803 | int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS; | ||
804 | |||
805 | /* drag the end of the tail */ | ||
806 | /* only one coordinate has to be altered. Here it is | ||
807 | determined which one */ | ||
808 | int dir = 0; /* specifies wether the coord has to be in- or decreased */ | ||
809 | if (w->x[w->tail] == w->x[tail_segment_end]) { | ||
810 | dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1; | ||
811 | w->y[w->tail] += dir; | ||
812 | } else { | ||
813 | dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1; | ||
814 | w->x[w->tail] += dir; | ||
815 | } | ||
816 | |||
817 | /* when the tail has been dragged so far that it meets | ||
818 | the next segment start the tail segment is obsolete and | ||
819 | must be freed */ | ||
820 | if (w->x[w->tail] == w->x[tail_segment_end] && | ||
821 | w->y[w->tail] == w->y[tail_segment_end]){ | ||
822 | |||
823 | /* drop the last tail point */ | ||
824 | w->tail = tail_segment_end; | ||
825 | } | ||
826 | } | ||
827 | } | ||
828 | } | ||
829 | |||
830 | /** | ||
831 | * Draws the head and clears the tail of the worm in | ||
832 | * the display buffer. lcd_update() is NOT called thus | ||
833 | * the caller has to take care that the buffer is displayed. | ||
834 | */ | ||
835 | static void draw_worm(struct worm *w) | ||
836 | { | ||
837 | /* draw the new head */ | ||
838 | int x = w->x[w->head]; | ||
839 | int y = w->y[w->head]; | ||
840 | if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) { | ||
841 | rb->lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); | ||
842 | } | ||
843 | |||
844 | /* clear the space behind the worm */ | ||
845 | x = w->x[w->tail] ; | ||
846 | y = w->y[w->tail] ; | ||
847 | if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) { | ||
848 | rb->lcd_clearpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); | ||
849 | } | ||
850 | } | ||
851 | |||
852 | /** | ||
853 | * Checks wether the coordinate is part of the worm. Returns | ||
854 | * true if any part of the worm was hit - including the head. | ||
855 | * @param x int The x coordinate | ||
856 | * @param y int The y coordinate | ||
857 | * @return int The index of the worm arrays that contain x, y. | ||
858 | * Returns -1 if the coordinates are not part of the worm. | ||
859 | */ | ||
860 | static int specific_worm_collision(struct worm *w, int x, int y) | ||
861 | { | ||
862 | int retVal = -1; | ||
863 | |||
864 | /* get_worm_array_length is expensive -> buffer the value */ | ||
865 | int wormLength = get_worm_array_length(w); | ||
866 | int i; | ||
867 | |||
868 | /* test each entry that is part of the worm */ | ||
869 | for (i = 0; i < wormLength && retVal == -1; i++) { | ||
870 | |||
871 | /* The iteration iterates the length of the worm. | ||
872 | Here's the conversion to the true indices within the worm arrays. */ | ||
873 | int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS; | ||
874 | int lineend = (linestart + 1) % MAX_WORM_SEGMENTS; | ||
875 | bool samex = (w->x[linestart] == x) && (w->x[lineend] == x); | ||
876 | bool samey = (w->y[linestart] == y) && (w->y[lineend] == y); | ||
877 | if (samex || samey){ | ||
878 | int test, min, max, tmp; | ||
879 | |||
880 | if (samey) { | ||
881 | min = w->x[linestart]; | ||
882 | max = w->x[lineend]; | ||
883 | test = x; | ||
884 | } else { | ||
885 | min = w->y[linestart]; | ||
886 | max = w->y[lineend]; | ||
887 | test = y; | ||
888 | } | ||
889 | |||
890 | tmp = min; | ||
891 | min = MIN(min, max); | ||
892 | max = MAX(tmp, max); | ||
893 | |||
894 | if (min <= test && test <= max) { | ||
895 | retVal = lineend; | ||
896 | } | ||
897 | } | ||
898 | } | ||
899 | return retVal; | ||
900 | } | ||
901 | |||
902 | /** | ||
903 | * Increases the length of the specified worm by marking | ||
904 | * that it may grow by len pixels. Note that the worm has | ||
905 | * to move to make the growing happen. | ||
906 | * @param worm *w The worm that is to be altered. | ||
907 | * @param int len A positive value specifying the amount of | ||
908 | * pixels the worm may grow. | ||
909 | */ | ||
910 | static void add_growing(struct worm *w, int len) { | ||
911 | w->growing += len; | ||
912 | } | ||
913 | |||
914 | /** | ||
915 | * Determins the worm that is at the coordinates x, y. The parameter | ||
916 | * w is a switch parameter that changes the functionality of worm_collision. | ||
917 | * If w is specified and x,y hits the head of w NULL is returned. | ||
918 | * This is a useful way to determine wether the head of w hits | ||
919 | * any worm but including itself but excluding its own head. | ||
920 | * (It hits always its own head ;)) | ||
921 | * If w is set to NULL worm_collision returns any worm including all heads | ||
922 | * that is at position of x,y. | ||
923 | * @param struct worm *w The worm of which the head should be excluded in | ||
924 | * the test. w may be set to NULL. | ||
925 | * @param int x The x coordinate that is checked | ||
926 | * @param int y The y coordinate that is checkec | ||
927 | * @return struct worm* The worm that has been hit by x,y. If no worm | ||
928 | * was at the position NULL is returned. | ||
929 | */ | ||
930 | static struct worm* worm_collision(struct worm *w, int x, int y){ | ||
931 | struct worm *retVal = NULL; | ||
932 | int i; | ||
933 | for (i = 0; (i < worm_count) && (retVal == NULL); i++) { | ||
934 | int collision_at = specific_worm_collision(&worms[i], x, y); | ||
935 | if (collision_at != -1) { | ||
936 | if (!(w == &worms[i] && collision_at == w->head)){ | ||
937 | retVal = &worms[i]; | ||
938 | } | ||
939 | } | ||
940 | } | ||
941 | return retVal; | ||
942 | } | ||
943 | |||
944 | /** | ||
945 | * Returns true if the head of the worm just has | ||
946 | * crossed the field boundaries. | ||
947 | * @return bool true if the worm just has wrapped. | ||
948 | */ | ||
949 | static bool field_collision(struct worm *w) | ||
950 | { | ||
951 | bool retVal = false; | ||
952 | if ((w->x[w->head] >= FIELD_RECT_WIDTH) || | ||
953 | (w->y[w->head] >= FIELD_RECT_HEIGHT) || | ||
954 | (w->x[w->head] < 0) || | ||
955 | (w->y[w->head] < 0)) | ||
956 | { | ||
957 | retVal = true; | ||
958 | } | ||
959 | return retVal; | ||
960 | } | ||
961 | |||
962 | |||
963 | /** | ||
964 | * Returns true if the specified coordinates are within the | ||
965 | * field specified by the FIELD_RECT_XXX constants. | ||
966 | * @param int x The x coordinate of the point that is investigated | ||
967 | * @param int y The y coordinate of the point that is investigated | ||
968 | * @return bool Returns false if x,y specifies a point outside the | ||
969 | * field of worms. | ||
970 | */ | ||
971 | static bool is_in_field_rect(int x, int y) { | ||
972 | bool retVal = false; | ||
973 | retVal = (x >= 0 && x < FIELD_RECT_WIDTH && | ||
974 | y >= 0 && y < FIELD_RECT_HEIGHT); | ||
975 | return retVal; | ||
976 | } | ||
977 | |||
978 | /** | ||
979 | * Checks and returns wether the head of the w | ||
980 | * is colliding with something currently. | ||
981 | * @return int One of the values: | ||
982 | * COLLISION_NONE | ||
983 | * COLLISION_w | ||
984 | * COLLISION_FOOD | ||
985 | * COLLISION_ARGH | ||
986 | * COLLISION_FIELD | ||
987 | */ | ||
988 | static int check_collision(struct worm *w) | ||
989 | { | ||
990 | int retVal = COLLISION_NONE; | ||
991 | |||
992 | if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) | ||
993 | retVal = COLLISION_WORM; | ||
994 | |||
995 | if (food_collision(w->x[w->head], w->y[w->head]) >= 0) | ||
996 | retVal = COLLISION_FOOD; | ||
997 | |||
998 | if (argh_collision(w->x[w->head], w->y[w->head]) >= 0) | ||
999 | retVal = COLLISION_ARGH; | ||
1000 | |||
1001 | if (field_collision(w)) | ||
1002 | retVal = COLLISION_FIELD; | ||
1003 | |||
1004 | return retVal; | ||
1005 | } | ||
1006 | |||
1007 | /** | ||
1008 | * Returns the index of the food that is closest to the point | ||
1009 | * specified by x, y. This index may be used in the foodx and | ||
1010 | * foody arrays. | ||
1011 | * @param int x The x coordinate of the point | ||
1012 | * @param int y The y coordinate of the point | ||
1013 | * @return int A value usable as index in foodx and foody. | ||
1014 | */ | ||
1015 | static int get_nearest_food(int x, int y){ | ||
1016 | int nearestfood = 0; | ||
1017 | int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT; | ||
1018 | int deltax = 0; | ||
1019 | int deltay = 0; | ||
1020 | int foodindex; | ||
1021 | for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) { | ||
1022 | int distance; | ||
1023 | deltax = foodx[foodindex] - x; | ||
1024 | deltay = foody[foodindex] - y; | ||
1025 | deltax = deltax > 0 ? deltax : deltax * (-1); | ||
1026 | deltay = deltay > 0 ? deltay : deltay * (-1); | ||
1027 | distance = deltax + deltay; | ||
1028 | |||
1029 | if (distance < olddistance) { | ||
1030 | olddistance = distance; | ||
1031 | nearestfood = foodindex; | ||
1032 | } | ||
1033 | } | ||
1034 | return nearestfood; | ||
1035 | } | ||
1036 | |||
1037 | /** | ||
1038 | * Returns wether the specified position is next to the worm | ||
1039 | * and in the direction the worm looks. Use this method to | ||
1040 | * test wether this position would be hit with the next move of | ||
1041 | * the worm unless the worm changes its direction. | ||
1042 | * @param struct worm *w - The worm to be investigated | ||
1043 | * @param int x - The x coordinate of the position to test. | ||
1044 | * @param int y - The y coordinate of the position to test. | ||
1045 | * @return Returns true if the worm will hit the position unless | ||
1046 | * it change its direction before the next move. | ||
1047 | */ | ||
1048 | static bool is_in_front_of_worm(struct worm *w, int x, int y) { | ||
1049 | bool infront = false; | ||
1050 | int deltax = x - w->x[w->head]; | ||
1051 | int deltay = y - w->y[w->head]; | ||
1052 | |||
1053 | if (w->dirx == 0) { | ||
1054 | infront = (w->diry * deltay) > 0; | ||
1055 | } else { | ||
1056 | infront = (w->dirx * deltax) > 0; | ||
1057 | } | ||
1058 | return infront; | ||
1059 | } | ||
1060 | |||
1061 | /** | ||
1062 | * Returns true if the worm will collide with the next move unless | ||
1063 | * it changes its direction. | ||
1064 | * @param struct worm *w - The worm to be investigated. | ||
1065 | * @return Returns true if the worm will collide with the next move | ||
1066 | * unless it changes its direction. | ||
1067 | */ | ||
1068 | static bool will_worm_collide(struct worm *w) { | ||
1069 | int x = w->x[w->head] + w->dirx; | ||
1070 | int y = w->y[w->head] + w->diry; | ||
1071 | bool retVal = !is_in_field_rect(x, y); | ||
1072 | if (!retVal) { | ||
1073 | retVal = (argh_collision(x, y) != -1); | ||
1074 | } | ||
1075 | |||
1076 | if (!retVal) { | ||
1077 | retVal = (worm_collision(w, x, y) != NULL); | ||
1078 | } | ||
1079 | return retVal; | ||
1080 | } | ||
1081 | |||
1082 | /** | ||
1083 | * This function | ||
1084 | * may be used to be stored in worm.fetch_worm_direction for | ||
1085 | * worms that are not controlled by humans but by artificial stupidity. | ||
1086 | * A direction is searched that doesn't lead to collision but to the nearest | ||
1087 | * food - but not very intelligent. The direction is written to the specified | ||
1088 | * worm. | ||
1089 | * @param struct worm *w - The worm of which the direction | ||
1090 | * is altered. | ||
1091 | */ | ||
1092 | static void virtual_player(struct worm *w) { | ||
1093 | bool isright; | ||
1094 | int plana, planb, planc; | ||
1095 | /* find the next lunch */ | ||
1096 | int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]); | ||
1097 | |||
1098 | /* determine in which direction it is */ | ||
1099 | |||
1100 | /* in front of me? */ | ||
1101 | bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); | ||
1102 | |||
1103 | /* left right of me? */ | ||
1104 | int olddir = get_worm_dir(w); | ||
1105 | set_worm_dir(w, (olddir + 1) % 4); | ||
1106 | isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); | ||
1107 | set_worm_dir(w, olddir); | ||
1108 | |||
1109 | /* detect situation, set strategy */ | ||
1110 | if (infront) { | ||
1111 | if (isright) { | ||
1112 | plana = olddir; | ||
1113 | planb = (olddir + 1) % 4; | ||
1114 | planc = (olddir + 3) % 4; | ||
1115 | } else { | ||
1116 | plana = olddir; | ||
1117 | planb = (olddir + 3) % 4; | ||
1118 | planc = (olddir + 1) % 4; | ||
1119 | } | ||
1120 | } else { | ||
1121 | if (isright) { | ||
1122 | plana = (olddir + 1) % 4; | ||
1123 | planb = olddir; | ||
1124 | planc = (olddir + 3) % 4; | ||
1125 | } else { | ||
1126 | plana = (olddir + 3) % 4; | ||
1127 | planb = olddir; | ||
1128 | planc = (olddir + 1) % 4; | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | /* test for collision */ | ||
1133 | set_worm_dir(w, plana); | ||
1134 | if (will_worm_collide(w)){ | ||
1135 | |||
1136 | /* plan b */ | ||
1137 | set_worm_dir(w, planb); | ||
1138 | |||
1139 | /* test for collision */ | ||
1140 | if (will_worm_collide(w)) { | ||
1141 | |||
1142 | /* plan c */ | ||
1143 | set_worm_dir(w, planc); | ||
1144 | } | ||
1145 | } | ||
1146 | } | ||
1147 | |||
1148 | /** | ||
1149 | * prints out the score board with all the status information | ||
1150 | * about the game. | ||
1151 | */ | ||
1152 | static void score_board(void) | ||
1153 | { | ||
1154 | char buf[15]; | ||
1155 | char* buf2 = NULL; | ||
1156 | int i; | ||
1157 | int y = 0; | ||
1158 | rb->lcd_clearrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT); | ||
1159 | for (i = 0; i < worm_count; i++) { | ||
1160 | int score = get_score(&worms[i]); | ||
1161 | |||
1162 | /* high score */ | ||
1163 | if (worms[i].fetch_worm_direction != virtual_player){ | ||
1164 | if (highscore < score) { | ||
1165 | highscore = score; | ||
1166 | } | ||
1167 | } | ||
1168 | |||
1169 | /* length */ | ||
1170 | rb->snprintf(buf, sizeof (buf),"Len:%d", score); | ||
1171 | |||
1172 | /* worm state */ | ||
1173 | switch (check_collision(&worms[i])) { | ||
1174 | case COLLISION_NONE: | ||
1175 | if (worms[i].growing > 0) | ||
1176 | buf2 = "Growing"; | ||
1177 | else { | ||
1178 | if (worms[i].alive) | ||
1179 | buf2 = "Hungry"; | ||
1180 | else | ||
1181 | buf2 = "Wormed"; | ||
1182 | } | ||
1183 | break; | ||
1184 | |||
1185 | case COLLISION_WORM: | ||
1186 | buf2 = "Wormed"; | ||
1187 | break; | ||
1188 | |||
1189 | case COLLISION_FOOD: | ||
1190 | buf2 = "Growing"; | ||
1191 | break; | ||
1192 | |||
1193 | case COLLISION_ARGH: | ||
1194 | buf2 = "Argh"; | ||
1195 | break; | ||
1196 | |||
1197 | case COLLISION_FIELD: | ||
1198 | buf2 = "Crashed"; | ||
1199 | break; | ||
1200 | } | ||
1201 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf); | ||
1202 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2); | ||
1203 | |||
1204 | if (!worms[i].alive){ | ||
1205 | rb->lcd_invertrect(FIELD_RECT_WIDTH + 2, y, | ||
1206 | LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17); | ||
1207 | } | ||
1208 | y += 19; | ||
1209 | } | ||
1210 | rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore); | ||
1211 | #ifndef DEBUG_WORMLET | ||
1212 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf); | ||
1213 | #else | ||
1214 | rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout); | ||
1215 | #endif | ||
1216 | } | ||
1217 | |||
1218 | /** | ||
1219 | * Checks for collisions of the worm and its environment and | ||
1220 | * takes appropriate actions like growing the worm or killing it. | ||
1221 | * @return bool Returns true if the worm is dead. Returns | ||
1222 | * false if the worm is healthy, up and creeping. | ||
1223 | */ | ||
1224 | static bool process_collisions(struct worm *w) | ||
1225 | { | ||
1226 | int index = -1; | ||
1227 | |||
1228 | w->alive &= !field_collision(w); | ||
1229 | |||
1230 | if (w->alive) { | ||
1231 | |||
1232 | /* check if food was eaten */ | ||
1233 | index = food_collision(w->x[w->head], w->y[w->head]); | ||
1234 | if (index != -1){ | ||
1235 | int i; | ||
1236 | |||
1237 | clear_food(index); | ||
1238 | make_food(index); | ||
1239 | draw_food(index); | ||
1240 | |||
1241 | for (i = 0; i < ARGHS_PER_FOOD; i++) { | ||
1242 | argh_count++; | ||
1243 | if (argh_count > MAX_ARGH) | ||
1244 | argh_count = MAX_ARGH; | ||
1245 | make_argh(argh_count - 1); | ||
1246 | draw_argh(argh_count - 1); | ||
1247 | } | ||
1248 | |||
1249 | add_growing(w, WORM_PER_FOOD); | ||
1250 | |||
1251 | draw_worm(w); | ||
1252 | } | ||
1253 | |||
1254 | /* check if argh was eaten */ | ||
1255 | else { | ||
1256 | index = argh_collision(w->x[w->head], w->y[w->head]); | ||
1257 | if (index != -1) { | ||
1258 | w->alive = false; | ||
1259 | } | ||
1260 | else { | ||
1261 | if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) { | ||
1262 | w->alive = false; | ||
1263 | } | ||
1264 | } | ||
1265 | } | ||
1266 | } | ||
1267 | return !w->alive; | ||
1268 | } | ||
1269 | |||
1270 | /** | ||
1271 | * The main loop of the game. | ||
1272 | * @return bool Returns true if the game ended | ||
1273 | * with a dead worm. Returns false if the user | ||
1274 | * aborted the game manually. | ||
1275 | */ | ||
1276 | static bool run(void) | ||
1277 | { | ||
1278 | int button = 0; | ||
1279 | int wormDead = false; | ||
1280 | |||
1281 | /* ticks are counted to compensate speed variations */ | ||
1282 | long cycle_start = 0, cycle_end = 0; | ||
1283 | #ifdef DEBUG_WORMLET | ||
1284 | int ticks_to_max_cycle_reset = 20; | ||
1285 | long max_cycle = 0; | ||
1286 | char buf[20]; | ||
1287 | #endif | ||
1288 | |||
1289 | /* initialize the board and so on */ | ||
1290 | init_wormlet(); | ||
1291 | |||
1292 | cycle_start = *rb->current_tick; | ||
1293 | /* change the direction of the worm */ | ||
1294 | while (button != BUTTON_OFF && ! wormDead) | ||
1295 | { | ||
1296 | int i; | ||
1297 | long cycle_duration ; | ||
1298 | switch (button) { | ||
1299 | case BUTTON_UP: | ||
1300 | if (players == 1 && !use_remote) { | ||
1301 | player1_dir = NORTH; | ||
1302 | } | ||
1303 | break; | ||
1304 | |||
1305 | case BUTTON_DOWN: | ||
1306 | if (players == 1 && !use_remote) { | ||
1307 | player1_dir = SOUTH; | ||
1308 | } | ||
1309 | break; | ||
1310 | |||
1311 | case BUTTON_LEFT: | ||
1312 | if (players != 1 || use_remote) { | ||
1313 | player1_dir = (player1_dir + 3) % 4; | ||
1314 | } else { | ||
1315 | player1_dir = WEST; | ||
1316 | } | ||
1317 | break; | ||
1318 | |||
1319 | case BUTTON_RIGHT: | ||
1320 | if (players != 1 || use_remote) { | ||
1321 | player1_dir = (player1_dir + 1) % 4; | ||
1322 | } else { | ||
1323 | player1_dir = EAST; | ||
1324 | } | ||
1325 | break; | ||
1326 | |||
1327 | case BUTTON_F2: | ||
1328 | player2_dir = (player2_dir + 3) % 4; | ||
1329 | break; | ||
1330 | |||
1331 | case BUTTON_F3: | ||
1332 | player2_dir = (player2_dir + 1) % 4; | ||
1333 | break; | ||
1334 | |||
1335 | case BUTTON_RC_VOL_UP: | ||
1336 | player3_dir = (player3_dir + 1) % 4; | ||
1337 | break; | ||
1338 | |||
1339 | case BUTTON_RC_VOL_DOWN: | ||
1340 | player3_dir = (player3_dir + 3) % 4; | ||
1341 | break; | ||
1342 | |||
1343 | case BUTTON_PLAY: | ||
1344 | do { | ||
1345 | button = rb->button_get(true); | ||
1346 | } while (button != BUTTON_PLAY && | ||
1347 | button != BUTTON_OFF && | ||
1348 | button != BUTTON_ON); | ||
1349 | break; | ||
1350 | } | ||
1351 | |||
1352 | for (i = 0; i < worm_count; i++) { | ||
1353 | worms[i].fetch_worm_direction(&worms[i]); | ||
1354 | } | ||
1355 | |||
1356 | wormDead = true; | ||
1357 | for (i = 0; i < worm_count; i++){ | ||
1358 | struct worm *w = &worms[i]; | ||
1359 | move_worm(w); | ||
1360 | wormDead &= process_collisions(w); | ||
1361 | draw_worm(w); | ||
1362 | } | ||
1363 | score_board(); | ||
1364 | rb->lcd_update(); | ||
1365 | if (button == BUTTON_ON) { | ||
1366 | wormDead = true; | ||
1367 | } | ||
1368 | |||
1369 | /* here the wormlet game cycle ends | ||
1370 | thus the current tick is stored | ||
1371 | as end time */ | ||
1372 | cycle_end = *rb->current_tick; | ||
1373 | |||
1374 | /* The duration of the game cycle */ | ||
1375 | cycle_duration = cycle_end - cycle_start; | ||
1376 | cycle_duration = MAX(0, cycle_duration); | ||
1377 | cycle_duration = MIN(SPEED -1, cycle_duration); | ||
1378 | |||
1379 | |||
1380 | #ifdef DEBUG_WORMLET | ||
1381 | ticks_to_max_cycle_reset--; | ||
1382 | if (ticks_to_max_cycle_reset <= 0) { | ||
1383 | max_cycle = 0; | ||
1384 | } | ||
1385 | |||
1386 | if (max_cycle < cycle_duration) { | ||
1387 | max_cycle = cycle_duration; | ||
1388 | ticks_to_max_cycle_reset = 20; | ||
1389 | } | ||
1390 | rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle); | ||
1391 | set_debug_out(buf); | ||
1392 | #endif | ||
1393 | /* adjust the number of ticks to wait for a button. | ||
1394 | This ensures that a complete game cycle including | ||
1395 | user input runs in constant time */ | ||
1396 | button = rb->button_get_w_tmo(SPEED - cycle_duration); | ||
1397 | cycle_start = *rb->current_tick; | ||
1398 | } | ||
1399 | return wormDead; | ||
1400 | } | ||
1401 | |||
1402 | #ifdef DEBUG_WORMLET | ||
1403 | |||
1404 | /** | ||
1405 | * Just a test routine that checks that worm_food_collision works | ||
1406 | * in some typical situations. | ||
1407 | */ | ||
1408 | static void test_worm_food_collision(void) { | ||
1409 | int collision_count = 0; | ||
1410 | int i; | ||
1411 | rb->lcd_clear_display(); | ||
1412 | init_worm(&worms[0], 10, 10); | ||
1413 | add_growing(&worms[0], 10); | ||
1414 | set_worm_dir(&worms[0], EAST); | ||
1415 | for (i = 0; i < 10; i++) { | ||
1416 | move_worm(&worms[0]); | ||
1417 | draw_worm(&worms[0]); | ||
1418 | } | ||
1419 | |||
1420 | set_worm_dir(&worms[0], SOUTH); | ||
1421 | for (i = 0; i < 10; i++) { | ||
1422 | move_worm(&worms[0]); | ||
1423 | draw_worm(&worms[0]); | ||
1424 | } | ||
1425 | |||
1426 | foodx[0] = 15; | ||
1427 | foody[0] = 12; | ||
1428 | for (foody[0] = 20; foody[0] > 0; foody[0] --) { | ||
1429 | char buf[20]; | ||
1430 | bool collision; | ||
1431 | draw_worm(&worms[0]); | ||
1432 | draw_food(0); | ||
1433 | collision = worm_food_collision(&worms[0], 0); | ||
1434 | if (collision) { | ||
1435 | collision_count++; | ||
1436 | } | ||
1437 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1438 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1439 | rb->lcd_update(); | ||
1440 | } | ||
1441 | if (collision_count != FOOD_SIZE) { | ||
1442 | rb->button_get(true); | ||
1443 | } | ||
1444 | |||
1445 | |||
1446 | foody[0] = 15; | ||
1447 | for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) { | ||
1448 | char buf[20]; | ||
1449 | bool collision; | ||
1450 | draw_worm(&worms[0]); | ||
1451 | draw_food(0); | ||
1452 | collision = worm_food_collision(&worms[0], 0); | ||
1453 | if (collision) { | ||
1454 | collision_count ++; | ||
1455 | } | ||
1456 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1457 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1458 | rb->lcd_update(); | ||
1459 | } | ||
1460 | if (collision_count != FOOD_SIZE * 2) { | ||
1461 | rb->button_get(true); | ||
1462 | } | ||
1463 | |||
1464 | } | ||
1465 | |||
1466 | |||
1467 | static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){ | ||
1468 | int x, y; | ||
1469 | bool retVal = false; | ||
1470 | for (x = rx; x < rx + rw; x++){ | ||
1471 | for (y = ry; y < ry + rh; y++) { | ||
1472 | if (specific_worm_collision(w, x, y) != -1) { | ||
1473 | retVal = true; | ||
1474 | } | ||
1475 | } | ||
1476 | } | ||
1477 | return retVal; | ||
1478 | } | ||
1479 | |||
1480 | static void test_worm_argh_collision(void) { | ||
1481 | int i; | ||
1482 | int dir; | ||
1483 | int collision_count = 0; | ||
1484 | rb->lcd_clear_display(); | ||
1485 | init_worm(&worms[0], 10, 10); | ||
1486 | add_growing(&worms[0], 40); | ||
1487 | for (dir = 0; dir < 4; dir++) { | ||
1488 | set_worm_dir(&worms[0], (EAST + dir) % 4); | ||
1489 | for (i = 0; i < 10; i++) { | ||
1490 | move_worm(&worms[0]); | ||
1491 | draw_worm(&worms[0]); | ||
1492 | } | ||
1493 | } | ||
1494 | |||
1495 | arghx[0] = 12; | ||
1496 | for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghy[0]++){ | ||
1497 | char buf[20]; | ||
1498 | bool collision; | ||
1499 | draw_argh(0); | ||
1500 | collision = worm_argh_collision(&worms[0], 0); | ||
1501 | if (collision) { | ||
1502 | collision_count ++; | ||
1503 | } | ||
1504 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1505 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1506 | rb->lcd_update(); | ||
1507 | } | ||
1508 | if (collision_count != ARGH_SIZE * 2) { | ||
1509 | rb->button_get(true); | ||
1510 | } | ||
1511 | |||
1512 | arghy[0] = 12; | ||
1513 | for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghx[0]++){ | ||
1514 | char buf[20]; | ||
1515 | bool collision; | ||
1516 | draw_argh(0); | ||
1517 | collision = worm_argh_collision(&worms[0], 0); | ||
1518 | if (collision) { | ||
1519 | collision_count ++; | ||
1520 | } | ||
1521 | rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); | ||
1522 | rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); | ||
1523 | rb->lcd_update(); | ||
1524 | } | ||
1525 | if (collision_count != ARGH_SIZE * 4) { | ||
1526 | rb->button_get(true); | ||
1527 | } | ||
1528 | } | ||
1529 | |||
1530 | static int testline_in_rect(void) { | ||
1531 | int testfailed = -1; | ||
1532 | |||
1533 | int rx = 10; | ||
1534 | int ry = 15; | ||
1535 | int rw = 20; | ||
1536 | int rh = 25; | ||
1537 | |||
1538 | /* Test 1 */ | ||
1539 | int x1 = 12; | ||
1540 | int y1 = 8; | ||
1541 | int x2 = 12; | ||
1542 | int y2 = 42; | ||
1543 | |||
1544 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1545 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1546 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1547 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1548 | rb->lcd_update(); | ||
1549 | rb->lcd_putsxy(0, 0, "failed 1"); | ||
1550 | rb->button_get(true); | ||
1551 | testfailed = 1; | ||
1552 | } | ||
1553 | |||
1554 | /* test 2 */ | ||
1555 | y2 = 20; | ||
1556 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1557 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1558 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1559 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1560 | rb->lcd_putsxy(0, 0, "failed 2"); | ||
1561 | rb->lcd_update(); | ||
1562 | rb->button_get(true); | ||
1563 | testfailed = 2; | ||
1564 | } | ||
1565 | |||
1566 | /* test 3 */ | ||
1567 | y1 = 30; | ||
1568 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1569 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1570 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1571 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1572 | rb->lcd_putsxy(0, 0, "failed 3"); | ||
1573 | rb->lcd_update(); | ||
1574 | rb->button_get(true); | ||
1575 | testfailed = 3; | ||
1576 | } | ||
1577 | |||
1578 | /* test 4 */ | ||
1579 | y2 = 45; | ||
1580 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1581 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1582 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1583 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1584 | rb->lcd_putsxy(0, 0, "failed 4"); | ||
1585 | rb->lcd_update(); | ||
1586 | rb->button_get(true); | ||
1587 | testfailed = 4; | ||
1588 | } | ||
1589 | |||
1590 | /* test 5 */ | ||
1591 | y1 = 50; | ||
1592 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1593 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1594 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1595 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1596 | rb->lcd_putsxy(0, 0, "failed 5"); | ||
1597 | rb->lcd_update(); | ||
1598 | rb->button_get(true); | ||
1599 | testfailed = 5; | ||
1600 | } | ||
1601 | |||
1602 | /* test 6 */ | ||
1603 | y1 = 5; | ||
1604 | y2 = 7; | ||
1605 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1606 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1607 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1608 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1609 | rb->lcd_putsxy(0, 0, "failed 6"); | ||
1610 | rb->lcd_update(); | ||
1611 | rb->button_get(true); | ||
1612 | testfailed = 6; | ||
1613 | } | ||
1614 | |||
1615 | /* test 7 */ | ||
1616 | x1 = 8; | ||
1617 | y1 = 20; | ||
1618 | x2 = 35; | ||
1619 | y2 = 20; | ||
1620 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1621 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1622 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1623 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1624 | rb->lcd_putsxy(0, 0, "failed 7"); | ||
1625 | rb->lcd_update(); | ||
1626 | rb->button_get(true); | ||
1627 | testfailed = 7; | ||
1628 | } | ||
1629 | |||
1630 | /* test 8 */ | ||
1631 | x2 = 12; | ||
1632 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1633 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1634 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1635 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1636 | rb->lcd_putsxy(0, 0, "failed 8"); | ||
1637 | rb->lcd_update(); | ||
1638 | rb->button_get(true); | ||
1639 | testfailed = 8; | ||
1640 | } | ||
1641 | |||
1642 | /* test 9 */ | ||
1643 | x1 = 25; | ||
1644 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1645 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1646 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1647 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1648 | rb->lcd_putsxy(0, 0, "failed 9"); | ||
1649 | rb->lcd_update(); | ||
1650 | rb->button_get(true); | ||
1651 | testfailed = 9; | ||
1652 | } | ||
1653 | |||
1654 | /* test 10 */ | ||
1655 | x2 = 37; | ||
1656 | if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1657 | !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1658 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1659 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1660 | rb->lcd_putsxy(0, 0, "failed 10"); | ||
1661 | rb->lcd_update(); | ||
1662 | rb->button_get(true); | ||
1663 | testfailed = 10; | ||
1664 | } | ||
1665 | |||
1666 | /* test 11 */ | ||
1667 | x1 = 42; | ||
1668 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1669 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1670 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1671 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1672 | rb->lcd_putsxy(0, 0, "failed 11"); | ||
1673 | rb->lcd_update(); | ||
1674 | rb->button_get(true); | ||
1675 | testfailed = 11; | ||
1676 | } | ||
1677 | |||
1678 | /* test 12 */ | ||
1679 | x1 = 5; | ||
1680 | x2 = 7; | ||
1681 | if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) || | ||
1682 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) { | ||
1683 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1684 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1685 | rb->lcd_putsxy(0, 0, "failed 12"); | ||
1686 | rb->lcd_update(); | ||
1687 | rb->button_get(true); | ||
1688 | testfailed = 12; | ||
1689 | } | ||
1690 | |||
1691 | /* test 13 */ | ||
1692 | rx = 9; | ||
1693 | ry = 15; | ||
1694 | rw = FOOD_SIZE; | ||
1695 | rh = FOOD_SIZE; | ||
1696 | |||
1697 | x1 = 10; | ||
1698 | y1 = 10; | ||
1699 | x2 = 10; | ||
1700 | y2 = 20; | ||
1701 | if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1702 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) { | ||
1703 | rb->lcd_drawrect(rx, ry, rw, rh); | ||
1704 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1705 | rb->lcd_putsxy(0, 0, "failed 13"); | ||
1706 | rb->lcd_update(); | ||
1707 | rb->button_get(true); | ||
1708 | testfailed = 13; | ||
1709 | } | ||
1710 | |||
1711 | /* test 14 */ | ||
1712 | rx = 9; | ||
1713 | ry = 15; | ||
1714 | rw = 4; | ||
1715 | rh = 4; | ||
1716 | |||
1717 | x1 = 10; | ||
1718 | y1 = 10; | ||
1719 | x2 = 10; | ||
1720 | y2 = 19; | ||
1721 | if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) && | ||
1722 | line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) { | ||
1723 | rb->lcd_drawline(x1, y1, x2, y2); | ||
1724 | rb->lcd_invertrect(rx, ry, rw, rh); | ||
1725 | rb->lcd_putsxy(0, 0, "failed 14"); | ||
1726 | rb->lcd_update(); | ||
1727 | rb->button_get(true); | ||
1728 | testfailed = 14; | ||
1729 | } | ||
1730 | |||
1731 | rb->lcd_clear_display(); | ||
1732 | |||
1733 | return testfailed; | ||
1734 | } | ||
1735 | |||
1736 | /** | ||
1737 | * Just a test routine to test wether specific_worm_collision might work properly | ||
1738 | */ | ||
1739 | static int test_specific_worm_collision(void) { | ||
1740 | int collisions = 0; | ||
1741 | int dir; | ||
1742 | int x = 0; | ||
1743 | int y = 0; | ||
1744 | char buf[20]; | ||
1745 | rb->lcd_clear_display(); | ||
1746 | init_worm(&worms[0], 10, 20); | ||
1747 | add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH); | ||
1748 | |||
1749 | for (dir = EAST; dir < EAST + 4; dir++) { | ||
1750 | int i; | ||
1751 | set_worm_dir(&worms[0], dir % 4); | ||
1752 | for (i = 0; i < 5; i++) { | ||
1753 | if (!(dir % 4 == NORTH && i == 9)) { | ||
1754 | move_worm(&worms[0]); | ||
1755 | draw_worm(&worms[0]); | ||
1756 | } | ||
1757 | } | ||
1758 | } | ||
1759 | |||
1760 | for (y = 15; y < 30; y ++){ | ||
1761 | for (x = 5; x < 20; x++) { | ||
1762 | if (specific_worm_collision(&worms[0], x, y) != -1) { | ||
1763 | collisions ++; | ||
1764 | } | ||
1765 | rb->lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y); | ||
1766 | rb->snprintf(buf, sizeof buf, "collisions %d", collisions); | ||
1767 | rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf); | ||
1768 | rb->lcd_update(); | ||
1769 | } | ||
1770 | } | ||
1771 | if (collisions != 21) { | ||
1772 | rb->button_get(true); | ||
1773 | } | ||
1774 | return collisions; | ||
1775 | } | ||
1776 | |||
1777 | static void test_make_argh(void){ | ||
1778 | int dir; | ||
1779 | int seed = 0; | ||
1780 | int hit = 0; | ||
1781 | int failures = 0; | ||
1782 | int last_failures = 0; | ||
1783 | int i, worm_idx; | ||
1784 | rb->lcd_clear_display(); | ||
1785 | worm_count = 3; | ||
1786 | |||
1787 | for (worm_idx = 0; worm_idx < worm_count; worm_idx++) { | ||
1788 | init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20); | ||
1789 | add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH); | ||
1790 | } | ||
1791 | |||
1792 | for (dir = EAST; dir < EAST + 4; dir++) { | ||
1793 | for (worm_idx = 0; worm_idx < worm_count; worm_idx++) { | ||
1794 | set_worm_dir(&worms[worm_idx], dir % 4); | ||
1795 | for (i = 0; i < 10; i++) { | ||
1796 | if (!(dir % 4 == NORTH && i == 9)) { | ||
1797 | move_worm(&worms[worm_idx]); | ||
1798 | draw_worm(&worms[worm_idx]); | ||
1799 | } | ||
1800 | } | ||
1801 | } | ||
1802 | } | ||
1803 | |||
1804 | rb->lcd_update(); | ||
1805 | |||
1806 | for (seed = 0; hit < 20; seed += 2) { | ||
1807 | char buf[20]; | ||
1808 | int x, y; | ||
1809 | rb->srand(seed); | ||
1810 | x = rb->rand() % (FIELD_RECT_WIDTH - ARGH_SIZE); | ||
1811 | y = rb->rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE); | ||
1812 | |||
1813 | for (worm_idx = 0; worm_idx < worm_count; worm_idx++){ | ||
1814 | if (expensive_worm_in_rect(&worms[worm_idx], x, y, ARGH_SIZE, ARGH_SIZE)) { | ||
1815 | int tries = 0; | ||
1816 | rb->srand(seed); | ||
1817 | |||
1818 | tries = make_argh(0); | ||
1819 | if ((x == arghx[0] && y == arghy[0]) || tries < 2) { | ||
1820 | failures ++; | ||
1821 | } | ||
1822 | |||
1823 | rb->snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d", x, y, failures, tries); | ||
1824 | rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf); | ||
1825 | rb->lcd_update(); | ||
1826 | rb->lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE); | ||
1827 | rb->lcd_update(); | ||
1828 | draw_argh(0); | ||
1829 | rb->lcd_update(); | ||
1830 | rb->lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE); | ||
1831 | rb->lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE); | ||
1832 | |||
1833 | if (failures > last_failures) { | ||
1834 | rb->button_get(true); | ||
1835 | } | ||
1836 | last_failures = failures; | ||
1837 | hit ++; | ||
1838 | } | ||
1839 | } | ||
1840 | } | ||
1841 | } | ||
1842 | |||
1843 | static void test_worm_argh_collision_in_moves(void) { | ||
1844 | int hit_count = 0; | ||
1845 | int i; | ||
1846 | rb->lcd_clear_display(); | ||
1847 | init_worm(&worms[0], 10, 20); | ||
1848 | |||
1849 | arghx[0] = 20; | ||
1850 | arghy[0] = 18; | ||
1851 | draw_argh(0); | ||
1852 | |||
1853 | set_worm_dir(&worms[0], EAST); | ||
1854 | for (i = 0; i < 20; i++) { | ||
1855 | char buf[20]; | ||
1856 | move_worm(&worms[0]); | ||
1857 | draw_worm(&worms[0]); | ||
1858 | if (worm_argh_collision_in_moves(&worms[0], 0, 5)){ | ||
1859 | hit_count ++; | ||
1860 | } | ||
1861 | rb->snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count); | ||
1862 | rb->lcd_putsxy(0, LCD_HEIGHT - 8, buf); | ||
1863 | rb->lcd_update(); | ||
1864 | } | ||
1865 | if (hit_count != ARGH_SIZE + 5) { | ||
1866 | rb->button_get(true); | ||
1867 | } | ||
1868 | } | ||
1869 | #endif /* DEBUG_WORMLET */ | ||
1870 | |||
1871 | extern bool use_old_rect; | ||
1872 | |||
1873 | /** | ||
1874 | * Main entry point | ||
1875 | */ | ||
1876 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
1877 | { | ||
1878 | bool worm_dead = false; | ||
1879 | int button; | ||
1880 | |||
1881 | TEST_PLUGIN_API(api); | ||
1882 | (void)(parameter); | ||
1883 | |||
1884 | rb = api; | ||
1885 | rb->lcd_setfont(FONT_SYSFIXED); | ||
1886 | |||
1887 | #ifdef DEBUG_WORMLET | ||
1888 | testline_in_rect(); | ||
1889 | test_worm_argh_collision_in_moves(); | ||
1890 | test_make_argh(); | ||
1891 | test_worm_food_collision(); | ||
1892 | test_worm_argh_collision(); | ||
1893 | test_specific_worm_collision(); | ||
1894 | #endif | ||
1895 | |||
1896 | /* Setup screen */ | ||
1897 | do { | ||
1898 | char buf[20]; | ||
1899 | char* ptr; | ||
1900 | rb->lcd_clear_display(); | ||
1901 | |||
1902 | /* first line players */ | ||
1903 | rb->snprintf(buf, sizeof buf, "%d Players UP/DN", players); | ||
1904 | rb->lcd_puts(0, 0, buf); | ||
1905 | |||
1906 | /* second line worms */ | ||
1907 | rb->snprintf(buf, sizeof buf, "%d Worms L/R", worm_count); | ||
1908 | rb->lcd_puts(0, 1, buf); | ||
1909 | |||
1910 | /* third line control */ | ||
1911 | if (players > 1) { | ||
1912 | if (use_remote) { | ||
1913 | ptr = "Remote Control F1"; | ||
1914 | } else { | ||
1915 | ptr = "No Rem. Control F1"; | ||
1916 | } | ||
1917 | } else { | ||
1918 | if (players > 0) { | ||
1919 | if (use_remote) { | ||
1920 | ptr = "2 Key Control F1"; | ||
1921 | } else { | ||
1922 | ptr = "4 Key Control F1"; | ||
1923 | } | ||
1924 | } else { | ||
1925 | ptr = "Out Of Control"; | ||
1926 | } | ||
1927 | } | ||
1928 | rb->lcd_puts(0, 2, ptr); | ||
1929 | rb->lcd_update(); | ||
1930 | |||
1931 | /* user selection */ | ||
1932 | button = rb->button_get(true); | ||
1933 | switch (button) { | ||
1934 | case BUTTON_UP: | ||
1935 | if (players < 3) { | ||
1936 | players ++; | ||
1937 | if (players > worm_count) { | ||
1938 | worm_count = players; | ||
1939 | } | ||
1940 | if (players > 2) { | ||
1941 | use_remote = true; | ||
1942 | } | ||
1943 | } | ||
1944 | break; | ||
1945 | case BUTTON_DOWN: | ||
1946 | if (players > 0) { | ||
1947 | players --; | ||
1948 | } | ||
1949 | break; | ||
1950 | case BUTTON_LEFT: | ||
1951 | if (worm_count > 1) { | ||
1952 | worm_count--; | ||
1953 | if (worm_count < players) { | ||
1954 | players = worm_count; | ||
1955 | } | ||
1956 | } | ||
1957 | break; | ||
1958 | case BUTTON_RIGHT: | ||
1959 | if (worm_count < MAX_WORMS) { | ||
1960 | worm_count ++; | ||
1961 | } | ||
1962 | break; | ||
1963 | case BUTTON_F1: | ||
1964 | use_remote = !use_remote; | ||
1965 | if (players > 2) { | ||
1966 | use_remote = true; | ||
1967 | } | ||
1968 | break; | ||
1969 | |||
1970 | case SYS_USB_CONNECTED: | ||
1971 | rb->usb_screen(); | ||
1972 | return PLUGIN_USB_CONNECTED; | ||
1973 | } | ||
1974 | } while (button != BUTTON_PLAY && | ||
1975 | button != BUTTON_OFF && button != BUTTON_ON); | ||
1976 | |||
1977 | rb->lcd_clear_display(); | ||
1978 | /* end of setup */ | ||
1979 | |||
1980 | do { | ||
1981 | |||
1982 | /* button state will be overridden if | ||
1983 | the game quits with the death of the worm. | ||
1984 | Initializing button to BUTTON_OFF ensures | ||
1985 | that the user can hit BUTTON_OFF during the | ||
1986 | game to return to the menu. | ||
1987 | */ | ||
1988 | button = BUTTON_OFF; | ||
1989 | |||
1990 | /* start the game */ | ||
1991 | worm_dead = run(); | ||
1992 | |||
1993 | /* if worm isn't dead the game was quit | ||
1994 | via BUTTON_OFF -> no need to wait for buttons. */ | ||
1995 | if (worm_dead) { | ||
1996 | do { | ||
1997 | button = rb->button_get(true); | ||
1998 | } | ||
1999 | /* BUTTON_ON -> start new game */ | ||
2000 | /* BUTTON_OFF -> back to game menu */ | ||
2001 | while (button != BUTTON_OFF && button != BUTTON_ON); | ||
2002 | } | ||
2003 | } | ||
2004 | while (button != BUTTON_OFF); | ||
2005 | |||
2006 | return PLUGIN_OK; | ||
2007 | } | ||
2008 | |||
2009 | #endif | ||