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