]> git.pond.sub.org Git - empserver/blob - src/lib/commands/move.c
Cleanup #includes of (mostly a long time) unused header files.
[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 "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,\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);
137         } else {
138             amount = amt_src;
139             pr("Only moving %d.\n", amount);
140         }
141     }
142
143     if (!istest && !want_to_abandon(&sect, vtype, amount, 0)) {
144         pr("Move cancelled.\n");
145         return RET_FAIL;
146     }
147
148     if (!check_sect_ok(&sect))
149         return RET_FAIL;
150
151     if (amount <= 0)
152         return RET_SYN;
153     packing = sect.sct_effic >= 60 ? dchr[sect.sct_type].d_pkg : IPKG;
154     weight = (double)amount * ip->i_lbs / ip->i_pkg[packing];
155     /*
156      * First remove commodities 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", amt_src,
167                ip->i_name, xyas(start.sct_x, start.sct_y, player->cnum));
168             amount = amt_src;
169             amt_src = 0;
170         } else
171             amt_src -= amount;
172
173         start.sct_item[vtype] = amt_src;
174         start.sct_flags |= MOVE_IN_PROGRESS;
175         putsect(&start);
176     }
177
178     /*
179      * Now parse the path and return ending sector.
180      */
181     dam = (istest ? 0 : 1);
182     if (dam && !chance(weight / 200.0))
183         dam = 0;
184     mcost = move_ground((s_char *)ip, &sect, &endsect,
185                         weight, player->argp[4],
186                         cmd_move_map, 0, &dam);
187
188     if (dam) {
189         left = commdamage(amount, dam, ip->i_vtype);
190         if (left < amount) {
191             if (left) {
192                 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));
193             } else {
194                 pr("All of the %s you were moving were destroyed!\n",
195                    ip->i_name);
196             }
197             amount = left;
198         }
199     }
200
201     if (mcost > 0)
202         pr("Total movement cost = %d\n", mcost);
203     else
204         pr("No mobility used\n");
205
206     left = 0;
207     if (mcost < 0) {
208         pr("Move aborted\n");
209         getsect(x, y, &sect);
210         sect.sct_mobil = (u_char)mob;
211         left = mob;
212     } else if (!istest) {
213         /*
214          * decrement mobility appropriately.
215          */
216         getsect(x, y, &start);
217         mob = start.sct_mobil;
218         if (mob < mcost) {
219             if (mob > 0)
220                 mob = 0;
221         } else
222             mob -= mcost;
223         start.sct_mobil = (u_char)mob;
224         left = start.sct_mobil;
225         putsect(&start);
226         getsect(endsect.sct_x, endsect.sct_y, &sect);
227     }
228
229     /*
230      * Check for lotsa stuff
231      */
232     if (sect.sct_own != player->cnum) {
233         if (sect.sct_own != 0)
234             pr("Somebody has captured that sector!\n");
235         getsect(x, y, &sect);
236     }
237     if (vtype == I_CIVIL && sect.sct_item[I_CIVIL]
238         && sect.sct_oldown != player->cnum) {
239         pr("Your civilians don't want to stay!\n");
240         getsect(x, y, &sect);
241     }
242
243     amt_dst = sect.sct_item[vtype];
244     if (amount > ITEM_MAX - amt_dst) {
245         pr("Only enough room for %d in %s.  The goods will be returned.\n",
246            ITEM_MAX - amt_dst, xyas(sect.sct_x, sect.sct_y, player->cnum));
247         /* FIXME Not nice.  Move what we can and return the rest.  */
248         getsect(x, y, &sect);
249     }
250
251     if (!istest)
252         pr("%d mob left in %s\n", left,
253            xyas(start.sct_x, start.sct_y, player->cnum));
254
255     if (amount <= 0) {
256         getsect(x, y, &start);
257         start.sct_flags &= ~MOVE_IN_PROGRESS;
258         putsect(&start);
259         return RET_OK;
260     }
261
262 /* If the sector that things are going to is no longer
263    owned by the player, and was the starting sector,
264    try to find somewhere to dump the stuff.  If nowhere
265    to dump it, it disappears. */
266     if (sect.sct_own != player->cnum && sect.sct_x == x && sect.sct_y == y) {
267         pr("Can't return the goods, since the starting point is no longer\n");
268         pr("owned by you.\n");
269         /* First lets see if there is one with room */
270         for (n = DIR_FIRST; n <= DIR_LAST; n++) {
271             getsect(x + diroff[n][0], y + diroff[n][1], &tsct);
272             if (tsct.sct_own != player->cnum)
273                 continue;
274             amt_dst = tsct.sct_item[vtype];
275             if (amount > ITEM_MAX - amt_dst)
276                 continue;
277             n = -1;
278             break;
279         }
280         if (n > -1) {
281             /* Find any sector if none with room */
282             for (n = DIR_FIRST; n <= DIR_LAST; n++) {
283                 getsect(x + diroff[n][0], y + diroff[n][1], &tsct);
284                 if (tsct.sct_own != player->cnum)
285                     continue;
286                 n = -1;
287                 break;
288             }
289             if (n > -1) {
290                 pr("The goods had nowhere to go, and were destroyed.\n");
291                 sect.sct_flags &= ~MOVE_IN_PROGRESS;
292                 putsect(&sect);
293                 return RET_OK;
294             }
295         }
296         pr("The goods were dumped into %s.\n",
297            xyas(tsct.sct_x, tsct.sct_y, player->cnum));
298         getsect(tsct.sct_x, tsct.sct_y, &sect);
299     }
300
301     amt_dst = sect.sct_item[vtype];
302     if (amount > ITEM_MAX - amt_dst) {
303         amount = ITEM_MAX - amt_dst;
304         pr("Only room for %d, the rest were lost.\n", amount);
305     }
306     if (istest)
307         return RET_OK;
308     sect.sct_item[vtype] = amount + amt_dst;
309     /*
310      * Now add commodities to destination sector,
311      * along with plague that came along for the ride.
312      * Takeover unowned sectors if not deity.
313      */
314     if (infected && sect.sct_pstage == PLG_HEALTHY)
315         sect.sct_pstage = PLG_EXPOSED;
316     if (vtype == I_CIVIL) {
317         sect.sct_loyal
318             = (amt_dst * sect.sct_loyal + amount * loyal) / (amt_dst + amount);
319         sect.sct_work
320             = (amt_dst * sect.sct_work + amount * work) / (amt_dst + amount);
321     }
322     putsect(&sect);
323     getsect(x, y, &start);
324     start.sct_flags &= ~MOVE_IN_PROGRESS;
325     putsect(&start);
326     return RET_OK;
327 }
328
329 /*
330  * Pretty tacky, but it works.
331  * If more commands start doing this, then
332  * rewrite map to do the right thing.
333  */
334 /* I think this is no longer used, check subs/move.c:move_ground() */
335 /*ARGSUSED*/
336 static int
337 cmd_move_map(s_char *what, coord curx, coord cury, s_char *arg)
338 {
339     player->argp[1] = arg;
340     player->argp[2] = "";
341     player->argp[3] = "";
342     player->argp[4] = "";
343     player->argp[5] = "";
344     player->condarg = 0;
345     return map();
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     /* First, would we be abandoning it?  If not, just return that it's
354        ok to move out */
355     if (!would_abandon(sp, vtype, amnt, lp))
356         return 1;
357
358     sprintf(prompt, "Do you really want to abandon %s [yn]? ",
359             xyas(sp->sct_x, sp->sct_y, player->cnum));
360
361     /* now, if they say yes that it's ok, just return 1 */
362     if (askyn(prompt))
363         return 1;
364
365     /* Nope, not ok */
366     return 0;
367 }
368
369 int
370 would_abandon(struct sctstr *sp, i_type vtype, int amnt, struct lndstr *lp)
371 {
372     int mil, civs, loyalcivs;
373
374     if ((vtype != I_CIVIL) && (vtype != I_MILIT))
375         return 0;
376
377     mil = sp->sct_item[I_MILIT];
378     civs = sp->sct_item[I_CIVIL];
379
380     if (vtype == I_MILIT)
381         mil -= amnt;
382
383     if (vtype == I_CIVIL)
384         civs -= amnt;
385
386     if (sp->sct_own == sp->sct_oldown)
387         loyalcivs = civs;
388     else
389         loyalcivs = 0;
390
391     /* If they have a military unit there, they still own it */
392     if (sp->sct_own != 0
393         && ((loyalcivs == 0) && (mil == 0)
394             && (has_units(sp->sct_x, sp->sct_y, sp->sct_own, lp) == 0)))
395         return 1;
396
397     return 0;
398 }