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