]> git.pond.sub.org Git - empserver/blob - src/lib/subs/move.c
COPYING duplicates information from README. Remove. Move GPL from
[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(s_char *what, coord curx, coord cury, s_char *arg);
50
51 int
52 move_ground(s_char *what, struct sctstr *start, struct sctstr *end,
53             double weight, s_char *path,
54             int (*map)(s_char *, coord, coord, s_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     s_char *movstr;
63     double sect_mcost;
64     double total_mcost;
65     double mv_cost;
66     double mobility = (double)start->sct_mobil;
67     int dir;
68     int intcost;
69     int takedam = (*dam), out = 0;
70     s_char bpath[512];
71     s_char buf2[512];
72     s_char prompt[128];
73     s_char buf[1024];
74
75     *end = *start;
76     if (mobility <= 0.0)
77         return -1;
78     *dam = 0;
79     if (path && sarg_xy(path, &dx, &dy) && getsect(dx, dy, &ending_sect)) {
80         if ((ending_sect.sct_x == start->sct_x) &&
81             (ending_sect.sct_y == start->sct_y)) {
82             pr("Start sector is ending sector!\n");
83             return -1;
84         }
85         pr("Looking for best path to %s\n", path);
86         path = BestLandPath(buf2, start, &ending_sect, &total_mcost,
87                             MOB_ROAD);
88         if (exploring && (path != (s_char *)0)) /* take off the 'h' */
89             *(path + strlen(path) - 1) = '\0';
90         if (path == (s_char *)0)
91             pr("No owned path exists!\n");
92         else {
93             pr("Using best path '%s', movement cost %1.3f\n",
94                path, total_mcost);
95             strncpy(bpath, path, sizeof(bpath));
96             path = bpath;
97         }
98         if ((total_mcost * weight) > mobility) {
99             pr("Not enough mobility to go all the way. Nothing moved.\n");
100             return -1;
101         }
102     }
103     movstr = path;
104     curx = start->sct_x;
105     cury = start->sct_y;
106     total_mcost = 0.0;
107     if (getsect(curx, cury, &sect) < 0) {
108         logerror("move_path: getsect %d,%d", curx, cury);
109         return -1;
110     }
111     for (;;) {
112         oldx = curx;
113         oldy = cury;
114         if (movstr == 0 || *movstr == 0) {
115             if (exploring) {
116                 map(what, curx, cury, (s_char *)0);
117             } else {
118                 move_map(what, curx, cury, (s_char *)0);
119             }
120             sprintf(prompt, "<%.1f: %c %s> ", mobility,
121                     dchr[sect.sct_type].d_mnem,
122                     xyas(sect.sct_x, sect.sct_y, player->cnum));
123             movstr = getstring(prompt, buf);
124         }
125         if (movstr && sarg_xy(movstr, &dx, &dy)) {
126             if (getsect(dx, dy, &dsect)) {
127                 movstr = BestLandPath(buf2, &sect, &dsect, &mv_cost,
128                                       MOB_ROAD);
129             } else {
130                 pr("Invalid destination sector!\n");
131                 movstr = (s_char *)0;
132             }
133
134             if (movstr == (s_char *)0) {
135                 pr("Can't get to %s from here!\n",
136                    xyas(dx, dy, player->cnum));
137                 movstr = (s_char *)0;
138             } else {
139                 if ((mv_cost * weight) > mobility) {
140                     pr("Not enough mobility to go all the way. Nothing moved.\n");
141                     movstr = (s_char *)0;
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 == 0 || *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(what, 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,
172                sect.sct_item[I_CIVIL]);
173             continue;
174         }
175         /*
176          * now see if we can move into the
177          * next sector.  Mobility, terrain,
178          * or ownership may prevent us.
179          */
180         tmpx = curx + diroff[dir][0];
181         tmpy = cury + diroff[dir][1];
182         if (getsect(tmpx, tmpy, &next) < 0) {
183             pr("You can't go there...\n");
184             *movstr = 0;
185             continue;
186         }
187         if (!player->god) {
188             if ((next.sct_type == SCT_SANCT) &&
189                 (next.sct_own != player->cnum)) {
190                 pr("Converts, huh?\n");
191                 *movstr = 0;
192                 continue;
193             }
194             sect_mcost = sector_mcost(&next, MOB_ROAD);
195             if ((!player->owner && (!exploring
196                                     || next.sct_item[I_MILIT]
197                                     || next.sct_item[I_CIVIL]))
198                 || sect_mcost == -1.0) {
199                 /* already-owned, or prohibited terrain */
200                 pr("You can't go there...\n");
201                 *movstr = 0;
202                 continue;
203             }
204             sect_mcost *= weight;
205             if (sect_mcost > mobility) {
206                 pr("Not enough mobility.  ");
207                 pr("You can't go there...\n");
208                 *movstr = 0;
209                 continue;
210             }
211             mobility -= sect_mcost;
212             total_mcost += sect_mcost;
213         }
214         curx = tmpx;
215         cury = tmpy;
216         if (cury != start->sct_y)
217             out = 1;
218         if (curx != start->sct_x)
219             out = 1;
220
221         sect = next;
222
223         if (takedam)
224             *dam += check_lmines(sect.sct_x, sect.sct_y, weight);
225         if (*dam >= 100)
226             break;
227         /*
228          * Check and see if anyone will interdict us
229          */
230         if (takedam && chance(weight / 100.0) &&
231             ((curx != oldx) || (cury != oldy)))
232             (*dam) += ground_interdict(curx, cury, player->cnum,
233                                        "commodities");
234         if (*dam >= 100)
235             break;
236     }
237     *end = sect;
238     intcost = (int)total_mcost;
239     if (intcost < 0)
240         return -1;
241     if ((start->sct_x == end->sct_x) && (start->sct_y == end->sct_y)
242         && !out)
243         return -1;
244
245     if (chance(total_mcost - intcost))
246         intcost++;
247     return intcost;
248 }
249
250
251 /*ARGSUSED*/
252 static int
253 move_map(s_char *what, coord curx, coord cury, s_char *arg)
254 {
255     struct nstr_sect ns;
256     struct natstr *np;
257     struct sctstr sect;
258     coord rel_x, rel_y;
259     s_char range[128];
260     s_char view[7];
261     int i;
262     int changed = 0;
263
264     np = getnatp(player->cnum);
265     rel_x = xrel(np, curx);
266     rel_y = yrel(np, cury);
267     sprintf(range, "%d:%d,%d:%d", rel_x - 2, rel_x + 2, rel_y - 1,
268             rel_y + 1);
269     player->condarg = 0;
270     /* This is necessary, otherwise move_map would attempt to pay */
271     /* attention to the conditional arguments left behind by such */
272     /* a command as "tran p -1,-1 ?eff=100".. It'd then only see  */
273     /* 100% efficienct sects, and get all screwed up         --ts */
274     if (!snxtsct(&ns, range))
275         return RET_FAIL;
276     i = 0;
277     while (i < 7 && nxtsct(&ns, &sect)) {
278         view[i] = dchr[sect.sct_type].d_mnem;
279         switch (sect.sct_type) {
280         case SCT_WATER:
281         case SCT_RURAL:
282         case SCT_MOUNT:
283         case SCT_WASTE:
284         case SCT_PLAINS:
285             break;
286         default:
287             if (sect.sct_own != player->cnum && !player->god)
288                 view[i] = '?';
289             break;
290         }
291         changed += map_set(player->cnum, ns.x, ns.y, view[i], 0);
292         i++;
293     }
294     if (changed)
295         writemap(player->cnum);
296     if (!getsect(curx, cury, &sect))
297         return RET_FAIL;
298     pr("    %c %c      eff   mob   civ  mil   uw food  work  avail\n",
299        view[0], view[1]);
300     pr("   %c %c %c     %3d   %3d  %4d %4d %4d %4d   %3d   %3d\n",
301        view[2], view[3], view[4],
302        sect.sct_effic, sect.sct_mobil,
303        sect.sct_item[I_CIVIL], sect.sct_item[I_MILIT], sect.sct_item[I_UW],
304        sect.sct_item[I_FOOD], sect.sct_work, sect.sct_avail);
305     pr("    %c %c\n", view[5], view[6]);
306     return RET_OK;
307 }
308
309 int
310 fly_map(coord curx, coord cury)
311 {
312     struct nstr_sect ns;
313     struct natstr *np;
314     struct sctstr sect;
315     coord rel_x, rel_y;
316     s_char view[7];
317     int i;
318     s_char range[128];
319
320     np = getnatp(player->cnum);
321     rel_x = xrel(np, curx);
322     rel_y = yrel(np, cury);
323     sprintf(range, "%d:%d,%d:%d", rel_x - 2, rel_x + 2, rel_y - 1,
324             rel_y + 1);
325     player->condarg = 0;
326     /* This is necessary, otherwise move_map would attempt to pay */
327     /* attention to the conditional arguments left behind by such */
328     /* a command as "tran p -1,-1 ?eff=100".. It'd then only see  */
329     /* 100% efficienct sects, and get all screwed up         --ts */
330
331     if (!snxtsct(&ns, range))
332         return RET_FAIL;
333     i = 0;
334     while (i < 7 && nxtsct(&ns, &sect)) {
335         if (!(view[i] = player->bmap[sctoff(ns.x, ns.y)]))
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 (sect.sct_mines > 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 }