blob: d6850dc039f8f9c669850d86f76613d2548c5f2f [file] [log] [blame]
Eric Linenbergb0a39842002-08-26 03:32:39 +00001/***************************************************************************
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
Robert Hak8b1085d2002-08-31 23:05:36 +000020#include "config.h"
Daniel Stenberg6e881b42002-09-06 07:35:40 +000021#include "options.h"
Robert Hak8b1085d2002-08-31 23:05:36 +000022
Robert Hak8b1085d2002-08-31 23:05:36 +000023#ifdef USE_GAMES
Robert Hak620bb0e2002-08-31 22:53:43 +000024
Björn Stenberg50053d12002-09-09 23:30:16 +000025/* #define DEBUG_WORMLET */
26
Eric Linenbergb0a39842002-08-26 03:32:39 +000027#include <sprintf.h>
28#include <stdlib.h>
29#include <string.h>
Björn Stenberg50053d12002-09-09 23:30:16 +000030#include "system.h"
Eric Linenbergb0a39842002-08-26 03:32:39 +000031#include "lcd.h"
32#include "button.h"
33#include "kernel.h"
34#include "menu.h"
Björn Stenberg50053d12002-09-09 23:30:16 +000035#include "rtc.h"
Robert Hak2eda5382002-10-03 09:19:11 +000036#include "lang.h"
Björn Stenbergb1b8bd42002-09-24 17:22:12 +000037#include "screens.h"
Robert Hak8f11dc02002-10-29 10:45:29 +000038#include "font.h"
Eric Linenbergb0a39842002-08-26 03:32:39 +000039
Björn Stenberg50053d12002-09-09 23:30:16 +000040/* size of the field the worm lives in */
41#define FIELD_RECT_X 1
42#define FIELD_RECT_Y 1
Daniel Stenberg17b3bea2002-09-27 07:13:02 +000043#define FIELD_RECT_WIDTH (LCD_WIDTH - 45)
Björn Stenberg50053d12002-09-09 23:30:16 +000044#define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
Eric Linenbergb0a39842002-08-26 03:32:39 +000045
Björn Stenberg50053d12002-09-09 23:30:16 +000046/* size of the ring of the worm
47 choos a value that is a power of 2 to help
48 the compiler optimize modul operations*/
49#define MAX_WORM_SEGMENTS 64
50
51/* when the game starts */
52#define INITIAL_WORM_LENGTH 10
53
54/* num of pixel the worm grows per eaten food */
55#define WORM_PER_FOOD 7
56
57/* num of worms creeping in the FIELD */
58#define MAX_WORMS 3
59
60/* minimal distance between a worm and an argh
61 when a new argh is made */
62#define MIN_ARGH_DIST 5
63
64/**
65 * All the properties that a worm has.
66 */
Björn Stenbergc3fd67c2002-12-18 14:57:45 +000067static struct worm {
Björn Stenberg50053d12002-09-09 23:30:16 +000068 /* The worm is stored in a ring of xy coordinates */
69 char x[MAX_WORM_SEGMENTS];
70 char y[MAX_WORM_SEGMENTS];
71
72 int head; /* index of the head within the buffer */
73 int tail; /* index of the tail within the buffer */
74 int growing; /* number of cyles the worm still keeps growing */
75 bool alive; /* the worms living state */
76
77 /* direction vector in which the worm moves */
78 int dirx; /* only values -1 0 1 allowed */
79 int diry; /* only values -1 0 1 allowed */
80
81 /* this method is used to fetch the direction the user
82 has selected. It can be one of the values
83 human_player1, human_player2, remote_player, virtual_player.
84 All these values are fuctions, that can change the direction
85 of the worm */
86 void (*fetch_worm_direction)(struct worm *w);
87} worms[MAX_WORMS];
88
89/* stores the highscore - besides it was scored by a virtual player */
90static int highscore;
Eric Linenbergb0a39842002-08-26 03:32:39 +000091
Björn Stenberge67958b2002-08-26 09:21:59 +000092#define MAX_FOOD 5 /* maximal number of food items */
93#define FOOD_SIZE 3 /* the width and height of a food */
Björn Stenberg50053d12002-09-09 23:30:16 +000094
95/* The arrays store the food coordinates */
96static char foodx[MAX_FOOD];
97static char foody[MAX_FOOD];
Eric Linenbergb0a39842002-08-26 03:32:39 +000098
Björn Stenberge67958b2002-08-26 09:21:59 +000099#define MAX_ARGH 100 /* maximal number of argh items */
100#define ARGH_SIZE 4 /* the width and height of a argh */
101#define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000102
Björn Stenberg50053d12002-09-09 23:30:16 +0000103/* The arrays store the argh coordinates */
104static char arghx[MAX_ARGH];
105static char arghy[MAX_ARGH];
Eric Linenbergb0a39842002-08-26 03:32:39 +0000106
Björn Stenberg50053d12002-09-09 23:30:16 +0000107/* the number of arghs that are currently in use */
108static int argh_count;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000109
Björn Stenberg50053d12002-09-09 23:30:16 +0000110#ifdef DEBUG_WORMLET
111/* just a buffer used for debug output */
112static char debugout[15];
113#endif
114
115/* the number of ticks each game cycle should take */
116#define SPEED 14
117
118/* the number of active worms (dead or alive) */
119static int worm_count = MAX_WORMS;
120
121/* in multiplayer mode: en- / disables the remote worm control
122 in singleplayer mode: toggles 4 / 2 button worm control */
123static bool use_remote = false;
124
125/* return values of check_collision */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000126#define COLLISION_NONE 0
127#define COLLISION_WORM 1
128#define COLLISION_FOOD 2
129#define COLLISION_ARGH 3
130#define COLLISION_FIELD 4
131
Björn Stenberg50053d12002-09-09 23:30:16 +0000132/* constants for use as directions.
133 Note that the values are ordered clockwise.
134 Thus increasing / decreasing the values
135 is equivalent to right / left turns. */
136#define WEST 0
137#define NORTH 1
138#define EAST 2
139#define SOUTH 3
140
141/* direction of human player 1 */
142static int player1_dir = EAST;
143/* direction of human player 2 */
144static int player2_dir = EAST;
145/* direction of human player 3 */
146static int player3_dir = EAST;
147
148/* the number of (human) players that currently
149 control a worm */
150static int players = 1;
151
152#ifdef DEBUG_WORMLET
153static void set_debug_out(char *str){
154 strcpy(debugout, str);
155}
156#endif
Eric Linenbergb0a39842002-08-26 03:32:39 +0000157
158/**
Björn Stenberg50053d12002-09-09 23:30:16 +0000159 * Returns the direction id in which the worm
160 * currently is creeping.
161 * @param struct worm *w The worm that is to be investigated.
162 * w Must not be null.
163 * @return int A value 0 <= value < 4
164 * Note the predefined constants NORTH, SOUTH, EAST, WEST
Eric Linenbergb0a39842002-08-26 03:32:39 +0000165 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000166static int get_worm_dir(struct worm *w) {
167 int retVal ;
168 if (w->dirx == 0) {
169 if (w->diry == 1) {
170 retVal = SOUTH;
171 } else {
172 retVal = NORTH;
173 }
174 } else {
175 if (w->dirx == 1) {
176 retVal = EAST;
177 } else {
178 retVal = WEST;
179 }
180 }
181 return retVal;
182}
183
184/**
185 * Set the direction of the specified worm with a direction id.
186 * Increasing the value by 1 means to turn the worm direction
187 * to right by 90 degree.
188 * @param struct worm *w The worm that is to be altered. w Must not be null.
189 * @param int dir The new direction in which the worm is to creep.
190 * dir must be 0 <= dir < 4. Use predefined constants
191 * NORTH, SOUTH, EAST, WEST
192 */
193static void set_worm_dir(struct worm *w, int dir) {
194 switch (dir) {
195 case WEST:
196 w->dirx = -1;
197 w->diry = 0;
198 break;
199 case NORTH:
200 w->dirx = 0;
201 w->diry = - 1;
202 break;
203 case EAST:
204 w->dirx = 1;
205 w->diry = 0;
206 break;
207 case SOUTH:
208 w->dirx = 0;
209 w->diry = 1;
210 break;
211 }
212}
213
214/**
215 * Returns the current length of the worm array. This
216 * is also a value for the number of bends that are in the worm.
217 * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
218 */
219static int get_worm_array_length(struct worm *w) {
Björn Stenberge67958b2002-08-26 09:21:59 +0000220 /* initial simple calculation will be overwritten if wrong. */
Björn Stenberg50053d12002-09-09 23:30:16 +0000221 int retVal = w->head - w->tail;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000222
Björn Stenberge67958b2002-08-26 09:21:59 +0000223 /* if the worm 'crosses' the boundaries of the ringbuffer */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000224 if (retVal < 0) {
Björn Stenberg50053d12002-09-09 23:30:16 +0000225 retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000226 }
227
228 return retVal;
229}
230
231/**
Björn Stenberg50053d12002-09-09 23:30:16 +0000232 * Returns the score the specified worm. The score is the length
233 * of the worm.
234 * @param struct worm *w The worm that is to be investigated.
235 * w must not be null.
236 * @return int The length of the worm (>= 0).
237 */
238static int get_score(struct worm *w) {
239 int retval = 0;
240 int length = get_worm_array_length(w);
241 int i;
242 for (i = 0; i < length; i++) {
243
244 /* The iteration iterates the length of the worm.
245 Here's the conversion to the true indices within the worm arrays. */
246 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
247 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
248 int startx = w->x[linestart];
249 int starty = w->y[linestart];
250 int endx = w->x[lineend];
251 int endy = w->y[lineend];
252
253 int minimum, maximum;
254
255 if (startx == endx) {
256 minimum = MIN(starty, endy);
257 maximum = MAX(starty, endy);
258 } else {
259 minimum = MIN(startx, endx);
260 maximum = MAX(startx, endx);
261 }
262 retval += abs(maximum - minimum);
263 }
264 return retval;
265}
266
267/**
268 * Determines wether the line specified by startx, starty, endx, endy intersects
269 * the rectangle specified by x, y, width, height. Note that the line must be exactly
270 * horizontal or vertical (startx == endx or starty == endy).
271 * @param int startx The x coordinate of the start point of the line.
272 * @param int starty The y coordinate of the start point of the line.
273 * @param int endx The x coordinate of the end point of the line.
274 * @param int endy The y coordinate of the end point of the line.
275 * @param int x The x coordinate of the top left corner of the rectangle.
276 * @param int y The y coordinate of the top left corner of the rectangle.
277 * @param int width The width of the rectangle.
278 * @param int height The height of the rectangle.
279 * @return bool Returns true if the specified line intersects with the recangle.
280 */
281static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) {
282 bool retval = false;
283 int simple, simplemin, simplemax;
284 int compa, compb, compmin, compmax;
285 int temp;
286 if (startx == endx) {
287 simple = startx;
288 simplemin = x;
289 simplemax = x + width;
290
291 compa = starty;
292 compb = endy;
293 compmin = y;
294 compmax = y + height;
295 } else {
296 simple = starty;
297 simplemin = y;
298 simplemax = y + height;
299
300 compa = startx;
301 compb = endx;
302 compmin = x;
303 compmax = x + width;
304 };
305
306 temp = compa;
307 compa = MIN(compa, compb);
308 compb = MAX(temp, compb);
309
Eric Linenberg380ca882002-09-17 12:39:47 +0000310 if (simplemin <= simple && simple <= simplemax) {
311 if ((compmin <= compa && compa <= compmax) ||
312 (compmin <= compb && compb <= compmax) ||
313 (compa <= compmin && compb >= compmax)) {
Björn Stenberg50053d12002-09-09 23:30:16 +0000314 retval = true;
315 }
316 }
317 return retval;
318}
319
320/**
321 * Tests wether the specified worm intersects with the rect.
322 * @param struct worm *w The worm to be investigated
323 * @param int x The x coordinate of the top left corner of the rect
324 * @param int y The y coordinate of the top left corner of the rect
325 * @param int widht The width of the rect
326 * @param int height The height of the rect
327 * @return bool Returns true if the worm intersects with the rect
328 */
329static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) {
330 bool retval = false;
331
332
333 /* get_worm_array_length is expensive -> buffer the value */
334 int wormLength = get_worm_array_length(w);
335 int i;
336
337 /* test each entry that is part of the worm */
338 for (i = 0; i < wormLength && retval == false; i++) {
339
340 /* The iteration iterates the length of the worm.
341 Here's the conversion to the true indices within the worm arrays. */
342 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
343 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
344 int startx = w->x[linestart];
345 int starty = w->y[linestart];
346 int endx = w->x[lineend];
347 int endy = w->y[lineend];
348
349 retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
350 }
351
352 return retval;
353}
354
355/**
Eric Linenbergb0a39842002-08-26 03:32:39 +0000356 * Checks wether a specific food in the food arrays is at the
357 * specified coordinates.
358 * @param int foodIndex The index of the food in the food arrays
359 * @param int x the x coordinate.
360 * @param int y the y coordinate.
361 * @return Returns true if the coordinate hits the food specified by
362 * foodIndex.
363 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000364static bool specific_food_collision(int foodIndex, int x, int y) {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000365 bool retVal = false;
366 if (x >= foodx[foodIndex] &&
367 x < foodx[foodIndex] + FOOD_SIZE &&
368 y >= foody[foodIndex] &&
369 y < foody[foodIndex] + FOOD_SIZE) {
370
371 retVal = true;
372 }
373 return retVal;
374}
375
376/**
377 * Returns the index of the food that is at the
378 * given coordinates. If no food is at the coordinates
379 * -1 is returned.
380 * @return int -1 <= value < MAX_FOOD
381 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000382static int food_collision(int x, int y) {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000383 int i = 0;
384 int retVal = -1;
Björn Stenberge67958b2002-08-26 09:21:59 +0000385 for (i = 0; i < MAX_FOOD; i++) {
Björn Stenberg50053d12002-09-09 23:30:16 +0000386 if (specific_food_collision(i, x, y)) {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000387 retVal = i;
Björn Stenberge67958b2002-08-26 09:21:59 +0000388 break;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000389 }
390 }
391 return retVal;
392}
393
394/**
395 * Checks wether a specific argh in the argh arrays is at the
396 * specified coordinates.
397 * @param int arghIndex The index of the argh in the argh arrays
398 * @param int x the x coordinate.
399 * @param int y the y coordinate.
400 * @return Returns true if the coordinate hits the argh specified by
401 * arghIndex.
402 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000403static bool specific_argh_collision(int arghIndex, int x, int y) {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000404
Björn Stenberge67958b2002-08-26 09:21:59 +0000405 if ( x >= arghx[arghIndex] &&
406 y >= arghy[arghIndex] &&
407 x < arghx[arghIndex] + ARGH_SIZE &&
408 y < arghy[arghIndex] + ARGH_SIZE )
409 {
410 return true;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000411 }
Björn Stenberge67958b2002-08-26 09:21:59 +0000412
413 return false;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000414}
415
416/**
417 * Returns the index of the argh that is at the
418 * given coordinates. If no argh is at the coordinates
419 * -1 is returned.
420 * @param int x The x coordinate.
421 * @param int y The y coordinate.
Björn Stenberg50053d12002-09-09 23:30:16 +0000422 * @return int -1 <= value < argh_count <= MAX_ARGH
Eric Linenbergb0a39842002-08-26 03:32:39 +0000423 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000424static int argh_collision(int x, int y) {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000425 int i = 0;
426 int retVal = -1;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000427
Björn Stenberge67958b2002-08-26 09:21:59 +0000428 /* search for the argh that has the specified coords */
Björn Stenberg50053d12002-09-09 23:30:16 +0000429 for (i = 0; i < argh_count; i++) {
430 if (specific_argh_collision(i, x, y)) {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000431 retVal = i;
Björn Stenberge67958b2002-08-26 09:21:59 +0000432 break;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000433 }
434 }
435 return retVal;
436}
437
438/**
439 * Checks wether the worm collides with the food at the specfied food-arrays.
440 * @param int foodIndex The index of the food in the arrays. Ensure the value is
441 * 0 <= foodIndex <= MAX_FOOD
442 * @return Returns true if the worm collides with the specified food.
443 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000444static bool worm_food_collision(struct worm *w, int foodIndex)
Björn Stenberge67958b2002-08-26 09:21:59 +0000445{
Eric Linenbergb0a39842002-08-26 03:32:39 +0000446 bool retVal = false;
447
Björn Stenberg50053d12002-09-09 23:30:16 +0000448 retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex],
Eric Linenberg380ca882002-09-17 12:39:47 +0000449 FOOD_SIZE - 1, FOOD_SIZE - 1);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000450
Björn Stenberg50053d12002-09-09 23:30:16 +0000451 return retVal;
452}
Eric Linenbergb0a39842002-08-26 03:32:39 +0000453
Björn Stenberg50053d12002-09-09 23:30:16 +0000454/**
455 * Returns true if the worm hits the argh within the next moves (unless
456 * the worm changes it's direction).
457 * @param struct worm *w - The worm to investigate
458 * @param int argh_idx - The index of the argh
459 * @param int moves - The number of moves that are considered.
460 * @return Returns false if the specified argh is not hit within the next
461 * moves.
462 */
463static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){
464 bool retVal = false;
465 int x1, y1, x2, y2;
466 x1 = w->x[w->head];
467 y1 = w->y[w->head];
Eric Linenbergb0a39842002-08-26 03:32:39 +0000468
Björn Stenberg50053d12002-09-09 23:30:16 +0000469 x2 = w->x[w->head] + moves * w->dirx;
470 y2 = w->y[w->head] + moves * w->diry;
471
472 retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx],
473 ARGH_SIZE, ARGH_SIZE);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000474 return retVal;
475}
476
477/**
478 * Checks wether the worm collides with the argh at the specfied argh-arrays.
Björn Stenberge67958b2002-08-26 09:21:59 +0000479 * @param int arghIndex The index of the argh in the arrays.
Björn Stenberg50053d12002-09-09 23:30:16 +0000480 * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
Eric Linenbergb0a39842002-08-26 03:32:39 +0000481 * @return Returns true if the worm collides with the specified argh.
482 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000483static bool worm_argh_collision(struct worm *w, int arghIndex)
Björn Stenberge67958b2002-08-26 09:21:59 +0000484{
Eric Linenbergb0a39842002-08-26 03:32:39 +0000485 bool retVal = false;
486
Björn Stenberg50053d12002-09-09 23:30:16 +0000487 retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex],
Eric Linenberg380ca882002-09-17 12:39:47 +0000488 ARGH_SIZE - 1, ARGH_SIZE - 1);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000489
490 return retVal;
491}
492
493/**
494 * Find new coordinates for the food stored in foodx[index], foody[index]
495 * that don't collide with any other food or argh
496 * @param int index
497 * Ensure that 0 <= index < MAX_FOOD.
498 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000499static int make_food(int index) {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000500
501 int x = 0;
502 int y = 0;
503 bool collisionDetected = false;
Björn Stenberg50053d12002-09-09 23:30:16 +0000504 int tries = 0;
505 int i;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000506
507 do {
Björn Stenberge67958b2002-08-26 09:21:59 +0000508 /* make coordinates for a new food so that
509 the entire food lies within the FIELD */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000510 x = rand() % (FIELD_RECT_WIDTH - FOOD_SIZE);
511 y = rand() % (FIELD_RECT_HEIGHT - FOOD_SIZE);
Björn Stenberg50053d12002-09-09 23:30:16 +0000512 tries ++;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000513
Björn Stenberge67958b2002-08-26 09:21:59 +0000514 /* Ensure that the new food doesn't collide with any
515 existing foods or arghs.
516 If one or more corners of the new food hit any existing
517 argh or food a collision is detected.
518 */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000519 collisionDetected =
Björn Stenberg50053d12002-09-09 23:30:16 +0000520 food_collision(x , y ) >= 0 ||
521 food_collision(x , y + FOOD_SIZE - 1) >= 0 ||
522 food_collision(x + FOOD_SIZE - 1, y ) >= 0 ||
523 food_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0 ||
524 argh_collision(x , y ) >= 0 ||
525 argh_collision(x , y + FOOD_SIZE - 1) >= 0 ||
526 argh_collision(x + FOOD_SIZE - 1, y ) >= 0 ||
527 argh_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000528
Björn Stenberge67958b2002-08-26 09:21:59 +0000529 /* use coordinates for further testing */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000530 foodx[index] = x;
531 foody[index] = y;
532
Björn Stenberge67958b2002-08-26 09:21:59 +0000533 /* now test wether we accidently hit the worm with food ;) */
Björn Stenberg50053d12002-09-09 23:30:16 +0000534 i = 0;
535 for (i = 0; i < worm_count && !collisionDetected; i++) {
536 collisionDetected |= worm_food_collision(&worms[i], index);
537 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000538 }
539 while (collisionDetected);
Björn Stenberg50053d12002-09-09 23:30:16 +0000540 return tries;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000541}
542
543/**
544 * Clears a food from 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 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000549static void clear_food(int index)
Björn Stenberge67958b2002-08-26 09:21:59 +0000550{
551 /* remove the old food from the screen */
552 lcd_clearrect(foodx[index] + FIELD_RECT_X,
553 foody[index] + FIELD_RECT_Y,
554 FOOD_SIZE, FOOD_SIZE);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000555}
556
557/**
558 * Draws a food in the lcd buffer.
559 * @param int index The index of the food arrays under which
560 * the coordinates of the desired food can be found. Ensure
561 * that the value is 0 <= index <= MAX_FOOD.
562 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000563static void draw_food(int index)
Björn Stenberge67958b2002-08-26 09:21:59 +0000564{
565 /* draw the food object */
566 lcd_fillrect(foodx[index] + FIELD_RECT_X,
567 foody[index] + FIELD_RECT_Y,
568 FOOD_SIZE, FOOD_SIZE);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000569 lcd_clearrect(foodx[index] + FIELD_RECT_X + 1,
Björn Stenberge67958b2002-08-26 09:21:59 +0000570 foody[index] + FIELD_RECT_Y + 1,
571 FOOD_SIZE - 2, FOOD_SIZE - 2);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000572}
573
574/**
575 * Find new coordinates for the argh stored in arghx[index], arghy[index]
576 * that don't collide with any other food or argh.
577 * @param int index
Björn Stenberg50053d12002-09-09 23:30:16 +0000578 * Ensure that 0 <= index < argh_count < MAX_ARGH.
Eric Linenbergb0a39842002-08-26 03:32:39 +0000579 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000580static int make_argh(int index)
Björn Stenberge67958b2002-08-26 09:21:59 +0000581{
Björn Stenberg50053d12002-09-09 23:30:16 +0000582 int x = -1;
583 int y = -1;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000584 bool collisionDetected = false;
Björn Stenberg50053d12002-09-09 23:30:16 +0000585 int tries = 0;
586 int i;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000587
588 do {
Björn Stenberge67958b2002-08-26 09:21:59 +0000589 /* make coordinates for a new argh so that
590 the entire food lies within the FIELD */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000591 x = rand() % (FIELD_RECT_WIDTH - ARGH_SIZE);
592 y = rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE);
Björn Stenberg50053d12002-09-09 23:30:16 +0000593 tries ++;
594
Björn Stenberge67958b2002-08-26 09:21:59 +0000595 /* Ensure that the new argh doesn't intersect with any
596 existing foods or arghs.
597 If one or more corners of the new argh hit any existing
598 argh or food an intersection is detected.
599 */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000600 collisionDetected =
Björn Stenberg50053d12002-09-09 23:30:16 +0000601 food_collision(x , y ) >= 0 ||
602 food_collision(x , y + ARGH_SIZE - 1) >= 0 ||
603 food_collision(x + ARGH_SIZE - 1, y ) >= 0 ||
604 food_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0 ||
605 argh_collision(x , y ) >= 0 ||
606 argh_collision(x , y + ARGH_SIZE - 1) >= 0 ||
607 argh_collision(x + ARGH_SIZE - 1, y ) >= 0 ||
608 argh_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000609
Björn Stenberge67958b2002-08-26 09:21:59 +0000610 /* use the candidate coordinates to make a real argh */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000611 arghx[index] = x;
612 arghy[index] = y;
613
Björn Stenberge67958b2002-08-26 09:21:59 +0000614 /* now test wether we accidently hit the worm with argh ;) */
Björn Stenberg50053d12002-09-09 23:30:16 +0000615 for (i = 0; i < worm_count && !collisionDetected; i++) {
616 collisionDetected |= worm_argh_collision(&worms[i], index);
617 collisionDetected |= worm_argh_collision_in_moves(&worms[i], index,
618 MIN_ARGH_DIST);
619 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000620 }
621 while (collisionDetected);
Björn Stenberg50053d12002-09-09 23:30:16 +0000622 return tries;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000623}
624
625/**
626 * Draws an argh in the lcd buffer.
627 * @param int index The index of the argh arrays under which
628 * the coordinates of the desired argh can be found. Ensure
Björn Stenberg50053d12002-09-09 23:30:16 +0000629 * that the value is 0 <= index < argh_count <= MAX_ARGH.
Eric Linenbergb0a39842002-08-26 03:32:39 +0000630 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000631static void draw_argh(int index)
Björn Stenberge67958b2002-08-26 09:21:59 +0000632{
633 /* draw the new argh */
634 lcd_fillrect(arghx[index] + FIELD_RECT_X,
635 arghy[index] + FIELD_RECT_Y,
636 ARGH_SIZE, ARGH_SIZE);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000637}
638
Björn Stenberg50053d12002-09-09 23:30:16 +0000639static void virtual_player(struct worm *w);
640/**
641 * Initialzes the specified worm with INITIAL_WORM_LENGTH
642 * and the tail at the specified position. The worm will
643 * be initialized alive and creeping EAST.
644 * @param struct worm *w The worm that is to be initialized
645 * @param int x The x coordinate at which the tail of the worm starts.
646 * x must be 0 <= x < FIELD_RECT_WIDTH.
647 * @param int y The y coordinate at which the tail of the worm starts
648 * y must be 0 <= y < FIELD_RECT_WIDTH.
649 */
650static void init_worm(struct worm *w, int x, int y){
651 /* initialize the worm size */
652 w->head = 1;
653 w->tail = 0;
654
655 w->x[w->head] = x + 1;
656 w->y[w->head] = y;
657
658 w->x[w->tail] = x;
659 w->y[w->tail] = y;
660
661 /* set the initial direction the worm creeps to */
662 w->dirx = 1;
663 w->diry = 0;
664
665 w->growing = INITIAL_WORM_LENGTH - 1;
666 w->alive = true;
667 w->fetch_worm_direction = virtual_player;
668}
669
670/**
671 * Writes the direction that was stored for
672 * human player 1 into the specified worm. This function
673 * may be used to be stored in worm.fetch_worm_direction.
674 * The value of
675 * the direction is read from player1_dir.
676 * @param struct worm *w - The worm of which the direction
677 * is altered.
678 */
679static void human_player1(struct worm *w) {
680 set_worm_dir(w, player1_dir);
681}
682
683/**
684 * Writes the direction that was stored for
685 * human player 2 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 player2_dir.
689 * @param struct worm *w - The worm of which the direction
690 * is altered.
691 */
692static void human_player2(struct worm *w) {
693 set_worm_dir(w, player2_dir);
694}
695
696/**
697 * Writes the direction that was stored for
698 * human player using a remote control
699 * into the specified worm. This function
700 * may be used to be stored in worm.fetch_worm_direction.
701 * The value of
702 * the direction is read from player3_dir.
703 * @param struct worm *w - The worm of which the direction
704 * is altered.
705 */
706static void remote_player(struct worm *w) {
707 set_worm_dir(w, player3_dir);
708}
709
Eric Linenbergb0a39842002-08-26 03:32:39 +0000710/**
711 * Initializes the worm-, food- and argh-arrays, draws a frame,
712 * makes some food and argh and display all that stuff.
713 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000714static void init_wormlet(void)
Björn Stenberge67958b2002-08-26 09:21:59 +0000715{
716 int i;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000717
Björn Stenberg50053d12002-09-09 23:30:16 +0000718 for (i = 0; i< worm_count; i++) {
719 /* Initialize all the worm coordinates to center. */
720 int x = (int)(FIELD_RECT_WIDTH / 2);
721 int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
722
723 init_worm(&worms[i], x, y);
724 }
725
726 player1_dir = EAST;
727 player2_dir = EAST;
728 player3_dir = EAST;
729
730 if (players > 0) {
731 worms[0].fetch_worm_direction = human_player1;
732 }
733
734 if (players > 1) {
735 if (use_remote) {
736 worms[1].fetch_worm_direction = remote_player;
737 } else {
738 worms[1].fetch_worm_direction = human_player2;
739 }
740 }
741
742 if (players > 2) {
743 worms[2].fetch_worm_direction = human_player2;
744 }
Björn Stenberge67958b2002-08-26 09:21:59 +0000745
746 /* Needed when the game is restarted using BUTTON_ON */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000747 lcd_clear_display();
748
Björn Stenberge67958b2002-08-26 09:21:59 +0000749 /* make and display some food and argh */
Björn Stenberg50053d12002-09-09 23:30:16 +0000750 argh_count = MAX_FOOD;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000751 for (i = 0; i < MAX_FOOD; i++) {
Björn Stenberg50053d12002-09-09 23:30:16 +0000752 make_food(i);
753 draw_food(i);
754 make_argh(i);
755 draw_argh(i);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000756 }
757
Björn Stenberge67958b2002-08-26 09:21:59 +0000758 /* draw the game field */
759 lcd_invertrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
760 lcd_invertrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000761
Björn Stenberge67958b2002-08-26 09:21:59 +0000762 /* make everything visible */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000763 lcd_update();
764}
765
Björn Stenberg50053d12002-09-09 23:30:16 +0000766
Eric Linenbergb0a39842002-08-26 03:32:39 +0000767/**
Björn Stenberg50053d12002-09-09 23:30:16 +0000768 * Move the worm one step further if it is alive.
769 * The direction in which the worm moves is taken from dirx and diry.
770 * move_worm decreases growing if > 0. While the worm is growing the tail
771 * is left untouched.
772 * @param struct worm *w The worm to move. w must not be NULL.
Eric Linenbergb0a39842002-08-26 03:32:39 +0000773 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000774static void move_worm(struct worm *w)
Björn Stenberge67958b2002-08-26 09:21:59 +0000775{
Björn Stenberg50053d12002-09-09 23:30:16 +0000776 if (w->alive) {
777 /* determine the head point and its precessor */
778 int headx = w->x[w->head];
779 int heady = w->y[w->head];
780 int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
781 int preheadx = w->x[prehead];
782 int preheady = w->y[prehead];
Eric Linenbergb0a39842002-08-26 03:32:39 +0000783
Björn Stenberg50053d12002-09-09 23:30:16 +0000784 /* determine the old direction */
785 int olddirx;
786 int olddiry;
787 if (headx == preheadx) {
788 olddirx = 0;
789 olddiry = (heady > preheady) ? 1 : -1;
790 } else {
791 olddiry = 0;
792 olddirx = (headx > preheadx) ? 1 : -1;
793 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000794
Björn Stenberg50053d12002-09-09 23:30:16 +0000795 /* olddir == dir?
796 a change of direction means a new segment
797 has been opened */
798 if (olddirx != w->dirx ||
799 olddiry != w->diry) {
800 w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
801 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000802
Björn Stenberg50053d12002-09-09 23:30:16 +0000803 /* new head position */
804 w->x[w->head] = headx + w->dirx;
805 w->y[w->head] = heady + w->diry;
Eric Linenbergb0a39842002-08-26 03:32:39 +0000806
Eric Linenbergb0a39842002-08-26 03:32:39 +0000807
Björn Stenberg50053d12002-09-09 23:30:16 +0000808 /* while the worm is growing no tail procession is necessary */
809 if (w->growing > 0) {
Björn Stenberge67958b2002-08-26 09:21:59 +0000810 /* update the worms grow state */
Björn Stenberg50053d12002-09-09 23:30:16 +0000811 w->growing--;
812 }
813
814 /* if the worm isn't growing the tail has to be dragged */
815 else {
816 /* index of the end of the tail segment */
817 int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
818
819 /* drag the end of the tail */
820 /* only one coordinate has to be altered. Here it is
821 determined which one */
822 int dir = 0; /* specifies wether the coord has to be in- or decreased */
823 if (w->x[w->tail] == w->x[tail_segment_end]) {
824 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
825 w->y[w->tail] += dir;
826 } else {
827 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
828 w->x[w->tail] += dir;
829 }
830
831 /* when the tail has been dragged so far that it meets
832 the next segment start the tail segment is obsolete and
833 must be freed */
834 if (w->x[w->tail] == w->x[tail_segment_end] &&
835 w->y[w->tail] == w->y[tail_segment_end]){
836
837 /* drop the last tail point */
838 w->tail = tail_segment_end;
839 }
840 }
841 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000842}
843
844/**
845 * Draws the head and clears the tail of the worm in
846 * the display buffer. lcd_update() is NOT called thus
847 * the caller has to take care that the buffer is displayed.
848 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000849static void draw_worm(struct worm *w)
Björn Stenberge67958b2002-08-26 09:21:59 +0000850{
851 /* draw the new head */
Björn Stenberg50053d12002-09-09 23:30:16 +0000852 int x = w->x[w->head];
853 int y = w->y[w->head];
854 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
855 lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
856 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000857
Björn Stenberge67958b2002-08-26 09:21:59 +0000858 /* clear the space behind the worm */
Björn Stenberg50053d12002-09-09 23:30:16 +0000859 x = w->x[w->tail] ;
860 y = w->y[w->tail] ;
861 if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
862 lcd_clearpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
863 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000864}
865
866/**
Björn Stenberg50053d12002-09-09 23:30:16 +0000867 * Checks wether the coordinate is part of the worm. Returns
868 * true if any part of the worm was hit - including the head.
Eric Linenbergb0a39842002-08-26 03:32:39 +0000869 * @param x int The x coordinate
870 * @param y int The y coordinate
871 * @return int The index of the worm arrays that contain x, y.
872 * Returns -1 if the coordinates are not part of the worm.
873 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000874static int specific_worm_collision(struct worm *w, int x, int y)
Björn Stenberge67958b2002-08-26 09:21:59 +0000875{
Eric Linenbergb0a39842002-08-26 03:32:39 +0000876 int retVal = -1;
877
Björn Stenberg50053d12002-09-09 23:30:16 +0000878 /* get_worm_array_length is expensive -> buffer the value */
879 int wormLength = get_worm_array_length(w);
Eric Linenbergb0a39842002-08-26 03:32:39 +0000880 int i;
881
Björn Stenberge67958b2002-08-26 09:21:59 +0000882 /* test each entry that is part of the worm */
Eric Linenbergb0a39842002-08-26 03:32:39 +0000883 for (i = 0; i < wormLength && retVal == -1; i++) {
884
Björn Stenberge67958b2002-08-26 09:21:59 +0000885 /* The iteration iterates the length of the worm.
886 Here's the conversion to the true indices within the worm arrays. */
Björn Stenberg50053d12002-09-09 23:30:16 +0000887 int linestart = (w->tail + i ) % MAX_WORM_SEGMENTS;
888 int lineend = (linestart + 1) % MAX_WORM_SEGMENTS;
889 bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
890 bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
891 if (samex || samey){
892 int test, min, max, tmp;
893
894 if (samey) {
895 min = w->x[linestart];
896 max = w->x[lineend];
897 test = x;
898 } else {
899 min = w->y[linestart];
900 max = w->y[lineend];
901 test = y;
902 }
903
904 tmp = min;
905 min = MIN(min, max);
906 max = MAX(tmp, max);
907
908 if (min <= test && test <= max) {
909 retVal = lineend;
910 }
911 }
912 }
913 return retVal;
914}
915
916/**
917 * Increases the length of the specified worm by marking
918 * that it may grow by len pixels. Note that the worm has
919 * to move to make the growing happen.
920 * @param worm *w The worm that is to be altered.
921 * @param int len A positive value specifying the amount of
922 * pixels the worm may grow.
923 */
924static void add_growing(struct worm *w, int len) {
925 w->growing += len;
926}
927
928/**
929 * Determins the worm that is at the coordinates x, y. The parameter
930 * w is a switch parameter that changes the functionality of worm_collision.
931 * If w is specified and x,y hits the head of w NULL is returned.
932 * This is a useful way to determine wether the head of w hits
933 * any worm but including itself but excluding its own head.
934 * (It hits always its own head ;))
935 * If w is set to NULL worm_collision returns any worm including all heads
936 * that is at position of x,y.
937 * @param struct worm *w The worm of which the head should be excluded in
938 * the test. w may be set to NULL.
939 * @param int x The x coordinate that is checked
940 * @param int y The y coordinate that is checkec
941 * @return struct worm* The worm that has been hit by x,y. If no worm
942 * was at the position NULL is returned.
943 */
944static struct worm* worm_collision(struct worm *w, int x, int y){
945 struct worm *retVal = NULL;
946 int i;
947 for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
948 int collision_at = specific_worm_collision(&worms[i], x, y);
949 if (collision_at != -1) {
950 if (!(w == &worms[i] && collision_at == w->head)){
951 retVal = &worms[i];
952 }
953 }
Eric Linenbergb0a39842002-08-26 03:32:39 +0000954 }
955 return retVal;
956}
957
958/**
959 * Returns true if the head of the worm just has
960 * crossed the field boundaries.
961 * @return bool true if the worm just has wrapped.
962 */
Björn Stenberg50053d12002-09-09 23:30:16 +0000963static bool field_collision(struct worm *w)
Björn Stenberge67958b2002-08-26 09:21:59 +0000964{
Eric Linenbergb0a39842002-08-26 03:32:39 +0000965 bool retVal = false;
Björn Stenberg50053d12002-09-09 23:30:16 +0000966 if ((w->x[w->head] >= FIELD_RECT_WIDTH) ||
967 (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
968 (w->x[w->head] < 0) ||
969 (w->y[w->head] < 0))
Björn Stenberge67958b2002-08-26 09:21:59 +0000970 {
Eric Linenbergb0a39842002-08-26 03:32:39 +0000971 retVal = true;
972 }
973 return retVal;
974}
975
Björn Stenberg50053d12002-09-09 23:30:16 +0000976
Eric Linenbergb0a39842002-08-26 03:32:39 +0000977/**
Björn Stenberg50053d12002-09-09 23:30:16 +0000978 * Returns true if the specified coordinates are within the
979 * field specified by the FIELD_RECT_XXX constants.
980 * @param int x The x coordinate of the point that is investigated
981 * @param int y The y coordinate of the point that is investigated
982 * @return bool Returns false if x,y specifies a point outside the
983 * field of worms.
984 */
985static bool is_in_field_rect(int x, int y) {
986 bool retVal = false;
987 retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
988 y >= 0 && y < FIELD_RECT_HEIGHT);
989 return retVal;
990}
991
992/**
993 * Checks and returns wether the head of the w
Eric Linenbergb0a39842002-08-26 03:32:39 +0000994 * is colliding with something currently.
Björn Stenberge67958b2002-08-26 09:21:59 +0000995 * @return int One of the values:
996 * COLLISION_NONE
Björn Stenberg50053d12002-09-09 23:30:16 +0000997 * COLLISION_w
Björn Stenberge67958b2002-08-26 09:21:59 +0000998 * COLLISION_FOOD
999 * COLLISION_ARGH
1000 * COLLISION_FIELD
Eric Linenbergb0a39842002-08-26 03:32:39 +00001001 */
Björn Stenberg50053d12002-09-09 23:30:16 +00001002static int check_collision(struct worm *w)
Björn Stenberge67958b2002-08-26 09:21:59 +00001003{
Eric Linenbergb0a39842002-08-26 03:32:39 +00001004 int retVal = COLLISION_NONE;
1005
Björn Stenberg50053d12002-09-09 23:30:16 +00001006 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
Eric Linenbergb0a39842002-08-26 03:32:39 +00001007 retVal = COLLISION_WORM;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001008
Björn Stenberg50053d12002-09-09 23:30:16 +00001009 if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
Eric Linenbergb0a39842002-08-26 03:32:39 +00001010 retVal = COLLISION_FOOD;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001011
Björn Stenberg50053d12002-09-09 23:30:16 +00001012 if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
Eric Linenbergb0a39842002-08-26 03:32:39 +00001013 retVal = COLLISION_ARGH;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001014
Björn Stenberg50053d12002-09-09 23:30:16 +00001015 if (field_collision(w))
Eric Linenbergb0a39842002-08-26 03:32:39 +00001016 retVal = COLLISION_FIELD;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001017
1018 return retVal;
1019}
1020
Eric Linenbergb0a39842002-08-26 03:32:39 +00001021/**
Björn Stenberg50053d12002-09-09 23:30:16 +00001022 * Returns the index of the food that is closest to the point
1023 * specified by x, y. This index may be used in the foodx and
1024 * foody arrays.
1025 * @param int x The x coordinate of the point
1026 * @param int y The y coordinate of the point
1027 * @return int A value usable as index in foodx and foody.
1028 */
1029static int get_nearest_food(int x, int y){
1030 int nearestfood = 0;
1031 int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
1032 int deltax = 0;
1033 int deltay = 0;
1034 int foodindex;
1035 for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
1036 int distance;
1037 deltax = foodx[foodindex] - x;
1038 deltay = foody[foodindex] - y;
1039 deltax = deltax > 0 ? deltax : deltax * (-1);
1040 deltay = deltay > 0 ? deltay : deltay * (-1);
1041 distance = deltax + deltay;
1042
1043 if (distance < olddistance) {
1044 olddistance = distance;
1045 nearestfood = foodindex;
1046 }
1047 }
1048 return nearestfood;
1049}
1050
1051/**
1052 * Returns wether the specified position is next to the worm
1053 * and in the direction the worm looks. Use this method to
1054 * test wether this position would be hit with the next move of
1055 * the worm unless the worm changes its direction.
1056 * @param struct worm *w - The worm to be investigated
1057 * @param int x - The x coordinate of the position to test.
1058 * @param int y - The y coordinate of the position to test.
1059 * @return Returns true if the worm will hit the position unless
1060 * it change its direction before the next move.
1061 */
1062static bool is_in_front_of_worm(struct worm *w, int x, int y) {
1063 bool infront = false;
1064 int deltax = x - w->x[w->head];
1065 int deltay = y - w->y[w->head];
1066
1067 if (w->dirx == 0) {
1068 infront = (w->diry * deltay) > 0;
1069 } else {
1070 infront = (w->dirx * deltax) > 0;
1071 }
1072 return infront;
1073}
1074
1075/**
1076 * Returns true if the worm will collide with the next move unless
1077 * it changes its direction.
1078 * @param struct worm *w - The worm to be investigated.
1079 * @return Returns true if the worm will collide with the next move
1080 * unless it changes its direction.
1081 */
1082static bool will_worm_collide(struct worm *w) {
1083 int x = w->x[w->head] + w->dirx;
1084 int y = w->y[w->head] + w->diry;
1085 bool retVal = !is_in_field_rect(x, y);
1086 if (!retVal) {
1087 retVal = (argh_collision(x, y) != -1);
1088 }
1089
1090 if (!retVal) {
1091 retVal = (worm_collision(w, x, y) != NULL);
1092 }
1093 return retVal;
1094}
1095
1096/**
1097 * This function
1098 * may be used to be stored in worm.fetch_worm_direction for
1099 * worms that are not controlled by humans but by artificial stupidity.
1100 * A direction is searched that doesn't lead to collision but to the nearest
1101 * food - but not very intelligent. The direction is written to the specified
1102 * worm.
1103 * @param struct worm *w - The worm of which the direction
1104 * is altered.
1105 */
1106static void virtual_player(struct worm *w) {
1107 bool isright;
1108 int plana, planb, planc;
1109 /* find the next lunch */
1110 int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
1111
1112 /* determine in which direction it is */
1113
1114 /* in front of me? */
1115 bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1116
1117 /* left right of me? */
1118 int olddir = get_worm_dir(w);
1119 set_worm_dir(w, (olddir + 1) % 4);
1120 isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
1121 set_worm_dir(w, olddir);
1122
1123 /* detect situation, set strategy */
1124 if (infront) {
1125 if (isright) {
1126 plana = olddir;
1127 planb = (olddir + 1) % 4;
1128 planc = (olddir + 3) % 4;
1129 } else {
1130 plana = olddir;
1131 planb = (olddir + 3) % 4;
1132 planc = (olddir + 1) % 4;
1133 }
1134 } else {
1135 if (isright) {
1136 plana = (olddir + 1) % 4;
1137 planb = olddir;
1138 planc = (olddir + 3) % 4;
1139 } else {
1140 plana = (olddir + 3) % 4;
1141 planb = olddir;
1142 planc = (olddir + 1) % 4;
1143 }
1144 }
1145
1146 /* test for collision */
1147 set_worm_dir(w, plana);
1148 if (will_worm_collide(w)){
1149
1150 /* plan b */
1151 set_worm_dir(w, planb);
1152
1153 /* test for collision */
1154 if (will_worm_collide(w)) {
1155
1156 /* plan c */
1157 set_worm_dir(w, planc);
1158 }
1159 }
1160}
1161
1162/**
1163 * prints out the score board with all the status information
Eric Linenbergb0a39842002-08-26 03:32:39 +00001164 * about the game.
1165 */
Björn Stenberg50053d12002-09-09 23:30:16 +00001166static void score_board(void)
Björn Stenberge67958b2002-08-26 09:21:59 +00001167{
Eric Linenbergb0a39842002-08-26 03:32:39 +00001168 char buf[15];
1169 char buf2[15];
Björn Stenberg50053d12002-09-09 23:30:16 +00001170 int i;
1171 int y = 0;
1172 lcd_clearrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
1173 for (i = 0; i < worm_count; i++) {
1174 int score = get_score(&worms[i]);
1175
1176 /* high score */
1177 if (worms[i].fetch_worm_direction != virtual_player){
1178 if (highscore < score) {
1179 highscore = score;
1180 }
1181 }
Eric Linenbergb0a39842002-08-26 03:32:39 +00001182
Björn Stenberge67958b2002-08-26 09:21:59 +00001183 /* length */
Björn Stenberg505eca72002-09-18 14:08:05 +00001184 snprintf(buf, sizeof (buf),str(LANG_WORMLET_LENGTH), score);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001185
Björn Stenberg50053d12002-09-09 23:30:16 +00001186 /* worm state */
1187 switch (check_collision(&worms[i])) {
Eric Linenbergb0a39842002-08-26 03:32:39 +00001188 case COLLISION_NONE:
Björn Stenberg50053d12002-09-09 23:30:16 +00001189 if (worms[i].growing > 0){
Björn Stenberg505eca72002-09-18 14:08:05 +00001190 snprintf(buf2, sizeof(buf2), str(LANG_WORMLET_GROWING));
Björn Stenberg50053d12002-09-09 23:30:16 +00001191 }
1192 else {
1193 if (worms[i].alive) {
Björn Stenberg505eca72002-09-18 14:08:05 +00001194 snprintf(buf2, sizeof(buf2), str(LANG_WORMLET_HUNGRY));
Björn Stenberg50053d12002-09-09 23:30:16 +00001195 } else {
Björn Stenberg505eca72002-09-18 14:08:05 +00001196 snprintf(buf2, sizeof(buf2), str(LANG_WORMLET_WORMED));
Björn Stenberg50053d12002-09-09 23:30:16 +00001197 }
1198 }
Eric Linenbergb0a39842002-08-26 03:32:39 +00001199 break;
1200
1201 case COLLISION_WORM:
Björn Stenberg505eca72002-09-18 14:08:05 +00001202 snprintf(buf2, sizeof(buf2), str(LANG_WORMLET_WORMED));
Eric Linenbergb0a39842002-08-26 03:32:39 +00001203 break;
1204
1205 case COLLISION_FOOD:
Björn Stenberg505eca72002-09-18 14:08:05 +00001206 snprintf(buf2, sizeof(buf2), str(LANG_WORMLET_GROWING));
Eric Linenbergb0a39842002-08-26 03:32:39 +00001207 break;
1208
1209 case COLLISION_ARGH:
Björn Stenberg505eca72002-09-18 14:08:05 +00001210 snprintf(buf2, sizeof(buf2), str(LANG_WORMLET_ARGH));
Eric Linenbergb0a39842002-08-26 03:32:39 +00001211 break;
1212
1213 case COLLISION_FIELD:
Björn Stenberg505eca72002-09-18 14:08:05 +00001214 snprintf(buf2, sizeof(buf2), str(LANG_WORMLET_CRASHED));
Eric Linenbergb0a39842002-08-26 03:32:39 +00001215 break;
1216 }
Björn Stenberga4c3b032002-09-24 18:04:15 +00001217 lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf);
1218 lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2);
Björn Stenberg50053d12002-09-09 23:30:16 +00001219
1220 if (!worms[i].alive){
1221 lcd_invertrect(FIELD_RECT_WIDTH + 2, y,
1222 LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
1223 }
1224 y += 19;
1225 }
Björn Stenberg505eca72002-09-18 14:08:05 +00001226 snprintf(buf , sizeof(buf), str(LANG_WORMLET_HIGHSCORE), highscore);
Björn Stenberg50053d12002-09-09 23:30:16 +00001227#ifndef DEBUG_WORMLET
Björn Stenberga4c3b032002-09-24 18:04:15 +00001228 lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001229#else
Björn Stenberga4c3b032002-09-24 18:04:15 +00001230 lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);
Björn Stenberg50053d12002-09-09 23:30:16 +00001231#endif
Eric Linenbergb0a39842002-08-26 03:32:39 +00001232}
1233
1234/**
1235 * Checks for collisions of the worm and its environment and
1236 * takes appropriate actions like growing the worm or killing it.
1237 * @return bool Returns true if the worm is dead. Returns
1238 * false if the worm is healthy, up and creeping.
1239 */
Björn Stenberg50053d12002-09-09 23:30:16 +00001240static bool process_collisions(struct worm *w)
Björn Stenberge67958b2002-08-26 09:21:59 +00001241{
Eric Linenbergb0a39842002-08-26 03:32:39 +00001242 int index = -1;
1243
Björn Stenberg50053d12002-09-09 23:30:16 +00001244 w->alive &= !field_collision(w);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001245
Björn Stenberg50053d12002-09-09 23:30:16 +00001246 if (w->alive) {
Eric Linenbergb0a39842002-08-26 03:32:39 +00001247
Björn Stenberge67958b2002-08-26 09:21:59 +00001248 /* check if food was eaten */
Björn Stenberg50053d12002-09-09 23:30:16 +00001249 index = food_collision(w->x[w->head], w->y[w->head]);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001250 if (index != -1){
Björn Stenberge67958b2002-08-26 09:21:59 +00001251 int i;
1252
Björn Stenberg50053d12002-09-09 23:30:16 +00001253 clear_food(index);
1254 make_food(index);
1255 draw_food(index);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001256
Eric Linenbergb0a39842002-08-26 03:32:39 +00001257 for (i = 0; i < ARGHS_PER_FOOD; i++) {
Björn Stenberg50053d12002-09-09 23:30:16 +00001258 argh_count++;
1259 if (argh_count > MAX_ARGH)
1260 argh_count = MAX_ARGH;
1261 make_argh(argh_count - 1);
1262 draw_argh(argh_count - 1);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001263 }
1264
Björn Stenberg50053d12002-09-09 23:30:16 +00001265 add_growing(w, WORM_PER_FOOD);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001266
Björn Stenberg50053d12002-09-09 23:30:16 +00001267 draw_worm(w);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001268 }
1269
Björn Stenberge67958b2002-08-26 09:21:59 +00001270 /* check if argh was eaten */
Eric Linenbergb0a39842002-08-26 03:32:39 +00001271 else {
Björn Stenberg50053d12002-09-09 23:30:16 +00001272 index = argh_collision(w->x[w->head], w->y[w->head]);
1273 if (index != -1) {
1274 w->alive = false;
1275 }
1276 else {
1277 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
1278 w->alive = false;
1279 }
1280 }
Eric Linenbergb0a39842002-08-26 03:32:39 +00001281 }
1282 }
Björn Stenberg50053d12002-09-09 23:30:16 +00001283 return !w->alive;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001284}
1285
1286/**
1287 * The main loop of the game.
1288 * @return bool Returns true if the game ended
1289 * with a dead worm. Returns false if the user
1290 * aborted the game manually.
1291 */
Björn Stenberge67958b2002-08-26 09:21:59 +00001292static bool run(void)
1293{
Eric Linenbergb0a39842002-08-26 03:32:39 +00001294 int button = 0;
1295 int wormDead = false;
1296
Björn Stenberg50053d12002-09-09 23:30:16 +00001297 /* ticks are counted to compensate speed variations */
1298 long cycle_start = 0, cycle_end = 0;
1299#ifdef DEBUG_WORMLET
1300 int ticks_to_max_cycle_reset = 20;
1301 long max_cycle = 0;
1302 char buf[20];
1303#endif
Eric Linenbergb0a39842002-08-26 03:32:39 +00001304
Björn Stenberg50053d12002-09-09 23:30:16 +00001305 /* initialize the board and so on */
1306 init_wormlet();
1307
1308 cycle_start = current_tick;
Björn Stenberge67958b2002-08-26 09:21:59 +00001309 /* change the direction of the worm */
Eric Linenbergb0a39842002-08-26 03:32:39 +00001310 while (button != BUTTON_OFF && ! wormDead)
1311 {
Björn Stenberg50053d12002-09-09 23:30:16 +00001312 int i;
1313 long cycle_duration ;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001314 switch (button) {
1315 case BUTTON_UP:
Björn Stenberg50053d12002-09-09 23:30:16 +00001316 if (players == 1 && !use_remote) {
1317 player1_dir = NORTH;
1318 }
Eric Linenbergb0a39842002-08-26 03:32:39 +00001319 break;
1320
1321 case BUTTON_DOWN:
Björn Stenberg50053d12002-09-09 23:30:16 +00001322 if (players == 1 && !use_remote) {
1323 player1_dir = SOUTH;
1324 }
Eric Linenbergb0a39842002-08-26 03:32:39 +00001325 break;
1326
1327 case BUTTON_LEFT:
Björn Stenberg50053d12002-09-09 23:30:16 +00001328 if (players != 1 || use_remote) {
1329 player1_dir = (player1_dir + 3) % 4;
1330 } else {
1331 player1_dir = WEST;
1332 }
Eric Linenbergb0a39842002-08-26 03:32:39 +00001333 break;
1334
1335 case BUTTON_RIGHT:
Björn Stenberg50053d12002-09-09 23:30:16 +00001336 if (players != 1 || use_remote) {
1337 player1_dir = (player1_dir + 1) % 4;
1338 } else {
1339 player1_dir = EAST;
1340 }
Eric Linenbergb0a39842002-08-26 03:32:39 +00001341 break;
1342
1343 case BUTTON_F2:
Björn Stenberg50053d12002-09-09 23:30:16 +00001344 player2_dir = (player2_dir + 3) % 4;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001345 break;
1346
1347 case BUTTON_F3:
Björn Stenberg50053d12002-09-09 23:30:16 +00001348 player2_dir = (player2_dir + 1) % 4;
1349 break;
1350
1351 case BUTTON_VOL_UP:
1352 player3_dir = (player3_dir + 1) % 4;
1353 break;
1354
1355 case BUTTON_VOL_DOWN:
1356 player3_dir = (player3_dir + 3) % 4;
1357 break;
1358
1359 case BUTTON_PLAY:
1360 do {
1361 button = button_get(true);
1362 } while (button != BUTTON_PLAY &&
1363 button != BUTTON_OFF &&
1364 button != BUTTON_ON);
Eric Linenbergb0a39842002-08-26 03:32:39 +00001365 break;
1366 }
1367
Björn Stenberg50053d12002-09-09 23:30:16 +00001368 for (i = 0; i < worm_count; i++) {
1369 worms[i].fetch_worm_direction(&worms[i]);
1370 }
1371
1372 wormDead = true;
1373 for (i = 0; i < worm_count; i++){
1374 struct worm *w = &worms[i];
1375 move_worm(w);
1376 wormDead &= process_collisions(w);
1377 draw_worm(w);
1378 }
1379 score_board();
Eric Linenbergb0a39842002-08-26 03:32:39 +00001380 lcd_update();
Björn Stenberg50053d12002-09-09 23:30:16 +00001381 if (button == BUTTON_ON) {
1382 wormDead = true;
1383 }
1384
1385 /* here the wormlet game cycle ends
1386 thus the current tick is stored
1387 as end time */
1388 cycle_end = current_tick;
1389
1390 /* The duration of the game cycle */
1391 cycle_duration = cycle_end - cycle_start;
1392 cycle_duration = MAX(0, cycle_duration);
1393 cycle_duration = MIN(SPEED -1, cycle_duration);
1394
1395
1396#ifdef DEBUG_WORMLET
1397 ticks_to_max_cycle_reset--;
1398 if (ticks_to_max_cycle_reset <= 0) {
1399 max_cycle = 0;
1400 }
1401
1402 if (max_cycle < cycle_duration) {
1403 max_cycle = cycle_duration;
1404 ticks_to_max_cycle_reset = 20;
1405 }
1406 snprintf(buf, sizeof buf, "ticks %d", max_cycle);
1407 set_debug_out(buf);
1408#endif
1409 /* adjust the number of ticks to wait for a button.
1410 This ensures that a complete game cycle including
1411 user input runs in constant time */
1412 button = button_get_w_tmo(SPEED - cycle_duration);
1413 cycle_start = current_tick;
1414
Eric Linenbergb0a39842002-08-26 03:32:39 +00001415 }
1416 return wormDead;
1417}
1418
Björn Stenberg50053d12002-09-09 23:30:16 +00001419#ifdef DEBUG_WORMLET
1420
1421/**
1422 * Just a test routine that checks that worm_food_collision works
1423 * in some typical situations.
1424 */
1425static void test_worm_food_collision(void) {
1426 int collision_count = 0;
1427 int i;
1428 lcd_clear_display();
1429 init_worm(&worms[0], 10, 10);
1430 add_growing(&worms[0], 10);
1431 set_worm_dir(&worms[0], EAST);
1432 for (i = 0; i < 10; i++) {
1433 move_worm(&worms[0]);
1434 draw_worm(&worms[0]);
1435 }
1436
1437 set_worm_dir(&worms[0], SOUTH);
1438 for (i = 0; i < 10; i++) {
1439 move_worm(&worms[0]);
1440 draw_worm(&worms[0]);
1441 }
1442
1443 foodx[0] = 15;
1444 foody[0] = 12;
1445 for (foody[0] = 20; foody[0] > 0; foody[0] --) {
1446 char buf[20];
1447 bool collision;
1448 draw_worm(&worms[0]);
1449 draw_food(0);
1450 collision = worm_food_collision(&worms[0], 0);
1451 if (collision) {
1452 collision_count++;
1453 }
1454 snprintf(buf, sizeof buf, "collisions: %d", collision_count);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001455 lcd_putsxy(0, LCD_HEIGHT -8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001456 lcd_update();
1457 }
1458 if (collision_count != FOOD_SIZE) {
1459 button_get(true);
1460 }
1461
1462
1463 foody[0] = 15;
1464 for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
1465 char buf[20];
1466 bool collision;
1467 draw_worm(&worms[0]);
1468 draw_food(0);
1469 collision = worm_food_collision(&worms[0], 0);
1470 if (collision) {
1471 collision_count ++;
1472 }
1473 snprintf(buf, sizeof buf, "collisions: %d", collision_count);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001474 lcd_putsxy(0, LCD_HEIGHT -8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001475 lcd_update();
1476 }
1477 if (collision_count != FOOD_SIZE * 2) {
1478 button_get(true);
1479 }
1480
1481}
1482
Eric Linenberg380ca882002-09-17 12:39:47 +00001483
1484static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){
1485 int x, y;
1486 bool retVal = false;
1487 for (x = rx; x < rx + rw; x++){
1488 for (y = ry; y < ry + rh; y++) {
1489 if (specific_worm_collision(w, x, y) != -1) {
1490 retVal = true;
1491 }
1492 }
1493 }
1494 return retVal;
1495}
1496
Björn Stenberg50053d12002-09-09 23:30:16 +00001497static void test_worm_argh_collision(void) {
1498 int i;
1499 int dir;
1500 int collision_count = 0;
1501 lcd_clear_display();
1502 init_worm(&worms[0], 10, 10);
1503 add_growing(&worms[0], 40);
1504 for (dir = 0; dir < 4; dir++) {
1505 set_worm_dir(&worms[0], (EAST + dir) % 4);
1506 for (i = 0; i < 10; i++) {
1507 move_worm(&worms[0]);
1508 draw_worm(&worms[0]);
1509 }
1510 }
1511
1512 arghx[0] = 12;
1513 for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghy[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 snprintf(buf, sizeof buf, "collisions: %d", collision_count);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001522 lcd_putsxy(0, LCD_HEIGHT -8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001523 lcd_update();
1524 }
1525 if (collision_count != ARGH_SIZE * 2) {
1526 button_get(true);
1527 }
1528
1529 arghy[0] = 12;
1530 for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghx[0]++){
1531 char buf[20];
1532 bool collision;
1533 draw_argh(0);
1534 collision = worm_argh_collision(&worms[0], 0);
1535 if (collision) {
1536 collision_count ++;
1537 }
1538 snprintf(buf, sizeof buf, "collisions: %d", collision_count);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001539 lcd_putsxy(0, LCD_HEIGHT -8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001540 lcd_update();
1541 }
1542 if (collision_count != ARGH_SIZE * 4) {
1543 button_get(true);
1544 }
1545}
1546
1547static int testline_in_rect(void) {
1548 int testfailed = -1;
1549
1550 int rx = 10;
1551 int ry = 15;
1552 int rw = 20;
1553 int rh = 25;
1554
1555 /* Test 1 */
1556 int x1 = 12;
1557 int y1 = 8;
1558 int x2 = 12;
1559 int y2 = 42;
1560
1561 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1562 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1563 lcd_drawrect(rx, ry, rw, rh);
1564 lcd_drawline(x1, y1, x2, y2);
1565 lcd_update();
Björn Stenberga4c3b032002-09-24 18:04:15 +00001566 lcd_putsxy(0, 0, "failed 1");
Björn Stenberg50053d12002-09-09 23:30:16 +00001567 button_get(true);
1568 testfailed = 1;
1569 }
1570
1571 /* test 2 */
1572 y2 = 20;
1573 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1574 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1575 lcd_drawrect(rx, ry, rw, rh);
1576 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001577 lcd_putsxy(0, 0, "failed 2");
Björn Stenberg50053d12002-09-09 23:30:16 +00001578 lcd_update();
1579 button_get(true);
1580 testfailed = 2;
1581 }
1582
1583 /* test 3 */
1584 y1 = 30;
1585 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1586 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1587 lcd_drawrect(rx, ry, rw, rh);
1588 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001589 lcd_putsxy(0, 0, "failed 3");
Björn Stenberg50053d12002-09-09 23:30:16 +00001590 lcd_update();
1591 button_get(true);
1592 testfailed = 3;
1593 }
1594
1595 /* test 4 */
1596 y2 = 45;
1597 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1598 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1599 lcd_drawrect(rx, ry, rw, rh);
1600 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001601 lcd_putsxy(0, 0, "failed 4");
Björn Stenberg50053d12002-09-09 23:30:16 +00001602 lcd_update();
1603 button_get(true);
1604 testfailed = 4;
1605 }
1606
1607 /* test 5 */
1608 y1 = 50;
1609 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1610 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1611 lcd_drawrect(rx, ry, rw, rh);
1612 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001613 lcd_putsxy(0, 0, "failed 5");
Björn Stenberg50053d12002-09-09 23:30:16 +00001614 lcd_update();
1615 button_get(true);
1616 testfailed = 5;
1617 }
1618
1619 /* test 6 */
1620 y1 = 5;
1621 y2 = 7;
1622 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1623 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1624 lcd_drawrect(rx, ry, rw, rh);
1625 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001626 lcd_putsxy(0, 0, "failed 6");
Björn Stenberg50053d12002-09-09 23:30:16 +00001627 lcd_update();
1628 button_get(true);
1629 testfailed = 6;
1630 }
1631
1632 /* test 7 */
1633 x1 = 8;
1634 y1 = 20;
1635 x2 = 35;
1636 y2 = 20;
1637 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1638 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1639 lcd_drawrect(rx, ry, rw, rh);
1640 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001641 lcd_putsxy(0, 0, "failed 7");
Björn Stenberg50053d12002-09-09 23:30:16 +00001642 lcd_update();
1643 button_get(true);
1644 testfailed = 7;
1645 }
1646
1647 /* test 8 */
1648 x2 = 12;
1649 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1650 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1651 lcd_drawrect(rx, ry, rw, rh);
1652 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001653 lcd_putsxy(0, 0, "failed 8");
Björn Stenberg50053d12002-09-09 23:30:16 +00001654 lcd_update();
1655 button_get(true);
1656 testfailed = 8;
1657 }
1658
1659 /* test 9 */
1660 x1 = 25;
1661 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1662 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1663 lcd_drawrect(rx, ry, rw, rh);
1664 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001665 lcd_putsxy(0, 0, "failed 9");
Björn Stenberg50053d12002-09-09 23:30:16 +00001666 lcd_update();
1667 button_get(true);
1668 testfailed = 9;
1669 }
1670
1671 /* test 10 */
1672 x2 = 37;
1673 if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
1674 !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1675 lcd_drawrect(rx, ry, rw, rh);
1676 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001677 lcd_putsxy(0, 0, "failed 10");
Björn Stenberg50053d12002-09-09 23:30:16 +00001678 lcd_update();
1679 button_get(true);
1680 testfailed = 10;
1681 }
1682
1683 /* test 11 */
1684 x1 = 42;
1685 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1686 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1687 lcd_drawrect(rx, ry, rw, rh);
1688 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001689 lcd_putsxy(0, 0, "failed 11");
Björn Stenberg50053d12002-09-09 23:30:16 +00001690 lcd_update();
1691 button_get(true);
1692 testfailed = 11;
1693 }
1694
1695 /* test 12 */
1696 x1 = 5;
1697 x2 = 7;
1698 if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
1699 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
1700 lcd_drawrect(rx, ry, rw, rh);
1701 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001702 lcd_putsxy(0, 0, "failed 12");
Björn Stenberg50053d12002-09-09 23:30:16 +00001703 lcd_update();
1704 button_get(true);
1705 testfailed = 12;
1706 }
1707
1708 /* test 13 */
1709 rx = 9;
1710 ry = 15;
1711 rw = FOOD_SIZE;
1712 rh = FOOD_SIZE;
1713
1714 x1 = 10;
1715 y1 = 10;
1716 x2 = 10;
1717 y2 = 20;
1718 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
Björn Stenberga4c3b032002-09-24 18:04:15 +00001719 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
Björn Stenberg50053d12002-09-09 23:30:16 +00001720 lcd_drawrect(rx, ry, rw, rh);
1721 lcd_drawline(x1, y1, x2, y2);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001722 lcd_putsxy(0, 0, "failed 13");
Björn Stenberg50053d12002-09-09 23:30:16 +00001723 lcd_update();
1724 button_get(true);
1725 testfailed = 13;
1726 }
1727
Eric Linenberg380ca882002-09-17 12:39:47 +00001728 /* test 14 */
1729 rx = 9;
1730 ry = 15;
1731 rw = 4;
1732 rh = 4;
1733
1734 x1 = 10;
1735 y1 = 10;
1736 x2 = 10;
1737 y2 = 19;
1738 if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
Björn Stenberga4c3b032002-09-24 18:04:15 +00001739 line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
Eric Linenberg380ca882002-09-17 12:39:47 +00001740 lcd_drawline(x1, y1, x2, y2);
1741 lcd_invertrect(rx, ry, rw, rh);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001742 lcd_putsxy(0, 0, "failed 14");
Eric Linenberg380ca882002-09-17 12:39:47 +00001743 lcd_update();
1744 button_get(true);
1745 testfailed = 14;
1746 }
1747
Björn Stenberg50053d12002-09-09 23:30:16 +00001748 lcd_clear_display();
1749
1750 return testfailed;
1751}
1752
1753/**
1754 * Just a test routine to test wether specific_worm_collision might work properly
1755 */
1756static int test_specific_worm_collision(void) {
1757 int collisions = 0;
1758 int dir;
1759 int x = 0;
1760 int y = 0;
1761 char buf[20];
1762 lcd_clear_display();
1763 init_worm(&worms[0], 10, 20);
1764 add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
1765
1766 for (dir = EAST; dir < EAST + 4; dir++) {
1767 int i;
1768 set_worm_dir(&worms[0], dir % 4);
1769 for (i = 0; i < 5; i++) {
1770 if (!(dir % 4 == NORTH && i == 9)) {
1771 move_worm(&worms[0]);
1772 draw_worm(&worms[0]);
1773 }
1774 }
1775 }
1776
1777 for (y = 15; y < 30; y ++){
1778 for (x = 5; x < 20; x++) {
1779 if (specific_worm_collision(&worms[0], x, y) != -1) {
1780 collisions ++;
1781 }
1782 lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
1783 snprintf(buf, sizeof buf, "collisions %d", collisions);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001784 lcd_putsxy(0, LCD_HEIGHT - 8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001785 lcd_update();
1786 }
1787 }
1788 if (collisions != 21) {
1789 button_get(true);
1790 }
1791 return collisions;
1792}
1793
1794static void test_make_argh(void){
1795 int dir;
1796 int seed = 0;
1797 int hit = 0;
1798 int failures = 0;
Eric Linenberg380ca882002-09-17 12:39:47 +00001799 int last_failures = 0;
1800 int i, worm_idx;
Björn Stenberg50053d12002-09-09 23:30:16 +00001801 lcd_clear_display();
Eric Linenberg380ca882002-09-17 12:39:47 +00001802 worm_count = 3;
1803
1804 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
1805 init_worm(&worms[worm_idx], 10 + worm_idx * 20, 20);
1806 add_growing(&worms[worm_idx], 40 - INITIAL_WORM_LENGTH);
1807 }
Björn Stenberg50053d12002-09-09 23:30:16 +00001808
1809 for (dir = EAST; dir < EAST + 4; dir++) {
Eric Linenberg380ca882002-09-17 12:39:47 +00001810 for (worm_idx = 0; worm_idx < worm_count; worm_idx++) {
1811 set_worm_dir(&worms[worm_idx], dir % 4);
Björn Stenberg50053d12002-09-09 23:30:16 +00001812 for (i = 0; i < 10; i++) {
1813 if (!(dir % 4 == NORTH && i == 9)) {
Eric Linenberg380ca882002-09-17 12:39:47 +00001814 move_worm(&worms[worm_idx]);
1815 draw_worm(&worms[worm_idx]);
1816 }
Björn Stenberg50053d12002-09-09 23:30:16 +00001817 }
1818 }
1819 }
1820
1821 lcd_update();
1822
Björn Stenberg50053d12002-09-09 23:30:16 +00001823 for (seed = 0; hit < 20; seed += 2) {
Björn Stenberg50053d12002-09-09 23:30:16 +00001824 char buf[20];
Eric Linenberg380ca882002-09-17 12:39:47 +00001825 int x, y;
1826 srand(seed);
1827 x = rand() % (FIELD_RECT_WIDTH - ARGH_SIZE);
1828 y = rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE);
Björn Stenberg50053d12002-09-09 23:30:16 +00001829
Eric Linenberg380ca882002-09-17 12:39:47 +00001830 for (worm_idx = 0; worm_idx < worm_count; worm_idx++){
1831 if (expensive_worm_in_rect(&worms[worm_idx], x, y, ARGH_SIZE, ARGH_SIZE)) {
1832 int tries = 0;
1833 srand(seed);
1834
1835 tries = make_argh(0);
1836 if ((x == arghx[0] && y == arghy[0]) || tries < 2) {
Björn Stenberg50053d12002-09-09 23:30:16 +00001837 failures ++;
1838 }
1839
Eric Linenberg380ca882002-09-17 12:39:47 +00001840 snprintf(buf, sizeof buf, "(%d;%d) fail%d try%d", x, y, failures, tries);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001841 lcd_putsxy(0, LCD_HEIGHT - 8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001842 lcd_update();
Eric Linenberg380ca882002-09-17 12:39:47 +00001843 lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE);
1844 lcd_update();
1845 draw_argh(0);
1846 lcd_update();
Björn Stenberg50053d12002-09-09 23:30:16 +00001847 lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE);
1848 lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE);
1849
Eric Linenberg380ca882002-09-17 12:39:47 +00001850 if (failures > last_failures) {
Björn Stenberg50053d12002-09-09 23:30:16 +00001851 button_get(true);
1852 }
Eric Linenberg380ca882002-09-17 12:39:47 +00001853 last_failures = failures;
Björn Stenberg50053d12002-09-09 23:30:16 +00001854 hit ++;
1855 }
1856 }
Eric Linenberg380ca882002-09-17 12:39:47 +00001857 }
Björn Stenberg50053d12002-09-09 23:30:16 +00001858}
1859
1860static void test_worm_argh_collision_in_moves(void) {
1861 int hit_count = 0;
1862 int i;
1863 lcd_clear_display();
1864 init_worm(&worms[0], 10, 20);
1865
1866 arghx[0] = 20;
1867 arghy[0] = 18;
1868 draw_argh(0);
1869
1870 set_worm_dir(&worms[0], EAST);
Eric Linenberg380ca882002-09-17 12:39:47 +00001871 for (i = 0; i < 20; i++) {
Björn Stenberg50053d12002-09-09 23:30:16 +00001872 char buf[20];
1873 move_worm(&worms[0]);
1874 draw_worm(&worms[0]);
1875 if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
1876 hit_count ++;
1877 }
1878 snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
Björn Stenberga4c3b032002-09-24 18:04:15 +00001879 lcd_putsxy(0, LCD_HEIGHT - 8, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001880 lcd_update();
Eric Linenberg380ca882002-09-17 12:39:47 +00001881 }
1882 if (hit_count != ARGH_SIZE + 5) {
Björn Stenberg50053d12002-09-09 23:30:16 +00001883 button_get(true);
1884 }
1885}
1886#endif /* DEBUG_WORMLET */
1887
Eric Linenberg380ca882002-09-17 12:39:47 +00001888extern bool use_old_rect;
1889
Eric Linenbergb0a39842002-08-26 03:32:39 +00001890/**
1891 * Main entry point from the menu to start the game control.
1892 */
Björn Stenbergb1b8bd42002-09-24 17:22:12 +00001893bool wormlet(void)
Eric Linenbergb0a39842002-08-26 03:32:39 +00001894{
Robert Hak8f11dc02002-10-29 10:45:29 +00001895 bool worm_dead = false;
Eric Linenbergb0a39842002-08-26 03:32:39 +00001896 int button;
Robert Hak8f11dc02002-10-29 10:45:29 +00001897
1898 lcd_setfont(FONT_SYSFIXED);
1899
Björn Stenberg50053d12002-09-09 23:30:16 +00001900#ifdef DEBUG_WORMLET
Eric Linenberg380ca882002-09-17 12:39:47 +00001901 testline_in_rect();
Björn Stenberg50053d12002-09-09 23:30:16 +00001902 test_worm_argh_collision_in_moves();
1903 test_make_argh();
Björn Stenberg50053d12002-09-09 23:30:16 +00001904 test_worm_food_collision();
1905 test_worm_argh_collision();
1906 test_specific_worm_collision();
1907#endif
Björn Stenberg50053d12002-09-09 23:30:16 +00001908 lcd_setmargins(0,0);
1909
1910 /* Setup screen */
1911 do {
1912 char buf[20];
1913 lcd_clear_display();
1914
1915 /* first line players */
Björn Stenberg505eca72002-09-18 14:08:05 +00001916 snprintf(buf, sizeof buf, str(LANG_WORMLET_PLAYERS), players);
Björn Stenberg50053d12002-09-09 23:30:16 +00001917 lcd_puts(0, 0, buf);
1918
1919 /* second line worms */
Björn Stenberg505eca72002-09-18 14:08:05 +00001920 snprintf(buf, sizeof buf, str(LANG_WORMLET_WORMS), worm_count);
Björn Stenberg50053d12002-09-09 23:30:16 +00001921 lcd_puts(0, 1, buf);
1922
1923 /* third line control */
1924 if (players > 1) {
1925 if (use_remote) {
Björn Stenberg505eca72002-09-18 14:08:05 +00001926 snprintf(buf, sizeof buf, str(LANG_WORMLET_REMOTE_CTRL));
Björn Stenberg50053d12002-09-09 23:30:16 +00001927 } else {
Björn Stenberg505eca72002-09-18 14:08:05 +00001928 snprintf(buf, sizeof buf, str(LANG_WORMLET_NO_REM_CTRL));
Björn Stenberg50053d12002-09-09 23:30:16 +00001929 }
1930 } else {
1931 if (players > 0) {
1932 if (use_remote) {
Björn Stenberg505eca72002-09-18 14:08:05 +00001933 snprintf(buf, sizeof buf, str(LANG_WORMLET_2_KEY_CTRL));
Björn Stenberg50053d12002-09-09 23:30:16 +00001934 } else {
Björn Stenberg505eca72002-09-18 14:08:05 +00001935 snprintf(buf, sizeof buf, str(LANG_WORMLET_4_KEY_CTRL));
Björn Stenberg50053d12002-09-09 23:30:16 +00001936 }
1937 } else {
Björn Stenberg505eca72002-09-18 14:08:05 +00001938 snprintf(buf, sizeof buf, str(LANG_WORMLET_NO_CONTROL));
Björn Stenberg50053d12002-09-09 23:30:16 +00001939 }
1940 }
1941 lcd_puts(0, 2, buf);
Björn Stenberg50053d12002-09-09 23:30:16 +00001942 lcd_update();
1943
1944 /* user selection */
1945 button = button_get(true);
1946 switch (button) {
1947 case BUTTON_UP:
1948 if (players < 3) {
1949 players ++;
1950 if (players > worm_count) {
1951 worm_count = players;
1952 }
1953 if (players > 2) {
1954 use_remote = true;
1955 }
1956 }
1957 break;
1958 case BUTTON_DOWN:
1959 if (players > 0) {
1960 players --;
1961 }
1962 break;
1963 case BUTTON_LEFT:
1964 if (worm_count > 1) {
1965 worm_count--;
1966 if (worm_count < players) {
1967 players = worm_count;
1968 }
1969 }
1970 break;
1971 case BUTTON_RIGHT:
1972 if (worm_count < MAX_WORMS) {
1973 worm_count ++;
1974 }
1975 break;
1976 case BUTTON_F1:
1977 use_remote = !use_remote;
1978 if (players > 2) {
1979 use_remote = true;
1980 }
1981 break;
Björn Stenbergb1b8bd42002-09-24 17:22:12 +00001982
1983 case SYS_USB_CONNECTED:
1984 usb_screen();
Robert Hak8f11dc02002-10-29 10:45:29 +00001985 lcd_setfont(FONT_UI);
Björn Stenbergb1b8bd42002-09-24 17:22:12 +00001986 return true;
Björn Stenberg50053d12002-09-09 23:30:16 +00001987 }
1988 } while (button != BUTTON_PLAY &&
1989 button != BUTTON_OFF && button != BUTTON_ON);
1990
1991 lcd_clear_display();
1992 /* end of setup */
1993
Eric Linenbergb0a39842002-08-26 03:32:39 +00001994 do {
1995
Björn Stenberge67958b2002-08-26 09:21:59 +00001996 /* button state will be overridden if
1997 the game quits with the death of the worm.
1998 Initializing button to BUTTON_OFF ensures
1999 that the user can hit BUTTON_OFF during the
2000 game to return to the menu.
2001 */
Eric Linenbergb0a39842002-08-26 03:32:39 +00002002 button = BUTTON_OFF;
2003
Björn Stenberge67958b2002-08-26 09:21:59 +00002004 /* start the game */
Robert Hak8f11dc02002-10-29 10:45:29 +00002005 worm_dead = run();
Eric Linenbergb0a39842002-08-26 03:32:39 +00002006
Björn Stenberge67958b2002-08-26 09:21:59 +00002007 /* if worm isn't dead the game was quit
2008 via BUTTON_OFF -> no need to wait for buttons. */
Robert Hak8f11dc02002-10-29 10:45:29 +00002009 if (worm_dead) {
Eric Linenbergb0a39842002-08-26 03:32:39 +00002010 do {
2011 button = button_get(true);
2012 }
Björn Stenberge67958b2002-08-26 09:21:59 +00002013 /* BUTTON_ON -> start new game */
2014 /* BUTTON_OFF -> back to game menu */
Eric Linenbergb0a39842002-08-26 03:32:39 +00002015 while (button != BUTTON_OFF && button != BUTTON_ON);
2016 }
2017 }
2018 while (button != BUTTON_OFF);
2019
Robert Hak8f11dc02002-10-29 10:45:29 +00002020 lcd_setfont(FONT_UI);
2021
Björn Stenbergb1b8bd42002-09-24 17:22:12 +00002022 return false;
Eric Linenbergb0a39842002-08-26 03:32:39 +00002023}
Robert Hak620bb0e2002-08-31 22:53:43 +00002024
Björn Stenberg50053d12002-09-09 23:30:16 +00002025
2026#endif /* USE_GAMES */
Robert Hak8f11dc02002-10-29 10:45:29 +00002027
2028
2029
2030
2031
2032
2033
2034
2035
2036