]> git.pond.sub.org Git - empserver/blob - src/lib/common/bestpath.c
Fix the previous revision.
[empserver] / src / lib / common / bestpath.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  bestpath.c: Find the best path between sectors
29  * 
30  *  Known contributors to this file:
31  *     Steve McClure, 1998-2000
32  */
33
34 /* 
35  * IMPORTANT: These routines are very selectively used in the server.
36  *
37  * "bestownedpath" is only used to determine paths for ships and planes.
38  * 
39  * Callers should not be calling these directly anymore. They should use
40  * the "BestShipPath", "BestAirPath", "BestLandPath" and "BestDistPath"
41  * functions.  Note that those last two use the A* algorithms to find
42  * information.
43  */
44
45 #include <config.h>
46
47 #include "misc.h"
48 #include "xy.h"
49 #include "sect.h"
50 #include "file.h"
51 #include "nat.h"
52 #include "common.h"
53 #include "optlist.h"
54
55 static int owned_and_navigable(char *, int, int, int);
56
57 #define MAXROUTE        100
58 #define valid(x,y)      (((x^y)&1)==0)
59
60 static char *dirchar = "juygbn";
61 int dx[6] = { 2, 1, -1, -2, -1, 1 };
62 int dy[6] = { 0, -1, -1, 0, 1, 1 };
63
64 /*
65  * Ok, note that here we malloc some buffers.  BUT, we never
66  * free them.  Why, you may ask?  Because we want to allocate
67  * them based on world size which is now (or soon to be) dynamic,
68  * but we don't want to allocate each and every time, since that
69  * would be slow.  And, since world size only changes at init
70  * time, we can do this safely.
71  */
72 static unsigned int *mapbuf;
73 static unsigned int **mapindex;
74
75 /*
76  * Find passable path from X, Y to EX, EY for nation OWN.
77  * BPATH is a buffer capable of holding at least MAXROUTE characters.
78  * If BIGMAP is null, all sectors are passable (useful for flying).
79  * Else it is taken to be a bmap.
80  * Sectors owned by or allied to OWN are checked according to the
81  * usual rules, and the result is correct.
82  * Other sectors are assumed to be passable when BIGMAP shows '.' or
83  * nothing.
84  * Return path or a null pointer.
85  */
86 char *
87 bestownedpath(char *bpath, char *bigmap,
88               int x, int y, int ex, int ey, int own)
89 {
90     int i, j, tx, ty, markedsectors;
91     int minx, maxx, miny, maxy, scanx, scany;
92     unsigned int routelen;
93
94     if (!mapbuf)
95         mapbuf = malloc(WORLD_X * WORLD_Y * sizeof(unsigned int));
96     if (!mapbuf)
97         return NULL;
98     if (!mapindex) {
99         mapindex = malloc(WORLD_X * sizeof(unsigned int *));
100         if (mapindex) {
101             /* Setup the map pointers */
102             for (i = 0; i < WORLD_X; i++)
103                 mapindex[i] = &mapbuf[WORLD_Y * i];
104         }
105     }
106     if (!mapindex)
107         return NULL;
108
109     x = XNORM(x);
110     y = YNORM(y);
111     ex = XNORM(ex);
112     ey = YNORM(ey);
113
114     if (x == ex && y == ey)
115         return "h";
116
117     if (!valid(x, y) || !valid(ex, ey))
118         return NULL;
119
120     for (i = 0; i < WORLD_X; i++)
121         for (j = 0; j < WORLD_Y; j++)
122             mapindex[i][j] = 0xFFFF;    /* clear the workspace  */
123
124     routelen = 0;               /* path length is now 0 */
125     mapindex[x][y] = 0;         /* mark starting spot   */
126     markedsectors = 1;          /* source sector marked */
127     minx = x - 2;               /* set X scan bounds    */
128     maxx = x + 2;
129     miny = y - 1;               /* set Y scan bounds    */
130     maxy = y + 1;
131
132     do {
133         if (++routelen == MAXROUTE - 1)
134             return NULL;
135         markedsectors = 0;
136         for (scanx = minx; scanx <= maxx; scanx++) {
137             x = XNORM(scanx);
138             for (scany = miny; scany <= maxy; scany++) {
139                 y = YNORM(scany);
140                 if (!valid(x, y))
141                     continue;
142                 if (((mapindex[x][y] & 0x1FFF) == routelen - 1)) {
143                     for (i = 0; i < 6; i++) {
144                         tx = x + dx[i];
145                         ty = y + dy[i];
146                         tx = XNORM(tx);
147                         ty = YNORM(ty);
148                         if (mapindex[tx][ty] == 0xFFFF) {
149                             if (owned_and_navigable(bigmap, tx, ty, own)) {
150                                 mapindex[tx][ty] =
151                                     ((i + 1) << 13) + routelen;
152                                 markedsectors++;
153                             }
154                         }
155                         if (tx == ex && ty == ey) {
156                             bpath[routelen] = 'h';
157                             bpath[routelen + 1] = 0;
158                             while (routelen--) {
159                                 i = (mapindex[tx][ty] >> 13) - 1;
160                                 bpath[routelen] = dirchar[i];
161                                 tx = tx - dx[i];
162                                 ty = ty - dy[i];
163                                 tx = XNORM(tx);
164                                 ty = YNORM(ty);
165                             }
166                             return bpath;
167                         }
168                     }
169                 }
170             }
171         }
172         miny--;
173         maxy++;
174         minx -= 2;
175         maxx += 2;
176     } while (markedsectors);
177
178     return NULL;                /* no route possible    */
179 }
180
181 /*
182  * Return non-zero if sector X, Y is passable.
183  * If BIGMAP is null, all sectors are passable (useful for flying).
184  * Else it is taken to be a bmap.
185  * Sectors owned by or allied to OWN are checked according to the
186  * usual rules, and the result is correct.
187  * Other sectors are assumed to be passable when BIGMAP shows '.' or
188  * nothing.
189  */
190 static int
191 owned_and_navigable(char *bigmap, int x, int y, int own)
192 {
193     char mapspot;
194     struct sctstr sect;
195
196     if (!bigmap)
197         return 1;
198
199     /* Owned or allied sector?  Check the real sector.  */
200     getsect(x, y, &sect);
201     if (sect.sct_own == own
202         || (sect.sct_own && getrel(getnatp(sect.sct_own), own) == ALLIED)) {
203         /* FIXME duplicates shp_check_nav() logic */
204         switch (dchr[sect.sct_type].d_nav) {
205         case NAVOK:
206             return 1;
207         case NAV_CANAL:
208             /* FIXME return 1 when all ships have M_CANAL */
209             return 0;
210         case NAV_02:
211             return sect.sct_effic >= 2;
212         case NAV_60:
213             return sect.sct_effic >= 60;
214         default:
215             return 0;
216         }
217     }
218
219     /* Can only check bigmap */
220     mapspot = bigmap[sctoff(x, y)];
221     return mapspot == '.' || mapspot == ' ' || mapspot == 0;
222 }