]> git.pond.sub.org Git - empserver/blob - src/lib/subs/move.c
Inline BestLandPath(), BestDistPath() glue
[empserver] / src / lib / subs / move.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  *  move.c: Move something somewhere.
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2004-2011
31  */
32
33 #include <config.h>
34
35 #include <ctype.h>
36 #include "damage.h"
37 #include "file.h"
38 #include "map.h"
39 #include "path.h"
40 #include "player.h"
41 #include "prototypes.h"
42 #include "sect.h"
43
44 static int move_map(coord curx, coord cury, char *arg);
45
46 int
47 move_ground(struct sctstr *start, struct sctstr *end,
48             double weight, char *path,
49             int (*map)(coord, coord, char *), int exploring,
50             int *dam)
51 {
52     struct sctstr sect, ending_sect;
53     struct sctstr next, dsect;
54     coord curx, cury, oldx, oldy;
55     coord tmpx, tmpy;
56     coord dx, dy;
57     char *movstr;
58     double sect_mcost;
59     double total_mcost;
60     double mv_cost;
61     size_t len;
62     double mobility = start->sct_mobil;
63     int dir;
64     int intcost;
65     int takedam = *dam;
66     int out = 0;
67     char bpath[1024];
68     char buf2[1024];
69     char prompt[128];
70     char buf[1024];
71
72     *end = *start;
73     if (mobility <= 0.0)
74         return -1;
75     *dam = 0;
76     if (path && sarg_xy(path, &dx, &dy) && getsect(dx, dy, &ending_sect)) {
77         if ((ending_sect.sct_x == start->sct_x) &&
78             (ending_sect.sct_y == start->sct_y)) {
79             pr("Start sector is ending sector!\n");
80             return -1;
81         }
82         pr("Looking for best path to %s\n", path);
83         buf2[0] = 0;
84         total_mcost = path_find(start->sct_x, start->sct_y,
85                                 ending_sect.sct_x, ending_sect.sct_y,
86                                 start->sct_own, MOB_MOVE);
87         if (total_mcost < 0) {
88             total_mcost = 0;
89             path = NULL;
90         } else {
91             len = path_find_route(buf2, 1024,
92                                   start->sct_x, start->sct_y,
93                                   ending_sect.sct_x, ending_sect.sct_y);
94             if (len + 1 >= 1024)
95                 path = NULL;
96             else {
97                 strcpy(buf2 + len, "h");
98                 path = buf2;
99             }
100         }
101         if (exploring && path)  /* take off the 'h' */
102             path[strlen(path) - 1] = '\0';
103         if (!path)
104             pr("No owned path exists!\n");
105         else {
106             pr("Using best path '%s', movement cost %1.3f\n",
107                path, total_mcost);
108             strncpy(bpath, path, sizeof(bpath));
109             path = bpath;
110         }
111         if ((total_mcost * weight) > mobility) {
112             pr("Not enough mobility to go all the way. Nothing moved.\n");
113             return -1;
114         }
115     }
116     movstr = path;
117     curx = start->sct_x;
118     cury = start->sct_y;
119     total_mcost = 0.0;
120     if (getsect(curx, cury, &sect) < 0) {
121         logerror("move_path: getsect %d,%d", curx, cury);
122         return -1;
123     }
124     for (;;) {
125         oldx = curx;
126         oldy = cury;
127         if (!movstr || *movstr == 0) {
128             if (exploring) {
129                 map(curx, cury, NULL);
130             } else {
131                 move_map(curx, cury, NULL);
132             }
133             sprintf(prompt, "<%.1f: %c %s> ", mobility,
134                     dchr[sect.sct_type].d_mnem,
135                     xyas(sect.sct_x, sect.sct_y, player->cnum));
136             movstr = getstring(prompt, buf);
137         }
138         if (movstr && sarg_xy(movstr, &dx, &dy)) {
139             if (getsect(dx, dy, &dsect)) {
140                 buf2[0] = 0;
141                 mv_cost = path_find(sect.sct_x, sect.sct_y,
142                                     dsect.sct_x, dsect.sct_y,
143                                     sect.sct_own, MOB_MOVE);
144                 if (mv_cost < 0) {
145                     mv_cost = 0;
146                     movstr = NULL;
147                 } else {
148                     len = path_find_route(buf2, 1024,
149                                           sect.sct_x, sect.sct_y,
150                                           dsect.sct_x, dsect.sct_y);
151                     if (len + 1 >= 1024)
152                         movstr = NULL;
153                     else {
154                         strcpy(buf2 + len, "h");
155                         movstr = buf2;
156                     }
157                 }
158             } else {
159                 pr("Invalid destination sector!\n");
160                 movstr = NULL;
161             }
162
163             if (movstr == NULL) {
164                 pr("Can't get to %s from here!\n",
165                    xyas(dx, dy, player->cnum));
166             } else {
167                 if ((mv_cost * weight) > mobility) {
168                     pr("Not enough mobility to go all the way. Nothing moved.\n");
169                     movstr = NULL;
170                 } else {
171                     pr("Using best path '%s', movement cost %1.3f\n",
172                        movstr, mv_cost);
173                     strncpy(bpath, movstr, sizeof(bpath));
174                     movstr = bpath;
175                 }
176             }
177         }
178         if (!movstr || *movstr == 0) {
179             buf2[0] = dirch[DIR_STOP];
180             buf2[1] = 0;
181             movstr = buf2;
182         }
183         if ((dir = chkdir(*movstr, DIR_STOP, DIR_MAP)) < 0) {
184             pr("\"%c\" is not legal...", *movstr);
185             direrr("'%c' to stop ", "'%c' to view ", "& '%c' to map\n");
186             *movstr = 0;
187             continue;
188         }
189         do  movstr++; while (isspace(*movstr));
190         if (dir == DIR_MAP) {
191             if (!exploring)
192                 map(curx, cury, movstr);
193             *movstr = 0;
194             continue;
195         } else if (dir == DIR_STOP)
196             break;
197         else if (dir == DIR_VIEW) {
198             pr("%d%% %s with %d civilians.\n", sect.sct_effic,
199                dchr[sect.sct_type].d_name, sect.sct_item[I_CIVIL]);
200             continue;
201         }
202         /*
203          * now see if we can move into the
204          * next sector.  Mobility, terrain,
205          * or ownership may prevent us.
206          */
207         tmpx = curx + diroff[dir][0];
208         tmpy = cury + diroff[dir][1];
209         if (getsect(tmpx, tmpy, &next) < 0) {
210             pr("You can't go there...\n");
211             *movstr = 0;
212             continue;
213         }
214         if (!player->god) {
215             if ((next.sct_type == SCT_SANCT) &&
216                 (next.sct_own != player->cnum)) {
217                 pr("Converts, huh?\n");
218                 *movstr = 0;
219                 continue;
220             }
221             sect_mcost = sector_mcost(&next, MOB_MOVE);
222             if ((!player->owner && (!exploring
223                                     || next.sct_item[I_MILIT]
224                                     || next.sct_item[I_CIVIL]))
225                 || sect_mcost == -1.0) {
226                 /* already-owned, or prohibited terrain */
227                 pr("You can't go there...\n");
228                 *movstr = 0;
229                 continue;
230             }
231             sect_mcost *= weight;
232             if (sect_mcost > mobility) {
233                 pr("Not enough mobility.  ");
234                 pr("You can't go there...\n");
235                 *movstr = 0;
236                 continue;
237             }
238             mobility -= sect_mcost;
239             total_mcost += sect_mcost;
240         }
241         curx = next.sct_x;
242         cury = next.sct_y;
243         if (cury != start->sct_y)
244             out = 1;
245         if (curx != start->sct_x)
246             out = 1;
247
248         sect = next;
249
250         if (takedam)
251             *dam += check_lmines(sect.sct_x, sect.sct_y, weight);
252         if (*dam >= 100)
253             break;
254         /*
255          * Check and see if anyone will interdict us
256          */
257         if (takedam && chance(weight / 100.0) &&
258             ((curx != oldx) || (cury != oldy)))
259             *dam += ground_interdict(curx, cury, player->cnum,
260                                      "commodities");
261         if (*dam >= 100)
262             break;
263     }
264     *end = sect;
265     intcost = (int)total_mcost;
266     if (intcost < 0)
267         return -1;
268     if ((start->sct_x == end->sct_x) && (start->sct_y == end->sct_y)
269         && !out)
270         return -1;
271
272     if (chance(total_mcost - intcost))
273         intcost++;
274     return intcost;
275 }
276
277
278 /*ARGSUSED*/
279 static int
280 move_map(coord curx, coord cury, char *arg)
281 {
282     struct nstr_sect ns;
283     struct sctstr sect;
284     char view[7];
285     int i;
286     int changed = 0;
287
288     snxtsct_dist(&ns, curx, cury, 1);
289     i = 0;
290     while (i < 7 && nxtsct(&ns, &sect)) {
291         /* Nasty: this relies on the iteration order */
292         view[i] = dchr[sect.sct_type].d_mnem;
293         switch (sect.sct_type) {
294         case SCT_WATER:
295         case SCT_RURAL:
296         case SCT_MOUNT:
297         case SCT_WASTE:
298         case SCT_PLAINS:
299             break;
300         default:
301             if (sect.sct_own != player->cnum && !player->god)
302                 view[i] = '?';
303             break;
304         }
305         changed += map_set(player->cnum, ns.x, ns.y, view[i], 0);
306         i++;
307     }
308     if (changed)
309         writemap(player->cnum);
310     if (!getsect(curx, cury, &sect))
311         return RET_FAIL;
312     pr("    %c %c      eff   mob   civ  mil   uw food  work  avail\n",
313        view[0], view[1]);
314     pr("   %c %c %c     %3d   %3d  %4d %4d %4d %4d   %3d   %3d\n",
315        view[2], view[3], view[4],
316        sect.sct_effic, sect.sct_mobil,
317        sect.sct_item[I_CIVIL], sect.sct_item[I_MILIT], sect.sct_item[I_UW],
318        sect.sct_item[I_FOOD], sect.sct_work, sect.sct_avail);
319     pr("    %c %c\n", view[5], view[6]);
320     return RET_OK;
321 }
322
323 int
324 fly_map(coord curx, coord cury)
325 {
326     struct nstr_sect ns;
327     struct sctstr sect;
328     char view[7];
329     int i;
330
331     snxtsct_dist(&ns, curx, cury, 1);
332     i = 0;
333     while (i < 7 && nxtsct(&ns, &sect)) {
334         /* Nasty: this relies on the iteration order */
335         if (!(view[i] = player->bmap[sect.sct_uid]))
336             view[i] = ' ';
337         i++;
338     }
339
340     pr("    %c %c\n", view[0], view[1]);
341     pr("   %c %c %c\n", view[2], view[3], view[4]);
342     pr("    %c %c\n", view[5], view[6]);
343     return RET_OK;
344 }
345
346 int
347 check_lmines(coord x, coord y, double weight)
348 {
349     struct sctstr sect;
350     int dam = 0;
351
352     getsect(x, y, &sect);
353     if (SCT_LANDMINES(&sect) > 0 &&
354         sect.sct_oldown != player->cnum &&
355         chance(DMINE_LHITCHANCE(sect.sct_mines)) && chance(weight / 100.0)) {
356         pr_beep();
357         pr("Blammo! Landmines detected! in %s  ",
358            xyas(sect.sct_x, sect.sct_y, player->cnum));
359         dam = roll(20);
360         --sect.sct_mines;
361         putsect(&sect);
362         pr("%d damage sustained.\n", dam);
363     }
364     return dam;
365 }