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