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