]> git.pond.sub.org Git - empserver/blob - src/lib/commands/move.c
(opt_NEWPOWER, Options, gen_power): Remove nooption NEWPOWER.
[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 != V_MILIT) {
98         int tot_mil = 0;
99         struct nstr_item ni;
100         struct lndstr land;
101         snxtitem_xy(&ni, EF_LAND, sect.sct_x, sect.sct_y);
102         while (nxtitem(&ni, (s_char *)&land)) {
103             if (land.lnd_own == player->cnum)
104                 tot_mil += total_mil(&land);
105         }
106         if ((sect.sct_item[I_MILIT] + tot_mil) * 10 < sect.sct_item[I_CIVIL]) {
107             pr("Military control required to move goods.\n");
108             return RET_FAIL;
109         }
110     }
111     stype = sect.sct_type;
112     dp = &dchr[stype];
113     infected = sect.sct_pstage == PLG_INFECT;
114     amt_src = sect.sct_item[vtype];
115     if (!istest && amt_src <= 0) {
116         pr("No %s in %s\n", ip->i_name,
117            xyas(sect.sct_x, sect.sct_y, player->cnum));
118         return RET_FAIL;
119     }
120     own = sect.sct_own;
121     mob = (int)sect.sct_mobil;
122     if (!istest && vtype == V_CIVIL && sect.sct_oldown != own) {
123         pr("You can't move conquered populace!\n");
124         return RET_FAIL;
125     }
126     if (mob <= 0) {
127         pr("No mobility in %s\n",
128            xyas(sect.sct_x, sect.sct_y, player->cnum));
129         return RET_SYN;
130     }
131     if (vtype == V_CIVIL) {
132         work = sect.sct_work;
133         if (work != 100)
134             pr("Warning: civil unrest\n");
135         loyal = sect.sct_loyal;
136     } else if (vtype == V_MILIT) {
137         work = 100;
138         loyal = 0;
139     }
140     if (istest)
141         sprintf(prompt, "Number of %s to test move? ", ip->i_name);
142     else
143         sprintf(prompt, "Number of %s to move? (max %d) ",
144                 ip->i_name, amt_src);
145     if ((amount = onearg(player->argp[3], prompt)) < 0)
146         return RET_FAIL;
147     if (!check_sect_ok(&sect))
148         return RET_FAIL;
149     if (amount > amt_src) {
150         if (istest) {
151             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);
152         } else {
153             amount = amt_src;
154             pr("Only moving %d.\n", amount);
155         }
156     }
157
158     if (!want_to_abandon(&sect, vtype, amount, 0)) {
159         pr("Move cancelled.\n");
160         return RET_FAIL;
161     }
162
163     if (!check_sect_ok(&sect))
164         return RET_FAIL;
165
166     if (amount <= 0)
167         return RET_SYN;
168     packing = ip->i_pkg[dp->d_pkg];
169     if (packing > 1 && sect.sct_effic < 60)
170         packing = 1;
171     weight = (double)amount *ip->i_lbs / packing;
172     /*
173      * First remove commodities from source sector
174      */
175     if (!istest) {
176         getsect(x, y, &start);
177         if (start.sct_own != player->cnum) {
178             pr("Somebody has captured that sector!\n");
179             return RET_FAIL;
180         }
181         amt_src = start.sct_item[vtype];
182         if (amt_src < amount) {
183             pr("Only %d %s left in %s!\n", amt_src,
184                ip->i_name, xyas(start.sct_x, start.sct_y, player->cnum));
185             amount = amt_src;
186             amt_src = 0;
187         } else
188             amt_src -= amount;
189
190         start.sct_item[vtype] = amt_src;
191         start.sct_flags |= MOVE_IN_PROGRESS;
192         putsect(&start);
193     }
194
195     /*
196      * Now parse the path and return ending sector.
197      */
198     dam = (istest ? 0 : 1);
199     if (dam && !chance(weight / 200.0))
200         dam = 0;
201     mcost = move_ground((s_char *)ip, &sect, &endsect,
202                         weight, player->argp[4],
203                         cmd_move_map, 0, &dam);
204
205     if (dam) {
206         left = commdamage(amount, dam, ip->i_vtype);
207         if (left < amount) {
208             if (left) {
209                 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));
210             } else {
211                 pr("All of the %s you were moving were destroyed!\n",
212                    ip->i_name);
213             }
214             amount = left;
215         }
216     }
217
218     if (mcost > 0)
219         pr("Total movement cost = %d\n", mcost);
220     else
221         pr("No mobility used\n");
222
223     left = 0;
224     if (mcost < 0) {
225         pr("Move aborted\n");
226         getsect(x, y, &sect);
227         sect.sct_mobil = (u_char)mob;
228         left = mob;
229     } else if (!istest) {
230         /*
231            * decrement mobility appropriately.
232          */
233         getsect(x, y, &start);
234         mob = start.sct_mobil;
235         if (mob < mcost) {
236             if (mob > 0)
237                 mob = 0;
238         } else
239             mob -= mcost;
240         start.sct_mobil = (u_char)mob;
241         left = start.sct_mobil;
242         putsect(&start);
243         getsect(endsect.sct_x, endsect.sct_y, &sect);
244     }
245
246     /*
247      * Check for lotsa stuff
248      */
249     if (sect.sct_own != player->cnum) {
250         if (sect.sct_own != 0)
251             pr("Somebody has captured that sector!\n");
252         getsect(x, y, &sect);
253     }
254     if (vtype == V_CIVIL && sect.sct_item[I_CIVIL]
255         && sect.sct_oldown != player->cnum) {
256         pr("Your civilians don't want to stay!\n");
257         getsect(x, y, &sect);
258     }
259
260     amt_dst = sect.sct_item[vtype];
261     if (32767 - amt_dst < amount) {
262         pr("Only enough room for %d in %s.  The goods will be returned.\n",
263            32767 - amt_dst, xyas(sect.sct_x, sect.sct_y, player->cnum));
264         getsect(x, y, &sect);
265     }
266
267     if (!istest)
268         pr("%d mob left in %s\n", left,
269            xyas(start.sct_x, start.sct_y, player->cnum));
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 (32767 - amt_dst < amount)
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 == V_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 != V_CIVIL) && (vtype != V_MILIT))
384         return 0;
385
386     mil = sp->sct_item[I_MILIT];
387     civs = sp->sct_item[I_CIVIL];
388
389     if (vtype == V_MILIT)
390         mil -= amnt;
391
392     if (vtype == V_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 }