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