]> git.pond.sub.org Git - empserver/blob - src/lib/subs/plnsub.c
New unit stat development functions
[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 "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 /*                                      plp->plane.pln_own = ship.shp_own;*/
219                     wu(0, ship.shp_own, "%s %s lands on your %s\n",
220                        cname(player->cnum), prplane(&plp->plane),
221                        prship(&ship));
222                 }
223             }
224         } else {
225             plp->plane.pln_x = tx;
226             plp->plane.pln_y = ty;
227             getsect(tx, ty, &sect);
228             if (plp->plane.pln_own != sect.sct_own) {
229 /*                              plp->plane.pln_own = sect.sct_own;*/
230                 wu(0, sect.sct_own,
231                    "%s %s lands at your sector %s\n",
232                    cname(player->cnum),
233                    prplane(&plp->plane), xyas(tx, ty, sect.sct_own));
234             }
235             plp->plane.pln_ship = cno;
236         }
237     }
238     if (cno >= 0)
239         putship(ship.shp_uid, &ship);
240 }
241
242 void
243 pln_dropoff(struct emp_qelem *list, struct ichrstr *ip, coord tx, coord ty,
244             void *ptr, int type)
245 {
246     struct emp_qelem *qp;
247     struct plist *plp;
248     int amt;
249     struct sctstr *sectp;
250     struct shpstr *sp;
251     int there;
252     int max;
253     struct mchrstr *mp;
254
255     if (ip == 0)
256         return;
257     amt = 0;
258     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
259         plp = (struct plist *)qp;
260         amt += plp->misc;
261     }
262     if (type == EF_SECTOR) {
263         sectp = ptr;
264         if (!sectp->sct_own) {
265             if (sectp->sct_type == SCT_WATER)
266                 pr("Your %s sink like a rock!\n", ip->i_name);
267             else
268                 pr("Your %s vanish without a trace.\n", ip->i_name);
269             return;
270         }
271         if (sectp->sct_own != player->cnum
272             && getrel(getnatp(sectp->sct_own), player->cnum) != ALLIED) {
273             pr("You don't own %s!  Cargo jettisoned...\n",
274                xyas(tx, ty, player->cnum));
275             return;
276         }
277         if (ip->i_uid == I_CIVIL && sectp->sct_own != sectp->sct_oldown) {
278             pr("%s is occupied.  Your civilians suffer from identity crisis and die.\n",
279                xyas(tx, ty, player->cnum));
280             return;
281         }
282         there = sectp->sct_item[ip->i_uid];
283         max = ITEM_MAX;
284     } else {
285         sp = ptr;
286         there = sp->shp_item[ip->i_uid];
287         mp = &mchr[(int)sp->shp_type];
288         max = mp->m_item[ip->i_uid];
289     }
290     there += amt;
291     if (there > max) {
292         pr("%d excess %s discarded\n", max - there, ip->i_name);
293         amt = max - there;
294         there = max;
295     }
296     pr("%d %s landed safely", amt, ip->i_name);
297     if (type == EF_SECTOR) {
298         sectp = ptr;
299         sectp->sct_item[ip->i_uid] = there;
300         if (sectp->sct_own != player->cnum)
301             wu(0, sectp->sct_own, "%s planes drop %d %s in %s\n",
302                cname(player->cnum), amt, ip->i_name,
303                xyas(sectp->sct_x, sectp->sct_y, sectp->sct_own));
304         pr(" at %s\n", xyas(tx, ty, player->cnum));
305         putsect((struct sctstr *)ptr);
306     } else {
307         struct shpstr *sp = (struct shpstr *)ptr;
308         sp->shp_item[ip->i_uid] = there;
309         if (sp->shp_own != player->cnum)
310             wu(0, sp->shp_own, "%s planes land %d %s on carrier %d\n",
311                cname(player->cnum), amt, ip->i_name, sp->shp_uid);
312         pr(" on carrier #%d\n", sp->shp_uid);
313         putship(sp->shp_uid, sp);
314     }
315 }
316
317 void
318 pln_mine(struct emp_qelem *list, struct sctstr *sectp)
319 {
320     struct emp_qelem *qp;
321     struct plist *plp;
322     int amt;
323
324     amt = 0;
325     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
326         plp = (struct plist *)qp;
327         amt += plp->misc;
328
329     }
330     if (amt > 0) {
331         if (sectp->sct_type != SCT_WATER) {
332             pr("Your seamines have no effect here.\n");
333             return;
334         }
335         sectp->sct_mines = MIN(sectp->sct_mines + amt, MINES_MAX);
336         pr("%d mines laid in %s.\n", amt,
337            xyas(sectp->sct_x, sectp->sct_y, player->cnum));
338         if (map_set(player->cnum, sectp->sct_x, sectp->sct_y, 'X', 0))
339             writemap(player->cnum);
340         putsect(sectp);
341     }
342 }
343
344 /*
345  * Has PP's type capabilities satisfying WANTFLAGS and NOWANTFLAGS?
346  * A plane type is capable unless
347  * - it lacks all of the P_F, P_ESC in WANTFLAGS, or
348  * - it lacks all of the P_E, P_L, P_K in WANTFLAGS, or
349  * - it lacks any of the other capabilities in WANTFLAGS, or
350  * - it has any of the capabilities in NOWANTFLAGS.
351  */
352 int
353 pln_capable(struct plnstr *pp, int wantflags, int nowantflags)
354 {
355     int flags = plchr[(int)pp->pln_type].pl_flags;
356
357     if (wantflags & (P_F | P_ESC)) {
358         if ((flags & wantflags & (P_F | P_ESC)) == 0)
359             return 0;
360         wantflags &= ~(P_F | P_ESC);
361     }
362
363     if (wantflags & (P_E | P_L | P_K)) {
364         if ((flags & wantflags & (P_E | P_L | P_K)) == 0)
365             return 0;
366         wantflags &= ~(P_E | P_L | P_K);
367     }
368
369     if ((flags & wantflags) != wantflags)
370         return 0;
371
372     if (flags & nowantflags)
373         return 0;
374
375     return 1;
376 }
377
378 /*
379  * Find plane types that can operate from carrier SP.
380  * If MSL find missile types, else non-missile types.
381  * Return a combination of P_L, P_K, P_E.
382  * It's zero if SP can't support air operations due to its type or
383  * state (low efficiency).
384  */
385 int
386 carrier_planes(struct shpstr *sp, int msl)
387 {
388     struct mchrstr *mcp = mchr + sp->shp_type;
389     int res;
390
391     if (sp->shp_effic < SHP_AIROPS_EFF)
392         return 0;
393
394     res = 0;
395     if (mcp->m_flags & M_FLY)
396         res |= P_L;
397     if ((mcp->m_flags & M_MSL) && msl)
398         res |= P_L;
399     if (mcp->m_nchoppers && !msl)
400         res |= P_K;
401     if (mcp->m_nxlight)
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 & P_M) & pcp->pl_flags))
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
501     emp_initque(list);
502     while (nxtitem(ni, &plane)) {
503         if (!player->owner)
504             continue;
505         if (plane.pln_mobil <= 0)
506             continue;
507         if (opt_MARKET) {
508             if (ontradingblock(EF_PLANE, &plane)) {
509                 pr("plane #%d inelligible - it's for sale.\n",
510                    plane.pln_uid);
511                 continue;
512             }
513         }
514
515         range = mapdist(plane.pln_x, plane.pln_y, ap->sct_x, ap->sct_y);
516         if (range > 4) {
517             pr("%s too far from assembly point\n", prplane(&plane));
518             continue;
519         }
520         if (plane.pln_effic < 40) {
521             pr("%s not efficient enough (must be 40%%)\n",
522                prplane(&plane));
523             continue;
524         }
525         range += ap_to_target;
526         range *= rangemult;
527         pcp = &plchr[(int)plane.pln_type];
528         if (!pln_capable(&plane, wantflags, nowantflags))
529             continue;
530         if (plane.pln_range < range) {
531             pr("%s out of range (%d:%d)\n",
532                prplane(&plane), plane.pln_range, range);
533             continue;
534         }
535         if (!pln_airbase_ok(&plane, rangemult != 2, 1))
536             continue;
537         pr("%s standing by\n", prplane(&plane));
538         plane.pln_mission = 0;
539         putplane(plane.pln_uid, &plane);
540         plp = malloc(sizeof(struct plist));
541         plp->misc = 0;
542         plp->bombs = 0;
543         plp->pcp = pcp;
544         plp->plane = plane;
545         emp_insque(&plp->queue, list);
546     }
547 }
548
549 int
550 pln_arm(struct emp_qelem *list, int dist, char mission, struct ichrstr *ip,
551         int flags, int mission_flags)
552 {
553     struct emp_qelem *qp;
554     struct emp_qelem *next;
555     struct plist *plp;
556
557     for (qp = list->q_forw; qp != list; qp = next) {
558         next = qp->q_forw;
559         plp = (struct plist *)qp;
560         if (pln_equip(plp, ip, flags, mission) < 0) {
561             emp_remque(qp);
562             free(qp);
563             continue;
564         }
565         if (flags & (P_S | P_I)) {
566             if (plp->pcp->pl_flags & P_S)
567                 mission_flags |= P_S;
568             if (plp->pcp->pl_flags & P_I)
569                 mission_flags |= P_I;
570         }
571         if (!(plp->pcp->pl_flags & P_H))
572             /* no stealth on this mission */
573             mission_flags &= ~P_H;
574         if (!(plp->pcp->pl_flags & P_X))
575             /* no stealth on this mission */
576             mission_flags &= ~P_X;
577         if (!(plp->pcp->pl_flags & P_A)) {
578             /* no asw on this mission */
579             mission_flags &= ~P_A;
580             /* FIXME escorts turn ASW patrol into ordinary recon */
581         }
582         if (!(plp->pcp->pl_flags & P_MINE)) {
583             /* no asw on this mission */
584             mission_flags &= ~P_MINE;
585             /* FIXME no effect */
586         }
587         plp->plane.pln_mobil -= pln_mobcost(dist, &plp->plane, flags);
588         pr("%s equipped\n", prplane(&plp->plane));
589     }
590     return mission_flags;
591 }
592
593 static int
594 pln_equip(struct plist *plp, struct ichrstr *ip, int flags, char mission)
595 {
596     struct plchrstr *pcp;
597     struct plnstr *pp;
598     int needed;
599     struct lndstr land;
600     struct shpstr ship;
601     struct sctstr sect;
602     i_type itype;
603     int rval;
604     short *item;
605     int own;
606     int abandon_needed;
607
608     pp = &plp->plane;
609     pcp = plp->pcp;
610     if (pp->pln_ship >= 0) {
611         getship(pp->pln_ship, &ship);
612         item = ship.shp_item;
613         own = ship.shp_own;
614     } else if (pp->pln_land >= 0) {
615         getland(pp->pln_land, &land);
616         item = land.lnd_item;
617         own = land.lnd_own;
618     } else {
619         getsect(pp->pln_x, pp->pln_y, &sect);
620         item = sect.sct_item;
621         own = sect.sct_oldown;
622     }
623     if (ip) {
624         if (ip->i_uid == I_CIVIL) {
625             if (pp->pln_own != own) {
626                 pr("You don't control those civilians!\n");
627                 return -1;
628             }
629         }
630     }
631     if (pcp->pl_fuel > item[I_PETROL]) {
632         pr("%s not enough petrol there!\n", prplane(pp));
633         return -1;
634     }
635     item[I_PETROL] -= pcp->pl_fuel;
636     rval = 0;
637     if ((flags & P_F) == 0) {
638         itype = I_NONE;
639         needed = 0;
640         switch (mission) {
641         case 's':
642         case 'p':
643             if (pp->pln_nuketype == -1) {
644                 itype = I_SHELL;
645                 needed = pp->pln_load;
646             }
647             break;
648         case 't':
649         case 'd':
650             if ((pcp->pl_flags & P_C) == 0 || ip == 0)
651                 break;
652             itype = ip->i_uid;
653             needed = (pp->pln_load * 2) / ip->i_lbs;
654             break;
655         case 'm':
656             if ((pcp->pl_flags & P_MINE) == 0)
657                 break;
658             itype = I_SHELL;
659             needed = (pp->pln_load * 2) / ip->i_lbs;
660             break;
661         case 'a':
662             if ((pcp->pl_flags & (P_V | P_C)) == 0)
663                 break;
664             itype = I_MILIT;
665             needed = pp->pln_load / ip->i_lbs;
666             break;
667         case 'n':
668             if (pp->pln_nuketype == -1)
669                 rval = -1;
670             break;
671         default:
672             break;
673         }
674         if (rval < 0 || (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 rval;
722 }
723
724 void
725 pln_put(struct emp_qelem *list)
726 {
727     struct emp_qelem *qp;
728     struct emp_qelem *newqp;
729     struct plist *plp;
730     struct plnstr *pp;
731     struct shpstr ship;
732     struct sctstr sect;
733
734     /* Here is where planes return home from bombing runs.
735        We need to make sure they still have somewhere to return
736        home to! */
737     qp = list->q_forw;
738     while (qp != list) {
739         plp = (struct plist *)qp;
740         pp = &plp->plane;
741         /* Ok, check out where it wants to land */
742         if (pp->pln_ship >= 0) {
743             /* It is landing on a carrier */
744             getship(pp->pln_ship, &ship);
745             /* We should do more, like make sure it's really
746                a carrier, etc. but for now just make sure it's
747                not sunk. */
748             if (ship.shp_effic < SHIP_MINEFF) {
749                 mpr(pp->pln_own,
750                     "Ship #%d has been sunk, plane #%d has nowhere to land, and\n"
751                     "splashes into the sea.\n",
752                     pp->pln_ship, pp->pln_uid);
753                 pp->pln_effic = 0;
754             }
755         } else {
756             /* Presume we are landing back in a sector. */
757             getsect(pp->pln_x, pp->pln_y, &sect);
758             if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
759                 mpr(pp->pln_own,
760                     "Nowhere to land at %s, plane #%d crashes and burns...\n",
761                     xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
762                 pp->pln_effic = 0;
763             }
764         }
765         putplane(pp->pln_uid, pp);
766         newqp = qp->q_forw;
767         emp_remque(qp);
768         free(qp);
769         qp = newqp;
770     }
771 }
772
773 void
774 pln_removedupes(struct emp_qelem *bomb_list, struct emp_qelem *esc_list)
775 {
776     struct emp_qelem *bomb;
777     struct emp_qelem *esc;
778     struct plist *bombp;
779     struct plist *escp;
780
781     if (QEMPTY(bomb_list) || QEMPTY(esc_list))
782         return;
783     bomb = bomb_list->q_forw;
784     while (bomb != bomb_list) {
785         if (QEMPTY(esc_list)) {
786             bomb = bomb_list;
787             continue;
788         }
789         esc = esc_list->q_forw;
790         bombp = (struct plist *)bomb;
791         while (esc != esc_list) {
792             escp = (struct plist *)esc;
793             if (escp->plane.pln_uid == bombp->plane.pln_uid) {
794                 emp_remque(esc);
795                 free(esc);
796                 esc = esc_list;
797             } else
798                 esc = esc->q_forw;
799         }
800         bomb = bomb->q_forw;
801     }
802 }
803
804 /*
805  * Fit a plane of PP's type on ship SP.
806  * Adjust SP's plane counters.
807  * Updating the plane accordingly is the caller's job.
808  * Return whether it fits.
809  */
810 static int
811 fit_plane_on_ship(struct plnstr *pp, struct shpstr *sp)
812 {
813     struct plchrstr *pcp = plchr + pp->pln_type;
814     struct mchrstr *mcp = mchr + sp->shp_type;
815     int wanted;
816
817     if (pcp->pl_flags & P_K) {
818         /* chopper, try chopper slot first */
819         if (sp->shp_nchoppers < mcp->m_nchoppers)
820             return ++sp->shp_nchoppers;
821         /* else try plane slot */
822         wanted = M_FLY;
823     } else if (pcp->pl_flags & P_E) {
824         /* x-light, try x-light slot first */
825         if (sp->shp_nxlight < mcp->m_nxlight)
826             return ++sp->shp_nxlight;
827         /* else try plane slot */
828         wanted = M_MSL | M_FLY;
829     } else if (!(pcp->pl_flags & P_L)) {
830         /* not light, no go */
831         wanted = 0;
832     } else if (pcp->pl_flags & P_M) {
833         /* missile, use plane slot */
834         wanted = M_MSL | M_FLY;
835     } else {
836         /* fixed-wing plane, use plane slot */
837         wanted = M_FLY;
838     }
839
840     if ((mcp->m_flags & wanted) == 0)
841         return 0;               /* ship not capable */
842
843     if (sp->shp_nplane < mcp->m_nplanes)
844         return ++sp->shp_nplane;
845
846     return 0;
847 }
848
849 /*
850  * Fit a plane of PP's type off ship SP.
851  * Adjust SP's plane counters, badly.  You need to run count_planes()
852  * before the next fit_plane_on_ship().
853  * Updating the plane accordingly is the caller's job.
854  */
855 static void
856 fit_plane_off_ship(struct plnstr *pp, struct shpstr *sp)
857 {
858     /*
859      * Frees chopper and nxlight slots first, which is why we need to
860      * run count_planes() before fit_plane_on_ship().
861      */
862     struct plchrstr *pcp = plchr + pp->pln_type;
863
864     if (pcp->pl_flags & P_K) {
865         if (sp->shp_nchoppers) {
866             sp->shp_nchoppers--;
867             return;
868         }
869     } else if (pcp->pl_flags & P_E) {
870         if (sp->shp_nxlight) {
871             sp->shp_nxlight--;
872             return;
873         }
874     }
875
876     if (CANT_HAPPEN(sp->shp_nplane == 0))
877         sp->shp_nplane = 1;
878     sp->shp_nplane--;
879 }
880
881 int
882 put_plane_on_ship(struct plnstr *plane, struct shpstr *ship)
883 {
884     if (plane->pln_ship == ship->shp_uid)
885         return 1;               /* Already on ship */
886
887     if (!fit_plane_on_ship(plane, ship))
888         return 0;
889
890     plane->pln_x = ship->shp_x;
891     plane->pln_y = ship->shp_y;
892     plane->pln_ship = ship->shp_uid;
893     putplane(plane->pln_uid, plane);
894     putship(ship->shp_uid, ship);
895     return 1;
896 }
897
898 void
899 take_plane_off_ship(struct plnstr *plane, struct shpstr *ship)
900 {
901     if (CANT_HAPPEN(plane->pln_ship != ship->shp_uid))
902         return;
903
904     fit_plane_off_ship(plane, ship);
905     plane->pln_ship = -1;
906     putship(ship->shp_uid, ship);
907     putplane(plane->pln_uid, plane);
908 }
909
910 /*
911  * Fit a plane of PP's type on land unit LP.
912  * Adjust LP's plane counters.
913  * Updating the plane accordingly is the caller's job.
914  * Return whether it fits.
915  */
916 static int
917 fit_plane_on_land(struct plnstr *pp, struct lndstr *lp)
918 {
919     struct plchrstr *pcp = plchr + pp->pln_type;
920     struct lchrstr *lcp = lchr + lp->lnd_type;
921
922     if ((pcp->pl_flags & P_E) && lp->lnd_nxlight < lcp->l_nxlight)
923         return ++lp->lnd_nxlight;
924
925     return 0;
926 }
927
928 /*
929  * Fit a plane of PP's type off land unit LP.
930  * Adjust LP's plane counters.
931  * Updating the plane accordingly is the caller's job.
932  */
933 static void
934 fit_plane_off_land(struct plnstr *pp, struct lndstr *lp)
935 {
936     struct plchrstr *pcp = plchr + pp->pln_type;
937
938     if (CANT_HAPPEN(lp->lnd_nxlight == 0))
939         lp->lnd_nxlight = 1;
940     lp->lnd_nxlight--;
941 }
942
943 int
944 put_plane_on_land(struct plnstr *plane, struct lndstr *land)
945 {
946     if (plane->pln_land == land->lnd_uid)
947         return 1;               /* Already on unit */
948
949     if (!fit_plane_on_land(plane, land))
950         return 0;
951
952     plane->pln_x = land->lnd_x;
953     plane->pln_y = land->lnd_y;
954     plane->pln_land = land->lnd_uid;
955     putplane(plane->pln_uid, plane);
956     putland(land->lnd_uid, land);
957     return 1;
958 }
959
960 void
961 take_plane_off_land(struct plnstr *plane, struct lndstr *land)
962 {
963     if (CANT_HAPPEN(plane->pln_land != land->lnd_uid))
964         return;
965
966     fit_plane_off_land(plane, land);
967     plane->pln_land = -1;
968     putland(land->lnd_uid, land);
969     putplane(plane->pln_uid, plane);
970 }
971
972 /*
973  * Could a plane of PP's type be on on ship SP?
974  */
975 int
976 could_be_on_ship(struct plnstr *pp, struct shpstr *sp)
977 {
978     struct shpstr ship;
979
980     ship = *sp;
981     ship.shp_nplane = ship.shp_nchoppers = ship.shp_nxlight = 0;
982     return fit_plane_on_ship(pp, &ship);
983 }
984
985 void
986 plane_sweep(struct emp_qelem *plane_list, coord x, coord y)
987 {
988     struct plnstr *pp;
989     struct plchrstr *pcp;
990     struct emp_qelem *qp;
991     struct emp_qelem *next;
992     struct plist *ip;
993     struct sctstr sect;
994     int mines_there;
995     int found = 0;
996
997     getsect(x, y, &sect);
998     mines_there = sect.sct_mines;
999
1000     if (mines_there == 0)
1001         return;
1002
1003     if ((sect.sct_type != SCT_WATER) && (sect.sct_type != SCT_HARBR))
1004         return;
1005
1006     for (qp = plane_list->q_forw; ((qp != plane_list) && (mines_there));
1007          qp = next) {
1008         next = qp->q_forw;
1009         ip = (struct plist *)qp;
1010         pp = &ip->plane;
1011         pcp = ip->pcp;
1012         if (!(pcp->pl_flags & P_SWEEP)) /* if it isn't an sweep plane */
1013             continue;
1014
1015         if (chance((100.0 - pp->pln_acc) / 100.0)) {
1016             pr("Sweep! in %s\n",
1017                xyas(sect.sct_x, sect.sct_y, pp->pln_own));
1018             mines_there--;
1019             found = 1;
1020         }
1021     }
1022
1023     if (found && map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
1024         writemap(player->cnum);
1025     sect.sct_mines = mines_there;
1026     putsect(&sect);
1027 }
1028
1029 void
1030 count_planes(struct shpstr *sp)
1031 {
1032     struct nstr_item ni;
1033     struct plnstr plane;
1034     int nplane, nchoppers, nxlight;
1035
1036     if (sp->shp_effic < SHIP_MINEFF)
1037         return;
1038
1039     nplane = sp->shp_nplane;
1040     sp->shp_nplane = 0;
1041     nchoppers = sp->shp_nchoppers;
1042     sp->shp_nchoppers = 0;
1043     nxlight = sp->shp_nxlight;
1044     sp->shp_nxlight = 0;
1045
1046     snxtitem_xy(&ni, EF_PLANE, sp->shp_x, sp->shp_y);
1047     while (nxtitem(&ni, &plane)) {
1048         if (plane.pln_own == 0)
1049             continue;
1050         if (plane.pln_ship == sp->shp_uid) {
1051             if (!fit_plane_on_ship(&plane, sp))
1052                 CANT_REACH();
1053         }
1054     }
1055
1056     if (nplane != sp->shp_nplane ||
1057         nxlight != sp->shp_nxlight || nchoppers != sp->shp_nchoppers) {
1058         putship(sp->shp_uid, sp);
1059     }
1060 }
1061
1062 void
1063 count_land_planes(struct lndstr *lp)
1064 {
1065     struct nstr_item ni;
1066     struct plnstr plane;
1067     int nplane;
1068
1069     if (lp->lnd_effic < LAND_MINEFF)
1070         return;
1071
1072     nplane = lp->lnd_nxlight;
1073     lp->lnd_nxlight = 0;
1074
1075     snxtitem_all(&ni, EF_PLANE);
1076     while (nxtitem(&ni, &plane)) {
1077         if (plane.pln_own == 0)
1078             continue;
1079         if (plane.pln_land == lp->lnd_uid)
1080             if (!fit_plane_on_land(&plane, lp))
1081                 CANT_REACH();
1082     }
1083
1084     if (lp->lnd_nxlight != nplane)
1085         putland(lp->lnd_uid, lp);
1086 }
1087
1088 int
1089 count_sect_planes(struct sctstr *sp)
1090 {
1091     int count = 0;
1092     struct nstr_item ni;
1093     struct plnstr plane;
1094
1095     snxtitem_all(&ni, EF_PLANE);
1096     while (nxtitem(&ni, &plane)) {
1097         if (!plane.pln_own)
1098             continue;
1099         if (plane.pln_flags & PLN_LAUNCHED)
1100             continue;
1101         if (plane.pln_x == sp->sct_x && plane.pln_y == sp->sct_y)
1102             ++count;
1103     }
1104
1105     return count;
1106 }
1107
1108 int
1109 pln_hitchance(struct plnstr *pp, int hardtarget, int type)
1110 {
1111     struct plchrstr *pcp = plchr + pp->pln_type;
1112     double tfact = (double)(pp->pln_tech - pcp->pl_tech) /
1113         (pp->pln_tech - pcp->pl_tech / 2);
1114     int acc = pp->pln_acc;
1115     int hitchance;
1116
1117     if (type == EF_SHIP) {
1118         if (pcp->pl_flags & P_A)
1119             acc -= 20;
1120         if (!(pcp->pl_flags & P_T))
1121             acc += 35;
1122     }
1123     hitchance = (int)(pp->pln_effic * (1.0 - 0.1 * tfact) *
1124                       (1.0 - acc / 100.0)) - hardtarget;
1125
1126     /* smooth out the bottom of the graph with asymtote at 5 -KHS */
1127     if (hitchance < 20)
1128         hitchance = 5 + ldround(300.0 / (40.0 - hitchance), 1);
1129     if (hitchance > 100)
1130         hitchance = 100;
1131     return hitchance;
1132 }
1133
1134 /* return 0 if there was a nuclear detonation */
1135
1136 int
1137 pln_damage(struct plnstr *pp, coord x, coord y, char type, int *nukedamp,
1138            int noisy)
1139 {
1140     struct nukstr nuke;
1141     struct plchrstr *pcp = plchr + pp->pln_type;
1142     int i;
1143     int hitroll;
1144     int dam = 0;
1145     int aim;
1146     int effective = 1;
1147     int pinbomber = 0;
1148
1149     if (pp->pln_nuketype != -1) {
1150         if (nuk_on_plane(&nuke, pp->pln_uid) >= 0) {
1151             mpr(pp->pln_own, "Releasing RV's for %s detonation...\n",
1152                 pp->pln_flags & PLN_AIRBURST ? "airburst" : "groundburst");
1153             pp->pln_nuketype = -1;
1154             *nukedamp = detonate(&nuke, x, y,
1155                                  pp->pln_flags & PLN_AIRBURST);
1156             return 0;
1157         }
1158         CANT_REACH();
1159     }
1160     *nukedamp = 0;
1161
1162     if (!pp->pln_load)          /* e.g. ab, blowing up on launch pad */
1163         return 0;
1164
1165     i = roll(pp->pln_load) + 1;
1166     if (i > pp->pln_load)
1167         i = pp->pln_load;
1168
1169     if (pcp->pl_flags & P_M) {
1170         if (pcp->pl_flags & P_MAR)
1171             pinbomber = 1;
1172     } else if (pcp->pl_flags & P_T)
1173         pinbomber = 1;
1174
1175     aim = 100 - pp->pln_acc;
1176     if (type == 's') {
1177         if (pinbomber) {
1178             aim = pp->pln_acc;
1179             effective = 0;
1180         }
1181         aim += 30;
1182     } else {
1183         if (!pinbomber) {
1184             effective = 0;
1185         }
1186     }
1187     while (i--) {
1188         dam += roll(6);
1189         hitroll = roll(100);
1190         if (hitroll >= 90) {
1191             dam += 8;
1192             if (noisy)
1193                 mpr(pp->pln_own, "BLAM");
1194         } else if (hitroll < aim) {
1195             dam += 5;
1196             if (noisy)
1197                 mpr(pp->pln_own, "Blam");
1198         } else {
1199             dam += 1;
1200             if (noisy)
1201                 mpr(pp->pln_own, "blam");
1202         }
1203         if (i && noisy)
1204             mpr(pp->pln_own, "-");
1205     }
1206     if (noisy)
1207         mpr(pp->pln_own, "\n");
1208     if (effective)
1209         dam *= 2;
1210     return dam;
1211 }
1212
1213 int
1214 pln_identchance(struct plnstr *pp, int hardtarget, int type)
1215 {
1216     double misschance =
1217         (100.0 - pln_hitchance(pp, hardtarget, type)) / 100.0;
1218     return (int)(100 - 100 * misschance * misschance);
1219 }
1220
1221 int
1222 pln_mobcost(int dist, struct plnstr *pp, int flags)
1223 {
1224     double cost;
1225
1226     cost = 20.0 / (pp->pln_effic / 100.0);
1227     if ((flags & P_F) || (flags & P_ESC))
1228         cost /= 2;
1229
1230     return ldround(cost * dist / pp->pln_range_max + 5, 1);
1231 }
1232
1233 /*
1234  * Set PP's tech to TLEV along with everything else that depends on it.
1235  */
1236 void
1237 pln_set_tech(struct plnstr *pp, int tlev)
1238 {
1239     struct plchrstr *pcp = plchr + pp->pln_type;
1240     int limited_range = pp->pln_range < pp->pln_range_max;
1241
1242     if (CANT_HAPPEN(tlev < pcp->pl_tech))
1243         tlev = pcp->pl_tech;
1244
1245     pp->pln_tech = tlev;
1246     pp->pln_att = pl_att(pcp, tlev);
1247     pp->pln_def = pl_def(pcp, tlev);
1248     pp->pln_acc = pl_acc(pcp, tlev);
1249     pp->pln_range_max = pl_range(pcp, tlev);
1250     pp->pln_load = pl_load(pcp, tlev);
1251
1252     if (!limited_range || pp->pln_range > pp->pln_range_max)
1253         pp->pln_range = pp->pln_range_max;
1254 }