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