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