src/lib/subs/aircombat.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  aircombat.c: Deal with air to air combat
00029  * 
00030  *  Known contributors to this file:
00031  *     Dave Pare, 1986
00032  *     Thomas Ruschak, 1992
00033  *     Steve McClure, 1996
00034  *     Markus Armbruster, 2006
00035  */
00036 
00037 #include <config.h>
00038 
00039 #include "file.h"
00040 #include "land.h"
00041 #include "map.h"
00042 #include "misc.h"
00043 #include "nat.h"
00044 #include "news.h"
00045 #include "nsc.h"
00046 #include "optlist.h"
00047 #include "path.h"
00048 #include "plane.h"
00049 #include "player.h"
00050 #include "prototypes.h"
00051 #include "sect.h"
00052 #include "ship.h"
00053 #include "xy.h"
00054 
00055 #define FLAK_GUN_MAX 14
00056 
00057 static void ac_intercept(struct emp_qelem *, struct emp_qelem *,
00058                          struct emp_qelem *, natid, coord, coord);
00059 static int all_missiles(struct emp_qelem *);
00060 static void ac_dog(struct plist *, struct plist *);
00061 static void ac_planedamage(struct plist *, natid, int, natid, int,
00062                            int, char *);
00063 static void ac_doflak(struct emp_qelem *, struct sctstr *);
00064 static void ac_landflak(struct emp_qelem *, coord, coord);
00065 static void ac_shipflak(struct emp_qelem *, coord, coord);
00066 static void ac_fireflak(struct emp_qelem *, natid, int);
00067 static void getilist(struct emp_qelem *, natid,
00068                      struct emp_qelem *, struct emp_qelem *,
00069                      struct emp_qelem *, struct emp_qelem *);
00070 static int do_evade(struct emp_qelem *, struct emp_qelem *);
00071 
00072 void
00073 ac_encounter(struct emp_qelem *bomb_list, struct emp_qelem *esc_list,
00074              coord x, coord y, char *path, int mission_flags,
00075              int no_air_defense, struct emp_qelem *obomb,
00076              struct emp_qelem *oesc)
00077 {
00078     int val, non_missiles;
00079     int rel;
00080     int dir;
00081     int nats[MAXNOC];
00082     int lnats[MAXNOC];
00083     int gotilist[MAXNOC];
00084     int unfriendly[MAXNOC];
00085     int overfly[MAXNOC];
00086     struct emp_qelem ilist[MAXNOC], *qp;
00087     char mypath[1024];
00088     int myp;
00089     int civ, mil;
00090     natid plane_owner;
00091     struct sctstr sect;
00092     struct shpstr ship;
00093     struct lndstr land;
00094     struct nstr_item ni;
00095     natid cn;
00096     struct natstr *over, *mynatp;
00097     struct plist *plp;
00098     int evaded;
00099     struct shiplist *head = NULL;
00100     int changed = 0;
00101     int intown = 0;             /* Last owner to intercept */
00102 /* We want to only intercept once per sector per owner.  So, if we overfly
00103    a sector, and then overfly some land units or ships, we don't want to
00104    potentially intercept 3 times. */
00105 
00106     plp = (struct plist *)bomb_list->q_forw;
00107     plane_owner = plp->plane.pln_own;
00108 
00109     strncpy(mypath, path, sizeof(mypath));
00110     myp = 0;
00111 
00112     memset(overfly, 0, sizeof(overfly));
00113     memset(gotilist, 0, sizeof(gotilist));
00114     memset(unfriendly, 0, sizeof(unfriendly));
00115     for (cn = 1; cn < MAXNOC; cn++) {
00116         if ((mynatp = getnatp(cn)) == 0)
00117             continue;
00118         rel = getrel(mynatp, plane_owner);
00119         if (rel > HOSTILE)
00120             continue;
00121         if (plane_owner == cn)
00122             continue;
00123         unfriendly[cn]++;
00124     }
00125     if (mission_flags & PM_R) {
00126         if (mission_flags & P_S) {
00127             PR(plane_owner, "\nSPY Plane report\n");
00128             PRdate(plane_owner);
00129             sathead();
00130         } else if (mission_flags & P_A) {
00131             PR(plane_owner, "\nAnti-Sub Patrol report\n");
00132         } else {
00133             PR(plane_owner, "\nReconnaissance report\n");
00134             PRdate(plane_owner);
00135         }
00136     }
00137 
00138     pln_removedupes(bomb_list, esc_list);
00139     while ((dir = mypath[myp++]) && !QEMPTY(bomb_list)) {
00140         if ((val = diridx(dir)) == DIR_STOP)
00141             break;
00142         /* XXX using xnorm is probably bad */
00143         x = xnorm(x + diroff[val][0]);
00144         y = ynorm(y + diroff[val][1]);
00145         getsect(x, y, &sect);
00146         over = getnatp(sect.sct_own);
00147 
00148         if (mission_flags & PM_R) {
00149             if (opt_HIDDEN)
00150                 setcont(plane_owner, sect.sct_own, FOUND_FLY);
00151             if (sect.sct_type == SCT_WATER) {
00152                 PR(plane_owner, "flying over %s at %s\n",
00153                    dchr[sect.sct_type].d_name, xyas(x, y, plane_owner));
00154                 if (mission_flags & PM_S)
00155                     plane_sweep(bomb_list, x, y);
00156                 if (mission_flags & P_A) {
00157                     plane_sona(bomb_list, x, y, &head);
00158                 }
00159                 changed += map_set(plane_owner,
00160                                    sect.sct_x, sect.sct_y,
00161                                    dchr[sect.sct_type].d_mnem, 0);
00162             } else if (mission_flags & P_S) {
00163                 satdisp_sect(&sect, (mission_flags & P_I) ? 10 : 50);
00164             } else {
00165                 /* This is borrowed from lookout */
00166                 if (sect.sct_own == plane_owner)
00167                     PR(plane_owner, "Your ");
00168                 else
00169                     PR(plane_owner, "%s (#%d) ",
00170                        cname(sect.sct_own), sect.sct_own);
00171                 PR(plane_owner, "%s", dchr[sect.sct_type].d_name);
00172                 changed += map_set(plane_owner,
00173                                    sect.sct_x, sect.sct_y,
00174                                    dchr[sect.sct_type].d_mnem, 0);
00175                 PR(plane_owner, " %d%% efficient ",
00176                    (sect.sct_own == plane_owner) ?
00177                    sect.sct_effic : roundintby((int)sect.sct_effic, 25));
00178                 civ = sect.sct_item[I_CIVIL];
00179                 mil = sect.sct_item[I_MILIT];
00180                 if (civ)
00181                     PR(plane_owner, "with %s%d civ ",
00182                        (sect.sct_own == plane_owner) ?
00183                        "" : "approx ",
00184                        (sect.sct_own == plane_owner) ?
00185                        civ : roundintby(civ, 25));
00186                 if (mil)
00187                     PR(plane_owner, "with %s%d mil ",
00188                        (sect.sct_own == plane_owner) ?
00189                        "" : "approx ",
00190                        (sect.sct_own == plane_owner) ?
00191                        mil : roundintby(mil, 25));
00192                 PR(plane_owner, "@ %s\n", xyas(x, y, plane_owner));
00193             }
00194             if (mission_flags & P_S)
00195                 satdisp_units(sect.sct_x, sect.sct_y);
00196         } else {
00197             PR(plane_owner, "flying over %s at %s\n",
00198                dchr[sect.sct_type].d_name, xyas(x, y, plane_owner));
00199             changed += map_set(plane_owner, sect.sct_x, sect.sct_y,
00200                                dchr[sect.sct_type].d_mnem, 0);
00201         }
00202         if ((rel = getrel(over, plane_owner)) == ALLIED)
00203             continue;
00204 
00205         evaded = do_evade(bomb_list, esc_list);
00206 
00207         if (sect.sct_own != 0 && sect.sct_own != plane_owner && !evaded) {
00208             /* We only show planes overhead if they didn't
00209              * evade radar */
00210             overfly[sect.sct_own]++;
00211             PR(sect.sct_own, "%s planes spotted over %s\n",
00212                cname(plane_owner), xyas(x, y, sect.sct_own));
00213             if (opt_HIDDEN)
00214                 setcont(cn, plane_owner, FOUND_FLY);
00215         }
00216 
00217         if (!evaded) {
00218             /* Fire flak */
00219             if (unfriendly[sect.sct_own])
00220                 ac_doflak(bomb_list, &sect);
00221             /* If bombers left, fire flak from units and ships */
00222             if (!QEMPTY(bomb_list))
00223                 ac_landflak(bomb_list, x, y);
00224             if (!QEMPTY(bomb_list))
00225                 ac_shipflak(bomb_list, x, y);
00226         }
00227         /* mission planes aborted due to flak -- don't send escorts */
00228         if (QEMPTY(bomb_list))
00229             break;
00230         if (!no_air_defense && !evaded)
00231             air_defense(x, y, plane_owner, bomb_list, esc_list);
00232 
00233         if (sect.sct_own == 0 || sect.sct_own == plane_owner)
00234             continue;
00235 
00236         if (evaded)
00237             continue;
00238 
00239         non_missiles = 0;
00240         for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw) {
00241             struct plist *ip = (struct plist *)qp;
00242             if (!(plchr[(int)ip->plane.pln_type].pl_flags & P_M))
00243                 non_missiles = 1;
00244         }
00245 
00246         if (!non_missiles)
00247             continue;
00248 
00249         if (unfriendly[sect.sct_own] && !gotilist[sect.sct_own]) {
00250             getilist(&ilist[sect.sct_own], sect.sct_own,
00251                      bomb_list, esc_list, obomb, oesc);
00252             gotilist[sect.sct_own]++;
00253         }
00254         if (rel > HOSTILE)
00255             continue;
00256         ac_intercept(bomb_list, esc_list, &ilist[sect.sct_own],
00257                      sect.sct_own, x, y);
00258         intown = sect.sct_own;
00259     }
00260 
00261     /* Let's report all of the overflights even if aborted */
00262     for (cn = 1; cn < MAXNOC; cn++) {
00263         if (plane_owner == cn)
00264             continue;
00265         if (overfly[cn] > 0)
00266             nreport(plane_owner, N_OVFLY_SECT, cn, overfly[cn]);
00267     }
00268     /* If the map changed, update it */
00269     if (changed)
00270         writemap(player->cnum);
00271     /* Now, if the bomber and escort lists are empty, we are done */
00272     if (QEMPTY(bomb_list) && QEMPTY(esc_list)) {
00273         if (mission_flags & P_A)
00274             free_shiplist(&head);
00275         return;
00276     }
00277 
00278     /* Something made it through */
00279     /* Go figure out if there are ships in this sector, and who's they are */
00280     memset(nats, 0, sizeof(nats));
00281     snxtitem_xy(&ni, EF_SHIP, x, y);
00282     while (nxtitem(&ni, &ship)) {
00283         if (mchr[(int)ship.shp_type].m_flags & M_SUB)
00284             continue;
00285         nats[ship.shp_own]++;
00286     }
00287     /* Go figure out if there are units in this sector, and who's they are */
00288     memset(lnats, 0, sizeof(lnats));
00289     snxtitem_xy(&ni, EF_LAND, x, y);
00290     while (nxtitem(&ni, &land)) {
00291         if (land.lnd_ship >= 0 || land.lnd_land >= 0)
00292             continue;
00293         lnats[land.lnd_own]++;
00294     }
00295 
00296     /* Now, let's make life a little rougher. */
00297     for (cn = 1; cn < MAXNOC && !QEMPTY(bomb_list); cn++) {
00298         if (plane_owner == cn)
00299             continue;
00300         intown = -1;
00301         /* Are there ships owned by this country? */
00302         if (nats[cn] != 0) {
00303             /* Yes. */
00304             if (cn != 0) {
00305                 PR(cn, "%s planes spotted over ships in %s\n",
00306                    cname(plane_owner), xyas(x, y, cn));
00307                 if (opt_HIDDEN)
00308                     setcont(cn, plane_owner, FOUND_FLY);
00309             }
00310             if (unfriendly[cn]) {
00311                 /* They are unfriendly too */
00312                 if (!gotilist[cn]) {
00313                     getilist(&ilist[cn], cn, bomb_list, esc_list, obomb,
00314                              oesc);
00315                     gotilist[cn]++;
00316                 }
00317                 PR(plane_owner, "Flying over %s ships in %s\n",
00318                    cname(cn), xyas(x, y, plane_owner));
00319                 /* This makes going for ships in harbors tough */
00320                 if (!evaded) {
00321                     /* We already fired flak up above.  Now we intercept again if we haven't already */
00322                     /* Flag that we intercepted */
00323                     intown = 1;
00324                     /* And now intercept again */
00325                     ac_intercept(bomb_list, esc_list, &ilist[cn],
00326                                  cn, x, y);
00327                 }
00328             }
00329         }
00330         /* Are there units owned by this country? */
00331         if (lnats[cn] != 0) {
00332             /* Yes. */
00333             if (cn != 0) {
00334                 PR(cn, "%s planes spotted over land units in %s\n",
00335                    cname(plane_owner), xyas(x, y, cn));
00336                 if (opt_HIDDEN)
00337                     setcont(cn, plane_owner, FOUND_FLY);
00338             }
00339             if (unfriendly[cn]) {
00340                 /* They are unfriendly too */
00341                 if (!gotilist[cn]) {
00342                     getilist(&ilist[cn], cn, bomb_list, esc_list, obomb,
00343                              oesc);
00344                     gotilist[cn]++;
00345                 }
00346                 PR(plane_owner, "Flying over %s land units in %s\n",
00347                    cname(cn), xyas(x, y, plane_owner));
00348                 if (!evaded) {
00349                     if (intown == -1) {
00350                         /* We haven't intercepted yet, so intercept */
00351                         ac_intercept(bomb_list, esc_list, &ilist[cn],
00352                                      cn, x, y);
00353                     }
00354                 }
00355             }
00356         }
00357     }
00358     if (mission_flags & P_A)
00359         free_shiplist(&head);
00360 }
00361 
00362 static int
00363 count_non_missiles(struct emp_qelem *list)
00364 {
00365     struct emp_qelem *qp;
00366     struct plist *plp;
00367     int att_count = 0;
00368 
00369     /* don't intercept missiles */
00370     for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
00371         plp = (struct plist *)qp;
00372         if (!(plp->pcp->pl_flags & P_M))
00373             att_count++;
00374     }
00375     return att_count;
00376 }
00377 
00378 void
00379 sam_intercept(struct emp_qelem *att_list, struct emp_qelem *def_list,
00380               natid def_own, natid plane_owner, coord x, coord y,
00381               int delete_missiles)
00382 {
00383     struct emp_qelem *aqp;
00384     struct emp_qelem *anext;
00385     struct emp_qelem *dqp;
00386     struct emp_qelem *dnext;
00387     struct plist *aplp;
00388     struct plist *dplp;
00389     int first = 1;
00390 
00391     for (aqp = att_list->q_forw,
00392          dqp = def_list->q_forw;
00393          aqp != att_list && dqp != def_list; aqp = anext) {
00394         anext = aqp->q_forw;
00395         aplp = (struct plist *)aqp;
00396         if (aplp->pcp->pl_flags & P_M)
00397             continue;
00398         if (aplp->pcp->pl_cost < 1000)
00399             continue;
00400         for (; dqp != def_list; dqp = dnext) {
00401             dnext = dqp->q_forw;
00402             dplp = (struct plist *)dqp;
00403             if (!(dplp->pcp->pl_flags & P_M))
00404                 continue;
00405 
00406             if (dplp->plane.pln_range <
00407                 mapdist(x, y, dplp->plane.pln_x, dplp->plane.pln_y)) {
00408                 emp_remque(dqp);
00409                 free(dqp);
00410                 continue;
00411             }
00412             if (mission_pln_equip(dplp, 0, P_F, 0) < 0) {
00413                 emp_remque(dqp);
00414                 free(dqp);
00415                 continue;
00416             }
00417             if (first) {
00418                 first = 0;
00419                 PR(plane_owner, "%s launches SAMs!\n", cname(def_own));
00420                 PR(def_own, "Launching SAMs at %s planes!\n",
00421                    cname(plane_owner));
00422                 ac_combat_headers(plane_owner, def_own);
00423             }
00424             ac_dog(aplp, dplp);
00425             dqp = dnext;
00426             break;
00427         }
00428     }
00429     if (!first) {
00430         PR(plane_owner, "\n");
00431         PR(def_own, "\n");
00432     }
00433     if (delete_missiles) {
00434         for (; dqp != def_list; dqp = dnext) {
00435             dnext = dqp->q_forw;
00436             dplp = (struct plist *)dqp;
00437             if (!(dplp->pcp->pl_flags & P_M))
00438                 continue;
00439             emp_remque(dqp);
00440             free(dqp);
00441             continue;
00442         }
00443     }
00444 }
00445 
00446 static void
00447 ac_intercept(struct emp_qelem *bomb_list, struct emp_qelem *esc_list,
00448              struct emp_qelem *def_list, natid def_own, coord x, coord y)
00449 {
00450     struct plnstr *pp;
00451     struct plist *plp;
00452     int icount;
00453     struct emp_qelem *next;
00454     struct emp_qelem *qp;
00455     struct emp_qelem int_list;
00456     int att_count;
00457     natid plane_owner;
00458     int dist;
00459 
00460     plp = (struct plist *)bomb_list->q_forw;
00461     plane_owner = plp->plane.pln_own;
00462 
00463     icount = 0;
00464 
00465     sam_intercept(bomb_list, def_list, def_own, plane_owner, x, y, 0);
00466     sam_intercept(esc_list, def_list, def_own, plane_owner, x, y, 1);
00467     if (!(att_count = count_non_missiles(bomb_list) +
00468           count_non_missiles(esc_list)))
00469         return;
00470 
00471     emp_initque(&int_list);
00472     for (qp = def_list->q_forw; qp != def_list; qp = next) {
00473         next = qp->q_forw;
00474         plp = (struct plist *)qp;
00475         pp = &plp->plane;
00476         /* SAMs interdict separately */
00477         if (plp->pcp->pl_flags & P_M)
00478             continue;
00479         dist = mapdist(x, y, pp->pln_x, pp->pln_y) * 2;
00480         if (pp->pln_range < dist)
00481             continue;
00482         if (mission_pln_equip(plp, 0, P_F, 0) < 0) {
00483             emp_remque(qp);
00484             free(qp);
00485             continue;
00486         }
00487         /* got one; delete from def_list, add to int_list */
00488         emp_remque(qp);
00489         emp_insque(qp, &int_list);
00490         pp->pln_mobil -= pln_mobcost(dist, pp, P_F);
00491         putplane(pp->pln_uid, pp);
00492         icount++;
00493         if (icount > att_count)
00494             break;
00495     }
00496     if (icount == 0)
00497         return;
00498     PR(plane_owner, "%d %s fighter%s rising to intercept!\n",
00499        icount, cname(def_own), icount == 1 ? " is" : "s are");
00500     PR(def_own, "%d fighter%s intercepting %s planes!\n",
00501        icount, icount == 1 ? " is" : "s are", cname(plane_owner));
00502     ac_combat_headers(plane_owner, def_own);
00503     ac_airtoair(esc_list, &int_list);
00504     ac_airtoair(bomb_list, &int_list);
00505     PR(plane_owner, "\n");
00506     PR(def_own, "\n");
00507 }
00508 
00509 void
00510 ac_combat_headers(natid plane_owner, natid def_own)
00511 {
00512     PR(plane_owner,
00513        " %-10.10s %-10.10s  strength int odds  damage           results\n",
00514        cname(plane_owner), cname(def_own));
00515     PR(def_own,
00516        " %-10.10s %-10.10s  strength int odds  damage           results\n",
00517        cname(def_own), cname(plane_owner));
00518 }
00519 
00520 /*
00521  * air-to-air combat.
00522  */
00523 void
00524 ac_airtoair(struct emp_qelem *att_list, struct emp_qelem *int_list)
00525 {
00526     struct plist *attacker;
00527     struct plist *interceptor;
00528     struct emp_qelem *att;
00529     struct emp_qelem *in;
00530     int nplanes;
00531     int more_att;
00532     int more_int;
00533     struct emp_qelem *att_next;
00534     struct emp_qelem *in_next;
00535 
00536     att = att_list->q_forw;
00537     in = int_list->q_forw;
00538     more_att = 1;
00539     more_int = 1;
00540     if (QEMPTY(att_list) || QEMPTY(int_list)) {
00541         more_att = 0;
00542         more_int = 0;
00543     }
00544     while (more_att || more_int) {
00545         in_next = in->q_forw;
00546         att_next = att->q_forw;
00547         attacker = (struct plist *)att;
00548 
00549         /* skip missiles. If only missiles left, we're done */
00550         if (plchr[(int)attacker->plane.pln_type].pl_flags & P_M) {
00551             att = att_next;
00552             if (att == att_list) {
00553                 more_att = 0;
00554                 if (QEMPTY(att_list))
00555                     more_int = 0;
00556                 else
00557                     att = att->q_forw;
00558             }
00559             if (all_missiles(att_list))
00560                 more_att = 0;
00561             continue;
00562         }
00563         interceptor = (struct plist *)in;
00564         nplanes = attacker->plane.pln_effic;
00565         if (nplanes > interceptor->plane.pln_effic)
00566             nplanes = interceptor->plane.pln_effic;
00567         ac_dog(attacker, interceptor);
00568         in = in_next;
00569         att = att_next;
00570         if (att == att_list) {
00571             more_att = 0;
00572             if (QEMPTY(att_list))
00573                 more_int = 0;
00574             else
00575                 att = att->q_forw;
00576         }
00577         if (in == int_list) {
00578             more_int = 0;
00579             if (QEMPTY(int_list))
00580                 more_att = 0;
00581             else
00582                 in = in->q_forw;
00583         }
00584     }
00585 }
00586 
00587 static int
00588 all_missiles(struct emp_qelem *att_list)
00589 {
00590     struct emp_qelem *qp;
00591     struct plist *p;
00592 
00593     qp = att_list->q_forw;
00594     while (qp != att_list) {
00595         p = (struct plist *)qp;
00596         if (!(plchr[(int)p->plane.pln_type].pl_flags & P_M))
00597             return 0;
00598 
00599         qp = qp->q_forw;
00600     }
00601     return 1;
00602 }
00603 
00604 static void
00605 ac_dog(struct plist *ap, struct plist *dp)
00606 {
00607     int att, def;
00608     double odds;
00609     int intensity;
00610     natid att_own, def_own;
00611     int adam, ddam;
00612     char mesg[1024];
00613     char temp[14];
00614 
00615     att_own = ap->plane.pln_own;
00616     def_own = dp->plane.pln_own;
00617 
00618     PR(att_own, " %3.3s #%-4d  %3.3s #%-4d",
00619        ap->pcp->pl_name,
00620        ap->plane.pln_uid, dp->pcp->pl_name, dp->plane.pln_uid);
00621     if (def_own)
00622         PR(def_own, " %3.3s #%-4d  %3.3s #%-4d",
00623            dp->pcp->pl_name,
00624            dp->plane.pln_uid, ap->pcp->pl_name, ap->plane.pln_uid);
00625     if (ap->plane.pln_att == 0) {
00626         att = ap->plane.pln_def * ap->plane.pln_effic / 100;
00627         att = MAX(att, ap->pcp->pl_def / 2);
00628     } else {
00629         att = ap->plane.pln_att * ap->plane.pln_effic / 100;
00630         att = MAX(att, ap->pcp->pl_att / 2);
00631     }
00632 
00633     def = dp->plane.pln_def * dp->plane.pln_effic / 100;
00634     def = MAX(def, dp->pcp->pl_def / 2);
00635 
00636     if ((ap->pcp->pl_flags & P_F) && ap->bombs != 0)
00637         att -= 2;
00638     if ((dp->pcp->pl_flags & P_F) && dp->bombs != 0)
00639         def -= 2;
00640     att += ap->pcp->pl_stealth / 25.0;
00641     def += dp->pcp->pl_stealth / 25.0;
00642     if (att < 1) {
00643         def += 1 - att;
00644         att = 1;
00645     }
00646     if (def < 1) {
00647         att += 1 - def;
00648         def = 1;
00649     }
00650     odds = ((double)att / ((double)def + (double)att));
00651     if (odds <= 0.05)
00652         odds = 0.05;
00653     intensity = roll(20) + roll(20) + roll(20) + roll(20) + 1;
00654 
00655     PR(att_own, "   %3d/%-3d %3d  %3.2f  ", att, def, intensity, odds);
00656     PR(def_own, "   %3d/%-3d %3d  %3.2f  ", def, att, intensity, odds);
00657 
00658     adam = 0;
00659     ddam = 0;
00660     while ((intensity--) > 0) {
00661 
00662         if (chance(odds)) {
00663             ddam += 1;
00664             if ((dp->plane.pln_effic - ddam) < PLANE_MINEFF)
00665                 intensity = 0;
00666         } else {
00667             adam += 1;
00668             if ((ap->plane.pln_effic - adam) < PLANE_MINEFF)
00669                 intensity = 0;
00670         }
00671     }
00672 
00673     if (dp->pcp->pl_flags & P_M)
00674         ddam = 100;
00675 
00676     PR(att_own, "%3d/%-3d", adam, ddam);
00677     PR(def_own, "%3d/%-3d", ddam, adam);
00678     ac_planedamage(ap, def_own, adam, def_own, 1, 0, mesg);
00679     strncpy(temp, mesg, 14);
00680     ac_planedamage(dp, att_own, ddam, att_own, 1, 0, mesg);
00681     PR(att_own, "%-13.13s %-13.13s\n", temp, mesg);
00682     PR(def_own, "%-13.13s %-13.13s\n", mesg, temp);
00683 
00684     if (opt_HIDDEN) {
00685         setcont(att_own, def_own, FOUND_FLY);
00686         setcont(def_own, att_own, FOUND_FLY);
00687     }
00688 }
00689 
00690 /*
00691  * zap plane associated with plp.
00692  * Damaging country is "from", damage is "dam".
00693  * def_own is the country on the other side of the conflict from the plane
00694  * owner. The only time def_own != from is when the interceptor is getting
00695  * damaged.
00696  *
00697  * NOTE: This routine removes the appropriate plane element from the
00698  * queue if it gets destroyed.  That means that the caller must assume
00699  * that the current queue pointer is invalid on return from the ac_planedamage
00700  * call.  (this has caused bugs in the past)
00701  */
00702 static void
00703 ac_planedamage(struct plist *plp, natid from, int dam, natid other,
00704                int checkabort, int show, char *mesg)
00705 {
00706     struct plnstr *pp;
00707     int disp;
00708     char dmess[255];
00709     int eff;
00710     struct shpstr ship;
00711     struct lndstr land;
00712     natid plane_owner;
00713 
00714     disp = 0;
00715     pp = &plp->plane;
00716     plane_owner = pp->pln_own;
00717     eff = pp->pln_effic;
00718     sprintf(dmess, " no damage");
00719     if (dam <= 0) {
00720         strcpy(mesg, dmess);
00721         return;
00722     }
00723     memset(dmess, 0, sizeof(dmess));
00724     eff -= dam;
00725     if (eff < 0)
00726         eff = 0;
00727     if (eff < PLANE_MINEFF) {
00728         sprintf(dmess, " shot down");
00729         disp = 1;
00730     } else if (eff < 80 && chance((80 - eff) / 100.0) && checkabort) {
00731         sprintf(dmess, " aborted @%2d%%", eff);
00732         disp = 2;
00733     } else if (show == 0) {
00734         sprintf(dmess, " cleared");
00735     }
00736 
00737     if ((plp->pcp->pl_flags & P_M) == 0) {
00738         if (show) {
00739             PR(plane_owner, "    %s %s takes %d%s.\n",
00740                cname(pp->pln_own), prplane(pp), dam, dmess);
00741             if (other)
00742                 PR(other, "    %s %s takes %d%s.\n",
00743                    cname(pp->pln_own), prplane(pp), dam, dmess);
00744         }
00745     }
00746     if (show && checkabort == 1) {
00747         PR(plane_owner, "\n");
00748         if (other)
00749             PR(other, "\n");
00750     }
00751 
00752     pp->pln_effic = eff;
00753     pp->pln_mobil -= MIN(32 + pp->pln_mobil, dam / 2);
00754     if (disp == 1) {
00755         if (from != 0 && (plp->pcp->pl_flags & P_M) == 0)
00756             nreport(from, N_DOWN_PLANE, pp->pln_own, 1);
00757         if (pp->pln_ship >= 0) {
00758             getship(pp->pln_ship, &ship);
00759             take_plane_off_ship(pp, &ship);
00760         }
00761         if (pp->pln_land >= 0) {
00762             getland(pp->pln_land, &land);
00763             take_plane_off_land(pp, &land);
00764         }
00765         pp->pln_effic = 0;
00766         putplane(pp->pln_uid, pp);
00767         emp_remque(&plp->queue);
00768         free(plp);
00769     } else if (disp == 2) {
00770         putplane(pp->pln_uid, pp);
00771         emp_remque(&plp->queue);
00772         free(plp);
00773     } else
00774         putplane(pp->pln_uid, pp);
00775     strcpy(mesg, dmess);
00776 }
00777 
00778 static void
00779 ac_doflak(struct emp_qelem *list, struct sctstr *from)
00780 {
00781     int shell;
00782     int gun;
00783     natid plane_owner;
00784     struct plist *plp;
00785 
00786     plp = (struct plist *)list->q_forw;
00787     plane_owner = plp->plane.pln_own;
00788 
00789     gun = MIN(FLAK_GUN_MAX, from->sct_item[I_GUN]);
00790     shell = from->sct_item[I_SHELL];
00791     if (gun > shell * 2) {
00792         shell += supply_commod(from->sct_own, from->sct_x, from->sct_y,
00793                                I_SHELL, (gun + 1) / 2 - shell);
00794         from->sct_item[I_SHELL] = shell;
00795         putsect(from);
00796     }
00797     if (gun > shell * 2)
00798         gun = shell * 2;
00799 
00800     gun = roundavg(tfact(from->sct_own, 2.0 * gun));
00801     if (gun > 0) {
00802         PR(plane_owner, "firing %d flak guns in %s...\n",
00803            gun, xyas(from->sct_x, from->sct_y, plane_owner));
00804         if (from->sct_own != 0)
00805             PR(from->sct_own, "firing %d flak guns in %s...\n",
00806                gun, xyas(from->sct_x, from->sct_y, from->sct_own));
00807         ac_fireflak(list, from->sct_own, gun);
00808     }
00809 }
00810 
00811 static void
00812 ac_shipflak(struct emp_qelem *list, coord x, coord y)
00813 {
00814     struct nstr_item ni;
00815     struct shpstr ship;
00816     struct mchrstr *mcp;
00817     double flak, total, ngun;
00818     int gun, shell;
00819     int rel;
00820     struct plist *plp;
00821     natid plane_owner;
00822     natid from;
00823     int nats[MAXNOC];
00824 
00825     plp = (struct plist *)list->q_forw;
00826     plane_owner = plp->plane.pln_own;
00827 
00828     memset(nats, 0, sizeof(nats));
00829     total = ngun = 0;
00830     snxtitem_xy(&ni, EF_SHIP, x, y);
00831     while (!QEMPTY(list) && nxtitem(&ni, &ship)) {
00832         if (ship.shp_own == 0 || ship.shp_own == plane_owner)
00833             continue;
00834         mcp = &mchr[(int)ship.shp_type];
00835         if (mcp->m_flags & M_SUB)
00836             continue;
00837         rel = getrel(getnatp(ship.shp_own), plane_owner);
00838         if (rel > HOSTILE)
00839             continue;
00840         shell = gun = 0;
00841         gun = MIN(ship.shp_item[I_GUN], ship.shp_glim);
00842         if (gun) {
00843             shell = ship.shp_item[I_SHELL];
00844             if (shell <= 0) {
00845                 shell = supply_commod(ship.shp_own, ship.shp_x, ship.shp_y,
00846                                       I_SHELL, 1);
00847                 ship.shp_item[I_SHELL] = shell;
00848                 putship(ship.shp_uid, &ship);
00849             }
00850         }
00851         if (gun == 0 || shell == 0)
00852             continue;
00853         flak = gun * (ship.shp_effic / 100.0);
00854         ngun += flak;
00855         total += techfact(ship.shp_tech, flak * 2.0);
00856 
00857         if (!nats[ship.shp_own]) {
00858             /* First time here, print the message */
00859             PR(ship.shp_own, "%s planes spotted over ships in %s\n",
00860                cname(plane_owner), xyas(x, y, ship.shp_own));
00861             PR(plane_owner, "Flying over %s ships in %s\n",
00862                cname(ship.shp_own), xyas(x, y, plane_owner));
00863             nats[ship.shp_own] = 1;
00864         }
00865         PR(ship.shp_own, "firing %.0f flak guns from %s...\n",
00866            flak, prship(&ship));
00867         from = ship.shp_own;
00868     }
00869 
00870     /* Limit to FLAK_GUN_MAX guns of average tech factor */
00871     if (ngun > FLAK_GUN_MAX)
00872         total *= FLAK_GUN_MAX / ngun;
00873 
00874     gun = roundavg(total);
00875     if (gun > 0) {
00876         PR(plane_owner, "Flak!  Ships firing %d flak guns...\n", gun);
00877         ac_fireflak(list, from, gun);
00878     }
00879 }
00880 
00881 static void
00882 ac_landflak(struct emp_qelem *list, coord x, coord y)
00883 {
00884     struct nstr_item ni;
00885     struct lndstr land;
00886     struct lchrstr *lcp;
00887     double flak, total, ngun;
00888     int gun;
00889     int rel;
00890     struct plist *plp;
00891     natid plane_owner;
00892     natid from;
00893     int nats[MAXNOC];
00894 
00895     plp = (struct plist *)list->q_forw;
00896     plane_owner = plp->plane.pln_own;
00897 
00898     memset(nats, 0, sizeof(nats));
00899     total = ngun = 0;
00900     snxtitem_xy(&ni, EF_LAND, x, y);
00901     while (!QEMPTY(list) && nxtitem(&ni, &land)) {
00902         if (land.lnd_own == 0 || land.lnd_own == plane_owner)
00903             continue;
00904         lcp = &lchr[(int)land.lnd_type];
00905         if ((lcp->l_flags & L_FLAK) == 0 || land.lnd_aaf == 0)
00906             continue;
00907         if (land.lnd_ship >= 0 || land.lnd_land >= 0)
00908             continue;
00909         rel = getrel(getnatp(land.lnd_own), plane_owner);
00910         if (rel > HOSTILE)
00911             continue;
00912         flak = land.lnd_aaf * 1.5 * land.lnd_effic / 100.0;
00913         ngun += flak;
00914         total += techfact(land.lnd_tech, flak * 2.0);
00915 
00916         if (!nats[land.lnd_own]) {
00917             /* First time here, print the message */
00918             PR(land.lnd_own, "%s planes spotted over land units in %s\n",
00919                cname(plane_owner), xyas(x, y, land.lnd_own));
00920             PR(plane_owner, "Flying over %s land units in %s\n",
00921                cname(land.lnd_own), xyas(x, y, plane_owner));
00922             nats[land.lnd_own] = 1;
00923         }
00924         PR(land.lnd_own, "firing flak guns from unit %s (aa rating %d)\n",
00925            prland(&land), land.lnd_aaf);
00926         from = land.lnd_own;
00927     }
00928 
00929     /* Limit to FLAK_GUN_MAX guns of average tech factor */
00930     if (ngun > FLAK_GUN_MAX)
00931         total *= FLAK_GUN_MAX / ngun;
00932 
00933     gun = roundavg(total);
00934     if (gun > 0) {
00935         PR(plane_owner, "Flak!  Land units firing %d flak guns...\n", gun);
00936         ac_fireflak(list, from, gun);
00937     }
00938 }
00939 
00940 /*
00941  * Called from shipflak, landflak, and doflak.
00942  */
00943 static void
00944 ac_fireflak(struct emp_qelem *list, natid from, int guns)
00945 {
00946     struct plist *plp;
00947     int n;
00948     struct emp_qelem *qp;
00949     struct emp_qelem *next;
00950     char msg[255];
00951 
00952     plp = (struct plist *)list->q_forw;
00953 
00954     for (qp = list->q_forw; qp != list; qp = next) {
00955         next = qp->q_forw;
00956         plp = (struct plist *)qp;
00957         n = ac_flak_dam(guns, plp->plane.pln_def, plp->pcp->pl_flags);
00958         ac_planedamage(plp, from, n, 0, 2, 1, msg);
00959     }
00960 }
00961 
00962 /*
00963  * Calculate flak damage
00964  */
00965 int
00966 ac_flak_dam(int guns, int def, int pl_flags)
00967 {
00968     int flak, dam;
00969     float mult;
00970     /*                             <-7      -7     -6     -5     -4 */
00971     static float flaktable[18] = { 0.132f, 0.20f, 0.20f, 0.25f, 0.30f,
00972     /*    -3     -2     -1      0     +1     +2     +3     +4 */
00973          0.35f, 0.40f, 0.45f, 0.50f, 0.50f, 0.55f, 0.60f, 0.65f,
00974     /*    +5    +6     +7     +8    >+8 */
00975          0.70f,0.75f, 0.80f, 0.85f, 1.1305f };
00976     enum { FLAK_MAX = sizeof(flaktable)/sizeof(flaktable[0]) - 1 };
00977 
00978     flak = guns - def;
00979     if ((pl_flags & P_T) == 0)
00980         flak--;
00981     if (pl_flags & P_X)
00982         flak -= 2;
00983     if (pl_flags & P_H)
00984         flak -= 1;
00985 
00986     if (flak > 8)
00987         mult = flaktable[FLAK_MAX];
00988     else if (flak < -7)
00989         mult = flaktable[0];
00990     else {
00991         flak += 8;
00992         mult = flaktable[flak];
00993     }
00994     mult *= flakscale;
00995     dam = (int)((roll(8) + 2) * mult);
00996     if (dam > 100)
00997         dam = 100;
00998     return dam;
00999 }
01000 
01001 /*
01002  * See if this plane is flying in this list
01003  */
01004 int
01005 ac_isflying(struct plnstr *plane, struct emp_qelem *list)
01006 {
01007     struct emp_qelem *qp;
01008     struct emp_qelem *next;
01009     struct plnstr *pp;
01010     struct plist *plp;
01011 
01012     if (!list)
01013         return 0;
01014     for (qp = list->q_forw; qp != list; qp = next) {
01015         next = qp->q_forw;
01016         plp = (struct plist *)qp;
01017         pp = &plp->plane;
01018         if (plane->pln_uid == pp->pln_uid)
01019             return 1;
01020     }
01021     return 0;
01022 }
01023 
01024 
01025 /*
01026  * Get a list of planes available for interception duties.
01027  */
01028 static void
01029 getilist(struct emp_qelem *list, natid own, struct emp_qelem *a,
01030          struct emp_qelem *b, struct emp_qelem *c, struct emp_qelem *d)
01031 {
01032     struct plchrstr *pcp;
01033     struct plnstr plane;
01034     struct nstr_item ni;
01035     struct plist *ip;
01036 
01037     emp_initque(list);
01038     snxtitem_all(&ni, EF_PLANE);
01039     while (nxtitem(&ni, &plane)) {
01040         if (plane.pln_own != own)
01041             continue;
01042         pcp = &plchr[(int)plane.pln_type];
01043         if ((pcp->pl_flags & P_F) == 0)
01044             continue;
01045         if (plane.pln_mission != 0)
01046             continue;
01047         if (plane.pln_mobil <= 0)
01048             continue;
01049         if (plane.pln_effic < 40)
01050             continue;
01051         if (!pln_airbase_ok(&plane, 0, 0))
01052             continue;
01053         /* Finally, is it in the list of planes already in
01054            flight? */
01055         if (ac_isflying(&plane, a))
01056             continue;
01057         if (ac_isflying(&plane, b))
01058             continue;
01059         if (ac_isflying(&plane, c))
01060             continue;
01061         if (ac_isflying(&plane, d))
01062             continue;
01063         /* got one! */
01064         ip = malloc(sizeof(*ip));
01065         ip->bombs = 0;
01066         ip->misc = 0;
01067         ip->pcp = &plchr[(int)plane.pln_type];
01068         ip->plane = plane;
01069         emp_insque(&ip->queue, list);
01070     }
01071 }
01072 
01073 static int
01074 do_evade(struct emp_qelem *bomb_list, struct emp_qelem *esc_list)
01075 {
01076     struct emp_qelem *qp;
01077     double evade;
01078     struct plist *plp;
01079 
01080     evade = 100.0;
01081     for (qp = bomb_list->q_forw; qp != bomb_list; qp = qp->q_forw) {
01082         plp = (struct plist *)qp;
01083         if (evade > plp->pcp->pl_stealth / 100.0)
01084             evade = plp->pcp->pl_stealth / 100.0;
01085     }
01086     for (qp = esc_list->q_forw; qp != esc_list; qp = qp->q_forw) {
01087         plp = (struct plist *)qp;
01088         if (evade > plp->pcp->pl_stealth / 100.0)
01089             evade = plp->pcp->pl_stealth / 100.0;
01090     }
01091 
01092     if (chance(evade))
01093         return 1;
01094 
01095     return 0;
01096 }

Generated on Fri Mar 28 11:01:15 2008 for empserver by  doxygen 1.5.2