📄 wormlet.c
字号:
/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id: wormlet.c,v 1.1 2003/06/29 16:33:04 zagor Exp $ * * 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 "plugin.h"#ifdef HAVE_LCD_BITMAP/* size of the field the worm lives in */#define FIELD_RECT_X 1#define FIELD_RECT_Y 1#define FIELD_RECT_WIDTH (LCD_WIDTH - 45)#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. */static 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;/* the rockbox plugin api */static struct plugin_api* rb;#ifdef DEBUG_WORMLETstatic 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 - 1, FOOD_SIZE - 1); 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 - 1, ARGH_SIZE - 1); 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 = rb->rand() % (FIELD_RECT_WIDTH - FOOD_SIZE); y = rb->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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -