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