]> git.pond.sub.org Git - empserver/blob - src/lib/subs/maps.c
Simplify draw_map()
[empserver] / src / lib / subs / maps.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, 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 draw_map(int, char, int, struct nstr_sect *);
57 static int bmnxtsct(struct nstr_sect *);
58 static char map_char(int, natid, int);
59 static int unit_map(int, int, struct nstr_sect *, char *);
60
61 int
62 do_map(int bmap, int unit_type, char *arg, char *map_flags_arg)
63 {
64     struct nstr_sect ns;
65     char origin = '\0';
66     char *b;
67     int map_flags = 0;
68
69     if (!snxtsct(&ns, arg)) {
70         if (unit_map(unit_type, atoi(arg), &ns, &origin))
71             return RET_FAIL;
72     }
73     for (b = map_flags_arg; b && *b; b++) {
74         switch (*b) {
75         case 's':
76         case 'S':
77             map_flags |= MAP_SHIP;
78             break;
79         case 'l':
80         case 'L':
81             map_flags |= MAP_LAND;
82             break;
83         case 'p':
84         case 'P':
85             map_flags |= MAP_PLANE;
86             break;
87         case 'n':
88         case 'N':
89             map_flags |= MAP_NUKE;
90             break;
91         case 'h':
92         case 'H':
93             map_flags |= MAP_HIGH;
94             break;
95         case '*':
96             map_flags |= MAP_ALL;
97             break;
98         case 't':
99             if (bmap != 'b')
100                 goto bad_flag;
101             bmap = 't';
102             *(b + 1) = 0;
103             break;
104         case 'r':
105             if (bmap != 'b')
106                 goto bad_flag;
107             bmap = 'r';
108             *(b + 1) = 0;
109             break;
110         default:
111         bad_flag:
112             pr("Bad flag %c!\n", *b);
113             break;
114         }
115     }
116     return draw_map(bmap, origin, map_flags, &ns);
117 }
118
119 static int
120 draw_map(int bmap, char origin, int map_flags, struct nstr_sect *nsp)
121 {
122     struct natstr *np;
123     struct range range;
124     struct nstr_item ni;
125     union empobj_storage unit;
126     coord x, y;
127     int i;
128     /* Note this is not re-entrant anyway, so we keep the buffers
129        around */
130     static unsigned char *bitmap = NULL;
131     static char *wmapbuf = NULL;
132     static char **wmap = NULL;
133     static int ef_mappable[] = { EF_PLANE, EF_SHIP, EF_LAND, EF_NUKE, EF_BAD };
134     static int ef_unit_map[] = { MAP_PLANE, MAP_SHIP, MAP_LAND, MAP_NUKE };
135     char *name;
136
137     if (!wmapbuf)
138         wmapbuf = malloc(WORLD_Y * MAPWIDTH(1));
139     if (!wmap) {
140         wmap = malloc(WORLD_Y * sizeof(char *));
141         if (wmap && wmapbuf) {
142             for (i = 0; i < WORLD_Y; i++)
143                 wmap[i] = &wmapbuf[MAPWIDTH(1) * i];
144         } else if (wmap) {
145             free(wmap);
146             wmap = NULL;
147         }
148     }
149     if (!bitmap)
150         bitmap = malloc((WORLD_X * WORLD_Y) / 8);
151     if (!wmapbuf || !wmap || !bitmap) {
152         pr("Memory error, tell the deity.\n");
153         logerror("malloc failed in draw_map\n");
154         return RET_FAIL;
155     }
156
157     if (bmap == 'r') {
158         if (!confirm("Are you sure you want to revert your bmap? "))
159             return RET_OK;
160     }
161     if (!(player->command->c_flags & C_MOD)) {
162         logerror("%s command needs C_MOD flag set",
163                  player->command->c_form);
164         player->command->c_flags |= C_MOD;
165     }
166     np = getnatp(player->cnum);
167     /* zap any conditionals */
168     nsp->ncond = 0;
169     xyrelrange(np, &nsp->range, &range);
170     border(&range, "     ", "");
171     blankfill(wmapbuf, &nsp->range, 1);
172     if (bmap) {
173         int c;
174         switch (bmap) {
175         default:
176             CANT_REACH();
177             bmap = 'b';
178             /* fall through */
179         case 'b':
180             while (bmnxtsct(nsp) && !player->aborted) {
181                 if (0 != (c = player->bmap[nsp->id]))
182                     wmap[nsp->dy][nsp->dx] = c;
183             }
184             break;
185         case 't':
186             while (bmnxtsct(nsp) && !player->aborted) {
187                 if (0 != (c = player->map[nsp->id]))
188                     wmap[nsp->dy][nsp->dx] = c;
189             }
190             break;
191         case 'r':
192             while (bmnxtsct(nsp) && !player->aborted) {
193                 player->bmap[nsp->id] =
194                     player->map[nsp->id];
195                 if (0 != (c = player->bmap[nsp->id]))
196                     wmap[nsp->dy][nsp->dx] = c;
197             }
198             ef_write(EF_BMAP, player->cnum, player->bmap);
199             break;
200         case 'n':
201             {
202                 struct sctstr sect;
203
204                 if (!player->god) {
205                     memset(bitmap, 0, (WORLD_X * WORLD_Y) / 8);
206                     bitinit2(nsp, bitmap, player->cnum);
207                 }
208                 while (nxtsct(nsp, &sect) && !player->aborted) {
209                     if (!player->god && !emp_getbit(nsp->x, nsp->y, bitmap))
210                         continue;
211                     wmap[nsp->dy][nsp->dx]
212                         = map_char(sect.sct_newtype, sect.sct_own,
213                                    player->owner);
214                 }
215                 break;
216             }
217         }
218     } else {
219         struct sctstr sect;
220         char mapch;
221         int changed = 0;
222
223         if (!player->god) {
224             memset(bitmap, 0, (WORLD_X * WORLD_Y) / 8);
225             bitinit2(nsp, bitmap, player->cnum);
226         }
227         while (nxtsct(nsp, &sect) && !player->aborted) {
228             if (!player->god && !emp_getbit(nsp->x, nsp->y, bitmap))
229                 continue;
230             mapch = map_char(sect.sct_type, sect.sct_own, player->owner);
231             wmap[nsp->dy][nsp->dx] = mapch;
232             changed |= map_set(player->cnum, nsp->x, nsp->y, mapch, 0);
233         }
234         if (changed)
235             writemap(player->cnum);
236     }
237     if (player->aborted)
238         return RET_OK;
239
240     i = 0;
241     while (ef_mappable[i] != EF_BAD) {
242         if (map_flags & ef_unit_map[i]) {
243             snxtitem_area(&ni, ef_mappable[i], &nsp->range);
244             while (nxtitem(&ni, &unit)) {
245                 if (unit.gen.own == 0)
246                     continue;
247                 if (unit.gen.own != player->cnum && !player->god)
248                     continue;
249
250                 x = deltx(&nsp->range, unit.gen.x);
251                 y = delty(&nsp->range, unit.gen.y);
252
253                 if (ef_mappable[i] == EF_NUKE)
254                     wmap[y][x] = 'N';
255                 else {
256                     name = empobj_chr_name(&unit.gen);
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(int 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 static int
344 unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp)
345 {
346     union empobj_storage unit;
347     struct range range;
348     char *name;
349
350     if (CANT_HAPPEN((ef_flags(unit_type) & (EFF_OWNER | EFF_XY))
351                     != (EFF_OWNER | EFF_XY)))
352         return RET_FAIL;
353
354     if (!get_empobj(unit_type, uid, &unit))
355         return RET_FAIL;
356     if (!player->owner || unit.gen.own == 0)
357         return RET_FAIL;
358
359     if (unit_type == EF_NUKE)
360         *originp = 'n';
361     else {
362         name = empobj_chr_name(&unit.gen);
363         *originp = *name;
364     }
365
366     range.lx = xnorm(unit.gen.x - 10);
367     range.hx = xnorm(unit.gen.x + 10);
368     range.ly = ynorm(unit.gen.y - 5);
369     range.hy = ynorm(unit.gen.y + 5);
370     xysize_range(&range);
371     snxtsct_area(nsp, &range);
372     return RET_OK;
373 }
374
375 int
376 display_region_map(int bmap, int unit_type, coord curx, coord cury,
377                    char *arg)
378 {
379     char coordinates[80];
380     char *map_flag_arg;
381
382     if (!arg || !*arg) {
383         struct natstr *np;
384
385         np = getnatp(player->cnum);
386         sprintf(coordinates, "%d:%d,%d:%d",
387             xrel(np, curx - 10), xrel(np, curx + 11),
388             yrel(np, cury - 5), yrel(np, cury + 6));
389         arg = coordinates;
390         map_flag_arg = NULL;
391     } else {
392         map_flag_arg = strchr(arg, ' ');
393         if (map_flag_arg != NULL) {
394             *map_flag_arg++  = '\0';
395             while (isspace(*map_flag_arg)) map_flag_arg++;
396         }
397     }
398     player->condarg = NULL;
399     return do_map(bmap, unit_type, arg, map_flag_arg);
400 }
401
402 int
403 bmaps_intersect(natid a, natid b)
404 {
405     char *mapa = ef_ptr(EF_MAP, a);
406     char *mapb = ef_ptr(EF_MAP, b);
407     int i;
408
409     for (i = 0; i < WORLD_SZ(); i++)
410         if (mapa[i] && mapa[i] != ' ' && mapb[i] && mapb[i] != ' ')
411             return 1;
412     return 0;
413 }
414
415 /* Note that this requires that the BMAP is mapped into memory */
416
417 int
418 share_bmap(natid from, natid to, struct nstr_sect *ns, char des,
419            char *from_name)
420 {
421     char *from_bmap = ef_ptr(EF_BMAP, from);
422     char *to_bmap = ef_ptr(EF_BMAP, to);
423     int n = 0;
424     struct sctstr sect;
425     char fromdes;
426     char todes;
427     char from_des = *from_name;
428
429     if (isalpha(from_des))
430         from_des &= ~0x20;
431
432     while (nxtsct(ns, &sect)) {
433         if (!(fromdes = from_bmap[sect.sct_uid]))
434             continue;
435         todes = to_bmap[sect.sct_uid];
436         if (todes &&
437             todes != '?' &&
438             todes != '.' && todes != ' ' && todes != from_des)
439             continue;
440         if (sect.sct_own == from) {
441             if (fromdes != '=' && fromdes != 'h' && fromdes != des)
442                 fromdes = from_des;
443         }
444         if (todes == fromdes)
445             continue;
446         n += map_set(to, ns->x, ns->y, fromdes, 1);
447     }
448
449     if (n)
450         writebmap(to);
451     return n;
452 }