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