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