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