]> git.pond.sub.org Git - empserver/blob - src/lib/subs/maps.c
maps: Drop bmap flags syntax deprecated in 4.3.27
[empserver] / src / lib / subs / maps.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2015, 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 "match.h"
45 #include "misc.h"
46 #include "nat.h"
47 #include "nsc.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 /* Flags for draw_map() */
57 /* whether to put ships, planes, land units or nukes on the map */
58 #define MAP_SHIP        bit(0)
59 #define MAP_PLANE       bit(1)
60 #define MAP_LAND        bit(2)
61 #define MAP_NUKE        bit(3)
62 #define MAP_ALL         (MAP_SHIP | MAP_PLANE | MAP_LAND | MAP_NUKE)
63 /* whether to highlight own sectors */
64 #define MAP_HIGH        bit(4)
65 /* whether to draw a map or a bmap */
66 #define MAP_BMAP        bit(5)
67 /* whether to draw an alternate map: newdes for map, true bmap for bmap */
68 #define MAP_ALT         bit(6)
69 /* whether to revert bmap, internal to do_map() */
70 #define MAP_BMAP_REVERT bit(7)
71
72 static int parse_map_arg(int, char *, struct nstr_sect *, char *);
73 static int parse_map_flags(int, char *);
74 static int revert_bmap(struct nstr_sect *);
75 static int draw_map(char, int, struct nstr_sect *);
76 static int bmnxtsct(struct nstr_sect *);
77 static char map_char(int, natid, int);
78 static int unit_map(int, int, struct nstr_sect *, char *);
79 static void snxtsct_around(struct nstr_sect *, coord, coord);
80
81 int
82 do_map(int bmap, int unit_type, char *arg1, char *arg2)
83 {
84     struct nstr_sect ns;
85     char origin;
86     int res, map_flags;
87
88     res = parse_map_arg(unit_type, arg1, &ns, &origin);
89     if (res != RET_OK)
90         return res;
91
92     map_flags = parse_map_flags(bmap, arg2);
93     if (map_flags < 0)
94         return RET_SYN;
95
96     if (map_flags & MAP_BMAP_REVERT)
97         return revert_bmap(&ns);
98     return draw_map(origin, map_flags, &ns);
99 }
100
101 static int
102 parse_map_arg(int unit_type, char *arg,
103               struct nstr_sect *nsp, char *originp)
104 {
105     switch (sarg_type(arg)) {
106     case NS_DIST:
107     case NS_AREA:
108     case NS_ALL:
109         if (!snxtsct(nsp, arg))
110             return RET_SYN;
111         *originp = 0;
112         break;
113     default:
114         if (unit_map(unit_type, atoi(arg), nsp, originp) < 0) {
115             pr("No such %s\n", ef_nameof(unit_type));
116             return RET_FAIL;
117         }
118     }
119     return RET_OK;
120 }
121
122 static int
123 parse_map_flags(int bmap, char *str)
124 {
125     int map_flags;
126     char *p;
127
128     switch (bmap) {
129     default: CANT_REACH();
130         /* fall through */
131     case 'b': map_flags = MAP_BMAP; break;
132     case 'n': map_flags = MAP_ALT; break;
133     case 0:   map_flags = 0;
134     }
135
136     if (!str || !*str)
137         return map_flags;
138
139     /* special case "revert" */
140     if (bmap == 'b' && mineq(str, "revert") != ME_MISMATCH)
141         return MAP_BMAP_REVERT;
142
143     for (p = str; *p; p++) {
144         switch (*p) {
145         case 's':
146         case 'S':
147             map_flags |= MAP_SHIP;
148             break;
149         case 'l':
150         case 'L':
151             map_flags |= MAP_LAND;
152             break;
153         case 'p':
154         case 'P':
155             map_flags |= MAP_PLANE;
156             break;
157         case 'n':
158         case 'N':
159             map_flags |= MAP_NUKE;
160             break;
161         case 'h':
162         case 'H':
163             map_flags |= MAP_HIGH;
164             break;
165         case '*':
166             map_flags |= MAP_ALL;
167             break;
168         case 't':
169             if (bmap != 'b')
170                 goto bad_flag;
171             map_flags |= MAP_ALT;
172             break;
173         default:
174         bad_flag:
175             pr("Bad flag %c!\n", *p);
176             return -1;
177         }
178     }
179
180     return map_flags;
181 }
182
183 static int
184 revert_bmap(struct nstr_sect *nsp)
185 {
186     if (!confirm("Are you sure you want to revert your bmap? "))
187         return RET_FAIL;
188     while (bmnxtsct(nsp))
189         player->bmap[nsp->id] = player->map[nsp->id];
190     ef_write(EF_BMAP, player->cnum, player->bmap);
191     return RET_OK;
192 }
193
194 static int
195 draw_map(char origin, int map_flags, struct nstr_sect *nsp)
196 {
197     struct natstr *np;
198     struct range range;
199     struct nstr_item ni;
200     union empobj_storage unit;
201     coord x, y;
202     int i;
203     /* Note this is not re-entrant anyway, so we keep the buffers
204        around */
205     static unsigned char *bitmap = NULL;
206     static char *wmapbuf = NULL;
207     static char **wmap = NULL;
208     static int ef_mappable[] = { EF_PLANE, EF_SHIP, EF_LAND, EF_NUKE, EF_BAD };
209     static int ef_unit_map[] = { MAP_PLANE, MAP_SHIP, MAP_LAND, MAP_NUKE };
210     char *name;
211
212     if (!wmapbuf)
213         wmapbuf = malloc(WORLD_Y * MAPWIDTH(1));
214     if (!wmap) {
215         wmap = malloc(WORLD_Y * sizeof(char *));
216         if (wmap && wmapbuf) {
217             for (i = 0; i < WORLD_Y; i++)
218                 wmap[i] = &wmapbuf[MAPWIDTH(1) * i];
219         } else if (wmap) {
220             free(wmap);
221             wmap = NULL;
222         }
223     }
224     if (!bitmap)
225         bitmap = malloc((WORLD_SZ() + 7) / 8);
226     if (!wmapbuf || !wmap || !bitmap) {
227         pr("Memory error, tell the deity.\n");
228         logerror("malloc failed in draw_map\n");
229         return RET_FAIL;
230     }
231
232     if (!(player->command->c_flags & C_MOD)) {
233         logerror("%s command needs C_MOD flag set",
234                  player->command->c_form);
235         player->command->c_flags |= C_MOD;
236     }
237     np = getnatp(player->cnum);
238     /* zap any conditionals */
239     nsp->ncond = 0;
240     xyrelrange(np, &nsp->range, &range);
241     border(&range, "     ", "");
242     blankfill(wmapbuf, &nsp->range, 1);
243
244     if (map_flags & MAP_BMAP) {
245         char *map = map_flags & MAP_ALT ? player->map : player->bmap;
246
247         while (bmnxtsct(nsp)) {
248             if (map[nsp->id])
249                 wmap[nsp->dy][nsp->dx] = map[nsp->id];
250         }
251     } else {
252         struct sctstr sect;
253         char mapch;
254         int changed = 0;
255
256         if (!player->god) {
257             memset(bitmap, 0, (WORLD_SZ() + 7) / 8);
258             bitinit2(nsp, bitmap, player->cnum);
259         }
260
261         while (nxtsct(nsp, &sect)) {
262             if (!player->god && !emp_getbit(nsp->x, nsp->y, bitmap))
263                 continue;
264             mapch = map_char(map_flags & MAP_ALT
265                              ? sect.sct_newtype : sect.sct_type,
266                              sect.sct_own, player->owner);
267             wmap[nsp->dy][nsp->dx] = mapch;
268             if (!(map_flags & MAP_ALT))
269                 changed |= map_set(player->cnum, nsp->x, nsp->y, mapch, 0);
270         }
271         if (changed)
272             writemap(player->cnum);
273     }
274
275     i = 0;
276     while (ef_mappable[i] != EF_BAD) {
277         if (map_flags & ef_unit_map[i]) {
278             snxtitem_area(&ni, ef_mappable[i], &nsp->range);
279             while (nxtitem(&ni, &unit)) {
280                 if (unit.gen.own == 0)
281                     continue;
282                 if (unit.gen.own != player->cnum && !player->god)
283                     continue;
284
285                 x = deltx(&nsp->range, unit.gen.x);
286                 y = delty(&nsp->range, unit.gen.y);
287
288                 if (ef_mappable[i] == EF_NUKE)
289                     wmap[y][x] = 'N';
290                 else {
291                     name = empobj_chr_name(&unit.gen);
292                     wmap[y][x] = *name & ~0x20;
293                 }
294             }
295         }
296         i++;
297     }
298     if (map_flags & MAP_HIGH) {
299         struct sctstr sect;
300
301         snxtsct_rewind(nsp);
302         while (nxtsct(nsp, &sect)) {
303             if (sect.sct_own == player->cnum)
304                  wmap[nsp->dy][nsp->dx] |= 0x80;
305         }
306     }
307     if (origin)
308         wmap[5][10] = origin & ~0x20;
309     for (y = nsp->range.ly, i = 0; i < nsp->range.height; y++, i++) {
310         int yval;
311
312         yval = yrel(np, y);
313         wmap[i][nsp->range.width] = '\0';
314         pr("%4d %s %d\n", yval, wmap[i], yval);
315         if (y >= WORLD_Y)
316             y -= WORLD_Y;
317     }
318     border(&range, "     ", "");
319     return RET_OK;
320 }
321
322 /*
323  * get the next sector in the range
324  */
325 static int
326 bmnxtsct(struct nstr_sect *np)
327 {
328     while (1) {
329         np->dx++;
330         np->x++;
331         if (np->x >= WORLD_X)
332             np->x = 0;
333         if (np->dx >= np->range.width) {
334             np->dx = 0;
335             np->x = np->range.lx;
336             np->dy++;
337             if (np->dy >= np->range.height)
338                 return 0;
339             np->y++;
340             if (np->y >= WORLD_Y)
341                 np->y = 0;
342         }
343         if ((np->y + np->x) & 01)
344             continue;
345         if (np->type == NS_DIST) {
346             np->curdist = mapdist(np->x, np->y, np->cx, np->cy);
347             if (np->curdist > np->dist)
348                 continue;
349         }
350         np->id = sctoff(np->x, np->y);
351         return 1;
352     }
353 }
354
355 /*
356  * Return character to use in maps for sector type TYPE owned by OWN.
357  * If OWNER_OR_GOD, the map is for the sector's owner or a deity.
358  */
359 static char
360 map_char(int type, natid own, int owner_or_god)
361 {
362     if (CANT_HAPPEN(type > SCT_TYPE_MAX || !dchr[type].d_mnem))
363         return '?';
364     if (owner_or_god
365         || type == SCT_WATER || type == SCT_MOUNT || type == SCT_WASTE
366         || (!own && (type == SCT_RURAL || type == SCT_PLAINS)))
367         return dchr[type].d_mnem;
368     return '?';
369 }
370
371 static int
372 unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp)
373 {
374     union empobj_storage unit;
375     char *name;
376
377     if (CANT_HAPPEN((ef_flags(unit_type) & (EFF_OWNER | EFF_XY))
378                     != (EFF_OWNER | EFF_XY)))
379         return -1;
380
381     if (!get_empobj(unit_type, uid, &unit))
382         return -1;
383     if (!player->owner || unit.gen.own == 0)
384         return -1;
385
386     if (unit_type == EF_NUKE)
387         *originp = 'n';
388     else {
389         name = empobj_chr_name(&unit.gen);
390         *originp = *name;
391     }
392
393     snxtsct_around(nsp, unit.gen.x, unit.gen.y);
394     return 0;
395 }
396
397 static void
398 snxtsct_around(struct nstr_sect *nsp, coord x, coord y)
399 {
400     struct range range;
401
402     range.lx = xnorm(x - 10);
403     range.hx = xnorm(x + 10);
404     range.ly = ynorm(y - 5);
405     range.hy = ynorm(y + 5);
406     xysize_range(&range);
407     snxtsct_area(nsp, &range);
408 }
409
410 int
411 display_region_map(int bmap, int unit_type, coord curx, coord cury,
412                    char *arg1, char *arg2)
413 {
414     struct nstr_sect ns;
415     char origin;
416     int res, map_flags;
417
418     if (arg1 && *arg1) {
419         res = parse_map_arg(unit_type, arg1, &ns, &origin);
420         if (res != RET_OK)
421             return res;
422
423         map_flags = parse_map_flags(bmap, arg2);
424         if (map_flags < 0)
425             return RET_SYN;
426     } else {
427         snxtsct_around(&ns, curx, cury);
428         map_flags = 0;
429         origin = 0;
430     }
431
432     if (map_flags & MAP_BMAP_REVERT)
433         return revert_bmap(&ns);
434     return draw_map(origin, map_flags, &ns);
435 }
436
437 int
438 nav_map(int x, int y, int show_designations)
439 {
440     char *ptr;
441     struct nstr_sect ns;
442     struct sctstr sect;
443     int i;
444     /* Note this is not re-entrant anyway, so we keep the buffers
445        around */
446     static char *wmapbuf = NULL;
447     static char **wmap = NULL;
448     int changed = 0;
449
450     if (!wmapbuf)
451         wmapbuf = malloc(WORLD_Y * MAPWIDTH(1));
452     if (!wmap) {
453         wmap = malloc(WORLD_Y * sizeof(*wmap));
454         if (wmap && wmapbuf) {
455             for (i = 0; i < WORLD_Y; i++)
456                 wmap[i] = &wmapbuf[MAPWIDTH(1) * i];
457         } else if (wmap) {
458             free(wmap);
459             wmap = NULL;
460         }
461     }
462     if (!wmapbuf || !wmap) {
463         pr("Memory error, tell the deity.\n");
464         logerror("malloc failed in navi\n");
465         return RET_FAIL;
466     }
467     snxtsct_dist(&ns, x, y, 1);
468     blankfill(wmapbuf, &ns.range, 1);
469     while (nxtsct(&ns, &sect)) {
470         ptr = &wmap[ns.dy][ns.dx];
471         *ptr = dchr[sect.sct_type].d_mnem;
472         if (!show_designations &&
473             sect.sct_own != player->cnum &&
474             sect.sct_type != SCT_WATER &&
475             sect.sct_type != SCT_BSPAN && sect.sct_type != SCT_HARBR)
476             *ptr = '?';
477         changed += map_set(player->cnum, sect.sct_x, sect.sct_y, *ptr, 0);
478         /*
479          * We do it this way so that 'x' and 'X'
480          * bdesignations will show up. This can
481          * be used to mark mined sectors. So, the
482          * player will see the current des, UNLESS
483          * they've marked the sector 'x' or 'X',
484          * in which case they'll see that.
485          * --ts
486          */
487         *ptr = player->bmap[sect.sct_uid];
488     }
489     if (changed)
490         writemap(player->cnum);
491     for (i = 0; i < ns.range.height; i++)
492         pr("%s\n", wmap[i]);
493     return RET_OK;
494 }
495
496 int
497 bmaps_intersect(natid a, natid b)
498 {
499     char *mapa = ef_ptr(EF_MAP, a);
500     char *mapb = ef_ptr(EF_MAP, b);
501     int i;
502
503     for (i = 0; i < WORLD_SZ(); i++)
504         if (mapa[i] && mapa[i] != ' ' && mapb[i] && mapb[i] != ' ')
505             return 1;
506     return 0;
507 }
508
509 /* Note that this requires that the BMAP is mapped into memory */
510
511 int
512 share_bmap(natid from, natid to, struct nstr_sect *ns, char des,
513            char *from_name)
514 {
515     char *from_bmap = ef_ptr(EF_BMAP, from);
516     char *to_bmap = ef_ptr(EF_BMAP, to);
517     int n = 0;
518     struct sctstr sect;
519     char fromdes;
520     char todes;
521     char from_des = *from_name;
522
523     if (from == to)
524         return 0;
525
526     if (isalpha(from_des))
527         from_des &= ~0x20;
528
529     while (nxtsct(ns, &sect)) {
530         if (!(fromdes = from_bmap[sect.sct_uid]))
531             continue;
532         todes = to_bmap[sect.sct_uid];
533         if (todes &&
534             todes != '?' &&
535             todes != '.' && todes != ' ' && todes != from_des)
536             continue;
537         if (sect.sct_own == from) {
538             if (fromdes != '=' && fromdes != 'h' && fromdes != des)
539                 fromdes = from_des;
540         }
541         if (todes == fromdes)
542             continue;
543         n += map_set(to, ns->x, ns->y, fromdes, 1);
544     }
545
546     if (n)
547         writebmap(to);
548     return n;
549 }