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