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