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