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