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