src/hcode/btech/artillery.c

Go to the documentation of this file.
00001 /*
00002  * Author: Markus Stenberg <fingon@iki.fi>
00003  *
00004  *  Copyright (c) 1996 Markus Stenberg
00005  *  Copyright (c) 1998-2002 Thomas Wouters
00006  *  Copyright (c) 2000-2002 Cord Awtry
00007  *       All rights reserved
00008  *
00009  */
00010 
00011 /* 
00012    Artillery code for
00013    - standard rounds (damage to target hex, damage/2 to neighbor hexes)
00014    - smoke rounds (to be implemented)
00015    - fascam rounds (to be implemented) 
00016  */
00017 
00018 #include <math.h>
00019 
00020 #include "mech.h"
00021 #include "artillery.h"
00022 #include "mech.events.h"
00023 #include "create.h"
00024 #include "p.artillery.h"
00025 #include "p.mech.fire.h"
00026 #include "p.mech.combat.h"
00027 #include "p.mech.combat.misc.h"
00028 #include "p.mech.damage.h"
00029 #include "p.mech.utils.h"
00030 #include "p.map.obj.h"
00031 #include "p.mech.hitloc.h"
00032 #include "p.mine.h"
00033 #include "spath.h"
00034 
00035 struct artillery_shot_type *free_shot_list = NULL;
00036 static void artillery_hit(artillery_shot * s);
00037 
00038 static const char *artillery_type(artillery_shot * s)
00039 {
00040         if(s->type == CL_ARROW || s->type == IS_ARROW)
00041                 return "a missile";
00042         return "a round";
00043 }
00044 
00045 static struct {
00046         int dir;
00047         char *desc;
00048 } arty_dirs[] = {
00049         {
00050         0, "north"}, {
00051         60, "northeast"}, {
00052         90, "east"}, {
00053         120, "southeast"}, {
00054         180, "south"}, {
00055         240, "southwest"}, {
00056         270, "west"}, {
00057         300, "northwest"}, {
00058         0, NULL}
00059 };
00060 
00061 static const char *artillery_direction(artillery_shot * s)
00062 {
00063         float fx, fy, tx, ty;
00064         int b, d, i, best = -1, bestd = 0;
00065 
00066         MapCoordToRealCoord(s->from_x, s->from_y, &fx, &fy);
00067         MapCoordToRealCoord(s->to_x, s->to_y, &tx, &ty);
00068         b = FindBearing(fx, fy, tx, ty);
00069         for(i = 0; arty_dirs[i].desc; i++) {
00070                 d = abs(b - arty_dirs[i].dir);
00071                 if(best < 0 || d < bestd) {
00072                         best = i;
00073                         bestd = d;
00074                 }
00075         }
00076         if(best < 0)
00077                 return "Invalid";
00078         return arty_dirs[best].desc;
00079 }
00080 
00081 int artillery_round_flight_time(float fx, float fy, float tx, float ty)
00082 {
00083         int delay = MAX(ARTILLERY_MINIMUM_FLIGHT,
00084                                         (FindHexRange(fx, fy, tx, ty) / ARTY_SPEED));
00085 
00086         /* XXX Different weapons, diff. speed? */
00087         return delay;
00088 }
00089 
00090 static void artillery_hit_event(MUXEVENT * e)
00091 {
00092         artillery_shot *s = (artillery_shot *) e->data;
00093 
00094         artillery_hit(s);
00095         ADD_TO_LIST_HEAD(free_shot_list, next, s);
00096 }
00097 
00098 void artillery_shoot(MECH * mech, int targx, int targy, int windex,
00099                                          int wmode, int ishit)
00100 {
00101         struct artillery_shot_type *s;
00102         float fx, fy, tx, ty;
00103 
00104         if(free_shot_list) {
00105                 s = free_shot_list;
00106                 free_shot_list = free_shot_list->next;
00107         } else
00108                 Create(s, artillery_shot, 1);
00109         s->from_x = MechX(mech);
00110         s->from_y = MechY(mech);
00111         s->to_x = targx;
00112         s->to_y = targy;
00113         s->type = windex;
00114         s->mode = wmode;
00115         s->ishit = ishit;
00116         s->shooter = mech->mynum;
00117         s->map = mech->mapindex;
00118         MechLOSBroadcast(mech, tprintf("shoots %s towards %s!",
00119                                                                    artillery_type(s),
00120                                                                    artillery_direction(s)));
00121         MapCoordToRealCoord(s->from_x, s->from_y, &fx, &fy);
00122         MapCoordToRealCoord(s->to_x, s->to_y, &tx, &ty);
00123         muxevent_add(artillery_round_flight_time(fx, fy, tx, ty), 0, EVENT_DHIT,
00124                                  artillery_hit_event, (void *) s, NULL);
00125 }
00126 
00127 static int blast_arcf(float fx, float fy, MECH * mech)
00128 {
00129         int b, dir;
00130 
00131         b = FindBearing(MechFX(mech), MechFY(mech), fx, fy);
00132         dir = AcceptableDegree(b - MechFacing(mech));
00133         if(dir > 120 && dir < 240)
00134                 return BACK;
00135         if(dir > 300 || dir < 60)
00136                 return FRONT;
00137         if(dir > 180)
00138                 return LEFTSIDE;
00139         return RIGHTSIDE;
00140 }
00141 
00142 #define TABLE_GEN   0
00143 #define TABLE_PUNCH 1
00144 #define TABLE_KICK  2
00145 
00146 void blast_hit_hexf(MAP * map, int dam, int singlehitsize, int heatdam,
00147                                         float fx, float fy, float tfx, float tfy, char *tomsg,
00148                                         char *otmsg, int table, int safeup, int safedown,
00149                                         int isunderwater)
00150 {
00151         MECH *tempMech;
00152         int loop;
00153         int isrear = 0, iscritical = 0, hitloc;
00154         int damleft, arc, ndam;
00155         int ground_zero;
00156         short tx, ty;
00157 
00158         /* Not on a map so just return */
00159         if(!map)
00160                 return;
00161 
00162         RealCoordToMapCoord(&tx, &ty, fx, fy);
00163         if(tx < 0 || ty < 0 || tx >= map->map_width || ty >= map->map_height)
00164                 return;
00165         if(!tomsg || !otmsg)
00166                 return;
00167         if(isunderwater)
00168                 ground_zero = Elevation(map, tx, ty);
00169         else
00170                 ground_zero = MAX(0, Elevation(map, tx, ty));
00171 
00172         for(loop = 0; loop < map->first_free; loop++)
00173                 if(map->mechsOnMap[loop] >= 0) {
00174                         tempMech = (MECH *)
00175                                 FindObjectsData(map->mechsOnMap[loop]);
00176                         if(!tempMech)
00177                                 continue;
00178                         if(MechX(tempMech) != tx || MechY(tempMech) != ty)
00179                                 continue;
00180                         /* Far too high.. */
00181                         if(MechZ(tempMech) >= (safeup + ground_zero))
00182                                 continue;
00183                         /* Far too below (underwater, mostly) */
00184                         if(                                     /* MechTerrain(tempMech) == WATER &&  */
00185                                   MechZ(tempMech) <= (ground_zero - safedown))
00186                                 continue;
00187                         MechLOSBroadcast(tempMech, otmsg);
00188                         mech_notify(tempMech, MECHALL, tomsg);
00189                         arc = blast_arcf(tfx, tfy, tempMech);
00190 
00191                         if(arc == BACK)
00192                                 isrear = 1;
00193                         damleft = dam;
00194 
00195                         while (damleft > 0) {
00196                                 if(singlehitsize <= damleft)
00197                                         ndam = singlehitsize;
00198                                 else
00199                                         ndam = damleft;
00200 
00201                                 damleft -= ndam;
00202 
00203                                 switch (table) {
00204                                 case TABLE_PUNCH:
00205                     if (MechType(tempMech) != CLASS_MECH) {
00206                         hitloc = FindHitLocation(tempMech, arc, &iscritical, &isrear);
00207                     } else {
00208                         hitloc = FindPunchLocation(tempMech, arc);
00209                     }
00210                                         break;
00211                                 case TABLE_KICK:
00212                     if (MechType(tempMech) != CLASS_MECH) {
00213                         hitloc = FindHitLocation(tempMech, arc, &iscritical, &isrear);
00214                     } else {
00215                         hitloc = FindKickLocation(tempMech, arc);
00216                     }
00217                                         break;
00218                                 default:
00219                                         hitloc =
00220                                                 FindHitLocation(tempMech, arc, &iscritical, &isrear);
00221                                 }
00222 
00223                                 DamageMech(tempMech, tempMech, 0, -1, hitloc, isrear,
00224                                                    iscritical, ndam, 0, -1, 0, -1, 0, 0);
00225                         }
00226                         heat_effect(NULL, tempMech, heatdam, 0);
00227                 }
00228 }
00229 
00230 void blast_hit_hex(MAP * map, int dam, int singlehitsize, int heatdam,
00231                                    int fx, int fy, int tx, int ty, char *tomsg, char *otmsg,
00232                                    int table, int safeup, int safedown, int isunderwater)
00233 {
00234         float ftx, fty;
00235         float ffx, ffy;
00236 
00237         MapCoordToRealCoord(tx, ty, &ftx, &fty);
00238         MapCoordToRealCoord(fx, fy, &ffx, &ffy);
00239         blast_hit_hexf(map, dam, singlehitsize, heatdam, ffx, ffy, ftx, fty,
00240                                    tomsg, otmsg, table, safeup, safedown, isunderwater);
00241 }
00242 
00243 void blast_hit_hexesf(MAP * map, int dam, int singlehitsize, int heatdam,
00244                                           float fx, float fy, float ftx, float fty, char *tomsg,
00245                                           char *otmsg, char *tomsg1, char *otmsg1, int table,
00246                                           int safeup, int safedown, int isunderwater,
00247                                           int doneighbors)
00248 {
00249         int x1, y1, x2, y2;
00250         int dm;
00251         short tx, ty;
00252         float hx, hy;
00253         float t = FindXYRange(fx, fy, ftx, fty);
00254 
00255         dm = MAX(1, (int) t + 1);
00256         blast_hit_hexf(map, dam / dm, singlehitsize, heatdam / dm, fx, fy, ftx,
00257                                    fty, tomsg, otmsg, table, safeup, safedown, isunderwater);
00258         if(!doneighbors)
00259                 return;
00260         RealCoordToMapCoord(&tx, &ty, fx, fy);
00261         for(x1 = (tx - doneighbors); x1 <= (tx + doneighbors); x1++)
00262                 for(y1 = (ty - doneighbors); y1 <= (ty + doneighbors); y1++) {
00263                         int spot;
00264 
00265                         if((dm = MyHexDist(tx, ty, x1, y1, 0)) > doneighbors)
00266                                 continue;
00267                         if((tx == x1) && (ty == y1))
00268                                 continue;
00269                         x2 = BOUNDED(0, x1, map->map_width - 1);
00270                         y2 = BOUNDED(0, y1, map->map_height - 1);
00271                         if(x1 != x2 || y1 != y2)
00272                                 continue;
00273                         spot = (x1 == tx && y1 == ty);
00274                         MapCoordToRealCoord(x1, y1, &hx, &hy);
00275                         dm++;
00276                         if(!(dam / dm))
00277                                 continue;
00278                         blast_hit_hexf(map, dam / dm, singlehitsize, heatdam / dm, hx,
00279                                                    hy, ftx, fty, spot ? tomsg : tomsg1,
00280                                                    spot ? otmsg : otmsg1, table, safeup, safedown,
00281                                                    isunderwater);
00282 
00283                         /*
00284                          * Added in burning woods when a mech's engine goes nova
00285                          *
00286                          * -Kipsta
00287                          * 8/4/99
00288                          */
00289 
00290                         switch (GetRTerrain(map, x1, y1)) {
00291                         case LIGHT_FOREST:
00292                         case HEAVY_FOREST:
00293                                 if(!find_decorations(map, x1, y1)) {
00294                                         add_decoration(map, x1, y1, TYPE_FIRE, FIRE,
00295                                                                    FIRE_DURATION);
00296                                 }
00297 
00298                                 break;
00299                         }
00300                 }
00301 }
00302 
00303 void blast_hit_hexes(MAP * map, int dam, int singlehitsize, int heatdam,
00304                                          int tx, int ty, char *tomsg, char *otmsg, char *tomsg1,
00305                                          char *otmsg1, int table, int safeup, int safedown,
00306                                          int isunderwater, int doneighbors)
00307 {
00308         float fx, fy;
00309 
00310         MapCoordToRealCoord(tx, ty, &fx, &fy);
00311         blast_hit_hexesf(map, dam, singlehitsize, heatdam, fx, fy, fx, fy,
00312                                          tomsg, otmsg, tomsg1, otmsg1, table, safeup, safedown,
00313                                          isunderwater, doneighbors);
00314 }
00315 
00316 static void artillery_hit_hex(MAP * map, artillery_shot * s, int type,
00317                                                           int mode, int dam, int tx, int ty, int isdirect)
00318 {
00319         char buf[LBUF_SIZE];
00320         char buf1[LBUF_SIZE];
00321         char buf2[LBUF_SIZE];
00322 
00323         /* Safety check -- shouldn't happen */
00324         if(tx < 0 || tx >= map->map_width || ty < 0 || ty >= map->map_height)
00325                 return;
00326 
00327         if((mode & SMOKE_MODE)) {
00328                 /* Add smoke */
00329                 add_decoration(map, tx, ty, TYPE_SMOKE, SMOKE, SMOKE_DURATION);
00330                 return;
00331         }
00332         if(mode & MINE_MODE) {
00333                 add_mine(map, tx, ty, dam);
00334                 return;
00335         }
00336         if(!(mode & CLUSTER_MODE)) {
00337                 if(isdirect)
00338                         sprintf(buf1, "receives a direct hit!");
00339                 else
00340                         sprintf(buf1, "is hit by fragments!");
00341                 if(isdirect)
00342                         sprintf(buf2, "You receive a direct hit!");
00343                 else
00344                         sprintf(buf2, "You are hit by fragments!");
00345         } else {
00346                 if(dam > 2)
00347                         strcpy(buf, "bomblets");
00348                 else
00349                         strcpy(buf, "a bomblet");
00350                 sprintf(buf1, "is hit by %s!", buf);
00351                 sprintf(buf2, "You are hit by %s!", buf);
00352         }
00353         blast_hit_hex(map, dam, (mode & CLUSTER_MODE) ? 2 : 5, 0, tx, ty, tx,
00354                                   ty, buf2, buf1,
00355                                   (mode & CLUSTER_MODE) ? TABLE_PUNCH : TABLE_GEN, 10, 4, 0);
00356 }
00357 
00358 static artillery_shot *hit_neighbors_s;
00359 static int hit_neighbors_type;
00360 static int hit_neighbors_mode;
00361 static int hit_neighbors_dam;
00362 
00363 static void artillery_hit_neighbors_callback(MAP * map, int x, int y)
00364 {
00365         artillery_hit_hex(map, hit_neighbors_s, hit_neighbors_type,
00366                                           hit_neighbors_mode, hit_neighbors_dam, x, y, 0);
00367 }
00368 
00369 static void artillery_hit_neighbors(MAP * map, artillery_shot * s,
00370                                                                         int type, int mode, int dam, int tx,
00371                                                                         int ty)
00372 {
00373         hit_neighbors_s = s;
00374         hit_neighbors_type = type;
00375         hit_neighbors_mode = mode;
00376         hit_neighbors_dam = dam;
00377         visit_neighbor_hexes(map, tx, ty, artillery_hit_neighbors_callback);
00378 }
00379 
00380 static void artillery_cluster_hit(MAP * map, artillery_shot * s, int type,
00381                                                                   int mode, int dam, int tx, int ty)
00382 {
00383         /* Main idea: Pick <dam/2> bombs of 2pts each, and scatter 'em
00384            over 5x5 area with weighted numbers */
00385         int xd, yd, x, y;
00386         int i;
00387 
00388         int targets[5][5];
00389         int d;
00390 
00391         bzero(targets, sizeof(targets));
00392         for(i = 0; i < dam; i++) {
00393                 do {
00394                         xd = Number(-2, 0) + Number(0, 2);
00395                         yd = Number(-2, 0) + Number(0, 2);
00396                         x = tx + xd;
00397                         y = ty + yd;
00398                 }
00399                 while (x < 0 || x >= map->map_width || y < 0 || y >= map->map_height);
00400                 /* Whee.. it's time to drop a bomb to the hex */
00401                 targets[xd + 2][yd + 2]++;
00402         }
00403         for(xd = 0; xd < 5; xd++)
00404                 for(yd = 0; yd < 5; yd++)
00405                         if((d = targets[xd][yd]))
00406                                 artillery_hit_hex(map, s, type, mode, d * 2, xd + tx - 2,
00407                                                                   yd + ty - 2, 1);
00408 }
00409 
00410 void artillery_FriendlyAdjustment(dbref mechnum, MAP * map, int x, int y)
00411 {
00412         MECH *mech;
00413         MECH *spotter;
00414         MECH *tempMech = NULL;
00415 
00416         if(!(mech = getMech(mechnum)))
00417                 return;
00418         /* Ok.. we've a valid guy */
00419         spotter = getMech(MechSpotter(mech));
00420         if(!((MechTargX(mech) == x && MechTargY(mech) == y)
00421                  || (spotter && (MechTargX(spotter) == x &&
00422                                                  MechTargY(spotter) == y))))
00423                 return;
00424         /* Ok.. we've a valid target to adjust fire on */
00425         /* Now, see if we've any friendlies in LOS.. NOTE: FRIENDLIES ;-) */
00426         if(spotter) {
00427                 if(MechSeesHex(spotter, map, x, y))
00428                         tempMech = spotter;
00429         } else
00430                 tempMech = find_mech_in_hex(mech, map, x, y, 2);
00431         if(!tempMech)
00432                 return;
00433         if(!Started(tempMech) || !Started(mech))
00434                 return;
00435         if(spotter) {
00436                 mech_printf(mech, MECHSTARTED,
00437                                         "%s sent you some trajectory-correction data.",
00438                                         GetMechToMechID(mech, tempMech));
00439                 mech_printf(tempMech, MECHSTARTED,
00440                                         "You provide %s with information about the miss.",
00441                                         GetMechToMechID(tempMech, mech));
00442         }
00443         MechFireAdjustment(mech)++;
00444 }
00445 
00446 static void artillery_hit(artillery_shot * s)
00447 {
00448         /* First, we figure where it exactly hits. Our first-hand information
00449            is only whether it hits or not, not _where_ it hits */
00450         double dir;
00451         int di;
00452         int dist;
00453         int weight;
00454         MAP *map = getMap(s->map);
00455         int original_x = 0, original_y = 0;
00456         int dam = MechWeapons[s->type].damage;
00457 
00458         if(!map)
00459                 return;
00460         if(!s->ishit) {
00461                 /* Shit! We missed target ;-) */
00462                 /* Time to calculate a new target hex */
00463                 di = Number(0, 359);
00464                 dir = di * TWOPIOVER360;
00465                 dist = Number(2, 7);
00466                 weight = 100 * (dist * 6) / ((dist * 6 + map->windspeed));
00467                 di = (di * weight + map->winddir * (100 - weight)) / 100;
00468                 dist = (dist * weight + (map->windspeed / 6) * (100 - weight)) / 100;
00469                 original_x = s->to_x;
00470                 original_y = s->to_y;
00471                 s->to_x = s->to_x + dist * cos(dir);
00472                 s->to_y = s->to_y + dist * sin(dir);
00473                 s->to_x = BOUNDED(0, s->to_x, map->map_width - 1);
00474                 s->to_y = BOUNDED(0, s->to_y, map->map_height - 1);
00475                 /* Time to calculate if any friendlies have LOS to hex,
00476                    and if so, adjust fire adjustment unless you lack information /
00477                    have changed target */
00478         }
00479         /* It's time to run for your lives, lil' ones ;-) */
00480         if(!(s->mode & ARTILLERY_MODES))
00481                 HexLOSBroadcast(map, s->to_x, s->to_y, tprintf("%s fire hits $H!",
00482                                                                                                            &MechWeapons[s->type].
00483                                                                                                            name[3]));
00484         else if(s->mode & CLUSTER_MODE)
00485                 HexLOSBroadcast(map, s->to_x, s->to_y,
00486                                                 "A rain of small bomblets hits $H's surroundings!");
00487         else if(s->mode & MINE_MODE)
00488                 HexLOSBroadcast(map, s->to_x, s->to_y,
00489                                                 "A rain of small bomblets hits $H!");
00490         else if(s->mode & SMOKE_MODE)
00491                 HexLOSBroadcast(map, s->to_x, s->to_y,
00492                                                 tprintf
00493                                                 ("A %s %s hits $h, and smoke starts to billow!",
00494                                                  &MechWeapons[s->type].name[3],
00495                                                  &(artillery_type(s)[2])));
00496 
00497         /* Basic theory:
00498            - smoke / ordinary rounds are spread with the ordinary functions 
00499            - mines are otherwise ordinary except no hitting of neighbor hexes
00500            - cluster bombs are special 
00501          */
00502         if(!(s->mode & CLUSTER_MODE)) {
00503                 /* Enjoy ourselves in all neighbor hexes, too */
00504                 artillery_hit_hex(map, s, s->type, s->mode, dam, s->to_x, s->to_y, 1);
00505                 if(!(s->mode & MINE_MODE))
00506                         artillery_hit_neighbors(map, s, s->type, s->mode, dam / 2,
00507                                                                         s->to_x, s->to_y);
00508         } else
00509                 artillery_cluster_hit(map, s, s->type, s->mode, dam, s->to_x,
00510                                                           s->to_y);
00511         if(!s->ishit)
00512                 artillery_FriendlyAdjustment(s->shooter, map, original_x, original_y);
00513 }

Generated on Mon May 28 04:25:20 2007 for BattletechMUX by  doxygen 1.4.7