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