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