From f365ccac2a29105c02663507b3b57a9a63657d4c Mon Sep 17 00:00:00 2001 From: stpohle Date: Sat, 7 Feb 2004 23:51:16 +0000 Subject: [PATCH] new player position placement coded by dcdillon --- include/map.h | 33 ++- src/map.c | 669 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 649 insertions(+), 53 deletions(-) diff --git a/include/map.h b/include/map.h index 988e650..704fb0c 100644 --- a/include/map.h +++ b/include/map.h @@ -1,4 +1,4 @@ -/* $Id: map.h,v 1.12 2004/02/05 21:32:17 stpohle Exp $ */ +/* $Id: map.h,v 1.13 2004/02/07 23:51:16 stpohle Exp $ */ /* map.h */ #ifndef _MAP_H_ @@ -14,6 +14,12 @@ #define ANI_STONETIMEOUT 1.0 // factor for the animation frame je seconds #define ANI_POWERUPTIMEOUT 0.5 // factor for powerup animations +/* this is the minimum distance that starting points should be set apart from each + * other. If the given tolerence cannot be satisfied on the map, it will be decremented + * by 1 until it can be satisfied. this is just our initial value + */ + +#define MAP_POSITION_TOLERENCE 10 struct __ex_field { unsigned char count; @@ -33,6 +39,14 @@ struct __field { } typedef _field; +/* holds the locatition of a start point and the use count */ + +struct __start_point { + _point pos; + int used; +} typedef _start_point; + + struct __map { _point size; // dimension of the field @@ -55,6 +69,7 @@ struct __map { int sp_row; int sp_kick; unsigned char state; // state of the map + _start_point start_point[MAX_PLAYERS]; // array of starting points for this map } typedef _map; @@ -76,5 +91,21 @@ extern void map_set_player_way (int pl); extern void map_set_player_way1 (int pl); extern void map_set_player_way2 (int pl, int hardway); +/* new functions for player positioning */ + +extern int map_ensure_corner_start_points(); +extern int map_init_start_points(); +extern int map_set_start_point(int idx, int x, int y); +extern int map_add_start_point(int x, int y); +extern int map_is_start_point(int x, int y); +extern int map_num_defined_start_points(); +extern int map_check_start_point(int x, int y, int tol); +extern int map_check_and_add_start_point(int x, int y, int tol); +extern int map_find_and_add_start_points(int num, int tol); +extern int map_is_possible_start_point(int x, int y); +extern int map_create_and_add_start_point(int tol); +extern int map_is_removable_field(int x, int y); +extern int map_place_player(int pl); +extern int map_respawn_player(int pl); #endif diff --git a/src/map.c b/src/map.c index f845ecf..266f8cb 100644 --- a/src/map.c +++ b/src/map.c @@ -1,4 +1,4 @@ -/* $Id: map.c,v 1.20 2004/01/07 23:04:32 patty21 Exp $ */ +/* $Id: map.c,v 1.21 2004/02/07 23:51:17 stpohle Exp $ */ /* map handling, like generate and load maps. */ #include "bomberclone.h" @@ -40,6 +40,11 @@ map_new (char *filename) y; FILE *fmap; signed char old_maptype = map.type; + int pl_cnt, pl; + + /* initialize the start_point array in the _map struct */ + + map_init_start_points(); if (filename) { fmap = fopen (filename, "r"); @@ -97,6 +102,32 @@ map_new (char *filename) for (y = 0; y < MAX_FIELDSIZE_Y; y++) map.bfield[x][y] = 0; + /* count the number of players on this map so we know how many starting points + * to find + */ + + pl_cnt = 0; + + for (pl = 0; pl < MAX_PLAYERS; pl++) { + + if (PS_IS_used (players[pl].state)) { + + pl_cnt++; + } + } + + + /* identify possible starting positions for players and store them in the + * start_point array in the _map struct. This will always succeed. If + * it cannot find starting points within the tolerance, it first attempts + * to create start points within the tolerence and otherwise lowers the + * tolerence until it can satisfy the proper number of start points. + * eventually the tolerence reaches 0, so it can, in the worst case, start + * all players at the same start point. + */ + + map_find_and_add_start_points(pl_cnt - map_num_defined_start_points(), MAP_POSITION_TOLERENCE); + /* Set the Playerinformation */ map_set_playerposition (fmap != NULL); @@ -159,6 +190,10 @@ map_genrandom () map.field[x][y].frame = 0.0f; map.field[x][y].special = FT_nothing; } + + /* set the corners of the map to be valid start points */ + + map_ensure_corner_start_points(); } @@ -336,65 +371,95 @@ map_set_playerposition (int usermap) int pl, ready, maxtry; + int all_players_set = 1; d_printf ("map_set_playerposition\n"); - - /* try to set every player on a good place */ - maxtry = 300; - ready = 0; - while (!ready && maxtry-- > 0) { - ready = 1; - for (pl = 0; pl < MAX_PLAYERS; pl++) - if (PS_IS_used (players[pl].state) && (PLX (pl) < 0 || PLY (pl) < 0)) { - /* set player */ - ready = 0; - map_set_player_way1 (pl); - map_playerpos_check (pl); - } + + /* This is the new code that will set every player in a starting point + * It should never fail, but if it does, it will fall through to the old method + */ + + for (pl = 0; pl < MAX_PLAYERS; pl++) { + if (PS_IS_used(players[pl].state)) { + map_place_player(pl); + } } - - /* every player which is still not set .. set now and delete some normal stones */ - maxtry = 200; - ready = 0; - while (!ready && maxtry-- > 0) { - ready = 1; - for (pl = 0; pl < MAX_PLAYERS; pl++) - if (PS_IS_used (players[pl].state) && (PLX (pl) < 0 || PLY (pl) < 0)) { - /* set player */ - ready = 0; - map_set_player_way2 (pl, 0); - if (maxtry > 50) - map_playerpos_check (pl); - } + + /* test to see if all players are placed */ + + for (pl = 0; pl < MAX_PLAYERS; pl++) { + + if ((PS_IS_used(players[pl].state)) + && ((players[pl].pos.x < 0) || (players[pl].pos.y < 0))) { + + all_players_set = 0; + break; + } } - /* try another way for setting the players */ - maxtry = 200; - ready = 0; - while (!ready && maxtry-- > 0) { - ready = 1; + + /* if a used player is not set at a valid start point, fall into the old mode */ + + if (!all_players_set) { + + d_fatal("Using old player set method. This should not happen.\n"); + + /* try to set every player on a good place */ + maxtry = 300; + ready = 0; + while (!ready && maxtry-- > 0) { + ready = 1; + for (pl = 0; pl < MAX_PLAYERS; pl++) + if (PS_IS_used (players[pl].state) && (PLX (pl) < 0 || PLY (pl) < 0)) { + /* set player */ + ready = 0; + map_set_player_way1 (pl); + map_playerpos_check (pl); + } + } + + /* every player which is still not set .. set now and delete some normal stones */ + maxtry = 200; + ready = 0; + while (!ready && maxtry-- > 0) { + ready = 1; + for (pl = 0; pl < MAX_PLAYERS; pl++) + if (PS_IS_used (players[pl].state) && (PLX (pl) < 0 || PLY (pl) < 0)) { + /* set player */ + ready = 0; + map_set_player_way2 (pl, 0); + if (maxtry > 50) + map_playerpos_check (pl); + } + } + /* try another way for setting the players */ + maxtry = 200; + ready = 0; + while (!ready && maxtry-- > 0) { + ready = 1; + for (pl = 0; pl < MAX_PLAYERS; pl++) + if (PS_IS_used (players[pl].state) && (PLX (pl) < 0 || PLY (pl) < 0)) { + /* set player */ + ready = 0; + map_set_player_way2 (pl, 1); + if (maxtry > 50) + map_playerpos_check (pl); + } + } + + + + /* every player who is still not set ... let them die before they + * can play put a warning on the screen */ + maxtry = 0; // mark our warning for (pl = 0; pl < MAX_PLAYERS; pl++) if (PS_IS_used (players[pl].state) && (PLX (pl) < 0 || PLY (pl) < 0)) { - /* set player */ - ready = 0; - map_set_player_way2 (pl, 1); - if (maxtry > 50) - map_playerpos_check (pl); + PLX (pl) = 0.0; + PLY (pl) = 0.0; + maxtry = 1; } + if (maxtry) + d_fatal ("Not All Player could been set\n"); } - - - - /* every player who is still not set ... let them die before they - * can play put a warning on the screen */ - maxtry = 0; // mark our warning - for (pl = 0; pl < MAX_PLAYERS; pl++) - if (PS_IS_used (players[pl].state) && (PLX (pl) < 0 || PLY (pl) < 0)) { - PLX (pl) = 0.0; - PLY (pl) = 0.0; - maxtry = 1; - } - if (maxtry) - d_fatal ("Not All Player could been set\n"); }; #undef PLX @@ -529,3 +594,503 @@ map_load (FILE * fmap) fclose (fmap); }; + + +/* This is called for randomly generated maps. It clears out each corner of the map + * to make sure that the 4 corners are legal start points. + */ + +int +map_ensure_corner_start_points () +{ + + /* make sure all the corners are empty as well as the 1 field in the Y direction + * and one in the X direction + */ + + /* top left corner is safe start point */ + map.field[1][1].type = FT_nothing; + map.field[1][2].type = FT_nothing; + map.field[2][1].type = FT_nothing; + + map_add_start_point(1, 1); + + /* bottom left corner is safe start point */ + map.field[1][map.size.y - 2].type = FT_nothing; + map.field[1][map.size.y - 3].type = FT_nothing; + map.field[2][map.size.y - 2].type = FT_nothing; + + map_add_start_point(1, map.size.y - 2); + + /* top right corner is safe start point */ + map.field[map.size.x - 2][1].type = FT_nothing; + map.field[map.size.x - 3][1].type = FT_nothing; + map.field[map.size.x - 2][2].type = FT_nothing; + + map_add_start_point(map.size.x - 2, 1); + + /* bottom right corner is safe start point */ + map.field[map.size.x - 2][map.size.y - 2].type = FT_nothing; + map.field[map.size.x - 2][map.size.y - 3].type = FT_nothing; + map.field[map.size.x - 3][map.size.y - 2].type = FT_nothing; + + map_add_start_point(map.size.x - 2, map.size.y - 2); + + return 1; +} + + +/* initializes all the start points for the map to (-1,-1) and their used flag to 0 */ + +int +map_init_start_points() +{ + int i; + + for (i = 0; i < MAX_PLAYERS; i++) { + map.start_point[i].pos.x = -1; + map.start_point[i].pos.y = -1; + map.start_point[i].used = 0; + } + + return 0; +} + +/* blindly sets (x,y) as the idx'th start point in the map */ + +int +map_set_start_point(int idx, int x, int y) +{ + map.start_point[idx].pos.x = x; + map.start_point[idx].pos.y = y; + return idx; +} + +/* checks to see if all start points have been set, if not, sets (x,y) as a possible start point + * returns 0 on successful set, -1 on failure + */ + +int +map_add_start_point(int x, int y) +{ + int i; + + /* find the first unset start point */ + for (i = 0; i < MAX_PLAYERS; i++) { + + if ((map.start_point[i].pos.x == -1) && (map.start_point[i].pos.y == -1)) { + map_set_start_point(i, x, y); + return 0; + } + } + + /* if all start points are already set, do nothing and return -1 */ + + return -1; +} + + +/* returns 0 if (x,y) is not already set as a start point in the current map, nonzero otherwise */ + +int +map_is_start_point(int x, int y) +{ + int i; + + for (i = 0; i < MAX_PLAYERS; i++) { + + if ((map.start_point[i].pos.x == x) && (map.start_point[i].pos.y == y)) + return 1; + } + + return 0; +} + +/* returns the number of start points set in the current map */ + +int +map_num_defined_start_points() +{ + int pts = 0; + int i; + + for (i = 0; i < MAX_PLAYERS; i++) { + + if ((map.start_point[i].pos.x != -1) && (map.start_point[i].pos.y != -1)) + pts++; + } + + return pts; +} + + +/* checks if the start point (x, y) is far enough away from all the other start points + * returns 1 if it is far enough, 0 otherwise + */ + +int +map_check_start_point(int x, int y, int tol) +{ + int i; + int dx, dy; + float dist; + + for (i = 0; i < MAX_PLAYERS; i++) { + + if ((map.start_point[i].pos.x != -1) && (map.start_point[i].pos.y != -1)) { + + dx = map.start_point[i].pos.x - x; + dy = map.start_point[i].pos.y - y; + + dist = sqrt(dx * dx + dy * dy); + + if (dist < tol) + return 0; + } + } + + return 1; +} + + +/* checks to see if there is an available start point and if (x, y) is sufficiently far from all + * defined start points and adds (x, y) as a start point if the conditions are met. + * returns 1 on success, 0 on failure + */ + +int +map_check_and_add_start_point(int x, int y, int tol) +{ + if ((map_num_defined_start_points() < MAX_PLAYERS) && (map_check_start_point(x, y, tol))) { + + map_add_start_point(x, y); + return 1; + } + + return 0; +} + + +/* locates and adds num start points to the current map. returns the number of start points added. */ + +int +map_find_and_add_start_points(int num, int tol) +{ + int x; + int y; + int i; + int added = 0; + + while ((added < num) && (tol >= 0)) { + for (x = 0; x < map.size.x; x++) { + + for (y = 0; y < map.size.y; y++) { + + if (map_is_possible_start_point(x, y)) { + + added += map_check_and_add_start_point(x, y, tol); + } + } + } + + for (i = 0; i < num - added; i++) { + + added += map_create_and_add_start_point(tol); + } + + tol--; + } + + /* printf("Minimum Tolerance: %d\n", tol + 1); */ + + return added; +} + +/* returns 1 if (x,y) is a feasible start point such that the player can safely lay a bomb and not die + * otherwise returns 0. Note: this is a quick check only checking immediately adjacent fields. It will not + * check secondary adjacencies, however, the idea is that when looking for possible start poitns, it will be run + * on every FT_nothing field so it will catch the secondary adjacencies when they become primary adjacencies + */ + +int +map_is_possible_start_point(int x, int y) +{ + + int x_ok_pos; + int x_ok_neg; + int y_ok_pos; + int y_ok_neg; + + int x_adj = 0; + int y_adj = 0; + int i; + + /* if (x, y) is not FT_nothing, this is not a valid start point */ + + if (map.field[x][y].type != FT_nothing) { + + return 0; + } + + x_ok_pos = (x < map.size.x - 2) ? 1:0; + x_ok_neg = (x > 1) ? 1:0; + + y_ok_pos = (y < map.size.y - 2) ? 1:0; + y_ok_neg = (y > 1) ? 1:0; + + /* calculate the number of adjacent FT_nothing fields in the X and Y directions */ + + for (i = 1; i < 4; i++) { + + if (x_ok_pos) { + + if (map.field[x+i][y].type == FT_nothing) { + + x_adj++; + } else { + + x_ok_pos = 0; + } + } + + if (x_ok_neg) { + + if (map.field[x-i][y].type == FT_nothing) { + + x_adj++; + } else { + + x_ok_neg = 0; + } + } + + if (y_ok_pos) { + + if (map.field[x][y+i].type == FT_nothing) { + + y_adj++; + } else { + + y_ok_pos = 0; + } + } + + if (y_ok_neg) { + + if (map.field[x][y-i].type == FT_nothing) { + + y_adj++; + } else { + y_ok_neg = 0; + } + } + } + + if ((x_adj >= 3) || (y_adj >= 3)) { + + return 1; + } + + if ((x_adj >= 1) && (y_adj >= 1)) { + + return 1; + } + + return 0; +} + + +/* alters the map to create another start point at least tol units from any other + * start point. returns 1 on success, 0 on failure + */ + +int +map_create_and_add_start_point(int tol) +{ + int x; + int y; + int dx; + int dy; + + int init_x; + int init_y; + int end_x; + int end_y; + int step_x; + int step_y; + + /* this changes how we traverse the map when looking for a place to put + * a start point. this is so all the start points don't get stuck in one + * part of the map if the map is large enough + */ + + if (s_random(100) % 2) { + + init_x = 0; + end_x = map.size.x; + step_x = 1; + } else { + + init_x = map.size.x - 1; + end_x = -1; + step_x = -1; + } + + if (s_random(100) % 2) { + + init_y = 0; + end_y = map.size.y; + step_y = 1; + } else { + + init_y = map.size.y - 1; + end_y = -1; + step_y = -1; + } + + + /* first try only FT_nothing fields as start points */ + + for (x = init_x; x != end_x; x += step_x) { + + for (y = init_y; y != end_y; y+= step_y) { + + if ((map.field[x][y].type == FT_nothing) && (map_check_start_point(x, y, tol))) { + + dx = (x >= map.size.x - 2) ? -1:1; + dy = (y >= map.size.y - 2) ? -1:1; + + if ((map_is_removable_field(x+dx, y)) && (map_is_removable_field(x, y+dy))) { + + /* printf("Creating Start Point (%d, %d).\n", x, y); */ + + map.field[x][y].type = FT_nothing; + + map.field[x+dx][y].type = FT_nothing; + map.field[x][y+dy].type = FT_nothing; + + map_add_start_point(x, y); + + return 1; + } + } + } + } + + /* if we get here we didn't find a useful FT_nothing field, so check the FT_stone + * fields + */ + + for (x = init_x; x != end_x; x += step_x) { + + for (y = init_y; y != end_y; y+= step_y) { + + if ((map.field[x][y].type == FT_stone) && (map_check_start_point(x, y, tol))) { + + dx = (x >= map.size.x - 2) ? -1:1; + dy = (y >= map.size.y - 2) ? -1:1; + + if ((map_is_removable_field(x+dx, y)) && (map_is_removable_field(x, y+dy))) { + + /* printf("Creating Start Point (%d, %d).\n", x, y); */ + + map.field[x][y].type = FT_nothing; + + map.field[x+dx][y].type = FT_nothing; + map.field[x][y+dy].type = FT_nothing; + + map_add_start_point(x, y); + + return 1; + } + } + } + } + + /* if we get to this point, we tried every field that we want to turn into a + * start point, so we return 0 indicating failure + */ + + return 0; +} + + +/* checks the type of the field at (x, y). if it is something we can remove without + * drastically altering the map (that is to say, not a FT_tunnel or FT_block) returns + * 1, returns 0 if this is not a field that we want to alter + */ + +int +map_is_removable_field(int x, int y) +{ + if ((map.field[x][y].type == FT_nothing) || (map.field[x][y].type == FT_stone)) { + + return 1; + } else { + + return 0; + } +} + + +/* sets the players[pl] initial position to one of the start_points found in the map + * randomly selects the start point so the same players don't always start near each + * other + */ + +int +map_place_player(int pl) +{ + int index; + int start_points; + int idx; + int i; + + start_points = map_num_defined_start_points(); + index = (s_random(MAX_PLAYERS) + 1) % start_points; + + for (i = 0; i < start_points; i++) { + + idx = (index + i) % start_points; + + if ((!map.start_point[idx].used) && (map.start_point[idx].pos.x != -1) + && (map.start_point[idx].pos.y != -1)) { + + players[pl].pos.x = map.start_point[idx].pos.x; + players[pl].pos.y = map.start_point[idx].pos.y; + map.start_point[idx].used = 1; + + return 1; + } + } + + return 0; +} + + +/* this will randomly select a start point, check to see if it is a safe place to + * respawn the player, and then set their position. It cannot fail unless there is + * no start point possible on the map (which would prevent the game from ever starting) + */ + +/* note: this is not used yet, but could be used to respawn players instead of the old + * method + */ + +int +map_respawn_player(int pl) +{ + int x; + int y; + + do { + + x = s_random (map.size.x - 2) + 1; + y = s_random (map.size.y - 2) + 1; + } while (!map_is_possible_start_point(x, y)); + + players[pl].pos.x = x; + players[pl].pos.y = y; + + return 1; +}