]> git.pond.sub.org Git - empserver/blob - src/lib/common/maps.c
44eef4594cd2c85e624c0007320156dfcc2a34e2
[empserver] / src / lib / common / maps.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  *  maps.c: Map routines
29  * 
30  *  Known contributors to this file:
31  *     Ken Stevens, 1995
32  *     Steve McClure, 1998
33  */
34
35 #include <config.h>
36
37 #include <ctype.h>
38 #include "com.h"
39 #include "empobj.h"
40 #include "file.h"
41 #include "land.h"
42 #include "map.h"
43 #include "misc.h"
44 #include "nat.h"
45 #include "nsc.h"
46 #include "nuke.h"
47 #include "optlist.h"
48 #include "plane.h"
49 #include "player.h"
50 #include "prototypes.h"
51 #include "sect.h"
52 #include "ship.h"
53 #include "xy.h"
54
55 static int bmnxtsct(struct nstr_sect *);
56 static char map_char(unsigned char type, natid own, int owner_or_god);
57
58 int
59 draw_map(int bmap, char origin, int map_flags, struct nstr_sect *nsp)
60 {
61     struct natstr *np;
62     struct range range;
63     struct nstr_item ni;
64     struct shpstr ship;
65     struct lndstr land;
66     struct plnstr plane;
67     struct nukstr nuke;
68     coord x, y;
69     int i;
70     /* Note this is not re-entrant anyway, so we keep the buffers
71        around */
72     static unsigned char *bitmap = NULL;
73     static char *wmapbuf = NULL;
74     static char **wmap = NULL;
75
76     if (!wmapbuf)
77         wmapbuf = malloc(WORLD_Y * MAPWIDTH(1));
78     if (!wmap) {
79         wmap = malloc(WORLD_Y * sizeof(char *));
80         if (wmap && wmapbuf) {
81             for (i = 0; i < WORLD_Y; i++)
82                 wmap[i] = &wmapbuf[MAPWIDTH(1) * i];
83         } else if (wmap) {
84             free(wmap);
85             wmap = NULL;
86         }
87     }
88     if (!bitmap)
89         bitmap = malloc((WORLD_X * WORLD_Y) / 8);
90     if (!wmapbuf || !wmap || !bitmap) {
91         pr("Memory error, tell the deity.\n");
92         logerror("malloc failed in draw_map\n");
93         return RET_FAIL;
94     }
95
96     if (bmap == 'r') {
97         if (!confirm("Are you sure you want to revert your bmap? "))
98             return RET_OK;
99     }
100     if (!(player->command->c_flags & C_MOD)) {
101         logerror("%s command needs C_MOD flag set",
102                  player->command->c_form);
103         player->command->c_flags |= C_MOD;
104     }
105     np = getnatp(player->cnum);
106     /* zap any conditionals */
107     nsp->ncond = 0;
108     xyrelrange(np, &nsp->range, &range);
109     border(&range, "     ", "");
110     blankfill(wmapbuf, &nsp->range, 1);
111     if (bmap) {
112         int c;
113         switch (bmap) {
114         default:
115             CANT_REACH();
116             bmap = 'b';
117             /* fall through */
118         case 'b':
119             while (bmnxtsct(nsp) && !player->aborted) {
120                 if (0 != (c = player->bmap[sctoff(nsp->x, nsp->y)]))
121                     wmap[nsp->dy][nsp->dx] = c;
122             }
123             break;
124         case 't':
125             while (bmnxtsct(nsp) && !player->aborted) {
126                 if (0 != (c = player->map[sctoff(nsp->x, nsp->y)]))
127                     wmap[nsp->dy][nsp->dx] = c;
128             }
129             break;
130         case 'r':
131             while (bmnxtsct(nsp) && !player->aborted) {
132                 player->bmap[sctoff(nsp->x, nsp->y)] =
133                     player->map[sctoff(nsp->x, nsp->y)];
134                 if (0 != (c = player->bmap[sctoff(nsp->x, nsp->y)]))
135                     wmap[nsp->dy][nsp->dx] = c;
136             }
137             ef_write(EF_BMAP, player->cnum, player->bmap);
138             break;
139         case 'n':
140             {
141                 struct sctstr sect;
142
143                 if (!player->god) {
144                     memset(bitmap, 0, (WORLD_X * WORLD_Y) / 8);
145                     bitinit2(nsp, bitmap, player->cnum);
146                 }
147                 while (nxtsct(nsp, &sect) && !player->aborted) {
148                     if (!player->god && !emp_getbit(nsp->x, nsp->y, bitmap))
149                         continue;
150                     wmap[nsp->dy][nsp->dx]
151                         = map_char(sect.sct_newtype, sect.sct_own,
152                                    player->owner);
153                 }
154                 break;
155             }
156         }
157     } else {
158         struct sctstr sect;
159         char mapch;
160         int changed = 0;
161
162         if (!player->god) {
163             memset(bitmap, 0, (WORLD_X * WORLD_Y) / 8);
164             bitinit2(nsp, bitmap, player->cnum);
165         }
166         while (nxtsct(nsp, &sect) && !player->aborted) {
167             if (!player->god && !emp_getbit(nsp->x, nsp->y, bitmap))
168                 continue;
169             mapch = map_char(sect.sct_type, sect.sct_own, player->owner);
170             wmap[nsp->dy][nsp->dx] = mapch;
171             changed |= map_set(player->cnum, nsp->x, nsp->y, mapch, 0);
172         }
173         if (changed)
174             writemap(player->cnum);
175     }
176     if (player->aborted)
177         return RET_OK;
178     if (map_flags & MAP_PLANE) {
179         snxtitem_all(&ni, EF_PLANE);
180         while (nxtitem(&ni, &plane)) {
181             if (plane.pln_own == 0)
182                 continue;
183             if (plane.pln_own != player->cnum && !player->god)
184                 continue;
185             if (!xyinrange(plane.pln_x, plane.pln_y, &nsp->range))
186                 continue;
187
188             x = xnorm(plane.pln_x - nsp->range.lx);
189             y = ynorm(plane.pln_y - nsp->range.ly);
190             wmap[y][x] = (*plchr[(int)plane.pln_type].pl_name) & ~0x20;
191         }
192     }
193     if (map_flags & MAP_SHIP) {
194         snxtitem_all(&ni, EF_SHIP);
195         while (nxtitem(&ni, &ship)) {
196             if (ship.shp_own == 0)
197                 continue;
198             if (ship.shp_own != player->cnum && !player->god)
199                 continue;
200             if (!xyinrange(ship.shp_x, ship.shp_y, &nsp->range))
201                 continue;
202
203             x = xnorm(ship.shp_x - nsp->range.lx);
204             y = ynorm(ship.shp_y - nsp->range.ly);
205             wmap[y][x] = (*mchr[(int)ship.shp_type].m_name) & ~0x20;
206         }
207     }
208     if (map_flags & MAP_LAND) {
209         snxtitem_all(&ni, EF_LAND);
210         while (nxtitem(&ni, &land)) {
211             if (land.lnd_own == 0)
212                 continue;
213             if (land.lnd_own != player->cnum && !player->god)
214                 continue;
215             if (!xyinrange(land.lnd_x, land.lnd_y, &nsp->range))
216                 continue;
217
218             x = xnorm(land.lnd_x - nsp->range.lx);
219             y = ynorm(land.lnd_y - nsp->range.ly);
220             wmap[y][x] = (*lchr[(int)land.lnd_type].l_name) & ~0x20;
221         }
222     }
223     if (map_flags & MAP_NUKE) {
224         snxtitem_all(&ni, EF_NUKE);
225         while (nxtitem(&ni, &nuke)) {
226             if (nuke.nuk_own == 0)
227                 continue;
228             if (nuke.nuk_own != player->cnum && !player->god)
229                 continue;
230             if (!xyinrange(nuke.nuk_x, nuke.nuk_y, &nsp->range))
231                 continue;
232
233             x = xnorm(nuke.nuk_x - nsp->range.lx);
234             y = ynorm(nuke.nuk_y - nsp->range.ly);
235             wmap[y][x] = 'N';
236         }
237     }
238     if (map_flags & MAP_HIGH) {
239         char *ptr;
240         struct sctstr sect;
241
242         snxtsct_rewind(nsp);
243         if (!player->god) {
244             memset(bitmap, 0, (WORLD_X * WORLD_Y) / 8);
245             bitinit2(nsp, bitmap, player->cnum);
246         }
247         while (nxtsct(nsp, &sect) && !player->aborted) {
248             if (!player->god && !emp_getbit(nsp->x, nsp->y, bitmap))
249                 continue;
250             ptr = &wmap[nsp->dy][nsp->dx];
251             if (sect.sct_own == player->cnum)
252                  *ptr |= 0x80;
253         }
254     }
255     if (origin)
256         wmap[5][10] = origin & ~0x20;
257     for (y = nsp->range.ly, i = 0; i < nsp->range.height; y++, i++) {
258         int yval;
259
260         yval = yrel(np, y);
261         wmap[i][nsp->range.width] = '\0';
262         pr("%4d %s %-4d\n", yval, wmap[i], yval);
263         if (y >= WORLD_Y)
264             y -= WORLD_Y;
265     }
266     border(&range, "     ", "");
267     return RET_OK;
268 }
269
270 /*
271  * get the next sector in the range
272  */
273 static int
274 bmnxtsct(struct nstr_sect *np)
275 {
276     while (1) {
277         np->dx++;
278         np->x++;
279         if (np->x >= WORLD_X)
280             np->x = 0;
281         if (np->dx >= np->range.width) {
282             np->dx = 0;
283             np->x = np->range.lx;
284             np->dy++;
285             if (np->dy >= np->range.height)
286                 return 0;
287             np->y++;
288             if (np->y >= WORLD_Y)
289                 np->y = 0;
290         }
291         if ((np->y + np->x) & 01)
292             continue;
293         if (np->type == NS_DIST) {
294             np->curdist = mapdist(np->x, np->y, np->cx, np->cy);
295             if (np->curdist > np->dist)
296                 continue;
297         }
298         np->id = sctoff(np->x, np->y);
299         return 1;
300     }
301     /*NOTREACHED*/
302 }
303
304 /*
305  * Return character to use in maps for sector type TYPE owned by OWN.
306  * If OWNER_OR_GOD, the map is for the sector's owner or a deity.
307  */
308 static char
309 map_char(unsigned char type, natid own, int owner_or_god)
310 {
311     if (CANT_HAPPEN(type > SCT_TYPE_MAX || !dchr[type].d_mnem))
312         return '?';
313     if (owner_or_god
314         || type == SCT_WATER || type == SCT_MOUNT || type == SCT_WASTE
315         || (!own && (type == SCT_RURAL || type == SCT_PLAINS)))
316         return dchr[type].d_mnem;
317     return '?';
318 }
319
320 int
321 unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp)
322 {
323     struct shpstr origs;
324     struct lndstr origl;
325     struct plnstr origp;
326     struct nukstr orign;
327     struct empobj *gp;
328     struct range range;
329
330     if (unit_type == EF_LAND) {
331         if (!getland(uid, &origl) || !player->owner || origl.lnd_own == 0)
332             return RET_FAIL;
333         gp = (struct empobj *)&origl;
334         *originp = *lchr[(int)origl.lnd_type].l_name;
335     } else if (unit_type == EF_PLANE) {
336         if (!getplane(uid, &origp) || !player->owner || origp.pln_own == 0)
337             return RET_FAIL;
338         gp = (struct empobj *)&origp;
339         *originp = *plchr[(int)origp.pln_type].pl_name;
340     } else if (unit_type == EF_NUKE) {
341         if (!getnuke(uid, &orign) || !player->owner || orign.nuk_own == 0)
342             return RET_FAIL;
343         gp = (struct empobj *)&orign;
344         *originp = 'n';
345     } else {
346         if (!getship(uid, &origs) || !player->owner || origs.shp_own == 0)
347             return RET_FAIL;
348         gp = (struct empobj *)&origs;
349         *originp = *mchr[(int)origs.shp_type].m_name;
350     }
351
352     range.lx = xnorm(gp->x - 10);
353     range.hx = xnorm(gp->x + 11);
354     range.ly = ynorm(gp->y - 5);
355     range.hy = ynorm(gp->y + 6);
356     xysize_range(&range);
357     snxtsct_area(nsp, &range);
358     return RET_OK;
359 }
360
361 int
362 display_region_map(char *cmd, coord curx, coord cury, char *arg)
363 {
364     char coordinates[80], *cp;
365
366     player->argp[0] = cmd;
367     if (!arg || !*arg) {
368         struct natstr *np;
369
370         np = getnatp(player->cnum);
371         sprintf(coordinates, "%d:%d,%d:%d",
372             xrel(np, curx - 10), xrel(np, curx + 11),
373             yrel(np, cury - 5), yrel(np, cury + 6));
374         player->argp[1] = coordinates;
375         player->argp[2] = NULL;
376     } else {
377         player->argp[1] = arg;
378         cp = strchr(arg, ' ');
379         if (cp != NULL) {
380             *cp++  = '\0';
381             while (isspace(*cp)) cp++;
382             player->argp[2] = cp;
383         } else
384             player->argp[2] = NULL;
385     }
386     player->condarg = NULL;
387     return map();
388 }
389
390 int
391 bmaps_intersect(natid a, natid b)
392 {
393     char *mapa = ef_ptr(EF_MAP, a);
394     char *mapb = ef_ptr(EF_MAP, b);
395     int i;
396
397     for (i = 0; i < WORLD_X * WORLD_Y / 2; ++i, ++mapa, ++mapb)
398         if (*mapa && *mapa != ' ' && *mapb && *mapb != ' ')
399             return 1;
400     return 0;
401 }
402
403 /* Note that this requires that the BMAP is mapped into memory */
404
405 int
406 share_bmap(natid from, natid to, struct nstr_sect *ns, char des,
407            char *from_name)
408 {
409     char *from_bmap = ef_ptr(EF_BMAP, from);
410     char *to_bmap = ef_ptr(EF_BMAP, to);
411     int n = 0;
412     struct sctstr sect;
413     char fromdes;
414     char todes;
415     char from_des = *from_name;
416
417     if (isalpha(from_des))
418         from_des &= ~0x20;
419
420     while (nxtsct(ns, &sect)) {
421         if (!(fromdes = from_bmap[sctoff(ns->x, ns->y)]))
422             continue;
423         todes = to_bmap[sctoff(ns->x, ns->y)];
424         if (todes &&
425             todes != '?' &&
426             todes != '.' && todes != ' ' && todes != from_des)
427             continue;
428         if (sect.sct_own == from) {
429             if (fromdes != '=' && fromdes != 'h' && fromdes != des)
430                 fromdes = from_des;
431         }
432         if (todes == fromdes)
433             continue;
434         n += map_set(to, ns->x, ns->y, fromdes, 1);
435     }
436
437     if (n)
438         writebmap(to);
439     return n;
440 }