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