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