/* * Empire - A multi-player, client/server Internet based war game. * Copyright (C) 1986-2008, 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 files README, COPYING and CREDITS in the root of the source * tree for related information and legal notices. It is expected * that 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 * Markus Armbruster, 2006 */ /* * IMPORTANT: These routines are very selectively used in the server. * * "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 #include "file.h" #include "misc.h" #include "nat.h" #include "optlist.h" #include "path.h" #include "prototypes.h" #include "sect.h" #include "xy.h" static int owned_and_navigable(char *, int, int, int); #define MAXROUTE 100 #define valid(x,y) ((((x) ^ (y)) & 1) == 0) /* * 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. */ static unsigned short *mapbuf; static unsigned short **mapindex; /* * Find passable path from X, Y to EX, EY for nation OWN. * BPATH is a buffer capable of holding at least MAXROUTE characters. * If BIGMAP is null, all sectors are passable (useful for flying). * Else it is taken to be a bmap. * Sectors owned by or allied to OWN are then passable according to * the usual rules. * Other sectors are assumed to be passable when BIGMAP shows '.' or * nothing. * Return a path if found, else a null pointer. * Wart: the path isn't terminated with 'h', except when if X,Y equals * EX,EY. */ char * bestownedpath(char *bpath, char *bigmap, int x, int y, int ex, int ey, int own) { int i, j, tx, ty, markedsectors; int minx, maxx, miny, maxy, scanx, scany; unsigned routelen; if (!mapbuf) mapbuf = malloc(WORLD_X * WORLD_Y * sizeof(*mapbuf)); if (!mapbuf) return NULL; if (!mapindex) { mapindex = malloc(WORLD_X * sizeof(*mapindex)); if (mapindex) { /* Setup the map pointers */ for (i = 0; i < WORLD_X; i++) mapindex[i] = &mapbuf[WORLD_Y * i]; } } if (!mapindex) return NULL; x = XNORM(x); y = YNORM(y); ex = XNORM(ex); ey = YNORM(ey); if (x == ex && y == ey) return "h"; if (!valid(x, y) || !valid(ex, ey)) return NULL; if (!owned_and_navigable(bigmap, ex, ey, 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) return NULL; markedsectors = 0; for (scanx = minx; scanx <= maxx; scanx++) { x = XNORM(scanx); for (scany = miny; scany <= maxy; scany++) { y = YNORM(scany); if (!valid(x, y)) continue; if (((mapindex[x][y] & 0x1FFF) == routelen - 1)) { for (i = DIR_FIRST; i <= DIR_LAST; i++) { tx = x + diroff[i][0]; ty = y + diroff[i][1]; tx = XNORM(tx); ty = YNORM(ty); if (mapindex[tx][ty] == 0xFFFF) { if (owned_and_navigable(bigmap, tx, ty, own)) { if (CANT_HAPPEN(i < DIR_FIRST || i > DIR_LAST)) i = DIR_STOP; mapindex[tx][ty] = ((i - DIR_FIRST + 1) << 13) + routelen; markedsectors++; } } if (tx == ex && ty == ey) { bpath[routelen] = 0; while (routelen--) { i = (mapindex[tx][ty] >> 13) - 1 + DIR_FIRST; bpath[routelen] = dirch[i]; tx = tx - diroff[i][0]; ty = ty - diroff[i][1]; tx = XNORM(tx); ty = YNORM(ty); } return bpath; } } } } } miny--; maxy++; minx -= 2; maxx += 2; } while (markedsectors); return NULL; /* no route possible */ } /* * Return non-zero if sector X, Y is passable. * If BIGMAP is null, all sectors are passable (useful for flying). * Else it is taken to be a bmap. * Sectors owned by or allied to OWN are checked according to the * usual rules, and the result is correct. * Other sectors are assumed to be passable when BIGMAP shows '.' or * nothing. */ static int owned_and_navigable(char *bigmap, int x, int y, int own) { char mapspot; struct sctstr sect; if (!bigmap) return 1; /* Owned or allied sector? Check the real sector. */ getsect(x, y, §); if (sect.sct_own == own || (sect.sct_own && getrel(getnatp(sect.sct_own), own) == ALLIED)) { /* FIXME duplicates shp_check_nav() logic */ switch (dchr[sect.sct_type].d_nav) { case NAVOK: return 1; case NAV_CANAL: /* FIXME return 1 when all ships have M_CANAL */ return 0; case NAV_02: return sect.sct_effic >= 2; case NAV_60: return sect.sct_effic >= 60; default: return 0; } } /* Can only check bigmap */ mapspot = bigmap[sctoff(x, y)]; return mapspot == '.' || mapspot == ' ' || mapspot == 0; }