]> git.pond.sub.org Git - empserver/blob - src/lib/subs/aircombat.c
Update copyright notice.
[empserver] / src / lib / subs / aircombat.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  *  aircombat.c: Deal with air to air combat
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Thomas Ruschak, 1992
33  *     Steve McClure, 1996
34  */
35
36 #include <string.h>
37
38 #include "misc.h"
39 #include "player.h"
40 #include "var.h"
41 #include "news.h"
42 #include "land.h"
43 #include "sect.h"
44 #include "nuke.h"
45 #include "plane.h"
46 #include "ship.h"
47 #include "nat.h"
48 #include "file.h"
49 #include "xy.h"
50 #include "nsc.h"
51 #include "path.h"
52 #include "prototypes.h"
53 #include "optlist.h"
54 #include "server.h"
55
56
57 #ifndef MAX
58 #define MAX(x,y) ((x) < (y) ? (y) : (x))
59 #endif
60
61 static void getilist(struct emp_qelem *list, natid own,
62                      struct emp_qelem *a, struct emp_qelem *b,
63                      struct emp_qelem *c, struct emp_qelem *d);
64 static void ac_dog(register struct plist *ap, register struct plist *dp);
65
66 #define FLAK_MAX        15
67
68                 /*       -7    -6    -5    -4    -3    -2    -1    0 */
69 float flaktable[16] = { 0.20, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,
70     0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85,
71 };
72
73 void
74 ac_encounter(struct emp_qelem *bomb_list, struct emp_qelem *esc_list,
75              coord x, coord y, s_char *path, int mission_flags,
76              int no_air_defense, struct emp_qelem *obomb,
77              struct emp_qelem *oesc)
78 {
79     int val, non_missiles;
80     int rel;
81     int dir;
82     int nats[MAXNOC];
83     int lnats[MAXNOC];
84     int gotilist[MAXNOC];
85     int unfriendly[MAXNOC];
86     int overfly[MAXNOC];
87     struct emp_qelem ilist[MAXNOC], *qp;
88     s_char mypath[1024];
89     int myp;
90     int civ, mil;
91     natid plane_owner;
92     struct sctstr sect;
93     struct shpstr ship;
94     struct lndstr land;
95     struct nstr_item ni;
96     natid cn;
97     struct natstr *over, *mynatp;
98     struct plist *plp;
99     int evaded;
100     struct shiplook head;
101     struct shiplook *s, *s2;
102     int changed = 0;
103     int intx, inty;             /* Last sector planes intercepted over */
104     int intown = 0;             /* Last owner to intercept */
105 /* We want to only intercept once per sector per owner.  So, if we overfly
106    a sector, and then overfly some land units or ships, we don't want to
107    potentially intercept 3 times. */
108
109     memset(&head, 0, sizeof(struct shiplook));
110     head.uid = -1;
111
112     plp = (struct plist *)bomb_list->q_forw;
113     plane_owner = plp->plane.pln_own;
114
115     strncpy(mypath, path, sizeof(mypath));
116     myp = 0;
117
118     memset(overfly, 0, sizeof(overfly));
119     memset(gotilist, 0, sizeof(gotilist));
120     memset(unfriendly, 0, sizeof(unfriendly));
121     for (cn = 1; cn < MAXNOC; cn++) {
122         if ((mynatp = getnatp(cn)) == 0)
123             continue;
124         rel = getrel(mynatp, plane_owner);
125         if (rel > HOSTILE)
126             continue;
127         if (plane_owner == cn)
128             continue;
129         unfriendly[cn]++;
130     }
131     if (mission_flags & PM_R) {
132         if (mission_flags & P_S) {
133             PR(plane_owner, "\nSPY Plane report\n");
134             PRdate(plane_owner);
135             sathead();
136         } else if (mission_flags & P_A) {
137             PR(plane_owner, "\nAnti-Sub Patrol report\n");
138         } else {
139             PR(plane_owner, "\nReconnaissance report\n");
140             PRdate(plane_owner);
141         }
142     }
143
144     pln_removedupes(bomb_list, esc_list);
145     while ((dir = mypath[myp++]) && !QEMPTY(bomb_list)) {
146         if ((val = chkdir(dir, DIR_STOP, DIR_LAST)) == 0)
147             break;
148         /* XXX using xnorm is probably bad */
149         x = xnorm(x + diroff[val][0]);
150         y = ynorm(y + diroff[val][1]);
151         getsect(x, y, &sect);
152         over = getnatp(sect.sct_own);
153         if (opt_HIDDEN) {
154             setcont(plane_owner, sect.sct_own, FOUND_FLY);
155         }
156
157         if (mission_flags & PM_R) {
158             if (sect.sct_type == SCT_WATER) {
159                 PR(plane_owner, "flying over %s at %s\n",
160                    dchr[sect.sct_type].d_name, xyas(x, y, plane_owner));
161                 if (mission_flags & PM_S)
162                     plane_sweep(bomb_list, x, y);
163                 if (mission_flags & P_A) {
164                     plane_sona(bomb_list, x, y, &head);
165                 }
166                 changed += map_set(plane_owner,
167                                    sect.sct_x, sect.sct_y,
168                                    dchr[sect.sct_type].d_mnem, 0);
169             } else if (mission_flags & P_S) {
170                 satdisp(&sect, (mission_flags & P_I) ? 10 : 50, 1);
171             } else {
172                 /* This is borrowed from lookout */
173                 if (sect.sct_own == plane_owner)
174                     PR(plane_owner, "Your ");
175                 else
176                     PR(plane_owner,
177                        "%s (#%d) ", cname(sect.sct_own), sect.sct_own);
178                 PR(plane_owner, dchr[sect.sct_type].d_name);
179                 changed += map_set(plane_owner,
180                                    sect.sct_x, sect.sct_y,
181                                    dchr[sect.sct_type].d_mnem, 0);
182                 PR(plane_owner, " %d%% efficient ",
183                    (sect.sct_own == plane_owner) ?
184                    sect.sct_effic : roundintby((int)sect.sct_effic, 25));
185                 civ = sect.sct_item[I_CIVIL];
186                 mil = sect.sct_item[I_MILIT];
187                 if (civ)
188                     PR(plane_owner, "with %s%d civ ",
189                        (sect.sct_own == plane_owner) ?
190                        "" : "approx ",
191                        (sect.sct_own == plane_owner) ?
192                        civ : roundintby(civ, 25));
193                 if (mil)
194                     PR(plane_owner, "with %s%d mil ",
195                        (sect.sct_own == plane_owner) ?
196                        "" : "approx ",
197                        (sect.sct_own == plane_owner) ?
198                        mil : roundintby(mil, 25));
199                 PR(plane_owner, "@ %s\n", xyas(x, y, plane_owner));
200             }
201         } else {
202             PR(plane_owner, "flying over %s at %s\n",
203                dchr[sect.sct_type].d_name, xyas(x, y, plane_owner));
204             changed += map_set(plane_owner, sect.sct_x,
205                                sect.sct_y, dchr[sect.sct_type].d_mnem, 0);
206         }
207         if ((rel = getrel(over, plane_owner)) == ALLIED)
208             continue;
209
210         evaded = do_evade(bomb_list, esc_list);
211
212         if (sect.sct_own != 0 && sect.sct_own != plane_owner && (!evaded)) {
213             /* We only show planes overhead if they didn't
214              * evade radar */
215             overfly[sect.sct_own]++;
216             PR(sect.sct_own, "%s planes spotted over %s\n",
217                cname(plane_owner), xyas(x, y, sect.sct_own));
218         }
219
220         if (!evaded) {
221             /* Fire flak */
222             if (unfriendly[sect.sct_own])
223                 ac_doflak(bomb_list, &sect);
224             /* If bombers left, fire flak from units and ships */
225             if (!QEMPTY(bomb_list))
226                 ac_landflak(bomb_list, x, y);
227             if (!QEMPTY(bomb_list))
228                 ac_shipflak(bomb_list, x, y);
229         }
230         /* mission planes aborted due to flak -- don't send escorts */
231         if (QEMPTY(bomb_list))
232             break;
233         if ((!no_air_defense) && (!evaded))
234             air_defense(x, y, plane_owner, bomb_list, esc_list);
235
236         if (sect.sct_own == 0 || sect.sct_own == plane_owner)
237             continue;
238
239         if (evaded)
240             continue;
241
242         non_missiles = 0;
243         for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw) {
244             struct plist *ip = (struct plist *)qp;
245             if (!(plchr[(int)ip->plane.pln_type].pl_flags & P_M))
246                 non_missiles = 1;
247         }
248
249         if (!non_missiles)
250             continue;
251
252         if (unfriendly[sect.sct_own] && !gotilist[sect.sct_own]) {
253             getilist(&ilist[sect.sct_own], sect.sct_own,
254                      bomb_list, esc_list, obomb, oesc);
255             gotilist[sect.sct_own]++;
256         }
257         if (rel > HOSTILE)
258             continue;
259         ac_intercept(bomb_list, esc_list, &ilist[sect.sct_own],
260                      sect.sct_own, x, y);
261         intx = x;
262         inty = y;
263         intown = sect.sct_own;
264     }
265
266     /* Let's report all of the overflights even if aborted */
267     for (cn = 1; cn < MAXNOC; cn++) {
268         if (plane_owner == cn)
269             continue;
270         if (overfly[cn] > 0)
271             nreport(plane_owner, N_OVFLY_SECT, cn, overfly[cn]);
272     }
273     /* If the map changed, update it */
274     if (changed)
275         if (!update_pending && plane_owner == player->cnum)
276             writemap(player->cnum);
277     /* Now, if the bomber and escort lists are empty, we are done */
278     if (QEMPTY(bomb_list) && QEMPTY(esc_list)) {
279         if (mission_flags & P_A) {
280             s = head.next;
281             while (s != (struct shiplook *)0) {
282                 s2 = s;
283                 s = s->next;
284                 free(s2);
285             }
286         }
287         return;
288     }
289
290     /* Something made it through */
291     /* Go figure out if there are ships in this sector, and who's they are */
292     memset(nats, 0, sizeof(nats));
293     snxtitem_xy(&ni, EF_SHIP, x, y);
294     while (nxtitem(&ni, (s_char *)&ship)) {
295         if (mchr[(int)ship.shp_type].m_flags & M_SUB)
296             continue;
297         nats[ship.shp_own]++;
298     }
299     /* Go figure out if there are units in this sector, and who's they are */
300     memset(lnats, 0, sizeof(lnats));
301     snxtitem_xy(&ni, EF_LAND, x, y);
302     while (nxtitem(&ni, (s_char *)&land)) {
303         lnats[land.lnd_own]++;
304     }
305
306     /* Now, let's make life a little rougher. */
307     for (cn = 1; cn < MAXNOC && !QEMPTY(bomb_list); cn++) {
308         if (plane_owner == cn)
309             continue;
310         intown = -1;
311         /* Are there ships owned by this country? */
312         if (nats[cn] != 0) {
313             /* Yes. */
314             if (cn != 0)
315                 PR(cn, "%s planes spotted over ships in %s\n",
316                    cname(plane_owner), xyas(x, y, cn));
317             if (unfriendly[cn]) {
318                 /* They are unfriendly too */
319                 if (!gotilist[cn]) {
320                     getilist(&ilist[cn], cn, bomb_list, esc_list, obomb,
321                              oesc);
322                     gotilist[cn]++;
323                 }
324                 PR(plane_owner, "Flying over %s ships in %s\n", cname(cn),
325                    xyas(x, y, plane_owner));
326                 /* This makes going for ships in harbors tough */
327                 if (!evaded) {
328                     /* We already fired flak up above.  Now we intercept again if we haven't already */
329                     /* Flag that we intercepted */
330                     intown = 1;
331                     /* And now intercept again */
332                     ac_intercept(bomb_list, esc_list, &ilist[cn], cn, x,
333                                  y);
334                 }
335             }
336         }
337         /* Are there units owned by this country? */
338         if (lnats[cn] != 0) {
339             /* Yes. */
340             if (cn != 0)
341                 PR(cn, "%s planes spotted over land units in %s\n",
342                    cname(plane_owner), xyas(x, y, cn));
343             if (unfriendly[cn]) {
344                 /* They are unfriendly too */
345                 if (!gotilist[cn]) {
346                     getilist(&ilist[cn], cn, bomb_list, esc_list, obomb,
347                              oesc);
348                     gotilist[cn]++;
349                 }
350                 PR(plane_owner, "Flying over %s land units in %s\n",
351                    cname(cn), xyas(x, y, plane_owner));
352                 if (!evaded) {
353                     if (intown == -1) {
354                         /* We haven't intercepted yet, so intercept */
355                         ac_intercept(bomb_list, esc_list, &ilist[cn], cn,
356                                      x, y);
357                     }
358                 }
359             }
360         }
361     }
362
363     if ((mission_flags & P_A) && (head.uid != -1)) {
364         s = head.next;
365         while (s != (struct shiplook *)0) {
366             s2 = s;
367             s = s->next;
368             free(s2);
369         }
370     }
371 }
372
373 static int
374 count_non_missiles(struct emp_qelem *list)
375 {
376     struct emp_qelem *qp;
377     struct plist *plp;
378     int att_count = 0;
379
380     /* don't intercept missiles */
381     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
382         plp = (struct plist *)qp;
383         if (!(plp->pcp->pl_flags & P_M))
384             att_count++;
385     }
386     return att_count;
387 }
388
389 void
390 sam_intercept(struct emp_qelem *att_list, struct emp_qelem *def_list,
391               natid def_own, natid plane_owner, coord x, coord y,
392               int delete_missiles)
393 {
394     struct emp_qelem *aqp;
395     struct emp_qelem *anext;
396     struct emp_qelem *dqp;
397     struct emp_qelem *dnext;
398     struct plist *aplp;
399     struct plist *dplp;
400     int first = 1;
401
402     for (aqp = att_list->q_forw,
403          dqp = def_list->q_forw;
404          aqp != att_list && dqp != def_list; aqp = anext) {
405         anext = aqp->q_forw;
406         aplp = (struct plist *)aqp;
407         if (aplp->pcp->pl_flags & P_M)
408             continue;
409         if (aplp->pcp->pl_cost < 1000)
410             continue;
411         for (; dqp != def_list; dqp = dnext) {
412             dnext = dqp->q_forw;
413             dplp = (struct plist *)dqp;
414             if (!(dplp->pcp->pl_flags & P_M))
415                 continue;
416
417             if (dplp->plane.pln_range <
418                 mapdist(x, y, dplp->plane.pln_x, dplp->plane.pln_y)) {
419                 emp_remque(dqp);
420                 free((s_char *)dqp);
421                 continue;
422             }
423             if (mission_pln_equip(dplp, 0, P_F, 0) < 0) {
424                 emp_remque(dqp);
425                 free((s_char *)dqp);
426                 continue;
427             }
428             if (first) {
429                 first = 0;
430                 PR(plane_owner, "%s launches SAMs!\n", cname(def_own));
431                 PR(def_own, "Launching SAMs at %s planes!\n",
432                    cname(plane_owner));
433                 ac_combat_headers(plane_owner, def_own);
434             }
435             ac_dog(aplp, dplp);
436             dqp = dnext;
437             break;
438         }
439     }
440     if (!first) {
441         PR(plane_owner, "\n");
442         PR(def_own, "\n");
443     }
444     if (delete_missiles) {
445         for (; dqp != def_list; dqp = dnext) {
446             dnext = dqp->q_forw;
447             dplp = (struct plist *)dqp;
448             if (!(dplp->pcp->pl_flags & P_M))
449                 continue;
450             emp_remque(dqp);
451             free((s_char *)dqp);
452             continue;
453         }
454     }
455 }
456
457 void
458 ac_intercept(struct emp_qelem *bomb_list, struct emp_qelem *esc_list,
459              struct emp_qelem *def_list, natid def_own, coord x, coord y)
460 {
461     register struct plnstr *pp;
462     struct plist *plp;
463     int icount;
464     struct emp_qelem *next;
465     struct emp_qelem *qp;
466     struct emp_qelem int_list;
467     int att_count;
468     natid plane_owner;
469     int dist;
470
471     plp = (struct plist *)bomb_list->q_forw;
472     plane_owner = plp->plane.pln_own;
473
474     icount = 0;
475
476     sam_intercept(bomb_list, def_list, def_own, plane_owner, x, y, 0);
477     sam_intercept(esc_list, def_list, def_own, plane_owner, x, y, 1);
478     if (!(att_count = count_non_missiles(bomb_list) +
479           count_non_missiles(esc_list)))
480         return;
481
482     emp_initque(&int_list);
483     for (qp = def_list->q_forw; qp != def_list; qp = next) {
484         next = qp->q_forw;
485         plp = (struct plist *)qp;
486         pp = &plp->plane;
487         /* SAMs interdict separately */
488         if (plp->pcp->pl_flags & P_M)
489             continue;
490         dist = mapdist(x, y, pp->pln_x, pp->pln_y) * 2;
491         if (pp->pln_range < dist)
492             continue;
493         if (mission_pln_equip(plp, 0, P_F, 0) < 0) {
494             emp_remque(qp);
495             free((s_char *)qp);
496             continue;
497         }
498         /* got one; delete from def_list, add to int_list */
499         emp_remque(qp);
500         emp_insque(qp, &int_list);
501         pp->pln_mobil -= pln_mobcost(dist, pp, P_F);
502         putplane(pp->pln_uid, pp);
503         icount++;
504         if (icount > att_count)
505             break;
506     }
507     if (icount == 0)
508         return;
509     PR(plane_owner, "%d %s fighter%s rising to intercept!\n", icount,
510        cname(def_own), icount == 1 ? " is" : "s are");
511     PR(def_own, "%d fighter%s intercepting %s planes!\n", icount,
512        icount == 1 ? " is" : "s are", cname(plane_owner));
513     ac_combat_headers(plane_owner, def_own);
514     ac_airtoair(esc_list, &int_list, def_own);
515     ac_airtoair(bomb_list, &int_list, def_own);
516     PR(plane_owner, "\n");
517     PR(def_own, "\n");
518 }
519
520 void
521 ac_combat_headers(natid plane_owner, natid def_own)
522 {
523     PR(plane_owner,
524        " %-10.10s %-10.10s  strength int odds  damage           results\n",
525        cname(plane_owner), cname(def_own));
526     PR(def_own,
527        " %-10.10s %-10.10s  strength int odds  damage           results\n",
528        cname(def_own), cname(plane_owner));
529 }
530
531 /*
532  * air-to-air combat.
533  */
534 void
535 ac_airtoair(struct emp_qelem *att_list, struct emp_qelem *int_list,
536             natid def_own)
537 {
538     register struct plist *attacker;
539     register struct plist *interceptor;
540     struct emp_qelem *att;
541     struct emp_qelem *in;
542     int nplanes;
543     int more_att;
544     int more_int;
545     struct emp_qelem *att_next;
546     struct emp_qelem *in_next;
547     natid att_own;
548
549     att = att_list->q_forw;
550     in = int_list->q_forw;
551     more_att = 1;
552     more_int = 1;
553     if (QEMPTY(att_list) || QEMPTY(int_list)) {
554         more_att = 0;
555         more_int = 0;
556     }
557     while (more_att || more_int) {
558         in_next = in->q_forw;
559         att_next = att->q_forw;
560         attacker = (struct plist *)att;
561
562         /* skip missiles. If only missiles left, we're done */
563         if (plchr[(int)attacker->plane.pln_type].pl_flags & P_M) {
564             att = att_next;
565             if (att == att_list) {
566                 more_att = 0;
567                 if (QEMPTY(att_list))
568                     more_int = 0;
569                 else
570                     att = att->q_forw;
571             }
572             if (all_missiles(att_list))
573                 more_att = 0;
574             continue;
575         }
576         interceptor = (struct plist *)in;
577         att_own = attacker->plane.pln_own;
578         def_own = interceptor->plane.pln_own;
579         nplanes = attacker->plane.pln_effic;
580         if (nplanes > interceptor->plane.pln_effic)
581             nplanes = interceptor->plane.pln_effic;
582         ac_dog(attacker, interceptor);
583         in = in_next;
584         att = att_next;
585         if (att == att_list) {
586             more_att = 0;
587             if (QEMPTY(att_list))
588                 more_int = 0;
589             else
590                 att = att->q_forw;
591         }
592         if (in == int_list) {
593             more_int = 0;
594             if (QEMPTY(int_list))
595                 more_att = 0;
596             else
597                 in = in->q_forw;
598         }
599     }
600 }
601
602 int
603 all_missiles(struct emp_qelem *att_list)
604 {
605     struct emp_qelem *qp;
606     struct plist *p;
607
608     qp = att_list->q_forw;
609     while (qp != att_list) {
610         p = (struct plist *)qp;
611         if (!(plchr[(int)p->plane.pln_type].pl_flags & P_M))
612             return 0;
613
614         qp = qp->q_forw;
615     }
616     return 1;
617 }
618
619 static void
620 ac_dog(register struct plist *ap, register struct plist *dp)
621 {
622     int att, def;
623     double odds;
624     int intensity;
625     natid att_own, def_own;
626     int adam, ddam;
627     s_char mesg[1024];
628     s_char temp[14];
629
630     att_own = ap->plane.pln_own;
631     def_own = dp->plane.pln_own;
632
633     PR(att_own, " %3.3s #%-4d  %3.3s #%-4d",
634        ap->pcp->pl_name,
635        ap->plane.pln_uid, dp->pcp->pl_name, dp->plane.pln_uid);
636     if (def_own)
637         PR(def_own, " %3.3s #%-4d  %3.3s #%-4d",
638            dp->pcp->pl_name,
639            dp->plane.pln_uid, ap->pcp->pl_name, ap->plane.pln_uid);
640     if (ap->plane.pln_att == 0) {
641         att = ap->plane.pln_def * ap->plane.pln_effic / 100;
642         att = MAX(att, ap->pcp->pl_def / 2);
643     } else {
644         att = ap->plane.pln_att * ap->plane.pln_effic / 100;
645         att = MAX(att, ap->pcp->pl_att / 2);
646     }
647
648     def = dp->plane.pln_def * dp->plane.pln_effic / 100;
649     def = MAX(def, dp->pcp->pl_def / 2);
650
651     if ((ap->pcp->pl_flags & P_F) && ap->bombs != 0)
652         att -= 2;
653     if ((dp->pcp->pl_flags & P_F) && dp->bombs != 0)
654         def -= 2;
655     att += ((float)ap->pcp->pl_stealth / 25.0);
656     def += ((float)dp->pcp->pl_stealth / 25.0);
657     if (att < 1) {
658         def += 1 - att;
659         att = 1;
660     }
661     if (def < 1) {
662         att += 1 - def;
663         def = 1;
664     }
665     odds = ((double)att / ((double)def + (double)att));
666     if (odds <= 0.05)
667         odds = 0.05;
668     intensity = roll(20) + roll(20) + roll(20) + roll(20) + 1;
669
670     PR(att_own, "   %3d/%-3d %3d  %3.2f  ", att, def, intensity, odds);
671     PR(def_own, "   %3d/%-3d %3d  %3.2f  ", def, att, intensity, odds);
672
673     adam = 0;
674     ddam = 0;
675     while ((intensity--) > 0) {
676
677         if (chance(odds)) {
678             ddam += 1;
679             if ((dp->plane.pln_effic - ddam) < PLANE_MINEFF)
680                 intensity = 0;
681         } else {
682             adam += 1;
683             if ((ap->plane.pln_effic - adam) < PLANE_MINEFF)
684                 intensity = 0;
685         }
686     }
687
688     if (dp->pcp->pl_flags & P_M)
689         ddam = 100;
690
691     PR(att_own, "%3d/%-3d", adam, ddam);
692     PR(def_own, "%3d/%-3d", ddam, adam);
693     ac_planedamage(ap, dp->plane.pln_own, adam, def_own, 1, 0, mesg);
694     strncpy(temp, mesg, 14);
695     ac_planedamage(dp, ap->plane.pln_own, ddam, att_own, 1, 0, mesg);
696     PR(att_own, "%-13.13s %-13.13s\n", temp, mesg);
697     PR(def_own, "%-13.13s %-13.13s\n", mesg, temp);
698 }
699
700 /*
701  * zap plane associated with plp.
702  * Damaging country is "from", damage is "dam".
703  * def_own is the country on the other side of the conflict from the plane
704  * owner. The only time def_own != from is when the interceptor is getting
705  * damaged.
706  *
707  * NOTE: This routine removes the appropriate plane element from the
708  * queue if it gets destroyed.  That means that the caller must assume
709  * that the current queue pointer is invalid on return from the ac_planedamage
710  * call.  (this has caused bugs in the past)
711  */
712 void
713 ac_planedamage(struct plist *plp, natid from, int dam, natid other,
714                int checkabort, int show, s_char *mesg)
715 {
716     register struct plnstr *pp;
717     int disp;
718     s_char dmess[255];
719     int eff;
720     struct shpstr ship;
721     struct lndstr land;
722     /* s_char *sprintf();           already in misc.h [JFW] */
723     natid plane_owner;
724
725     disp = 0;
726     pp = &plp->plane;
727     plane_owner = pp->pln_own;
728     eff = pp->pln_effic;
729     sprintf(dmess, " no damage");
730     if (dam <= 0) {
731         strcpy(mesg, dmess);
732         return;
733     }
734     memset(dmess, 0, sizeof(dmess));
735     eff -= dam;
736     if (eff < 0)
737         eff = 0;
738     if (eff < PLANE_MINEFF) {
739         sprintf(dmess, " shot down");
740         disp = 1;
741     } else if (eff < 80 && chance((100 - eff) / 100.0) && checkabort) {
742         sprintf(dmess, " aborted @%2d%%", eff);
743         disp = 2;
744     } else if (show == 0) {
745         sprintf(dmess, " cleared");
746     }
747
748     if ((plp->pcp->pl_flags & P_M) == 0) {
749         if (show) {
750             PR(plane_owner, "    %s %s takes %d%s.\n",
751                cname(pp->pln_own), prplane(pp), dam, dmess);
752             if (other)
753                 PR(other, "    %s %s takes %d%s.\n",
754                    cname(pp->pln_own), prplane(pp), dam, dmess);
755         }
756     }
757     if (show && checkabort == 1) {
758         PR(plane_owner, "\n");
759         if (other)
760             PR(other, "\n");
761     }
762
763     pp->pln_effic = eff;
764     pp->pln_mobil -= min(32 + pp->pln_mobil, dam / 2);
765     if (disp == 1) {
766         if (from != 0 && (plp->pcp->pl_flags & P_M) == 0)
767             nreport(from, N_DOWN_PLANE, pp->pln_own, 1);
768         if (pp->pln_ship >= 0) {
769             getship(pp->pln_ship, &ship);
770             take_plane_off_ship(pp, &ship);
771         }
772         if (pp->pln_land >= 0) {
773             getland(pp->pln_land, &land);
774             take_plane_off_land(pp, &land);
775         }
776         makelost(EF_PLANE, pp->pln_own, pp->pln_uid, pp->pln_x, pp->pln_y);
777         pp->pln_own = 0;
778         putplane(pp->pln_uid, pp);
779         emp_remque(&plp->queue);
780         free((s_char *)plp);
781     } else if (disp == 2) {
782         putplane(pp->pln_uid, pp);
783         emp_remque(&plp->queue);
784         free((s_char *)plp);
785     } else
786         putplane(pp->pln_uid, pp);
787     strcpy(mesg, dmess);
788 }
789
790 void
791 ac_doflak(struct emp_qelem *list, struct sctstr *from)
792 {
793     int shell;
794     int gun;
795     natid plane_owner;
796     struct plist *plp;
797
798     plp = (struct plist *)list->q_forw;
799     plane_owner = plp->plane.pln_own;
800
801     gun = from->sct_item[I_GUN];
802     shell = from->sct_item[I_SHELL];
803     if (gun > shell * 2) {
804         shell += supply_commod(from->sct_own, from->sct_x, from->sct_y,
805                                I_SHELL, (gun + 1) / 2 - shell);
806         from->sct_item[I_SHELL] = shell;
807         putsect(from);
808     }
809     if (gun > shell * 2)
810         gun = shell * 2;
811     if (gun > 14)
812         gun = 14;
813
814     gun = 2.0 * tfact(from->sct_own, gun);
815     if (gun > 0) {
816         PR(plane_owner, "firing %d flak guns in %s...\n",
817            gun, xyas(from->sct_x, from->sct_y, plane_owner));
818         if (from->sct_own != 0)
819             PR(from->sct_own, "firing %d flak guns in %s...\n",
820                gun, xyas(from->sct_x, from->sct_y, from->sct_own));
821         ac_fireflak(list, from->sct_own, 0, gun);
822     }
823 }
824
825 void
826 ac_shipflak(struct emp_qelem *list, coord x, coord y)
827 {
828     struct nstr_item ni;
829     struct shpstr ship;
830     struct mchrstr *mcp;
831     int firing;
832     int guns;
833     int gun;
834     int shell;
835     int rel;
836     struct plist *plp;
837     natid plane_owner;
838     natid from;
839     int nats[MAXNOC];
840
841     plp = (struct plist *)list->q_forw;
842     plane_owner = plp->plane.pln_own;
843
844     memset(nats, 0, sizeof(nats));
845     guns = 0;
846     snxtitem_xy(&ni, EF_SHIP, x, y);
847     while (!QEMPTY(list) && nxtitem(&ni, (s_char *)&ship)) {
848         if (ship.shp_own == 0 || ship.shp_own == plane_owner)
849             continue;
850         if (guns >= 14)
851             break;
852         mcp = &mchr[(int)ship.shp_type];
853         if (mcp->m_flags & M_SUB)
854             continue;
855         rel = getrel(getnatp(ship.shp_own), plane_owner);
856         if (rel > HOSTILE)
857             continue;
858         shell = gun = 0;
859         gun = min(ship.shp_item[I_GUN], ship.shp_glim);
860         if (gun) {
861             shell = ship.shp_item[I_SHELL];
862             if (shell <= 0) {
863                 shell = supply_commod(ship.shp_own, ship.shp_x,
864                                       ship.shp_y, I_SHELL, 1);
865                 ship.shp_item[I_SHELL] = shell;
866                 putship(ship.shp_uid, &ship);
867             }
868         }
869         if (gun == 0 || shell == 0)
870             continue;
871         firing = (int)(techfact(ship.shp_tech, (double)gun) * 2.0);
872         guns += firing;
873
874         if (!nats[ship.shp_own]) {
875             /* First time here, print the message */
876             PR(ship.shp_own, "%s planes spotted over ships in %s\n",
877                cname(plane_owner), xyas(x, y, ship.shp_own));
878             PR(plane_owner, "Flying over %s ships in %s\n",
879                cname(ship.shp_own), xyas(x, y, plane_owner));
880             nats[ship.shp_own] = 1;
881         }
882         PR(ship.shp_own, "firing %d flak guns from %s...\n",
883            firing, prship(&ship));
884         from = ship.shp_own;
885     }
886     if (guns > 0) {
887         if (guns > 14)
888             guns = 14;
889         guns = 2.0 * tfact(from, (double)guns);
890         PR(plane_owner, "Flak!  Ships firing %d flak guns...\n", guns);
891         ac_fireflak(list, from, 0, guns);
892     }
893 }
894
895 void
896 ac_landflak(struct emp_qelem *list, coord x, coord y)
897 {
898     struct nstr_item ni;
899     struct lndstr land;
900     struct lchrstr *lcp;
901     int firing;
902     int guns;
903     int rel;
904     natid from;
905     struct plist *plp;
906     natid plane_owner;
907     int nats[MAXNOC];
908
909     plp = (struct plist *)list->q_forw;
910     plane_owner = plp->plane.pln_own;
911
912     memset(nats, 0, sizeof(nats));
913     guns = 0;
914     snxtitem_xy(&ni, EF_LAND, x, y);
915     while (!QEMPTY(list) && nxtitem(&ni, (s_char *)&land)) {
916         if (land.lnd_own == 0 || land.lnd_own == plane_owner)
917             continue;
918         if (guns >= 14)
919             break;
920         lcp = &lchr[(int)land.lnd_type];
921
922         if ((lcp->l_flags & L_FLAK) == 0)
923             continue;
924
925         if (land.lnd_aaf == 0)
926             continue;
927
928         rel = getrel(getnatp(land.lnd_own), plane_owner);
929         if (rel > HOSTILE)
930             continue;
931         firing =
932             (int)(techfact(land.lnd_tech, (double)land.lnd_aaf) * 3.0);
933         guns += firing;
934
935         if (!nats[land.lnd_own]) {
936             /* First time here, print the message */
937             PR(land.lnd_own, "%s planes spotted over land units in %s\n",
938                cname(plane_owner), xyas(x, y, land.lnd_own));
939             PR(plane_owner, "Flying over %s land units in %s\n",
940                cname(land.lnd_own), xyas(x, y, plane_owner));
941             nats[land.lnd_own] = 1;
942         }
943         PR(land.lnd_own, "firing flak guns from unit %s (aa rating %d)\n",
944            prland(&land), land.lnd_aaf);
945
946         from = land.lnd_own;    /* We always use the last owner as the from */
947     }
948     if (guns > 0) {
949         if (guns > 14)
950             guns = 14;
951         guns = 2.0 * tfact(from, (double)guns);
952         PR(plane_owner, "Flak!  Land units firing %d flak guns...\n",
953            guns);
954         ac_fireflak(list, from, 0, guns);
955     }
956 }
957
958 /*
959  * Called from shipflak, landflak, and doflak.
960  */
961 void
962 ac_fireflak(struct emp_qelem *list, natid from, natid other, int guns)
963 {
964     register struct plnstr *pp;
965     struct plist *plp;
966     int n;
967     float mult;
968     int diff;
969     struct emp_qelem *qp;
970     struct emp_qelem *next;
971     s_char msg[255];
972
973     plp = (struct plist *)list->q_forw;
974
975     for (qp = list->q_forw; qp != list; qp = next) {
976         /*
977          * fighters don't get shot at by flak
978          * non-tactical bombers are harder to hit with flak.
979          * ('Cause they're not dive-bombing?)
980          */
981         next = qp->q_forw;
982         plp = (struct plist *)qp;
983         pp = &plp->plane;
984         diff = guns - pp->pln_def;
985         if ((plp->pcp->pl_flags & P_T) == 0)
986             diff--;
987         if (plp->pcp->pl_flags & P_X)
988             diff -= 2;
989         if (plp->pcp->pl_flags & P_H)
990             diff -= 1;
991         if (diff > 8)
992             mult = flaktable[FLAK_MAX] * 1.33;
993         else if (diff < -7)
994             mult = flaktable[0] * 0.66;
995         else {
996             diff += 7;
997             mult = flaktable[diff];
998         }
999         mult *= flakscale;
1000         n = (int)((roll(8) + 2) * mult);
1001         if (n > 100)
1002             n = 100;
1003         ac_planedamage(plp, from, n, other, 2, 1, msg);
1004     }
1005 }
1006
1007 /*
1008  * See if this plane is flying in this list
1009  */
1010 int
1011 ac_isflying(struct plnstr *plane, struct emp_qelem *list)
1012 {
1013     struct emp_qelem *qp;
1014     struct emp_qelem *next;
1015     struct plnstr *pp;
1016     struct plist *plp;
1017
1018     if (!list)
1019         return 0;
1020     for (qp = list->q_forw; qp != list; qp = next) {
1021         next = qp->q_forw;
1022         plp = (struct plist *)qp;
1023         pp = &plp->plane;
1024         if (plane->pln_uid == pp->pln_uid)
1025             return 1;
1026     }
1027     return 0;
1028 }
1029
1030
1031 /*
1032  * Get a list of planes available for interception duties.
1033  */
1034 static void
1035 getilist(struct emp_qelem *list, natid own, struct emp_qelem *a,
1036          struct emp_qelem *b, struct emp_qelem *c, struct emp_qelem *d)
1037 {
1038     register struct plchrstr *pcp;
1039     struct plnstr plane;
1040     struct shpstr ship;
1041     struct lndstr land;
1042     struct sctstr sect;
1043     struct nstr_item ni;
1044     int petrol;
1045     struct plist *ip;
1046
1047     emp_initque(list);
1048     snxtitem_all(&ni, EF_PLANE);
1049     while (nxtitem(&ni, (s_char *)&plane)) {
1050         if (plane.pln_own != own)
1051             continue;
1052         pcp = &plchr[(int)plane.pln_type];
1053         if ((pcp->pl_flags & P_F) == 0)
1054             continue;
1055         if (plane.pln_mission != 0)
1056             continue;
1057         if (plane.pln_mobil <= 0)
1058             continue;
1059         if (plane.pln_effic < 40)
1060             continue;
1061         if (plane.pln_ship >= 0) {
1062             if (!can_fly(plane.pln_uid))
1063                 continue;
1064             getship(plane.pln_ship, &ship);
1065             petrol = ship.shp_item[I_PETROL];
1066         } else if (plane.pln_land >= 0) {
1067             if (!can_fly(plane.pln_uid))
1068                 continue;
1069             getland(plane.pln_land, &land);
1070             petrol = land.lnd_item[I_PETROL];
1071         } else {
1072             getsect(plane.pln_x, plane.pln_y, &sect);
1073             petrol = sect.sct_item[I_PETROL];
1074             if ((sect.sct_effic < 60 || sect.sct_type != SCT_AIRPT)
1075                 && (pcp->pl_flags & P_V) == 0)
1076                 continue;
1077         }
1078         if ((float)petrol < (float)pcp->pl_fuel / 2.0)
1079             continue;
1080         /* Finally, is it in the list of planes already in
1081            flight? */
1082         if (ac_isflying(&plane, a))
1083             continue;
1084         if (ac_isflying(&plane, b))
1085             continue;
1086         if (ac_isflying(&plane, c))
1087             continue;
1088         if (ac_isflying(&plane, d))
1089             continue;
1090         /* got one! */
1091         ip = (struct plist *)malloc(sizeof(*ip));
1092         ip->state = P_OK;
1093         ip->bombs = 0;
1094         ip->misc = 0;
1095         ip->pcp = &plchr[(int)plane.pln_type];
1096         ip->plane = plane;
1097         emp_insque(&ip->queue, list);
1098     }
1099 }
1100
1101
1102
1103 int
1104 can_fly(int p)
1105 {                               /* Can this plane fly from the ship or land unit it is on? */
1106     struct plnstr plane;
1107     struct shpstr ship;
1108     struct lndstr land;
1109     struct plchrstr *pcp;
1110     struct mchrstr *scp;
1111     struct lchrstr *lcp;
1112
1113     getplane(p, &plane);
1114     pcp = &plchr[(int)plane.pln_type];
1115
1116     if (plane.pln_ship >= 0) {
1117         if (!(pcp->pl_flags & P_L) && !(pcp->pl_flags & P_M)
1118             && !(pcp->pl_flags & P_K)
1119             && !(pcp->pl_flags & P_E)
1120             )
1121             return 0;
1122
1123         getship(plane.pln_ship, &ship);
1124         scp = &mchr[(int)ship.shp_type];
1125
1126         if ((pcp->pl_flags & P_L) && (scp->m_flags & M_FLY)) {
1127             return 1;
1128         }
1129
1130         if ((pcp->pl_flags & P_M) && (scp->m_flags & M_MSL)) {
1131             return 1;
1132         }
1133
1134         if ((pcp->pl_flags & P_K) && (scp->m_flags & M_CHOPPER)) {
1135             return 1;
1136         }
1137
1138         if ((pcp->pl_flags & P_E) && (scp->m_flags & M_XLIGHT)) {
1139             return 1;
1140         }
1141     }
1142
1143     if (plane.pln_land >= 0) {
1144         if (!(pcp->pl_flags & P_E))
1145             return 0;
1146
1147         getland(plane.pln_land, &land);
1148         lcp = &lchr[(int)land.lnd_type];
1149
1150         if ((pcp->pl_flags & P_E) && (lcp->l_flags & L_XLIGHT)) {
1151             return 1;
1152         }
1153     }
1154
1155     return 0;
1156 }
1157
1158 int
1159 do_evade(struct emp_qelem *bomb_list, struct emp_qelem *esc_list)
1160 {
1161     struct emp_qelem *qp;
1162     double evade;
1163     struct plist *plp;
1164
1165     evade = 100.0;
1166     for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw) {
1167         plp = (struct plist *)qp;
1168         if (evade > ((float)plp->pcp->pl_stealth / 100.0))
1169             evade = (plp->pcp->pl_stealth / 100.0);
1170     }
1171     for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw) {
1172         plp = (struct plist *)qp;
1173         if (evade > plp->pcp->pl_stealth / 100.0)
1174             evade = (plp->pcp->pl_stealth / 100.0);
1175     }
1176
1177     if (chance(evade))
1178         return 1;
1179
1180     return 0;
1181 }