]> git.pond.sub.org Git - empserver/blob - src/lib/commands/move.c
(move, explore): Return early when interdiction destroys everything
[empserver] / src / lib / commands / move.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, 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 commodities around
29  * 
30  *  Known contributors to this file:
31  *     
32  */
33
34 #include "misc.h"
35 #include "player.h"
36 #include "var.h"
37 #include "sect.h"
38 #include "item.h"
39 #include "file.h"
40 #include "xy.h"
41 #include "nat.h"
42 #include "nsc.h"
43 #include "land.h"
44 #include "optlist.h"
45 #include "path.h"
46 #include "commands.h"
47
48
49 static int cmd_move_map(s_char *what, coord curx, coord cury, s_char *arg);
50
51 int
52 move(void)
53 {
54     register int amount;
55     struct sctstr sect;
56     struct sctstr endsect;
57     struct sctstr start;
58     struct sctstr tsct;
59     int packing;
60     double weight;
61     int left;
62     int mcost, dam;
63     int infected;
64     int stype;
65     int vtype;
66     int amt_src;
67     int amt_dst;
68     struct dchrstr *dp;
69     struct ichrstr *ip;
70     int work;
71     int loyal;
72     int own, mob;
73     int istest = 0;
74     int n;
75     coord x, y;
76     s_char *p;
77     s_char prompt[1024];
78     s_char buf[1024];
79
80
81     istest = *player->argp[0] == 't';
82     if ((ip = whatitem(player->argp[1], "move what? ")) == 0)
83         return RET_SYN;
84     vtype = ip->i_vtype;
85     if (!(p = getstarg(player->argp[2], "from sector : ", buf)))
86         return RET_SYN;
87     if (!sarg_xy(p, &x, &y))
88         return RET_SYN;
89     if (!getsect(x, y, &sect) || !player->owner) {
90         pr("Not yours\n");
91         return RET_FAIL;
92     }
93     /*
94      * military control necessary to move
95      * goodies in occupied territory.
96      */
97     if (!istest && sect.sct_oldown != player->cnum && vtype != V_MILIT) {
98         int tot_mil = 0;
99         struct nstr_item ni;
100         struct lndstr land;
101         snxtitem_xy(&ni, EF_LAND, sect.sct_x, sect.sct_y);
102         while (nxtitem(&ni, (s_char *)&land)) {
103             if (land.lnd_own == player->cnum)
104                 tot_mil += total_mil(&land);
105         }
106         if ((sect.sct_item[I_MILIT] + tot_mil) * 10 < sect.sct_item[I_CIVIL]) {
107             pr("Military control required to move goods.\n");
108             return RET_FAIL;
109         }
110     }
111     stype = sect.sct_type;
112     dp = &dchr[stype];
113     infected = sect.sct_pstage == PLG_INFECT;
114     amt_src = sect.sct_item[vtype];
115     if (!istest && amt_src <= 0) {
116         pr("No %s in %s\n", ip->i_name,
117            xyas(sect.sct_x, sect.sct_y, player->cnum));
118         return RET_FAIL;
119     }
120     own = sect.sct_own;
121     mob = (int)sect.sct_mobil;
122     if (!istest && vtype == V_CIVIL && sect.sct_oldown != own) {
123         pr("You can't move conquered populace!\n");
124         return RET_FAIL;
125     }
126     if (mob <= 0) {
127         pr("No mobility in %s\n",
128            xyas(sect.sct_x, sect.sct_y, player->cnum));
129         return RET_SYN;
130     }
131     if (vtype == V_CIVIL) {
132         work = sect.sct_work;
133         if (work != 100)
134             pr("Warning: civil unrest\n");
135         loyal = sect.sct_loyal;
136     } else if (vtype == V_MILIT) {
137         work = 100;
138         loyal = 0;
139     }
140     if (istest)
141         sprintf(prompt, "Number of %s to test move? ", ip->i_name);
142     else
143         sprintf(prompt, "Number of %s to move? (max %d) ",
144                 ip->i_name, amt_src);
145     if ((amount = onearg(player->argp[3], prompt)) < 0)
146         return RET_FAIL;
147     if (!check_sect_ok(&sect))
148         return RET_FAIL;
149     if (amount > amt_src) {
150         if (istest) {
151             pr("Note: there are actually only %d %s in %s,\nbut the test will be made for %d %s as you requested.\n", amt_src, ip->i_name, xyas(sect.sct_x, sect.sct_y, player->cnum), amount, ip->i_name);
152         } else {
153             amount = amt_src;
154             pr("Only moving %d.\n", amount);
155         }
156     }
157
158     if (!istest && !want_to_abandon(&sect, vtype, amount, 0)) {
159         pr("Move cancelled.\n");
160         return RET_FAIL;
161     }
162
163     if (!check_sect_ok(&sect))
164         return RET_FAIL;
165
166     if (amount <= 0)
167         return RET_SYN;
168     packing = ip->i_pkg[dp->d_pkg];
169     if (packing > 1 && sect.sct_effic < 60)
170         packing = 1;
171     weight = (double)amount *ip->i_lbs / packing;
172     /*
173      * First remove commodities from source sector
174      */
175     if (!istest) {
176         getsect(x, y, &start);
177         if (start.sct_own != player->cnum) {
178             pr("Somebody has captured that sector!\n");
179             return RET_FAIL;
180         }
181         amt_src = start.sct_item[vtype];
182         if (amt_src < amount) {
183             pr("Only %d %s left in %s!\n", amt_src,
184                ip->i_name, xyas(start.sct_x, start.sct_y, player->cnum));
185             amount = amt_src;
186             amt_src = 0;
187         } else
188             amt_src -= amount;
189
190         start.sct_item[vtype] = amt_src;
191         start.sct_flags |= MOVE_IN_PROGRESS;
192         putsect(&start);
193     }
194
195     /*
196      * Now parse the path and return ending sector.
197      */
198     dam = (istest ? 0 : 1);
199     if (dam && !chance(weight / 200.0))
200         dam = 0;
201     mcost = move_ground((s_char *)ip, &sect, &endsect,
202                         weight, player->argp[4],
203                         cmd_move_map, 0, &dam);
204
205     if (dam) {
206         left = commdamage(amount, dam, ip->i_vtype);
207         if (left < amount) {
208             if (left) {
209                 pr("%d of the %s you were moving were destroyed!\nOnly %d %s made it to %s\n", amount - left, ip->i_name, left, ip->i_name, xyas(endsect.sct_x, endsect.sct_y, player->cnum));
210             } else {
211                 pr("All of the %s you were moving were destroyed!\n",
212                    ip->i_name);
213             }
214             amount = left;
215         }
216     }
217
218     if (mcost > 0)
219         pr("Total movement cost = %d\n", mcost);
220     else
221         pr("No mobility used\n");
222
223     left = 0;
224     if (mcost < 0) {
225         pr("Move aborted\n");
226         getsect(x, y, &sect);
227         sect.sct_mobil = (u_char)mob;
228         left = mob;
229     } else if (!istest) {
230         /*
231          * decrement mobility appropriately.
232          */
233         getsect(x, y, &start);
234         mob = start.sct_mobil;
235         if (mob < mcost) {
236             if (mob > 0)
237                 mob = 0;
238         } else
239             mob -= mcost;
240         start.sct_mobil = (u_char)mob;
241         left = start.sct_mobil;
242         putsect(&start);
243         getsect(endsect.sct_x, endsect.sct_y, &sect);
244     }
245
246     /*
247      * Check for lotsa stuff
248      */
249     if (sect.sct_own != player->cnum) {
250         if (sect.sct_own != 0)
251             pr("Somebody has captured that sector!\n");
252         getsect(x, y, &sect);
253     }
254     if (vtype == V_CIVIL && sect.sct_item[I_CIVIL]
255         && sect.sct_oldown != player->cnum) {
256         pr("Your civilians don't want to stay!\n");
257         getsect(x, y, &sect);
258     }
259
260     amt_dst = sect.sct_item[vtype];
261     if (amount > ITEM_MAX - amt_dst) {
262         pr("Only enough room for %d in %s.  The goods will be returned.\n",
263            ITEM_MAX - amt_dst, xyas(sect.sct_x, sect.sct_y, player->cnum));
264         /* FIXME Not nice.  Move what we can and return the rest.  */
265         getsect(x, y, &sect);
266     }
267
268     if (!istest)
269         pr("%d mob left in %s\n", left,
270            xyas(start.sct_x, start.sct_y, player->cnum));
271
272     if (amount <= 0) {
273         getsect(x, y, &start);
274         start.sct_flags &= ~MOVE_IN_PROGRESS;
275         putsect(&start);
276         return RET_OK;
277     }
278
279 /* If the sector that things are going to is no longer
280    owned by the player, and was the starting sector,
281    try to find somewhere to dump the stuff.  If nowhere
282    to dump it, it disappears. */
283     if (sect.sct_own != player->cnum && sect.sct_x == x && sect.sct_y == y) {
284         pr("Can't return the goods, since the starting point is no longer\n");
285         pr("owned by you.\n");
286         /* First lets see if there is one with room */
287         for (n = DIR_FIRST; n <= DIR_LAST; n++) {
288             getsect(x + diroff[n][0], y + diroff[n][1], &tsct);
289             if (tsct.sct_own != player->cnum)
290                 continue;
291             amt_dst = tsct.sct_item[vtype];
292             if (amount > ITEM_MAX - amt_dst)
293                 continue;
294             n = -1;
295             break;
296         }
297         if (n > -1) {
298             /* Find any sector if none with room */
299             for (n = DIR_FIRST; n <= DIR_LAST; n++) {
300                 getsect(x + diroff[n][0], y + diroff[n][1], &tsct);
301                 if (tsct.sct_own != player->cnum)
302                     continue;
303                 n = -1;
304                 break;
305             }
306             if (n > -1) {
307                 pr("The goods had nowhere to go, and were destroyed.\n");
308                 sect.sct_flags &= ~MOVE_IN_PROGRESS;
309                 putsect(&sect);
310                 return RET_OK;
311             }
312         }
313         pr("The goods were dumped into %s.\n",
314            xyas(tsct.sct_x, tsct.sct_y, player->cnum));
315         getsect(tsct.sct_x, tsct.sct_y, &sect);
316     }
317
318     amt_dst = sect.sct_item[vtype];
319     if (amount > ITEM_MAX - amt_dst) {
320         amount = ITEM_MAX - amt_dst;
321         pr("Only room for %d, the rest were lost.\n", amount);
322     }
323     if (istest)
324         return RET_OK;
325     sect.sct_item[vtype] = amount + amt_dst;
326     /*
327      * Now add commodities to destination sector,
328      * along with plague that came along for the ride.
329      * Takeover unowned sectors if not deity.
330      */
331     if (infected && sect.sct_pstage == PLG_HEALTHY)
332         sect.sct_pstage = PLG_EXPOSED;
333     if (vtype == V_CIVIL) {
334         sect.sct_loyal
335             = (amt_dst * sect.sct_loyal + amount * loyal) / (amt_dst + amount);
336         sect.sct_work
337             = (amt_dst * sect.sct_work + amount * work) / (amt_dst + amount);
338     }
339     putsect(&sect);
340     getsect(x, y, &start);
341     start.sct_flags &= ~MOVE_IN_PROGRESS;
342     putsect(&start);
343     return RET_OK;
344 }
345
346 /*
347  * Pretty tacky, but it works.
348  * If more commands start doing this, then
349  * rewrite map to do the right thing.
350  */
351 /* I think this is no longer used, check subs/move.c:move_ground() */
352 /*ARGSUSED*/
353 static int
354 cmd_move_map(s_char *what, coord curx, coord cury, s_char *arg)
355 {
356     player->argp[1] = arg;
357     player->argp[2] = "";
358     player->argp[3] = "";
359     player->argp[4] = "";
360     player->argp[5] = "";
361     player->condarg = 0;
362     return map();
363 }
364
365 int
366 want_to_abandon(struct sctstr *sp, int vtype, int amnt, struct lndstr *lp)
367 {
368     char prompt[80];
369
370     /* First, would we be abandoning it?  If not, just return that it's
371        ok to move out */
372     if (!would_abandon(sp, vtype, amnt, lp))
373         return 1;
374
375     sprintf(prompt, "Do you really want to abandon %s [yn]? ",
376             xyas(sp->sct_x, sp->sct_y, player->cnum));
377
378     /* now, if they say yes that it's ok, just return 1 */
379     if (askyn(prompt))
380         return 1;
381
382     /* Nope, not ok */
383     return 0;
384 }
385
386 int
387 would_abandon(struct sctstr *sp, int vtype, int amnt, struct lndstr *lp)
388 {
389     int mil, civs, loyalcivs;
390
391     if ((vtype != V_CIVIL) && (vtype != V_MILIT))
392         return 0;
393
394     mil = sp->sct_item[I_MILIT];
395     civs = sp->sct_item[I_CIVIL];
396
397     if (vtype == V_MILIT)
398         mil -= amnt;
399
400     if (vtype == V_CIVIL)
401         civs -= amnt;
402
403     if (sp->sct_own == sp->sct_oldown)
404         loyalcivs = civs;
405     else
406         loyalcivs = 0;
407
408     /* If they have a military unit there, they still own it */
409     if (sp->sct_own != 0
410         && ((loyalcivs == 0) && (mil == 0)
411             && (has_units(sp->sct_x, sp->sct_y, sp->sct_own, lp) == 0)))
412         return 1;
413
414     return 0;
415 }