316 lines
8.8 KiB
C
316 lines
8.8 KiB
C
/*
|
|
* Empire - A multi-player, client/server Internet based war game.
|
|
* Copyright (C) 1986-2005, Dave Pare, Jeff Bailey, Thomas Ruschak,
|
|
* Ken Stevens, Steve McClure
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* ---
|
|
*
|
|
* See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
|
|
* related information and legal notices. It is expected that any future
|
|
* projects/authors will amend these files as needed.
|
|
*
|
|
* ---
|
|
*
|
|
* bestpath.c: Find the best path between sectors
|
|
*
|
|
* Known contributors to this file:
|
|
* Steve McClure, 1998-2000
|
|
*/
|
|
|
|
/*
|
|
* IMPORTANT: These routines are very selectively used in the server.
|
|
*
|
|
* "bestpath" is now obsolete (and removed)
|
|
* "bestownedpath" is only used to determine paths for ships and planes.
|
|
*
|
|
* Callers should not be calling these directly anymore. They should use
|
|
* the "BestShipPath", "BestAirPath", "BestLandPath" and "BestDistPath"
|
|
* functions. Note that those last two use the A* algorithms to find
|
|
* information.
|
|
*/
|
|
|
|
#include "misc.h"
|
|
#include "xy.h"
|
|
#include "sect.h"
|
|
#include "file.h"
|
|
#include "nat.h"
|
|
#include "common.h"
|
|
#include "optlist.h"
|
|
|
|
static int owned_and_navigable(s_char *, int, int, s_char *, int);
|
|
|
|
#define MAXROUTE 100 /* return '?' if path longer than this */
|
|
#define valid(x,y) (((x^y)&1)==0)
|
|
|
|
/* ________________________________________________________________
|
|
**
|
|
** bestpath(x1,y1,x2,y2,(s_char *)terrain);
|
|
**
|
|
** Calculate routing string to get from sector [x1,y1] to sector [x2,y2]
|
|
** via a specified type of terrain.
|
|
**
|
|
** Specify:
|
|
**
|
|
** x1,y1 starting coordinates
|
|
**
|
|
** x2,y2 destination coordinates
|
|
**
|
|
** terrain ptr to string showing the types of sectors that
|
|
** we're allowed to pass through:
|
|
**
|
|
** A null string enables routing through any kind of
|
|
** sector (useful for airplanes).
|
|
**
|
|
** A string that begins with an 'R' ensures that
|
|
** the source and destination sectors also match
|
|
** the specified type of terrain.
|
|
**
|
|
** A string that begins with a '~' (after the 'R',
|
|
** if necessary) specifies that we can pass through
|
|
** any kind of sector EXCEPT those in the remainder
|
|
** of the string.
|
|
**
|
|
** Examples:
|
|
**
|
|
** "R~.^" all sectors along route must be
|
|
** non-ocean, non-mountain
|
|
**
|
|
** "+" all sectors between start and end
|
|
** must be highway
|
|
**
|
|
** "h. " all sectors along route must be
|
|
** harbor, water, or unmapped
|
|
**
|
|
** 'bestpath' returns a pointer to a route string containing either:
|
|
**
|
|
** yugjbn - string of normal routing characters if route possible
|
|
** ? - if route is longer than MAXROUTE characters
|
|
** \0 - (null string) if no route possible
|
|
** h - if start and end points are the same sector
|
|
** ________________________________________________________________
|
|
*/
|
|
|
|
s_char *dirchar = "juygbn";
|
|
int dx[6] = { 2, 1, -1, -2, -1, 1 };
|
|
int dy[6] = { 0, -1, -1, 0, 1, 1 };
|
|
|
|
/*
|
|
* Ok, note that here we malloc some buffers. BUT, we never
|
|
* free them. Why, you may ask? Because we want to allocate
|
|
* them based on world size which is now (or soon to be) dynamic,
|
|
* but we don't want to allocate each and every time, since that
|
|
* would be slow. And, since world size only changes at init
|
|
* time, we can do this safely.
|
|
* We also share these buffers between "bestpath" and "bestownedpath"
|
|
* since you can never be in both functions at the same time. If that
|
|
* did happen, we'd already be so broken that it won't matter.
|
|
*/
|
|
static unsigned int *mapbuf = (unsigned int *)0;
|
|
static unsigned int **mapindex = (unsigned int **)0;
|
|
|
|
s_char *
|
|
bestownedpath(s_char *bpath,
|
|
s_char *bigmap,
|
|
int x, int y, int ex, int ey, s_char *terrain, int own)
|
|
{
|
|
int i, j, tx, ty, markedsectors, restr2;
|
|
int minx, maxx, miny, maxy, scanx, scany;
|
|
unsigned int routelen;
|
|
|
|
if (!mapbuf)
|
|
mapbuf = malloc((WORLD_X * WORLD_Y) *
|
|
sizeof(unsigned int));
|
|
if (!mapbuf)
|
|
return NULL;
|
|
if (!mapindex) {
|
|
mapindex = malloc(WORLD_X * sizeof(unsigned int *));
|
|
if (mapindex) {
|
|
/* Setup the map pointers */
|
|
for (i = 0; i < WORLD_X; i++)
|
|
mapindex[i] = &mapbuf[WORLD_Y * i];
|
|
}
|
|
}
|
|
if (!mapindex)
|
|
return NULL;
|
|
|
|
bpath[0] = 0;
|
|
if (0 != (restr2 = (*terrain == 'R')))
|
|
terrain++;
|
|
|
|
x = XNORM(x);
|
|
y = YNORM(y);
|
|
ex = XNORM(ex);
|
|
ey = YNORM(ey);
|
|
|
|
if (x == ex && y == ey) {
|
|
bpath[0] = 'h';
|
|
bpath[1] = 0;
|
|
return bpath;
|
|
}
|
|
|
|
if (!valid(x, y) || !valid(ex, ey))
|
|
return NULL;
|
|
|
|
if (restr2 && (!owned_and_navigable(bigmap, x, y, terrain, own) ||
|
|
!owned_and_navigable(bigmap, x, y, terrain, own)))
|
|
return NULL;
|
|
|
|
for (i = 0; i < WORLD_X; i++)
|
|
for (j = 0; j < WORLD_Y; j++)
|
|
mapindex[i][j] = 0xFFFF; /* clear the workspace */
|
|
|
|
routelen = 0; /* path length is now 0 */
|
|
mapindex[x][y] = 0; /* mark starting spot */
|
|
markedsectors = 1; /* source sector marked */
|
|
minx = x - 2; /* set X scan bounds */
|
|
maxx = x + 2;
|
|
miny = y - 1; /* set Y scan bounds */
|
|
maxy = y + 1;
|
|
|
|
do {
|
|
if (++routelen == MAXROUTE) {
|
|
bpath[0] = '?';
|
|
bpath[1] = 0;
|
|
return bpath;
|
|
}
|
|
markedsectors = 0;
|
|
for (scanx = minx; scanx <= maxx; scanx++) {
|
|
x = XNORM(scanx);
|
|
for (scany = miny; scany <= maxy; scany++) {
|
|
y = YNORM(scany);
|
|
if (valid(x, y)) {
|
|
if ((((mapindex[x][y]) & 0x1FFF) == (routelen - 1))) {
|
|
for (i = 0; i < 6; i++) {
|
|
tx = x + dx[i];
|
|
ty = y + dy[i];
|
|
tx = XNORM(tx);
|
|
ty = YNORM(ty);
|
|
if (mapindex[tx][ty] == 0xFFFF) {
|
|
if (owned_and_navigable(bigmap, tx, ty,
|
|
terrain, own)
|
|
|| (tx == ex && ty == ey && !restr2)) {
|
|
mapindex[tx][ty] =
|
|
((i + 1) << 13) + routelen;
|
|
markedsectors++;
|
|
}
|
|
}
|
|
if (tx == ex && ty == ey) {
|
|
bpath[routelen] = 0;
|
|
while (routelen--) {
|
|
i = ((mapindex[tx][ty]) >> 13) - 1;
|
|
bpath[routelen] = dirchar[i];
|
|
tx = tx - dx[i];
|
|
ty = ty - dy[i];
|
|
tx = XNORM(tx);
|
|
ty = YNORM(ty);
|
|
}
|
|
return bpath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
miny--;
|
|
maxy++;
|
|
minx -= 2;
|
|
maxx += 2;
|
|
} while (markedsectors);
|
|
|
|
bpath[0] = 0;
|
|
return NULL; /* no route possible */
|
|
}
|
|
|
|
/* return TRUE if sector is passable */
|
|
static int
|
|
owned_and_navigable(s_char *map, int x, int y, s_char *terrain, int own)
|
|
{
|
|
s_char c;
|
|
s_char *t;
|
|
s_char mapspot; /* What this spot on the bmap is */
|
|
int negate;
|
|
struct sctstr *sect;
|
|
int rel;
|
|
|
|
/* No terrain to check? Everything is navigable! (this
|
|
probably means we are flying) */
|
|
if (!(*terrain))
|
|
return 1;
|
|
|
|
/* Are we checking this map? */
|
|
if (map) {
|
|
/* Do we know what this sector is? If not, we assume it's ok,
|
|
since otherwise we'll never venture anywhere */
|
|
mapspot = map[sctoff(x, y)];
|
|
if (mapspot == ' ' || mapspot == 0)
|
|
return 1;
|
|
|
|
/* Now, is it marked with a 'x' or 'X'? If so, avoid it! */
|
|
if (mapspot == 'x' || mapspot == 'X')
|
|
return 0;
|
|
} else {
|
|
/* We don't know what it is since we have no map, so return ok! */
|
|
return 1;
|
|
}
|
|
|
|
/* Now, check this bmap entry to see if it is one of the
|
|
terrain types. */
|
|
t = terrain;
|
|
if (*t == '~') {
|
|
negate = 1;
|
|
t++;
|
|
} else
|
|
negate = 0;
|
|
|
|
while (*t) {
|
|
if (*t == mapspot)
|
|
break;
|
|
t++;
|
|
}
|
|
if (negate && *t) {
|
|
/* We found it, so we say it's bad since we are negating */
|
|
return 0;
|
|
} else if (!negate && !*t) {
|
|
/* We didn't find it, so we say it's bad since we aren't negating */
|
|
return 0;
|
|
}
|
|
|
|
/* According to our bmap, this sector is ok so far. */
|
|
|
|
/* Ok, we made it this far. Now get the sector */
|
|
sect = getsectp(x, y);
|
|
c = dchr[sect->sct_type].d_mnem;
|
|
/* Ok, now, check the owner if needed */
|
|
if (own >= 0) {
|
|
if (sect->sct_own != own) {
|
|
rel = getrel(getnatp(sect->sct_own), own);
|
|
/* We can't sail through deity sectors, but we can sail
|
|
through any ocean */
|
|
if (rel < FRIENDLY && sect->sct_type != SCT_WATER)
|
|
return 0;
|
|
}
|
|
}
|
|
/* Ok, now, check these two sector types */
|
|
/* check for bad harbors. */
|
|
if (c == 'h' && sect->sct_effic < 2)
|
|
return 0;
|
|
/* check for bad bridges */
|
|
if (c == '=' && sect->sct_effic < 60)
|
|
return 0;
|
|
/* Woo-hoo, it's ok! */
|
|
return 1;
|
|
}
|