]> git.pond.sub.org Git - empserver/blob - src/lib/subs/plnsub.c
Remove columns lnd, pln from spy report to fix spy unit leak
[empserver] / src / lib / subs / plnsub.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, 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 files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  plnsub.c: Plane subroutine stuff
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Ken Stevens, 1995
33  *     Steve McClure, 1998-2000
34  *     Markus Armbruster, 2004-2008
35  */
36
37 #include <config.h>
38
39 #include "file.h"
40 #include "item.h"
41 #include "land.h"
42 #include "lost.h"
43 #include "map.h"
44 #include "misc.h"
45 #include "nat.h"
46 #include "nsc.h"
47 #include "nuke.h"
48 #include "optlist.h"
49 #include "path.h"
50 #include "plane.h"
51 #include "player.h"
52 #include "prototypes.h"
53 #include "sect.h"
54 #include "ship.h"
55 #include "xy.h"
56
57 static int pln_equip(struct plist *, struct ichrstr *, int, char);
58 static int fit_plane_on_ship(struct plnstr *, struct shpstr *);
59
60 /*
61  * Get assembly point argument.
62  * If INPUT is not empty, use it, else prompt for more input using PROMPT.
63  * If this yields a valid assembly point, read it into *AP_SECT and
64  * return AP_SECT.
65  * Else complain and return NULL.
66  * *AP_SECT and BUF[1024] may be modified in either case.
67  */
68 struct sctstr *
69 get_assembly_point(char *input, struct sctstr *ap_sect, char *buf)
70 {
71     char *p;
72     coord x, y;
73     struct nstr_item ni;
74     struct shpstr ship;
75
76     p = getstarg(input, "assembly point? ", buf);
77     if (!p || *p == 0)
78         return NULL;
79     if (!sarg_xy(p, &x, &y) || !getsect(x, y, ap_sect))
80         return NULL;
81
82     /* over own or allied sector is fine */
83     if (ap_sect->sct_own == player->cnum
84         || getrel(getnatp(ap_sect->sct_own), player->cnum) == ALLIED)
85         return ap_sect;
86
87     /* over own or allied ship is fine */
88     snxtitem_xy(&ni, EF_SHIP, x, y);
89     while (nxtitem(&ni, &ship)) {
90         if (ship.shp_effic < SHIP_MINEFF || ship.shp_own == 0)
91             continue;
92         if (ship.shp_own == player->cnum
93             || getrel(getnatp(ship.shp_own), player->cnum) == ALLIED)
94             return ap_sect;
95     }
96
97     pr("Assembly point not owned by you or an ally!\n");
98     return NULL;
99 }
100
101 int
102 pln_onewaymission(struct sctstr *target, int *shipno, int *flagp)
103 {
104     int nships;
105     int cno;
106     int flags, fl;
107     struct shpstr ship;
108     char buf[1024];
109     char *p;
110
111     flags = *flagp;
112
113     /* offer carriers */
114     nships = carriersatxy(target->sct_x, target->sct_y, player->cnum);
115     if (nships) {
116         for (;;) {
117             if (!(p = getstarg(0, "Carrier #? ", buf)) || !*p)
118                 break;
119             cno = atoi(p);
120             if (cno < 0
121                 || !getship(cno, &ship)
122                 || (!player->owner
123                     && (getrel(getnatp(ship.shp_own), player->cnum)
124                         != ALLIED))) {
125                 pr("Not yours\n");
126                 continue;
127             }
128             if (ship.shp_x != target->sct_x || ship.shp_y != target->sct_y) {
129                 pr("Ship #%d not in %s\n", cno,
130                    xyas(target->sct_x, target->sct_y, player->cnum));
131                 continue;
132             }
133             fl = carrier_planes(&ship, 0);
134             if (fl == 0) {
135                 pr("Can't land on %s.\n", prship(&ship));
136                 continue;
137             }
138             /* clear to land on ship#CNO */
139             pr("landing on carrier %d\n", cno);
140             flags |= fl;
141             *shipno = cno;
142             *flagp = flags;
143             return 0;
144         }
145     }
146
147     /* try to land at sector */
148     if (target->sct_own != player->cnum
149         && getrel(getnatp(target->sct_own), player->cnum) != ALLIED) {
150         pr("Nowhere to land at sector %s!\n",
151            xyas(target->sct_x, target->sct_y, player->cnum));
152         return -1;
153     }
154     if (target->sct_type == SCT_MOUNT) {
155         pr("Nowhere to land at sector %s!\n",
156            xyas(target->sct_x, target->sct_y, player->cnum));
157         return -1;
158     }
159     if (target->sct_type != SCT_AIRPT || target->sct_effic < 60)
160         flags |= P_V;
161
162     /* clear to land at sector */
163     *shipno = -1;
164     *flagp = flags;
165     return 0;
166 }
167
168 int
169 pln_oneway_to_carrier_ok(struct emp_qelem *bomb_list,
170                          struct emp_qelem *esc_list, int cno)
171 {
172     struct emp_qelem *list, *qp;
173     struct plist *plp;
174     struct shpstr ship;
175
176     if (cno < 0 || !getship(cno, &ship))
177         return 0;
178
179     count_planes(&ship);
180
181     /* for both lists */
182     for (list = bomb_list;
183          list;
184          list = list == bomb_list ? esc_list : NULL) {
185         for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
186             plp = (struct plist *)qp;
187             if (plp->plane.pln_ship == ship.shp_uid)
188                 continue;
189             if (!fit_plane_on_ship(&plp->plane, &ship))
190                 return 0;
191         }
192     }
193     return 1;
194 }
195
196 void
197 pln_newlanding(struct emp_qelem *list, coord tx, coord ty, int cno)
198 {
199     struct emp_qelem *qp;
200     struct plist *plp;
201     struct shpstr ship;
202     struct sctstr sect;
203
204     if (cno >= 0)
205         getship(cno, &ship);
206     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
207         plp = (struct plist *)qp;
208         if (cno >= 0) {
209             count_planes(&ship);
210             if (!could_be_on_ship(&plp->plane, &ship))
211                 pr("\t%s cannot land on ship #%d! %s aborts!\n",
212                    prplane(&plp->plane), cno, prplane(&plp->plane));
213             else if (!put_plane_on_ship(&plp->plane, &ship))
214                 pr("\tNo room on ship #%d! %s aborts!\n",
215                    cno, prplane(&plp->plane));
216             else {
217                 if (plp->plane.pln_own != ship.shp_own) {
218                     wu(0, ship.shp_own, "%s %s lands on your %s\n",
219                        cname(player->cnum), prplane(&plp->plane),
220                        prship(&ship));
221                 }
222             }
223         } else {
224             plp->plane.pln_x = tx;
225             plp->plane.pln_y = ty;
226             getsect(tx, ty, &sect);
227             if (plp->plane.pln_own != sect.sct_own) {
228                 wu(0, sect.sct_own,
229                    "%s %s lands at your sector %s\n",
230                    cname(player->cnum),
231                    prplane(&plp->plane), xyas(tx, ty, sect.sct_own));
232             }
233             plp->plane.pln_ship = cno;
234         }
235     }
236     if (cno >= 0)
237         putship(ship.shp_uid, &ship);
238 }
239
240 void
241 pln_dropoff(struct emp_qelem *list, struct ichrstr *ip, coord tx, coord ty,
242             void *ptr, int type)
243 {
244     struct emp_qelem *qp;
245     struct plist *plp;
246     int amt;
247     struct sctstr *sectp;
248     struct shpstr *sp;
249     int there;
250     int max;
251     struct mchrstr *mp;
252
253     if (ip == 0)
254         return;
255     amt = 0;
256     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
257         plp = (struct plist *)qp;
258         amt += plp->misc;
259     }
260     if (type == EF_SECTOR) {
261         sectp = ptr;
262         if (!sectp->sct_own) {
263             if (sectp->sct_type == SCT_WATER)
264                 pr("Your %s sink like a rock!\n", ip->i_name);
265             else
266                 pr("Your %s vanish without a trace.\n", ip->i_name);
267             return;
268         }
269         if (sectp->sct_own != player->cnum
270             && getrel(getnatp(sectp->sct_own), player->cnum) != ALLIED) {
271             pr("You don't own %s!  Cargo jettisoned...\n",
272                xyas(tx, ty, player->cnum));
273             return;
274         }
275         if (ip->i_uid == I_CIVIL && sectp->sct_own != sectp->sct_oldown) {
276             pr("%s is occupied.  Your civilians suffer from identity crisis and die.\n",
277                xyas(tx, ty, player->cnum));
278             return;
279         }
280         there = sectp->sct_item[ip->i_uid];
281         max = ITEM_MAX;
282     } else {
283         sp = ptr;
284         there = sp->shp_item[ip->i_uid];
285         mp = &mchr[(int)sp->shp_type];
286         max = mp->m_item[ip->i_uid];
287     }
288     there += amt;
289     if (there > max) {
290         pr("%d excess %s discarded\n", max - there, ip->i_name);
291         amt = max - there;
292         there = max;
293     }
294     pr("%d %s landed safely", amt, ip->i_name);
295     if (type == EF_SECTOR) {
296         sectp = ptr;
297         sectp->sct_item[ip->i_uid] = there;
298         if (sectp->sct_own != player->cnum)
299             wu(0, sectp->sct_own, "%s planes drop %d %s in %s\n",
300                cname(player->cnum), amt, ip->i_name,
301                xyas(sectp->sct_x, sectp->sct_y, sectp->sct_own));
302         pr(" at %s\n", xyas(tx, ty, player->cnum));
303         putsect((struct sctstr *)ptr);
304     } else {
305         struct shpstr *sp = (struct shpstr *)ptr;
306         sp->shp_item[ip->i_uid] = there;
307         if (sp->shp_own != player->cnum)
308             wu(0, sp->shp_own, "%s planes land %d %s on carrier %d\n",
309                cname(player->cnum), amt, ip->i_name, sp->shp_uid);
310         pr(" on carrier #%d\n", sp->shp_uid);
311         putship(sp->shp_uid, sp);
312     }
313 }
314
315 void
316 pln_mine(struct emp_qelem *list, struct sctstr *sectp)
317 {
318     struct emp_qelem *qp;
319     struct plist *plp;
320     int amt;
321
322     amt = 0;
323     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
324         plp = (struct plist *)qp;
325         amt += plp->misc;
326
327     }
328     if (amt > 0) {
329         if (sectp->sct_type != SCT_WATER) {
330             pr("Your seamines have no effect here.\n");
331             return;
332         }
333         sectp->sct_mines = MIN(sectp->sct_mines + amt, MINES_MAX);
334         pr("%d mines laid in %s.\n", amt,
335            xyas(sectp->sct_x, sectp->sct_y, player->cnum));
336         if (map_set(player->cnum, sectp->sct_x, sectp->sct_y, 'X', 0))
337             writemap(player->cnum);
338         putsect(sectp);
339     }
340 }
341
342 /*
343  * Has PP's type capabilities satisfying WANTFLAGS and NOWANTFLAGS?
344  * A plane type is capable unless
345  * - it lacks all of the P_F, P_ESC in WANTFLAGS, or
346  * - it lacks all of the P_E, P_L, P_K in WANTFLAGS, or
347  * - it lacks any of the other capabilities in WANTFLAGS, or
348  * - it has any of the capabilities in NOWANTFLAGS.
349  */
350 int
351 pln_capable(struct plnstr *pp, int wantflags, int nowantflags)
352 {
353     int flags = plchr[(int)pp->pln_type].pl_flags;
354
355     if (wantflags & (P_F | P_ESC)) {
356         if ((flags & wantflags & (P_F | P_ESC)) == 0)
357             return 0;
358         wantflags &= ~(P_F | P_ESC);
359     }
360
361     if (wantflags & (P_E | P_L | P_K)) {
362         if ((flags & wantflags & (P_E | P_L | P_K)) == 0)
363             return 0;
364         wantflags &= ~(P_E | P_L | P_K);
365     }
366
367     if ((flags & wantflags) != wantflags)
368         return 0;
369
370     if (flags & nowantflags)
371         return 0;
372
373     return 1;
374 }
375
376 /*
377  * Find plane types that can operate from carrier SP.
378  * If MSL find missile types, else non-missile types.
379  * Return a combination of P_L, P_K, P_E.
380  * It's zero if SP can't support air operations due to its type or
381  * state (low efficiency).
382  */
383 int
384 carrier_planes(struct shpstr *sp, int msl)
385 {
386     struct mchrstr *mcp = mchr + sp->shp_type;
387     int res;
388
389     if (sp->shp_effic < SHP_AIROPS_EFF)
390         return 0;
391
392     res = 0;
393     if (mcp->m_flags & M_FLY)
394         res |= P_L;
395     if ((mcp->m_flags & M_MSL) && msl)
396         res |= P_L;
397     if (mcp->m_nchoppers && !msl)
398         res |= P_K;
399     if (mcp->m_nxlight)
400         res |= P_E;
401     return res;
402 }
403
404 int
405 pln_airbase_ok(struct plnstr *pp, int oneway, int noisy)
406 {
407     struct shpstr ship;
408     struct lndstr land;
409     struct sctstr sect;
410     struct plchrstr *pcp = plchr + pp->pln_type;
411
412     if (CANT_HAPPEN(noisy && pp->pln_own != player->cnum))
413         noisy = 0;
414
415     if (pp->pln_ship >= 0) {
416         /* ship: needs to be own or allied, efficient */
417         if (!getship(pp->pln_ship, &ship)) {
418             CANT_REACH();
419             return 0;
420         }
421         if (ship.shp_own != pp->pln_own
422             && getrel(getnatp(ship.shp_own), pp->pln_own) != ALLIED) {
423             if (noisy)
424                 pr("(note) An ally does not own the ship %s is on\n",
425                    prplane(pp));
426             return 0;
427         }
428         if (!(carrier_planes(&ship, pcp->pl_flags & P_M) & pcp->pl_flags))
429             return 0;
430
431     } else if (pp->pln_land >= 0) {
432         /* land: needs to be own or allied, efficient, not embarked */
433         if (!getland(pp->pln_land, &land)) {
434             CANT_REACH();
435             return 0;
436         }
437         if (land.lnd_own != pp->pln_own
438             && getrel(getnatp(land.lnd_own), pp->pln_own) != ALLIED) {
439             if (noisy)
440                 pr("(note) An ally does not own the unit %s is on\n",
441                    prplane(pp));
442             return 0;
443         }
444         if (land.lnd_effic < LND_AIROPS_EFF || !(pcp->pl_flags & P_E))
445             return 0;
446         if (land.lnd_ship >= 0 || land.lnd_land >= 0)
447             return 0;
448
449     } else {
450         /* sector: needs to be own or allied, efficient airfield */
451         if (!getsect(pp->pln_x, pp->pln_y, &sect)) {
452             CANT_REACH();
453             return 0;
454         }
455
456         if (sect.sct_own != pp->pln_own
457             && getrel(getnatp(sect.sct_own), pp->pln_own) != ALLIED) {
458             if (noisy)
459                 pr("(note) An ally does not own the sector %s is in\n",
460                    prplane(pp));
461             return 0;
462         }
463         /* need airfield unless VTOL */
464         if ((pcp->pl_flags & P_V) == 0) {
465             if (sect.sct_type != SCT_AIRPT) {
466                 if (noisy)
467                     pr("%s not at airport\n", prplane(pp));
468                 return 0;
469             }
470             if (sect.sct_effic < 40) {
471                 if (noisy)
472                     pr("%s is not 40%% efficient, %s can't take off from there.\n",
473                        xyas(sect.sct_x, sect.sct_y, pp->pln_own),
474                        prplane(pp));
475                 return 0;
476             }
477             if (!oneway && sect.sct_effic < 60) {
478                 if (noisy)
479                     pr("%s is not 60%% efficient, %s can't land there.\n",
480                        xyas(sect.sct_x, sect.sct_y, pp->pln_own),
481                        prplane(pp));
482                 return 0;
483             }
484         }
485     }
486
487     return 1;
488 }
489
490 void
491 pln_sel(struct nstr_item *ni, struct emp_qelem *list, struct sctstr *ap,
492         int ap_to_target, int rangemult, int wantflags, int nowantflags)
493 {
494     struct plnstr plane;
495     int range;
496     struct plchrstr *pcp;
497     struct plist *plp;
498
499     emp_initque(list);
500     while (nxtitem(ni, &plane)) {
501         if (!player->owner)
502             continue;
503         if (plane.pln_mobil <= 0)
504             continue;
505         if (plane.pln_effic < 40) {
506             pr("%s not efficient enough (must be 40%%)\n",
507                prplane(&plane));
508             continue;
509         }
510         if (!pln_capable(&plane, wantflags, nowantflags))
511             continue;
512         if (opt_MARKET) {
513             if (ontradingblock(EF_PLANE, &plane)) {
514                 pr("plane #%d inelligible - it's for sale.\n",
515                    plane.pln_uid);
516                 continue;
517             }
518         }
519
520         range = mapdist(plane.pln_x, plane.pln_y, ap->sct_x, ap->sct_y);
521         if (range > 4) {
522             pr("%s too far from assembly point\n", prplane(&plane));
523             continue;
524         }
525         range += ap_to_target;
526         range *= rangemult;
527         pcp = &plchr[(int)plane.pln_type];
528         if (plane.pln_range < range) {
529             pr("%s out of range (%d:%d)\n",
530                prplane(&plane), plane.pln_range, range);
531             continue;
532         }
533         if (!pln_airbase_ok(&plane, rangemult != 2, 1))
534             continue;
535         pr("%s standing by\n", prplane(&plane));
536         plane.pln_mission = 0;
537         putplane(plane.pln_uid, &plane);
538         plp = malloc(sizeof(struct plist));
539         plp->misc = 0;
540         plp->bombs = 0;
541         plp->pcp = pcp;
542         plp->plane = plane;
543         emp_insque(&plp->queue, list);
544     }
545 }
546
547 int
548 pln_arm(struct emp_qelem *list, int dist, char mission, struct ichrstr *ip,
549         int flags, int mission_flags)
550 {
551     struct emp_qelem *qp;
552     struct emp_qelem *next;
553     struct plist *plp;
554     struct plnstr *pp;
555
556     for (qp = list->q_forw; qp != list; qp = next) {
557         next = qp->q_forw;
558         plp = (struct plist *)qp;
559         pp = &plp->plane;
560         if ((pp->pln_flags & PLN_LAUNCHED)
561             || pln_equip(plp, ip, flags, mission) < 0) {
562             emp_remque(qp);
563             free(qp);
564             continue;
565         }
566         if (flags & (P_S | P_I)) {
567             if (plp->pcp->pl_flags & P_S)
568                 mission_flags |= P_S;
569             if (plp->pcp->pl_flags & P_I)
570                 mission_flags |= P_I;
571         }
572         if (!(plp->pcp->pl_flags & P_H))
573             /* no stealth on this mission */
574             mission_flags &= ~P_H;
575         if (!(plp->pcp->pl_flags & P_X))
576             /* no stealth on this mission */
577             mission_flags &= ~P_X;
578         if (!(plp->pcp->pl_flags & P_A)) {
579             /* no asw on this mission */
580             mission_flags &= ~P_A;
581             /* FIXME escorts turn ASW patrol into ordinary recon */
582         }
583         if (!(plp->pcp->pl_flags & P_MINE)) {
584             /* no asw on this mission */
585             mission_flags &= ~P_MINE;
586             /* FIXME no effect */
587         }
588         pp->pln_flags |= PLN_LAUNCHED;
589         pp->pln_mobil -= pln_mobcost(dist, pp, flags);
590         putplane(pp->pln_uid, pp);
591         pr("%s equipped\n", prplane(pp));
592     }
593     return mission_flags;
594 }
595
596 static int
597 pln_equip(struct plist *plp, struct ichrstr *ip, int flags, char mission)
598 {
599     struct plchrstr *pcp;
600     struct plnstr *pp;
601     int load, needed;
602     struct lndstr land;
603     struct shpstr ship;
604     struct sctstr sect;
605     i_type itype;
606     short *item;
607     int own;
608     int abandon_needed;
609
610     pp = &plp->plane;
611     pcp = plp->pcp;
612     if (pp->pln_ship >= 0) {
613         getship(pp->pln_ship, &ship);
614         item = ship.shp_item;
615         own = ship.shp_own;
616     } else if (pp->pln_land >= 0) {
617         getland(pp->pln_land, &land);
618         item = land.lnd_item;
619         own = land.lnd_own;
620     } else {
621         getsect(pp->pln_x, pp->pln_y, &sect);
622         item = sect.sct_item;
623         own = sect.sct_oldown;
624     }
625     if (ip) {
626         if (ip->i_uid == I_CIVIL) {
627             if (pp->pln_own != own) {
628                 pr("You don't control those civilians!\n");
629                 return -1;
630             }
631         }
632     }
633     if (pcp->pl_fuel > item[I_PETROL]) {
634         pr("%s not enough petrol there!\n", prplane(pp));
635         return -1;
636     }
637     item[I_PETROL] -= pcp->pl_fuel;
638     if ((flags & P_F) == 0) {
639         load = pln_load(pp);
640         itype = I_NONE;
641         needed = 0;
642         switch (mission) {
643         case 's':               /* strategic bomb */
644         case 'p':               /* pinpoint bomb */
645             if (pp->pln_nuketype == -1) {
646                 itype = I_SHELL;
647                 needed = load;
648             }
649             break;
650         case 't':               /* transport */
651         case 'd':               /* drop */
652             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
653                 break;
654             itype = ip->i_uid;
655             needed = (load * 2) / ip->i_lbs;
656             break;
657         case 'm':               /* mine */
658             if ((pcp->pl_flags & P_MINE) == 0)
659                 break;
660             itype = I_SHELL;
661             needed = (load * 2) / ip->i_lbs;
662             break;
663         case 'a':               /* paradrop */
664             if ((pcp->pl_flags & (P_V | P_C)) == 0)
665                 break;
666             itype = I_MILIT;
667             needed = load / ip->i_lbs;
668             break;
669         case 'r':               /* reconnaissance */
670             break;
671         default:
672             CANT_REACH();
673         }
674         if (itype != I_NONE && needed <= 0) {
675             pr("%s can't contribute to mission\n", prplane(pp));
676             return -1;
677         }
678         if (itype != I_NONE) {
679 #if 0
680             /* Supply is broken somewhere, so don't use it for now */
681             if (itype == I_SHELL && item[itype] < needed)
682                 item[itype] += supply_commod(plp->plane.pln_own,
683                                              plp->plane.pln_x,
684                                              plp->plane.pln_y,
685                                              I_SHELL, needed);
686 #endif
687             abandon_needed = !!would_abandon(&sect, itype, needed, NULL);
688             if (item[itype] < needed + abandon_needed) {
689                 pr("Not enough %s for %s\n", ichr[itype].i_name, prplane(pp));
690                 return -1;
691             }
692             item[itype] -= needed;
693         }
694         if (itype == I_SHELL && (mission == 's' || mission == 'p'))
695             plp->bombs = needed;
696         else
697             plp->misc = needed;
698     }
699     if (pp->pln_ship >= 0) {
700         if (pp->pln_own != ship.shp_own) {
701             wu(0, ship.shp_own,
702                "%s %s prepares for takeoff from ship %s\n",
703                cname(pp->pln_own), prplane(pp), prship(&ship));
704         }
705         putship(ship.shp_uid, &ship);
706     } else if (pp->pln_land >= 0) {
707         if (pp->pln_own != land.lnd_own) {
708             wu(0, land.lnd_own,
709                "%s %s prepares for takeoff from unit %s\n",
710                cname(pp->pln_own), prplane(pp), prland(&land));
711         }
712         putland(land.lnd_uid, &land);
713     } else {
714         if (pp->pln_own != sect.sct_own) {
715             wu(0, sect.sct_own, "%s %s prepares for takeoff from %s\n",
716                cname(pp->pln_own), prplane(pp),
717                xyas(sect.sct_x, sect.sct_y, sect.sct_own));
718         }
719         putsect(&sect);
720     }
721     return 0;
722 }
723
724 void
725 pln_put(struct emp_qelem *list)
726 {
727     struct emp_qelem *qp, *next;
728
729     for (qp = list->q_forw; qp != list; qp = next) {
730         next = qp->q_forw;
731         pln_put1((struct plist *)qp);
732     }
733 }
734
735 void
736 pln_put1(struct plist *plp)
737 {
738     struct plnstr *pp;
739     struct shpstr ship;
740     struct lndstr land;
741     struct sctstr sect;
742
743     pp = &plp->plane;
744
745     if (CANT_HAPPEN((pp->pln_flags & PLN_LAUNCHED)
746                     && (plchr[pp->pln_type].pl_flags & P_M)
747                     && pp->pln_effic >= PLANE_MINEFF))
748         pp->pln_effic = 0;   /* bug: missile launched but not used up */
749
750     if (!(pp->pln_flags & PLN_LAUNCHED))
751         ;                       /* never took off */
752     else if (pp->pln_effic < PLANE_MINEFF) {
753         /* destroyed */
754         if (pp->pln_ship >= 0) {
755             getship(pp->pln_ship, &ship);
756             take_plane_off_ship(pp, &ship);
757         }
758         if (pp->pln_land >= 0) {
759             getland(pp->pln_land, &land);
760             take_plane_off_land(pp, &land);
761         }
762     } else if (pp->pln_ship >= 0) {
763         /* It is landing on a carrier */
764         getship(pp->pln_ship, &ship);
765         /* We should do more, like make sure it's really
766            a carrier, etc. but for now just make sure it's
767            not sunk. */
768         if (ship.shp_effic < SHIP_MINEFF) {
769             mpr(pp->pln_own,
770                 "Ship #%d has been sunk, plane #%d has nowhere to land, and\n"
771                 "splashes into the sea.\n",
772                 pp->pln_ship, pp->pln_uid);
773             pp->pln_effic = 0;
774         }
775     } else {
776         /* Presume we are landing back in a sector. */
777         getsect(pp->pln_x, pp->pln_y, &sect);
778         if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
779             mpr(pp->pln_own,
780                 "Nowhere to land at %s, plane #%d crashes and burns...\n",
781                 xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
782             pp->pln_effic = 0;
783         }
784     }
785     pp->pln_flags &= ~PLN_LAUNCHED;
786     putplane(pp->pln_uid, pp);
787     emp_remque(&plp->queue);
788     free(plp);
789 }
790
791 /*
792  * Fit a plane of PP's type on ship SP.
793  * Adjust SP's plane counters.
794  * Updating the plane accordingly is the caller's job.
795  * Return whether it fits.
796  */
797 static int
798 fit_plane_on_ship(struct plnstr *pp, struct shpstr *sp)
799 {
800     struct plchrstr *pcp = plchr + pp->pln_type;
801     struct mchrstr *mcp = mchr + sp->shp_type;
802     int wanted;
803
804     if (pcp->pl_flags & P_K) {
805         /* chopper, try chopper slot first */
806         if (sp->shp_nchoppers < mcp->m_nchoppers)
807             return ++sp->shp_nchoppers;
808         /* else try plane slot */
809         wanted = M_FLY;
810     } else if (pcp->pl_flags & P_E) {
811         /* x-light, try x-light slot first */
812         if (sp->shp_nxlight < mcp->m_nxlight)
813             return ++sp->shp_nxlight;
814         /* else try plane slot */
815         wanted = M_MSL | M_FLY;
816     } else if (!(pcp->pl_flags & P_L)) {
817         /* not light, no go */
818         wanted = 0;
819     } else if (pcp->pl_flags & P_M) {
820         /* missile, use plane slot */
821         wanted = M_MSL | M_FLY;
822     } else {
823         /* fixed-wing plane, use plane slot */
824         wanted = M_FLY;
825     }
826
827     if ((mcp->m_flags & wanted) == 0)
828         return 0;               /* ship not capable */
829
830     if (sp->shp_nplane < mcp->m_nplanes)
831         return ++sp->shp_nplane;
832
833     return 0;
834 }
835
836 /*
837  * Fit a plane of PP's type off ship SP.
838  * Adjust SP's plane counters, badly.  You need to run count_planes()
839  * before the next fit_plane_on_ship().
840  * Updating the plane accordingly is the caller's job.
841  */
842 static void
843 fit_plane_off_ship(struct plnstr *pp, struct shpstr *sp)
844 {
845     /*
846      * Frees chopper and nxlight slots first, which is why we need to
847      * run count_planes() before fit_plane_on_ship().
848      */
849     struct plchrstr *pcp = plchr + pp->pln_type;
850
851     if (pcp->pl_flags & P_K) {
852         if (sp->shp_nchoppers) {
853             sp->shp_nchoppers--;
854             return;
855         }
856     } else if (pcp->pl_flags & P_E) {
857         if (sp->shp_nxlight) {
858             sp->shp_nxlight--;
859             return;
860         }
861     }
862
863     if (CANT_HAPPEN(sp->shp_nplane == 0))
864         sp->shp_nplane = 1;
865     sp->shp_nplane--;
866 }
867
868 int
869 put_plane_on_ship(struct plnstr *plane, struct shpstr *ship)
870 {
871     if (plane->pln_ship == ship->shp_uid)
872         return 1;               /* Already on ship */
873
874     if (!fit_plane_on_ship(plane, ship))
875         return 0;
876
877     plane->pln_x = ship->shp_x;
878     plane->pln_y = ship->shp_y;
879     plane->pln_ship = ship->shp_uid;
880     putplane(plane->pln_uid, plane);
881     putship(ship->shp_uid, ship);
882     return 1;
883 }
884
885 void
886 take_plane_off_ship(struct plnstr *plane, struct shpstr *ship)
887 {
888     if (CANT_HAPPEN(plane->pln_ship != ship->shp_uid))
889         return;
890
891     fit_plane_off_ship(plane, ship);
892     plane->pln_ship = -1;
893     putship(ship->shp_uid, ship);
894     putplane(plane->pln_uid, plane);
895 }
896
897 /*
898  * Fit a plane of PP's type on land unit LP.
899  * Adjust LP's plane counters.
900  * Updating the plane accordingly is the caller's job.
901  * Return whether it fits.
902  */
903 static int
904 fit_plane_on_land(struct plnstr *pp, struct lndstr *lp)
905 {
906     struct plchrstr *pcp = plchr + pp->pln_type;
907     struct lchrstr *lcp = lchr + lp->lnd_type;
908
909     if ((pcp->pl_flags & P_E) && lp->lnd_nxlight < lcp->l_nxlight)
910         return ++lp->lnd_nxlight;
911
912     return 0;
913 }
914
915 /*
916  * Fit a plane of PP's type off land unit LP.
917  * Adjust LP's plane counters.
918  * Updating the plane accordingly is the caller's job.
919  */
920 static void
921 fit_plane_off_land(struct plnstr *pp, struct lndstr *lp)
922 {
923     if (CANT_HAPPEN(lp->lnd_nxlight == 0))
924         lp->lnd_nxlight = 1;
925     lp->lnd_nxlight--;
926 }
927
928 int
929 put_plane_on_land(struct plnstr *plane, struct lndstr *land)
930 {
931     if (plane->pln_land == land->lnd_uid)
932         return 1;               /* Already on unit */
933
934     if (!fit_plane_on_land(plane, land))
935         return 0;
936
937     plane->pln_x = land->lnd_x;
938     plane->pln_y = land->lnd_y;
939     plane->pln_land = land->lnd_uid;
940     putplane(plane->pln_uid, plane);
941     putland(land->lnd_uid, land);
942     return 1;
943 }
944
945 void
946 take_plane_off_land(struct plnstr *plane, struct lndstr *land)
947 {
948     if (CANT_HAPPEN(plane->pln_land != land->lnd_uid))
949         return;
950
951     fit_plane_off_land(plane, land);
952     plane->pln_land = -1;
953     putland(land->lnd_uid, land);
954     putplane(plane->pln_uid, plane);
955 }
956
957 /*
958  * Could a plane of PP's type be on on ship SP?
959  */
960 int
961 could_be_on_ship(struct plnstr *pp, struct shpstr *sp)
962 {
963     struct shpstr ship;
964
965     ship = *sp;
966     ship.shp_nplane = ship.shp_nchoppers = ship.shp_nxlight = 0;
967     return fit_plane_on_ship(pp, &ship);
968 }
969
970 void
971 plane_sweep(struct emp_qelem *plane_list, coord x, coord y)
972 {
973     struct plnstr *pp;
974     struct plchrstr *pcp;
975     struct emp_qelem *qp;
976     struct emp_qelem *next;
977     struct plist *ip;
978     struct sctstr sect;
979     int mines_there;
980     int found = 0;
981
982     getsect(x, y, &sect);
983     mines_there = sect.sct_mines;
984
985     if (mines_there == 0)
986         return;
987
988     if ((sect.sct_type != SCT_WATER) && (sect.sct_type != SCT_HARBR))
989         return;
990
991     for (qp = plane_list->q_forw; ((qp != plane_list) && (mines_there));
992          qp = next) {
993         next = qp->q_forw;
994         ip = (struct plist *)qp;
995         pp = &ip->plane;
996         pcp = ip->pcp;
997         if (!(pcp->pl_flags & P_SWEEP)) /* if it isn't an sweep plane */
998             continue;
999
1000         if (chance((100.0 - pln_acc(pp)) / 100.0)) {
1001             pr("Sweep! in %s\n",
1002                xyas(sect.sct_x, sect.sct_y, pp->pln_own));
1003             mines_there--;
1004             found = 1;
1005         }
1006     }
1007
1008     if (found && map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
1009         writemap(player->cnum);
1010     sect.sct_mines = mines_there;
1011     putsect(&sect);
1012 }
1013
1014 void
1015 count_planes(struct shpstr *sp)
1016 {
1017     struct nstr_item ni;
1018     struct plnstr plane;
1019     int nplane, nchoppers, nxlight;
1020
1021     if (sp->shp_effic < SHIP_MINEFF)
1022         return;
1023
1024     nplane = sp->shp_nplane;
1025     sp->shp_nplane = 0;
1026     nchoppers = sp->shp_nchoppers;
1027     sp->shp_nchoppers = 0;
1028     nxlight = sp->shp_nxlight;
1029     sp->shp_nxlight = 0;
1030
1031     snxtitem_xy(&ni, EF_PLANE, sp->shp_x, sp->shp_y);
1032     while (nxtitem(&ni, &plane)) {
1033         if (plane.pln_own == 0)
1034             continue;
1035         if (plane.pln_ship == sp->shp_uid) {
1036             if (!fit_plane_on_ship(&plane, sp))
1037                 CANT_REACH();
1038         }
1039     }
1040
1041     if (nplane != sp->shp_nplane ||
1042         nxlight != sp->shp_nxlight || nchoppers != sp->shp_nchoppers) {
1043         putship(sp->shp_uid, sp);
1044     }
1045 }
1046
1047 void
1048 count_land_planes(struct lndstr *lp)
1049 {
1050     struct nstr_item ni;
1051     struct plnstr plane;
1052     int nplane;
1053
1054     if (lp->lnd_effic < LAND_MINEFF)
1055         return;
1056
1057     nplane = lp->lnd_nxlight;
1058     lp->lnd_nxlight = 0;
1059
1060     snxtitem_all(&ni, EF_PLANE);
1061     while (nxtitem(&ni, &plane)) {
1062         if (plane.pln_own == 0)
1063             continue;
1064         if (plane.pln_land == lp->lnd_uid)
1065             if (!fit_plane_on_land(&plane, lp))
1066                 CANT_REACH();
1067     }
1068
1069     if (lp->lnd_nxlight != nplane)
1070         putland(lp->lnd_uid, lp);
1071 }
1072
1073 int
1074 pln_hitchance(struct plnstr *pp, int hardtarget, int type)
1075 {
1076     struct plchrstr *pcp = plchr + pp->pln_type;
1077     double tfact = (double)(pp->pln_tech - pcp->pl_tech) /
1078         (pp->pln_tech - pcp->pl_tech / 2);
1079     int acc = pln_acc(pp);
1080     int hitchance;
1081
1082     if (type == EF_SHIP) {
1083         if (pcp->pl_flags & P_A)
1084             acc -= 20;
1085         if (!(pcp->pl_flags & P_T))
1086             acc += 35;
1087     }
1088     hitchance = (int)(pp->pln_effic * (1.0 - 0.1 * tfact) *
1089                       (1.0 - acc / 100.0)) - hardtarget;
1090
1091     /* smooth out the bottom of the graph with asymtote at 5 -KHS */
1092     if (hitchance < 20)
1093         hitchance = 5 + ldround(300.0 / (40.0 - hitchance), 1);
1094     if (hitchance > 100)
1095         hitchance = 100;
1096     return hitchance;
1097 }
1098
1099 /* return 0 if there was a nuclear detonation */
1100
1101 int
1102 pln_damage(struct plnstr *pp, coord x, coord y, char type, int *nukedamp,
1103            int noisy)
1104 {
1105     struct nukstr nuke;
1106     struct plchrstr *pcp = plchr + pp->pln_type;
1107     int load, i;
1108     int hitroll;
1109     int dam = 0;
1110     int aim;
1111     int effective = 1;
1112     int pinbomber = 0;
1113
1114     if (pp->pln_nuketype != -1) {
1115         if (nuk_on_plane(&nuke, pp->pln_uid) >= 0) {
1116             mpr(pp->pln_own, "Releasing RV's for %s detonation...\n",
1117                 pp->pln_flags & PLN_AIRBURST ? "airburst" : "groundburst");
1118             pp->pln_nuketype = -1;
1119             *nukedamp = detonate(&nuke, x, y,
1120                                  pp->pln_flags & PLN_AIRBURST);
1121             return 0;
1122         }
1123         CANT_REACH();
1124     }
1125     *nukedamp = 0;
1126
1127     load = pln_load(pp);
1128     if (!load)                 /* e.g. ab, blowing up on launch pad */
1129         return 0;
1130
1131     i = roll(load) + 1;
1132     if (i > load)
1133         i = load;
1134
1135     if (pcp->pl_flags & P_M) {
1136         if (pcp->pl_flags & P_MAR)
1137             pinbomber = 1;
1138     } else if (pcp->pl_flags & P_T)
1139         pinbomber = 1;
1140
1141     aim = pln_acc(pp);
1142     if (type == 's') {
1143         effective = !pinbomber;
1144         aim = 30 + (pinbomber ? aim : 100 - aim);
1145     } else {
1146         effective = pinbomber;
1147         aim = 100 - aim;
1148     }
1149
1150     while (i--) {
1151         dam += roll(6);
1152         hitroll = roll(100);
1153         if (hitroll >= 90) {
1154             dam += 8;
1155             if (noisy)
1156                 mpr(pp->pln_own, "BLAM");
1157         } else if (hitroll < aim) {
1158             dam += 5;
1159             if (noisy)
1160                 mpr(pp->pln_own, "Blam");
1161         } else {
1162             dam += 1;
1163             if (noisy)
1164                 mpr(pp->pln_own, "blam");
1165         }
1166         if (i && noisy)
1167             mpr(pp->pln_own, "-");
1168     }
1169     if (noisy)
1170         mpr(pp->pln_own, "\n");
1171     if (effective)
1172         dam *= 2;
1173     return dam;
1174 }
1175
1176 int
1177 pln_identchance(struct plnstr *pp, int hardtarget, int type)
1178 {
1179     double misschance =
1180         (100.0 - pln_hitchance(pp, hardtarget, type)) / 100.0;
1181     return (int)(100 - 100 * misschance * misschance);
1182 }
1183
1184 int
1185 pln_mobcost(int dist, struct plnstr *pp, int flags)
1186 {
1187     double cost;
1188
1189     cost = 20.0 / (pp->pln_effic / 100.0);
1190     if ((flags & P_F) || (flags & P_ESC))
1191         cost /= 2;
1192
1193     return ldround(cost * dist / pln_range_max(pp) + 5, 1);
1194 }
1195
1196 int
1197 pln_is_in_orbit(struct plnstr *pp)
1198 {
1199     return (plchr[pp->pln_type].pl_flags & (P_M | P_O)) == P_O
1200         && (pp->pln_flags & PLN_LAUNCHED);
1201 }
1202
1203 /*
1204  * Set PP's tech to TLEV along with everything else that depends on it.
1205  */
1206 void
1207 pln_set_tech(struct plnstr *pp, int tlev)
1208 {
1209     struct plchrstr *pcp = plchr + pp->pln_type;
1210     int limited_range = pp->pln_range < pln_range_max(pp);
1211     int range_max;
1212
1213     if (CANT_HAPPEN(tlev < pcp->pl_tech))
1214         tlev = pcp->pl_tech;
1215     pp->pln_tech = tlev;
1216
1217     range_max = pln_range_max(pp);
1218     if (!limited_range || pp->pln_range > range_max)
1219         pp->pln_range = range_max;
1220 }