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