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