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