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