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