forked from len0rd/rockbox
		
	git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2250 a1c6a512-1295-4272-9138-f99709370657
		
			
				
	
	
		
			1997 lines
		
	
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1997 lines
		
	
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************
 | |
|  *             __________               __   ___.
 | |
|  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 | |
|  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 | |
|  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 | |
|  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 | |
|  *                     \/            \/     \/    \/            \/
 | |
|  * $Id$
 | |
|  *
 | |
|  * Copyright (C) 2002 Philipp Pertermann
 | |
|  *
 | |
|  * All files in this archive are subject to the GNU General Public License.
 | |
|  * See the file COPYING in the source tree root for full license agreement.
 | |
|  *
 | |
|  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | |
|  * KIND, either express or implied.
 | |
|  *
 | |
|  ****************************************************************************/
 | |
| 
 | |
| #include "config.h"
 | |
| #include "options.h"
 | |
| 
 | |
| #ifdef USE_GAMES
 | |
| 
 | |
| /* #define DEBUG_WORMLET */
 | |
| 
 | |
| #include <sprintf.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include "system.h"
 | |
| #include "lcd.h"
 | |
| #include "button.h"
 | |
| #include "kernel.h"
 | |
| #include "menu.h"
 | |
| #include "rtc.h"
 | |
| 
 | |
| /* I extracted string constants for output to 
 | |
|    simplify inernationalization - once we have
 | |
|    decided how to do that exactly. Even though
 | |
|    there are other strings in the code they don't
 | |
|    have to be internationalized because they are
 | |
|    only for debugging purposes and are only included
 | |
|    if DEBUG_WORMLET is defined anyway.*/
 | |
| 
 | |
| /* Length restriction for score board strings (LANG_SB_XXX):
 | |
|    LCD_PROPFONTS: max 43 pixel
 | |
|    fix font: max 7 characters*/  
 | |
| #define LANG_SB_LENGTH    "Len:%d"
 | |
| #define LANG_SB_GROWING   "Growing"
 | |
| #define LANG_SB_HUNGRY    "Hungry"
 | |
| #define LANG_SB_WORMED    "Wormed"
 | |
| #define LANG_SB_ARGH      "Argh"
 | |
| #define LANG_SB_CRASHED   "Crashed"
 | |
| #define LANG_SB_HIGHSCORE "Hs: %d"
 | |
| 
 | |
| /* Length restriction for config screen strings (LANG_CS_XXX):
 | |
|    The must fit on the entire screen - preferably
 | |
|    with the key names aligned right. */
 | |
| #define LANG_CS_PLAYERS     "%d Players    up/dn"
 | |
| #define LANG_CS_WORMS       "%d Worms        l/r"
 | |
| #define LANG_CS_REMOTE_CTRL "Remote control  F1"
 | |
| #define LANG_CS_NO_REM_CTRL "No rem. control F1"
 | |
| #define LANG_CS_2_KEY_CTRL  "2 key control   F1"
 | |
| #define LANG_CS_4_KEY_CTRL  "4 key control   F1"
 | |
| #define LANG_CS_NO_CONTROL  "Out of control"
 | |
| 
 | |
| /* size of the field the worm lives in */
 | |
| #define FIELD_RECT_X 1
 | |
| #define FIELD_RECT_Y 1
 | |
| #ifdef LCD_PROPFONTS
 | |
| #define FIELD_RECT_WIDTH  (LCD_WIDTH - 39)
 | |
| #else
 | |
| #define FIELD_RECT_WIDTH  (LCD_WIDTH - 46)
 | |
| #endif
 | |
| #define FIELD_RECT_HEIGHT (LCD_HEIGHT - 2)
 | |
| 
 | |
| /* size of the ring of the worm 
 | |
|    choos a value that is a power of 2 to help 
 | |
|    the compiler optimize modul operations*/
 | |
| #define MAX_WORM_SEGMENTS 64   
 | |
| 
 | |
| /* when the game starts */
 | |
| #define INITIAL_WORM_LENGTH 10 
 | |
| 
 | |
| /* num of pixel the worm grows per eaten food */
 | |
| #define WORM_PER_FOOD 7        
 | |
| 
 | |
| /* num of worms creeping in the FIELD */
 | |
| #define MAX_WORMS 3            
 | |
| 
 | |
| /* minimal distance between a worm and an argh 
 | |
|    when a new argh is made */
 | |
| #define MIN_ARGH_DIST 5
 | |
| 
 | |
| /**
 | |
|  * All the properties that a worm has.
 | |
|  */
 | |
| struct worm {
 | |
|     /* The worm is stored in a ring of xy coordinates */
 | |
|     char x[MAX_WORM_SEGMENTS];
 | |
|     char y[MAX_WORM_SEGMENTS];
 | |
| 
 | |
|     int head;      /* index of the head within the buffer */
 | |
|     int tail;      /* index of the tail within the buffer */
 | |
|     int growing;   /* number of cyles the worm still keeps growing */
 | |
|     bool alive;    /* the worms living state */
 | |
| 
 | |
|     /* direction vector in which the worm moves */
 | |
|     int dirx; /* only values -1 0 1 allowed */
 | |
|     int diry; /* only values -1 0 1 allowed */
 | |
| 
 | |
|     /* this method is used to fetch the direction the user
 | |
|        has selected. It can be one of the values
 | |
|        human_player1, human_player2, remote_player, virtual_player. 
 | |
|        All these values are fuctions, that can change the direction 
 | |
|        of the worm */
 | |
|     void (*fetch_worm_direction)(struct worm *w);
 | |
| } worms[MAX_WORMS];
 | |
| 
 | |
| /* stores the highscore - besides it was scored by a virtual player */
 | |
| static int highscore;
 | |
| 
 | |
| #define MAX_FOOD  5 /* maximal number of food items */
 | |
| #define FOOD_SIZE 3 /* the width and height of a food */
 | |
| 
 | |
| /* The arrays store the food coordinates  */
 | |
| static char foodx[MAX_FOOD];
 | |
| static char foody[MAX_FOOD];
 | |
| 
 | |
| #define MAX_ARGH  100    /* maximal number of argh items */
 | |
| #define ARGH_SIZE 4      /* the width and height of a argh */
 | |
| #define ARGHS_PER_FOOD 2 /* number of arghs produced per eaten food */
 | |
| 
 | |
| /* The arrays store the argh coordinates */
 | |
| static char arghx[MAX_ARGH];
 | |
| static char arghy[MAX_ARGH];
 | |
| 
 | |
| /* the number of arghs that are currently in use */ 
 | |
| static int argh_count;
 | |
| 
 | |
| #ifdef DEBUG_WORMLET
 | |
| /* just a buffer used for debug output */
 | |
| static char debugout[15];
 | |
| #endif
 | |
| 
 | |
| /* the number of ticks each game cycle should take */ 
 | |
| #define SPEED 14
 | |
| 
 | |
| /* the number of active worms (dead or alive) */
 | |
| static int worm_count = MAX_WORMS;
 | |
| 
 | |
| /* in multiplayer mode: en- / disables the remote worm control
 | |
|    in singleplayer mode: toggles 4 / 2 button worm control */
 | |
| static bool use_remote = false;
 | |
| 
 | |
| /* return values of check_collision */
 | |
| #define COLLISION_NONE 0
 | |
| #define COLLISION_WORM 1
 | |
| #define COLLISION_FOOD 2
 | |
| #define COLLISION_ARGH 3
 | |
| #define COLLISION_FIELD 4
 | |
| 
 | |
| /* constants for use as directions.
 | |
|    Note that the values are ordered clockwise.
 | |
|    Thus increasing / decreasing the values
 | |
|    is equivalent to right / left turns. */
 | |
| #define WEST  0
 | |
| #define NORTH 1
 | |
| #define EAST  2
 | |
| #define SOUTH 3
 | |
| 
 | |
| /* direction of human player 1 */
 | |
| static int player1_dir = EAST; 
 | |
| /* direction of human player 2 */
 | |
| static int player2_dir = EAST; 
 | |
| /* direction of human player 3 */
 | |
| static int player3_dir = EAST;
 | |
| 
 | |
| /* the number of (human) players that currently
 | |
|    control a worm */
 | |
| static int players = 1;
 | |
| 
 | |
| #ifdef DEBUG_WORMLET
 | |
