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