1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2021, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  plnsub.c: Plane subroutine stuff
28  *
29  *  Known contributors to this file:
30  *     Dave Pare, 1986
31  *     Ken Stevens, 1995
32  *     Steve McClure, 1998-2000
33  *     Markus Armbruster, 2004-2021
34  */
35 
36 #include <config.h>
37 
38 #include "chance.h"
39 #include "empobj.h"
40 #include "item.h"
41 #include "land.h"
42 #include "map.h"
43 #include "misc.h"
44 #include "nat.h"
45 #include "nsc.h"
46 #include "nuke.h"
47 #include "optlist.h"
48 #include "plague.h"
49 #include "plane.h"
50 #include "player.h"
51 #include "prototypes.h"
52 #include "sect.h"
53 #include "ship.h"
54 #include "xy.h"
55 
56 static int ship_can_carry(struct shpstr *, int, int, int, int);
57 static int inc_shp_nplane(struct plnstr *, int *, int *, int *);
58 
59 /*
60  * Get planes and escorts argument.
61  * Read planes into *@ni_bomb, and (optional) escorts into *@ni_esc.
62  * If @input_bomb is not empty, use it, else prompt for more input.
63  * Same for @input_esc.
64  * If we got a plane argument, initialize *@ni_bomb and *@ni_esc, and
65  * return 0.
66  * Else return -1 (*@ni_bomb and *@ni_esc may be modified).
67  */
68 int
get_planes(struct nstr_item * ni_bomb,struct nstr_item * ni_esc,char * input_bomb,char * input_esc)69 get_planes(struct nstr_item *ni_bomb, struct nstr_item *ni_esc,
70 	   char *input_bomb, char *input_esc)
71 {
72     if (!snxtitem(ni_bomb, EF_PLANE, input_bomb, NULL))
73 	return -1;
74     if (!snxtitem(ni_esc, EF_PLANE, input_esc, "escort(s)? ")) {
75 	if (player->aborted)
76 	    return -1;
77 	pr("No escorts...\n");
78     }
79     return 0;
80 }
81 
82 /*
83  * Get assembly point argument.
84  * If @input is not empty, use it.
85  * Else prompt for more input using @prompt.
86  * If this yields a valid assembly point, read it into *@ap_sect and
87  * return @ap_sect.
88  * Else complain and return NULL.
89  * *@ap_sect and @buf[1024] may be modified in either case.
90  */
91 struct sctstr *
get_assembly_point(char * input,struct sctstr * ap_sect,char * buf)92 get_assembly_point(char *input, struct sctstr *ap_sect, char *buf)
93 {
94     char *p;
95     coord x, y;
96     struct nstr_item ni;
97     struct shpstr ship;
98 
99     p = getstarg(input, "assembly point? ", buf);
100     if (!p || *p == 0)
101 	return NULL;
102     if (!sarg_xy(p, &x, &y) || !getsect(x, y, ap_sect))
103 	return NULL;
104 
105     /* over own or allied sector is fine */
106     if (relations_with(ap_sect->sct_own, player->cnum) == ALLIED)
107 	return ap_sect;
108 
109     /* over own or allied ship is fine */
110     snxtitem_xy(&ni, EF_SHIP, x, y);
111     while (nxtitem(&ni, &ship)) {
112 	if (!ship.shp_own)
113 	    continue;
114 	if (relations_with(ship.shp_own, player->cnum) == ALLIED)
115 	    return ap_sect;
116     }
117 
118     pr("Assembly point not owned by you or an ally!\n");
119     return NULL;
120 }
121 
122 /*
123  * Find out whether planes can fly one-way to @x,@y.
124  * Offer the player any carriers there.  If he chooses one, read it
125  * into @target->ship.  Else read the target sector into @target->sect.
126  * If planes can land there, set required plane flags in *@flagsp, and
127  * return 0.  Else return -1.
128  */
129 int
pln_where_to_land(coord x,coord y,union empobj_storage * target,int * flagsp)130 pln_where_to_land(coord x, coord y,
131 		  union empobj_storage *target, int *flagsp)
132 {
133     /* Keep conditions for landing consistent with pln_airbase_ok() */
134     int nships;
135     int cno;
136     int fl;
137     char buf[1024];
138     char *p;
139 
140     /* offer carriers */
141     nships = carriersatxy(x, y, player->cnum);
142     if (nships) {
143 	for (;;) {
144 	    p = getstring("Carrier #? ", buf);
145 	    if (!p)
146 		return -1;
147 	    if (!*p)
148 		break;
149 	    cno = atoi(p);
150 	    if (!getship(cno, &target->ship)
151 		|| (!player->owner
152 		    && (relations_with(target->ship.shp_own, player->cnum)
153 			!= ALLIED))) {
154 		pr("Not yours\n");
155 		continue;
156 	    }
157 	    if (target->ship.shp_x != x || target->ship.shp_y != y) {
158 		pr("Ship #%d not in %s\n", cno, xyas(x, y, player->cnum));
159 		continue;
160 	    }
161 	    fl = carrier_planes(&target->ship, 0);
162 	    if (fl == 0) {
163 		pr("Can't land on %s.\n", prship(&target->ship));
164 		continue;
165 	    }
166 	    /* clear to land on ship#CNO */
167 	    pr("landing on carrier %d\n", cno);
168 	    *flagsp |= fl;
169 	    return 0;
170 	}
171     }
172 
173     /* try to land at sector */
174     getsect(x, y, &target->sect);
175     if (relations_with(target->sect.sct_own, player->cnum) != ALLIED) {
176 	pr("Nowhere to land at sector %s!\n", xyas(x, y, player->cnum));
177 	return -1;
178     }
179     /* clear to land at sector */
180     if (target->sect.sct_type == SCT_MOUNT) {
181 	*flagsp |= P_K;
182     }
183     if (target->sect.sct_type != SCT_AIRPT || target->sect.sct_effic < 60)
184 	*flagsp |= P_V;
185     return 0;
186 }
187 
188 int
pln_can_land_on_carrier(struct emp_qelem * bomb_list,struct emp_qelem * esc_list,struct shpstr * sp)189 pln_can_land_on_carrier(struct emp_qelem *bomb_list,
190 			struct emp_qelem *esc_list,
191 			struct shpstr *sp)
192 
193 {
194     int n, nch, nxl, nmsl;
195     struct emp_qelem *list, *qp;
196     struct plist *plp;
197 
198     n = shp_nplane(sp, &nch, &nxl, &nmsl);
199 
200     /* for both lists */
201     for (list = bomb_list;
202 	 list;
203 	 list = list == bomb_list ? esc_list : NULL) {
204 	for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
205 	    plp = (struct plist *)qp;
206 	    if (plp->plane.pln_ship == sp->shp_uid)
207 		continue;
208 	    n++;
209 	    if (!inc_shp_nplane(&plp->plane, &nch, &nxl, &nmsl))
210 		return 0;
211 	}
212     }
213     return ship_can_carry(sp, n, nch, nxl, nmsl);
214 }
215 
216 void
pln_newlanding(struct emp_qelem * list,coord tx,coord ty,int cno)217 pln_newlanding(struct emp_qelem *list, coord tx, coord ty, int cno)
218 {
219     struct emp_qelem *qp;
220     struct plist *plp;
221     struct shpstr ship;
222     struct sctstr sect;
223 
224     if (cno >= 0)
225 	getship(cno, &ship);
226     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
227 	plp = (struct plist *)qp;
228 	if (cno >= 0) {
229 	    if (!could_be_on_ship(&plp->plane, &ship))
230 		pr("\t%s cannot land on ship #%d! %s aborts!\n",
231 		   prplane(&plp->plane), cno, prplane(&plp->plane));
232 	    else if (!put_plane_on_ship(&plp->plane, &ship))
233 		pr("\tNo room on ship #%d! %s aborts!\n",
234 		   cno, prplane(&plp->plane));
235 	    else {
236 		if (plp->plane.pln_own != ship.shp_own) {
237 		    wu(0, ship.shp_own, "%s %s lands on your %s\n",
238 		       cname(player->cnum), prplane(&plp->plane),
239 		       prship(&ship));
240 		}
241 		if (plp->pstage == PLG_INFECT
242 		    && ship.shp_pstage == PLG_HEALTHY)
243 		    ship.shp_pstage = PLG_EXPOSED;
244 	    }
245 	} else {
246 	    plp->plane.pln_x = tx;
247 	    plp->plane.pln_y = ty;
248 	    getsect(tx, ty, &sect);
249 	    if (plp->plane.pln_own != sect.sct_own) {
250 		wu(0, sect.sct_own,
251 		   "%s %s lands at your sector %s\n",
252 		   cname(player->cnum),
253 		   prplane(&plp->plane), xyas(tx, ty, sect.sct_own));
254 	    }
255 	    if (plp->pstage == PLG_INFECT
256 		&& sect.sct_pstage == PLG_HEALTHY)
257 		sect.sct_pstage = PLG_EXPOSED;
258 	    plp->plane.pln_ship = cno;
259 	}
260     }
261 }
262 
263 void
pln_dropoff(struct emp_qelem * list,struct ichrstr * ip,coord tx,coord ty,int cno)264 pln_dropoff(struct emp_qelem *list, struct ichrstr *ip, coord tx, coord ty,
265 	    int cno)
266 {
267     struct emp_qelem *qp;
268     struct plist *plp;
269     int amt;
270     struct sctstr sect;
271     struct shpstr ship;
272     int there;
273     int max;
274     int pstage;
275 
276     if (!ip)
277 	return;
278     if (cno < 0) {
279 	getsect(tx, ty, &sect);
280 	if (!sect.sct_own) {
281 	    if (sect.sct_type == SCT_WATER)
282 		pr("Your %s sink like a rock!\n", ip->i_name);
283 	    else
284 		pr("Your %s vanish without a trace.\n", ip->i_name);
285 	    return;
286 	}
287 	if (relations_with(sect.sct_own, player->cnum) != ALLIED) {
288 	    pr("You don't own %s!  Cargo jettisoned...\n",
289 	       xyas(tx, ty, player->cnum));
290 	    return;
291 	}
292 	if (ip->i_uid == I_CIVIL && sect.sct_own != sect.sct_oldown) {
293 	    pr("%s is occupied.  Your civilians suffer from identity crisis and die.\n",
294 	       xyas(tx, ty, player->cnum));
295 	    return;
296 	}
297 	there = sect.sct_item[ip->i_uid];
298 	max = ITEM_MAX;
299 	pstage = sect.sct_pstage;
300     } else {
301 	getship(cno, &ship);
302 	there = ship.shp_item[ip->i_uid];
303 	max = mchr[ship.shp_type].m_item[ip->i_uid];
304 	pstage = ship.shp_pstage;
305     }
306 
307     amt = 0;
308     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
309 	plp = (struct plist *)qp;
310 	amt += plp->load;
311 	if (plp->load
312 	    && plp->pstage == PLG_INFECT && pstage == PLG_HEALTHY)
313 	    pstage = PLG_EXPOSED;
314     }
315 
316     there += amt;
317     if (there > max) {
318 	pr("%d excess %s discarded\n", there - max, ip->i_name);
319 	amt -= there - max;
320 	there = max;
321     }
322     pr("%d %s landed safely", amt, ip->i_name);
323     if (cno < 0) {
324 	sect.sct_item[ip->i_uid] = there;
325 	sect.sct_pstage = pstage;
326 	if (sect.sct_own != player->cnum)
327 	    wu(0, sect.sct_own, "%s planes drop %d %s in %s\n",
328 	       cname(player->cnum), amt, ip->i_name,
329 	       xyas(tx, ty, sect.sct_own));
330 	pr(" at %s\n", xyas(tx, ty, player->cnum));
331 	putsect(&sect);
332     } else {
333 	ship.shp_item[ip->i_uid] = there;
334 	ship.shp_pstage = pstage;
335 	if (ship.shp_own != player->cnum)
336 	    wu(0, ship.shp_own, "%s planes land %d %s on carrier %d\n",
337 	       cname(player->cnum), amt, ip->i_name, ship.shp_uid);
338 	pr(" on carrier #%d\n", ship.shp_uid);
339 	putship(ship.shp_uid, &ship);
340     }
341 }
342 
343 void
pln_mine(struct emp_qelem * list,coord tx,coord ty)344 pln_mine(struct emp_qelem *list, coord tx, coord ty)
345 {
346     struct emp_qelem *qp;
347     struct plist *plp;
348     int amt;
349     struct sctstr sect;
350 
351     amt = 0;
352     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
353 	plp = (struct plist *)qp;
354 	amt += plp->load;
355 
356     }
357     if (amt > 0) {
358 	getsect(tx, ty, &sect);
359 	if (sect.sct_type != SCT_WATER) {
360 	    pr("Your sea mines have no effect here.\n");
361 	    return;
362 	}
363 	sect.sct_mines = MIN(sect.sct_mines + amt, MINES_MAX);
364 	pr("%d mines laid in %s.\n", amt, xyas(tx, ty, player->cnum));
365 	if (map_set(player->cnum, tx, ty, 'X', 0))
366 	    writemap(player->cnum);
367 	putsect(&sect);
368     }
369 }
370 
371 /*
372  * Has @pp's type capabilities satisfying @wantflags and @nowantflags?
373  * A plane type is capable unless
374  * - it lacks all of the P_B, P_T in @wantflags, or
375  * - it lacks all of the P_F, P_ESC in @wantflags, or
376  * - it lacks all of the P_E, P_L, P_K in @wantflags, or
377  * - it lacks any of the other capabilities in @wantflags, or
378  * - it has any of the capabilities in @nowantflags.
379  */
380 int
pln_capable(struct plnstr * pp,int wantflags,int nowantflags)381 pln_capable(struct plnstr *pp, int wantflags, int nowantflags)
382 {
383     int flags = plchr[(int)pp->pln_type].pl_flags;
384 
385     if (wantflags & (P_B | P_T)) {
386 	if ((flags & wantflags & (P_B | P_T)) == 0)
387 	    return 0;
388 	wantflags &= ~(P_B | P_T);
389     }
390 
391     if (wantflags & (P_F | P_ESC)) {
392 	if ((flags & wantflags & (P_F | P_ESC)) == 0)
393 	    return 0;
394 	wantflags &= ~(P_F | P_ESC);
395     }
396 
397     if (wantflags & (P_E | P_L | P_K)) {
398 	if ((flags & wantflags & (P_E | P_L | P_K)) == 0)
399 	    return 0;
400 	wantflags &= ~(P_E | P_L | P_K);
401     }
402 
403     if ((flags & wantflags) != wantflags)
404 	return 0;
405 
406     if (flags & nowantflags)
407 	return 0;
408 
409     return 1;
410 }
411 
412 /*
413  * Return union of capabilities of planes in @list.
414  */
415 int
pln_caps(struct emp_qelem * list)416 pln_caps(struct emp_qelem *list)
417 {
418     struct emp_qelem *qp;
419     struct plist *plp;
420     int fl;
421 
422     fl = 0;
423     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
424 	plp = (struct plist *)qp;
425 	fl |= plp->pcp->pl_flags;
426     }
427 
428     return fl;
429 }
430 
431 /*
432  * Find plane types that can operate from carrier @sp.
433  * If @msl find missile types, else non-missile types.
434  * Return a combination of P_L, P_K, P_E.
435  * It's zero if @sp can't support air operations due to its type or
436  * state (low efficiency).
437  */
438 int
carrier_planes(struct shpstr * sp,int msl)439 carrier_planes(struct shpstr *sp, int msl)
440 {
441     struct mchrstr *mcp = mchr + sp->shp_type;
442     int res;
443 
444     if (sp->shp_effic < SHP_AIROPS_EFF)
445 	return 0;
446 
447     res = 0;
448     if (mcp->m_flags & M_FLY)
449 	res |= P_L;
450     if ((mcp->m_flags & M_MSL) && msl)
451 	res |= P_L;
452     if (mcp->m_nchoppers && !msl)
453 	res |= P_K;
454     if (mcp->m_nxlight)
455 	res |= P_E;
456     return res;
457 }
458 
459 /*
460  * Can @pp operate out its sector?
461  * If @oneway, consider only takeoff, else takeoff and landing.
462  * If @noisy, report to current player when it can't.
463  */
464 int
pln_airbase_ok(struct plnstr * pp,int oneway,int noisy)465 pln_airbase_ok(struct plnstr *pp, int oneway, int noisy)
466 {
467     /* Keep conditions for landing consistent with pln_where_to_land() */
468     struct shpstr ship;
469     struct lndstr land;
470     struct sctstr sect;
471     struct plchrstr *pcp = plchr + pp->pln_type;
472 
473     if (CANT_HAPPEN(noisy && pp->pln_own != player->cnum))
474 	noisy = 0;
475 
476     if (pp->pln_ship >= 0) {
477 	/* ship: needs to be own or allied, efficient */
478 	if (!getship(pp->pln_ship, &ship)) {
479 	    CANT_REACH();
480 	    return 0;
481 	}
482 	if (relations_with(ship.shp_own, pp->pln_own) != ALLIED) {
483 	    if (noisy)
484 		pr("(note) An ally does not own the ship %s is on\n",
485 		   prplane(pp));
486 	    return 0;
487 	}
488 	if (!(carrier_planes(&ship, pcp->pl_flags & P_M) & pcp->pl_flags))
489 	    return 0;
490 
491     } else if (pp->pln_land >= 0) {
492 	/* land: needs to be own or allied, efficient, not embarked */
493 	if (!getland(pp->pln_land, &land)) {
494 	    CANT_REACH();
495 	    return 0;
496 	}
497 	if (relations_with(land.lnd_own, pp->pln_own) != ALLIED) {
498 	    if (noisy)
499 		pr("(note) An ally does not own the unit %s is on\n",
500 		   prplane(pp));
501 	    return 0;
502 	}
503 	if (land.lnd_effic < LND_AIROPS_EFF || !(pcp->pl_flags & P_E))
504 	    return 0;
505 	if (land.lnd_ship >= 0 || land.lnd_land >= 0)
506 	    return 0;
507 
508     } else {
509 	/* sector: needs to be own or allied, efficient, suitable type */
510 	if (!getsect(pp->pln_x, pp->pln_y, &sect)) {
511 	    CANT_REACH();
512 	    return 0;
513 	}
514 	/* mountain requires helo or missile */
515 	if (sect.sct_type == SCT_MOUNT && !(pcp->pl_flags & (P_K | P_M))) {
516 	    if (noisy)
517 		pr("(note) %s is in a mountain and can't take off\n",
518 		   prplane(pp));
519 	    return 0;
520 	}
521 
522 	if (relations_with(sect.sct_own, pp->pln_own) != ALLIED) {
523 	    if (noisy)
524 		pr("(note) An ally does not own the sector %s is in\n",
525 		   prplane(pp));
526 	    return 0;
527 	}
528 	/* need airfield unless VTOL */
529 	if ((pcp->pl_flags & P_V) == 0) {
530 	    if (sect.sct_type != SCT_AIRPT) {
531 		if (noisy)
532 		    pr("%s not at airport\n", prplane(pp));
533 		return 0;
534 	    }
535 	    if (sect.sct_effic < 40) {
536 		if (noisy)
537 		    pr("%s is not 40%% efficient, %s can't take off from there.\n",
538 		       xyas(sect.sct_x, sect.sct_y, player->cnum),
539 		       prplane(pp));
540 		return 0;
541 	    }
542 	    if (!oneway && sect.sct_effic < 60) {
543 		if (noisy)
544 		    pr("%s is not 60%% efficient, %s can't land there.\n",
545 		       xyas(sect.sct_x, sect.sct_y, player->cnum),
546 		       prplane(pp));
547 		return 0;
548 	    }
549 	}
550     }
551 
552     return 1;
553 }
554 
555 void
pln_sel(struct nstr_item * ni,struct emp_qelem * list,struct sctstr * ap,int ap_to_target,int rangemult,int wantflags,int nowantflags)556 pln_sel(struct nstr_item *ni, struct emp_qelem *list, struct sctstr *ap,
557 	int ap_to_target, int rangemult, int wantflags, int nowantflags)
558 {
559     struct plnstr plane;
560     int range;
561     struct plchrstr *pcp;
562     struct plist *plp;
563 
564     emp_initque(list);
565     while (nxtitem(ni, &plane)) {
566 	/*
567 	 * It would be nice to let deities fly foreign planes, but
568 	 * much of the code assumes that only the plane's owner can
569 	 * fly it.
570 	 */
571 	if (!plane.pln_own || plane.pln_own != player->cnum)
572 	    continue;
573 	if (plane.pln_mobil <= 0)
574 	    continue;
575 	if (plane.pln_effic < 40) {
576 	    pr("%s not efficient enough (must be 40%%)\n",
577 	       prplane(&plane));
578 	    continue;
579 	}
580 	if (!pln_capable(&plane, wantflags, nowantflags))
581 	    continue;
582 	if (opt_MARKET) {
583 	    if (ontradingblock(EF_PLANE, &plane)) {
584 		pr("plane #%d ineligible - it's for sale.\n",
585 		   plane.pln_uid);
586 		continue;
587 	    }
588 	}
589 
590 	range = mapdist(plane.pln_x, plane.pln_y, ap->sct_x, ap->sct_y);
591 	if (range > 4) {
592 	    pr("%s too far from assembly point\n", prplane(&plane));
593 	    continue;
594 	}
595 	range += ap_to_target;
596 	range *= rangemult;
597 	pcp = &plchr[(int)plane.pln_type];
598 	if (plane.pln_range < range) {
599 	    pr("%s out of range (%d:%d)\n",
600 	       prplane(&plane), plane.pln_range, range);
601 	    continue;
602 	}
603 	if (!pln_airbase_ok(&plane, rangemult != 2, 1))
604 	    continue;
605 	pr("%s standing by\n", prplane(&plane));
606 	plane.pln_mission = 0;
607 	putplane(plane.pln_uid, &plane);
608 	plp = malloc(sizeof(struct plist));
609 	plp->load = 0;
610 	plp->pstage = PLG_HEALTHY;
611 	plp->pcp = pcp;
612 	plp->plane = plane;
613 	emp_insque(&plp->queue, list);
614     }
615 }
616 
617 void
pln_arm(struct emp_qelem * list,int dist,char mission,struct ichrstr * ip)618 pln_arm(struct emp_qelem *list, int dist, char mission, struct ichrstr *ip)
619 {
620     struct emp_qelem *qp;
621     struct emp_qelem *next;
622     struct plist *plp;
623     struct plnstr *pp;
624 
625     for (qp = list->q_forw; qp != list; qp = next) {
626 	next = qp->q_forw;
627 	plp = (struct plist *)qp;
628 	pp = &plp->plane;
629 	getplane(pp->pln_uid, pp);
630 	if ((pp->pln_flags & PLN_LAUNCHED)
631 	    || pln_equip(plp, ip, mission) < 0) {
632 	    emp_remque(qp);
633 	    free(qp);
634 	    continue;
635 	}
636 	pp->pln_flags |= PLN_LAUNCHED;
637 	pp->pln_mobil -= pln_mobcost(dist, pp, mission);
638 	putplane(pp->pln_uid, pp);
639 	pr("%s equipped\n", prplane(pp));
640     }
641 }
642 
643 int
pln_equip(struct plist * plp,struct ichrstr * ip,char mission)644 pln_equip(struct plist *plp, struct ichrstr *ip, char mission)
645 {
646     struct plchrstr *pcp;
647     struct plnstr *pp;
648     int load, needed;
649     struct lndstr land;
650     struct shpstr ship;
651     struct sctstr sect;
652     i_type itype;
653     short *item;
654     int own;
655     int abandon_needed;
656 
657     pp = &plp->plane;
658     pcp = plp->pcp;
659     if (pp->pln_ship >= 0) {
660 	getship(pp->pln_ship, &ship);
661 	plp->pstage = ship.shp_pstage;
662 	item = ship.shp_item;
663 	own = ship.shp_own;
664     } else if (pp->pln_land >= 0) {
665 	getland(pp->pln_land, &land);
666 	plp->pstage = land.lnd_pstage;
667 	item = land.lnd_item;
668 	own = land.lnd_own;
669     } else {
670 	getsect(pp->pln_x, pp->pln_y, &sect);
671 	plp->pstage = sect.sct_pstage;
672 	item = sect.sct_item;
673 	own = sect.sct_oldown;
674     }
675     if (pcp->pl_fuel > item[I_PETROL]) {
676 	pr("%s not enough petrol there!\n", prplane(pp));
677 	return -1;
678     }
679     item[I_PETROL] -= pcp->pl_fuel;
680     load = pln_load(pp);
681     itype = I_NONE;
682     switch (mission) {
683     case 's':		/* strategic bomb */
684     case 'p':		/* pinpoint bomb */
685 	itype = I_SHELL;
686 	break;
687     case 't':		/* transport */
688 	if (!(pcp->pl_flags & P_C) || !ip)
689 	    break;
690 	itype = ip->i_uid;
691 	load *= 2;
692 	break;
693     case 'm':		/* mine */
694 	if ((pcp->pl_flags & P_MINE) == 0)
695 	    break;
696 	itype = I_SHELL;
697 	load *= 2;
698 	break;
699     case 'd':		/* drop */
700 	if (!(pcp->pl_flags & P_C) || CANT_HAPPEN(!ip))
701 	    break;
702 	itype = ip->i_uid;
703 	if (pcp->pl_flags & P_V)
704 	    load *= 2;
705 	break;
706     case 'a':		/* paradrop */
707 	if (!(pcp->pl_flags & P_P))
708 	    break;
709 	itype = I_MILIT;
710 	if (pcp->pl_flags & P_V)
711 	    load *= 2;
712 	break;
713     case 'r':		/* reconnaissance */
714     case 'e':		/* escort */
715 	load = 0;
716 	break;
717     default:
718 	CANT_REACH();
719 	load = 0;
720     }
721 
722     if (itype != I_NONE) {
723 	needed = load / ichr[itype].i_lbs;
724 	if (needed <= 0) {
725 	    pr("%s can't contribute to mission\n", prplane(pp));
726 	    return -1;
727 	}
728 	if (nuk_on_plane(pp) >= 0) {
729 	    if (mission == 's' || mission == 't')
730 		needed = 0;
731 	    else {
732 		pr("%s can't fly this mission"
733 		   " while it is carrying a nuclear weapon\n",
734 		   prplane(pp));
735 		return -1;
736 	    }
737 	}
738 	if (itype == I_CIVIL && pp->pln_own != own) {
739 	    pr("You don't control those civilians!\n");
740 	    return -1;
741 	}
742 #if 0
743 	/* Supply is broken somewhere, so don't use it for now */
744 	if (itype == I_SHELL && item[itype] < needed)
745 	    item[itype] += supply_commod(plp->plane.pln_own,
746 					 plp->plane.pln_x,
747 					 plp->plane.pln_y,
748 					 I_SHELL, needed);
749 #endif
750 	if (pp->pln_ship >= 0 || pp->pln_land >= 0)
751 	    abandon_needed = 0;
752 	else
753 	    abandon_needed = !!would_abandon(&sect, itype, needed, NULL);
754 	if (item[itype] < needed + abandon_needed) {
755 	    pr("Not enough %s for %s\n", ichr[itype].i_name, prplane(pp));
756 	    return -1;
757 	}
758 	item[itype] -= needed;
759 	plp->load = needed;
760     }
761 
762     if (pp->pln_ship >= 0) {
763 	if (pp->pln_own != ship.shp_own) {
764 	    wu(0, ship.shp_own,
765 	       "%s %s prepares for takeoff from ship %s\n",
766 	       cname(pp->pln_own), prplane(pp), prship(&ship));
767 	}
768 	putship(ship.shp_uid, &ship);
769     } else if (pp->pln_land >= 0) {
770 	if (pp->pln_own != land.lnd_own) {
771 	    wu(0, land.lnd_own,
772 	       "%s %s prepares for takeoff from unit %s\n",
773 	       cname(pp->pln_own), prplane(pp), prland(&land));
774 	}
775 	putland(land.lnd_uid, &land);
776     } else {
777 	if (pp->pln_own != sect.sct_own) {
778 	    wu(0, sect.sct_own, "%s %s prepares for takeoff from %s\n",
779 	       cname(pp->pln_own), prplane(pp),
780 	       xyas(sect.sct_x, sect.sct_y, sect.sct_own));
781 	}
782 	putsect(&sect);
783     }
784     return 0;
785 }
786 
787 void
pln_put(struct emp_qelem * list)788 pln_put(struct emp_qelem *list)
789 {
790     struct emp_qelem *qp, *next;
791 
792     for (qp = list->q_forw; qp != list; qp = next) {
793 	next = qp->q_forw;
794 	pln_put1((struct plist *)qp);
795     }
796 }
797 
798 void
pln_put1(struct plist * plp)799 pln_put1(struct plist *plp)
800 {
801     struct plnstr *pp;
802     struct shpstr ship;
803     struct sctstr sect;
804 
805     pp = &plp->plane;
806 
807     if (CANT_HAPPEN((pp->pln_flags & PLN_LAUNCHED)
808 		    && (plchr[pp->pln_type].pl_flags & P_M)
809 		    && pp->pln_effic >= PLANE_MINEFF))
810 	pp->pln_effic = 0;   /* bug: missile launched but not used up */
811 
812     if (!(pp->pln_flags & PLN_LAUNCHED))
813 	;			/* never took off */
814     else if (pp->pln_effic < PLANE_MINEFF) {
815 	;			/* destroyed */
816     } else if (pp->pln_ship >= 0) {
817 	/* It is landing on a carrier */
818 	getship(pp->pln_ship, &ship);
819 	/* We should do more, like make sure it's really
820 	   a carrier, etc. but for now just make sure it's
821 	   not sunk. */
822 	if (!ship.shp_own) {
823 	    mpr(pp->pln_own,
824 		"Ship #%d has been sunk, plane #%d has nowhere to land, and\n"
825 		"splashes into the sea.\n",
826 		pp->pln_ship, pp->pln_uid);
827 	    pp->pln_effic = 0;
828 	}
829     } else {
830 	/* Presume we are landing back in a sector. */
831 	getsect(pp->pln_x, pp->pln_y, &sect);
832 	if (sect.sct_type == SCT_WATER || sect.sct_type == SCT_WASTE) {
833 	    mpr(pp->pln_own,
834 		"Nowhere to land at %s, plane #%d crashes and burns...\n",
835 		xyas(pp->pln_x, pp->pln_y, pp->pln_own), pp->pln_uid);
836 	    pp->pln_effic = 0;
837 	}
838     }
839     pp->pln_flags &= ~PLN_LAUNCHED;
840     putplane(pp->pln_uid, pp);
841     emp_remque(&plp->queue);
842     free(plp);
843 }
844 
845 /*
846  * Can a carrier of @sp's type carry this load of planes?
847  * The load consists of @n planes, of which @nch are choppers, @nxl
848  * xlight, @nmsl light missiles, and the rest are light fixed-wing
849  * planes.
850  */
851 static int
ship_can_carry(struct shpstr * sp,int n,int nch,int nxl,int nmsl)852 ship_can_carry(struct shpstr *sp, int n, int nch, int nxl, int nmsl)
853 {
854     struct mchrstr *mcp = &mchr[sp->shp_type];
855     int nfw = n - nch - nxl - nmsl;
856 
857     if (nch > mcp->m_nchoppers) /* overflow into fixed-wing slots */
858 	nfw += nch - mcp->m_nchoppers;
859     if (nxl > mcp->m_nxlight)	/* overflow into missile slots */
860 	nmsl += nxl - mcp->m_nxlight;
861     if (nmsl && !(mcp->m_flags & (M_MSL | M_FLY)))
862 	return 0;		/* missile slots wanted */
863     if (nfw && !(mcp->m_flags & M_FLY))
864 	return 0;		/* fixed-wing slots wanted */
865     return nfw + nmsl <= mcp->m_nplanes;
866 }
867 
868 /*
869  * Increment carrier plane counters for @pp.
870  * If it's a chopper, increment *@nch.
871  * Else, if it's x-light, increment *@nxl.
872  * Else, if it's a light missile, increment *@msl.
873  * Return non-zero if it's a chopper, x-light or light.
874  */
875 static int
inc_shp_nplane(struct plnstr * pp,int * nch,int * nxl,int * nmsl)876 inc_shp_nplane(struct plnstr *pp, int *nch, int *nxl, int *nmsl)
877 {
878     struct plchrstr *pcp = &plchr[pp->pln_type];
879 
880     if (pcp->pl_flags & P_K)
881 	(*nch)++;
882     else if (pcp->pl_flags & P_E)
883 	(*nxl)++;
884     else if (!(pcp->pl_flags & P_L))
885 	return 0;
886     else if (pcp->pl_flags & P_M)
887 	(*nmsl)++;
888     return 1;
889 }
890 
891 /*
892  * Can @pp be loaded on a ship of @sp's type?
893  */
894 int
could_be_on_ship(struct plnstr * pp,struct shpstr * sp)895 could_be_on_ship(struct plnstr *pp, struct shpstr *sp)
896 {
897     int nch = 0, nxl = 0, nmsl = 0;
898 
899     if (!inc_shp_nplane(pp, &nch, &nxl, &nmsl))
900 	return 0;
901     return ship_can_carry(sp, 1, nch, nxl, nmsl);
902 }
903 
904 int
put_plane_on_ship(struct plnstr * plane,struct shpstr * ship)905 put_plane_on_ship(struct plnstr *plane, struct shpstr *ship)
906 {
907     int n, nch, nxl, nmsl;
908 
909     if (plane->pln_ship == ship->shp_uid)
910 	return 1;		/* Already on ship */
911 
912     n = shp_nplane(ship, &nch, &nxl, &nmsl);
913     if (!inc_shp_nplane(plane, &nch, &nxl, &nmsl))
914 	return 0;		/* not a carrier plane */
915     if (!ship_can_carry(ship, n + 1, nch, nxl, nmsl))
916 	return 0;		/* no space */
917 
918     plane->pln_x = ship->shp_x;
919     plane->pln_y = ship->shp_y;
920     plane->pln_ship = ship->shp_uid;
921     putplane(plane->pln_uid, plane);
922     return 1;
923 }
924 
925 int
put_plane_on_land(struct plnstr * plane,struct lndstr * land)926 put_plane_on_land(struct plnstr *plane, struct lndstr *land)
927 {
928     if (plane->pln_land == land->lnd_uid)
929 	return 1;		/* Already on unit */
930     if (!(plchr[plane->pln_type].pl_flags & P_E))
931 	return 0;
932     if (lnd_nxlight(land) >= lchr[land->lnd_type].l_nxlight)
933 	return 0;
934 
935     plane->pln_x = land->lnd_x;
936     plane->pln_y = land->lnd_y;
937     plane->pln_land = land->lnd_uid;
938     putplane(plane->pln_uid, plane);
939     return 1;
940 }
941 
942 void
plane_sweep(struct emp_qelem * plane_list,coord x,coord y)943 plane_sweep(struct emp_qelem *plane_list, coord x, coord y)
944 {
945     struct plnstr *pp;
946     struct plchrstr *pcp;
947     struct emp_qelem *qp;
948     struct emp_qelem *next;
949     struct plist *ip;
950     struct sctstr sect;
951     int mines_there;
952     int found = 0;
953 
954     getsect(x, y, &sect);
955     mines_there = sect.sct_mines;
956 
957     if (mines_there == 0)
958 	return;
959 
960     if (sect.sct_type != SCT_WATER)
961 	return;
962 
963     for (qp = plane_list->q_forw; ((qp != plane_list) && (mines_there));
964 	 qp = next) {
965 	next = qp->q_forw;
966 	ip = (struct plist *)qp;
967 	pp = &ip->plane;
968 	pcp = ip->pcp;
969 	if (!(pcp->pl_flags & P_SWEEP))	/* if it isn't an sweep plane */
970 	    continue;
971 
972 	if (chance((100.0 - pln_acc(pp)) / 100.0)) {
973 	    pr("Sweep! in %s\n",
974 	       xyas(sect.sct_x, sect.sct_y, player->cnum));
975 	    mines_there--;
976 	    found = 1;
977 	}
978     }
979 
980     if (found && map_set(player->cnum, sect.sct_x, sect.sct_y, 'X', 0))
981 	writemap(player->cnum);
982     sect.sct_mines = mines_there;
983     putsect(&sect);
984 }
985 
986 int
pln_hitchance(struct plnstr * pp,int hardtarget,int type)987 pln_hitchance(struct plnstr *pp, int hardtarget, int type)
988 {
989     struct plchrstr *pcp = plchr + pp->pln_type;
990     double tfact = (double)(pp->pln_tech - pcp->pl_tech) /
991 	(pp->pln_tech - pcp->pl_tech / 2);
992     int acc = pln_acc(pp);
993     int hitchance;
994 
995     if (type == EF_SHIP) {
996 	if (pcp->pl_flags & P_A)
997 	    acc -= 20;
998 	if (!(pcp->pl_flags & P_T))
999 	    acc += 35;
1000     }
1001     hitchance = (int)(pp->pln_effic * (1.0 - 0.1 * tfact) *
1002 		      (1.0 - acc / 100.0)) - hardtarget;
1003 
1004     /* smooth out the bottom of the graph with asymtote at 5 -KHS */
1005     if (hitchance < 20)
1006 	hitchance = 5 + ldround(300.0 / (40.0 - hitchance), 1);
1007     if (hitchance > 100)
1008 	hitchance = 100;
1009     return hitchance;
1010 }
1011 
1012 int
pln_damage(struct plnstr * pp,char type,char * noisy)1013 pln_damage(struct plnstr *pp, char type, char *noisy)
1014 {
1015     struct plchrstr *pcp = plchr + pp->pln_type;
1016     int load, i, hitroll, aim, len;
1017     int dam = 0;
1018     int effective = 1;
1019     int pinbomber = 0;
1020     char buf[80];
1021 
1022     if (CANT_HAPPEN(nuk_on_plane(pp) >= 0))
1023 	return 0;
1024 
1025     load = pln_load(pp);
1026     if (!load)		       /* e.g. ab, blowing up on launch pad */
1027 	return 0;
1028 
1029     i = roll(load) + 1;
1030     if (i > load)
1031 	i = load;
1032 
1033     if (pcp->pl_flags & P_M) {
1034 	if (pcp->pl_flags & P_MAR)
1035 	    pinbomber = 1;
1036     } else if (pcp->pl_flags & P_T)
1037 	pinbomber = 1;
1038 
1039     aim = pln_acc(pp);
1040     if (type == 's') {
1041 	effective = !pinbomber;
1042 	aim = 30 + (pinbomber ? aim : 100 - aim);
1043     } else {
1044 	effective = pinbomber;
1045 	aim = 100 - aim;
1046     }
1047 
1048     len = noisy ? snprintf(buf, sizeof(buf), "%s", noisy) : 0;
1049     while (i--) {
1050 	if (noisy) {
1051 	    if (len > 75) {
1052 		mpr(pp->pln_own, "%s\n", buf);
1053 		len = 0;
1054 	    }
1055 	}
1056 	dam += roll(6);
1057 	hitroll = roll(100);
1058 	if (hitroll >= 90) {
1059 	    dam += 8;
1060 	    if (noisy)
1061 		len += sprintf(buf + len, "BLAM");
1062 	} else if (hitroll < aim) {
1063 	    dam += 5;
1064 	    if (noisy)
1065 		len += sprintf(buf + len, "Blam");
1066 	} else {
1067 	    dam += 1;
1068 	    if (noisy)
1069 		len += sprintf(buf + len, "blam");
1070 	}
1071 	if (noisy) {
1072 	    if (i)
1073 		len += sprintf(buf + len, "-");
1074 	}
1075     }
1076     if (noisy && len)
1077 	mpr(pp->pln_own, "%s\n", buf);
1078     if (effective)
1079 	dam *= 2;
1080     return dam;
1081 }
1082 
1083 int
pln_identchance(struct plnstr * pp,int hardtarget,int type)1084 pln_identchance(struct plnstr *pp, int hardtarget, int type)
1085 {
1086     double misschance =
1087 	(100.0 - pln_hitchance(pp, hardtarget, type)) / 100.0;
1088     return (int)(100 - 100 * misschance * misschance);
1089 }
1090 
1091 int
pln_mobcost(int dist,struct plnstr * pp,char mission)1092 pln_mobcost(int dist, struct plnstr *pp, char mission)
1093 {
1094     double cost;
1095 
1096     cost = 20.0 / (pp->pln_effic / 100.0);
1097     if (mission == 'e' || mission == 0)
1098 	cost /= 2;		/* escort or intercept */
1099 
1100     return ldround(cost * dist / pln_range_max(pp) + 5, 1);
1101 }
1102 
1103 int
pln_is_in_orbit(struct plnstr * pp)1104 pln_is_in_orbit(struct plnstr *pp)
1105 {
1106     return (plchr[pp->pln_type].pl_flags & (P_M | P_O)) == P_O
1107 	&& (pp->pln_flags & PLN_LAUNCHED);
1108 }
1109 
1110 /*
1111  * Set @pp's tech to @tlev along with everything else that depends on it.
1112  */
1113 void
pln_set_tech(struct plnstr * pp,int tlev)1114 pln_set_tech(struct plnstr *pp, int tlev)
1115 {
1116     struct plchrstr *pcp = plchr + pp->pln_type;
1117     int limited_range = pp->pln_range < pln_range_max(pp);
1118     int range_max;
1119 
1120     if (CANT_HAPPEN(tlev < pcp->pl_tech))
1121 	tlev = pcp->pl_tech;
1122     pp->pln_tech = tlev;
1123 
1124     range_max = pln_range_max(pp);
1125     if (!limited_range || pp->pln_range > range_max)
1126 	pp->pln_range = range_max;
1127 }
1128