| static void set_debug_out(char *str){
 | |
|     strcpy(debugout, str);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /**
 | |
|  * Returns the direction id in which the worm
 | |
|  * currently is creeping.
 | |
|  * @param struct worm *w The worm that is to be investigated. 
 | |
|  *        w Must not be null.
 | |
|  * @return int A value 0 <= value < 4
 | |
|  *         Note the predefined constants NORTH, SOUTH, EAST, WEST
 | |
|  */
 | |
| static int get_worm_dir(struct worm *w) {
 | |
|     int retVal ;
 | |
|     if (w->dirx == 0) {
 | |
|         if (w->diry == 1) {
 | |
|             retVal = SOUTH;
 | |
|         } else {
 | |
|             retVal = NORTH;
 | |
|         }
 | |
|     } else {
 | |
|         if (w->dirx == 1) {
 | |
|             retVal = EAST;
 | |
|         } else {
 | |
|             retVal = WEST;
 | |
|         }
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the direction of the specified worm with a direction id.
 | |
|  * Increasing the value by 1 means to turn the worm direction
 | |
|  * to right by 90 degree.
 | |
|  * @param struct worm *w The worm that is to be altered. w Must not be null.
 | |
|  * @param int dir The new direction in which the worm is to creep.
 | |
|  *        dir must be  0 <= dir < 4. Use predefined constants 
 | |
|  *        NORTH, SOUTH, EAST, WEST
 | |
|  */
 | |
| static void set_worm_dir(struct worm *w, int dir) {
 | |
|     switch (dir) {
 | |
|         case WEST:
 | |
|             w->dirx = -1;
 | |
|             w->diry = 0;
 | |
|             break;
 | |
|          case NORTH:
 | |
|             w->dirx = 0;
 | |
|             w->diry = - 1;
 | |
|             break;
 | |
|         case EAST:
 | |
|             w->dirx = 1;
 | |
|             w->diry = 0;
 | |
|             break;
 | |
|         case SOUTH:
 | |
|             w->dirx = 0;
 | |
|             w->diry = 1;
 | |
|             break;
 | |
|    }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the current length of the worm array. This
 | |
|  * is also a value for the number of bends that are in the worm.
 | |
|  * @return int a positive value with 0 <= value < MAX_WORM_SEGMENTS
 | |
|  */
 | |
| static int get_worm_array_length(struct worm *w) {
 | |
|     /* initial simple calculation will be overwritten if wrong. */
 | |
|     int retVal = w->head - w->tail;
 | |
| 
 | |
|     /* if the worm 'crosses' the boundaries of the ringbuffer */
 | |
|     if (retVal < 0) {
 | |
|         retVal = w->head + MAX_WORM_SEGMENTS - w->tail;
 | |
|     }
 | |
| 
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the score the specified worm. The score is the length
 | |
|  * of the worm.
 | |
|  * @param struct worm *w The worm that is to be investigated.
 | |
|  *        w must not be null.
 | |
|  * @return int The length of the worm (>= 0).
 | |
|  */
 | |
| static int get_score(struct worm *w) {
 | |
|     int retval = 0;
 | |
|     int length = get_worm_array_length(w);
 | |
|     int i;
 | |
|     for (i = 0; i < length; i++) {
 | |
| 
 | |
|         /* The iteration iterates the length of the worm.
 | |
|            Here's the conversion to the true indices within the worm arrays. */
 | |
|         int linestart = (w->tail + i  ) % MAX_WORM_SEGMENTS;
 | |
|         int lineend   = (linestart + 1) % MAX_WORM_SEGMENTS;
 | |
|         int startx = w->x[linestart];
 | |
|         int starty = w->y[linestart];
 | |
|         int endx = w->x[lineend];
 | |
|         int endy = w->y[lineend];
 | |
| 
 | |
|         int minimum, maximum;
 | |
| 
 | |
|         if (startx == endx) {
 | |
|             minimum = MIN(starty, endy);
 | |
|             maximum = MAX(starty, endy);
 | |
|         } else {
 | |
|             minimum = MIN(startx, endx);
 | |
|             maximum = MAX(startx, endx);
 | |
|         }
 | |
|             retval += abs(maximum - minimum);
 | |
|     }
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Determines wether the line specified by startx, starty, endx, endy intersects
 | |
|  * the rectangle specified by x, y, width, height. Note that the line must be exactly
 | |
|  * horizontal or vertical (startx == endx or starty == endy). 
 | |
|  * @param int startx The x coordinate of the start point of the line.
 | |
|  * @param int starty The y coordinate of the start point of the line.
 | |
|  * @param int endx The x coordinate of the end point of the line.
 | |
|  * @param int endy The y coordinate of the end point of the line.
 | |
|  * @param int x The x coordinate of the top left corner of the rectangle.
 | |
|  * @param int y The y coordinate of the top left corner of the rectangle.
 | |
|  * @param int width The width of the rectangle.
 | |
|  * @param int height The height of the rectangle.
 | |
|  * @return bool Returns true if the specified line intersects with the recangle.
 | |
|  */
 | |
| static bool line_in_rect(int startx, int starty, int endx, int endy, int x, int y, int width, int height) {
 | |
|     bool retval = false;
 | |
|     int simple, simplemin, simplemax;
 | |
|     int compa, compb, compmin, compmax;
 | |
|     int temp;
 | |
|     if (startx == endx) {
 | |
|         simple = startx;
 | |
|         simplemin = x;
 | |
|         simplemax = x + width;
 | |
| 
 | |
|         compa = starty;
 | |
|         compb = endy;
 | |
|         compmin = y;
 | |
|         compmax = y + height;
 | |
|     } else {
 | |
|         simple = starty;
 | |
|         simplemin = y;
 | |
|         simplemax = y + height;
 | |
| 
 | |
|         compa = startx;
 | |
|         compb = endx;
 | |
|         compmin = x;
 | |
|         compmax = x + width;
 | |
|     };
 | |
| 
 | |
|     temp = compa;
 | |
|     compa = MIN(compa, compb);
 | |
|     compb = MAX(temp, compb);
 | |
| 
 | |
|     if (simplemin <= simple && simple < simplemax) {
 | |
|         if ((compmin <= compa && compa < compmax) ||
 | |
|             (compmin <= compb && compb < compmax) ||
 | |
|             (compa <= compmin && compb > compmax)) {
 | |
|             retval = true;
 | |
|         }
 | |
|     }
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| /** 
 | |
|  * Tests wether the specified worm intersects with the rect.
 | |
|  * @param struct worm *w The worm to be investigated
 | |
|  * @param int x The x coordinate of the top left corner of the rect
 | |
|  * @param int y The y coordinate of the top left corner of the rect
 | |
|  * @param int widht The width of the rect
 | |
|  * @param int height The height of the rect
 | |
|  * @return bool Returns true if the worm intersects with the rect
 | |
|  */
 | |
| static bool worm_in_rect(struct worm *w, int x, int y, int width, int height) {
 | |
|     bool retval = false;
 | |
| 
 | |
| 
 | |
|     /* get_worm_array_length is expensive -> buffer the value */
 | |
|     int wormLength = get_worm_array_length(w);
 | |
|     int i;
 | |
| 
 | |
|     /* test each entry that is part of the worm */
 | |
|     for (i = 0; i < wormLength && retval == false; i++) {
 | |
| 
 | |
|         /* The iteration iterates the length of the worm.
 | |
|            Here's the conversion to the true indices within the worm arrays. */
 | |
|         int linestart = (w->tail + i  ) % MAX_WORM_SEGMENTS;
 | |
|         int lineend   = (linestart + 1) % MAX_WORM_SEGMENTS;
 | |
|         int startx = w->x[linestart];
 | |
|         int starty = w->y[linestart];
 | |
|         int endx = w->x[lineend];
 | |
|         int endy = w->y[lineend];
 | |
| 
 | |
|         retval = line_in_rect(startx, starty, endx, endy, x, y, width, height);
 | |
|     }
 | |
| 
 | |
|     return retval;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks wether a specific food in the food arrays is at the
 | |
|  * specified coordinates.
 | |
|  * @param int foodIndex The index of the food in the food arrays
 | |
|  * @param int x the x coordinate.
 | |
|  * @param int y the y coordinate.
 | |
|  * @return Returns true if the coordinate hits the food specified by
 | |
|  * foodIndex.
 | |
|  */
 | |
| static bool specific_food_collision(int foodIndex, int x, int y) {
 | |
|     bool retVal = false;
 | |
|     if (x >= foodx[foodIndex]             &&
 | |
|         x <  foodx[foodIndex] + FOOD_SIZE &&
 | |
|         y >= foody[foodIndex]             &&
 | |
|         y <  foody[foodIndex] + FOOD_SIZE) {
 | |
| 
 | |
|         retVal = true;
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the index of the food that is at the
 | |
|  * given coordinates. If no food is at the coordinates
 | |
|  * -1 is returned.
 | |
|  * @return int  -1 <= value < MAX_FOOD
 | |
|  */
 | |
| static int food_collision(int x, int y) {
 | |
|     int i = 0;
 | |
|     int retVal = -1;
 | |
|     for (i = 0; i < MAX_FOOD; i++) {
 | |
|         if (specific_food_collision(i, x, y)) {
 | |
|             retVal = i;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks wether a specific argh in the argh arrays is at the
 | |
|  * specified coordinates.
 | |
|  * @param int arghIndex The index of the argh in the argh arrays
 | |
|  * @param int x the x coordinate.
 | |
|  * @param int y the y coordinate.
 | |
|  * @return Returns true if the coordinate hits the argh specified by
 | |
|  * arghIndex.
 | |
|  */
 | |
| static bool specific_argh_collision(int arghIndex, int x, int y) {
 | |
| 
 | |
|     if ( x >= arghx[arghIndex] &&
 | |
|          y >= arghy[arghIndex] &&
 | |
|          x <  arghx[arghIndex] + ARGH_SIZE &&
 | |
|          y <  arghy[arghIndex] + ARGH_SIZE )
 | |
|     {
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the index of the argh that is at the
 | |
|  * given coordinates. If no argh is at the coordinates
 | |
|  * -1 is returned.
 | |
|  * @param int x The x coordinate.
 | |
|  * @param int y The y coordinate.
 | |
|  * @return int  -1 <= value < argh_count <= MAX_ARGH
 | |
|  */
 | |
| static int argh_collision(int x, int y) {
 | |
|     int i = 0;
 | |
|     int retVal = -1;
 | |
| 
 | |
|     /* search for the argh that has the specified coords */
 | |
|     for (i = 0; i < argh_count; i++) {
 | |
|         if (specific_argh_collision(i, x, y)) {
 | |
|             retVal = i;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks wether the worm collides with the food at the specfied food-arrays.
 | |
|  * @param int foodIndex The index of the food in the arrays. Ensure the value is
 | |
|  * 0 <= foodIndex <= MAX_FOOD
 | |
|  * @return Returns true if the worm collides with the specified food.
 | |
|  */
 | |
| static bool worm_food_collision(struct worm *w, int foodIndex)
 | |
| {
 | |
|     bool retVal = false;
 | |
| 
 | |
|     retVal = worm_in_rect(w, foodx[foodIndex], foody[foodIndex], 
 | |
|                           FOOD_SIZE, FOOD_SIZE);
 | |
| 
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns true if the worm hits the argh within the next moves (unless
 | |
|  * the worm changes it's direction).
 | |
|  * @param struct worm *w - The worm to investigate
 | |
|  * @param int argh_idx - The index of the argh 
 | |
|  * @param int moves - The number of moves that are considered.
 | |
|  * @return Returns false if the specified argh is not hit within the next
 | |
|  *         moves.
 | |
|  */
 | |
| static bool worm_argh_collision_in_moves(struct worm *w, int argh_idx, int moves){
 | |
|     bool retVal = false;
 | |
|     int x1, y1, x2, y2;
 | |
|     x1 = w->x[w->head];
 | |
|     y1 = w->y[w->head];
 | |
| 
 | |
|     x2 = w->x[w->head] + moves * w->dirx;
 | |
|     y2 = w->y[w->head] + moves * w->diry;
 | |
| 
 | |
|     retVal = line_in_rect(x1, y1, x2, y2, arghx[argh_idx], arghy[argh_idx], 
 | |
|                          ARGH_SIZE, ARGH_SIZE);
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks wether the worm collides with the argh at the specfied argh-arrays.
 | |
|  * @param int arghIndex The index of the argh in the arrays.
 | |
|  * Ensure the value is 0 <= arghIndex < argh_count <= MAX_ARGH
 | |
|  * @return Returns true if the worm collides with the specified argh.
 | |
|  */
 | |
| static bool worm_argh_collision(struct worm *w, int arghIndex)
 | |
| {
 | |
|     bool retVal = false;
 | |
| 
 | |
|     retVal = worm_in_rect(w, arghx[arghIndex], arghy[arghIndex], 
 | |
|                           ARGH_SIZE, ARGH_SIZE);
 | |
| 
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Find new coordinates for the food stored in foodx[index], foody[index]
 | |
|  * that don't collide with any other food or argh
 | |
|  * @param int index 
 | |
|  * Ensure that 0 <= index < MAX_FOOD.
 | |
|  */
 | |
| static int make_food(int index) {
 | |
| 
 | |
|     int x = 0;
 | |
|     int y = 0;
 | |
|     bool collisionDetected = false;
 | |
|     int tries = 0;
 | |
|     int i;
 | |
| 
 | |
|     do {
 | |
|         /* make coordinates for a new food so that
 | |
|            the entire food lies within the FIELD */
 | |
|         x = rand() % (FIELD_RECT_WIDTH  - FOOD_SIZE);
 | |
|         y = rand() % (FIELD_RECT_HEIGHT - FOOD_SIZE);
 | |
|         tries ++;
 | |
| 
 | |
|         /* Ensure that the new food doesn't collide with any
 | |
|            existing foods or arghs.
 | |
|            If one or more corners of the new food hit any existing
 | |
|            argh or food a collision is detected.
 | |
|         */
 | |
|         collisionDetected = 
 | |
|             food_collision(x                , y                ) >= 0 ||
 | |
|             food_collision(x                , y + FOOD_SIZE - 1) >= 0 ||
 | |
|             food_collision(x + FOOD_SIZE - 1, y                ) >= 0 ||
 | |
|             food_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0 ||
 | |
|             argh_collision(x                , y                ) >= 0 || 
 | |
|             argh_collision(x                , y + FOOD_SIZE - 1) >= 0 ||
 | |
|             argh_collision(x + FOOD_SIZE - 1, y                ) >= 0 ||
 | |
|             argh_collision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0;
 | |
| 
 | |
|         /* use coordinates for further testing */
 | |
|         foodx[index] = x;
 | |
|         foody[index] = y;
 | |
| 
 | |
|         /* now test wether we accidently hit the worm with food ;) */
 | |
|         i = 0;
 | |
|         for (i = 0; i < worm_count && !collisionDetected; i++) {
 | |
|             collisionDetected |= worm_food_collision(&worms[i], index);
 | |
|         }
 | |
|     }
 | |
|     while (collisionDetected);
 | |
|     return tries;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Clears a food from the lcd buffer.
 | |
|  * @param int index The index of the food arrays under which
 | |
|  * the coordinates of the desired food can be found. Ensure 
 | |
|  * that the value is 0 <= index <= MAX_FOOD.
 | |
|  */
 | |
| static void clear_food(int index)
 | |
| {
 | |
|     /* remove the old food from the screen */
 | |
|     lcd_clearrect(foodx[index] + FIELD_RECT_X, 
 | |
|                   foody[index] + FIELD_RECT_Y, 
 | |
|                   FOOD_SIZE, FOOD_SIZE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Draws a food in the lcd buffer.
 | |
|  * @param int index The index of the food arrays under which
 | |
|  * the coordinates of the desired food can be found. Ensure 
 | |
|  * that the value is 0 <= index <= MAX_FOOD.
 | |
|  */
 | |
| static void draw_food(int index)
 | |
| {
 | |
|     /* draw the food object */
 | |
|     lcd_fillrect(foodx[index] + FIELD_RECT_X, 
 | |
|                  foody[index] + FIELD_RECT_Y, 
 | |
|                  FOOD_SIZE, FOOD_SIZE);
 | |
|     lcd_clearrect(foodx[index] + FIELD_RECT_X + 1, 
 | |
|                   foody[index] + FIELD_RECT_Y + 1, 
 | |
|                   FOOD_SIZE - 2, FOOD_SIZE - 2);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Find new coordinates for the argh stored in arghx[index], arghy[index]
 | |
|  * that don't collide with any other food or argh.
 | |
|  * @param int index 
 | |
|  * Ensure that 0 <= index < argh_count < MAX_ARGH.
 | |
|  */
 | |
| static int make_argh(int index)
 | |
| {
 | |
|     int x = -1; 
 | |
|     int y = -1; 
 | |
|     bool collisionDetected = false;
 | |
|     int tries = 0;
 | |
|     int i;
 | |
| 
 | |
|     do {
 | |
|         /* make coordinates for a new argh so that
 | |
|            the entire food lies within the FIELD */
 | |
|         x = rand() % (FIELD_RECT_WIDTH  - ARGH_SIZE);
 | |
|         y = rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE);
 | |
|         tries ++;
 | |
| 
 | |
|         /* Ensure that the new argh doesn't intersect with any
 | |
|            existing foods or arghs.
 | |
|            If one or more corners of the new argh hit any existing
 | |
|            argh or food an intersection is detected.
 | |
|         */
 | |
|         collisionDetected = 
 | |
|             food_collision(x                , y                ) >= 0 || 
 | |
|             food_collision(x                , y + ARGH_SIZE - 1) >= 0 ||
 | |
|             food_collision(x + ARGH_SIZE - 1, y                ) >= 0 ||
 | |
|             food_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0 ||
 | |
|             argh_collision(x                , y                ) >= 0 || 
 | |
|             argh_collision(x                , y + ARGH_SIZE - 1) >= 0 ||
 | |
|             argh_collision(x + ARGH_SIZE - 1, y                ) >= 0 ||
 | |
|             argh_collision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0;
 | |
| 
 | |
|         /* use the candidate coordinates to make a real argh */
 | |
|         arghx[index] = x;
 | |
|         arghy[index] = y;
 | |
| 
 | |
|         /* now test wether we accidently hit the worm with argh ;) */
 | |
|         for (i = 0; i < worm_count && !collisionDetected; i++) {
 | |
|             collisionDetected |= worm_argh_collision(&worms[i], index);
 | |
|             collisionDetected |= worm_argh_collision_in_moves(&worms[i], index, 
 | |
|                                                               MIN_ARGH_DIST);
 | |
|         }
 | |
|     }
 | |
|     while (collisionDetected);
 | |
|     return tries;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Draws an argh in the lcd buffer.
 | |
|  * @param int index The index of the argh arrays under which
 | |
|  * the coordinates of the desired argh can be found. Ensure 
 | |
|  * that the value is 0 <= index < argh_count <= MAX_ARGH.
 | |
|  */
 | |
| static void draw_argh(int index)
 | |
| {
 | |
|     /* draw the new argh */
 | |
|     lcd_fillrect(arghx[index] + FIELD_RECT_X, 
 | |
|                  arghy[index] + FIELD_RECT_Y, 
 | |
|                  ARGH_SIZE, ARGH_SIZE);
 | |
| }
 | |
| 
 | |
| static void virtual_player(struct worm *w);
 | |
| /**
 | |
|  * Initialzes the specified worm with INITIAL_WORM_LENGTH
 | |
|  * and the tail at the specified position. The worm will
 | |
|  * be initialized alive and creeping EAST.
 | |
|  * @param struct worm *w The worm that is to be initialized
 | |
|  * @param int x The x coordinate at which the tail of the worm starts.
 | |
|  *        x must be 0 <= x < FIELD_RECT_WIDTH.
 | |
|  * @param int y The y coordinate at which the tail of the worm starts
 | |
|  *        y must be 0 <= y < FIELD_RECT_WIDTH.
 | |
|  */
 | |
| static void init_worm(struct worm *w, int x, int y){
 | |
|         /* initialize the worm size */
 | |
|         w->head = 1;
 | |
|         w->tail = 0;
 | |
| 
 | |
|         w->x[w->head] = x + 1;
 | |
|         w->y[w->head] = y;
 | |
| 
 | |
|         w->x[w->tail] = x;
 | |
|         w->y[w->tail] = y;
 | |
| 
 | |
|         /* set the initial direction the worm creeps to */
 | |
|         w->dirx = 1;
 | |
|         w->diry = 0;
 | |
| 
 | |
|         w->growing = INITIAL_WORM_LENGTH - 1;
 | |
|         w->alive = true;
 | |
|         w->fetch_worm_direction = virtual_player;
 | |
| }
 | |
| 
 | |
| /** 
 | |
|  * Writes the direction that was stored for
 | |
|  * human player 1 into the specified worm. This function
 | |
|  * may be used to be stored in worm.fetch_worm_direction. 
 | |
|  * The value of 
 | |
|  * the direction is read from player1_dir.
 | |
|  * @param struct worm *w - The worm of which the direction 
 | |
|  * is altered.
 | |
|  */
 | |
| static void human_player1(struct worm *w) {
 | |
|     set_worm_dir(w, player1_dir);
 | |
| }
 | |
| 
 | |
| /** 
 | |
|  * Writes the direction that was stored for
 | |
|  * human player 2 into the specified worm. This function
 | |
|  * may be used to be stored in worm.fetch_worm_direction. 
 | |
|  * The value of 
 | |
|  * the direction is read from player2_dir.
 | |
|  * @param struct worm *w - The worm of which the direction 
 | |
|  * is altered.
 | |
|  */
 | |
| static void human_player2(struct worm *w) {
 | |
|     set_worm_dir(w, player2_dir);
 | |
| }
 | |
| 
 | |
| /** 
 | |
|  * Writes the direction that was stored for
 | |
|  * human player using a remote control 
 | |
|  * into the specified worm. This function
 | |
|  * may be used to be stored in worm.fetch_worm_direction. 
 | |
|  * The value of 
 | |
|  * the direction is read from player3_dir.
 | |
|  * @param struct worm *w - The worm of which the direction 
 | |
|  * is altered.
 | |
|  */
 | |
| static void remote_player(struct worm *w) {
 | |
|     set_worm_dir(w, player3_dir);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Initializes the worm-, food- and argh-arrays, draws a frame,
 | |
|  * makes some food and argh and display all that stuff.
 | |
|  */
 | |
| static void init_wormlet(void)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i< worm_count; i++) {
 | |
|             /* Initialize all the worm coordinates to center. */
 | |
|             int x = (int)(FIELD_RECT_WIDTH / 2);
 | |
|             int y = (int)((FIELD_RECT_HEIGHT - 20)/ 2) + i * 10;
 | |
| 
 | |
|         init_worm(&worms[i], x, y);
 | |
|     }
 | |
| 
 | |
|     player1_dir = EAST;
 | |
|     player2_dir = EAST;
 | |
|     player3_dir = EAST;
 | |
| 
 | |
|     if (players > 0) {
 | |
|         worms[0].fetch_worm_direction = human_player1;
 | |
|     }
 | |
| 
 | |
|     if (players > 1) {
 | |
|         if (use_remote) {
 | |
|             worms[1].fetch_worm_direction = remote_player;
 | |
|         } else {
 | |
|             worms[1].fetch_worm_direction = human_player2;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (players > 2) {
 | |
|         worms[2].fetch_worm_direction = human_player2;
 | |
|     }
 | |
| 
 | |
|     /* Needed when the game is restarted using BUTTON_ON */
 | |
|     lcd_clear_display();
 | |
| 
 | |
|     /* make and display some food and argh */
 | |
|     argh_count = MAX_FOOD;
 | |
|     for (i = 0; i < MAX_FOOD; i++) {
 | |
|         make_food(i);
 | |
|         draw_food(i);
 | |
|         make_argh(i);
 | |
|         draw_argh(i);
 | |
|     }
 | |
| 
 | |
|     /* draw the game field */
 | |
|     lcd_invertrect(0, 0, FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
 | |
|     lcd_invertrect(1, 1, FIELD_RECT_WIDTH, FIELD_RECT_HEIGHT);
 | |
| 
 | |
|     /* make everything visible */
 | |
|     lcd_update();
 | |
| }
 | |
| 
 | |
| 
 | |
| /** 
 | |
|  * Move the worm one step further if it is alive.
 | |
|  * The direction in which the worm moves is taken from dirx and diry. 
 | |
|  * move_worm decreases growing if > 0. While the worm is growing the tail
 | |
|  * is left untouched.
 | |
|  * @param struct worm *w The worm to move. w must not be NULL.
 | |
|  */
 | |
| static void move_worm(struct worm *w)
 | |
| {
 | |
|     if (w->alive) {
 | |
|         /* determine the head point and its precessor */
 | |
|         int headx = w->x[w->head];
 | |
|         int heady = w->y[w->head];
 | |
|         int prehead = (w->head + MAX_WORM_SEGMENTS - 1) % MAX_WORM_SEGMENTS;
 | |
|         int preheadx = w->x[prehead];
 | |
|         int preheady = w->y[prehead];
 | |
| 
 | |
|         /* determine the old direction */
 | |
|         int olddirx;
 | |
|         int olddiry;
 | |
|         if (headx == preheadx) {
 | |
|             olddirx = 0;
 | |
|             olddiry = (heady > preheady) ? 1 : -1;
 | |
|         } else {
 | |
|             olddiry = 0;
 | |
|             olddirx = (headx > preheadx) ? 1 : -1;
 | |
|         }
 | |
| 
 | |
|         /* olddir == dir?
 | |
|            a change of direction means a new segment 
 | |
|            has been opened */
 | |
|         if (olddirx != w->dirx ||
 | |
|             olddiry != w->diry) {
 | |
|             w->head = (w->head + 1) % MAX_WORM_SEGMENTS;
 | |
|         }
 | |
| 
 | |
|         /* new head position */
 | |
|         w->x[w->head] = headx + w->dirx;
 | |
|         w->y[w->head] = heady + w->diry;
 | |
| 
 | |
| 
 | |
|         /* while the worm is growing no tail procession is necessary */
 | |
|         if (w->growing > 0) {
 | |
|     /* update the worms grow state */
 | |
|             w->growing--;
 | |
|         }
 | |
|         
 | |
|         /* if the worm isn't growing the tail has to be dragged */
 | |
|         else {
 | |
|             /* index of the end of the tail segment */ 
 | |
|             int tail_segment_end = (w->tail + 1) % MAX_WORM_SEGMENTS;
 | |
| 
 | |
|             /* drag the end of the tail */
 | |
|             /* only one coordinate has to be altered. Here it is 
 | |
|                determined which one */ 
 | |
|             int dir = 0; /* specifies wether the coord has to be in- or decreased */
 | |
|             if (w->x[w->tail] == w->x[tail_segment_end]) {
 | |
|                 dir = (w->y[w->tail] - w->y[tail_segment_end] < 0) ? 1 : -1;
 | |
|                 w->y[w->tail] += dir;
 | |
|             } else {
 | |
|                 dir = (w->x[w->tail] - w->x[tail_segment_end] < 0) ? 1 : -1;
 | |
|                 w->x[w->tail] += dir;
 | |
|             }
 | |
| 
 | |
|             /* when the tail has been dragged so far that it meets
 | |
|                the next segment start the tail segment is obsolete and
 | |
|                must be freed */
 | |
|             if (w->x[w->tail] == w->x[tail_segment_end] &&
 | |
|                 w->y[w->tail] == w->y[tail_segment_end]){
 | |
|                 
 | |
|                 /* drop the last tail point */
 | |
|                 w->tail = tail_segment_end;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Draws the head and clears the tail of the worm in 
 | |
|  * the display buffer. lcd_update() is NOT called thus
 | |
|  * the caller has to take care that the buffer is displayed.
 | |
|  */
 | |
| static void draw_worm(struct worm *w)
 | |
| {
 | |
|     /* draw the new head */
 | |
|     int x = w->x[w->head];
 | |
|     int y = w->y[w->head];
 | |
|     if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
 | |
|         lcd_drawpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
 | |
|     }
 | |
| 
 | |
|     /* clear the space behind the worm */
 | |
|     x = w->x[w->tail] ;
 | |
|     y = w->y[w->tail] ;
 | |
|     if (x >= 0 && x < FIELD_RECT_WIDTH && y >= 0 && y < FIELD_RECT_HEIGHT) {
 | |
|         lcd_clearpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks wether the coordinate is part of the worm. Returns
 | |
|  * true if any part of the worm was hit - including the head.
 | |
|  * @param x int The x coordinate 
 | |
|  * @param y int The y coordinate
 | |
|  * @return int The index of the worm arrays that contain x, y.
 | |
|  * Returns -1 if the coordinates are not part of the worm.
 | |
|  */ 
 | |
| static int specific_worm_collision(struct worm *w, int x, int y)
 | |
| {
 | |
|     int retVal = -1;
 | |
| 
 | |
|     /* get_worm_array_length is expensive -> buffer the value */
 | |
|     int wormLength = get_worm_array_length(w);
 | |
|     int i;
 | |
| 
 | |
|     /* test each entry that is part of the worm */
 | |
|     for (i = 0; i < wormLength && retVal == -1; i++) {
 | |
| 
 | |
|         /* The iteration iterates the length of the worm.
 | |
|            Here's the conversion to the true indices within the worm arrays. */
 | |
|         int linestart = (w->tail + i  ) % MAX_WORM_SEGMENTS;
 | |
|         int lineend   = (linestart + 1) % MAX_WORM_SEGMENTS;
 | |
|         bool samex = (w->x[linestart] == x) && (w->x[lineend] == x);
 | |
|         bool samey = (w->y[linestart] == y) && (w->y[lineend] == y);
 | |
|         if (samex || samey){
 | |
|             int test, min, max, tmp;
 | |
| 
 | |
|             if (samey) {
 | |
|                 min = w->x[linestart];
 | |
|                 max = w->x[lineend];
 | |
|                 test = x; 
 | |
|             } else {
 | |
|                 min = w->y[linestart];
 | |
|                 max = w->y[lineend];
 | |
|                 test = y;
 | |
|             }
 | |
| 
 | |
|             tmp = min;
 | |
|             min = MIN(min, max);
 | |
|             max = MAX(tmp, max);
 | |
| 
 | |
|             if (min <= test && test <= max) {
 | |
|                 retVal = lineend;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Increases the length of the specified worm by marking 
 | |
|  * that it may grow by len pixels. Note that the worm has
 | |
|  * to move to make the growing happen.
 | |
|  * @param worm *w The worm that is to be altered.
 | |
|  * @param int len A positive value specifying the amount of
 | |
|  * pixels the worm may grow.
 | |
|  */
 | |
| static void add_growing(struct worm *w, int len) {
 | |
|     w->growing += len;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Determins the worm that is at the coordinates x, y. The parameter
 | |
|  * w is a switch parameter that changes the functionality of worm_collision.
 | |
|  * If w is specified and x,y hits the head of w NULL is returned.
 | |
|  * This is a useful way to determine wether the head of w hits 
 | |
|  * any worm but including itself but excluding its own head. 
 | |
|  * (It hits always its own head ;))
 | |
|  * If w is set to NULL worm_collision returns any worm including all heads
 | |
|  * that is at position of x,y.
 | |
|  * @param struct worm *w The worm of which the head should be excluded in
 | |
|  * the test. w may be set to NULL.
 | |
|  * @param int x The x coordinate that is checked
 | |
|  * @param int y The y coordinate that is checkec
 | |
|  * @return struct worm*  The worm that has been hit by x,y. If no worm
 | |
|  * was at the position NULL is returned.
 | |
|  */
 | |
| static struct worm* worm_collision(struct worm *w, int x, int y){
 | |
|     struct worm *retVal = NULL;
 | |
|     int i;
 | |
|     for (i = 0; (i < worm_count) && (retVal == NULL); i++) {
 | |
|         int collision_at = specific_worm_collision(&worms[i], x, y);
 | |
|         if (collision_at != -1) {
 | |
|             if (!(w == &worms[i] && collision_at == w->head)){
 | |
|                 retVal = &worms[i];
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns true if the head of the worm just has
 | |
|  * crossed the field boundaries. 
 | |
|  * @return bool true if the worm just has wrapped.
 | |
|  */ 
 | |
| static bool field_collision(struct worm *w)
 | |
| {
 | |
|     bool retVal = false;
 | |
|     if ((w->x[w->head] >= FIELD_RECT_WIDTH)  ||
 | |
|         (w->y[w->head] >= FIELD_RECT_HEIGHT) ||
 | |
|         (w->x[w->head] < 0)                  ||
 | |
|         (w->y[w->head] < 0))
 | |
|     {
 | |
|         retVal = true;
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Returns true if the specified coordinates are within the 
 | |
|  * field specified by the FIELD_RECT_XXX constants.
 | |
|  * @param int x The x coordinate of the point that is investigated
 | |
|  * @param int y The y coordinate of the point that is investigated
 | |
|  * @return bool Returns false if x,y specifies a point outside the 
 | |
|  * field of worms.
 | |
|  */
 | |
| static bool is_in_field_rect(int x, int y) {
 | |
|     bool retVal = false;
 | |
|     retVal = (x >= 0 && x < FIELD_RECT_WIDTH &&
 | |
|               y >= 0 && y < FIELD_RECT_HEIGHT);
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks and returns wether the head of the w
 | |
|  * is colliding with something currently.
 | |
|  * @return int One of the values:
 | |
|  *   COLLISION_NONE
 | |
|  *   COLLISION_w
 | |
|  *   COLLISION_FOOD
 | |
|  *   COLLISION_ARGH
 | |
|  *   COLLISION_FIELD
 | |
|  */
 | |
| static int check_collision(struct worm *w)
 | |
| {
 | |
|     int retVal = COLLISION_NONE;
 | |
| 
 | |
|     if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL)
 | |
|         retVal = COLLISION_WORM;
 | |
| 
 | |
|     if (food_collision(w->x[w->head], w->y[w->head]) >= 0)
 | |
|         retVal = COLLISION_FOOD;
 | |
| 
 | |
|     if (argh_collision(w->x[w->head], w->y[w->head]) >= 0)
 | |
|         retVal = COLLISION_ARGH;
 | |
| 
 | |
|     if (field_collision(w))
 | |
|         retVal = COLLISION_FIELD;
 | |
| 
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the index of the food that is closest to the point
 | |
|  * specified by x, y. This index may be used in the foodx and 
 | |
|  * foody arrays.
 | |
|  * @param int x The x coordinate of the point
 | |
|  * @param int y The y coordinate of the point
 | |
|  * @return int A value usable as index in foodx and foody.
 | |
|  */
 | |
| static int get_nearest_food(int x, int y){
 | |
|     int nearestfood = 0;
 | |
|     int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT;
 | |
|     int deltax = 0;
 | |
|     int deltay = 0;
 | |
|     int foodindex;
 | |
|     for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) {
 | |
|         int distance;
 | |
|         deltax = foodx[foodindex] - x;
 | |
|         deltay = foody[foodindex] - y;
 | |
|         deltax = deltax > 0 ? deltax : deltax * (-1);
 | |
|         deltay = deltay > 0 ? deltay : deltay * (-1);
 | |
|         distance = deltax + deltay;
 | |
| 
 | |
|         if (distance < olddistance) {
 | |
|             olddistance = distance;
 | |
|             nearestfood = foodindex;
 | |
|         }
 | |
|     }
 | |
|     return nearestfood;
 | |
| }
 | |
| 
 | |
| /** 
 | |
|  * Returns wether the specified position is next to the worm
 | |
|  * and in the direction the worm looks. Use this method to 
 | |
|  * test wether this position would be hit with the next move of 
 | |
|  * the worm unless the worm changes its direction.
 | |
|  * @param struct worm *w - The worm to be investigated
 | |
|  * @param int x - The x coordinate of the position to test.
 | |
|  * @param int y - The y coordinate of the position to test.
 | |
|  * @return Returns true if the worm will hit the position unless
 | |
|  * it change its direction before the next move.
 | |
|  */
 | |
| static bool is_in_front_of_worm(struct worm *w, int x, int y) {
 | |
|     bool infront = false;
 | |
|     int deltax = x - w->x[w->head];
 | |
|     int deltay = y - w->y[w->head];
 | |
| 
 | |
|     if (w->dirx == 0) {
 | |
|         infront = (w->diry * deltay) > 0;
 | |
|     } else {
 | |
|         infront = (w->dirx * deltax) > 0;
 | |
|     }
 | |
|     return infront;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns true if the worm will collide with the next move unless
 | |
|  * it changes its direction.
 | |
|  * @param struct worm *w - The worm to be investigated.
 | |
|  * @return Returns true if the worm will collide with the next move
 | |
|  * unless it changes its direction.
 | |
|  */
 | |
| static bool will_worm_collide(struct worm *w) {
 | |
|     int x = w->x[w->head] + w->dirx;
 | |
|     int y = w->y[w->head] + w->diry;
 | |
|     bool retVal = !is_in_field_rect(x, y);
 | |
|     if (!retVal) {
 | |
|         retVal = (argh_collision(x, y) != -1);
 | |
|     }
 | |
|     
 | |
|     if (!retVal) {
 | |
|         retVal = (worm_collision(w, x, y) != NULL);
 | |
|     }
 | |
|     return retVal;
 | |
| }
 | |
| 
 | |
| /** 
 | |
|  * This function
 | |
|  * may be used to be stored in worm.fetch_worm_direction for
 | |
|  * worms that are not controlled by humans but by artificial stupidity. 
 | |
|  * A direction is searched that doesn't lead to collision but to the nearest
 | |
|  * food - but not very intelligent. The direction is written to the specified
 | |
|  * worm.
 | |
|  * @param struct worm *w - The worm of which the direction 
 | |
|  * is altered.
 | |
|  */
 | |
| static void virtual_player(struct worm *w) {
 | |
|     bool isright;
 | |
|     int plana, planb, planc;
 | |
|     /* find the next lunch */
 | |
|     int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]);
 | |
| 
 | |
|     /* determine in which direction it is */
 | |
| 
 | |
|     /* in front of me? */
 | |
|     bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
 | |
| 
 | |
|     /* left right of me? */
 | |
|     int olddir = get_worm_dir(w);
 | |
|     set_worm_dir(w, (olddir + 1) % 4);
 | |
|     isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]);
 | |
|     set_worm_dir(w, olddir);
 | |
| 
 | |
|     /* detect situation, set strategy */
 | |
|     if (infront) {
 | |
|         if (isright) {
 | |
|             plana = olddir;
 | |
|             planb = (olddir + 1) % 4;
 | |
|             planc = (olddir + 3) % 4;
 | |
|         } else {
 | |
|             plana = olddir;
 | |
|             planb = (olddir + 3) % 4;
 | |
|             planc = (olddir + 1) % 4;
 | |
|         }
 | |
|     } else {
 | |
|         if (isright) {
 | |
|             plana = (olddir + 1) % 4;
 | |
|             planb = olddir;
 | |
|             planc = (olddir + 3) % 4;
 | |
|         } else {
 | |
|             plana = (olddir + 3) % 4;
 | |
|             planb = olddir;
 | |
|             planc = (olddir + 1) % 4;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* test for collision */
 | |
|     set_worm_dir(w, plana);
 | |
|     if (will_worm_collide(w)){
 | |
| 
 | |
|         /* plan b */
 | |
|         set_worm_dir(w, planb);
 | |
| 
 | |
|         /* test for collision */
 | |
|         if (will_worm_collide(w)) {
 | |
| 
 | |
|             /* plan c */
 | |
|             set_worm_dir(w, planc);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * prints out the score board with all the status information
 | |
|  * about the game.
 | |
|  */
 | |
| static void score_board(void)
 | |
| {
 | |
|     char buf[15];
 | |
|     char buf2[15];
 | |
|     int i;
 | |
|     int y = 0;
 | |
|     lcd_clearrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT);
 | |
|     for (i = 0; i < worm_count; i++) {
 | |
|         int score = get_score(&worms[i]);
 | |
|     
 | |
|         /* high score */
 | |
|         if (worms[i].fetch_worm_direction != virtual_player){
 | |
|             if (highscore < score) {
 | |
|                 highscore = score;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     /* length */
 | |
|         snprintf(buf, sizeof (buf), LANG_SB_LENGTH, score);
 | |
| 
 | |
|         /* worm state */
 | |
|         switch (check_collision(&worms[i])) {
 | |
|         case COLLISION_NONE:  
 | |
|                 if (worms[i].growing > 0){
 | |
|                     snprintf(buf2, sizeof(buf2), LANG_SB_GROWING);
 | |
|                 }
 | |
|                 else {
 | |
|                     if (worms[i].alive) {
 | |
|                         snprintf(buf2, sizeof(buf2), LANG_SB_HUNGRY);
 | |
|                     } else {
 | |
|                         snprintf(buf2, sizeof(buf2), LANG_SB_WORMED);
 | |
|                     }
 | |
|                 }
 | |
|             break;
 | |
| 
 | |
|         case COLLISION_WORM:  
 | |
|                 snprintf(buf2, sizeof(buf2), LANG_SB_WORMED);
 | |
|             break;
 | |
| 
 | |
|         case COLLISION_FOOD:  
 | |
|                 snprintf(buf2, sizeof(buf2), LANG_SB_GROWING);
 | |
|             break;
 | |
| 
 | |
|         case COLLISION_ARGH:  
 | |
|                 snprintf(buf2, sizeof(buf2), LANG_SB_ARGH);
 | |
|             break;
 | |
| 
 | |
|         case COLLISION_FIELD: 
 | |
|                 snprintf(buf2, sizeof(buf2), LANG_SB_CRASHED);
 | |
|             break;
 | |
|     }
 | |
|         lcd_putsxy(FIELD_RECT_WIDTH + 3, y  , buf, 0);
 | |
|         lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2, 0);
 | |
| 
 | |
|         if (!worms[i].alive){
 | |
|             lcd_invertrect(FIELD_RECT_WIDTH + 2, y, 
 | |
|                            LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17);
 | |
|         }
 | |
|         y += 19;
 | |
|     }
 | |
|     snprintf(buf , sizeof(buf), LANG_SB_HIGHSCORE, highscore);
 | |
| #ifndef DEBUG_WORMLET
 | |
|     lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf, 0);
 | |
| #else
 | |
|     lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout, 0);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Checks for collisions of the worm and its environment and
 | |
|  * takes appropriate actions like growing the worm or killing it.
 | |
|  * @return bool Returns true if the worm is dead. Returns
 | |
|  * false if the worm is healthy, up and creeping.
 | |
|  */
 | |
| static bool process_collisions(struct worm *w)
 | |
| {
 | |
|     int index = -1;
 | |
| 
 | |
|     w->alive &= !field_collision(w);
 | |
| 
 | |
|     if (w->alive) {
 | |
| 
 | |
|         /* check if food was eaten */
 | |
|         index = food_collision(w->x[w->head], w->y[w->head]);
 | |
|         if (index != -1){
 | |
|             int i;
 | |
|             
 | |
|             clear_food(index);
 | |
|             make_food(index);
 | |
|             draw_food(index);
 | |
|             
 | |
|             for (i = 0; i < ARGHS_PER_FOOD; i++) {
 | |
|                 argh_count++;
 | |
|                 if (argh_count > MAX_ARGH)
 | |
|                     argh_count = MAX_ARGH;
 | |
|                 make_argh(argh_count - 1);
 | |
|                 draw_argh(argh_count - 1);
 | |
|             }
 | |
| 
 | |
|             add_growing(w, WORM_PER_FOOD);
 | |
| 
 | |
|             draw_worm(w);
 | |
|         }
 | |
| 
 | |
|         /* check if argh was eaten */
 | |
|         else {
 | |
|             index = argh_collision(w->x[w->head], w->y[w->head]);
 | |
|             if (index != -1) {
 | |
|                 w->alive = false;
 | |
|         }
 | |
|             else {
 | |
|                 if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) {
 | |
|                     w->alive = false;
 | |
|     }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return !w->alive;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * The main loop of the game.
 | |
|  * @return bool Returns true if the game ended
 | |
|  * with a dead worm. Returns false if the user
 | |
|  * aborted the game manually.
 | |
|  */ 
 | |
| static bool run(void)
 | |
| {
 | |
|     int button = 0;
 | |
|     int wormDead = false;
 | |
| 
 | |
|     /* ticks are counted to compensate speed variations */
 | |
|     long cycle_start = 0, cycle_end = 0;
 | |
| #ifdef DEBUG_WORMLET
 | |
|     int ticks_to_max_cycle_reset = 20;
 | |
|     long max_cycle = 0;
 | |
|     char buf[20];
 | |
| #endif
 | |
| 
 | |
|     /* initialize the board and so on */
 | |
|     init_wormlet();
 | |
| 
 | |
|     cycle_start = current_tick;
 | |
|     /* change the direction of the worm */
 | |
|     while (button != BUTTON_OFF && ! wormDead)
 | |
|     {
 | |
|         int i;
 | |
|         long cycle_duration ;
 | |
|         switch (button) {
 | |
|             case BUTTON_UP:
 | |
|                 if (players == 1 && !use_remote) {
 | |
|                     player1_dir = NORTH;
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_DOWN:
 | |
|                 if (players == 1 && !use_remote) {
 | |
|                     player1_dir = SOUTH;
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_LEFT:
 | |
|                 if (players != 1 || use_remote) {
 | |
|                     player1_dir = (player1_dir + 3) % 4;
 | |
|                 } else {
 | |
|                     player1_dir = WEST;
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_RIGHT:
 | |
|                 if (players != 1 || use_remote) {
 | |
|                     player1_dir = (player1_dir + 1) % 4;
 | |
|                 } else {
 | |
|                     player1_dir = EAST;
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_F2:
 | |
|                 player2_dir = (player2_dir + 3) % 4;
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_F3:
 | |
|                 player2_dir = (player2_dir + 1) % 4;
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_VOL_UP:
 | |
|                 player3_dir = (player3_dir + 1) % 4;
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_VOL_DOWN:
 | |
|                 player3_dir = (player3_dir + 3) % 4;
 | |
|                 break;
 | |
| 
 | |
|             case BUTTON_PLAY:
 | |
|                 do {
 | |
|                     button = button_get(true);
 | |
|                 } while (button != BUTTON_PLAY &&
 | |
|                          button != BUTTON_OFF  &&
 | |
|                          button != BUTTON_ON);
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         for (i = 0; i < worm_count; i++) {
 | |
|             worms[i].fetch_worm_direction(&worms[i]);
 | |
|         }
 | |
| 
 | |
|         wormDead = true;
 | |
|         for (i = 0; i < worm_count; i++){
 | |
|             struct worm *w = &worms[i];
 | |
|             move_worm(w);
 | |
|             wormDead &= process_collisions(w);
 | |
|             draw_worm(w);
 | |
|         }
 | |
|         score_board();
 | |
|         lcd_update();
 | |
|         if (button == BUTTON_ON) {
 | |
|             wormDead = true;
 | |
|         }
 | |
| 
 | |
|         /* here the wormlet game cycle ends
 | |
|            thus the current tick is stored
 | |
|            as end time */
 | |
|         cycle_end = current_tick;
 | |
| 
 | |
|         /* The duration of the game cycle */
 | |
|         cycle_duration = cycle_end - cycle_start;
 | |
|         cycle_duration = MAX(0, cycle_duration);
 | |
|         cycle_duration = MIN(SPEED -1, cycle_duration);
 | |
| 
 | |
| 
 | |
| #ifdef DEBUG_WORMLET
 | |
|         ticks_to_max_cycle_reset--;
 | |
|         if (ticks_to_max_cycle_reset <= 0) {
 | |
|             max_cycle = 0;
 | |
|         }
 | |
| 
 | |
|         if (max_cycle < cycle_duration) {
 | |
|             max_cycle = cycle_duration;
 | |
|             ticks_to_max_cycle_reset = 20;
 | |
|         }
 | |
|         snprintf(buf, sizeof buf, "ticks %d", max_cycle);
 | |
|         set_debug_out(buf);
 | |
| #endif
 | |
|         /* adjust the number of ticks to wait for a button.
 | |
|            This ensures that a complete game cycle including
 | |
|            user input runs in constant time */
 | |
|         button = button_get_w_tmo(SPEED - cycle_duration);
 | |
|         cycle_start = current_tick;
 | |
| 
 | |
|     }
 | |
|     return wormDead;
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG_WORMLET
 | |
| 
 | |
| /**
 | |
|  * Just a test routine that checks that worm_food_collision works
 | |
|  * in some typical situations.
 | |
|  */
 | |
| static void test_worm_food_collision(void) {
 | |
|     int collision_count = 0;
 | |
|     int i;
 | |
|     lcd_clear_display();
 | |
|     init_worm(&worms[0], 10, 10);
 | |
|     add_growing(&worms[0], 10);
 | |
|     set_worm_dir(&worms[0], EAST);
 | |
|     for (i = 0; i < 10; i++) {
 | |
|         move_worm(&worms[0]);
 | |
|         draw_worm(&worms[0]);
 | |
|     }
 | |
| 
 | |
|     set_worm_dir(&worms[0], SOUTH);
 | |
|     for (i = 0; i < 10; i++) {
 | |
|         move_worm(&worms[0]);
 | |
|         draw_worm(&worms[0]);
 | |
|     }
 | |
| 
 | |
|     foodx[0] = 15;
 | |
|     foody[0] = 12;
 | |
|     for (foody[0] = 20; foody[0] > 0; foody[0] --) {
 | |
|         char buf[20];
 | |
|         bool collision;
 | |
|         draw_worm(&worms[0]);
 | |
|         draw_food(0);
 | |
|         collision = worm_food_collision(&worms[0], 0);
 | |
|         if (collision) {
 | |
|             collision_count++;
 | |
|         }
 | |
|         snprintf(buf, sizeof buf, "collisions: %d", collision_count);
 | |
|         lcd_putsxy(0, LCD_HEIGHT -8, buf, 0);
 | |
|         lcd_update();
 | |
|     }
 | |
|     if (collision_count != FOOD_SIZE) {
 | |
|         button_get(true);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     foody[0] = 15;
 | |
|     for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) {
 | |
|         char buf[20];
 | |
|         bool collision;
 | |
|         draw_worm(&worms[0]);
 | |
|         draw_food(0);
 | |
|         collision = worm_food_collision(&worms[0], 0);
 | |
|         if (collision) {
 | |
|             collision_count ++;
 | |
|         }
 | |
|         snprintf(buf, sizeof buf, "collisions: %d", collision_count);
 | |
|         lcd_putsxy(0, LCD_HEIGHT -8, buf, 0);
 | |
|         lcd_update();
 | |
|     }
 | |
|     if (collision_count != FOOD_SIZE * 2) {
 | |
|         button_get(true);
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| static void test_worm_argh_collision(void) {
 | |
|     int i;
 | |
|     int dir;
 | |
|     int collision_count = 0;
 | |
|     lcd_clear_display();
 | |
|     init_worm(&worms[0], 10, 10);
 | |
|     add_growing(&worms[0], 40);
 | |
|     for (dir = 0; dir < 4; dir++) {
 | |
|         set_worm_dir(&worms[0], (EAST + dir) % 4);
 | |
|         for (i = 0; i < 10; i++) {
 | |
|             move_worm(&worms[0]);
 | |
|             draw_worm(&worms[0]);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     arghx[0] = 12;
 | |
|     for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghy[0]++){
 | |
|         char buf[20];
 | |
|         bool collision;
 | |
|         draw_argh(0);
 | |
|         collision = worm_argh_collision(&worms[0], 0);
 | |
|         if (collision) {
 | |
|             collision_count ++;
 | |
|         }
 | |
|         snprintf(buf, sizeof buf, "collisions: %d", collision_count);
 | |
|         lcd_putsxy(0, LCD_HEIGHT -8, buf, 0);
 | |
|         lcd_update();
 | |
|     }
 | |
|     if (collision_count != ARGH_SIZE * 2) {
 | |
|         button_get(true);
 | |
|     }
 | |
| 
 | |
|     arghy[0] = 12;
 | |
|     for (arghx[0] = 0; arghx[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghx[0]++){
 | |
|         char buf[20];
 | |
|         bool collision;
 | |
|         draw_argh(0);
 | |
|         collision = worm_argh_collision(&worms[0], 0);
 | |
|         if (collision) {
 | |
|             collision_count ++;
 | |
|         }
 | |
|         snprintf(buf, sizeof buf, "collisions: %d", collision_count);
 | |
|         lcd_putsxy(0, LCD_HEIGHT -8, buf, 0);
 | |
|         lcd_update();
 | |
|     }
 | |
|     if (collision_count != ARGH_SIZE * 4) {
 | |
|         button_get(true);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int testline_in_rect(void) {
 | |
|     int testfailed = -1;
 | |
| 
 | |
|     int rx = 10;
 | |
|     int ry = 15;
 | |
|     int rw = 20;
 | |
|     int rh = 25;
 | |
| 
 | |
|     /* Test 1 */
 | |
|     int x1 = 12;
 | |
|     int y1 = 8;
 | |
|     int x2 = 12;
 | |
|     int y2 = 42;
 | |
| 
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_update();
 | |
|         lcd_putsxy(0, 0, "failed 1", 0);
 | |
|         button_get(true);
 | |
|         testfailed = 1;
 | |
|     }
 | |
| 
 | |
|     /* test 2 */
 | |
|     y2 = 20;
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 2", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 2;
 | |
|     }
 | |
| 
 | |
|     /* test 3 */
 | |
|     y1 = 30;
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 3", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 3;
 | |
|     }
 | |
| 
 | |
|     /* test 4 */
 | |
|     y2 = 45;
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 4", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 4;
 | |
|     }
 | |
| 
 | |
|     /* test 5 */
 | |
|     y1 = 50;
 | |
|     if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
 | |
|         line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 5", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 5;
 | |
|     }
 | |
| 
 | |
|     /* test 6 */
 | |
|     y1 = 5;
 | |
|     y2 = 7;
 | |
|     if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
 | |
|         line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 6", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 6;
 | |
|     }
 | |
| 
 | |
|     /* test 7 */
 | |
|     x1 = 8;
 | |
|     y1 = 20;
 | |
|     x2 = 35;
 | |
|     y2 = 20;
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 7", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 7;
 | |
|     }
 | |
| 
 | |
|     /* test 8 */
 | |
|     x2 = 12;
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 8", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 8;
 | |
|     }
 | |
| 
 | |
|     /* test 9 */
 | |
|     x1 = 25;
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 9", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 9;
 | |
|     }
 | |
| 
 | |
|     /* test 10 */
 | |
|     x2 = 37;
 | |
|     if (!line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         !line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 10", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 10;
 | |
|     }
 | |
| 
 | |
|     /* test 11 */
 | |
|     x1 = 42;
 | |
|     if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
 | |
|         line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 11", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 11;
 | |
|     }
 | |
| 
 | |
|     /* test 12 */
 | |
|     x1 = 5;
 | |
|     x2 = 7;
 | |
|     if (line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) ||
 | |
|         line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh)) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 12", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 12;
 | |
|     }
 | |
| 
 | |
|     /* test 13 */
 | |
|     rx = 9;
 | |
|     ry = 15;
 | |
|     rw = FOOD_SIZE;
 | |
|     rh = FOOD_SIZE;
 | |
| 
 | |
|     x1 = 10;
 | |
|     y1 = 10;
 | |
|     x2 = 10;
 | |
|     y2 = 20;
 | |
|     if (!(line_in_rect(x1, y1, x2, y2, rx, ry, rw, rh) &&
 | |
|         line_in_rect(x2, y2, x1, y1, rx, ry, rw, rh))) {
 | |
|         lcd_drawrect(rx, ry, rw, rh);
 | |
|         lcd_drawline(x1, y1, x2, y2);
 | |
|         lcd_putsxy(0, 0, "failed 13", 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|         testfailed = 13;
 | |
|     }
 | |
| 
 | |
|     lcd_clear_display();
 | |
| 
 | |
|     return testfailed;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Just a test routine to test wether specific_worm_collision might work properly
 | |
|  */
 | |
| static int test_specific_worm_collision(void) {
 | |
|     int collisions = 0;
 | |
|     int dir;
 | |
|     int x = 0;
 | |
|     int y = 0;
 | |
|     char buf[20];
 | |
|     lcd_clear_display();
 | |
|     init_worm(&worms[0], 10, 20);
 | |
|     add_growing(&worms[0], 20 - INITIAL_WORM_LENGTH);
 | |
| 
 | |
|     for (dir = EAST; dir < EAST + 4; dir++) {
 | |
|         int i;
 | |
|         set_worm_dir(&worms[0], dir % 4);
 | |
|         for (i = 0; i < 5; i++) {
 | |
|             if (!(dir % 4 == NORTH && i == 9)) {
 | |
|                 move_worm(&worms[0]);
 | |
|                 draw_worm(&worms[0]);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (y = 15; y < 30; y ++){
 | |
|         for (x = 5; x < 20; x++) {
 | |
|             if (specific_worm_collision(&worms[0], x, y) != -1) {
 | |
|                 collisions ++;
 | |
|             }
 | |
|             lcd_invertpixel(x + FIELD_RECT_X, y + FIELD_RECT_Y);
 | |
|             snprintf(buf, sizeof buf, "collisions %d", collisions);
 | |
|             lcd_putsxy(0, LCD_HEIGHT - 8, buf, 0);
 | |
|             lcd_update();
 | |
|         }
 | |
|     }
 | |
|     if (collisions != 21) {
 | |
|         button_get(true);
 | |
|     }
 | |
|     return collisions;
 | |
| }
 | |
| 
 | |
| static void test_make_argh(void){
 | |
|     int dir;
 | |
|     int seed = 0;
 | |
|     int hit = 0;
 | |
|     int failures = 0;
 | |
|     lcd_clear_display();
 | |
|     init_worm(&worms[0], 10, 20);
 | |
|     add_growing(&worms[0], 40 - INITIAL_WORM_LENGTH);
 | |
| 
 | |
|     for (dir = EAST; dir < EAST + 4; dir++) {
 | |
|         int i;
 | |
|         set_worm_dir(&worms[0], dir % 4);
 | |
|         for (i = 0; i < 10; i++) {
 | |
|             if (!(dir % 4 == NORTH && i == 9)) {
 | |
|                 move_worm(&worms[0]);
 | |
|                 draw_worm(&worms[0]);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     lcd_update();
 | |
| 
 | |
|     srand(seed);
 | |
|     for (seed = 0; hit < 20; seed += 2) {
 | |
|         int x = rand() % (FIELD_RECT_WIDTH  - ARGH_SIZE);
 | |
|         int y = rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE);
 | |
|         
 | |
|         if (worm_in_rect(&worms[0], x, y, ARGH_SIZE, ARGH_SIZE)) {
 | |
|             char buf[20];
 | |
|             srand(seed);
 | |
|             make_argh(0);
 | |
|             draw_argh(0);
 | |
|             lcd_invertrect(x + FIELD_RECT_X, y+ FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE);
 | |
| 
 | |
|             if (x == arghx[0] && y == arghy[0]) {
 | |
|                 failures ++;
 | |
|             }
 | |
| 
 | |
|             snprintf(buf, sizeof buf, "hit at x%d y%d fail%d", x, y, failures);
 | |
|             lcd_putsxy(0, LCD_HEIGHT - 8, buf, 0);
 | |
|             lcd_update();
 | |
|             lcd_invertrect(x + FIELD_RECT_X, y + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE);
 | |
|             lcd_clearrect(arghx[0] + FIELD_RECT_X, arghy[0] + FIELD_RECT_Y, ARGH_SIZE, ARGH_SIZE);
 | |
| 
 | |
|             if (failures > 0) {
 | |
|                 button_get(true);
 | |
|             }
 | |
|             hit ++;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void test_worm_argh_collision_in_moves(void) {
 | |
|     int hit_count = 0;
 | |
|     int i;
 | |
|     lcd_clear_display();
 | |
|     init_worm(&worms[0], 10, 20);
 | |
| 
 | |
|     arghx[0] = 20;
 | |
|     arghy[0] = 18;
 | |
|     draw_argh(0);
 | |
| 
 | |
|     set_worm_dir(&worms[0], EAST);
 | |
|     for (i = 0; i < 10; i++) {
 | |
|         char buf[20];
 | |
|         move_worm(&worms[0]);
 | |
|         draw_worm(&worms[0]);
 | |
|         if (worm_argh_collision_in_moves(&worms[0], 0, 5)){
 | |
|             hit_count ++;
 | |
|         }
 | |
|         snprintf(buf, sizeof buf, "in 5 moves hits: %d", hit_count);
 | |
|         lcd_putsxy(0, LCD_HEIGHT - 8, buf, 0);
 | |
|         lcd_update();
 | |
|         button_get(true);
 | |
|     }    
 | |
| }
 | |
| #endif /* DEBUG_WORMLET */
 | |
| 
 | |
| /**
 | |
|  * Main entry point from the menu to start the game control.
 | |
|  */
 | |
| Menu wormlet(void)
 | |
| {
 | |
|     bool wormDead = false;
 | |
|     int button;
 | |
| #ifdef DEBUG_WORMLET
 | |
|     test_worm_argh_collision_in_moves();
 | |
|     test_make_argh();
 | |
|     testline_in_rect();
 | |
|     test_worm_food_collision();
 | |
|     test_worm_argh_collision();
 | |
|     test_specific_worm_collision();
 | |
| #endif 
 | |
| 
 | |
|     lcd_setmargins(0,0);
 | |
| 
 | |
|     /* Setup screen */
 | |
|     do {
 | |
|         char buf[20];
 | |
|         lcd_clear_display();
 | |
| 
 | |
|         /* first line players */
 | |
|         snprintf(buf, sizeof buf, LANG_CS_PLAYERS, players);
 | |
|         lcd_puts(0, 0, buf);
 | |
| 
 | |
|         /* second line worms */
 | |
|         snprintf(buf, sizeof buf, LANG_CS_WORMS, worm_count);
 | |
|         lcd_puts(0, 1, buf);
 | |
| 
 | |
|         /* third line control */
 | |
|         if (players > 1) {
 | |
|             if (use_remote) {
 | |
|                 snprintf(buf, sizeof buf, LANG_CS_REMOTE_CTRL);
 | |
|             } else {
 | |
|                 snprintf(buf, sizeof buf, LANG_CS_NO_REM_CTRL);
 | |
|             }
 | |
|         } else {
 | |
|             if (players > 0) {
 | |
|                 if (use_remote) {
 | |
|                     snprintf(buf, sizeof buf, LANG_CS_2_KEY_CTRL);
 | |
|                 } else {
 | |
|                     snprintf(buf, sizeof buf, LANG_CS_4_KEY_CTRL);
 | |
|                 }
 | |
|             } else {
 | |
|                 snprintf(buf, sizeof buf, LANG_CS_NO_CONTROL);
 | |
|             }
 | |
|         }
 | |
|         lcd_puts(0, 2, buf);
 | |
|         
 | |
|         lcd_update();
 | |
| 
 | |
|         /* user selection */
 | |
|         button = button_get(true);
 | |
|         switch (button) {
 | |
|             case BUTTON_UP:
 | |
|                 if (players < 3) {
 | |
|                     players ++;
 | |
|                     if (players > worm_count) {
 | |
|                         worm_count = players;
 | |
|                     }
 | |
|                     if (players > 2) {
 | |
|                         use_remote = true;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             case BUTTON_DOWN:
 | |
|                 if (players > 0) {
 | |
|                     players --;
 | |
|                 }
 | |
|                 break;
 | |
|             case BUTTON_LEFT: 
 | |
|                 if (worm_count > 1) {
 | |
|                     worm_count--;
 | |
|                     if (worm_count < players) {
 | |
|                         players = worm_count;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             case BUTTON_RIGHT:
 | |
|                 if (worm_count < MAX_WORMS) {
 | |
|                     worm_count ++;
 | |
|                 }
 | |
|                 break;
 | |
|             case BUTTON_F1:
 | |
|                 use_remote = !use_remote;
 | |
|                 if (players > 2) {
 | |
|                     use_remote = true;
 | |
|                 }
 | |
|                 break;
 | |
|         }
 | |
|     } while (button != BUTTON_PLAY && 
 | |
|              button != BUTTON_OFF  && button != BUTTON_ON);
 | |
| 
 | |
|     lcd_clear_display();
 | |
|     /* end of setup */
 | |
| 
 | |
|     do {    
 | |
| 
 | |
|         /* button state will be overridden if
 | |
|            the game quits with the death of the worm.
 | |
|            Initializing button to BUTTON_OFF ensures
 | |
|            that the user can hit BUTTON_OFF during the
 | |
|            game to return to the menu.
 | |
|         */
 | |
|         button  = BUTTON_OFF;
 | |
| 
 | |
|         /* start the game */
 | |
|         wormDead = run();
 | |
| 
 | |
|         /* if worm isn't dead the game was quit
 | |
|            via BUTTON_OFF -> no need to wait for buttons. */
 | |
|         if (wormDead) {
 | |
|             do {
 | |
|                 button = button_get(true);
 | |
|             }
 | |
|             /* BUTTON_ON -> start new game */
 | |
|             /* BUTTON_OFF -> back to game menu */
 | |
|             while (button != BUTTON_OFF && button != BUTTON_ON);
 | |
|         }
 | |
|     }
 | |
|     while (button != BUTTON_OFF);
 | |
| 
 | |
|     return MENU_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| #endif /* USE_GAMES */
 |