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