]> git.pond.sub.org Git - empserver/blob - src/lib/subs/mission.c
c92f9cdbc9e8e6ad59f5e8c0451fa36a36c9aff9
[empserver] / src / lib / subs / mission.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  *  mission.c: Mission subroutines for planes/ships/units
29  * 
30  *  Known contributors to this file:
31  *     Ken Stevens, 1995
32  *     Steve McClure, 1996-2000
33  */
34
35 #include "options.h"
36 #include "misc.h"
37 #include "player.h"
38 #include "var.h"
39 #include "xy.h"
40 #include "sect.h"
41 #include "retreat.h"
42 #include "ship.h"
43 #include "land.h"
44 #include "plane.h"
45 #include "nat.h"
46 #include "nsc.h"
47 #include "deity.h"
48 #include "file.h"
49 #include "path.h"
50 #include "mission.h"
51 #include "genitem.h"
52 #include "nuke.h"
53 #include "news.h"
54 #include "item.h"
55 #include <fcntl.h>
56 #include "damage.h"
57 #include "prototypes.h"
58 #include "optlist.h"
59
60 struct airport{
61         struct  emp_qelem queue;
62         coord   x,y;
63         natid   own;
64 };
65
66 s_char  *mission_name(short int mission);
67 s_char  *nameofitem(s_char *buf, struct genitem *gp, int type);
68
69 /*
70  * Interdict commodities & transported planes
71  */
72 int
73 ground_interdict(coord x, coord y, natid victim, s_char *s)
74 {
75         register int cn;
76         int     dam=0, newdam, rel;
77         struct  genlist mi[MAXNOC];
78         int     z;
79
80         bzero((s_char *)mi,sizeof(mi));
81         for(z=1;z<MAXNOC;z++)
82                 emp_initque((struct emp_qelem *)&mi[z]);
83
84         build_mission_list(mi,x,y,MI_INTERDICT,victim);
85
86         for(cn=1;cn<MAXNOC;cn++){
87                 rel = getrel(getnatp(cn),victim);
88                 if (rel > HOSTILE)
89                         continue;
90
91                 if (QEMPTY(&mi[cn].queue))
92                         continue;
93
94                 newdam = perform_mission(x,y,victim,&mi[cn].queue,
95                         MI_INTERDICT,s,SECT_HARDTARGET);
96                 dam += newdam;
97                 if (newdam)
98                         mpr(victim, "%s interdiction mission does %d damage!\n",
99                                 cname(cn), newdam);
100         }
101         if (dam) {
102                 collateral_damage(x,y,dam, 0);
103         }               
104         return dam;
105 }
106
107 int
108 collateral_damage(coord x, coord y, int dam, struct emp_qelem *list)
109 {
110         extern double collateral_dam;
111         int     coll;
112         struct  sctstr sect;
113
114         if (!dam)
115                 return 0;
116
117         getsect(x,y,&sect);
118         if (sect.sct_own) {
119                 coll = ldround((double)dam * collateral_dam, 1);
120                 if (coll == 0)
121                         return 0;
122                 mpr(sect.sct_own, "%s takes %d%% collateral damage\n",
123                     xyas(x,y,sect.sct_own), coll);
124                 sectdamage(&sect, coll, list);
125                 putsect(&sect);
126                 return coll;
127         }
128         return 0;
129 }
130
131 int
132 only_subs(struct emp_qelem *list)
133 {
134     struct emp_qelem *qp;
135     struct genlist *glp;
136     struct genitem *gp;
137     struct mchrstr *mcp;
138
139     for(qp = list->q_forw; qp != list; qp = qp->q_forw) {
140         glp = (struct genlist *)qp;
141         gp = (struct genitem *)glp->thing;
142
143         if (glp->type != EF_SHIP)
144             return 0;
145         mcp = (struct mchrstr *)glp->cp;
146         if (!(mcp->m_flags & M_SUB))
147             return 0;
148         /* It's a sub! */
149     }
150     /* They were all subs! */
151     return 1;
152 }
153
154
155 /*
156  *  Interdict ships & land units
157  *
158  */
159 int
160 unit_interdict(coord x, coord y, natid victim, s_char *s, int hardtarget, int mission)
161 {
162         register int cn;
163         int     dam=0, newdam;
164         struct  genlist mi[MAXNOC];
165         int     z;
166         int     osubs;
167
168         bzero((s_char *)mi,sizeof(mi));
169         for(z=1;z<MAXNOC;z++)
170                 emp_initque((struct emp_qelem *)&mi[z]);
171
172         build_mission_list(mi,x,y,mission,victim);
173
174         for(cn=1;cn<MAXNOC;cn++){
175                 if (cn == victim)
176                         continue;
177                 if (mission == MI_SINTERDICT) {
178                         if (getrel(getnatp(cn),victim) >= FRIENDLY)
179                                 continue;
180                 } else if (getrel(getnatp(cn),victim) > HOSTILE)
181                         continue;
182
183                 if (QEMPTY(&mi[cn].queue))
184                         continue;
185
186                 osubs = only_subs(&mi[cn].queue);
187                 newdam = perform_mission(x,y,victim,&mi[cn].queue,
188                         mission,s,hardtarget);
189                 dam += newdam;
190                 if (newdam) {
191                     /* If only subs responded, then we don't know who's
192                        subs they are */
193                     if (osubs) {
194                         mpr(victim,
195                             "Enemy interdiction mission does %d damage!\n",
196                             newdam);
197                     } else {
198                         mpr(victim,
199                             "%s interdiction mission does %d damage!\n",
200                             cname(cn), newdam);
201                     }
202                 }
203         }
204         if (dam) {
205                 collateral_damage(x,y,dam, 0);
206         }               
207         return dam;
208 }
209
210 /*
211  *  Perform a mission against victim, on behalf of actee
212  */
213 int
214 off_support(coord x, coord y, natid victim, natid actee)
215 {
216         int     dam=0;
217         struct  genlist mi[MAXNOC];
218         int     z;
219
220         bzero((s_char *)mi,sizeof(mi));
221         for(z=1;z<MAXNOC;z++)
222                 emp_initque((struct emp_qelem *)&mi[z]);
223
224         build_mission_list(mi,x,y,MI_SUPPORT,victim);
225         build_mission_list(mi,x,y,MI_OSUPPORT,victim);
226
227         dam = dosupport(mi,x,y,victim,actee);
228         return dam;
229 }
230
231 /*
232  *  Perform a mission against victim, on behalf of actee
233  */
234 int
235 def_support(coord x, coord y, natid victim, natid actee)
236 {
237         int     dam=0;
238         struct  genlist mi[MAXNOC];
239         int     z;
240
241         bzero((s_char *)mi,sizeof(mi));
242         for(z=1;z<MAXNOC;z++)
243                 emp_initque((struct emp_qelem *)&mi[z]);
244
245         build_mission_list(mi,x,y,MI_SUPPORT,victim);
246         build_mission_list(mi,x,y,MI_DSUPPORT,victim);
247
248         dam = dosupport(mi,x,y,victim,actee);
249         return dam;
250 }
251
252 int
253 dosupport(struct genlist *mi, coord x, coord y, natid victim, natid actee)
254 {
255         register int cn;
256         int     rel;
257         int     dam=0;
258
259         for(cn=1;cn<MAXNOC;cn++){
260                 rel = getrel(getnatp(cn),actee);
261                 if ((cn != actee) && (rel != ALLIED))
262                         continue;
263                 rel = getrel(getnatp(cn),victim);
264                 if ((cn != actee) && (rel != AT_WAR))
265                         continue;
266
267                 if (QEMPTY(&mi[cn].queue))
268                         continue;
269
270                 dam += perform_mission(x,y,victim,&mi[cn].queue,MI_SUPPORT,
271                         "", SECT_HARDTARGET);
272         }
273         return dam;
274 }
275
276 void
277 build_mission_list(struct genlist *mi, coord x, coord y, int mission, natid victim)
278 {
279         build_mission_list_type(mi,x,y,mission,EF_LAND,victim);
280         build_mission_list_type(mi,x,y,mission,EF_SHIP,victim);
281         build_mission_list_type(mi,x,y,mission,EF_PLANE,victim);
282 }
283
284 void
285 build_mission_list_type(struct genlist *mi, coord x, coord y, int mission, int type, natid victim)
286 {
287         struct  nstr_item ni;
288         struct  genlist *glp;
289         struct  genitem *gp;
290         union {
291           struct shpstr u_sp;
292           struct lndstr u_lp;
293           struct plnstr u_pp;
294         } u_block;
295         s_char  *block;
296         int     dist, size;
297         int     radius;
298         int     relat;
299         struct  sctstr sect;
300
301 /*
302         size = max(sizeof(struct shpstr),sizeof(struct lndstr));
303         size = max(size,sizeof(struct plnstr));
304         block = (s_char *)malloc(size);
305  */
306         size = sizeof(u_block);
307         block = (s_char *)&u_block;
308
309         snxtitem_all(&ni,type);
310         while(nxtitem(&ni, block)){
311                 gp = (struct genitem *)block;
312
313                 if (gp->own == 0)
314                         continue;
315
316                 if (gp->mobil < 1)
317                         continue;
318
319                 if ((gp->mission != mission) && (mission != MI_SINTERDICT))
320                         continue;
321
322                 if ((gp->mission != mission) && (mission == MI_SINTERDICT) &&
323                         (gp->mission != MI_INTERDICT))
324                         continue;
325
326                 relat = getrel(getnatp(gp->own), victim);
327                 if (mission == MI_SINTERDICT) {
328                   if (relat >= FRIENDLY)
329                     continue;
330                   else if (type != EF_PLANE && relat > HOSTILE)
331                     continue;
332                 } else if (relat > HOSTILE)
333                   continue;
334
335                 dist = mapdist(x, y, gp->opx, gp->opy);
336
337                 radius = gp->radius;
338                 if (mission != MI_RESERVE) /* XXX */
339                         oprange(gp,type,&radius);
340
341                 if (dist > radius)
342                         continue;
343
344                 /* Ok, it is within the operations range. */
345                 /* Now check from where the object actually is */
346                 dist = mapdist(x, y, gp->x, gp->y);
347                 radius = 999;
348                 oprange(gp, type, &radius);
349                 if (dist > radius)
350                     continue;
351                 /* Ok, the object can get to where the x,y is */
352
353                 if (opt_SLOW_WAR) {
354                   if (mission != MI_AIR_DEFENSE) {
355                         getsect(x,y,(s_char *)&sect);
356                         if (getrel(getnatp(gp->own), sect.sct_own) > AT_WAR) {
357
358                         /*
359                          * If the player->owner of the unit isn't at war
360                          * with the victim, and doesn't own the
361                          * sect being acted upon, and isn't the
362                          * old player->owner of that sect, bounce them.
363                          */
364                                 if (sect.sct_type != SCT_WATER &&
365                                     sect.sct_own != gp->own &&
366                                     sect.sct_oldown != gp->own)
367                                         continue;
368                         }
369                   }
370                 }
371
372                 glp = (struct genlist *) malloc(sizeof(struct genlist));
373                 bzero((s_char *)glp,sizeof(struct genlist));
374                 glp->x = gp->x;
375                 glp->y = gp->y;
376                 glp->type = type;
377                 switch(type){
378                         case EF_LAND:   glp->cp = (s_char *)&lchr[(int)gp->type];
379                                         break;
380                         case EF_SHIP:   glp->cp = (s_char *)&mchr[(int)gp->type];
381                                         break;
382                         case EF_PLANE:  glp->cp = (s_char *)&plchr[(int)gp->type];
383                                         break;
384                 }
385                 glp->thing = (s_char *)malloc(size);
386                 bcopy(block,glp->thing,size);
387                 emp_insque(&glp->queue, &mi[gp->own].queue);
388         }
389 }
390
391 void
392 find_escorts(coord x, coord y, natid cn, struct emp_qelem *escorts)
393 {
394         struct  nstr_item ni;
395         struct  plist *plp;
396         struct  plnstr plane;
397         int     dist;
398
399         snxtitem_all(&ni,EF_PLANE);
400         while(nxtitem(&ni, (s_char *)&plane)){
401                 if (plane.pln_own != cn)
402                         continue;
403
404                 if (plane.pln_mission != MI_ESCORT)
405                         continue;
406
407                 dist = mapdist(x,y,plane.pln_x,plane.pln_y);
408
409                 if (dist > ((int)((float)plane.pln_range/2.0)) )
410                         continue;
411
412                 plp = (struct plist *) malloc(sizeof(struct plist));
413                 bzero((s_char *)plp,sizeof(struct plist));
414                 plp->pcp = &plchr[(int)plane.pln_type];
415                 bcopy((s_char *)&plane,(s_char *)&plp->plane,sizeof(struct plnstr));
416                 emp_insque(&plp->queue, escorts);
417         }
418 }
419
420 int
421 perform_mission(coord x, coord y, natid victim, struct emp_qelem *list, int mission, s_char *s, int hardtarget)
422 {
423         extern  int land_max_interdiction_range;
424         extern  int ship_max_interdiction_range;
425         s_char  *mission_name(short int mission), *nameofitem(s_char *buf, struct genitem *gp, int type);
426         struct  emp_qelem *qp, missiles, bombers, escorts, airp, b, e;
427         struct  emp_qelem *newqp;
428         struct  genlist *glp;
429         struct  plist *plp;
430         struct  genitem *gp;
431         struct  lndstr *lp;
432         struct  shpstr *sp;
433         struct  sctstr sect;
434         struct  lchrstr *lcp;
435         struct  mchrstr *mcp;
436         struct  plchrstr *pcp;
437         int     dam=0,dam2, mission_flags, tech;
438         natid   plane_owner = 0;
439         int     gun,shell,md, air_dam=0;
440         double  range2, prb, range, mobcost, hitchance;
441         extern  int torpedo_damage;
442         
443         getsect(x,y,&sect);
444         
445         emp_initque(&missiles);
446         emp_initque(&bombers);
447         emp_initque(&escorts);
448         emp_initque(&airp);
449         
450         for(qp = list->q_forw; qp != list; qp = qp->q_forw){
451                 glp = (struct genlist *)qp;
452                 gp = (struct genitem *)glp->thing;
453                 
454                 md = mapdist(x,y,gp->x,gp->y);
455                 
456                 if (glp->type == EF_LAND){
457                         lp = (struct lndstr *)glp->thing;
458                         lcp = (struct lchrstr *)glp->cp;
459                         
460                         if (lp->lnd_effic < LAND_MINFIREEFF)
461                                 continue;
462                         
463                         if (mission == MI_SINTERDICT)
464                                 continue;
465                         
466                         if ((mission == MI_INTERDICT) &&
467                                 (md > land_max_interdiction_range))
468                                 continue;
469                         
470                         if (md > (lp->lnd_frg / 2))
471                                 continue;
472                         
473                         if (lnd_getmil(lp) < 1)
474                                 continue;
475                         
476                         range = techfact((int)lp->lnd_tech,
477                                                          (double)lp->lnd_frg / 2.0);
478                         range2 = (double)roundrange(range);
479                         
480                         if (md > range2)
481                                 continue;
482                         
483                         shell = getvar(V_SHELL, (s_char *)lp, EF_LAND);
484                         gun = getvar(V_GUN, (s_char *)lp, EF_LAND);
485                         if (shell == 0 || gun == 0)
486                                 continue;
487
488                         if (has_supply(lp)){
489                                 use_supply(lp);
490                                 putland(lp->lnd_uid, lp);
491                                 dam2 = ldround(landunitgun(lp->lnd_effic, lp->lnd_dam, gun,
492                                                                                    lp->lnd_ammo, shell),1);
493                                 if (sect.sct_type == SCT_WATER){
494                                         double dam3 = (double)dam2;
495                                         if (chance(((double)lp->lnd_acc)/100.0))
496                                                 dam2=ldround((dam3/2.0),1);
497                                 }
498                                 dam += dam2;
499                                 if (sect.sct_type == SCT_WATER)
500                                         nreport(lp->lnd_own,N_SHP_SHELL,victim,1);
501                                 else
502                                         nreport(lp->lnd_own,N_SCT_SHELL,victim,1);
503                                 wu(0,lp->lnd_own,
504                                    "%s fires at %s %s at %s\n",
505                                    prland(lp), cname(victim),
506                                    s, xyas(x,y,lp->lnd_own));
507                                 
508                                 mpr(victim, "%s %s fires at you at %s\n",
509                                     cname(lp->lnd_own), prland(lp),
510                                     xyas(x,y,victim));
511                         }
512                 }else if (glp->type == EF_SHIP){
513                         sp = (struct shpstr *)glp->thing;
514                         mcp = (struct mchrstr *)glp->cp;
515                         
516                         if (sp->shp_effic < 60)
517                                 continue;
518                         if (sp->shp_frnge == 0)
519                                 continue;
520                         if (((mission == MI_INTERDICT) ||
521                                  (mission == MI_SINTERDICT)) &&
522                                 (md > ship_max_interdiction_range))
523                                 continue;
524                         if (getvar(V_MILIT, (s_char *)sp, EF_SHIP) < 1)
525                                 continue;
526 /*
527   if ((mcp->m_flags & M_SUB) &&
528   (sect.sct_type != SCT_WATER))
529   continue;
530 */
531                         if (mission == MI_SINTERDICT){
532                                 if (!(mcp->m_flags & M_SONAR))
533                                         continue;
534                                 if (!(mcp->m_flags & M_DCH) &&
535                                         !(mcp->m_flags & M_SUBT))
536                                         continue;
537                                 range2 = techfact(sp->shp_tech,
538                                                                   (double)mcp->m_vrnge);
539                                 range2 *= (double)sp->shp_effic / 200.0;
540                                 if (md > range2)
541                                         continue;
542                                 /* can't look all the time */
543                                 if (chance(0.5))
544                                         continue;
545                         }
546                         if (mcp->m_flags & M_SUB){
547 /* If we aren't shooting at "subs" or "ships" don't fire at all from
548    a sub. */
549                                 if (*s != 's')
550                                         continue;
551                                 if (sp->shp_mobil < (s_char)0)
552                                         continue;
553                                 gun = getvar(V_GUN,(s_char *)sp,EF_SHIP);
554                                 if (gun < 1)
555                                         continue;
556                                 shell = getvar(V_SHELL,(s_char *)sp,EF_SHIP);
557                                 if (shell < 3)
558                                         shell += supply_commod(sp->shp_own,
559                                                                                    sp->shp_x,sp->shp_y,I_SHELL,
560                                                                                    3-shell);
561                                 if (shell < 3)
562                                         continue;
563                                 
564                                 range = sp->shp_effic* techfact(sp->shp_tech,
565                                                                                                 ((double)sp->shp_frnge)) / 100.0;
566                                 
567                                 range2 = (double)roundrange(range);
568                                 if (md > range)
569                                         continue;
570                                 
571                                 if (!line_of_sight((s_char **)0, x, y,
572                                                                    gp->x, gp->y))
573                                     continue;
574                                 putvar(V_SHELL,shell-3,(s_char *)sp,EF_SHIP);
575                                 mobcost = sp->shp_effic * 0.01 * sp->shp_speed;
576                                 mobcost = (480.0/(mobcost +
577                                                                   techfact(sp->shp_tech, mobcost)));
578                                 sp->shp_mobil -= mobcost;
579                                 putship(sp->shp_uid,sp);
580                                 hitchance = DTORP_HITCHANCE(md, sp->shp_visib);
581                                 
582                                 wu(0,sp->shp_own,
583                                    "%s locking on %s %s in %s\n",
584                                    prship(sp),cname(victim),s,
585                                    xyas(x,y,sp->shp_own));
586                                 wu(0,sp->shp_own,
587                                    "\tEffective torpedo range is %.1f\n", range);
588                                 wu(0,sp->shp_own,
589                                    "\tWhooosh... Hitchance = %d%%\n",
590                                    (int)(hitchance*100));
591                                 
592                                 if (hitchance < 1.0 && !chance(hitchance)) {
593                                         wu(0,sp->shp_own,
594                                            "\tMissed\n");
595                                         mpr(victim, "Incoming torpedo sighted @ %s missed (whew)!\n",
596                                             xyas(x,y,victim));
597                                         continue;
598                                 }
599                                 wu(0,sp->shp_own,
600                                    "\tBOOM!...\n");
601                                 dam2 = TORP_DAMAGE();
602                                 
603                                 dam += dam2;
604                                 nreport(victim,N_TORP_SHIP,sp->shp_own,1);
605                                 wu(0,sp->shp_own,
606                                    "\tTorpedo hit %s %s for %d damage\n",
607                                    cname(victim),s,dam2);
608                                 
609                                 mpr(victim,
610                                     "Incoming torpedo sighted @ %s hits and does %d damage!\n",
611                                     xyas(x,y,victim), dam2);
612                         }else{
613                                 range = techfact(sp->shp_tech,
614                                                                  (double)mcp->m_frnge/2.0);
615                                 range2 = (double)roundrange(range);
616                                 if (md > range2)
617                                         continue;
618                                 gun = getvar(V_GUN, (s_char *)sp, EF_SHIP);
619                                 gun = min(gun, sp->shp_glim);
620                                 shell = getvar(V_SHELL, (s_char *)sp, EF_SHIP);
621                                 if (shell < gun)
622                                         shell += supply_commod(sp->shp_own,
623                                                                                    sp->shp_x,sp->shp_y,I_SHELL,
624                                                                                    gun-shell);
625                                 gun = min(gun, shell);
626                                 gun = min(gun,(int)((float)getvar(V_MILIT,
627                                                                                                   (s_char *)sp,EF_SHIP)/2.0));
628                                 if (gun ==0)
629                                         continue;
630                                 gun = max(gun,1);
631                                 dam2 = seagun(sp->shp_effic,gun);
632                                 if ( range2 == 0.0 )
633                                         prb = 1.0;
634                                 else
635                                         prb = ((double)md) / range2;
636                                 prb *= prb;
637                                 if (chance(prb))
638                                         dam2 = (int)((float)dam2/2.0);
639                                 dam += dam2;
640                                 if (sect.sct_type == SCT_WATER)
641                                         nreport(sp->shp_own,N_SHP_SHELL,victim,1);
642                                 else
643                                         nreport(sp->shp_own,N_SCT_SHELL,victim,1);
644                                 wu(0,sp->shp_own,
645                                    "%s fires at %s %s at %s\n",
646                                    prship(sp),
647                                    cname(victim), s,
648                                    xyas(x,y,sp->shp_own));
649                                 
650                                 mpr(victim, "%s fires at you at %s\n",
651                                         cname(sp->shp_own), prship(sp),
652                                         xyas(x,y,victim));
653                                 
654                                 putvar(V_SHELL,shell-gun,(s_char *)sp, EF_SHIP);
655                                 putship(sp->shp_uid,sp);
656                         }
657                 }else if (glp->type == EF_PLANE) {
658                         pcp = (struct plchrstr *)glp->cp;
659                         if (pcp->pl_flags & P_M)
660                                 /* units have their own missile interdiction */
661                                 if (hardtarget != SECT_HARDTARGET ||
662                                     pcp->pl_flags & P_MAR)
663                                         continue;
664                         
665                         /* save planes for later */
666                         plp = (struct plist *)malloc(sizeof(struct plist));
667                         
668                         bzero((s_char *)plp,sizeof(struct plist));
669                         plp->pcp = pcp;
670                         bcopy(glp->thing, (s_char *)&plp->plane,
671                                   sizeof(struct plnstr));
672                         if (plp->pcp->pl_flags & P_M)
673                                 emp_insque(&plp->queue, &missiles);
674                         else
675                                 emp_insque(&plp->queue, &bombers);
676                         plane_owner = plp->plane.pln_own;
677                 }
678         }
679         if (!QEMPTY(&missiles)) {
680                 /* I arbitrarily chose 100 mindam -KHS */
681                 dam += msl_launch_mindam(&missiles, x, y, hardtarget, EF_SECTOR, 100, "sector", victim, mission);
682                 qp=missiles.q_forw;
683                 while(qp != (&missiles)){
684                         qp=qp->q_forw;
685                         free(qp);
686                 }
687         }
688         
689         if (QEMPTY(&bombers)) {
690                 qp=list->q_forw;
691                 while(qp != list){
692                         glp = (struct genlist *)qp;
693                         qp=qp->q_forw;
694                         
695                         free(glp->thing);
696                         free((s_char *)glp);
697                 }
698                 return dam;
699         }
700         /*
701          * If there are planes performing an
702          * interdict or support mission, find
703          * some escorts for them, if possible.
704          * Up to 2 per bomber, if possible.
705          */
706         find_escorts(x,y,plane_owner,&escorts);
707         
708         if (mission == MI_SINTERDICT)
709                 mission_pln_sel(&bombers,P_T|P_A,0,hardtarget);
710         else
711                 mission_pln_sel(&bombers,P_T,P_A,SECT_HARDTARGET);
712         
713         mission_pln_sel(&escorts,P_ESC|P_F,0,SECT_HARDTARGET);
714         
715         for(qp = bombers.q_forw; qp != (&bombers); qp = qp->q_forw){
716                 plp = (struct plist *)qp;
717                 if (!find_airport(&airp,plp->plane.pln_x,plp->plane.pln_y))
718                         add_airport(&airp,plp->plane.pln_x,
719                                                 plp->plane.pln_y);
720         }
721         
722         for(qp = airp.q_forw; qp != (&airp); qp = qp->q_forw){
723                 struct  airport *air;
724                 s_char  pp[512];
725                 
726                 air = (struct airport *)qp;
727                 md = mapdist(x,y,air->x,air->y);
728                 
729                 emp_initque(&b);
730                 emp_initque(&e);
731                 
732                 /* Split off the bombers at this base into b */
733                 divide(&bombers,&b,air->x,air->y);
734                 
735                 /* Split off the escorts at this base into e */
736                 divide(&escorts,&e,air->x,air->y);
737                 
738                 tech = 0;
739                 mission_flags = 0;
740                 mission_flags |= P_X;           /* stealth (shhh) */
741                 mission_flags |= P_H; /* gets turned off if not all choppers */
742                 
743                 mission_flags = mission_pln_arm(&b,air->x,air->y,2*md,'p',0,
744                                                 0,mission_flags,&tech);
745                 
746                 if (QEMPTY(&b)){
747                         continue;
748                 }
749                 
750                 mission_flags = mission_pln_arm(&e,air->x,air->y,2*md,'p',0,
751                                                 P_F|P_ESC,mission_flags,&tech);
752                 
753                 BestAirPath(pp, air->x, air->y, x, y);
754                 wu(0, plane_owner, "Flying %s mission from %s\n",
755                    mission_name(mission), xyas(air->x, air->y, plane_owner));
756                 if (air->own && (air->own != plane_owner)) {
757                     wu(0, air->own, "%s is flying %s mission from %s\n",
758                        cname(plane_owner), mission_name(mission),
759                        xyas(air->x, air->y, air->own));
760                 }
761                 
762                 ac_encounter(&b,&e,air->x,air->y,pp,mission_flags,0, 0, 0);
763                 
764                 if (!QEMPTY(&b))
765                         air_dam += air_damage(&b,x,y,mission,victim,s,hardtarget);
766                 
767                 pln_put(&b);
768                 pln_put(&e);
769         }
770         
771         if (air_dam > 0){
772                 dam += air_dam;
773                 if (sect.sct_type == SCT_WATER)
774                         nreport(plane_owner,N_SHP_BOMB,victim,1);
775                 else
776                         nreport(plane_owner,N_SCT_BOMB,victim,1);
777         }
778         
779         /* free up all this memory */
780         qp=list->q_forw;
781         while(qp != list){
782                 glp = (struct genlist *)qp;
783                 qp=qp->q_forw;
784                 
785                 free(glp->thing);
786                 free((s_char *)glp);
787         }
788         
789         qp = escorts.q_forw;
790         while(qp != (&escorts)){
791                 newqp = qp->q_forw;
792                 emp_remque(qp);
793                 free((s_char *)qp);
794                 qp = newqp;
795         }
796         
797         qp=bombers.q_forw;
798         while(qp != (&bombers)){
799                 newqp = qp->q_forw;
800                 emp_remque(qp);
801                 free((s_char *)qp);
802                 qp = newqp;
803         }
804         
805         return dam;
806 }
807
808 int
809 cando(int mission, int type)
810 {
811         switch (mission){
812         case MI_ESCORT:
813                 if (type == EF_PLANE)
814                         return 1;
815                 return 0;
816         case MI_AIR_DEFENSE:
817                 if (type == EF_PLANE)
818                         return 1;
819                 return 0;
820         case MI_SINTERDICT:
821                 if ((type == EF_PLANE) || (type == EF_SHIP))
822                         return 1;
823                 return 0;
824         case MI_INTERDICT:
825                 return 1;
826         case MI_SUPPORT:
827         case MI_OSUPPORT:
828         case MI_DSUPPORT:
829                 if (type == EF_PLANE)
830                         return 1;
831                 return 0;
832         case MI_RESERVE:
833                 if (type == EF_LAND)
834                         return 1;
835                 return 0;
836         }
837         
838         return 0;
839 }
840
841 s_char *nameofitem(s_char *buf, struct genitem *gp, int type)
842 {
843         switch(type){
844         case EF_SHIP:
845                 return prship((struct shpstr *)gp);
846                 break;
847         case EF_PLANE:
848                 return prplane((struct plnstr *)gp);
849                 break;
850         case EF_LAND:
851                 return prland((struct lndstr *)gp);
852         }
853         return NULL;
854 }
855
856 s_char *mission_short_name(int mission)
857 {
858         switch (mission){
859                 case MI_INTERDICT:      return "interdict";
860                 case MI_SUPPORT:        return "support  ";
861                 case MI_OSUPPORT:       return "offensive";
862                 case MI_DSUPPORT:       return "defensive";
863                 case MI_RESERVE:        return "reserve  ";
864                 case MI_ESCORT:         return "escort   ";
865                 case MI_SINTERDICT:     return "interdict";
866                 case MI_AIR_DEFENSE:    return "air def  ";
867                 default:                return "         ";
868         }
869 }
870
871 s_char *mission_name(short int mission)
872 {
873         switch (mission){
874                 case MI_INTERDICT:      return "an interdiction";
875                 case MI_SUPPORT:        return "a support";
876                 case MI_OSUPPORT:       return "a offensive support";
877                 case MI_DSUPPORT:       return "a defensive support";
878                 case MI_RESERVE:        return "a reserve";
879                 case MI_ESCORT:         return "an escort";
880                 case MI_SINTERDICT:     return "a sub interdiction";
881                 case MI_AIR_DEFENSE:    return "an air defense";
882         }
883         return "a mysterious";
884 }
885
886 void
887 show_mission(int type, struct nstr_item *np)
888 {
889         int     size, first=1, radius;
890         s_char  *block;
891         struct  genitem *gp;
892         s_char  buf[128];
893
894         size = max(sizeof(struct lndstr),sizeof(struct plnstr));
895         size = max(size,sizeof(struct shpstr));
896         block = (s_char *)malloc(size);
897
898         while (nxtitem(np, block)) {
899                 gp = (struct genitem *)block;
900                 if (!player->owner || gp->own == 0)
901                         continue;
902
903                 if (first){
904                         pr("Thing                         x,y   op-sect rad mission\n");
905                         first=0;
906                 }
907                 pr("%-25s", nameofitem(buf, gp,type));
908                 pr(" %7s", xyas(gp->x,gp->y,player->cnum));
909                 if (gp->mission == MI_INTERDICT || gp->mission == MI_SUPPORT ||
910                         gp->mission == MI_OSUPPORT ||
911                         gp->mission == MI_DSUPPORT ||
912                         gp->mission == MI_AIR_DEFENSE){
913                         radius = 999;
914                         oprange(gp,type,&radius);
915                         pr(" %7s", xyas(gp->opx,gp->opy,player->cnum));
916                         if (radius < gp->radius)
917                                 pr("  %4d", radius);
918                         else
919                                 pr("  %4d", gp->radius);
920                 }else if (gp->mission == MI_RESERVE){
921                         struct  sctstr sect;
922                         int     plus=2;
923
924                         getsect(gp->x,gp->y,&sect);
925                         if ((sect.sct_type == SCT_HEADQ) &&
926                                 (sect.sct_effic >= 60))
927                                 plus++;
928
929                         if (((struct lndstr *)block)->lnd_rad_max == 0)
930                           plus = 0;
931                         else
932                           plus += ((struct lndstr *)block)->lnd_rad_max;
933                         pr(" %7s", xyas(gp->x,gp->y,player->cnum));
934                         pr("  %4d", plus);
935                 }else if (gp->mission == MI_ESCORT){
936                         pr("        ");
937                         pr("  %4d", (int)
938                                 ((float)((struct plnstr *)block)->pln_range/2.0)
939                                 );
940                 }else
941                         pr("              ");
942                 if (gp->mission)
943                         pr(" is on %s mission\n",
944                                 mission_name(gp->mission));
945                 else
946                         pr(" has no mission.\n");
947         }
948 }
949
950 int
951 oprange(struct genitem *gp, int type, int *radius)
952 {
953         double  techfact(int, double);
954         int     range;
955         struct  shpstr ship;
956         struct  lndstr land;
957         struct  plnstr plane;
958
959         switch (type){
960                 case EF_SHIP:
961                         getship(gp->uid,&ship);
962                         range = ldround(techfact(gp->tech,
963                                 (double)ship.shp_frnge / 2.0),1);
964                         break;
965                 case EF_LAND:
966                         getland(gp->uid,&land);
967                         range = ldround(techfact((int)land.lnd_tech,
968                                                                                          (double)land.lnd_frg / 2.0),1);
969                         break;
970                 case EF_PLANE:
971                         getplane(gp->uid,&plane);
972                         /* missiles go one way, so we can use all the range */
973                         if (plchr[(int)plane.pln_type].pl_flags & P_M)
974                                 range = plane.pln_range;
975                         else
976                                 range = ldround((double)plane.pln_range/2.0,1);;
977                         break;
978         }
979
980         if ((*radius) > range)
981                 *radius = range;
982
983         return range;
984 }
985
986 /*
987  *  Remove all planes who cannot go on
988  *  the mission from the plane list.
989  */
990 void
991 mission_pln_sel(struct emp_qelem *list, int wantflags, int nowantflags, int hardtarget)
992 {
993         struct  emp_qelem *qp, *next;
994         struct  plnstr *pp;
995         struct  shpstr ship;
996         struct  lndstr land;
997         struct  sctstr sect;
998         struct  plchrstr *pcp;
999         struct  plist *plp;
1000         register int   y, bad, bad1;
1001         unsigned int x;
1002
1003         for (qp = list->q_forw; qp != list; qp = next) {
1004                 next = qp->q_forw;
1005                 plp = (struct plist *)qp;
1006                 pp = &plp->plane;
1007                 pcp = plp->pcp;
1008
1009                 if (pp->pln_effic < 40){
1010                         emp_remque(qp);
1011                         free((s_char *)qp);
1012                         continue;
1013                 }
1014
1015                 if (pp->pln_mobil < 1) {
1016                     emp_remque(qp);
1017                     free((s_char *)qp);
1018                     continue;
1019                 }
1020
1021                 if (opt_MARKET) {
1022                     if (ontradingblock(EF_PLANE, (int *)pp)) {
1023                         emp_remque(qp);
1024                         free((s_char *)qp);
1025                         continue;
1026                     }
1027                 }
1028
1029                 bad=0;
1030                 bad1=0;
1031                 if (wantflags) {
1032                         for(x=0;x<sizeof(wantflags)*8;x++){
1033                                 y=(1<<x);
1034                                 if ((wantflags & y) == y)
1035                                         if ((pcp->pl_flags & y) != y){
1036                                                 switch(y){
1037                                                         case P_F:
1038                                                         case P_ESC: bad1=2;
1039                                                                     break;
1040                                                         case P_E:
1041                                                         case P_L:
1042                                                         case P_K: bad1=1;
1043                                                                   break;
1044                                                         default:  bad=1;
1045                                                 }
1046                                         }
1047                         }
1048                         if (bad){
1049                                 emp_remque(qp);
1050                                 free((s_char *)qp);
1051                                 continue;
1052                         }
1053                         if (bad1 == 2){
1054                                 if ((pcp->pl_flags &  P_ESC) ||
1055                                         (pcp->pl_flags & P_F))
1056                                         bad1=0;
1057                         }
1058                         if (bad1 == 1){
1059                                 if ((pcp->pl_flags &  P_E) ||
1060                                         (pcp->pl_flags & P_K) ||
1061                                         (pcp->pl_flags & P_L))
1062                                         bad1=0;
1063                         }
1064                         if (bad1){
1065                                 emp_remque(qp);
1066                                 free((s_char *)qp);
1067                                 continue;
1068                         }
1069                 }
1070                 bad=0;
1071                 bad1=0;
1072                 if (nowantflags) {
1073                         for(x=0;x<sizeof(nowantflags)*8;x++){
1074                                 y=(1<<x);
1075                                 if ((nowantflags & y) == y)
1076                                         if ((pcp->pl_flags & y) == y)
1077                                                 bad=1;
1078                         }
1079                         if (bad){
1080                                 emp_remque(qp);
1081                                 free((s_char *)qp);
1082                                 continue;
1083                         }
1084                 }
1085                 if (pp->pln_ship >= 0) {
1086                         if (!getship(pp->pln_ship, &ship)){
1087         shipsunk:
1088                                 pp->pln_effic = 0;
1089                                 putplane(pp->pln_uid, pp);
1090                                 emp_remque(qp);
1091                                 free((s_char *)qp);
1092                                 continue;
1093                         }
1094                         if (!can_be_on_ship(pp->pln_uid,ship.shp_uid)) {
1095                                 goto shipsunk;
1096                         }
1097                         if (ship.shp_effic < SHIP_MINEFF){
1098                                 goto shipsunk;
1099                         }
1100                         /* Can't fly off of ships < 50%, or non-owned or non-allied ships */
1101                         if ((ship.shp_effic < 50) ||
1102                             ((ship.shp_own != pp->pln_own) &&
1103                              (getrel(getnatp(ship.shp_own), pp->pln_own) != ALLIED))) {
1104                                 emp_remque(qp);
1105                                 free((s_char *)qp);
1106                                 continue;
1107                         }
1108                 }
1109                 if (pp->pln_land >= 0) {
1110                         if (!getland(pp->pln_land, &land)){
1111         landdead:
1112                                 pp->pln_effic = 0;
1113                                 putplane(pp->pln_uid, pp);
1114                                 emp_remque(qp);
1115                                 free((s_char *)qp);
1116                                 continue;
1117                         }
1118                         if (!(pcp->pl_flags & P_E))
1119                                 goto landdead;
1120                         if (land.lnd_effic < LAND_MINEFF)
1121                                 goto landdead;
1122
1123                         /* Can't fly off of units < 50%, or non-owned or non-allied units */
1124                         if ((land.lnd_effic < 50) ||
1125                             ((land.lnd_own != pp->pln_own) &&
1126                              (getrel(getnatp(land.lnd_own), pp->pln_own) != ALLIED))) {
1127                                 emp_remque(qp);
1128                                 free((s_char *)qp);
1129                                 continue;
1130                         }
1131
1132                         /* Can't fly off units in ships or other units */
1133                         if ((land.lnd_ship >= 0) || (land.lnd_land >= 0)) {
1134                                 emp_remque(qp);
1135                                 free((s_char *)qp);
1136                                 continue;
1137                         }
1138                 }
1139                 /* Now, check the sector status if not on a plane or unit */
1140                 if ((pp->pln_ship < 0) && (pp->pln_land < 0)) {
1141                     /* If we can't get the sector, we can't check it, and can't fly */
1142                     if (!getsect(pp->pln_x, pp->pln_y, &sect)) {
1143                         emp_remque(qp);
1144                         free((s_char *)qp);
1145                         continue;
1146                     }
1147                     /* First, check allied status */
1148                     /* Can't fly from non-owned sectors or non-allied sectors */
1149                     if ((sect.sct_own != pp->pln_own) &&
1150                         (getrel(getnatp(sect.sct_own), pp->pln_own) != ALLIED)) {
1151                         emp_remque(qp);
1152                         free((s_char *)qp);
1153                         continue;
1154                     }
1155                     /* non-vtol plane */
1156                     if ((pcp->pl_flags & P_V) == 0) {
1157                         if ((sect.sct_type != SCT_AIRPT) || (sect.sct_effic < 40)) {
1158                             emp_remque(qp);
1159                             free((s_char *)qp);
1160                             continue;
1161                         }
1162                     }
1163                 }
1164                 if (pcp->pl_flags & P_A) {
1165                         if (roll(100) > pln_identchance(pp, hardtarget, EF_SHIP)) {
1166                                 emp_remque(qp);
1167                                 free((s_char *)qp);
1168                                 continue;
1169                         }
1170                 }                               
1171
1172                 putplane(pp->pln_uid, pp);
1173         }
1174 }
1175
1176 /*
1177  * Arm only the planes at x,y
1178  *
1179  */
1180 int
1181 mission_pln_arm(struct emp_qelem *list, coord x, coord y, int dist, int mission, struct ichrstr *ip, int flags, int mission_flags, int *tech)
1182 {
1183         struct  emp_qelem *qp;
1184         struct  emp_qelem *next;
1185         struct  plist *plp;
1186
1187         if (*tech == 0)
1188                 *tech = 9999;
1189         for (qp = list->q_forw; qp != list; qp = next) {
1190                 next = qp->q_forw;
1191                 plp = (struct plist *) qp;
1192
1193                 if (plp->plane.pln_x != x)
1194                         continue;
1195                 if (plp->plane.pln_y != y)
1196                         continue;
1197
1198                 if (mission_pln_equip(plp, ip, flags, mission) < 0) {
1199                         emp_remque(qp);
1200                         free((s_char *)qp);
1201                         continue;
1202                 }
1203                 if (flags & (P_S|P_I)) {
1204                         if(plp->pcp->pl_flags & P_S)
1205                                 mission_flags |= P_S;
1206                         if(plp->pcp->pl_flags & P_I)
1207                                 mission_flags |= P_I;
1208                 }
1209                 if (*tech > plp->plane.pln_tech)
1210                         *tech = plp->plane.pln_tech;
1211                 if (!(plp->pcp->pl_flags & P_H))
1212                         /* no stealth on this mission */
1213                         mission_flags &= ~P_H;
1214                 if (!(plp->pcp->pl_flags & P_X))
1215                         /* no stealth on this mission */
1216                         mission_flags &= ~P_X;
1217                 if (!(plp->pcp->pl_flags & P_A)) {
1218                         /* no asw on this mission */
1219                         mission_flags &= ~P_A;
1220                 }
1221                 if (!(plp->pcp->pl_flags & P_MINE)) {
1222                         /* no asw on this mission */
1223                         mission_flags &= ~P_MINE;
1224                 }
1225
1226                 /*
1227                  *      Mob costs for missions are 1/2 normal
1228                  *       Not anymore. :)
1229                  */
1230 /*      plp->plane.pln_mobil -= pln_mobcost(dist,&plp->plane,flags)/2;*/
1231                 plp->plane.pln_mobil -= pln_mobcost(dist,&plp->plane,flags);
1232
1233         }
1234         return mission_flags;
1235 }
1236
1237 int
1238 mission_pln_equip(struct plist *plp, struct ichrstr *ip, int flags, s_char mission)
1239 {
1240         register struct plchrstr *pcp;
1241         struct  plnstr *pp;
1242         int     needed;
1243         struct  lndstr land;
1244         struct  shpstr ship;
1245         struct  sctstr sect;
1246         int     type;
1247         s_char  *ptr;
1248         int     item;
1249         int     rval;
1250         int     vec[I_MAX+1];
1251
1252         pp = &plp->plane;
1253         pcp = plp->pcp;
1254         if (pp->pln_ship >= 0) {
1255                 getship(pp->pln_ship, &ship);
1256                 type = EF_SHIP;
1257                 ptr = (s_char *) &ship;
1258         } else
1259         if (pp->pln_land >= 0) {
1260                 getland(pp->pln_land, &land);
1261                 type = EF_LAND;
1262                 ptr = (s_char *) &land;
1263         } else {
1264                 getsect(pp->pln_x, pp->pln_y, &sect);
1265                 type = EF_SECTOR;
1266                 ptr = (s_char *) &sect;
1267         }
1268         getvec(VT_ITEM, vec, ptr, type);
1269         if (pcp->pl_fuel > vec[I_PETROL]) {
1270                 return -1;
1271         }
1272         vec[I_PETROL] -= pcp->pl_fuel;
1273         rval = 0;
1274         if (!(flags & P_F)) {
1275                 item = 0;
1276                 needed = 0;
1277                 switch (mission) {
1278                 case 's':
1279                 case 'p':
1280                         if (pp->pln_nuketype == -1) {
1281                                 item = I_SHELL;
1282                                 needed = pp->pln_load;
1283                         }
1284                         break;
1285                 case 't':
1286                         if ((pcp->pl_flags & P_C) == 0 || ip == 0)
1287                                 break;
1288                         item = ip - ichr;
1289                         needed = (pp->pln_load * 2) / ip->i_lbs;
1290                         break;
1291                 case 'd':
1292                         if ((pcp->pl_flags & P_C) == 0 || ip == 0)
1293                                 break;
1294                         item = ip - ichr;
1295                         needed = (pp->pln_load * 2) / ip->i_lbs;
1296                         break;
1297                 case 'a':
1298                         if ((pcp->pl_flags & (P_V|P_C)) == 0)
1299                                 break;
1300                         item = I_MILIT;
1301                         needed = pp->pln_load / ip->i_lbs;
1302                         break;
1303                 case 'n':
1304                         if (pp->pln_nuketype == -1)
1305                                 rval = -1;
1306                         break;
1307                 case 'i': /* missile interception */
1308                         if (pp->pln_load) {
1309                                 item = I_SHELL;
1310                                 needed = pp->pln_load;
1311                         }
1312                         break;
1313                 default:
1314                         break;
1315                 }
1316                 if (rval < 0 || (item && needed <= 0)) {
1317                         return -1;
1318                 }
1319                 if ((vec[item] < needed) && (item == I_SHELL))
1320                         vec[item] += supply_commod(plp->plane.pln_own,
1321                                         plp->plane.pln_x,plp->plane.pln_y,
1322                                         I_SHELL,needed);
1323                 if (vec[item] < needed) {
1324                         return -1;
1325                 } else {
1326                         vec[item] -= needed;
1327                 }
1328                 if (item == I_SHELL && (mission == 's' || mission == 'p'))
1329                         plp->bombs = needed;
1330                 else
1331                         plp->misc = needed;
1332         }
1333                 putvec(VT_ITEM, vec, ptr, type);
1334         if (type == EF_SHIP)
1335                 putship(ship.shp_uid,&ship);
1336         else if (type == EF_LAND)
1337                 putland(land.lnd_uid,&land);
1338         else
1339                 putsect(&sect);
1340         return rval;
1341 }
1342
1343 /*
1344  *  Return 1 if this x,y pair is in the list
1345  */
1346 int
1347 find_airport(struct emp_qelem *airp, coord x, coord y)
1348 {
1349         struct  emp_qelem *qp;
1350         struct  airport *a;
1351
1352         for (qp = airp->q_forw; qp != airp; qp = qp->q_forw){
1353                 a = (struct airport *)qp;
1354                 if ((a->x == x) && (a->y == y))
1355                         return 1;
1356         }
1357
1358         return 0;
1359 }
1360
1361 /* #*# This needs to be changed to include acc's -KHS */
1362 void
1363 add_airport(struct emp_qelem *airp, coord x, coord y)
1364 {
1365         struct  airport *a;
1366         struct  sctstr sect;
1367
1368         a = (struct airport *)malloc(sizeof(struct airport));
1369
1370         a->x = x;
1371         a->y = y;
1372         getsect(x,y,&sect);
1373         a->own = sect.sct_own;
1374
1375         emp_insque((struct emp_qelem *)a,airp);
1376 }
1377
1378 /*
1379  *  Take all the planes in list 1 that
1380  *  are at x,y, and put them into list 2.
1381  */
1382 void
1383 divide(struct emp_qelem *l1, struct emp_qelem *l2, coord x, coord y)
1384 {
1385         struct  emp_qelem *qp, *next;
1386         struct  plist *plp;
1387
1388         for (qp = l1->q_forw; qp != l1; qp = next){
1389                 next = qp->q_forw;
1390                 plp = (struct plist *)qp;
1391
1392                 if (plp->plane.pln_x != x)
1393                         continue;
1394                 if (plp->plane.pln_y != y)
1395                         continue;
1396
1397                 emp_remque(qp);
1398                 emp_insque(qp,l2);
1399         }
1400 }
1401
1402 int
1403 air_damage(struct emp_qelem *bombers, coord x, coord y, int mission, natid victim, s_char *s, int hardtarget)
1404 {
1405         struct  emp_qelem *qp;
1406         struct  plist *plp;
1407         struct  plnstr *pp;
1408         int     newdam,dam=0;
1409         int     hitchance;
1410         int     nukedam;
1411         
1412         for(qp = bombers->q_forw; qp != bombers; qp = qp->q_forw){
1413                 plp = (struct plist *)qp;
1414                 pp = &plp->plane;
1415
1416                 if ((mission == MI_SINTERDICT) && !(plp->pcp->pl_flags & P_A))
1417                         continue;
1418
1419                 if (!plp->bombs)
1420                         continue;
1421
1422                 newdam = 0;
1423                 if (plp->pcp->pl_flags & P_A) {
1424                         if (roll(100) >  pln_identchance(pp, hardtarget, EF_SHIP)) {
1425                                 wu(0,pp->pln_own,
1426                                    "\t%s detects sub movement in %s\n",
1427                                    prplane(pp), xyas(x,y,pp->pln_own));
1428                                 continue;
1429                         }
1430                         if (getrel(getnatp(pp->pln_own),victim) > HOSTILE) {
1431                                 wu(0,pp->pln_own,
1432                                    "\t%s tracks %s %s at %s\n",
1433                                    prplane(pp),
1434                                    cname(victim), s, xyas(x,y,pp->pln_own));
1435                                 continue;
1436                         }
1437                         wu(0,pp->pln_own,
1438                            "\t%s depth-charging %s %s in %s\n",
1439                            prplane(pp),
1440                            cname(victim),
1441                            s,
1442                            xyas(x,y,pp->pln_own));
1443                 } else {
1444                         wu(0,pp->pln_own,
1445                            "\t%s pinbombing %s %s in %s\n",
1446                            prplane(pp),
1447                            cname(victim),
1448                            s,
1449                            xyas(x,y,pp->pln_own));
1450                 }               
1451                 hitchance = pln_hitchance(pp, hardtarget, EF_SHIP);
1452                 if (plp->plane.pln_nuketype != -1)
1453                         hitchance = 100;
1454                 else if (hardtarget != SECT_HARDTARGET)
1455                         wu(0,pp->pln_own,"\t\t%d%% hitchance...", hitchance);
1456                 /* Always calculate damage */
1457                 if (roll(100) <= hitchance) {
1458                         newdam = pln_damage(&plp->plane,x,y,'p',&nukedam, 1);
1459                         if (nukedam) {
1460                                 if (mission == MI_INTERDICT) {
1461                                         wu(0,pp->pln_own,
1462                                            "\t\tnuclear warhead does %d damage to %s %s\n",
1463                                            prplane(pp),
1464                                            nukedam, cname(victim), s);
1465                                         dam += nukedam;
1466                                 }
1467                         } else {
1468                                 wu(0,pp->pln_own,
1469                                    "\t\thit %s %s for %d damage\n",
1470                                    cname(victim), s, newdam);
1471                                 dam += newdam;
1472                         }
1473                 } else {
1474                         newdam = pln_damage(&plp->plane,x,y,'p',&nukedam, 0);
1475                         wu(0,pp->pln_own,"missed\n");
1476                         if (mission == MI_SINTERDICT) {
1477                                 mpr(victim,
1478                                     "RUMBLE... your sub in %s hears a depth-charge explode nearby\n",
1479                                     xyas(x,y,victim));
1480                         } else if (*s == 's') {
1481                                 mpr(victim,
1482                                     "SPLASH!  Bombs miss your %s in %s\n",
1483                                     s, xyas(x,y,victim));
1484                         } else {
1485                             mpr(victim, "SPLAT!  Bombs miss your %s in %s\n",
1486                                 s, xyas(x, y, victim));
1487                         }
1488                         /* Now, even though we missed, the bombs
1489                            land somewhere. */
1490                         collateral_damage(x, y, newdam, bombers);
1491                 }
1492                         
1493                 /* use up missiles */
1494                 if (plp->pcp->pl_flags & P_M) {
1495                     makelost(EF_PLANE, pp->pln_own, pp->pln_uid, pp->pln_x, pp->pln_y);
1496                     pp->pln_own = 0;
1497                 }
1498         }
1499
1500         return dam;
1501 }
1502
1503 /*
1504  * Check to see if anyone hostile to the victim
1505  * is running an air defense mission on this
1506  * sector. If so, do air combat
1507  */
1508 int
1509 air_defense(coord x, coord y, natid victim, struct emp_qelem *bomb_list, struct emp_qelem *esc_list)
1510 {
1511     int     dam=0,cn;
1512     int     mission_flags, tech, combat=0, rel, dist, z;
1513     struct  emp_qelem *qp, interceptors, airp, i, empty, *next;
1514     struct  plist *plp;
1515     struct  genlist *glp;
1516     struct  genitem *gp;
1517     struct  genlist mi[MAXNOC];
1518     s_char  path[512];
1519     int     count;
1520     int     tcount;
1521     
1522     count = 0;
1523     for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw)
1524         count++;
1525     for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw)
1526         count++;
1527     
1528     bzero((s_char *)mi,sizeof(mi));
1529     for (z = 1; z < MAXNOC; z++)
1530         emp_initque((struct emp_qelem *)&mi[z]);
1531     
1532     build_mission_list_type(mi,x,y,MI_AIR_DEFENSE,EF_PLANE,victim);
1533     
1534     for (cn = 1; cn < MAXNOC; cn++) {
1535         /* Check our relations */
1536         rel = getrel(getnatp(cn),victim);
1537         
1538         if (rel > HOSTILE)
1539             continue;
1540         
1541         if (QEMPTY(&mi[cn].queue))
1542             continue;
1543         
1544         /* Ok, make a list of all the interceptors.  Note that this *copies* the
1545          * list from the mission creation.  This list must be deleted later. */
1546         emp_initque(&interceptors);
1547         for (qp = mi[cn].queue.q_forw; qp != (&mi[cn].queue); qp = next) {
1548             next = qp->q_forw;
1549
1550             glp = (struct genlist *)qp;
1551             gp = (struct genitem *)glp->thing;
1552             plp = (struct plist *)qp;
1553             
1554             dist = mapdist(x,y,gp->x,gp->y);
1555             
1556             plp = (struct plist *)malloc(sizeof(struct plist));
1557             bzero((s_char *)plp,sizeof(struct plist));
1558             plp->pcp = (struct plchrstr *)glp->cp;
1559             bcopy(glp->thing, (s_char *)&plp->plane,
1560                   sizeof(struct plnstr));
1561             
1562             /* missiles go one way, so we can use all the range */
1563             if (!(plp->pcp->pl_flags & P_M))
1564                 dist *= 2;
1565             /* If it's out of range, free it and continue on */
1566             if (dist > plp->plane.pln_range) {
1567                 free(plp);
1568                 continue;
1569             }
1570             emp_insque(&plp->queue, &interceptors);
1571         }
1572         
1573         /* Remove those who cannot go */
1574         mission_pln_sel(&interceptors,P_F,0,SECT_HARDTARGET);
1575         
1576         if (QEMPTY(&interceptors))
1577             continue;
1578         
1579         /* Now, delete all the extras, but delete the first ones, not the last ones, so
1580          * that the higher numbered planes go into battle (they should be the better ones
1581          * at fighting, if all went well.) */
1582         tcount = 0;
1583         for (qp = interceptors.q_forw; qp != (&interceptors); qp = qp->q_forw) 
1584             tcount++;
1585         tcount -= (count * 2);
1586         /* Just in case there are more incoming than we have */
1587         if (tcount < 0)
1588             tcount = 0;
1589         for (qp = interceptors.q_forw; qp != (&interceptors); qp = next) {
1590             next = qp->q_forw;
1591             if (tcount) {
1592                 tcount--;
1593                 /* Free it up and continue */
1594                 emp_remque(qp);
1595                 glp = (struct genlist *)qp;
1596                 free((s_char *)glp);
1597             }
1598         }
1599
1600         /* Now, make a list of all the airports these planes are coming from */
1601         emp_initque(&airp);
1602         for (qp = interceptors.q_forw ; qp != (&interceptors); qp = qp->q_forw) {
1603             plp = (struct plist *)qp;
1604             if (!find_airport(&airp,plp->plane.pln_x, plp->plane.pln_y))
1605                 add_airport(&airp,plp->plane.pln_x, plp->plane.pln_y);
1606         }
1607         
1608         /* Now, fly them out one airport at a time */
1609         for (qp = airp.q_forw; qp != (&airp); qp = qp->q_forw) {
1610             struct  airport *air;
1611             
1612             air = (struct airport *)qp;
1613             dist = mapdist(x,y,air->x,air->y);
1614             
1615             emp_initque(&i);
1616             
1617             /* Split off the interceptors at this base into i */
1618             divide(&interceptors, &i, air->x, air->y);
1619             
1620             tech = 0;
1621             mission_flags = 0;
1622             mission_flags |= P_X;           /* stealth (shhh) */
1623             /* gets turned off if not all choppers */
1624             mission_flags |= P_H;
1625             sam_intercept(bomb_list, &i, cn, victim, x, y, 0);
1626             sam_intercept(esc_list, &i, cn, victim, x, y, 1);
1627
1628             /* Did we run out of interceptors? */
1629             if (QEMPTY(&i))
1630                 continue;
1631             /* Did we run out of bombers? */
1632             if (QEMPTY(bomb_list)) {
1633                 /* Yes, so we have to put the rest of the interceptors back, and
1634                    then continue, or we leak memory */
1635                 pln_put(&i);
1636                 continue;
1637             }
1638             mission_flags = mission_pln_arm(&i,air->x,air->y,2*dist,'r',0,
1639                                             P_F,mission_flags,&tech);
1640
1641             /* Did we run out of interceptors? */
1642             if (QEMPTY(&i))
1643                 continue;
1644             /* Did we run out of bombers? */
1645             if (QEMPTY(bomb_list)) {
1646                 /* Yes, so we have to put the rest of the interceptors back, and
1647                    then continue, or we leak memory */
1648                 pln_put(&i);
1649                 continue;
1650             }
1651             
1652             BestAirPath(path, air->x, air->y, x, y);
1653             wu(0, cn, "Flying %s mission from %s\n",
1654                mission_name(MI_AIR_DEFENSE),
1655                xyas(air->x, air->y, cn));
1656             if (air->own && (air->own != cn)) {
1657                 wu(0, air->own, "%s is flying %s mission from %s\n",
1658                    cname(cn), mission_name(MI_AIR_DEFENSE),
1659                    xyas(air->x, air->y, air->own));
1660             }
1661             
1662             /* Now, fly the planes to the sector */
1663             emp_initque(&empty);
1664             ac_encounter(&i, &empty, air->x, air->y,
1665                          path, mission_flags,1, bomb_list, esc_list);
1666             
1667             /* If none made it, continue */
1668             if (QEMPTY(&i))
1669                 continue;
1670             
1671             /* Some made it, so now they get to try to fight. */
1672             /* Intercept the escorts first */
1673             combat = 0;
1674             if (!QEMPTY(esc_list)){
1675                 mpr(victim, "%s air defense planes intercept!\n",
1676                     cname(cn));
1677                 ac_combat_headers(victim, cn);
1678                 ac_airtoair(esc_list,&i,air->own);
1679                 combat = 1;
1680             }
1681             /* Now intercept the bombers */
1682             if (!QEMPTY(bomb_list)) {
1683                 if (!combat) {
1684                     mpr(victim, "%s air defense planes intercept!\n", cname(cn));
1685                     ac_combat_headers(victim, cn);
1686                 }
1687                 ac_airtoair(bomb_list, &i, air->own);
1688                 PR(cn, "\n");
1689                 PR(victim, "\n");
1690             }
1691             
1692             pln_put(&i);
1693         }
1694     }
1695
1696     /* We have to free all of these, if they are still there, otherwise they get
1697        lost and we leak memory all over the place. */
1698     for (cn = 1; cn < MAXNOC; cn++) {
1699         /* free up all this memory if it's still there */
1700         for (qp = mi[cn].queue.q_forw; qp != (&mi[cn].queue); qp = next) {
1701             next = qp->q_forw;
1702             glp = (struct genlist *)qp;
1703             free(glp->thing);
1704             free((s_char *)glp);
1705         }
1706     }
1707
1708     return dam;
1709 }
1710
1711
1712