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