]> git.pond.sub.org Git - empserver/blob - src/lib/subs/maps.c
Use get_empobj() instead of get_empobjp() in unit_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 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                     if ((name = emp_obj_chr_name(&unit.gen)) == NULL)
258                         return RET_FAIL;
259                     wmap[y][x] = *name & ~0x20;
260                 }
261             }
262         }
263         i++;
264     }
265     if (map_flags & MAP_HIGH) {
266         struct sctstr sect;
267
268         snxtsct_rewind(nsp);
269         if (!player->god) {
270             memset(bitmap, 0, (WORLD_X * WORLD_Y) / 8);
271             bitinit2(nsp, bitmap, player->cnum);
272         }
273         while (nxtsct(nsp, &sect) && !player->aborted) {
274             if (!player->god && !emp_getbit(nsp->x, nsp->y, bitmap))
275                 continue;
276             if (sect.sct_own == player->cnum)
277                  wmap[nsp->dy][nsp->dx] |= 0x80;
278         }
279     }
280     if (origin)
281         wmap[5][10] = origin & ~0x20;
282     for (y = nsp->range.ly, i = 0; i < nsp->range.height; y++, i++) {
283         int yval;
284
285         yval = yrel(np, y);
286         wmap[i][nsp->range.width] = '\0';
287         pr("%4d %s %-4d\n", yval, wmap[i], yval);
288         if (y >= WORLD_Y)
289             y -= WORLD_Y;
290     }
291     border(&range, "     ", "");
292     return RET_OK;
293 }
294
295 /*
296  * get the next sector in the range
297  */
298 static int
299 bmnxtsct(struct nstr_sect *np)
300 {
301     while (1) {
302         np->dx++;
303         np->x++;
304         if (np->x >= WORLD_X)
305             np->x = 0;
306         if (np->dx >= np->range.width) {
307             np->dx = 0;
308             np->x = np->range.lx;
309             np->dy++;
310             if (np->dy >= np->range.height)
311                 return 0;
312             np->y++;
313             if (np->y >= WORLD_Y)
314                 np->y = 0;
315         }
316         if ((np->y + np->x) & 01)
317             continue;
318         if (np->type == NS_DIST) {
319             np->curdist = mapdist(np->x, np->y, np->cx, np->cy);
320             if (np->curdist > np->dist)
321                 continue;
322         }
323         np->id = sctoff(np->x, np->y);
324         return 1;
325     }
326     /*NOTREACHED*/
327 }
328
329 /*
330  * Return character to use in maps for sector type TYPE owned by OWN.
331  * If OWNER_OR_GOD, the map is for the sector's owner or a deity.
332  */
333 static char
334 map_char(int type, natid own, int owner_or_god)
335 {
336     if (CANT_HAPPEN(type > SCT_TYPE_MAX || !dchr[type].d_mnem))
337         return '?';
338     if (owner_or_god
339         || type == SCT_WATER || type == SCT_MOUNT || type == SCT_WASTE
340         || (!own && (type == SCT_RURAL || type == SCT_PLAINS)))
341         return dchr[type].d_mnem;
342     return '?';
343 }
344
345 static int
346 unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp)
347 {
348     union empobj_storage unit;
349     struct range range;
350     char *name;
351
352     if (!get_empobj(unit_type, uid, &unit))
353         return RET_FAIL;
354     if (!player->owner || unit.gen.own == 0)
355         return RET_FAIL;
356
357     if (unit_type == EF_NUKE)
358         *originp = 'n';
359     else {
360         if ((name = emp_obj_chr_name(&unit.gen)) == NULL)
361             return RET_FAIL;
362         *originp = *name;
363     }
364
365     range.lx = xnorm(unit.gen.x - 10);
366     range.hx = xnorm(unit.gen.x + 11);
367     range.ly = ynorm(unit.gen.y - 5);
368     range.hy = ynorm(unit.gen.y + 6);
369     xysize_range(&range);
370     snxtsct_area(nsp, &range);
371     return RET_OK;
372 }
373
374 int
375 display_region_map(int bmap, int unit_type, coord curx, coord cury,
376                    char *arg)
377 {
378     char coordinates[80];
379     char *map_flag_arg;
380
381     if (!arg || !*arg) {
382         struct natstr *np;
383
384         np = getnatp(player->cnum);
385         sprintf(coordinates, "%d:%d,%d:%d",
386             xrel(np, curx - 10), xrel(np, curx + 11),
387             yrel(np, cury - 5), yrel(np, cury + 6));
388         arg = coordinates;
389         map_flag_arg = NULL;
390     } else {
391         map_flag_arg = strchr(arg, ' ');
392         if (map_flag_arg != NULL) {
393             *map_flag_arg++  = '\0';
394             while (isspace(*map_flag_arg)) map_flag_arg++;
395         }
396     }
397     player->condarg = NULL;
398     return do_map(bmap, unit_type, arg, map_flag_arg);
399 }
400
401 int
402 bmaps_intersect(natid a, natid b)
403 {
404     char *mapa = ef_ptr(EF_MAP, a);
405     char *mapb = ef_ptr(EF_MAP, b);
406     int i;
407
408     for (i = 0; i < WORLD_SZ(); i++)
409         if (mapa[i] && mapa[i] != ' ' && mapb[i] && mapb[i] != ' ')
410             return 1;
411     return 0;
412 }
413
414 /* Note that this requires that the BMAP is mapped into memory */
415
416 int
417 share_bmap(natid from, natid to, struct nstr_sect *ns, char des,
418            char *from_name)
419 {
420     char *from_bmap = ef_ptr(EF_BMAP, from);
421     char *to_bmap = ef_ptr(EF_BMAP, to);
422     int n = 0;
423     struct sctstr sect;
424     char fromdes;
425     char todes;
426     char from_des = *from_name;
427
428     if (isalpha(from_des))
429         from_des &= ~0x20;
430
431     while (nxtsct(ns, &sect)) {
432         if (!(fromdes = from_bmap[sctoff(ns->x, ns->y)]))
433             continue;
434         todes = to_bmap[sctoff(ns->x, ns->y)];
435         if (todes &&
436             todes != '?' &&
437             todes != '.' && todes != ' ' && todes != from_des)
438             continue;
439         if (sect.sct_own == from) {
440             if (fromdes != '=' && fromdes != 'h' && fromdes != des)
441                 fromdes = from_des;
442         }
443         if (todes == fromdes)
444             continue;
445         n += map_set(to, ns->x, ns->y, fromdes, 1);
446     }
447
448     if (n)
449         writebmap(to);
450     return n;
451 }