]> git.pond.sub.org Git - empserver/blob - src/lib/commands/load.c
unload lunload: Fail early when carrier can't carry land units
[empserver] / src / lib / commands / load.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2018, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  load.c: load/unload goods from a sector onto a ship or land unit
28  *
29  *  Known contributors to this file:
30  *     David Sharnoff, 1987
31  *     Ken Stevens, 1995 (rewritten)
32  *     Steve McClure, 1998-2000
33  *     Markus Armbruster, 2004-2017
34  */
35
36 #include <config.h>
37
38 #include <ctype.h>
39 #include "commands.h"
40 #include "item.h"
41 #include "land.h"
42 #include "optlist.h"
43 #include "plague.h"
44 #include "plane.h"
45 #include "ship.h"
46 #include "unit.h"
47
48 static int load_plane_ship(struct sctstr *sectp, struct shpstr *sp,
49                            int noisy, int loading, int *nshipsp);
50 static int load_land_ship(struct sctstr *sectp, struct shpstr *sp,
51                           int noisy, int loading, int *nshipsp);
52 static int load_comm_ship(struct sctstr *sectp, struct shpstr *sp,
53                           struct ichrstr *ich, int loading,
54                           int *nshipsp);
55 static int load_plane_land(struct sctstr *sectp, struct lndstr *lp,
56                            int noisy, int loading, int *nunitsp);
57 static int load_land_land(struct sctstr *sectp, struct lndstr *lp,
58                           int noisy, int loading, int *nunitsp);
59 static int load_comm_land(struct sctstr *sectp, struct lndstr *lp,
60                           struct ichrstr *ich, int loading,
61                           int *nunitsp);
62
63 int
64 load(void)
65 {
66     int loading = **player->argp == 'l';
67     int noisy;
68     int type;
69     struct nstr_item nbst;
70     struct ichrstr *ich;
71     int nships;
72     struct sctstr sect;
73     struct shpstr ship;
74     int retval;
75     char *p;
76     char buf[1024];
77
78     p = getstarg(player->argp[1],
79                  "What commodity (or 'plane' or 'land')? ", buf);
80     if (!p || !*p)
81         return RET_SYN;
82
83     if (!strncmp(p, "plane", 5))
84         type = EF_PLANE;
85     else if (!strncmp(p, "land", 4))
86         type = EF_LAND;
87     else if (NULL != (ich = item_by_name(p)))
88         type = EF_SECTOR;
89     else {
90         pr("Can't %sload '%s'\n", loading ? "" : "un", p);
91         return RET_SYN;
92     }
93
94     p = getstarg(player->argp[2], "Ship(s): ", buf);
95     if (!p || !*p)
96         return RET_SYN;
97
98     if (!snxtitem(&nbst, EF_SHIP, p, NULL))
99         return RET_SYN;
100
101     noisy = nbst.sel == NS_LIST;
102
103     nships = 0;
104     while (nxtitem(&nbst, &ship)) {
105         if (!ship.shp_own)
106             continue;
107         if (!player->owner) {
108             if (!loading || !noisy)
109                 continue;
110             if (relations_with(ship.shp_own, player->cnum) < FRIENDLY)
111                 continue;
112         }
113
114         if (!getsect(ship.shp_x, ship.shp_y, &sect))    /* XXX */
115             continue;
116         if (!sect.sct_own)
117             continue;
118         if (!player->owner) {
119             if (ship.shp_own != player->cnum)
120                 continue;
121             if (!sect_has_dock(&sect))
122                 continue;
123             if (loading) {
124                 if (noisy)
125                     pr("You don't own %s\n",
126                        xyas(sect.sct_x, sect.sct_y, player->cnum));
127                 continue;
128             }
129         }
130         if (!sect_has_dock(&sect)) {
131             if (noisy)
132                 pr("Sector %s is not a harbor or canal.\n",
133                    xyas(sect.sct_x, sect.sct_y, player->cnum));
134             continue;
135         }
136         if (!loading
137             && !player->owner
138             && relations_with(sect.sct_own, player->cnum) < FRIENDLY) {
139             if (noisy)
140                 pr("You can't unload into an unfriendly %s\n",
141                    dchr[sect.sct_type].d_name);
142             continue;
143         }
144         if (sect.sct_effic < 2) {
145             if (noisy)
146                 pr("The %s at %s is not 2%% efficient yet.\n",
147                    dchr[sect.sct_type].d_name,
148                    xyas(sect.sct_x, sect.sct_y, player->cnum));
149             continue;
150         }
151
152         if (opt_MARKET) {
153             if (ontradingblock(EF_SHIP, &ship)) {
154                 pr("You cannot load/unload an item on the trading block!\n");
155                 continue;
156             }
157         }
158
159         switch (type) {
160         case EF_PLANE:
161             retval = load_plane_ship(&sect, &ship, noisy, loading, &nships);
162             if (retval != 0)
163                 return retval;
164             break;
165         case EF_LAND:
166             retval = load_land_ship(&sect, &ship, noisy, loading, &nships);
167             if (retval != 0)
168                 return retval;
169             break;
170         case EF_SECTOR:
171             retval = load_comm_ship(&sect, &ship, ich, loading, &nships);
172             if (retval != 0)
173                 return retval;
174         }
175         /* load/unload plague */
176         if (sect.sct_pstage == PLG_INFECT
177             && ship.shp_pstage == PLG_HEALTHY)
178             ship.shp_pstage = PLG_EXPOSED;
179         if (ship.shp_pstage == PLG_INFECT
180             && sect.sct_pstage == PLG_HEALTHY)
181             sect.sct_pstage = PLG_EXPOSED;
182         putsect(&sect);
183         putship(ship.shp_uid, &ship);
184     }
185     if (!nships)
186         pr("No ships affected\n");
187     else
188         pr("%d ship%s %sloaded\n", nships, splur(nships),
189            loading ? "" : "un");
190     return RET_OK;
191 }
192
193 int
194 lload(void)
195 {
196     int loading = player->argp[0][1] == 'l';
197     int noisy;
198     int type;
199     struct nstr_item nbst;
200     struct ichrstr *ich;
201     int nunits;
202     struct sctstr sect;
203     struct lndstr land;
204     int retval;
205     char *p;
206     char buf[1024];
207
208     p = getstarg(player->argp[1],
209                  "What commodity (or 'plane' or 'land')? ", buf);
210     if (!p || !*p)
211         return RET_SYN;
212     if (!strncmp(p, "plane", 5))
213         type = EF_PLANE;
214     else if (!strncmp(p, "land", 4))
215         type = EF_LAND;
216     else if (NULL != (ich = item_by_name(p)))
217         type = EF_SECTOR;
218     else {
219         pr("Can't %sload '%s'\n", loading ? "" : "un", p);
220         return RET_SYN;
221     }
222
223     p = getstarg(player->argp[2], "Unit(s): ", buf);
224     if (!p || !*p)
225         return RET_SYN;
226
227     if (!snxtitem(&nbst, EF_LAND, p, NULL))
228         return RET_SYN;
229
230     noisy = nbst.sel == NS_LIST;
231
232     nunits = 0;
233     while (nxtitem(&nbst, &land)) {
234         if (land.lnd_own == 0)
235             continue;
236         if (!player->owner) {
237             if (!loading || !noisy)
238                 continue;
239             if (relations_with(land.lnd_own, player->cnum) != ALLIED)
240                 continue;
241         }
242
243         if (!getsect(land.lnd_x, land.lnd_y, &sect))    /* XXX */
244             continue;
245         if (!player->owner) {
246             if (land.lnd_own != player->cnum)
247                 continue;
248             if (loading) {
249                 if (noisy)
250                     pr("Sector %s is not yours.\n",
251                        xyas(sect.sct_x, sect.sct_y, player->cnum));
252                 continue;
253             }
254             if (relations_with(sect.sct_own, player->cnum) != ALLIED) {
255                 pr("Sector %s is not yours.\n",
256                    xyas(sect.sct_x, sect.sct_y, player->cnum));
257                 continue;
258             }
259         }
260
261         if (opt_MARKET) {
262             if (ontradingblock(EF_LAND, &land)) {
263                 pr("You cannot load/unload an item on the trading block!\n");
264                 continue;
265             }
266         }
267
268         switch (type) {
269         case EF_LAND:
270             retval = load_land_land(&sect, &land, noisy, loading, &nunits);
271             if (retval != 0)
272                 return retval;
273             break;
274         case EF_PLANE:
275             retval = load_plane_land(&sect, &land, noisy, loading, &nunits);
276             if (retval != 0)
277                 return retval;
278             break;
279         case EF_SECTOR:
280             retval = load_comm_land(&sect, &land, ich, loading, &nunits);
281             if (retval != 0)
282                 return retval;
283         }
284         /* load/unload plague */
285         if (sect.sct_pstage == PLG_INFECT
286             && land.lnd_pstage == PLG_HEALTHY)
287             land.lnd_pstage = PLG_EXPOSED;
288         if (land.lnd_pstage == PLG_INFECT
289             && sect.sct_pstage == PLG_HEALTHY)
290             sect.sct_pstage = PLG_EXPOSED;
291
292         putsect(&sect);
293         putland(land.lnd_uid, &land);
294     }
295     if (nunits == 0)
296         pr("No units affected\n");
297     else
298         pr("%d unit%s %sloaded\n", nunits, splur(nunits),
299            loading ? "" : "un");
300     return RET_OK;
301 }
302
303 static int
304 move_amount(int sect_amt, int unit_amt, int unit_max,
305            int loading, int amount)
306 {
307     int move_amt;
308
309     if (amount < 0)
310         move_amt = -amount - unit_amt;
311     else
312         move_amt = loading ? amount : -amount;
313     move_amt = LIMIT_TO(move_amt, -unit_amt, unit_max - unit_amt);
314     move_amt = LIMIT_TO(move_amt, sect_amt - ITEM_MAX, sect_amt);
315     return move_amt;
316 }
317
318 int
319 load_comm_ok(struct sctstr *sectp, natid unit_own,
320              i_type item, int move_amt)
321 {
322     if (!move_amt)
323         return 0;
324     if (move_amt < 0 && !player->god && unit_own != player->cnum)
325         return 0;
326     if (move_amt > 0 && !player->god && sectp->sct_own != player->cnum)
327         return 0;
328     if (sectp->sct_oldown != unit_own && item == I_CIVIL) {
329         pr("%s civilians refuse to %s at %s!\n",
330            (move_amt < 0 ? unit_own : sectp->sct_oldown) == player->cnum
331            ? "Your" : "Foreign",
332            move_amt < 0 ? "disembark" : "board",
333            xyas(sectp->sct_x, sectp->sct_y, player->cnum));
334         return 0;
335     }
336     return 1;
337 }
338
339 void
340 gift(natid givee, natid giver, void *ptr, char *mesg)
341 {
342     if (giver != givee)
343         wu(0, givee, "%s %s %s\n", cname(giver), unit_nameof(ptr), mesg);
344     unit_give_away(ptr, givee, 0);
345 }
346
347 static int
348 still_ok_ship(struct sctstr *sectp, struct shpstr *shipp)
349 {
350     if (!check_sect_ok(sectp))
351         return 0;
352     if (!check_ship_ok(shipp))
353         return 0;
354     return 1;
355 }
356
357 static int
358 still_ok_land(struct sctstr *sectp, struct lndstr *landp)
359 {
360     if (!check_sect_ok(sectp))
361         return 0;
362     if (!check_land_ok(landp))
363         return 0;
364     return 1;
365 }
366
367 static int
368 load_plane_ship(struct sctstr *sectp, struct shpstr *sp, int noisy,
369                 int loading, int *nshipsp)
370 {
371     struct nstr_item ni;
372     struct plnstr pln;
373     int loaded = 0;
374     char buf[1024];
375     char *p;
376     char prompt[512];
377     struct mchrstr *mcp = mchr + sp->shp_type;
378
379     if (mcp->m_nplanes + mcp->m_nchoppers + mcp->m_nxlight == 0) {
380         if (noisy)
381             pr("%s cannot carry planes\n", prship(sp));
382         return 0;
383     }
384     if (loading &&
385         shp_nplane(sp, NULL, NULL, NULL)
386                 >= mcp->m_nchoppers + mcp->m_nxlight + mcp->m_nplanes) {
387         if (noisy)
388             pr("%s doesn't have room for any more planes\n", prship(sp));
389         return 0;
390     }
391     sprintf(prompt, "Plane(s) to %s %s? ",
392             loading ? "load onto" : "unload from", prship(sp));
393     p = getstarg(player->argp[3], prompt, buf);
394     if (!p)
395         return RET_SYN;
396     if (!snxtitem(&ni, EF_PLANE, p, NULL))
397         return RET_SYN;
398
399     if (!still_ok_ship(sectp, sp))
400         return RET_SYN;
401
402     if (noisy)
403         noisy = ni.sel == NS_LIST;
404
405     while (nxtitem(&ni, &pln)) {
406         if (!player->owner)
407             continue;
408         if (!(plchr[(int)pln.pln_type].pl_flags & P_L)
409             && !(plchr[(int)pln.pln_type].pl_flags & P_E)
410             && !(plchr[(int)pln.pln_type].pl_flags & P_K)
411             && !(plchr[(int)pln.pln_type].pl_flags & P_M)
412             ) {
413             if (noisy)
414                 pr("You can only load light planes, helos, xtra-light, or missiles onto ships.\n");
415             continue;
416         }
417         if (loading && pln.pln_ship > -1) {
418             if (noisy)
419                 pr("%s is already on ship #%d!\n",
420                    prplane(&pln), pln.pln_ship);
421             continue;
422         }
423         if (loading && pln.pln_land > -1) {
424             if (noisy)
425                 pr("%s is already on land unit #%d!\n",
426                    prplane(&pln), pln.pln_land);
427             continue;
428         }
429         if (pln.pln_harden != 0) {
430             if (noisy)
431                 pr("%s has been hardened and can't be loaded\n",
432                    prplane(&pln));
433             continue;
434         }
435
436         if (!loading) {
437             if (pln.pln_ship != sp->shp_uid)
438                 continue;
439         } else if (sp->shp_x != pln.pln_x || sp->shp_y != pln.pln_y)
440             continue;
441
442         if (!could_be_on_ship(&pln, sp)) {
443             if (noisy) {
444                 if (plchr[(int)pln.pln_type].pl_flags & P_K)
445                     p = "choppers";
446                 else if (plchr[(int)pln.pln_type].pl_flags & P_E)
447                     p = "extra light planes";
448                 else if (plchr[(int)pln.pln_type].pl_flags & P_M)
449                     p = "missiles";
450                 else
451                     p = "planes";
452                 pr("%s cannot carry %s.\n", prship(sp), p);
453             }
454             continue;
455         }
456         /* Fit plane on ship */
457         if (loading) {
458             if (!put_plane_on_ship(&pln, sp)) {
459                 if (noisy)
460                     pr("Can't put plane %d on this ship!\n", pln.pln_uid);
461                 continue;
462             }
463             sprintf(buf, "loaded on your %s at %s",
464                     prship(sp), xyas(sp->shp_x, sp->shp_y, sp->shp_own));
465             gift(sp->shp_own, player->cnum, &pln, buf);
466             putplane(pln.pln_uid, &pln);
467         } else {
468             pln.pln_ship = -1;
469             sprintf(buf, "unloaded in your %s at %s",
470                     dchr[sectp->sct_type].d_name,
471                     xyas(sectp->sct_x, sectp->sct_y, sectp->sct_own));
472             gift(sectp->sct_own, player->cnum, &pln, buf);
473             putplane(pln.pln_uid, &pln);
474         }
475         pr("%s %s %s at %s.\n",
476            prplane(&pln),
477            loading ? "loaded onto" : "unloaded from",
478            prship(sp), xyas(sp->shp_x, sp->shp_y, player->cnum));
479         loaded = 1;
480     }
481     *nshipsp += loaded;
482     return 0;
483 }
484
485 static int
486 load_land_ship(struct sctstr *sectp, struct shpstr *sp, int noisy,
487                int loading, int *nshipsp)
488 {
489     struct nstr_item ni;
490     struct lndstr land;
491     int loaded = 0;
492     char *p;
493     char prompt[512];
494     char buf[1024];
495     int load_spy = 0;
496
497     if (!mchr[(int)sp->shp_type].m_nland
498         && !(mchr[sp->shp_type].m_flags & M_SUB)) {
499         if (noisy)
500             pr("%s cannot carry land units!\n", prship(sp));
501         return 0;
502     }
503     if (loading) {
504         if ((mchr[(int)sp->shp_type].m_flags & M_SUB) &&
505             (mchr[(int)sp->shp_type].m_nland == 0)) {
506             if (shp_nland(sp) >= 2) {
507                 pr("Non-land unit carrying subs can only carry up to two spy units.\n");
508                 return 0;
509             }
510             /* Eh, let 'em load a spy only */
511             load_spy = 1;
512         }
513         if (!load_spy && shp_nland(sp) >= mchr[sp->shp_type].m_nland) {
514             if (noisy) {
515                 pr("%s doesn't have room for any more land units!\n",
516                    prship(sp));
517             }
518             return 0;
519         }
520     }
521     sprintf(prompt, "Land unit(s) to %s %s? ",
522             loading ? "load onto" : "unload from", prship(sp));
523     p = getstarg(player->argp[3], prompt, buf);
524     if (!p)
525         return RET_SYN;
526     if (!snxtitem(&ni, EF_LAND, p, NULL))
527         return RET_SYN;
528
529     if (!still_ok_ship(sectp, sp))
530         return RET_SYN;
531
532     if (noisy)
533         noisy = ni.sel == NS_LIST;
534
535     while (nxtitem(&ni, &land)) {
536         if (!player->owner)
537             continue;
538
539         if (loading) {
540             if (land.lnd_ship > -1) {
541                 if (noisy)
542                     pr("%s is already on ship #%d!\n",
543                        prland(&land), land.lnd_ship);
544                 continue;
545             }
546             if (land.lnd_land > -1) {
547                 if (noisy)
548                     pr("%s is already on land #%d!\n",
549                        prland(&land), land.lnd_land);
550                 continue;
551             }
552             if (lnd_first_on_land(&land) >= 0) {
553                 if (noisy)
554                     pr("%s cannot be loaded since it is carrying units\n",
555                        prland(&land));
556                 continue;
557             }
558             if (lchr[(int)land.lnd_type].l_flags & L_HEAVY) {
559                 if (noisy)
560                     pr("%s is too heavy to load.\n", prland(&land));
561                 continue;
562             }
563             if (load_spy && !(lchr[(int)land.lnd_type].l_flags & L_SPY)) {
564                 if (noisy)
565                     pr("Subs can only carry spy units.\n");
566                 continue;
567             }
568         }
569
570         /* Unit sanity done */
571         /* Find the right ship */
572         if (!loading) {
573             if (land.lnd_ship != sp->shp_uid)
574                 continue;
575             if (land.lnd_land > -1)
576                 continue;
577         } else if (sp->shp_x != land.lnd_x || sp->shp_y != land.lnd_y)
578             continue;
579
580         if ((!(lchr[(int)land.lnd_type].l_flags & L_LIGHT)) &&
581             (!((mchr[(int)sp->shp_type].m_flags & M_SUPPLY) &&
582                (!(mchr[(int)sp->shp_type].m_flags & M_SUB))))) {
583             if (noisy) {
584                 pr("You can only load light units onto ships,\n");
585                 pr("unless the ship is a non-sub supply ship\n");
586                 pr("%s not loaded\n", prland(&land));
587             }
588             continue;
589         }
590         /* Fit unit on ship */
591         if (loading) {
592             /* We have to check again, since it may have changed */
593             if ((mchr[(int)sp->shp_type].m_flags & M_SUB) &&
594                 (mchr[(int)sp->shp_type].m_nland == 0)) {
595                 if (shp_nland(sp) >= 2) {
596                     pr("Non-land unit carrying subs can only carry up to two spy units.\n");
597                     return 0;
598                 }
599                 /* Eh, let 'em load a spy only */
600                 load_spy = 1;
601             }
602             if (!load_spy && shp_nland(sp) >= mchr[sp->shp_type].m_nland) {
603                 if (noisy) {
604                     if (mchr[(int)sp->shp_type].m_nland)
605                         pr("%s doesn't have room for any more land units!\n",
606                            prship(sp));
607                     else
608                         pr("%s cannot carry land units!\n", prship(sp));
609                 }
610                 return 0;
611             }
612             sprintf(buf, "loaded on your %s at %s",
613                     prship(sp), xyas(sp->shp_x, sp->shp_y, sp->shp_own));
614             gift(sp->shp_own, player->cnum, &land, buf);
615             land.lnd_ship = sp->shp_uid;
616             land.lnd_harden = 0;
617             putland(land.lnd_uid, &land);
618 #if 0
619            /*
620             * FIXME if this supplies from the sector, the putsect in
621             * load() / lload() duplicates those supplies, causing a
622             * seqno mismatch
623             */
624             if (!lnd_supply_all(&land))
625                 pr("WARNING: %s is out of supply!\n", prland(&land));
626 #else
627             if (!lnd_in_supply(&land))
628                 pr("WARNING: %s is out of supply!\n", prland(&land));
629 #endif
630         } else {
631             sprintf(buf, "unloaded in your %s at %s",
632                     dchr[sectp->sct_type].d_name,
633                     xyas(sectp->sct_x, sectp->sct_y, sectp->sct_own));
634
635             /* Spies are unloaded quietly, others aren't */
636             if (!(lchr[(int)land.lnd_type].l_flags & L_SPY))
637                 gift(sectp->sct_own, player->cnum, &land, buf);
638             land.lnd_ship = -1;
639             putland(land.lnd_uid, &land);
640         }
641         pr("%s %s %s at %s.\n",
642            prland(&land),
643            loading ? "loaded onto" : "unloaded from",
644            prship(sp), xyas(sp->shp_x, sp->shp_y, player->cnum));
645         loaded = 1;
646     }
647     *nshipsp += loaded;
648     return 0;
649 }
650
651 static int
652 load_comm_ship(struct sctstr *sectp, struct shpstr *sp,
653                struct ichrstr *ich, int loading, int *nshipsp)
654 {
655     i_type item = ich->i_uid;
656     struct mchrstr *mcp = &mchr[(int)sp->shp_type];
657     int ship_amt, sect_amt, move_amt;
658     char prompt[512];
659     char *p;
660     char buf[1024];
661
662     sprintf(prompt, "Number of %s to %s %s at %s? ",
663             ich->i_name,
664             loading ? "load onto" : "unload from",
665             prship(sp), xyas(sp->shp_x, sp->shp_y, player->cnum));
666     p = getstarg(player->argp[3], prompt, buf);
667     if (!p || !*p)
668         return RET_SYN;
669
670     if (!still_ok_ship(sectp, sp))
671         return RET_SYN;
672
673     ship_amt = sp->shp_item[item];
674     sect_amt = sectp->sct_item[item];
675     move_amt = move_amount(sect_amt, ship_amt, mcp->m_item[item],
676                            loading, atoi(p));
677     if (!load_comm_ok(sectp, sp->shp_own, item, move_amt))
678         return RET_OK;
679     if (!abandon_askyn(sectp, item, move_amt, NULL))
680         return RET_FAIL;
681     if (!still_ok_ship(sectp, sp))
682         return RET_SYN;
683     sectp->sct_item[item] = sect_amt - move_amt;
684     sp->shp_item[item] = ship_amt + move_amt;
685
686     if (move_amt >= 0) {
687         pr("%d %s loaded onto %s at %s\n",
688            move_amt, ich->i_name,
689            prship(sp), xyas(sp->shp_x, sp->shp_y, player->cnum));
690         if (sp->shp_own != player->cnum) {
691             wu(0, sp->shp_own, "%s loaded %d %s onto %s at %s\n",
692                cname(player->cnum), move_amt, ich->i_name,
693                prship(sp), xyas(sp->shp_x, sp->shp_y, sp->shp_own));
694         }
695     } else {
696         pr("%d %s unloaded from %s at %s\n",
697            -move_amt, ich->i_name,
698            prship(sp), xyas(sp->shp_x, sp->shp_y, player->cnum));
699         if (sectp->sct_own != player->cnum) {
700             wu(0, sectp->sct_own, "%s unloaded %d %s from %s at %s\n",
701                cname(player->cnum), -move_amt, ich->i_name,
702                prship(sp), xyas(sp->shp_x, sp->shp_y, sectp->sct_own));
703         }
704     }
705     ++*nshipsp;
706     return 0;
707 }
708
709 static int
710 load_plane_land(struct sctstr *sectp, struct lndstr *lp, int noisy,
711                 int loading, int *nunitsp)
712 {
713     struct nstr_item ni;
714     struct plnstr pln;
715     int loaded = 0;
716     char *p;
717     char prompt[512];
718     char buf[1024];
719     struct lchrstr *lcp = lchr + lp->lnd_type;
720
721     if (!lcp->l_nxlight) {
722         if (noisy)
723             pr("%s cannot carry extra-light planes.\n", prland(lp));
724         return 0;
725     }
726     if (loading && lnd_nxlight(lp) >= lcp->l_nxlight) {
727         if (noisy)
728             pr("%s doesn't have room for any more extra-light planes\n",
729                prland(lp));
730         return 0;
731     }
732     sprintf(prompt, "Plane(s) to %s %s? ",
733             loading ? "load onto" : "unload from", prland(lp));
734     p = getstarg(player->argp[3], prompt, buf);
735     if (!p)
736         return RET_SYN;
737     if (!snxtitem(&ni, EF_PLANE, p, NULL))
738         return RET_SYN;
739
740     if (!still_ok_land(sectp, lp))
741         return RET_SYN;
742
743     if (noisy)
744         noisy = ni.sel == NS_LIST;
745
746     while (nxtitem(&ni, &pln)) {
747         if (!player->owner)
748             continue;
749
750         if (!(plchr[(int)pln.pln_type].pl_flags & P_E)) {
751             if (noisy)
752                 pr("You can only load xlight planes onto units.\n");
753             continue;
754         }
755
756         if (loading && pln.pln_ship > -1) {
757             if (noisy)
758                 pr("%s is already on ship #%d!\n",
759                    prplane(&pln), pln.pln_ship);
760             continue;
761         }
762         if (loading && pln.pln_land > -1) {
763             if (noisy)
764                 pr("%s is already on unit #%d!\n",
765                    prplane(&pln), pln.pln_land);
766             continue;
767         }
768         if (pln.pln_harden != 0) {
769             if (noisy)
770                 pr("%s has been hardened and can't be loaded\n",
771                    prplane(&pln));
772             continue;
773         }
774
775         /* Plane sanity done */
776         /* Find the right unit */
777         if (!loading) {
778             if (pln.pln_land != lp->lnd_uid)
779                 continue;
780         } else if (lp->lnd_x != pln.pln_x || lp->lnd_y != pln.pln_y)
781             continue;
782
783         /* Fit plane on unit */
784         if (loading) {
785             if (!put_plane_on_land(&pln, lp)) {
786                 if (noisy)
787                     pr("Can't put plane %d on this unit!\n", pln.pln_uid);
788                 continue;
789             }
790             sprintf(buf, "loaded on %s at %s",
791                     prland(lp), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
792             gift(lp->lnd_own, player->cnum, &pln, buf);
793             putplane(pln.pln_uid, &pln);
794         } else {
795             pln.pln_land = -1;
796             sprintf(buf, "unloaded at your sector at %s",
797                     xyas(sectp->sct_x, sectp->sct_y, sectp->sct_own));
798             gift(sectp->sct_own, player->cnum, &pln, buf);
799             putplane(pln.pln_uid, &pln);
800         }
801         pr("%s %s %s at %s.\n",
802            prplane(&pln),
803            loading ? "loaded onto" : "unloaded from",
804            prland(lp), xyas(lp->lnd_x, lp->lnd_y, player->cnum));
805         loaded = 1;
806     }
807     *nunitsp += loaded;
808     return 0;
809 }
810
811 static int
812 load_comm_land(struct sctstr *sectp, struct lndstr *lp,
813                struct ichrstr *ich, int loading, int *nunitsp)
814 {
815     i_type item = ich->i_uid;
816     struct lchrstr *lcp = &lchr[(int)lp->lnd_type];
817     int land_amt, sect_amt, move_amt;
818     char prompt[512];
819     char *p;
820     char buf[1024];
821
822     sprintf(prompt, "Number of %s to %s %s at %s? ",
823             ich->i_name,
824             loading ? "load onto" : "unload from",
825             prland(lp), xyas(lp->lnd_x, lp->lnd_y, player->cnum));
826     p = getstarg(player->argp[3], prompt, buf);
827     if (!p || !*p)
828         return RET_SYN;
829
830     if (!still_ok_land(sectp, lp))
831         return RET_SYN;
832
833     land_amt = lp->lnd_item[item];
834     sect_amt = sectp->sct_item[item];
835     move_amt = move_amount(sect_amt, land_amt, lcp->l_item[item],
836                            loading, atoi(p));
837     if (!load_comm_ok(sectp, lp->lnd_own, item, move_amt))
838         return RET_OK;
839     sectp->sct_item[item] = sect_amt - move_amt;
840     lp->lnd_item[item] = land_amt + move_amt;
841
842     /* Did we put mils onto this unit? If so, reset the fortification */
843     if (item == I_MILIT && move_amt > 0)
844         lp->lnd_harden = 0;
845
846     if (move_amt >= 0) {
847         pr("%d %s loaded onto %s at %s\n",
848            move_amt, ich->i_name,
849            prland(lp), xyas(lp->lnd_x, lp->lnd_y, player->cnum));
850         if (lp->lnd_own != player->cnum) {
851             wu(0, lp->lnd_own, "%s loaded %d %s onto %s at %s\n",
852                cname(player->cnum), move_amt, ich->i_name,
853                prland(lp), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
854         }
855     } else {
856         pr("%d %s unloaded from %s at %s\n",
857            -move_amt, ich->i_name,
858            prland(lp), xyas(lp->lnd_x, lp->lnd_y, player->cnum));
859         if (sectp->sct_own != player->cnum) {
860             wu(0, sectp->sct_own, "%s unloaded %d %s from %s at %s\n",
861                cname(player->cnum), -move_amt, ich->i_name,
862                prland(lp), xyas(lp->lnd_x, lp->lnd_y, sectp->sct_own));
863         }
864     }
865     ++*nunitsp;
866     return 0;
867 }
868
869 static int
870 load_land_land(struct sctstr *sectp, struct lndstr *lp, int noisy,
871                int loading, int *nunitsp)
872 {
873     struct nstr_item ni;
874     struct lndstr land;
875     int loaded = 0;
876     char *p;
877     char prompt[512];
878     char buf[1024];
879
880     if (!lchr[lp->lnd_type].l_nland) {
881         if (noisy)
882             pr("%s cannot carry land units!\n", prland(lp));
883         return 0;
884     }
885     if (loading && lnd_nland(lp) >= lchr[lp->lnd_type].l_nland) {
886         if (noisy)
887             pr("%s doesn't have room for any more land units!\n",
888                prland(lp));
889         return 0;
890     }
891     sprintf(prompt, "Land unit(s) to %s %s? ",
892             loading ? "load onto" : "unload from", prland(lp));
893     p = getstarg(player->argp[3], prompt, buf);
894     if (!p)
895         return RET_SYN;
896     if (!snxtitem(&ni, EF_LAND, p, NULL))
897         return RET_SYN;
898
899     if (!still_ok_land(sectp, lp))
900         return RET_SYN;
901
902     if (noisy)
903         noisy = ni.sel == NS_LIST;
904
905     while (nxtitem(&ni, &land)) {
906         if (!player->owner)
907             continue;
908
909         if (loading) {
910             if (land.lnd_ship > -1) {
911                 if (noisy)
912                     pr("%s is already on ship #%d!\n",
913                        prland(&land), land.lnd_ship);
914                 continue;
915             }
916             if (land.lnd_land > -1) {
917                 if (noisy)
918                     pr("%s is already on land #%d!\n",
919                        prland(&land), land.lnd_land);
920                 continue;
921             }
922             if (lnd_first_on_land(&land) >= 0) {
923                 if (noisy)
924                     pr("%s cannot be loaded since it is carrying units\n",
925                        prland(&land));
926                 continue;
927             }
928             if (land.lnd_uid == lp->lnd_uid) {
929                 if (noisy)
930                     pr("%s can't be loaded onto itself!\n", prland(&land));
931                 continue;
932             }
933             if (lchr[(int)land.lnd_type].l_flags & (L_HEAVY | L_TRAIN)) {
934                 if (noisy)
935                     pr("%s is too heavy to load.\n", prland(&land));
936                 continue;
937             }
938         }
939
940         /* Unit sanity done */
941         /* Find the right ship */
942         if (!loading) {
943             if (land.lnd_land != lp->lnd_uid)
944                 continue;
945             if (land.lnd_ship > -1)
946                 continue;
947         } else if (lp->lnd_x != land.lnd_x || lp->lnd_y != land.lnd_y)
948             continue;
949
950         /* Fit unit on ship */
951         if (loading) {
952             if (lnd_nland(lp) >= lchr[lp->lnd_type].l_nland) {
953                 if (noisy) {
954                     if (lchr[lp->lnd_type].l_nland)
955                         pr("%s doesn't have room for any more land units!\n",
956                            prland(lp));
957                     else
958                         pr("%s cannot carry land units!\n", prland(lp));
959                 }
960                 break;
961             }
962             sprintf(buf, "loaded on your %s at %s",
963                     prland(lp), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
964             gift(lp->lnd_own, player->cnum, &land, buf);
965             land.lnd_land = lp->lnd_uid;
966             land.lnd_harden = 0;
967             putland(land.lnd_uid, &land);
968 #if 0
969            /* FIXME same issue as in load_land_ship() */
970             if (!lnd_supply_all(&land))
971                 pr("WARNING: %s is out of supply!\n", prland(&land));
972 #else
973             if (!lnd_in_supply(&land))
974                 pr("WARNING: %s is out of supply!\n", prland(&land));
975 #endif
976         } else {
977             sprintf(buf, "unloaded in your %s at %s",
978                     dchr[sectp->sct_type].d_name,
979                     xyas(sectp->sct_x, sectp->sct_y, sectp->sct_own));
980             gift(sectp->sct_own, player->cnum, &land, buf);
981             land.lnd_land = -1;
982             putland(land.lnd_uid, &land);
983         }
984         pr("%s %s %s at %s.\n",
985            prland(&land),
986            loading ? "loaded onto" : "unloaded from",
987            prland(lp), xyas(lp->lnd_x, lp->lnd_y, player->cnum));
988         loaded = 1;
989     }
990     *nunitsp += loaded;
991     return 0;
992 }