src/hcode/btech/mech.combat.c

Go to the documentation of this file.
00001 /*
00002  * Author: Markus Stenberg <fingon@iki.fi>
00003  *
00004  *  Copyright (c) 1997-2002 Markus Stenberg
00005  *  Copyright (c) 1998-2002 Thomas Wouters
00006  *  Copyright (c) 2000-2002 Cord Awtry
00007  *  Copyright (c) 1999-2005 Kevin Stevens
00008  *       All rights reserved
00009  */
00010 
00011 #include <stdio.h>
00012 #include <stdlib.h>
00013 #include <math.h>
00014 #include <sys/file.h>
00015 
00016 #include "mech.h"
00017 #include "map.h"
00018 #include "btmacros.h"
00019 #include "mech.events.h"
00020 #include "create.h"
00021 #include "failures.h"
00022 #include "mech.combat.h"
00023 #include "mech.partnames.h"
00024 #include "p.mech.ice.h"
00025 #include "p.pcombat.h"
00026 #include "p.mech.utils.h"
00027 #include "p.mech.los.h"
00028 #include "p.mech.build.h"
00029 #include "p.map.obj.h"
00030 #include "p.mech.combat.h"
00031 #include "p.btechstats.h"
00032 #include "p.mech.update.h"
00033 #include "p.bsuit.h"
00034 #include "p.artillery.h"
00035 #include "p.mech.fire.h"
00036 #include "p.mech.c3.h"
00037 #include "p.mech.c3i.h"
00038 #include "p.mech.c3.misc.h"
00039 #include "p.mine.h"
00040 #include "p.mech.hitloc.h"
00041 #include "p.eject.h"
00042 #include "p.crit.h"
00043 #include "p.mech.ood.h"
00044 #include "p.map.conditions.h"
00045 #include "p.template.h"
00046 #include "p.mech.pickup.h"
00047 #include "p.event.h"
00048 #include "p.mech.tag.h"
00049 #include "p.mech.ecm.h"
00050 #include "mech.ecm.h"
00051 #include "p.mech.enhanced.criticals.h"
00052 #include "p.mech.spot.h"
00053 #include "p.mech.combat.misc.h"
00054 #include "p.mech.combat.missile.h"
00055 #include "p.mech.bth.h"
00056 #include "p.mech.damage.h"
00057 
00058 extern int arc_override;
00059 extern dbref pilot_override;
00060 
00061 /*
00062 Optional firing modes:
00063 Autocannons:
00064 Rapid-Fire:
00065 std and light ACs only
00066 fires like ultra
00067 fails on roll of 2-4
00068 failure results in 1 round in chamber exploding
00069 explosion does not cause MW damage
00070 MGs
00071 Rapid-Fire
00072 once set can't unset (lame?)
00073 roll 1d6
00074 result is heat generated
00075 result is damage inflicted
00076 ammo == damage * 3
00077 LRMs
00078 Hotloading
00079 no min range
00080 roll 3d6 for number of missiles hit and take lowest two rolls
00081 can not un-hotload
00082 critical hit to launcher destroys launcher and does damage equal to one flight of missiles
00083 roll 2d6. On roll of 2-5 launcher explosion triggers ammo boom of launcher's ammo that's in the
00084 same loc as the launcher.
00085 PPC
00086 Disengage field inhibitor
00087 removes min range
00088 roll 2d6 for feed back check and refer to chart below.
00089 If failure, mech takes 10 points of internal damage to loc of PPC
00090 Target distance Avoid feedback on:
00091 1 10+
00092 2 6+
00093 3 3+
00094 */
00095 void mech_target(dbref player, void *data, char *buffer)
00096 {
00097         MECH *mech = (MECH *) data;
00098         MECH *target;
00099         char *args[5];
00100         int argc;
00101         char section[50];
00102         char type, move, index;
00103 
00104         cch(MECH_USUALO);
00105         argc = mech_parseattributes(buffer, args, 5);
00106         DOCHECK(argc != 1, "Invalid number of arguments to function!");
00107         if(!strcmp(args[0], "-")) {
00108                 MechAim(mech) = NUM_SECTIONS;
00109                 notify(player, "Targetting disabled.");
00110                 return;
00111         }
00112         DOCHECK(MechTarget(mech) < 0 ||
00113                         !(target =
00114                           FindObjectsData(MechTarget(mech))),
00115                         "Error: You need to be locked onto something to target its part!");
00116         type = MechType(target);
00117         move = MechMove(target);
00118         DOCHECK((index =
00119                          ArmorSectionFromString(type, move, args[0])) < 0,
00120                         "Invalid location!");
00121         MechAim(mech) = index;
00122         MechAimType(mech) = type;
00123         ArmorStringFromIndex(index, section, type, move);
00124         notify_printf(player, "%s targetted.", section);
00125 }
00126 
00127 /* Varying messages based on the distance to foe, and size of your vehicle
00128    vs size of the guy targeting you: */
00129 
00130 /*-20 (or less), -15 to 15, and 20+ ton difference (targetertons - yourtons)*/
00131 
00132 /*Distance: <9, <20, rest */
00133 
00134 /* Idea: Tonseverity + 3 * distseverity */
00135 char *ss_messages[] = {
00136         "You feel you'll have your hands full before too long..",
00137         "You have a bad feeling about this..",
00138         "You feel a homicidal maniac is about to pounce on you!",
00139 
00140         "You think something is amiss..",
00141         "You have a slightly bad feeling about this..",
00142         "You think someone thinks ill of you..",
00143 
00144         "Something makes you somewhat feel uneasy..",
00145         "Something makes you definitely feel uneasy..",
00146         "Something makes you feel out of your element.."
00147 };
00148 
00149 #define SSDistMod(r) ((r < 9) ? 0 : ((r < 20) ? 1 : 2))
00150 #define SSTonMod(d)  ((d <= -20) ? 0 : (d >= 20) ? 2 : 1)
00151 
00152 static void mech_ss_event(MUXEVENT * ev)
00153 {
00154         MECH *mech = (MECH *) ev->data;
00155         int i = (int) ev->data2;
00156 
00157         if(Uncon(mech))
00158                 return;
00159         if(!RGotPilot(mech))
00160                 return;
00161         mech_notify(mech, MECHPILOT, ss_messages[BOUNDED(0, i, 8)]);
00162 }
00163 
00164 void sixth_sense_check(MECH * mech, MECH * target)
00165 {
00166         float r;
00167         int d;
00168 
00169         if(!(MechSpecials(target) & SS_ABILITY) || MechIsObservator(mech))
00170                 return;
00171         if(Destroyed(target))
00172                 return;
00173         if(Roll() > 8)
00174                 return;
00175         r = FaMechRange(mech, target);
00176         d = (MechRTonsV(mech) - MechRTonsV(target)) / 1024;
00177         MECHEVENT(target, EVENT_SS, mech_ss_event, Number(1, 3),
00178                           ((3 * (SSDistMod(r))) + (SSTonMod(d))));
00179 }
00180 
00181 void mech_settarget(dbref player, void *data, char *buffer)
00182 {
00183         MECH *mech = (MECH *) data, *target;
00184         MAP *mech_map;
00185         char *args[5];
00186         char targetID[2];
00187         int argc;
00188         int LOS = 1;
00189         int newx, newy;
00190         dbref targetref;
00191         int mode;
00192 
00193         cch(MECH_USUALO);
00194 
00195         argc = mech_parseattributes(buffer, args, 5);
00196         switch (argc) {
00197         case 1:
00198                 mech_map = getMap(mech->mapindex);
00199                 if(args[0][0] == '-') {
00200                         MechTarget(mech) = -1;
00201                         MechTargX(mech) = -1;
00202                         MechTargY(mech) = -1;
00203                         mech_notify(mech, MECHALL, "All locks cleared.");
00204                         StopLock(mech);
00205                         if(MechSpotter(mech) == mech->mynum)
00206                                 ClearFireAdjustments(mech_map, mech->mynum);
00207                         return;
00208                 }
00209                 targetID[0] = args[0][0];
00210                 targetID[1] = args[0][1];
00211                 targetref = FindTargetDBREFFromMapNumber(mech, targetID);
00212                 target = getMech(targetref);
00213                 if(target)
00214                         LOS =
00215                                 InLineOfSight(mech, target, MechX(target), MechY(target),
00216                                                           FlMechRange(mech_map, mech, target));
00217                 else
00218                         targetref = -1;
00219                 DOCHECK(targetref == -1 ||
00220                                 !LOS, "That is not a valid targetID. Try again.");
00221 
00222                 if(MechSwarmTarget(mech) > 0) {
00223                         if(MechSwarmTarget(mech) != target->mynum) {
00224                                 mech_notify(mech, MECHALL,
00225                                                         "You're a bit too busy holding on for dear life to lock a target!");
00226                                 return;
00227                         }
00228                 }
00229 
00230                 mech_printf(mech, MECHALL, "Target set to %s.",
00231                                         GetMechToMechID(mech, target));
00232                 StopLock(mech);
00233                 MechTarget(mech) = targetref;
00234                 MechStatus(mech) |= LOCK_TARGET;
00235                 sixth_sense_check(mech, target);
00236 #if LOCK_TICK > 0
00237                 if(!arc_override)
00238                         MECHEVENT(mech, EVENT_LOCK, mech_lock_event, LOCK_TICK, 0);
00239 #endif
00240                 break;
00241         case 2:
00242                 /* Targetted a square */
00243                 if(MechSwarmTarget(mech) > 0) {
00244                         mech_notify(mech, MECHALL,
00245                                                 "You're a bit too busy holding on for dear life to lock a target!");
00246                         return;
00247                 }
00248 
00249                 mech_map = getMap(mech->mapindex);
00250                 newx = atoi(args[0]);
00251                 newy = atoi(args[1]);
00252                 ValidCoord(mech_map, newx, newy);
00253                 MechTarget(mech) = -1;
00254                 MechTargX(mech) = newx;
00255                 MechTargY(mech) = newy;
00256                 MechFireAdjustment(mech) = 0;
00257                 if(MechSpotter(mech) == mech->mynum)
00258                         ClearFireAdjustments(mech_map, mech->mynum);
00259                 MechTargZ(mech) = Elevation(mech_map, newx, newy);
00260                 notify_printf(player, "Target coordinates set at (X,Y) %d, %d",
00261                                           newx, newy);
00262                 StopLock(mech);
00263                 MechStatus(mech) |= LOCK_TARGET;
00264 #if LOCK_TICK > 0
00265                 if(!arc_override)
00266                         MECHEVENT(mech, EVENT_LOCK, mech_lock_event, LOCK_TICK, 0);
00267 #endif
00268                 break;
00269         case 3:
00270                 /* Targetted a square w/ special mode (hex / building) */
00271                 if(MechSwarmTarget(mech) > 0) {
00272                         mech_notify(mech, MECHALL,
00273                                                 "You're a bit too busy holding on for dear life to lock a target!");
00274                         return;
00275                 }
00276 
00277                 DOCHECK(strlen(args[2]) > 1, "Invalid lock mode!");
00278                 switch (toupper(args[2][0])) {
00279                 case 'B':
00280                         mode = LOCK_BUILDING;
00281                         break;
00282                 case 'I':
00283                         mode = LOCK_HEX_IGN;
00284                         break;
00285                 case 'C':
00286                         mode = LOCK_HEX_CLR;
00287                         break;
00288                 case 'H':
00289                         mode = LOCK_HEX;
00290                         break;
00291                 default:
00292                         notify(player, "Invalid mode selected!");
00293                         return;
00294                 }
00295                 mech_map = getMap(mech->mapindex);
00296                 newx = atoi(args[0]);
00297                 newy = atoi(args[1]);
00298                 ValidCoord(mech_map, newx, newy);
00299                 MechTarget(mech) = -1;
00300                 MechTargX(mech) = newx;
00301                 MechTargY(mech) = newy;
00302                 MechFireAdjustment(mech) = 0;
00303                 if(MechSpotter(mech) == mech->mynum)
00304                         ClearFireAdjustments(mech_map, mech->mynum);
00305                 MechTargZ(mech) = Elevation(mech_map, newx, newy);
00306                 switch (mode) {
00307                 case LOCK_HEX:
00308                         notify_printf(player,
00309                                                   "Target coordinates set to hex at (X,Y) %d, %d",
00310                                                   newx, newy);
00311                         break;
00312                 case LOCK_HEX_CLR:
00313                         notify_printf(player,
00314                                                   "Target coordinates set to clearing hex at (X,Y) %d, %d",
00315                                                   newx, newy);
00316                         break;
00317                 case LOCK_HEX_IGN:
00318                         notify_printf(player,
00319                                                   "Target coordinates set to igniting hex at (X,Y) %d, %d",
00320                                                   newx, newy);
00321                         break;
00322                 default:
00323                         notify_printf(player,
00324                                                   "Target coordinates set to building at (X,Y) %d, %d",
00325                                                   newx, newy);
00326                         break;
00327                 }
00328 
00329                 StopLock(mech);
00330                 MechStatus(mech) |= mode;
00331 #if LOCK_TICK > 0
00332                 if(!arc_override)
00333                         MECHEVENT(mech, EVENT_LOCK, mech_lock_event, LOCK_TICK, 0);
00334 #endif
00335         }
00336 }
00337 
00338 /*
00339  * Fire weapon command handler
00340  */
00341 void mech_fireweapon(dbref player, void *data, char *buffer)
00342 {
00343         MECH *mech = (MECH *) data;
00344         MAP *mech_map;
00345         char *args[5];
00346         int argc;
00347         int weapnum;
00348 
00349         mech_map = getMap(mech->mapindex);
00350         cch(MECH_USUALO);
00351         argc = mech_parseattributes(buffer, args, 5);
00352         DOCHECK(argc < 1, "Not enough arguments to the function");
00353         weapnum = atoi(args[0]);
00354         FireWeaponNumber(player, mech, mech_map, weapnum, argc, args, 0);
00355 }
00356 
00357 #define ARCCHECK(mech,ex,ey,sec,crit,msg) \
00358 if (AeroUnusableArcs(mech)) \
00359 { int ar; ar = InWeaponArc(mech, ex, ey); \
00360 DOCHECK0((!arc_override && (AeroUnusableArcs(mech) & ar)) || \
00361 (arc_override && !(arc_override & ar)), \
00362 "That arc's weapons aren't under your control!"); \
00363 }; \
00364 DOCHECK0(!IsInWeaponArc (mech, ex, ey, sec, crit), \
00365 msg);
00366 
00367 /*
00368  * Main weapon firing routine
00369  */
00370 int FireWeaponNumber(dbref player,
00371                                          MECH * mech,
00372                                          MAP * mech_map, int weapnum, int argc, char **args,
00373                                          int sight)
00374 {
00375         int weaptype;
00376         dbref target;
00377         char targetID[2];
00378         int mapx = 0, mapy = 0, LOS = 0;
00379         MECH *tempMech = NULL;
00380         int section, critical;
00381         float range = 0;
00382         float enemyX, enemyY, enemyZ;
00383         int ishex = 0;
00384         int wcDeadLegs = 0;
00385         char location[20];
00386         int mode;
00387         int i;
00388 
00389         if(MechType(mech) == CLASS_BSUIT) {
00390                 for(i = 0; i < NUM_BSUIT_MEMBERS; i++) {
00391                         DOCHECK1(!SectIsDestroyed(mech, i) &&
00392                                          MechSections(mech)[i].recycle,
00393                                          tprintf("Suit %d is still recovering from attack.",
00394                                                          i + 1));
00395                 }
00396         }
00397 
00398         /* If they fire their weapon while hidden, they should appear */
00399         if(!sight && (MechCritStatus(mech) & HIDDEN)) {
00400                 mech_notify(mech, MECHALL,
00401                                         "You break out of your cover to initiate weapons fire!");
00402                 MechLOSBroadcast(mech,
00403                                                  "breaks out of its cover and begins firing rabidly!");
00404                 MechCritStatus(mech) &= ~HIDDEN;
00405         }
00406 
00407         /* If they fire a weapon while trying to hide stop them from hiding */
00408         if(!sight) {
00409                 StopHiding(mech);
00410         }
00411 #ifdef BT_MOVEMENT_MODES
00412         DOCHECK0(MoveModeLock(mech),
00413                          "You cannot fire while using a special movement mode.");
00414 #endif
00415         DOCHECK0(MechSpotter(mech) > 0
00416                          && MechSpotter(mech) == mech->mynum,
00417                          "You cannot fire while spotting.");
00418         DOCHECK0(weapnum < 0,
00419                          "The weapons system chirps: 'Illegal Weapon Number!'");
00420 
00421         weaptype =
00422                 FindWeaponNumberOnMech_Advanced(mech, weapnum, &section, &critical,
00423                                                                                 sight);
00424 
00425         DOCHECK0(weaptype == -1,
00426                          "The weapons system chirps: 'Illegal Weapon Number!'");
00427 
00428         mode = GetPartFireMode(mech, section, critical);
00429 
00430         if(!sight) {
00431 
00432                 /* Exile Stun Code Check */
00433                 DOCHECK0((MechCritStatus(mech) & MECH_STUNNED),
00434                                  "You cannot take actions while stunned! That includes finding the trigger.");
00435 
00436                 DOCHECK0(PartTempNuke(mech, section, critical),
00437                                  "The weapons system chirps: 'That weapon is still unusable - please stand by.'");
00438                 DOCHECK0(weaptype == -3,
00439                                  "The weapons system chirps: 'That weapon is still reloading!'");
00440                 DOCHECK0(weaptype == -4,
00441                                  "The weapons system chirps: 'That weapon is still recharging!'");
00442 
00443                 /* New fancy message for when they try and fire a weapon and the section
00444                  * is busy */
00445                 if(weaptype == -5) {
00446 
00447                         /* Get the section name and print the message */
00448                         ArmorStringFromIndex(section, location, MechType(mech),
00449                                                                  MechMove(mech));
00450                         notify_printf(player, "%s%s is still recovering from a "
00451                                                   "previous action!",
00452                                                   MechType(mech) == CLASS_BSUIT ? "" : "Your ",
00453                                                   location);
00454                         return 0;
00455                 }
00456 
00457                 DOCHECK0(MechSections(mech)[section].specials & CARRYING_CLUB,
00458                                  "You're carrying a club in that arm.");
00459 
00460                 if(Fallen(mech) && MechType(mech) == CLASS_MECH) {
00461 
00462                         /* if a quad has 3 of 4 legs dead, it can't fire at all while prone */
00463                         wcDeadLegs = CountDestroyedLegs(mech);
00464                         if(MechIsQuad(mech))
00465                                 DOCHECK0(wcDeadLegs > 2,
00466                                                  "Quads need at least 3 legs to fire while prone.");
00467 
00468                         /* quads with all 4 legs can fire all weapons while prone. They do not need to prop. */
00469                         if(!MechIsQuad(mech) || (MechIsQuad(mech) && wcDeadLegs > 0)) {
00470                                 DOCHECK0(section == RLEG || section == LLEG,
00471                                                  "You cannot fire leg mounted weapons when prone.");
00472                                 switch (section) {
00473                                 case RARM:
00474                                         DOCHECK0(SectHasBusyWeap(mech, LARM) ||
00475                                                          MechSections(mech)[LARM].recycle ||
00476                                                          SectIsDestroyed(mech, LARM),
00477                                                          "You currently can't use your Left Arm to prop yourself up.");
00478                                         break;
00479                                 case LARM:
00480                                         DOCHECK0(SectHasBusyWeap(mech, RARM) ||
00481                                                          MechSections(mech)[RARM].recycle ||
00482                                                          SectIsDestroyed(mech, RARM),
00483                                                          "Your currently can't use your Right Arm to prop yourself up.");
00484                                         break;
00485                                 default:
00486                                         DOCHECK0((SectHasBusyWeap(mech, RARM) ||
00487                                                           MechSections(mech)[RARM].recycle ||
00488                                                           SectIsDestroyed(mech, RARM))
00489                                                          && (SectHasBusyWeap(mech, LARM) ||
00490                                                                  MechSections(mech)[LARM].recycle ||
00491                                                                  SectIsDestroyed(mech, LARM)),
00492                                                          "You currently don't have any arms to spare to prop yourself up.");
00493                                 }
00494                         }
00495                 }
00496         }
00497 
00498         if(IsMechMounted(mech)) {
00499                 DOCHECK0(((section == CTORSO) || (section == RTORSO) ||
00500                                   (section == LTORSO)),
00501                                  "You cannot fire torso-mounted weapons while you have battlesuits on you!");
00502         }
00503 
00504         DOCHECK0((MechDugIn(mech)) && section != TURRET,
00505                          "Only turret weapons are available while in cover.");
00506         DOCHECK0(weaptype == -2 ||
00507                          (PartTempNuke(mech, section, critical) == FAIL_DESTROYED),
00508                          "The weapons system chirps: 'That weapon has been destroyed!'");
00509         DOCHECK0(IsAMS(weaptype), "That weapon is defensive only!");
00510         DOCHECK0(argc > 3, "Invalid number of arguments!");
00511 
00512         if((MechWeapons[weaptype].special & IDF) && MechSpotter(mech) != -1 &&
00513            MechTarget(mech) == -1 && MechTargY(mech) == -1 &&
00514            MechTargX(mech) == -1)
00515                 if(FireSpot(player, mech, mech_map, weapnum, weaptype, sight,
00516                                         section, critical))
00517                         return 1;
00518 
00519         switch (argc) {
00520 
00521                 /* Fire at default target */
00522         case 1:
00523 
00524                 /* If its a coolant gun in heat mode we should shot our mech */
00525                 if(IsCoolant(weaptype) && (mode & HEAT_MODE)) {
00526 
00527                         /* Setting our mech as the target and the other parameters
00528                          * as well */
00529                         tempMech = mech;
00530                         DOCHECK0(!tempMech, "Error in FireWeaponNumber routine");
00531                         mapx = MechX(tempMech);
00532                         mapy = MechY(tempMech);
00533                         range = 0.2;
00534                         LOS = 1;
00535 
00536                 } else {
00537 
00538                         DOCHECK0(!FindTargetXY(mech, &enemyX, &enemyY, &enemyZ),
00539                                          "You do not have a default target set!");
00540 
00541                         if(MechTarget(mech) != -1) {
00542 
00543                                 tempMech = getMech(MechTarget(mech));
00544                                 DOCHECK0(!tempMech, "Error in FireWeaponNumber routine");
00545                                 mapx = MechX(tempMech);
00546                                 mapy = MechY(tempMech);
00547                                 range = FaMechRange(mech, tempMech);
00548                                 LOS = LOS_NB(mech, tempMech, mapx, mapy, range);
00549 
00550                                 if(!(MechWeapons[weaptype].special & IDF)) {
00551                                         DOCHECK0(!LOS,
00552                                                          "That target is not in your line of sight!");
00553                                 } else if(MapIsUnderground(mech_map)) {
00554                                         DOCHECK0(!LOS,
00555                                                          "That target is not in your direct line of sight, and "
00556                                                          "you cannot fire your IDF weapons underground!");
00557                                 }
00558                                 if(mudconf.btech_idf_requires_spotter
00559                                    && (MechWeapons[weaptype].special & IDF)
00560                                    && (MechSpotter(mech) == -1))
00561                                         DOCHECK0(!LOS,
00562                                                          "That target is not in your direct line of sight"
00563                                                          " and you do not have a spotter set!!");
00564                         } else {
00565 
00566                                 /* default target is a hex */
00567                                 ishex = 1;
00568                                 if(!sight && !IsArtillery(weaptype) && MechLockFire(mech)) {
00569 
00570                                         /* look for enemies in the default hex cause they may have moved */
00571                                         if((tempMech =
00572                                                 find_mech_in_hex(mech, mech_map, MechTargX(mech),
00573                                                                                  MechTargY(mech), 0))) {
00574 
00575                                                 enemyX = MechFX(tempMech);
00576                                                 enemyY = MechFY(tempMech);
00577                                                 enemyZ = MechFZ(tempMech);
00578                                                 mapx = MechX(tempMech);
00579                                                 mapy = MechY(tempMech);
00580                                         }
00581 
00582                                 }
00583 
00584                                 if(!tempMech) {
00585                                         mapx = MechTargX(mech);
00586                                         mapy = MechTargY(mech);
00587                                         MechTargZ(mech) = Elevation(mech_map, mapx, mapy);
00588                                         enemyZ = ZSCALE * MechTargZ(mech);
00589                                         MapCoordToRealCoord(mapx, mapy, &enemyX, &enemyY);
00590                                 }
00591 
00592                                 /* don't check LOS for missile weapons firing at hex number */
00593                                 range =
00594                                         FindRange(MechFX(mech), MechFY(mech), MechFZ(mech),
00595                                                           enemyX, enemyY, enemyZ);
00596                                 LOS = LOS_NB(mech, tempMech, mapx, mapy, range);
00597 
00598                                 /* Check for Spotter here */
00599                                 if(mudconf.btech_idf_requires_spotter &&
00600                                    (MechWeapons[weaptype].special & IDF)
00601                                    && (MechSpotter(mech) == -1))
00602                                         DOCHECK0(!LOS,
00603                                                          "That hex target is not in your direct line of sight"
00604                                                          " and you do not have a spotter set!!");
00605 
00606                                 if(!
00607                                    (IsArtillery(weaptype)
00608                                         || (MechWeapons[weaptype].special & IDF))) {
00609                                         DOCHECK0(!LOS,
00610                                                          "That hex target is not in your line of sight!");
00611                                 } else if(MapIsUnderground(mech_map)) {
00612                                         DOCHECK0(!LOS,
00613                                                          "That target is not in your direct line of sight, and "
00614                                                          "you cannot fire your IDF weapons underground!");
00615                                 }
00616                         }
00617 
00618                         if(MechType(mech) != CLASS_BSUIT) {
00619                                 ARCCHECK(mech, enemyX, enemyY, section, critical,
00620                                                  "Default target is not in your weapons arc!");
00621                         }
00622                 }
00623                 break;
00624 
00625         case 2:
00626                 /* Fire at the numbered target */
00627                 targetID[0] = args[1][0];
00628                 targetID[1] = args[1][1];
00629                 target = FindTargetDBREFFromMapNumber(mech, targetID);
00630                 DOCHECK0(target == -1, "That target is not in your line of sight!");
00631                 tempMech = getMech(target);
00632                 DOCHECK0(!tempMech, "Error in FireWeaponNumber routine!");
00633                 enemyX = MechFX(tempMech);
00634                 enemyY = MechFY(tempMech);
00635                 enemyZ = MechFZ(tempMech);
00636                 mapx = MechX(tempMech);
00637                 mapy = MechY(tempMech);
00638 
00639                 range = FindRange(MechFX(mech), MechFY(mech), MechFZ(mech), enemyX,
00640                                                   enemyY, enemyZ);
00641                 LOS = LOS_NB(mech, tempMech, MechX(tempMech), MechY(tempMech), range);
00642 
00643                 DOCHECK0(!LOS, "That target is not in your line of sight!");
00644 
00645                 if(MechType(mech) != CLASS_BSUIT) {
00646                         ARCCHECK(mech, enemyX, enemyY, section, critical,
00647                                          "That target is not in your weapons arc!");
00648                 }
00649                 break;
00650 
00651         case 3:
00652 
00653                 /* Fire at the Map X Y */
00654                 mapx = atoi(args[1]);
00655                 mapy = atoi(args[2]);
00656                 ishex = 1;
00657                 DOCHECK0(mapx < 0 || mapx >= mech_map->map_width || mapy < 0 ||
00658                                  mapy >= mech_map->map_height,
00659                                  "Map coordinates out of range!");
00660 
00661                 if(!sight && !IsArtillery(weaptype))
00662 
00663                         /* look for enemies in that hex... */
00664                         if((tempMech = find_mech_in_hex(mech, mech_map, MechTargX(mech),
00665                                                                                         MechTargY(mech), 0))) {
00666                                 enemyX = MechFX(tempMech);
00667                                 enemyY = MechFY(tempMech);
00668                                 enemyZ = MechFZ(tempMech);
00669                         }
00670 
00671                 if(!tempMech) {
00672                         MapCoordToRealCoord(mapx, mapy, &enemyX, &enemyY);
00673                         MechTargZ(mech) = Elevation(mech_map, mapx, mapy);
00674                         enemyZ = ZSCALE * MechTargZ(mech);
00675                 }
00676 
00677                 if(MechType(mech) != CLASS_BSUIT) {
00678                         ARCCHECK(mech, enemyX, enemyY, section, critical,
00679                                          "That hex target is not in your weapons arc!");
00680                 }
00681 
00682                 /* Don't check LOS for missile weapons */
00683                 range = FindRange(MechFX(mech), MechFY(mech), MechFZ(mech), enemyX,
00684                                                   enemyY, enemyZ);
00685                 LOS = LOS_NB(mech, tempMech, mapx, mapy, range);
00686 
00687                 if(!IsArtillery(weaptype))
00688                         DOCHECK0(!LOS, "That hex target is not in your line of sight!");
00689         }
00690 
00691     if(tempMech) {
00692         DOCHECK0(IsArtillery(weaptype),
00693                 "You can only target hexes with this kind of artillery.");
00694         DOCHECK0(MechSwarmTarget(tempMech) == mech->mynum,
00695                 "You are unable to use your weapons against a 'swarmer!");
00696         DOCHECK0(StealthArmorActive(tempMech) &&
00697                 ((MechTarget(mech) != tempMech->mynum) || Locking(mech)),
00698                 "You need a stable lock to fire on that target!");
00699         DOCHECK0(!IsCoolant(weaptype) && MechTeam(tempMech) == MechTeam(mech)
00700                 && MechNoFriendlyFire(mech),
00701                 "You can't fire on a teammate with FFSafeties on!");
00702         DOCHECK0(!IsCoolant(weaptype) && MechTeam(tempMech) == MechTeam(mech)
00703                 && MapNoFriendlyFire(mech_map),
00704                 "Friendly Fire? I don't think so...");
00705         DOCHECK0(MechType(tempMech) == CLASS_MW && MechType(mech) != CLASS_MW
00706                 && !MechPKiller(mech),
00707                 "That's a living, breathing person! Switch off the safety first, "
00708                 "if you really want to assassinate the target.");
00709     }
00710 
00711         FireWeapon(mech, mech_map, tempMech, LOS, weaptype, weapnum, section,
00712                            critical, enemyX, enemyY, mapx, mapy, range, 1000, sight,
00713                            ishex);
00714         return (1);
00715 }
00716 
00717 int weapon_failure_stuff(MECH * mech,
00718                                                  int *weapnum,
00719                                                  int *weapindx,
00720                                                  int *section,
00721                                                  int *critical,
00722                                                  int *ammoLoc,
00723                                                  int *ammoCrit,
00724                                                  int *ammoLoc1,
00725                                                  int *ammoCrit1,
00726                                                  int *modifier,
00727                                                  int *type, float range, int *range_ok,
00728                                                  int wGattlingShots)
00729 {
00730         CheckWeaponFailed(mech, *weapnum, *weapindx, *section, *critical,
00731                                           modifier, type);
00732         if(*type == POWER_SPIKE)
00733                 return 1;
00734         if(*type == WEAPON_JAMMED || *type == WEAPON_DUD) {
00735                 /* Just decrement ammunition */
00736                 decrement_ammunition(mech, *weapindx, *section, *critical,
00737                                                          *ammoLoc, *ammoCrit, *ammoLoc1, *ammoCrit1,
00738                                                          wGattlingShots);
00739                 return 1;
00740         }
00741         if(*type == RANGE)
00742                 if((EGunRangeWithCheck(mech, *section,
00743                                                            *weapindx) - *modifier) < range) {
00744                         mech_notify(mech, MECHALL,
00745                                                 "Due to weapons failure your shot falls short of its target!");
00746                         *range_ok = 0;
00747                 }
00748         return 0;
00749 }
00750 
00751 void sendC3TrackEmit(MECH * mech, dbref c3Ref, MECH * c3Mech)
00752 {
00753         if(c3Mech && (c3Mech->mynum != mech->mynum)) {
00754                 mech_printf(mech, MECHALL,
00755                                         "Using range data from %s [%s]",
00756                                         silly_atr_get(c3Mech->mynum,
00757                                                                   A_MECHNAME), MechIDS(c3Mech, 1));
00758         }
00759 }
00760 
00761 void FireWeapon(MECH * mech,
00762                                 MAP * mech_map,
00763                                 MECH * target,
00764                                 int LOS,
00765                                 int weapindx,
00766                                 int weapnum,
00767                                 int section,
00768                                 int critical,
00769                                 float enemyX,
00770                                 float enemyY,
00771                                 int mapx,
00772                                 int mapy, float range, int indirectFire, int sight, int ishex)
00773 {
00774         MECH *altTarget;
00775         int baseToHit,RbaseToHit;
00776         int ammoLoc;
00777         int ammoCrit;
00778         int ammoLoc1;
00779         int ammoCrit1;
00780         int roll;
00781         int r1, r2, r3, rtmp;
00782         int type = -1, modifier;
00783         int isarty = (IsArtillery(weapindx));
00784         int range_ok = 1;
00785         int wGattlingShots = 0;         /* If we're a gattling MG, then we need to figure out how many shots */
00786         char buf[SBUF_SIZE];
00787         char buf3[SBUF_SIZE];
00788         char buf2[LBUF_SIZE];
00789         int wRACHeat = 0;
00790         int wHGRPSkillMod = 0;
00791         int tIsSwarmAttack = 0;
00792         dbref c3Ref = -1;
00793         MECH *c3Mech = NULL;
00794         int firstCrit = 0;
00795         int mode = GetPartFireMode(mech, section, critical);
00796         int wAmmoMode = GetPartAmmoMode(mech, section, critical);
00797 
00798         DOCHECKMA((wAmmoMode & STINGER_MODE)
00799                           && ishex, "Stinger missiles cannot shoot hexes!");
00800         DOCHECKMA((wAmmoMode & STINGER_MODE) && target
00801                           && !(Jumping(target) || OODing(target)
00802                                    || (FlyingT(target)
00803                                            && !Landed(target))),
00804                           "Stinger missiles can only engage airborne targets!");
00805 
00806         /* If its a coolant gun set to heat, set the target
00807          * to the mech (ie it shoots itself with coolant gun) */
00808         if(IsCoolant(weapindx) && mode & HEAT_MODE)
00809                 target = mech;
00810 
00811         DOCHECKMA((SectionUnderwater(mech, section) &&
00812                            (MechWeapons[weapindx].shortrange_water <= 0)),
00813                           "This weapon may not be fired underwater.");
00814         DOCHECKMA(CrewStunned(mech), "You are too stunned to fire a weapon!");
00815         DOCHECKMA(UnjammingTurret(mech),
00816                           "You are too busy unjamming your turret!");
00817         DOCHECKMA(UnJammingAmmo(mech), "You are too busy unjamming a weapon!");
00818         DOCHECKMA(RemovingPods(mech), "You are too busy removing iNARC pods!");
00819 
00820         DOCHECKMA((MechSwarmTarget(mech) > 0) && ((!target) ||
00821                                                                                           (MechSwarmTarget(mech) !=
00822                                                                                            target->mynum)),
00823                           "You're too busy holding on for dear life!");
00824 
00825         if(MechSwarmTarget(mech) > 0) {
00826                 if(target && (MechSwarmTarget(mech) == target->mynum))
00827                         tIsSwarmAttack = 1;
00828         }
00829 
00830         /*
00831          * Gattling MGs do d6 damage (all as one hit), causing the same in heat
00832          * and using 3 * damage in ammo.
00833          */
00834         if(GetPartFireMode(mech, section, critical) & GATTLING_MODE)
00835                 wGattlingShots = Number(1, 6);
00836 
00837         /* Find and check Ammunition */
00838         if(!sight)
00839                 if(!FindAndCheckAmmo(mech, weapindx, section, critical, &ammoLoc,
00840                                                          &ammoCrit, &ammoLoc1, &ammoCrit1,
00841                                                          &wGattlingShots))
00842                         return;
00843 
00844                         /****************************************
00845             * START: Calc BTH and Roll
00846         ****************************************/
00847         if(!isarty) {
00848                 baseToHit =
00849                         FindNormalBTH(mech, mech_map, section, critical, weapindx,
00850                                                   range, target, indirectFire, &c3Ref);
00851 
00852                 if(c3Ref) {
00853                         c3Mech = getMech(c3Ref);
00854 
00855                         if(c3Mech && ((MechTeam(c3Mech) != MechTeam(mech)) ||
00856                                                   (c3Ref == mech->mynum))) {
00857                                 c3Mech = NULL;
00858                         }
00859                 }
00860 
00861         } else
00862                 baseToHit = FindArtilleryBTH(mech, section, weapindx, !LOS, range);
00863 
00864         /* If it's a swarm attack, make the BTH 0 'cause they always hit */
00865         if(tIsSwarmAttack)
00866                 baseToHit = 0;
00867 
00868         /* Mod the roll for DFMs and ELRMS */
00869         if((MechWeapons[weapindx].special & DFM) ||
00870            ((MechWeapons[weapindx].special & ELRM) &&
00871                 range < MechWeapons[weapindx].min)) {
00872                 r1 = Number(1, 6);
00873                 r2 = Number(1, 6);
00874                 r3 = Number(1, 6);
00875                 /* Sort 'em to ascending order */
00876                 if(r1 > r2)
00877                         Swap(r1, r2);
00878                 if(r2 > r3)
00879                         Swap(r2, r3);
00880                 roll = r1 + r2;
00881         } else {
00882                 if(target)
00883                         roll = Roll();
00884                 else
00885                         roll = Roll();
00886         }
00887         buf[0] = 0;
00888         if(LOS)
00889                 sprintf(buf, "Roll: %d ", roll);
00890 
00891                         /****************************************
00892             * END: Calc BTH and Roll
00893         ****************************************/
00894 
00895                 /****************************************
00896         * START: Do all the necessary emits for sighting and firing
00897         ****************************************/
00898         if(target && !ishex) {
00899                 range = FaMechRange(mech, target);
00900                 strcpy(buf2, "");
00901                 if(MechAim(mech) != NUM_SECTIONS &&
00902                    MechAimType(mech) == MechType(target) && !IsMissile(weapindx)) {
00903                         ArmorStringFromIndex(MechAim(mech), buf3, MechType(target),
00904                                                                  MechMove(target));
00905                         sprintf(buf2, "'s %s", buf3);
00906                 }
00907 
00908                 if(sight) {
00909                         DOCHECKMA(baseToHit >= 900,
00910                                           tprintf("You aim %s at %s%s - Out of range.",
00911                                                           &MechWeapons[weapindx].name[3],
00912                                                           GetMechToMechID(mech, target), buf2));
00913 
00914                         sendC3TrackEmit(mech, c3Ref, c3Mech);
00915 
00916                         mech_printf(mech, MECHALL,
00917                                                 "You aim %s at %s%s - BTH: %d %s",
00918                                                 &MechWeapons[weapindx].name[3],
00919                                                 GetMechToMechID(mech, target), buf2,
00920                                                 baseToHit,
00921                                                 MechStatus(target) & PARTIAL_COVER ?
00922                                                 "(Partial cover)" : "");
00923                         return;
00924                 }
00925                 if(baseToHit > 12) {
00926                         DOCHECKMA(baseToHit >= 900,
00927                                           tprintf("Fire %s at %s%s - Out of range.",
00928                                                           &MechWeapons[weapindx].name[3],
00929                                                           GetMechToMechID(mech, target), buf2));
00930                         mech_printf(mech, MECHALL,
00931                                                 "Fire %s at %s%s - BTH: %d  Roll: Impossible! %s",
00932                                                 &MechWeapons[weapindx].name[3],
00933                                                 GetMechToMechID(mech, target), buf2, baseToHit,
00934                                                 MechStatus(target) & PARTIAL_COVER ?
00935                                                 "(Partial cover)" : "");
00936                         return;
00937                 }
00938         } else {
00939                 /* Hex target sight info */
00940                 if(sight) {
00941                         if(baseToHit > 900)
00942                                 mech_printf(mech, MECHPILOT,
00943                                                         "You aim your %s at (%d,%d) - Out of Range.",
00944                                                         &MechWeapons[weapindx].name[3], mapx, mapy);
00945                         else {
00946                                 sendC3TrackEmit(mech, c3Ref, c3Mech);
00947 
00948                                 mech_printf(mech, MECHPILOT,
00949                                                         "You aim your %s at (%d,%d) - BTH: %d",
00950                                                         &MechWeapons[weapindx].name[3], mapx,
00951                                                         mapy, baseToHit);
00952                         }
00953 
00954                         return;
00955                 }
00956                 if(!isarty && baseToHit > 12) {
00957                         mech_printf(mech, MECHALL,
00958                                                 "Fire %s at (%d,%d) - BTH: %d  Roll: Impossible!",
00959                                                 &MechWeapons[weapindx].name[3], mapx, mapy,
00960                                                 baseToHit);
00961                         return;
00962                 }
00963         }
00964 
00965         if(target && !ishex) {
00966                 sendC3TrackEmit(mech, c3Ref, c3Mech);
00967 
00968                 mech_printf(mech, MECHALL,
00969                                         "You fire %s at %s%s - BTH: %d  %s%s",
00970                                         &MechWeapons[weapindx].name[3],
00971                                         GetMechToMechID(mech, target), buf2, baseToHit,
00972                                         buf,
00973                                         MechStatus(target) & PARTIAL_COVER ?
00974                                         "(Partial cover)" : "");
00975 
00976                 /* Switching to Exile method of tracking xp, where we split
00977                  * Attacking and Piloting xp into two different channels
00978                  * And since this is neither it goes to its own channel
00979                  */
00980                 SendAttacks(tprintf("#%i attacks #%i (weapon) (%i/%i)",
00981                                                         mech->mynum, target->mynum, baseToHit, roll));
00982 /*    
00983         SendXP(tprintf("#%i attacks #%i (weapon) (%i/%i)", mech->mynum,
00984             target->mynum, baseToHit, roll));
00985 */
00986                 /* If the target has the ATTACKEMIT_MECH flag on have it
00987                  * output this info as well
00988                  */
00989                 if(MechStatus2(target) & ATTACKEMIT_MECH)
00990                         SendAttackEmits(tprintf("#%i attacks #%i (weapon) (%i/%i)",
00991                                                                         mech->mynum, target->mynum, baseToHit,
00992                                                                         roll));
00993 
00994         } else {
00995                 sendC3TrackEmit(mech, c3Ref, c3Mech);
00996 
00997                 mech_printf(mech, MECHALL,
00998                                         "You fire %s %s (%d,%d) - BTH: %d  %s",
00999                                         &MechWeapons[weapindx].name[3],
01000                                         hex_target_id(mech), mapx, mapy, baseToHit, buf);
01001 
01002                 /* Switching to Exile method of tracking xp, where we split
01003                  * Attacking and Piloting xp into two different channels
01004                  * And since this is neither it goes to its own channel
01005                  */
01006                 SendAttacks(tprintf("#%i attacks %d,%d (%s) (weapon) (%i/%i)",
01007                                                         mech->mynum, mapx, mapy, short_hextarget(mech),
01008                                                         baseToHit, roll));
01009 /*
01010         SendXP(tprintf("#%i attacks %d,%d (%s) (weapon) (%i/%i)",
01011             mech->mynum, mapx, mapy, short_hextarget(mech),
01012             baseToHit, roll));
01013 */
01014 
01015                 /* Big Block of code here. Basicly it checks all the targets
01016                  * in the hex the attacker is firing at. For each one that
01017                  * has ATTACKEMIT_MECH set, it broadcasts that info
01018                  */
01019                 {
01020                         MECH *tmpmech;
01021                         int foo;
01022 
01023                         for(foo = 0; foo < mech_map->first_free; foo++) {
01024 
01025                                 if(mech_map->mechsOnMap[foo] >= 0) {
01026 
01027                                         if(!(tmpmech = getMech(mech_map->mechsOnMap[foo])))
01028                                                 continue;
01029                                         if(mech->mynum == tmpmech->mynum)
01030                                                 continue;
01031                                         if(MechX(tmpmech) != mapx && MechY(tmpmech) != mapy)
01032                                                 continue;
01033                                         if(MechStatus2(tmpmech) & ATTACKEMIT_MECH)
01034                                                 SendAttackEmits(tprintf
01035                                                                                 ("#%i attacks %d,%d (%s) (weapon)"
01036                                                                                  " (%i/%i)", mech->mynum, mapx, mapy,
01037                                                                                  short_hextarget(mech), baseToHit,
01038                                                                                  roll));
01039                                 }
01040 
01041                         }
01042 
01043                 }
01044 
01045         }
01046 
01047                 /****************************************
01048         * END: Do all the necessary emits for sighting and firing
01049         ****************************************/
01050 
01051         /* Check for weapon failures */
01052         if(weapon_failure_stuff(mech, &weapnum, &weapindx, &section,
01053                                                         &critical, &ammoLoc, &ammoCrit, &ammoLoc1,
01054                                                         &ammoCrit1, &modifier, &type, range,
01055                                                         &range_ok, wGattlingShots))
01056                 return;
01057 
01058         /* See if our streaks work */
01059         if(MechWeapons[weapindx].special & STREAK) {
01060                 if(target && (AngelECMDisturbed(mech) || AngelECMProtected(target)))
01061                         mech_notify(mech, MECHALL,
01062                                                 "The ECM confuses your streak homing system!");
01063                 else if(roll < baseToHit) {
01064                         SetRecyclePart(mech, section, critical,
01065                                                    WEAPON_TICK * MechWeapons[weapindx].vrt);
01066                         mech_notify(mech, MECHALL, "Your streak fails to lock on.");
01067                         return;
01068                 }
01069         }
01070 
01071         /* Hotload LRM jams on 2 or 3 */
01072         if(GetPartFireMode(mech, section, critical) & HOTLOAD_MODE) {
01073                 if(roll == 2 || roll == 3) {
01074                         mech_printf(mech, MECHALL,
01075                                                 "%%ch%%crThe ammo loading mechanism jams on your %s!%%cn",
01076                                                 &(MechWeapons[weapindx].name[3]));
01077                         SetPartTempNuke(mech, section, critical, FAIL_AMMOJAMMED);
01078                         return;
01079                 }
01080         }
01081 
01082         /* Caseless jams on a 2. Next internal roll of 8+ explodes */
01083         if(GetPartAmmoMode(mech, section, critical) & AC_CASELESS_MODE) {
01084                 if(roll == 2 || roll == 3) {
01085                         mech_printf(mech, MECHALL,
01086                                         "%%ch%%crThe ammo loading mechanism jams on your %s!%%cn",
01087                                         &(MechWeapons[weapindx].name[3]));
01088                         SetPartTempNuke(mech, section, critical, FAIL_AMMOJAMMED);
01089                         /* do 8+ explosion check. Per tac handbook, the launcher explodes on failure*/
01090                         if(Roll() > 7 ) {
01091                                 /* Rut roh shaggy. Time to cause some damage! */
01092                                 mech_printf(mech, MECHALL,
01093                                                 "%%ch%%crPropellant from your %s ignites and destroys it!%%cn",
01094                                                 &(MechWeapons[weapindx].name[3]));
01095                                 firstCrit = FindFirstWeaponCrit(mech, section, -1, 0, I2Weapon(weapindx), GetWeaponCrits(mech,weapindx));
01096                                 DestroyWeapon(mech, section, I2Weapon(weapindx), firstCrit,
01097                                                 GetWeaponCrits(mech, weapindx), GetWeaponCrits(mech,weapindx));
01098                                 MechLOSBroadcast(mech, "shudders from an internal explosion!");
01099                                 /* Apply damage equal to one shot, follow crits as well */
01100 
01101                                 DamageMech(mech, mech, 0, -1, section, 0, 1, 0,
01102                                                 MechWeapons[weapindx].damage, -1, 0, -1, 0,1);
01103                                 decrement_ammunition(mech, weapindx, section, critical, ammoLoc, ammoCrit,
01104                                                 ammoLoc1, ammoCrit1, wGattlingShots);
01105                                 
01106 
01107                         }
01108                         
01109                         return;
01110                 }
01111         }
01112         
01113         /* Check for RFAC explosion/jams */
01114         if(GetPartFireMode(mech, section, critical) & RFAC_MODE) {
01115                 if(roll == 2) {
01116                         mech_printf(mech, MECHALL,
01117                                                 "%%ch%%crA catastrophic misload on your %s destroys it and causes an internal explosion!%%cn",
01118                                                 &(MechWeapons[weapindx].name[3]));
01119                         firstCrit =
01120                                 FindFirstWeaponCrit(mech, section, -1, 0,
01121                                                                         I2Weapon(weapindx), GetWeaponCrits(mech,
01122                                                                                                                                            weapindx));
01123                         DestroyWeapon(mech, section, I2Weapon(weapindx), firstCrit,
01124                                                   GetWeaponCrits(mech, weapindx), GetWeaponCrits(mech,
01125                                                                                                                                                  weapindx));
01126                         MechLOSBroadcast(mech, "shudders from an internal explosion!");
01127                         DamageMech(mech, mech, 0, -1, section, 0, 0, 0,
01128                                            MechWeapons[weapindx].damage, -1, 0, -1, 0, 1);
01129                         decrement_ammunition(mech, weapindx, section, critical,
01130                                                                  ammoLoc, ammoCrit, ammoLoc1, ammoCrit1,
01131                                                                  wGattlingShots);
01132                         return;
01133                 } else if(roll < 5) {
01134                         mech_printf(mech, MECHALL,
01135                                                 "%%ch%%crThe ammo loader mechanism jams on your %s!%%cn",
01136                                                 &(MechWeapons[weapindx].name[3]));
01137                         SetPartTempNuke(mech, section, critical, FAIL_AMMOJAMMED);
01138                         return;
01139                 }
01140         }
01141 
01142         /* Check for RAC explosion/jams */
01143         if(MechWeapons[weapindx].special & RAC) {
01144                 if(((GetPartFireMode(mech, section, critical) & RAC_TWOSHOT_MODE)
01145                         && (roll == 2)) ||
01146                    ((GetPartFireMode(mech, section, critical) & RAC_FOURSHOT_MODE)
01147                         && (roll <= 3)) ||
01148                    ((GetPartFireMode(mech, section, critical) & RAC_SIXSHOT_MODE)
01149                         && (roll <= 4))) {
01150                         mech_printf(mech, MECHALL,
01151                                                 "%%ch%%crThe ammo loader mechanism jams on your %s!%%cn",
01152                                                 &(MechWeapons[weapindx].name[3]));
01153                         SetPartTempNuke(mech, section, critical, FAIL_AMMOJAMMED);
01154                         return;
01155                 }
01156         }
01157 
01158         /* Check for Ultra explosion/jams */
01159         if(GetPartFireMode(mech, section, critical) & ULTRA_MODE) {
01160                 if(roll == 2) {
01161                         mech_printf(mech, MECHALL,
01162                                                 "The loader jams on your %s, destroying it!",
01163                                                 &(MechWeapons[weapindx].name[3]));
01164                         firstCrit =
01165                                 FindFirstWeaponCrit(mech, section, -1, 0,
01166                                                                         I2Weapon(weapindx), GetWeaponCrits(mech,
01167                                                                                                                                            weapindx));
01168                         DestroyWeapon(mech, section, I2Weapon(weapindx), firstCrit,
01169                                                   GetWeaponCrits(mech, weapindx), GetWeaponCrits(mech,
01170                                                                                                                                                  weapindx));
01171                         return;
01172                 }
01173         }
01174 
01175         /* See if the sucker will explode from damage taken */
01176         if(canWeapExplodeFromDamage(mech, section, critical, roll)) {
01177                 if(IsEnergy(weapindx)) {
01178                         mech_printf(mech, MECHALL,
01179                                                 "%%ch%%crThe damaged charging crystal on your %s overloads!%%cn",
01180                                                 &(MechWeapons[weapindx].name[3]));
01181                 } else {
01182                         mech_printf(mech, MECHALL,
01183                                                 "%%ch%%crThe damaged ammo feed on your %s triggers an internal explosion!%%cn",
01184                                                 &(MechWeapons[weapindx].name[3]));
01185                         decrement_ammunition(mech, weapindx, section, critical,
01186                                                                  ammoLoc, ammoCrit, ammoLoc1, ammoCrit1,
01187                                                                  wGattlingShots);
01188                 }
01189 
01190                 MechLOSBroadcast(mech, "shudders from an internal explosion!");
01191                 firstCrit =
01192                         FindFirstWeaponCrit(mech, section, -1, 0, I2Weapon(weapindx),
01193                                                                 GetWeaponCrits(mech, weapindx));
01194                 DestroyWeapon(mech, section, I2Weapon(weapindx), firstCrit,
01195                                           GetWeaponCrits(mech, weapindx), GetWeaponCrits(mech,
01196                                                                                                                                          weapindx));
01197                 DamageMech(mech, mech, 0, -1, section, 0, 0, 0,
01198                                    MechWeapons[weapindx].damage, -1, 0, -1, 0, 1);
01199 
01200                 return;
01201         }
01202 
01203         /* See if the sucker will jam from damage taken */
01204         if(canWeapJamFromDamage(mech, section, critical, roll)) {
01205                 mech_printf(mech, MECHALL,
01206                                         "%%ch%%crThe ammo loader mechanism jams on your %s!%%cn",
01207                                         &(MechWeapons[weapindx].name[3]));
01208                 SetPartTempNuke(mech, section, critical, FAIL_AMMOCRITJAMMED);
01209 
01210                 return;
01211         }
01212 
01213         /* Trash our cocoon if we're OODing */
01214         if(OODing(mech)) {
01215                 if(MechZ(mech) > MechElevation(mech)) {
01216                         if(MechJumpSpeed(mech) >= MP1) {
01217                                 mech_notify(mech, MECHALL,
01218                                                         "You initiate your jumpjets to compensate for the opened cocoon!");
01219                                 MechCocoon(mech) = -1;
01220                         } else {
01221                                 mech_notify(mech, MECHALL,
01222                                                         "Your action splits open the cocoon - have a nice fall!");
01223                                 MechLOSBroadcast(mech,
01224                                                                  "starts plummeting down, as the cocoon opens!.");
01225                                 MechCocoon(mech) = 0;
01226                                 StopOOD(mech);
01227                                 MECHEVENT(mech, EVENT_FALL, mech_fall_event, FALL_TICK, -1);
01228                         }
01229                 }
01230         }
01231 
01232         /* Better setup some glancing stuff. There will be 3 ways to get hit..
01233          * Glancing_Blows = 0: NO Glancing. ROLL>=BTH=Normal
01234          * Glancing_Blows = 1: MaxTech Glancing. ROLL=BTH=Glance, ROLL>BTH=Normal
01235          * Glancing_Blows = 2: Exile Glancing. ROLL=BTH-1=Glance, ROLL>=BTH=Normal
01236          * We need to do a little handling here. The rest happens over it HitTarget 
01237          */
01238         RbaseToHit = baseToHit;
01239         if(mudconf.btech_glancing_blows == 2)
01240                 RbaseToHit = baseToHit - 1; /* only time we modify it */
01241         
01242         if(!isarty)
01243                 MechFireBroadcast(mech, ishex ? NULL : target, mapx, mapy,
01244                                                   mech_map, &MechWeapons[weapindx].name[3],
01245                                                   (roll >= RbaseToHit) && range_ok);
01246 
01247         /* Tell our target they were just shot at... */
01248         if(target) {
01249                 if(InLineOfSight(target, mech, MechX(mech), MechY(mech), range))
01250                         mech_printf(target, MECHALL,
01251                                                 "%s has fired a %s at you!",
01252                                                 GetMechToMechID(target, mech),
01253                                                 &MechWeapons[weapindx].name[3]);
01254                 else
01255                         mech_printf(target, MECHALL,
01256                                                 "Something has fired a %s at you from bearing %d!",
01257                                                 &MechWeapons[weapindx].name[3],
01258                                                 FindBearing(MechFX(target), MechFY(target),
01259                                                                         MechFX(mech), MechFY(mech)));
01260         }
01261 
01262         /* Time to decide if we've really hit them and wot to do */
01263         MechStatus(mech) |= FIRED;
01264 
01265         if(isarty) {
01266                 artillery_shoot(mech, mapx, mapy, weapindx,
01267                                                 GetPartAmmoMode(mech, section, critical),
01268                                                 baseToHit <= roll);
01269         } else if(range_ok) {
01270                 if(IsMissile(weapindx)) {
01271                         HitTarget(mech, weapindx, section, critical, target, mapx,
01272                                           mapy, LOS, type, modifier,
01273                                  (roll >= RbaseToHit) && range_ok, baseToHit, wGattlingShots, tIsSwarmAttack,
01274                                           roll);
01275                 } else {
01276                         if(roll >= RbaseToHit) {
01277                                 HitTarget(mech, weapindx, section, critical, target, mapx,
01278                                                   mapy, LOS, type, modifier, 1, RbaseToHit,
01279                                                   wGattlingShots, tIsSwarmAttack, roll);
01280                         } else {
01281                                 int tTryClear = 1;
01282 
01283                                 if(target) {
01284                                         if((MechType(target) == CLASS_BSUIT) &&
01285                                            (MechSwarmTarget(target) > -1) &&
01286                                            (altTarget = getMech(MechSwarmTarget(target)))) {
01287 
01288                                                 baseToHit =
01289                                                         FindNormalBTH(mech, mech_map, section,
01290                                                                                   critical, weapindx, range,
01291                                                                                   altTarget, indirectFire, &c3Ref);
01292 
01293                                                 if(roll >= baseToHit) {
01294                                                         mech_notify(altTarget, MECHALL,
01295                                                                                 "The shot hits you instead!");
01296                                                         MechLOSBroadcast(altTarget,
01297                                                                                          "manages to get in the way of the shot instead!");
01298                                                         HitTarget(mech, weapindx, section, critical,
01299                                                                           altTarget, mapx, mapy, LOS, type,
01300                                                                           modifier, 1, baseToHit, wGattlingShots,
01301                                                                           tIsSwarmAttack, roll);
01302 
01303                                                         tTryClear = 0;
01304                                                 } else {
01305                                                         if(Elevation(mech_map, MechX(target),
01306                                                                                  MechY(target)) < (MechZ(target) - 2))
01307                                                                 tTryClear = 0;
01308                                                 }
01309                                         }
01310                                 }
01311 
01312                                 if(tTryClear) {
01313                                         int tempDamage =
01314                                                 determineDamageFromHit(mech, section, critical,
01315                                                                                            target, mapx,
01316                                                                                            mapy, weapindx, wGattlingShots,
01317                                                                                            MechWeapons[weapindx].damage,
01318                                                                                            GetPartAmmoMode(mech, section,
01319                                                                                                                            critical),
01320                                                                                            type,
01321                                                                                            modifier, 1);
01322 
01323                                         possibly_ignite_or_clear(mech, weapindx,
01324                                                                                          GetPartAmmoMode(mech, section,
01325                                                                                                                          critical),
01326                                                                                          tempDamage, mapx, mapy, 0);
01327                                 }
01328                         }
01329                 }
01330         }
01331 
01332         /* Recycle the weapon */
01333         SetRecyclePart(mech, section, critical,
01334                                    WEAPON_TICK * MechWeapons[weapindx].vrt);
01335 
01336                 /****************************************
01337         * START: Set the heat after firing
01338         ****************************************/
01339         if(type == HEAT)
01340                 MechWeapHeat(mech) += (float) modifier;
01341 
01342         if(GetPartFireMode(mech, section, critical) & GATTLING_MODE) {
01343                 MechWeapHeat(mech) += wGattlingShots;
01344         } else if(MechWeapons[weapindx].special & RAC) {
01345                 if(GetPartFireMode(mech, section, critical) & RAC_TWOSHOT_MODE)
01346                         wRACHeat = 2;
01347                 else if(GetPartFireMode(mech, section, critical) & RAC_FOURSHOT_MODE)
01348                         wRACHeat = 4;
01349                 else if(GetPartFireMode(mech, section, critical) & RAC_SIXSHOT_MODE)
01350                         wRACHeat = 6;
01351                 else
01352                         wRACHeat = 1;
01353 
01354                 MechWeapHeat(mech) += (float) (MechWeapons[weapindx].heat * wRACHeat);
01355 
01356                 if(type == HEAT)
01357                         MechWeapHeat(mech) += (float) (modifier * wRACHeat);
01358         } else {
01359                 MechWeapHeat(mech) += (float) MechWeapons[weapindx].heat;
01360 
01361                 if(IsEnergy(weapindx))
01362                         MechWeapHeat(mech) +=
01363                                 (float) getCritAddedHeat(mech, section, critical);
01364 
01365                 if((GetPartFireMode(mech, section, critical) & ULTRA_MODE) ||
01366                    (GetPartFireMode(mech, section, critical) & RFAC_MODE)) {
01367 
01368                         if(type == HEAT)
01369                                 MechWeapHeat(mech) += (float) modifier;
01370 
01371                         MechWeapHeat(mech) += (float) MechWeapons[weapindx].heat;
01372                 }
01373         }
01374 
01375                 /****************************************
01376         * END: Set the heat after firing
01377         ****************************************/
01378 
01379         /* Decrement Ammunition */
01380         decrement_ammunition(mech, weapindx, section, critical, ammoLoc,
01381                                                  ammoCrit, ammoLoc1, ammoCrit1, wGattlingShots);
01382 
01383         /* Special for Heavy Gauss Rifles */
01384         if((MechWeapons[weapindx].special & HVYGAUSS) &&
01385            (MechType(mech) == CLASS_MECH)) {
01386                 if(abs(MechSpeed(mech)) > 0.0) {
01387                         mech_notify(mech, MECHALL,
01388                                                 "You realize that moving while firing this weapon may not be a good idea after all.");
01389                         if(MechTons(mech) <= 35)
01390                                 wHGRPSkillMod = 2;
01391                         else if(MechTons(mech) <= 55)
01392                                 wHGRPSkillMod = 1;
01393                         else if(MechTons(mech) <= 75)
01394                                 wHGRPSkillMod = 0;
01395                         else
01396                                 wHGRPSkillMod = -1;
01397                         if(!MadePilotSkillRoll(mech, wHGRPSkillMod)) {
01398                                 mech_notify(mech, MECHALL,
01399                                                         "The weapon's recoil knocks you to the ground!");
01400                                 MechLOSBroadcast(mech,
01401                                                                  tprintf("topples over from the %s's recoil!",
01402                                                                                  &MechWeapons[weapindx].name[3]));
01403                                 MechFalls(mech, 1, 0);
01404                         }
01405                 }
01406         }
01407 }
01408 
01409 int determineDamageFromHit(MECH * mech,
01410                                                    int wSection,
01411                                                    int wCritSlot,
01412                                                    MECH * hitMech,
01413                                                    int hitX,
01414                                                    int hitY,
01415                                                    int weapindx,
01416                                                    int wGattlingShots,
01417                                                    int wBaseWeapDamage,
01418                                                    int wAmmoMode, int type, int modifier,
01419                                                    int isTempCalc)
01420 {
01421         MAP *mech_map;
01422         float fRange = 0.0;
01423         int wWeapDamage = wBaseWeapDamage;
01424         int wClearDamage = 0;
01425 
01426         /* Find the range to our target */
01427         if(hitMech)
01428                 fRange = FaMechRange(mech, hitMech);
01429         else {
01430                 float fx, fy;
01431                 MapCoordToRealCoord(hitX, hitY, &fx, &fy);
01432                 fRange = FindHexRange(MechFX(mech), MechFY(mech), fx, fy);
01433         }
01434 
01435         /* If our Gattling shots are greater then 0, use that as the damage. */
01436         if(wGattlingShots > 0)
01437                 wWeapDamage = wGattlingShots;
01438 
01439         /* If we're a heavy gauss rifle, damage gets altered by range. */
01440         if(MechWeapons[weapindx].special & HVYGAUSS) {
01441                 if(fRange > MechWeapons[weapindx].medrange)
01442                         wWeapDamage = 10;
01443                 else if(fRange > MechWeapons[weapindx].shortrange)
01444                         wWeapDamage = 20;
01445         }
01446 
01447         wWeapDamage -= getCritSubDamage(mech, wSection, wCritSlot);
01448 
01449         /* See if we're using flechette ammo */
01450         if(hitMech) {
01451                 if(wAmmoMode & AC_FLECHETTE_MODE) {
01452                         if(MechType(hitMech) == CLASS_MW) {
01453                                 if(MechRTerrain(hitMech) == GRASSLAND)
01454                                         wWeapDamage *= 4;
01455                                 else
01456                                         wWeapDamage *= 2;
01457                         } else if(MechType(hitMech) != CLASS_BSUIT)
01458                                 wWeapDamage /= 2;
01459                 }
01460 
01461                 if(wAmmoMode & AC_INCENDIARY_MODE) {
01462                         if(MechType(hitMech) == CLASS_MW)
01463                                 wWeapDamage += 2;
01464                 }
01465         }
01466 
01467         /* Check to see if we have an energy weapon and we're modding the damage based on range */
01468         if(mudconf.btech_moddamagewithrange && IsEnergy(weapindx)) {
01469                 if(fRange <= 1.0)
01470                         wWeapDamage++;
01471                 else {
01472                         if(SectionUnderwater(mech, wSection)) {
01473                                 if(fRange > MechWeapons[weapindx].longrange_water)
01474                                         wWeapDamage = (wWeapDamage / 2);
01475                                 else if(fRange > MechWeapons[weapindx].medrange_water)
01476                                         wWeapDamage--;
01477                         } else {
01478                                 if(fRange > MechWeapons[weapindx].longrange)
01479                                         wWeapDamage = (wWeapDamage / 2);
01480                                 else if(fRange > MechWeapons[weapindx].medrange)
01481                                         wWeapDamage--;
01482                         }
01483                 }
01484         }
01485 
01486         /* Check to see if we're modding the damage based on woods cover */
01487         mech_map = getMap(mech->mapindex);
01488 
01489         /* If there was a damage type failure, mod the damage */
01490         if(type == DAMAGE)
01491                 wWeapDamage -= modifier;
01492 
01493         if(hitMech && !isTempCalc) {
01494                 if(mudconf.btech_moddamagewithwoods &&
01495                    IsForestHex(mech_map, MechX(hitMech), MechY(hitMech)) &&
01496                    ((MechZ(hitMech) - 2) <= Elevation(mech_map, MechX(hitMech),
01497                                                                                           MechY(hitMech)))) {
01498                         wClearDamage = wWeapDamage;
01499 
01500                         if(GetRTerrain(mech_map, MechX(hitMech),
01501                                                    MechY(hitMech)) == LIGHT_FOREST)
01502                                 wWeapDamage -= 2;
01503                         else if(GetRTerrain(mech_map, MechX(hitMech),
01504                                                                 MechY(hitMech)) == HEAVY_FOREST)
01505                                 wWeapDamage -= 4;
01506 
01507                         mech_notify(mech, MECHALL, "The woods absorb some of your shot!");
01508                         mech_notify(hitMech, MECHALL,
01509                                                 "The woods absorb some of the damage!");
01510 
01511                         possibly_ignite_or_clear(mech, weapindx, wAmmoMode,
01512                                                                          wClearDamage, MechX(hitMech),
01513                                                                          MechY(hitMech), 1);
01514                 }
01515         }
01516 
01517         if(wWeapDamage <= 0)
01518                 wWeapDamage = 1;
01519 
01520         return wWeapDamage;
01521 }
01522 
01523 void HitTarget(MECH * mech,
01524                            int weapindx,
01525                            int wSection,
01526                            int wCritSlot,
01527                            MECH * hitMech,
01528                            int hitX,
01529                            int hitY,
01530                            int LOS,
01531                            int type,
01532                            int modifier,
01533                            int reallyhit, int bth, int wGattlingShots, int tIsSwarmAttack,
01534                            int player_roll)
01535 {
01536         int isrear = 0, iscritical = 0;
01537         int hitloc = 0;
01538         int loop;
01539         int roll;
01540         int aim_hit = 0;
01541         int wBaseWeapDamage = MechWeapons[weapindx].damage;
01542         int wWeapDamage = 0;
01543         int num_missiles_hit;
01544         int wFireMode = GetPartFireMode(mech, wSection, wCritSlot);
01545         int wAmmoMode = GetPartAmmoMode(mech, wSection, wCritSlot);
01546         int tIsUltra = ((wFireMode & ULTRA_MODE) || (wFireMode & RFAC_MODE));
01547         int tIsRAC = (wFireMode & RAC_MODES);
01548         int tIsLBX = (wAmmoMode & LBX_MODE);
01549         int tIsSwarm = ((wAmmoMode & SWARM_MODE) || (wAmmoMode & SWARM1_MODE));
01550         char strMissileFakeName[30];
01551         int tFoundRACFake = 0;
01552         int tUsingTC = ((wFireMode & ON_TC) && !IsArtillery(weapindx) &&
01553                                         !IsMissile(weapindx) &&
01554                                         (!(MechCritStatus(mech) & TC_DESTROYED)) &&
01555                                         ((MechAim(mech) != NUM_SECTIONS) && hitMech &&
01556                                          (MechAimType(mech) == MechType(hitMech))));
01557         int missileindex = 0;
01558 
01559         if(hitMech) {
01560 
01561                 /* Check to see if we're aiming at a particular location. Swarm attacks can't aim. */
01562                 if((MechAim(mech) != NUM_SECTIONS) && hitMech && Immobile(hitMech)
01563                    && !tIsSwarmAttack) {
01564 
01565                         roll = Roll();
01566 
01567                         if(roll == 6 || roll == 7 || roll == 8)
01568                                 aim_hit = 1;
01569                 }
01570 
01571         }
01572 
01573         if(!IsMissile(weapindx)) {
01574                 wWeapDamage =
01575                         determineDamageFromHit(mech, wSection, wCritSlot, hitMech, hitX,
01576                                                                    hitY, weapindx, wGattlingShots,
01577                                                                    wBaseWeapDamage, wAmmoMode, type, modifier,
01578                                                                    0);
01579 
01580                 /* Check if it is a glancing blow, if so, make an emit */
01581                 if((mudconf.btech_glancing_blows) && (player_roll == bth) && hitMech) {
01582                         /* Yes, even though we have two different glance modes, the above is correct
01583                          * because we modified the bth in FireWeapon. Nothing to see here. move along
01584                          */
01585                         MechLOSBroadcast(hitMech, "is nicked by a glancing blow!");
01586                         mech_notify(hitMech, MECHALL, 
01587                                         "You are nicked by a glancing blow!");
01588                         wWeapDamage = (int ) (wWeapDamage +1) / 2 ;
01589                         if(wWeapDamage < 1 )
01590                                 wWeapDamage = 1; /* very rare case */
01591                 }
01592         }
01593 
01594         /*
01595          * Ok, if we're not an artillery weapon or missile and we're not in
01596          * LBX, RAC, Ultra or RFAC mode...
01597          */
01598         if(!IsArtillery(weapindx) && !IsMissile(weapindx) && !tIsUltra &&
01599            !tIsLBX && !tIsRAC) {
01600 
01601                 if(hitMech) {
01602 
01603                         /* Flamers - if in heat mode don't do damage */
01604                         if((IsFlamer(weapindx)) && (wFireMode & HEAT_MODE)) {
01605 
01606                                 mech_notify(hitMech, MECHALL,
01607                                                         "%cy%chThe flaming plasma sprays all over you!%cn");
01608                                 mech_notify(mech, MECHALL,
01609                                                         "%cgYou cover your target in flaming plasma!%cn");
01610                                 MechWeapHeat(hitMech) += (float) wBaseWeapDamage;
01611                                 return;
01612 
01613                         } else if((IsCoolant(weapindx))
01614                                           && (MechType(hitMech) != CLASS_MW)) {
01615 
01616                                 /* Its a Coolant Gun */
01617                                 /* So now we figure out if we want to hit our unit with it
01618                                  * or a target */
01619 
01620                                 if(wFireMode & HEAT_MODE) {
01621 
01622                                         /* Hit our own unit with the coolant gun */
01623                                         mech_notify(mech, MECHALL,
01624                                                                 "%ccCoolant washes over your systems!!%c");
01625                                         MechWeapHeat(mech) -= (float) wBaseWeapDamage;
01626 
01627                                 } else {
01628 
01629                                         /* Hit the target with the coolant gun */
01630                                         mech_notify(mech, MECHALL,
01631                                                                 "%ccYou hit with the stream of coolant!!%c");
01632                                         mech_notify(hitMech, MECHALL,
01633                                                                 "%ccCoolant washes over your systems!!%c");
01634                                         MechWeapHeat(hitMech) -= (float) wBaseWeapDamage;
01635 
01636                                 }
01637 
01638                                 /* Never does damage so return */
01639                                 return;
01640 
01641                         }
01642 
01643                         if(aim_hit)
01644                                 hitloc = FindAimHitLoc(mech, hitMech, &isrear, &iscritical);
01645                         else if(tUsingTC)
01646                                 hitloc = FindTCHitLoc(mech, hitMech, &isrear, &iscritical);
01647                         else
01648                                 hitloc =
01649                                         FindTargetHitLoc(mech, hitMech, &isrear, &iscritical);
01650 
01651                         DamageMech(hitMech, mech, LOS, GunPilot(mech), hitloc, isrear,
01652                                            iscritical, pc_to_dam_conversion(hitMech, weapindx,
01653                                                                                                                 wWeapDamage), 0,
01654                                            weapindx, bth, weapindx, wAmmoMode, tIsSwarmAttack);
01655 
01656                 } else {
01657                         hex_hit(mech, hitX, hitY, weapindx, wAmmoMode, wWeapDamage, 1);
01658                 }
01659 
01660                 return;
01661         }
01662 
01663         /*
01664          * Since we're here, we're either
01665          *      - A missile weapon
01666          *      - An artillery weapon
01667          *      - An AC in Ultra, RF or LBX mode
01668          *      - A RAC in RAC mode
01669          */
01670 
01671         /*
01672          * Do special case for RACs since they don't have an entry in the MissileHitTable.
01673          *
01674          * We're gonna fake it by pretending we're either an SRM-2, SRM-4 or SRM-6, depending
01675          * upon the mode
01676          */
01677         if(tIsRAC) {
01678                 if(GetPartFireMode(mech, wSection, wCritSlot) & RAC_TWOSHOT_MODE)
01679                         strcpy(strMissileFakeName, "IS.SRM-2");
01680                 else if(GetPartFireMode(mech, wSection,
01681                                                                 wCritSlot) & RAC_FOURSHOT_MODE)
01682                         strcpy(strMissileFakeName, "IS.SRM-4");
01683                 else if(GetPartFireMode(mech, wSection, wCritSlot) & RAC_SIXSHOT_MODE)
01684                         strcpy(strMissileFakeName, "IS.SRM-6");
01685                 for(loop = 0; MissileHitTable[loop].key != -1; loop++) {
01686                         if(!strcmp(MissileHitTable[loop].name, strMissileFakeName)) {
01687                                 tFoundRACFake = 1;
01688                                 break;
01689                         }
01690                 }
01691 
01692                 if(!tFoundRACFake)
01693                         return;
01694         } else {
01695                 for(loop = 0; MissileHitTable[loop].key != -1; loop++)
01696                         if(MissileHitTable[loop].key == weapindx)
01697                                 break;
01698                 if(!(MissileHitTable[loop].key == weapindx))
01699                         return;
01700         }
01701 
01702         if(IsMissile(weapindx)) {
01703                 if(player_roll < bth )
01704                 {
01705                         return;
01706                 }
01707                 else
01708 
01709                 if(tIsSwarm && hitMech) /* No swarms on hex hits */
01710                         SwarmHitTarget(mech, weapindx, wSection, wCritSlot, hitMech,
01711                                                    LOS, bth, reallyhit ? bth + 1 : bth - 1,
01712                                                    (type ==
01713                                                         CRAZY_MISSILES) ? MissileHitTable[loop].
01714                                                    num_missiles[10] * modifier /
01715                                                    100 : MissileHitTable[loop].num_missiles[10],
01716                                                    (GetPartAmmoMode(mech, wSection, wCritSlot) &
01717                                                         SWARM1_MODE), tIsSwarmAttack, player_roll);
01718                 else
01719                         MissileHitTarget(mech, weapindx, wSection, wCritSlot, hitMech,
01720                                                          hitX, hitY, LOS ? 1 : 0, bth,
01721                                                          reallyhit ? bth + 1 : bth - 1,
01722                                                          (type ==
01723                                                           CRAZY_MISSILES) ? MissileHitTable[loop].
01724                                                          num_missiles[10] * modifier /
01725                                                          100 : MissileHitTable[loop].num_missiles[10],
01726                                                          tIsSwarmAttack, player_roll);
01727 
01728                 return;
01729         }
01730 
01731         missileindex = MissileHitIndex(mech, hitMech,weapindx, wSection, wCritSlot, (mudconf.btech_glancing_blows) && (player_roll == bth) ? 1: 0);
01732         /* This is how we'll handle glancing. Any roll < 2 is considering just one missile hit, full damage */
01733         if (missileindex == -1)
01734                 num_missiles_hit = 1;
01735         else
01736                 num_missiles_hit = MissileHitTable[loop].num_missiles[missileindex];
01737 
01738         /*
01739          * Check for non-missile, multiple hit weapons, like LBXs, RACs, RFACs and Ultras
01740          */
01741         if(LOS)
01742                 mech_printf(mech, MECHALL,
01743                                         "%%cgYou hit with %d %s%s!%%c",
01744                                         num_missiles_hit, (tIsUltra ||
01745                                                                            tIsRAC ? "slug" : tIsLBX ? "pellet" :
01746                                                                            "missile"),
01747                                         (num_missiles_hit > 1 ? "s" : ""));
01748 
01749         if(tIsLBX)
01750                 Missile_Hit(mech, hitMech, hitX, hitY, isrear, iscritical,
01751                                         weapindx, wFireMode, wAmmoMode, num_missiles_hit,
01752                                         tIsLBX ? 1 : wWeapDamage, Clustersize(weapindx), LOS, bth,
01753                                         tIsSwarmAttack);
01754         else {
01755                 while (num_missiles_hit) {
01756                         if(hitMech) {
01757                                 if(aim_hit)
01758                                         hitloc =
01759                                                 FindAimHitLoc(mech, hitMech, &isrear, &iscritical);
01760                                 if(tUsingTC)
01761                                         hitloc =
01762                                                 FindTCHitLoc(mech, hitMech, &isrear, &iscritical);
01763                                 else
01764                                         hitloc =
01765                                                 FindTargetHitLoc(mech, hitMech, &isrear, &iscritical);
01766                                 DamageMech(hitMech, mech, LOS, GunPilot(mech), hitloc,
01767                                                    isrear, iscritical, pc_to_dam_conversion(hitMech,
01768                                                                                                                                         weapindx,
01769                                                                                                                                         wWeapDamage),
01770                                                    0, weapindx, bth, weapindx, wAmmoMode,
01771                                                    tIsSwarmAttack);
01772                         } else
01773                                 hex_hit(mech, hitX, hitY, weapindx, wAmmoMode, wWeapDamage,
01774                                                 1);
01775 
01776                         num_missiles_hit--;
01777                 }
01778         }
01779 
01780 }
01781 
01782 /****************************************
01783  * Start: Hex hitting related functions
01784  ****************************************/
01785 
01786 char *hex_target_id(MECH * mech)
01787 {
01788         if(MechStatus(mech) & LOCK_HEX_IGN)
01789                 return "at the hex, trying to ignite it";
01790         if(MechStatus(mech) & LOCK_HEX_CLR)
01791                 return "at the hex, trying to clear it";
01792         if(MechStatus(mech) & LOCK_HEX)
01793                 return "at the hex";
01794         if(MechStatus(mech) & LOCK_BUILDING)
01795                 return "at the building at";
01796         return "at";
01797 }
01798 
01799 /*
01800 intentional:
01801     if ignite attack hits, roll 2D6 and consult table:
01802         (Success Numbers) Weapon Type:
01803             Flamer 4+,
01804             Incendiary LRMs 5+,
01805             Energy Weapon (minus small lasers) 7+,
01806             Missile or Ballistic (minus GR or SRM2s) 9+
01807 
01808 Modifiers for terrain to those bths are as follows:
01809     Woods/Light Buildings no bth mod,
01810     Med Bldg +1,
01811     Heavy Bldg +2,
01812     Hardened Building +3
01813 
01814 A unit attempting to clear a wooded hex runs the risk of setting fire to the woods accidently.
01815 To represent this risk, the player rolls 2D6 before attempting to clear.
01816 If the result is 5 or less, then the woods catch on fire.
01817 
01818 If a weapon attack against a unit occupying a wooded hex misses its target and
01819 the weapon can be used to start fires (weapons listed above),
01820 the attacking player rolls 2D6. On a result of 2 or 3, the hex catches fire.
01821 Buildings can't accidently be set on fire.
01822 
01823 */
01824 
01825 int canWeaponIgnite(int weapindx)
01826 {
01827         if(strcmp(&MechWeapons[weapindx].name[3], "ERSmallLaser") &&
01828            strcmp(&MechWeapons[weapindx].name[3], "SmallLaser") &&
01829            strcmp(&MechWeapons[weapindx].name[3], "SmallPulseLaser") &&
01830            strcmp(&MechWeapons[weapindx].name[3], "X-SmallPulseLaser") &&
01831            strcmp(&MechWeapons[weapindx].name[3], "ERSmallPulseLaser") &&
01832            strcmp(&MechWeapons[weapindx].name[3], "HeavySmallLaser") &&
01833            strcmp(&MechWeapons[weapindx].name[3], "GaussRifle") &&
01834            strcmp(&MechWeapons[weapindx].name[3], "LightGaussRifle") &&
01835            strcmp(&MechWeapons[weapindx].name[3], "HeavyGaussRifle") &&
01836            strcmp(&MechWeapons[weapindx].name[3], "MagshotGaussRifle") &&
01837            strcmp(&MechWeapons[weapindx].name[3], "MachineGun") &&
01838            strcmp(&MechWeapons[weapindx].name[3], "LightMachineGun") &&
01839            strcmp(&MechWeapons[weapindx].name[3], "HeavyMachineGun") &&
01840            strcmp(&MechWeapons[weapindx].name[3], "StreakSRM-2") &&
01841            strcmp(&MechWeapons[weapindx].name[3], "SRM-2") &&
01842            strcmp(&MechWeapons[weapindx].name[3], "NarcBeacon") &&
01843            strcmp(&MechWeapons[weapindx].name[3], "iNarcBeacon"))
01844                 return 1;
01845 
01846         return 0;
01847 }
01848 
01849 int canWeaponClear(int weapindx)
01850 {
01851         if(strcmp(&MechWeapons[weapindx].name[3], "ERSmallLaser") &&
01852            strcmp(&MechWeapons[weapindx].name[3], "SmallLaser") &&
01853            strcmp(&MechWeapons[weapindx].name[3], "SmallPulseLaser") &&
01854            strcmp(&MechWeapons[weapindx].name[3], "X-SmallPulseLaser") &&
01855            strcmp(&MechWeapons[weapindx].name[3], "ERSmallPulseLaser") &&
01856            strcmp(&MechWeapons[weapindx].name[3], "HeavySmallLaser") &&
01857            strcmp(&MechWeapons[weapindx].name[3], "MachineGun") &&
01858            strcmp(&MechWeapons[weapindx].name[3], "LightMachineGun") &&
01859            strcmp(&MechWeapons[weapindx].name[3], "HeavyMachineGun") &&
01860            strcmp(&MechWeapons[weapindx].name[3], "AC/2") &&
01861            strcmp(&MechWeapons[weapindx].name[3], "UltraAC/2") &&
01862            strcmp(&MechWeapons[weapindx].name[3], "CaselessAC/2") &&
01863            strcmp(&MechWeapons[weapindx].name[3], "HyperAC/2") &&
01864            strcmp(&MechWeapons[weapindx].name[3], "LightAC/2") &&
01865            strcmp(&MechWeapons[weapindx].name[3], "RotaryAC/2") &&
01866            strcmp(&MechWeapons[weapindx].name[3], "LB2-XAC") &&
01867            strcmp(&MechWeapons[weapindx].name[3], "AC/5") &&
01868            strcmp(&MechWeapons[weapindx].name[3], "UltraAC/5") &&
01869            strcmp(&MechWeapons[weapindx].name[3], "CaselessAC/5") &&
01870            strcmp(&MechWeapons[weapindx].name[3], "HyperAC/5") &&
01871            strcmp(&MechWeapons[weapindx].name[3], "LightAC/5") &&
01872            strcmp(&MechWeapons[weapindx].name[3], "RotaryAC/5") &&
01873            strcmp(&MechWeapons[weapindx].name[3], "LB5-XAC") &&
01874            strcmp(&MechWeapons[weapindx].name[3], "StreakSRM-2") &&
01875            strcmp(&MechWeapons[weapindx].name[3], "SRM-2"))
01876                 return 1;
01877 
01878         return 0;
01879 }
01880 
01881 void possibly_ignite(MECH * mech, MAP * map, int weapindx, int ammoMode,
01882                                          int x, int y, int intentional)
01883 {
01884         char terrain = GetTerrain(map, x, y);
01885         int roll = Roll();
01886         int bth = 13;
01887 
01888         if(MechWeapons[weapindx].special & PCOMBAT)
01889                 return;
01890 
01891         if((terrain != LIGHT_FOREST) && (terrain != HEAVY_FOREST))
01892                 return;
01893 
01894         if(!strcmp(&MechWeapons[weapindx].name[3], "Flamer") ||
01895            !strcmp(&MechWeapons[weapindx].name[3], "HeavyFlamer"))
01896                 bth = 4;
01897         else if(IsMissile(weapindx) && (ammoMode & INFERNO_MODE))
01898                 bth = 5;
01899         else if(IsBallistic(weapindx) && (ammoMode & AC_FLECHETTE_MODE))
01900                 bth = 5;
01901         else if(IsEnergy(weapindx) && canWeaponIgnite(weapindx))
01902                 bth = 5;
01903         else if((IsMissile(weapindx) || IsBallistic(weapindx)) &&
01904                         canWeaponIgnite(weapindx))
01905                 bth = 9;
01906 
01907         if(roll >= bth)
01908                 fire_hex(mech, x, y, intentional);
01909 }
01910 
01911 void possibly_clear(MECH * mech, MAP * map, int weapindx, int ammoMode,
01912                                         int damage, int x, int y, int intentional)
01913 {
01914         int igniteBTH = 5;                      /* This is for intentional clearing */
01915         int igniteRoll = Roll();
01916         int clearRoll = Roll();
01917 
01918         if(MechWeapons[weapindx].special & PCOMBAT)
01919                 return;
01920 
01921         if(!intentional)
01922                 igniteBTH = 3;
01923 
01924         if(igniteRoll <= igniteBTH) {
01925                 possibly_ignite(mech, map, weapindx, ammoMode, x, y, intentional);
01926                 return;
01927         }
01928 
01929         if(!canWeaponClear(weapindx))
01930                 return;
01931 
01932         if(clearRoll > damage)
01933                 return;
01934 
01935         clear_hex(mech, x, y, intentional);
01936         possibly_remove_mines(mech, x, y);
01937 }
01938 
01939 void possibly_ignite_or_clear(MECH * mech, int weapindx, int ammoMode,
01940                                                           int damage, int x, int y, int intentional)
01941 {
01942         int r;
01943         MAP *map;
01944 
01945         r = Roll();
01946         map = FindObjectsData(mech->mapindex);
01947 
01948         if(!map)
01949                 return;
01950 
01951         if(MechStatus(mech) & LOCK_HEX_IGN) {
01952                 possibly_ignite(mech, map, weapindx, ammoMode, x, y, 1);
01953                 return;
01954         }
01955 
01956         if(MechStatus(mech) & LOCK_HEX_CLR) {
01957                 possibly_clear(mech, map, weapindx, ammoMode, damage, x, y, 1);
01958                 return;
01959         }
01960 
01961         possibly_clear(mech, map, weapindx, ammoMode, damage, x, y, intentional);
01962 }
01963 
01964 void hex_hit(MECH * mech, int x, int y, int weapindx,
01965                          int ammoMode, int damage, int ishit)
01966 {
01967         if(!(MechStatus(mech) & (LOCK_BUILDING | LOCK_HEX | LOCK_HEX_IGN |
01968                                                          LOCK_HEX_CLR)))
01969                 return;
01970 
01971         /* Ok.. we either try to clear/ignite the hex, or alternatively we try to hit building in it */
01972         if(MechStatus(mech) & LOCK_BUILDING) {
01973                 if(ishit > 0)
01974                         hit_building(mech, x, y, weapindx, damage);
01975         } else {
01976                 possibly_ignite_or_clear(mech, weapindx, ammoMode, damage, x, y, 1);
01977 
01978                 if(MechStatus(mech) & LOCK_HEX) {
01979                         possibly_blow_ice(mech, weapindx, x, y);
01980                         possibly_blow_bridge(mech, weapindx, x, y);
01981                 }
01982         }
01983 }
01984 
01985 /****************************************
01986  * End: Hex hitting related functions
01987  ****************************************/

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