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