]> git.pond.sub.org Git - empserver/blob - src/lib/commands/move.c
(tran_nuke, tran_plane, set, sell, move): Use military_control().
[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     if (vtype == I_CIVIL) {
124         work = sect.sct_work;
125         if (work != 100)
126             pr("Warning: civil unrest\n");
127         loyal = sect.sct_loyal;
128     } else if (vtype == I_MILIT) {
129         work = 100;
130         loyal = 0;
131     }
132     if (istest)
133         sprintf(prompt, "Number of %s to test move? ", ip->i_name);
134     else
135         sprintf(prompt, "Number of %s to move? (max %d) ",
136                 ip->i_name, amt_src);
137     if ((amount = onearg(player->argp[3], prompt)) < 0)
138         return RET_FAIL;
139     if (!check_sect_ok(&sect))
140         return RET_FAIL;
141     if (amount > amt_src) {
142         if (istest) {
143             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);
144         } else {
145             amount = amt_src;
146             pr("Only moving %d.\n", amount);
147         }
148     }
149
150     if (!istest && !want_to_abandon(&sect, vtype, amount, 0)) {
151         pr("Move cancelled.\n");
152         return RET_FAIL;
153     }
154
155     if (!check_sect_ok(&sect))
156         return RET_FAIL;
157
158     if (amount <= 0)
159         return RET_SYN;
160     packing = ip->i_pkg[dp->d_pkg];
161     if (packing > 1 && sect.sct_effic < 60)
162         packing = 1;
163     weight = (double)amount *ip->i_lbs / packing;
164     /*
165      * First remove commodities from source sector
166      */
167     if (!istest) {
168         getsect(x, y, &start);
169         if (start.sct_own != player->cnum) {
170             pr("Somebody has captured that sector!\n");
171             return RET_FAIL;
172         }
173         amt_src = start.sct_item[vtype];
174         if (amt_src < amount) {
175             pr("Only %d %s left in %s!\n", amt_src,
176                ip->i_name, xyas(start.sct_x, start.sct_y, player->cnum));
177             amount = amt_src;
178             amt_src = 0;
179         } else
180             amt_src -= amount;
181
182         start.sct_item[vtype] = amt_src;
183         start.sct_flags |= MOVE_IN_PROGRESS;
184         putsect(&start);
185     }
186
187     /*
188      * Now parse the path and return ending sector.
189      */
190     dam = (istest ? 0 : 1);
191     if (dam && !chance(weight / 200.0))
192         dam = 0;
193     mcost = move_ground((s_char *)ip, &sect, &endsect,
194                         weight, player->argp[4],
195                         cmd_move_map, 0, &dam);
196
197     if (dam) {
198         left = commdamage(amount, dam, ip->i_vtype);
199         if (left < amount) {
200             if (left) {
201                 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));
202             } else {
203                 pr("All of the %s you were moving were destroyed!\n",
204                    ip->i_name);
205             }
206             amount = left;
207         }
208     }
209
210     if (mcost > 0)
211         pr("Total movement cost = %d\n", mcost);
212     else
213         pr("No mobility used\n");
214
215     left = 0;
216     if (mcost < 0) {
217         pr("Move aborted\n");
218         getsect(x, y, &sect);
219         sect.sct_mobil = (u_char)mob;
220         left = mob;
221     } else 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         getsect(endsect.sct_x, endsect.sct_y, &sect);
236     }
237
238     /*
239      * Check for lotsa stuff
240      */
241     if (sect.sct_own != player->cnum) {
242         if (sect.sct_own != 0)
243             pr("Somebody has captured that sector!\n");
244         getsect(x, y, &sect);
245     }
246     if (vtype == I_CIVIL && sect.sct_item[I_CIVIL]
247         && sect.sct_oldown != player->cnum) {
248         pr("Your civilians don't want to stay!\n");
249         getsect(x, y, &sect);
250     }
251
252     amt_dst = sect.sct_item[vtype];
253     if (amount > ITEM_MAX - amt_dst) {
254         pr("Only enough room for %d in %s.  The goods will be returned.\n",
255            ITEM_MAX - amt_dst, xyas(sect.sct_x, sect.sct_y, player->cnum));
256         /* FIXME Not nice.  Move what we can and return the rest.  */
257         getsect(x, y, &sect);
258     }
259
260     if (!istest)
261         pr("%d mob left in %s\n", left,
262            xyas(start.sct_x, start.sct_y, player->cnum));
263
264     if (amount <= 0) {
265         getsect(x, y, &start);
266         start.sct_flags &= ~MOVE_IN_PROGRESS;
267         putsect(&start);
268         return RET_OK;
269     }
270
271 /* If the sector that things are going to is no longer
272    owned by the player, and was the starting sector,
273    try to find somewhere to dump the stuff.  If nowhere
274    to dump it, it disappears. */
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     if (istest)
316         return RET_OK;
317     sect.sct_item[vtype] = amount + amt_dst;
318     /*
319      * Now add commodities to destination sector,
320      * along with plague that came along for the ride.
321      * Takeover unowned sectors if not deity.
322      */
323     if (infected && sect.sct_pstage == PLG_HEALTHY)
324         sect.sct_pstage = PLG_EXPOSED;
325     if (vtype == I_CIVIL) {
326         sect.sct_loyal
327             = (amt_dst * sect.sct_loyal + amount * loyal) / (amt_dst + amount);
328         sect.sct_work
329             = (amt_dst * sect.sct_work + amount * work) / (amt_dst + amount);
330     }
331     putsect(&sect);
332     getsect(x, y, &start);
333     start.sct_flags &= ~MOVE_IN_PROGRESS;
334     putsect(&start);
335     return RET_OK;
336 }
337
338 /*
339  * Pretty tacky, but it works.
340  * If more commands start doing this, then
341  * rewrite map to do the right thing.
342  */
343 /* I think this is no longer used, check subs/move.c:move_ground() */
344 /*ARGSUSED*/
345 static int
346 cmd_move_map(s_char *what, coord curx, coord cury, s_char *arg)
347 {
348     player->argp[1] = arg;
349     player->argp[2] = "";
350     player->argp[3] = "";
351     player->argp[4] = "";
352     player->argp[5] = "";
353     player->condarg = 0;
354     return map();
355 }
356
357 int
358 want_to_abandon(struct sctstr *sp, int vtype, int amnt, struct lndstr *lp)
359 {
360     char prompt[80];
361
362     /* First, would we be abandoning it?  If not, just return that it's
363        ok to move out */
364     if (!would_abandon(sp, vtype, amnt, lp))
365         return 1;
366
367     sprintf(prompt, "Do you really want to abandon %s [yn]? ",
368             xyas(sp->sct_x, sp->sct_y, player->cnum));
369
370     /* now, if they say yes that it's ok, just return 1 */
371     if (askyn(prompt))
372         return 1;
373
374     /* Nope, not ok */
375     return 0;
376 }
377
378 int
379 would_abandon(struct sctstr *sp, int 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 }