/* * Author: Markus Stenberg <fingon@iki.fi> * * Copyright (c) 1996 Markus Stenberg * Copyright (c) 1998-2002 Thomas Wouters * Copyright (c) 2000-2002 Cord Awtry * Copyright (c) 1999-2005 Kevin Stevens * Copyright (c) 2005-2006 Gregory Taylor * All rights reserved */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <sys/file.h> #include "mech.h" #include "map.h" #include "mech.events.h" #include "mech.physical.h" #include "p.mech.physical.h" #include "p.mech.combat.h" #include "p.mech.damage.h" #include "p.mech.utils.h" #include "p.mech.los.h" #include "p.mech.hitloc.h" #include "p.bsuit.h" #include "p.btechstats.h" #include "p.template.h" #include "p.mech.bth.h" // Only allows arm physical attacks for CLASS_MECH. #define ARM_PHYS_CHECK(a) \ DOCHECK(MechType(mech) == CLASS_MW || MechType(mech) == CLASS_BSUIT, \ tprintf("You cannot %s without a 'mech!", a)); \ DOCHECK(MechType(mech) != CLASS_MECH, \ tprintf("You cannot %s with this vehicle!", a)); // Checks a unit's legs for kicking. #define GENERIC_CHECK(a,wDeadLegs) \ ARM_PHYS_CHECK(a);\ DOCHECK(!MechIsQuad(mech) && (wDeadLegs > 1), \ "Without legs? Are you kidding?");\ DOCHECK(!MechIsQuad(mech) && (wDeadLegs > 0),\ "With one leg? Are you kidding?");\ DOCHECK(wDeadLegs > 1,"It'd unbalance you too much in your condition..");\ DOCHECK(wDeadLegs > 2, "Exactly _what_ are you going to kick with?"); // If it's a quad, we can't play with sharp things (Swords, Axes, etc.) #define QUAD_CHECK(a) \ DOCHECK(MechType(mech) == CLASS_MECH && MechIsQuad(mech), \ tprintf("What are you going to %s with, your front right leg?", a)) /** * Checks to see if all limbs have recycled from any previous physical attacks. */ int all_limbs_recycled(MECH * mech) { if(MechSections(mech)[LARM].recycle || MechSections(mech)[RARM].recycle) { mech_notify(mech, MECHALL, "You still have arms recovering from another attack."); return 0; } if(MechSections(mech)[RLEG].recycle || MechSections(mech)[LLEG].recycle) { mech_notify(mech, MECHALL, "Your legs are still recovering from your last attack."); return 0; } // Fall through to success. return 1; } // end all_limbs_recycled() /** * Returns the correct verb for each physical attack. */ char *phys_form(int AttackType, int add_s) { // Holds our attack verb. char *verb; // See if we need the verb with an s on the end. if(add_s) { // With the S. switch (AttackType) { case PA_PUNCH: verb = "punchs"; break; case PA_CLUB: verb = "clubs"; break; case PA_MACE: verb = "maces"; break; case PA_SWORD: verb = "chops"; break; case PA_AXE: verb = "axes"; break; case PA_KICK: verb = "kicks"; break; case PA_TRIP: verb = "trips"; break; case PA_SAW: verb = "saws"; break; // Ohboy, we're using some funky, unknown physical. default: verb = "??bugs??"; } // end switch() } else { // Without the S. switch (AttackType) { case PA_PUNCH: verb = "punch"; break; case PA_CLUB: verb = "club"; break; case PA_MACE: verb = "maces"; break; case PA_SWORD: verb = "chop"; break; case PA_AXE: verb = "axe"; break; case PA_KICK: verb = "kick"; break; case PA_TRIP: verb = "trip"; break; case PA_SAW: verb = "saw"; break; // Ohboy, we're using some funky, unknown physical. default: verb = "??bugs??"; } // end switch() } // end if/else() return verb; } // end phys_form #define phys_message(txt) \ MechLOSBroadcasti(mech,target,txt) void phys_succeed(MECH * mech, MECH * target, int at) { phys_message(tprintf("%s %%s!", phys_form(at, 1))); } void phys_fail(MECH * mech, MECH * target, int at) { phys_message(tprintf("attempts to %s %%s!", phys_form(at, 0))); } /* * All 'mechs with arms can punch. */ static int have_punch(MECH * mech, int loc) { return 1; } /** * Does our unit have an axe? */ int have_axe(MECH * mech, int loc) { return FindObj(mech, loc, I2Special(AXE)) >= (MechTons(mech) / 15); } /** * Does our unit have a dual_saw? */ int have_saw(MECH * mech, int loc) { return FindObj(mech, loc, I2Special(DUAL_SAW)) >= 7; } /** * Does our unit have a sword? */ int have_sword(MECH * mech, int loc) { return FindObj(mech, loc, I2Special(SWORD)) >= ((MechTons(mech) + 15) / 20); } /** * Does our unit have a mace? */ int have_mace(MECH * mech, int loc) { return FindObj(mech, loc, I2Special(MACE)) >= (MechTons(mech) / 10); } /* * Carry out some checks common to all types of physical attacks. */ int phys_common_checks(MECH * mech) { if(Jumping(mech)) { mech_notify(mech, MECHALL, "You can't perform physical attacks while in the air!"); return 0; } if(Standing(mech)) { mech_notify(mech, MECHALL, "You are still trying to stand up!"); return 0; } #ifdef BT_MOVEMENT_MODES if(Dodging(mech) || MoveModeLock(mech)) { mech_notify(mech, MECHALL, "You cannot use physicals while using a special movement mode."); return 0; } #endif if(!all_limbs_recycled(mech)) { return 0; } // Fall through to success. return 1; } // end phys_common_checks() /* * Parse a physical attack command's arguments that allow an arm or both * to be specified. eg. AXE [B|L|R] [ID] */ static int get_arm_args(int *using, int *argc, char ***args, MECH * mech, int (*have_fn) (MECH * mech, int loc), char *weapon) { if(*argc != 0 && args[0][0][0] != '\0' && args[0][0][1] == '\0') { char arm = toupper(args[0][0][0]); // Determine which flag we're dealing with (Both, Left, Right) switch (arm) { case 'B': *using = P_LEFT | P_RIGHT; --*argc; ++*args; break; case 'L': *using = P_LEFT; --*argc; ++*args; break; case 'R': *using = P_RIGHT; --*argc; ++*args; } // end switch() } // end if() // Check for the presence of specified arms, or pick one. *using set in // the above switch statement. switch (*using) { case P_LEFT: if(!have_fn(mech, LARM)) { mech_printf(mech, MECHALL, "You don't have %s in your left arm!", weapon); return 1; } break; case P_RIGHT: if(!have_fn(mech, RARM)) { mech_printf(mech, MECHALL, "You don't have %s in your right arm!", weapon); return 1; } break; case P_LEFT | P_RIGHT: if(!have_fn(mech, LARM)) *using &= ~P_LEFT; if(!have_fn(mech, RARM)) *using &= ~P_RIGHT; break; } // end switch() // Fall through to success. return 0; } // end get_arm_args() /** * Performs some generic checks for arms to punch with. */ int punch_checkArm(MECH * mech, int arm) { char *arm_used = (arm == LARM ? "left" : "right"); if(SectIsDestroyed(mech, arm)) { mech_printf(mech, MECHALL, "Your %s arm is destroyed, you can't punch with it.", arm_used); return 0; } else if(!OkayCritSectS(arm, 0, SHOULDER_OR_HIP)) { mech_printf(mech, MECHALL, "Your %s shoulder is destroyed, you can't punch with that arm.", arm_used); return 0; } else if(MechSections(mech)[arm].specials & CARRYING_CLUB) { mech_printf(mech, MECHALL, "You're carrying a club in your %s arm and can't punch with it.", arm_used); return 0; } // Fall through to success. return 1; } // end checkArm() /** * Mech punch routines. */ void mech_punch(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map = getMap(mech->mapindex); char *argl[5]; char **args = argl; int argc, ltohit = 4, rtohit = 4; int punching = P_LEFT | P_RIGHT; // Carry out the common checks (started, on map, etc.) cch(MECH_USUALO); // Make sure we have arms to punch with. ARM_PHYS_CHECK("punch"); // Disallow quads from punching. QUAD_CHECK("punch"); argc = mech_parseattributes(buffer, args, 5); // If the directive is true, use the pilot's piloting skill. If not, we // use a constant BTH of 4. if(mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 1; // Manipulate punching var to contain only the arms we're punching with. if(get_arm_args(&punching, &argc, &args, mech, have_punch, "")) { return; } // Carry out our standard physical checks. This happens in PhysicalAttack // but the player gets double-spammed since PhysicalAttack can be called // twice in here. So, we add the check before. if(!phys_common_checks(mech)) return; // For each arm we're using, check to make sure it's good to punch with // and carry out the roll if it is. if(punching & P_LEFT) { if(punch_checkArm(mech, LARM)) PhysicalAttack(mech, 10, ltohit, PA_PUNCH, argc, args, mech_map, LARM); } if(punching & P_RIGHT) { if(punch_checkArm(mech, RARM)) PhysicalAttack(mech, 10, rtohit, PA_PUNCH, argc, args, mech_map, RARM); } } // end mech_punch() /** * Mech clubbing routines. */ void mech_club(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map = getMap(mech->mapindex); char *args[5]; int argc; int clubLoc = -1; // Make sure unit is started, on map, etc. cch(MECH_USUALO); // Make sure we're in a biped. ARM_PHYS_CHECK("club"); // Don't let quads club. QUAD_CHECK("club"); if(MechSections(mech)[RARM].specials & CARRYING_CLUB) clubLoc = RARM; else if(MechSections(mech)[LARM].specials & CARRYING_CLUB) clubLoc = LARM; if(clubLoc == -1) { DOCHECKMA(MechRTerrain(mech) != HEAVY_FOREST && MechRTerrain(mech) != LIGHT_FOREST, "You can not seem to find any trees around to club with."); // Since we have trees nearby, assume the club goes to right hand. clubLoc = RARM; } argc = mech_parseattributes(buffer, args, 5); DOCHECKMA(SectIsDestroyed(mech, LARM), "Your left arm is destroyed, you can't club."); DOCHECKMA(SectIsDestroyed(mech, RARM), "Your right arm is destroyed, you can't club."); DOCHECKMA(!OkayCritSectS(RARM, 0, SHOULDER_OR_HIP), "You can't club anyone with a destroyed or missing right shoulder."); DOCHECKMA(!OkayCritSectS(LARM, 0, SHOULDER_OR_HIP), "You can't club anyone with a destroyed or missing left shoulder."); DOCHECKMA(!OkayCritSectS(RARM, 3, HAND_OR_FOOT_ACTUATOR), "You can't club anyone with a destroyed or missing right hand."); DOCHECKMA(!OkayCritSectS(LARM, 3, HAND_OR_FOOT_ACTUATOR), "You can't club anyone with a destroyed or missing left hand."); // Clubbing is usually done with the right arm but a club may be // grabbed by the left hand. Clubbing requires both arms to be cycled, // but only one is checked by PhysicalAttack(). So, we check both // here just in case. DOCHECKMA(SectHasBusyWeap(mech, LARM) || SectHasBusyWeap(mech, RARM), "You have weapons recycling on your arms."); PhysicalAttack(mech, 5, (mudconf.btech_phys_use_pskill ? FindPilotPiloting(mech) - 1 : 4) + 2 * MechSections(mech)[RARM].basetohit + 2 * MechSections(mech)[LARM].basetohit, PA_CLUB, argc, args, mech_map, RARM); } // end mech_club() /** * Check to see if the specified arm can be used to axe with. */ int axe_checkArm(MECH * mech, int arm) { char *arm_used = (arm == RARM ? "right" : "left"); if(SectIsDestroyed(mech, arm)) { mech_printf(mech, MECHALL, "Your %s arm is destroyed, you can't axe with it", arm_used); return 0; } else if(!OkayCritSectS(arm, 0, SHOULDER_OR_HIP)) { mech_printf(mech, MECHALL, "Your %s shoulder is destroyed, you can't axe with that arm.", arm_used); return 0; } else if(!OkayCritSectS(arm, 3, HAND_OR_FOOT_ACTUATOR)) { mech_printf(mech, MECHALL, "Your left hand is destroyed, you can't axe with that arm.", arm_used); return 0; } // Fall through to success. return 1; } // end axe_checkArm() /** * Mech axe routines. */ void mech_axe(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map = getMap(mech->mapindex); char *argl[5]; char **args = argl; int argc, ltohit = 4, rtohit = 4; int using = P_LEFT | P_RIGHT; // Make sure we're started, on a map, etc. cch(MECH_USUALO); // Do we have arms? ARM_PHYS_CHECK("axe"); // Make sure we're not a quad. QUAD_CHECK("axe"); argc = mech_parseattributes(buffer, args, 5); // If btech_phys_use_pskill is on, use the player's piloting skill. // If not, assume a skill level of 4. if(mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 1; ltohit += MechSections(mech)[LARM].basetohit; rtohit += MechSections(mech)[RARM].basetohit; // Figure out which arm to use. if(get_arm_args(&using, &argc, &args, mech, have_axe, "an axe")) { return; } if(using & P_LEFT) { if(axe_checkArm(mech, LARM)) PhysicalAttack(mech, 5, ltohit, PA_AXE, argc, args, mech_map, LARM); } if(using & P_RIGHT) { if(axe_checkArm(mech, RARM)) PhysicalAttack(mech, 5, rtohit, PA_AXE, argc, args, mech_map, RARM); } // We don't have an axe. DOCHECKMA(!using, "You may lack the axe, but not the will! Try punch/club until you find one."); } // end mech_axe() /** * Check to see if the specified arm can be used to saw with. */ int saw_checkArm(MECH * mech, int arm) { char *arm_used = (arm == RARM ? "right" : "left"); if(SectIsDestroyed(mech, arm)) { mech_printf(mech, MECHALL, "Your %s arm is destroyed, you can't saw with it", arm_used); return 0; } else if(!OkayCritSectS(arm, 0, SHOULDER_OR_HIP)) { mech_printf(mech, MECHALL, "Your %s shoulder is destroyed, you can't saw with that arm.", arm_used); return 0; } // Fall through to success. return 1; } // end saw_checkArm() /** * Mech dual saw routines. */ void mech_saw(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map = getMap(mech->mapindex); char *argl[5]; char **args = argl; int argc, ltohit = 4, rtohit = 4; int using = P_LEFT | P_RIGHT; // Make sure we're started, on a map, etc. cch(MECH_USUALO); // Do we have arms? ARM_PHYS_CHECK("saw"); // Make sure we're not a quad. QUAD_CHECK("saw"); argc = mech_parseattributes(buffer, args, 5); // If btech_phys_use_pskill is on, use the player's piloting skill. // If not, assume a skill level of 4. if(mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 1; ltohit += MechSections(mech)[LARM].basetohit; rtohit += MechSections(mech)[RARM].basetohit; // Figure out which arm to use. if(get_arm_args(&using, &argc, &args, mech, have_saw, "a saw")) { return; } if(using & P_LEFT) { if(saw_checkArm(mech, LARM)) PhysicalAttack(mech, 7, ltohit, PA_SAW, argc, args, mech_map, LARM); } if(using & P_RIGHT) { if(saw_checkArm(mech, RARM)) PhysicalAttack(mech, 7, rtohit, PA_SAW, argc, args, mech_map, RARM); } // We don't have a saw. DOCHECKMA(!using, "You don't have a dual saw!"); } // end mech_saw() /** * Check our arms to see if they can mace. */ int mace_checkArm(MECH * mech, int arm) { char *arm_used = (arm == RARM ? "right" : "left"); if(SectIsDestroyed(mech, arm)) { mech_printf(mech, MECHALL, "Your %s arm is destroyed, you can't use a mace with it.", arm_used); return 0; } else if(!OkayCritSectS(arm, 0, SHOULDER_OR_HIP)) { mech_printf(mech, MECHALL, "Your %s shoulder is destroyed, you can't use a mace with that arm.", arm_used); return 0; } else if(!OkayCritSectS(arm, 3, HAND_OR_FOOT_ACTUATOR)) { mech_printf(mech, MECHALL, "Your %s hand is destroyed, you can't use a mace with that arm.", arm_used); return 0; } // Fall through to success. return 1; } // end mace_checkArm() /** * Mech mace routines. */ void mech_mace(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map = getMap(mech->mapindex); char *argl[5]; char **args = argl; int argc, ltohit = 4, rtohit = 4; int using = P_LEFT | P_RIGHT; // Make sure we're started, on a map, etc. cch(MECH_USUALO); // Do we have arms? ARM_PHYS_CHECK("mace"); // Make sure we're not a quad. QUAD_CHECK("mace"); argc = mech_parseattributes(buffer, args, 5); // If btech_phys_use_pskill is on, use the player's piloting skill. // If not, assume a skill level of 4. if(mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 1; ltohit += MechSections(mech)[LARM].basetohit; rtohit += MechSections(mech)[RARM].basetohit; // Figure out which arm to use. if(get_arm_args(&using, &argc, &args, mech, have_mace, "a mace")) { return; } if(using & P_LEFT) { if(mace_checkArm(mech, LARM)) PhysicalAttack(mech, 4, ltohit, PA_MACE, argc, args, mech_map, LARM); } if(using & P_RIGHT) { if(mace_checkArm(mech, RARM)) PhysicalAttack(mech, 4, rtohit, PA_MACE, argc, args, mech_map, RARM); } // We don't have a mace. DOCHECKMA(!using, "You don't have a mace!"); } // end mech_mace() /** * Check our arms to see if they can chop. */ int sword_checkArm(MECH * mech, int arm) { char *arm_used = (arm == RARM ? "right" : "left"); if(SectIsDestroyed(mech, arm)) { mech_printf(mech, MECHALL, "Your %s arm is destroyed, you can't use a sword with it.", arm_used); return 0; } else if(!OkayCritSectS(arm, 0, SHOULDER_OR_HIP)) { mech_printf(mech, MECHALL, "Your %s shoulder is destroyed, you can't use a sword with that arm.", arm_used); return 0; } else if(!OkayCritSectS(arm, 3, HAND_OR_FOOT_ACTUATOR)) { mech_printf(mech, MECHALL, "Your %s hand is destroyed, you can't use a sword with that arm.", arm_used); return 0; } // Fall through to success. return 1; } // end sword_checkArm() /** * Mech sword routines. */ void mech_sword(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map = getMap(mech->mapindex); char *argl[5]; char **args = argl; int argc, ltohit = 3, rtohit = 3; int using = P_LEFT | P_RIGHT; // Make sure we're started, on a map, etc. cch(MECH_USUALO); // Do we have arms to chop with? ARM_PHYS_CHECK("chop"); // Quads can't do it. QUAD_CHECK("chop"); argc = mech_parseattributes(buffer, args, 5); // If btech_phys_use_pskill is defined, use the pilot's piloting skill, // otherwise use a constant skill 3 for left hand, 2 for right. // // NOTE: This seems funky to have different skills for left/right when // this define is off but not on. if(mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 2; ltohit += MechSections(mech)[LARM].basetohit; rtohit += MechSections(mech)[RARM].basetohit; // Which arm(s) have sword crits? if(get_arm_args(&using, &argc, &args, mech, have_sword, "a sword")) { return; } if(using & P_LEFT) { if(sword_checkArm(mech, LARM)) PhysicalAttack(mech, 10, ltohit, PA_SWORD, argc, args, mech_map, LARM); } if(using & P_RIGHT) { if(sword_checkArm(mech, RARM)) PhysicalAttack(mech, 10, rtohit, PA_SWORD, argc, args, mech_map, RARM); } // Ninja what? DOCHECKMA(!using, "You have no sword to chop people with!"); } // end mech_sword() /** * Mech tripping command hook. */ void mech_trip(dbref player, void *data, char *buffer) { mech_kickortrip(player, data, buffer, PA_TRIP); } // end mech_trip() /** * Mech kick command hook. */ void mech_kick(dbref player, void *data, char *buffer) { mech_kickortrip(player, data, buffer, PA_KICK); } // end mech_trip() /** * Mech kick/trip routines. */ void mech_kickortrip(dbref player, void *data, char *buffer, int AttackType) { MECH *mech = (MECH *) data; MAP *mech_map = getMap(mech->mapindex); char *argl[5]; char **args = argl; int argc; int rl = RLEG, ll = LLEG; int leg; int using = P_RIGHT; // Make sure we're started, on a map, etc. cch(MECH_USUALO); // If we're a quad, re-map front legs. if(MechIsQuad(mech)) { rl = RARM; ll = LARM; } // See if we have enough usable legs to kick/trip with. GENERIC_CHECK("kick", CountDestroyedLegs(mech)); argc = mech_parseattributes(buffer, args, 5); // Figure out which leg we're using. if(get_arm_args(&using, &argc, &args, mech, have_punch, "")) { return; } switch (using) { case P_LEFT: leg = ll; break; case P_RIGHT: leg = rl; break; default: case P_LEFT | P_RIGHT: mech_notify(mech, MECHALL, "What, yer gonna LEVITATE? I Don't Think So."); return; } if((MechCritStatus(mech) & HIP_DAMAGED)) { mech_printf(mech, MECHALL, "You can't %s with a destroyed hip.", phys_form(AttackType, 0)); return; } PhysicalAttack(mech, 5, (mudconf.btech_phys_use_pskill ? FindPilotPiloting(mech) - 2 : 3), AttackType, argc, args, mech_map, leg); } // end mech_kickortrip() /** * Mech/tank charge routines */ void mech_charge(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data, *target; MAP *mech_map = getMap(mech->mapindex); int targetnum; char targetID[5]; char *args[5]; int argc; int wcDeadLegs = 0; // Make sure we're started, on a map, etc. cch(MECH_USUALO); // Mechwarriors can't chage. DOCHECK(MechType(mech) == CLASS_MW || MechType(mech) == CLASS_BSUIT, "You cannot charge without a 'mech!"); // Salvage vehicles can't charge. DOCHECK(MechType(mech) != CLASS_MECH && (MechType(mech) != CLASS_VEH_GROUND || MechSpecials(mech) & SALVAGE_TECH), "You cannot charge with this vehicle!"); // Figure out if we have enough legs to kick with. if(MechType(mech) == CLASS_MECH) { /* set the number of dead legs we have */ wcDeadLegs = CountDestroyedLegs(mech); DOCHECK(!MechIsQuad(mech) && (wcDeadLegs > 0), "With one leg? Are you kidding?"); DOCHECK(!MechIsQuad(mech) && (wcDeadLegs > 1), "Without legs? Are you kidding?"); DOCHECK(wcDeadLegs > 1, "It'd unbalance you too much in your condition.."); DOCHECK(wcDeadLegs > 2, "Exactly _what_ are you going to kick with?"); } // end if() - Dead leg counting. argc = mech_parseattributes(buffer, args, 2); switch (argc) { // No arguments given with charge. Assume default target. case 0: DOCHECKMA(MechTarget(mech) == -1, "You do not have a default target set!"); target = getMech(MechTarget(mech)); if(!target) { mech_notify(mech, MECHALL, "Invalid default target!"); MechTarget(mech) = -1; return; } // Don't allow charging Mechwarriors. if(MechType(target) == CLASS_MW) { mech_notify(mech, MECHALL, "You can't charge THAT sack of bones and squishy bits!"); return; } MechChargeTarget(mech) = MechTarget(mech); mech_notify(mech, MECHALL, "Charge target set to default target."); break; // We've supplied an argument, either a '-' or an ID. case 1: if(args[0][0] == '-') { MechChargeTarget(mech) = -1; MechChargeTimer(mech) = 0; MechChargeDistance(mech) = 0; mech_notify(mech, MECHPILOT, "You are no longer charging."); return; } targetID[0] = args[0][0]; targetID[1] = args[0][1]; targetnum = FindTargetDBREFFromMapNumber(mech, targetID); DOCHECKMA(targetnum == -1, "Target is not in line of sight!"); target = getMech(targetnum); DOCHECKMA(!InLineOfSight_NB(mech, target, MechX(target), MechY(target), FaMechRange(mech, target)), "Target is not in line of sight!"); if(!target) { mech_notify(mech, MECHALL, "Invalid target data!"); return; } // Don't allow charging mechwarriors. if(MechType(target) == CLASS_MW) { mech_notify(mech, MECHALL, "You can't charge THAT sack of bones and squishy bits!"); return; } MechChargeTarget(mech) = targetnum; mech_printf(mech, MECHALL, "%s target set to %s.", MechType(mech) == CLASS_MECH ? "Charge" : "Ram", GetMechToMechID(mech, target)); break; // Something other than 0-1 arguments. default: notify(player, "Invalid number of arguments!"); } } // end mech_charge() /* * Home to the code to carry out physical attacks. * * NOTE: Do NOT put any logic/checker code in here that is specific to a * certain type of attack if possible. Put it in its respective function. * Only things that are generic and on-specific to an attack type go here. */ void PhysicalAttack(MECH * mech, int damageweight, int baseToHit, int AttackType, int argc, char **args, MAP * mech_map, int sect) { MECH *target; float range; float maxRange = 1; char targetID[2]; int targetnum, roll, swarmingUs; char location[20]; int ts = 0, iwa; int RbaseToHit, glance = 0; /* * Common Checks */ // Since we can punch with two arms, often back to back, we want to run // these generic checks in mech_punch() -BEFORE- PhysicalAttack() is called // twice (if we have two working arms). if(AttackType != PA_PUNCH) if(!phys_common_checks(mech)) return; #define Check(num,okval,mod) \ if (AttackType == PA_PUNCH) \ if (MechType(mech) == CLASS_MECH) \ if (PartIsNonfunctional(mech, sect, num) || \ GetPartType(mech, sect, num) != \ I2Special(okval)) \ baseToHit += mod; Check(1, UPPER_ACTUATOR, 2); Check(2, LOWER_ACTUATOR, 2); Check(3, HAND_OR_FOOT_ACTUATOR, 1); // All weapons must be cycled in the target limb. if(SectHasBusyWeap(mech, sect)) { ArmorStringFromIndex(sect, location, MechType(mech), MechMove(mech)); mech_printf(mech, MECHALL, "You have weapons recycling on your %s.", location); return; } // Figure out what to do with the arguments provided with the physical // command. switch (argc) { case -1: case 0: // No argument DOCHECKMA(MechTarget(mech) == -1, "You do not have a target set!"); // Populate target variable with current lock. target = getMech(MechTarget(mech)); DOCHECKMA(!target, "Invalid default target!"); break; default: // In this case, default means user has specified an argument // with the physical attack. // Populate target variable from user input. targetID[0] = args[0][0]; targetID[1] = args[0][1]; targetnum = FindTargetDBREFFromMapNumber(mech, targetID); target = getMech(targetnum); DOCHECKMA(targetnum == -1, "Target is not in line of sight!"); DOCHECKMA(!target, "Invalid default target!"); } // end switch() - argc checking // Is the target swarming us? swarmingUs = (MechSwarmTarget(target) == mech->mynum ? 1 : 0); /* * Common checks. */ // If we're attacking something while fallen that isn't swarming us, // no-go it. Kicks/trips are automatically stopped. if(Fallen(mech) && (AttackType == PA_KICK || AttackType == PA_TRIP)) { mech_printf(mech, MECHALL, "You can't %s from a prone position.", phys_form(AttackType, 0)); return; // If we are fallen AND // The target is not a BSuit AND We're not punching } else if(Fallen(mech) && (MechType(target) != CLASS_VEH_GROUND && MechType(target) != CLASS_BSUIT)) { mech_printf(mech, MECHALL, "You can't %s from a prone position.", phys_form(AttackType, 0)); return; } else if(Fallen(mech) && MechType(target) == CLASS_BSUIT && !swarmingUs) { mech_notify(mech, MECHALL, "You may only physical suits that are swarming you while prone."); return; } else if(Fallen(mech) && MechType(target) == CLASS_VEH_GROUND && AttackType != PA_PUNCH) { mech_notify(mech, MECHALL, "You may only punch vehicles while prone."); return; } // end if() - Physical while fallen. range = FaMechRange(mech, target); DOCHECKMA(!InLineOfSight_NB(mech, target, MechX(target), MechY(target), range), "Target is not in line of sight!"); // BSuits have to be <= 0.5 hexes to attack units. if((MechType(target) == CLASS_BSUIT) || (MechType(target) == CLASS_MW)) maxRange = 0.5; DOCHECKMA(range >= maxRange, "Target out of range!"); DOCHECKMA(Jumping(target), "You can't perform physical attacks on airborne mechs!"); DOCHECKMA(MechTeam(target) == MechTeam(mech) && MechNoFriendlyFire(mech), "You can't attack a teammate with FFSafeties on!"); DOCHECKMA(MechTeam(target) == MechTeam(mech) && MapNoFriendlyFire(mech_map), "Friendly Fire? I don't think so..."); DOCHECKMA(MechType(target) == CLASS_MW && !MechPKiller(mech), "That's a living, breathing person! Switch off the safety first, " "if you really want to assassinate the target."); DOCHECKMA(MechCritStatus(mech) & MECH_STUNNED, "You are still recovering from your stunning experience!"); /* * Attack-Specific checks. */ DOCHECKMA(AttackType == PA_PUNCH && (MechType(target) == CLASS_VEH_GROUND) && !Fallen(mech), "You can't punch vehicles unless you are prone!"); // As per BMR, can only trip mechs. DOCHECKMA(AttackType == PA_TRIP && MechType(target) != CLASS_MECH, "You can only trip mechs!"); // Can't trip mechs that are fallen or in the process of standing. DOCHECKMA(AttackType == PA_TRIP && (Fallen(target) || Standing(target)), "Your target is already down!"); // We're attacking a ground/naval unit. if(MechMove(target) != MOVE_VTOL && MechMove(target) != MOVE_FLY) { if((AttackType != PA_KICK && AttackType != PA_TRIP) && (MechZ(mech) >= MechZ(target))) { int isTooLow = 0; // Track whether we're too low or not. // If it's a fallen mech, too low. if(MechType(target) == CLASS_MECH && Fallen(target)) isTooLow = 1; // If it's not a mech, bsuit, or DS, too low. if(MechType(target) != CLASS_MECH && MechType(target) != CLASS_BSUIT && !IsDS(target)) isTooLow = 1; // If it's a ground vehicle and we're fallen, then we can // punch as per BMR. if(AttackType == PA_PUNCH && MechType(target) == CLASS_VEH_GROUND && Fallen(mech)) isTooLow = 0; // If it's a suit that's not on us, we can't physical it. if(MechType(target) == CLASS_BSUIT && swarmingUs == 0) { mech_printf(mech, MECHALL, "You can't directly physical suits that are swarmed or mounted on another mech!"); return; } // end if() - Disallow physicals on swarmed/mounted suits. if(isTooLow == 1) { mech_printf(mech, MECHALL, "The target is too low in elevation for you to %s.", phys_form(AttackType, 0)); return; } // end if() - Check isTooLow } // end if() - Target is too low checks. DOCHECKMA((AttackType == PA_KICK || AttackType == PA_TRIP) && MechZ(mech) < MechZ(target), "The target is too high in elevation for you to kick at."); DOCHECKMA(MechZ(mech) - MechZ(target) > 1 || MechZ(target) - MechZ(mech) > 1, "You can't attack, the elevation difference is too large."); DOCHECKMA((AttackType == PA_KICK || AttackType == PA_TRIP) && (MechZ(target) < MechZ(mech) && (((MechType(target) == CLASS_MECH) && Fallen(target)) || (MechType(target) == CLASS_VEH_GROUND) || (MechType(target) == CLASS_BSUIT) || (MechType(target) == CLASS_MW))), "The target is too low in elevation for you to kick.") } else { // We're attacking a VTOL/Aero. if((AttackType != PA_KICK) && MechZ(target) - MechZ(mech) > 3) { mech_printf(mech, MECHALL, "The target is too far away for you to %s.", phys_form(AttackType, 0)); } if((AttackType == PA_KICK || AttackType == PA_TRIP) && MechZ(mech) != MechZ(target)) { mech_printf(mech, MECHALL, "The target is too far away for you to %s.", phys_form(AttackType, 0)); return; } DOCHECKMA(!(MechZ(target) - MechZ(mech) > -1 && MechZ(target) - MechZ(mech) < 4), "You can't attack, the elevation difference is too large."); } // end if/else() - Ground/VTOL + Physical Z comparisons /* Check weapon arc! */ /* Theoretically, physical attacks occur only to 'real' forward arc, not rottorsoed one, but we let it pass this time */ /* This is wrong according to BMR * * Which states that the Torso twist is taken into account * as well as punching/axing/swords can attack in their * respective arcs - Dany * * So I went and changed it according to FASA rules */ if(AttackType == PA_KICK || AttackType == PA_TRIP) { ts = MechStatus(mech) & (TORSO_LEFT | TORSO_RIGHT); MechStatus(mech) &= ~ts; iwa = InWeaponArc(mech, MechFX(target), MechFY(target)); MechStatus(mech) |= ts; DOCHECKMA(!(iwa & FORWARDARC), "Target is not in your 'real' forward arc!"); } else { // We're punching, clubbing, or other sharp things. iwa = InWeaponArc(mech, MechFX(target), MechFY(target)); if(AttackType == PA_CLUB) { // Clubs are a frontal attack. Go off of the forward arc, don't // take arms into account. DOCHECKMA(!(iwa & FORWARDARC) && swarmingUs != 1, "Target is not in your forward arc!"); } else { // For other attacks, check on a per-arm basis. if(sect == RARM) { // We're attacking with right arm. Forward or right will do. DOCHECKMA(! ((iwa & FORWARDARC) || (iwa & RSIDEARC) || swarmingUs), "Target is not in your forward or right side arc!"); } else { // We're attacking with left arm. Forward or left will do. DOCHECKMA(!((iwa & FORWARDARC) || (iwa & LSIDEARC)) || swarmingUs, "Target is not in your forward or left side arc!"); } // end } // end if/else() - club/punch arc check } // end if/else() - kick/punch arc check /** * Add in the movement modifiers */ // If we have melee_specialist advantage, knock -1 off the BTH. baseToHit += HasBoolAdvantage(MechPilot(mech), "melee_specialist") ? MIN(0, AttackMovementMods(mech) - 1) : AttackMovementMods(mech); baseToHit += TargetMovementMods(mech, target, 0.0); // BSuits get +1 BTH baseToHit += MechType(target) == CLASS_BSUIT ? 1 : 0; // Kicking a BSuit is +3 BTH baseToHit += ((MechType(target) == CLASS_BSUIT) && (AttackType == PA_KICK)) ? 3 : 0; #ifdef MOVEMENT_MODES // A dodging unit is +2, requires maneuvering_ace. if(Dodging(target)) baseToHit += 2; #endif // Saws get a +1 BTH. if(AttackType == PA_SAW) baseToHit += 1; // Maces get a +2 BTH. if(AttackType == PA_MACE) baseToHit += 2; // If we're axing or chopping a bsuit, add +3 to BTH, else (punching) +5. if(AttackType != PA_PUNCH && MechType(target) == CLASS_BSUIT && MechSwarmTarget(target) > 0) baseToHit += (AttackType != PA_PUNCH) ? 3 : 5; // As per BMR, can only physical bsuits with punches, axes, or swords. // Added saw since it's the same idea. DOCHECKMA(AttackType == PA_KICK && MechType(target) == CLASS_BSUIT && MechSwarmTarget(target) > 0, "You can't hit a swarmed suit with that, try a hand-held weapon!"); roll = Roll(); // Carry out the attack. mech_printf(mech, MECHALL, "You try to %s %s. BTH: %d,\tRoll: %d", phys_form(AttackType, 0), GetMechToMechID(mech, target), baseToHit, roll); mech_printf(target, MECHSTARTED, "%s tries to %s you!", GetMechToMechID(target, mech), phys_form(AttackType, 0)); // We send to MechAttacks channel SendAttacks(tprintf("#%i attacks #%i (%s) (%i/%i)", mech->mynum, target->mynum, phys_form(AttackType, 0), baseToHit, roll)); // Set the appropriate section(s) to recycle. SetRecycleLimb(mech, sect, PHYSICAL_RECYCLE_TIME); /* * Attack-specific recycles and flags. */ if(AttackType == PA_AXE || AttackType == PA_SWORD || AttackType == PA_SAW || AttackType == PA_MACE) MechSections(mech)[sect].config |= AXED; if(AttackType == PA_PUNCH) MechSections(mech)[sect].config &= ~AXED; // Clubbing recycles both arms. if(AttackType == PA_CLUB) SetRecycleLimb(mech, LARM, PHYSICAL_RECYCLE_TIME); RbaseToHit = baseToHit; if(mudconf.btech_glancing_blows == 2) RbaseToHit = baseToHit - 1; // We've successfully hit the target. if(roll >= RbaseToHit) { phys_succeed(mech, target, AttackType); if (mudconf.btech_glancing_blows && (roll == RbaseToHit)) { MechLOSBroadcast(target,"is nicked by a glancing blow!"); mech_notify(target, MECHALL, "You are nicked by a glancing blow!"); glance = 1 ; } if(AttackType == PA_CLUB) { int clubLoc = -1; if(MechSections(mech)[RARM].specials & CARRYING_CLUB) clubLoc = RARM; else if(MechSections(mech)[LARM].specials & CARRYING_CLUB) clubLoc = LARM; if(clubLoc > -1) { mech_notify(mech, MECHALL, "Your club shatters on contact."); MechLOSBroadcast(mech, "'s club shatters with a loud *CRACK*!"); MechSections(mech)[clubLoc].specials &= ~CARRYING_CLUB; } } // End if() - Club shattering // Do the deed - Damage the victim. If we're tripping, we don't do // damage but try to make a skill roll. if(AttackType != PA_TRIP) PhysicalDamage(mech, target, damageweight, AttackType, sect, glance); else PhysicalTrip(mech, target); } else { // We have failed! phys_fail(mech, target, AttackType); if(MechType(target) == CLASS_BSUIT && MechSwarmTarget(target) == mech->mynum) { if(!MadePilotSkillRoll(mech, 4)) { mech_notify(mech, MECHALL, "Uh oh. You miss the little buggers, but hit yourself!"); MechLOSBroadcast(mech, "misses, and hits itself!"); PhysicalDamage(mech, mech, damageweight, AttackType, sect, glance); } // If we really screw up against suits swarmed on ourselves, // nail us for damage. } // end if() - Suit + Swarmed + Physical + Self Damage checks if(AttackType == PA_KICK || AttackType == PA_CLUB || AttackType == PA_MACE) { int failRoll = (AttackType == PA_KICK ? 0 : 2); mech_notify(mech, MECHALL, "You miss and try to remain standing!"); // We fail the piloting skill roll and flop on our face. if(!MadePilotSkillRoll(mech, failRoll)) { mech_notify(mech, MECHALL, "You lose your balance and fall down!"); MechFalls(mech, 1, 1); } // end if() - Miss/fall. } // end if() - Miss kick/club and risk falling. } // end if() - Physical failure handling. } //end PhysicalAttack() extern int global_physical_flag; #define MyDamageMech(a,b,c,d,e,f,g,h,i) \ global_physical_flag = 1 ; DamageMech(a,b,c,d,e,f,g,h,i,-1,0,-1,0,0); \ global_physical_flag = 0 #define MyDamageMech2(a,b,c,d,e,f,g,h,i) \ global_physical_flag = 2 ; DamageMech(a,b,c,d,e,f,g,h,i,-1,0,-1,0,0); \ global_physical_flag = 0 /* * Try to trip the victim. */ void PhysicalTrip(MECH * mech, MECH * target) { // If we trip our target (who is a mech), make a roll to see if he falls. if(!MadePilotSkillRoll(target, 0) && !Fallen(target)) { // Emit to Attacker mech_printf(mech, MECHALL, "You trip %s!", GetMechToMechID(mech, target)); // Emit to victim and LOS. mech_notify(target, MECHSTARTED, "You are tripped and fall to the ground!"); MechLOSBroadcast(target, "trips up and falls down!"); MechFalls(target, 1, 0); } else { MechLOSBroadcast(target, "manages to stay upright!"); } } // end PhysicalTrip() /* * Damage the victim. */ void PhysicalDamage(MECH * mech, MECH * target, int weightdmg, int AttackType, int sect, int glance) { int hitloc = 0, damage, hitgroup = 0, isrear, iscritical, i; // If we're hitting with a sword, adjust damage differently from // a typical physical. if(AttackType == PA_SWORD) damage = (MechTons(mech) + 5) / weightdmg + 1; else if(AttackType == PA_SAW) // Saws do a constant 7 damage due to their mechanical nature. damage = 7; else damage = (MechTons(mech) + weightdmg / 2) / weightdmg; // Figure in TSM damage bonus. if((MechHeat(mech) >= 9.) && (MechSpecials(mech) & TRIPLE_MYOMER_TECH)) damage = damage * 2; // If we have melee_specialist, add a point of damage. if(HasBoolAdvantage(MechPilot(mech), "melee_specialist")) damage++; switch (AttackType) { case PA_PUNCH: // If we have damaged/missing actuators, lose damage. if(!OkayCritSectS(sect, 2, LOWER_ACTUATOR)) damage = damage / 2; if(!OkayCritSectS(sect, 1, UPPER_ACTUATOR)) damage = damage / 2; hitgroup = FindAreaHitGroup(mech, target); if(MechType(mech) == CLASS_MECH) { // Hit locs for fallen units. Currently not even allowed // but we'll leave it in, someone may want to enable it later. // EXCEPTION: You may punch vehicles in same hex while proned. if(Fallen(mech)) { if((MechType(target) != CLASS_MECH) || (Fallen(target) && (MechElevation(mech) == MechElevation (target)))) hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical); else if(!Fallen(target) && (MechElevation(mech) > MechElevation(target))) hitloc = FindPunchLocation(hitgroup); else if(MechElevation(mech) == MechElevation(target)) hitloc = FindKickLocation(hitgroup); } else if(MechElevation(mech) < MechElevation(target)) { if(Fallen(target) || MechType(target) != CLASS_MECH) hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical); else hitloc = FindKickLocation(hitgroup); } else hitloc = FindPunchLocation(hitgroup); } else { isrear = hitgroup == REAR; hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical); } break; // End punch hitloc code. case PA_SWORD: case PA_AXE: case PA_MACE: case PA_CLUB: hitgroup = FindAreaHitGroup(mech, target); if(MechType(mech) == CLASS_MECH) { if(MechElevation(mech) < MechElevation(target)) { if(Fallen(target) || MechType(target) != CLASS_MECH) hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical); else hitloc = FindKickLocation(hitgroup); } else if(MechElevation(mech) > MechElevation(target)) { hitloc = FindPunchLocation(hitgroup); } else { hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical); } } else { isrear = hitgroup == REAR; hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical); } break; // End club hitloc code. case PA_KICK: // Reduce damage if actuators are missing/damaged. if(!OkayCritSectS(sect, 2, LOWER_ACTUATOR)) damage = damage / 2; if(!OkayCritSectS(sect, 1, UPPER_ACTUATOR)) damage = damage / 2; if(Fallen(target) || MechType(target) != CLASS_MECH) hitloc = FindTargetHitLoc(mech, target, &isrear, &iscritical); else { hitgroup = FindAreaHitGroup(mech, target); if(MechElevation(mech) > MechElevation(target)) hitloc = FindPunchLocation(hitgroup); else { hitloc = FindKickLocation(hitgroup); if(!GetSectInt(target, hitloc) && GetSectInt(target, (i = BOUNDED(0, 5 + (6 - hitloc), NUM_SECTIONS - 1)))) hitloc = i; } } break; // End kick hitloc code. } // end switch() - hitloc selection. if(glance) damage = (damage + 1) / 2; // Damage the target. MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, (hitgroup == BACK) ? 1 : 0, 0, damage, 0); // If we've successfully hit a suit, knock him off. if(MechType(target) == CLASS_BSUIT && MechSwarmTarget(target) > 0 && AttackType != PA_KICK) StopSwarming(target, 0); // If we kick our target (who is a mech), make a roll to see if he falls. if(MechType(target) == CLASS_MECH && AttackType == PA_KICK) if(!MadePilotSkillRoll(target, 0) && !Fallen(target)) { mech_notify(target, MECHSTARTED, "The kick knocks you to the ground!"); MechLOSBroadcast(target, "stumbles and falls down!"); MechFalls(target, 1, 0); } } // end PhysicalDamage() #define CHARGE_SECTIONS 6 #define DFA_SECTIONS 4 /* 4 if pure FASA */ const int resect[CHARGE_SECTIONS] = { LARM, RARM, LLEG, RLEG, LTORSO, RTORSO }; /* * Executed at the end of a DFA */ int DeathFromAbove(MECH * mech, MECH * target) { int baseToHit = 5; int roll; int hitGroup; int hitloc; int isrear = 0; int iscritical = 0; int target_damage; int mech_damage; int spread; int i, tmpi; char location[50]; /* Weapons recycling check on each major section */ for(i = 0; i < DFA_SECTIONS; i++) if(SectHasBusyWeap(mech, resect[i])) { ArmorStringFromIndex(resect[i], location, MechType(mech), MechMove(mech)); mech_printf(mech, MECHALL, "You have weapons recycling on your %s.", location); return 0; } // Our target is no longer on the map. DOCHECKMA0((mech->mapindex != target->mapindex), "Your target is no longer valid."); #ifdef BT_MOVEMENT_MODES DOCHECKMA0(Dodging(mech) || MoveModeLock(mech), "You cannot use physicals while using a special movement mode."); #endif DOCHECKMA0(MechSections(mech)[LLEG].recycle || MechSections(mech)[RLEG].recycle, "Your legs are still recovering from your last attack."); DOCHECKMA0(MechSections(mech)[RARM].recycle || MechSections(mech)[LARM].recycle, "Your arms are still recovering from your last attack."); DOCHECKMA0(Jumping(target), "Your target is airborne, you cannot land on it."); if((MechType(target) == CLASS_VTOL) || (MechType(target) == CLASS_AERO) || (MechType(target) == CLASS_DS)) DOCHECKMA0(!Landed(target), "Your target is airborne, you cannot land on it."); if(mudconf.btech_phys_use_pskill) baseToHit = FindPilotPiloting(mech); baseToHit += (HasBoolAdvantage(MechPilot(mech), "melee_specialist") ? MIN(0, AttackMovementMods(mech)) - 1 : AttackMovementMods(mech)); baseToHit += TargetMovementMods(mech, target, 0.0); baseToHit += MechType(target) == CLASS_BSUIT ? 1 : 0; #ifdef BT_MOVEMENT_MODES if(Dodging(target)) baseToHit += 2; #endif DOCHECKMA0(baseToHit > 12, tprintf ("DFA: BTH %d\tYou choose not to attack and land from your jump.", baseToHit)); roll = Roll(); mech_printf(mech, MECHALL, "DFA: BTH %d\tRoll: %d", baseToHit, roll); MechStatus(mech) &= ~JUMPING; MechStatus(mech) &= ~DFA_ATTACK; if(roll >= baseToHit) { /* OUCH */ mech_printf(target, MECHSTARTED, "DEATH FROM ABOVE!!!\n%s lands on you from above!", GetMechToMechID(target, mech)); mech_notify(mech, MECHALL, "You land on your target legs first!"); MechLOSBroadcasti(mech, target, "lands on %s!"); hitGroup = FindAreaHitGroup(mech, target); if(hitGroup == BACK) isrear = 1; target_damage = (3 * MechRealTons(mech)) / 10; if(MechTons(mech) % 10) target_damage++; if(HasBoolAdvantage(MechPilot(mech), "melee_specialist")) target_damage++; spread = target_damage / 5; for(i = 0; i < spread; i++) { if(Fallen(target) || MechType(target) != CLASS_MECH) hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); else hitloc = FindPunchLocation(hitGroup); MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, isrear, iscritical, 5, 0); } if(target_damage % 5) { if(Fallen(target) || (MechType(target) != CLASS_MECH)) hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); else hitloc = FindPunchLocation(hitGroup); MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, isrear, iscritical, (target_damage % 5), 0); } mech_damage = MechTons(mech) / 5; spread = mech_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindKickLocation(FRONT); MyDamageMech2(mech, mech, 0, -1, hitloc, 0, 0, 5, 0); } if(mech_damage % 5) { hitloc = FindKickLocation(FRONT); MyDamageMech2(mech, mech, 0, -1, hitloc, 0, 0, (mech_damage % 5), 0); } if(!Fallen(mech)) { if(!MadePilotSkillRoll(mech, 4)) { mech_notify(mech, MECHALL, "Your piloting skill fails and you fall over!!"); MechLOSBroadcast(mech, "stumbles and falls down!"); MechFalls(mech, 1, 0); } if(MechType(target) == CLASS_MECH && !MadePilotSkillRoll(target, 2)) { mech_notify(target, MECHSTARTED, "Your piloting skill fails and you fall over!!"); MechLOSBroadcast(target, "stumbles and falls down!"); MechFalls(target, 1, 0); } } } else { /* Missed DFA attack */ if(!Fallen(mech)) { mech_notify(mech, MECHALL, "You miss your DFA attack and fall on your back!!"); MechLOSBroadcast(mech, "misses DFA and falls down!"); } mech_damage = MechTons(mech) / 5; spread = mech_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindHitLocation(mech, BACK, &iscritical, &tmpi); MyDamageMech2(mech, mech, 0, -1, hitloc, 1, iscritical, 5, 0); } if(mech_damage % 5) { hitloc = FindHitLocation(mech, BACK, &iscritical, &tmpi); MyDamageMech2(mech, mech, 0, -1, hitloc, 1, iscritical, (mech_damage % 5), 0); } /* now damage pilot */ if(!MadePilotSkillRoll(mech, 2)) { mech_notify(mech, MECHALL, "You take personal injury from the fall!"); headhitmwdamage(mech, 1); } MechSpeed(mech) = 0.0; MechDesiredSpeed(mech) = 0.0; MakeMechFall(mech); MechZ(mech) = MechElevation(mech); MechFZ(mech) = MechZ(mech) * ZSCALE; if(MechZ(mech) < 0) MechFloods(mech); } for(i = 0; i < DFA_SECTIONS; i++) SetRecycleLimb(mech, resect[i], PHYSICAL_RECYCLE_TIME); return 1; } // end DeathFromAbove() /* * Executed when we're ready to finish the charge. */ void ChargeMech(MECH * mech, MECH * target) { int baseToHit = 5; int roll; int hitGroup; int hitloc; int isrear = 0; int iscritical = 0; int target_damage; int mech_damage; int received_damage; int inflicted_damage; int spread; int i; int mech_charge; int target_charge; int mech_baseToHit; int targ_baseToHit; int mech_roll; int targ_roll; int done = 0; char location[50]; int ts, iwa; char emit_buff[LBUF_SIZE]; /* Are they both charging ? */ if(MechChargeTarget(target) == mech->mynum) { /* They are both charging each other */ mech_charge = 1; target_charge = 1; /* Check the sections of the first unit for weapons that are cycling */ done = 0; for(i = 0; i < CHARGE_SECTIONS && !done; i++) { if(SectHasBusyWeap(mech, resect[i])) { ArmorStringFromIndex(resect[i], location, MechType(mech), MechMove(mech)); mech_printf(mech, MECHALL, "You have weapons recycling on your %s.", location); mech_charge = 0; done = 1; } } /* Check the sections of the second unit for weapons that are cycling */ done = 0; for(i = 0; i < CHARGE_SECTIONS && !done; i++) { if(SectHasBusyWeap(target, resect[i])) { ArmorStringFromIndex(resect[i], location, MechType(target), MechMove(target)); mech_printf(target, MECHALL, "You have weapons recycling on your %s.", location); target_charge = 0; done = 1; } } /* Is the second unit capable of charging */ if(!Started(target) || Uncon(target) || Blinded(target)) target_charge = 0; /* Is the first unit capable of charging */ if(!Started(mech) || Uncon(mech) || Blinded(mech)) mech_charge = 0; /* Is the first unit moving fast enough to charge */ if(MechSpeed(mech) < MP1) { mech_notify(mech, MECHALL, "You aren't moving fast enough to charge."); mech_charge = 0; } /* Is the second unit moving fast enough to charge */ if(MechSpeed(target) < MP1) { mech_notify(target, MECHALL, "You aren't moving fast enough to charge."); target_charge = 0; } /* Check to see if any sections cycling from a previous attack */ if(MechType(mech) == CLASS_MECH) { /* Is the first unit's legs cycling */ if(MechSections(mech)[LLEG].recycle || MechSections(mech)[RLEG].recycle) { mech_notify(mech, MECHALL, "Your legs are still recovering from your last attack."); mech_charge = 0; } /* Is the first unit's arms cycling */ if(MechSections(mech)[RARM].recycle || MechSections(mech)[LARM].recycle) { mech_notify(mech, MECHALL, "Your arms are still recovering from your last attack."); mech_charge = 0; } } else { /* Is the first unit's front side cycling */ if(MechSections(mech)[FSIDE].recycle) { mech_notify(mech, MECHALL, "You are still recovering from your last attack!"); mech_charge = 0; } } /* Check to see if any sections cycling from a previous attack */ if(MechType(target) == CLASS_MECH) { /* Is the second unit's legs cycling */ if(MechSections(target)[LLEG].recycle || MechSections(target)[RLEG].recycle) { mech_notify(target, MECHALL, "Your legs are still recovering from your last attack."); target_charge = 0; } /* Is the second unit's arms cycling */ if(MechSections(target)[RARM].recycle || MechSections(target)[LARM].recycle) { mech_notify(target, MECHALL, "Your arms are still recovering from your last attack."); target_charge = 0; } } else { /* Is the second unit's front side cycling */ if(MechSections(target)[FSIDE].recycle) { mech_notify(target, MECHALL, "You are still recovering from your last attack!"); target_charge = 0; } } /* Is the second unit jumping */ if(Jumping(target)) { mech_notify(mech, MECHALL, "Your target is jumping, you charge underneath it."); mech_notify(target, MECHALL, "You can't charge while jumping, try death from above."); mech_charge = 0; target_charge = 0; } /* Is the first unit jumping */ if(Jumping(mech)) { mech_notify(target, MECHALL, "Your target is jumping, you charge underneath it."); mech_notify(mech, MECHALL, "You can't charge while jumping, try death from above."); mech_charge = 0; target_charge = 0; } /* Is the second unit fallen and the first unit not a tank */ if(Fallen(target) && (MechType(mech) != CLASS_VEH_GROUND)) { mech_notify(mech, MECHALL, "Your target's too low for you to charge it!"); mech_charge = 0; } /* Not sure at the moment if I need this here, but I figured * couldn't hurt for now */ /* Is the first unit fallen and the second unit not a tank */ if(Fallen(mech) && (MechType(target) != CLASS_VEH_GROUND)) { mech_notify(target, MECHALL, "Your target's too low for you to charge it!"); target_charge = 0; } /* If the second unit is a mech it can only charge mechs */ if((MechType(target) == CLASS_MECH) && (MechType(mech) != CLASS_MECH)) { mech_notify(target, MECHALL, "You can only charge mechs!"); target_charge = 0; } /* If the first unit is a mech it can only charge mechs */ if((MechType(mech) == CLASS_MECH) && (MechType(target) != CLASS_MECH)) { mech_notify(mech, MECHALL, "You can only charge mechs!"); mech_charge = 0; } /* If the second unit is a tank, it can only charge tanks and mechs */ if((MechType(target) == CLASS_VEH_GROUND) && ((MechType(mech) != CLASS_MECH) && (MechType(mech) != CLASS_VEH_GROUND))) { mech_notify(target, MECHALL, "You can only charge mechs and tanks!"); target_charge = 0; } /* If the first unit is a tank, it can only charge tanks and mechs */ if((MechType(mech) == CLASS_VEH_GROUND) && ((MechType(target) != CLASS_MECH) && (MechType(target) != CLASS_VEH_GROUND))) { mech_notify(mech, MECHALL, "You can only charge mechs and tanks!"); mech_charge = 0; } /* Are they stunned ? */ if(CrewStunned(mech)) { mech_notify(mech, MECHALL, "You are too stunned to ram!"); mech_charge = 0; } if(CrewStunned(target)) { mech_notify(target, MECHALL, "You are too stunned to ram!"); target_charge = 0; } /* Are they trying to unjam their turrets ? */ if(UnjammingTurret(mech)) { mech_notify(mech, MECHALL, "You are too busy unjamming your turret!"); mech_charge = 0; } if(UnjammingTurret(target)) { mech_notify(mech, MECHALL, "You are too busy unjamming your turret!"); target_charge = 0; } /* Check the arcs to make sure the target is in the front arc */ ts = MechStatus(mech) & (TORSO_LEFT | TORSO_RIGHT); MechStatus(mech) &= ~ts; if(!(InWeaponArc(mech, MechFX(target), MechFY(target)) & FORWARDARC)) { mech_notify(mech, MECHALL, "Your charge target is not in your forward arc and you are unable to charge it."); mech_charge = 0; } MechStatus(mech) |= ts; ts = MechStatus(target) & (TORSO_LEFT | TORSO_RIGHT); MechStatus(mech) &= ~ts; if(!(InWeaponArc(target, MechFX(mech), MechFY(mech)) & FORWARDARC)) { mech_notify(target, MECHALL, "Your charge target is not in your forward arc and you are unable to charge it."); target_charge = 0; } MechStatus(mech) |= ts; /* Now to calculate how much damage the first unit will do */ if(mudconf.btech_newcharge) target_damage = (((((float) MechChargeDistance(mech)) * MP1) - MechSpeed(target) * cos((MechFacing(mech) - MechFacing(target)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(mech) + 5) / 10; else target_damage = ((MechSpeed(mech) - MechSpeed(target) * cos((MechFacing(mech) - MechFacing(target)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(mech) + 5) / 10; if(HasBoolAdvantage(MechPilot(mech), "melee_specialist")) target_damage++; /* Not able to do any damage */ if(target_damage <= 0) { mech_notify(mech, MECHPILOT, "Your target pulls away from you and you are unable to charge it."); mech_charge = 0; } /* Now see how much damage the second unit will do */ if(mudconf.btech_newcharge) mech_damage = (((((float) MechChargeDistance(target)) * MP1) - MechSpeed(mech) * cos((MechFacing(target) - MechFacing(mech)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(target) + 5) / 10; else mech_damage = ((MechSpeed(target) - MechSpeed(mech) * cos((MechFacing(target) - MechFacing(mech)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(target) + 5) / 10; if(HasBoolAdvantage(MechPilot(target), "melee_specialist")) mech_damage++; /* Not able to do any damage */ if(mech_damage <= 0) { mech_notify(target, MECHPILOT, "Your target pulls away from you and you are unable to charge it."); target_charge = 0; } /* BTH for first unit */ mech_baseToHit = 5; mech_baseToHit += FindPilotPiloting(mech) - FindSPilotPiloting(target); mech_baseToHit += (HasBoolAdvantage(MechPilot(mech), "melee_specialist") ? MIN(0, AttackMovementMods(mech) - 1) : AttackMovementMods(mech)); mech_baseToHit += TargetMovementMods(mech, target, 0.0); #ifdef BT_MOVEMENT_MODES if(Dodging(target)) mech_baseToHit += 2; #endif /* BTH for second unit */ targ_baseToHit = 5; targ_baseToHit += FindPilotPiloting(target) - FindSPilotPiloting(mech); targ_baseToHit += (HasBoolAdvantage(MechPilot(target), "melee_specialist") ? MIN(0, AttackMovementMods(target) - 1) : AttackMovementMods(target)); targ_baseToHit += TargetMovementMods(target, mech, 0.0); #ifdef BT_MOVEMENT_MODES if(Dodging(mech)) targ_baseToHit += 2; #endif /* Now check to see if its possible for them to even charge */ if(mech_charge) if(mech_baseToHit > 12) { mech_printf(mech, MECHALL, "Charge: BTH %d\tYou choose not to charge.", mech_baseToHit); mech_charge = 0; } if(target_charge) if(targ_baseToHit > 12) { mech_printf(target, MECHALL, "Charge: BTH %d\tYou choose not to charge.", targ_baseToHit); target_charge = 0; } /* Since neither can charge lets exit */ if(!mech_charge && !target_charge) { /* MechChargeTarget(mech) and the others are set after the return */ MechChargeTarget(target) = -1; MechChargeTimer(target) = 0; MechChargeDistance(target) = 0; return; } /* Roll */ mech_roll = Roll(); targ_roll = Roll(); if(mech_charge) mech_printf(mech, MECHALL, "Charge: BTH %d\tRoll: %d", mech_baseToHit, mech_roll); if(target_charge) mech_printf(target, MECHALL, "Charge: BTH %d\tRoll: %d", targ_baseToHit, targ_roll); /* Ok the first unit made its roll */ if(mech_charge && mech_roll >= mech_baseToHit) { /* OUCH */ mech_printf(target, MECHALL, "CRASH!!!\n%s charges into you!", GetMechToMechID(target, mech)); mech_notify(mech, MECHALL, "SMASH!!! You crash into your target!"); hitGroup = FindAreaHitGroup(mech, target); isrear = (hitGroup == BACK); /* Record the damage for debugging then dish it out */ inflicted_damage = target_damage; spread = target_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, isrear, iscritical, 5, 0); } if(target_damage % 5) { hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, isrear, iscritical, (target_damage % 5), 0); } hitGroup = FindAreaHitGroup(target, mech); isrear = (hitGroup == BACK); /* Ok now how much damage will the first unit take from * charging */ if(mudconf.btech_newcharge && mudconf.btech_tl3_charge) target_damage = (((((float) MechChargeDistance(mech)) * MP1) - MechSpeed(target) * cos((MechFacing(mech) - MechFacing(target)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(mech) + 5) / 20; else target_damage = (MechRealTons(target) + 5) / 10; /* REUSED! */ /* Record the damage for debugging then dish it out */ received_damage = target_damage; spread = target_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); MyDamageMech2(mech, mech, 0, -1, hitloc, isrear, iscritical, 5, 0); } if(target_damage % 5) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); MyDamageMech2(mech, mech, 0, -1, hitloc, isrear, iscritical, (target_damage % 5), 0); } /* Stop him */ MechSpeed(mech) = 0; MechDesiredSpeed(mech) = 0; /* Emit the damage for debugging purposes */ snprintf(emit_buff, LBUF_SIZE, "#%i charges #%i (%i/%i) Distance:" " %.2f DI: %i DR: %i", mech->mynum, target->mynum, mech_baseToHit, mech_roll, MechChargeDistance(mech), inflicted_damage, received_damage); SendDebug(emit_buff); /* Make the first unit roll for doing the charge if it is a mech */ if(MechType(mech) == CLASS_MECH && !MadePilotSkillRoll(mech, 2)) { mech_notify(mech, MECHALL, "Your piloting skill fails and you fall over!!"); MechFalls(mech, 1, 1); } /* Make the second unit roll for receiving the charge if it is a mech */ if(MechType(mech) == CLASS_MECH && !MadePilotSkillRoll(target, 2)) { mech_notify(target, MECHALL, "Your piloting skill fails and you fall over!!"); MechFalls(target, 1, 1); } } /* Ok the second unit made its roll */ if(target_charge && targ_roll >= targ_baseToHit) { /* OUCH */ mech_printf(mech, MECHALL, "CRASH!!!\n%s charges into you!", GetMechToMechID(mech, target)); mech_notify(target, MECHALL, "SMASH!!! You crash into your target!"); hitGroup = FindAreaHitGroup(target, mech); isrear = (hitGroup == BACK); /* Record the damage for debugging then dish it out */ inflicted_damage = mech_damage; spread = mech_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); MyDamageMech(mech, target, 1, MechPilot(target), hitloc, isrear, iscritical, 5, 0); } if(mech_damage % 5) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); MyDamageMech(mech, target, 1, MechPilot(target), hitloc, isrear, iscritical, (mech_damage % 5), 0); } hitGroup = FindAreaHitGroup(mech, target); isrear = (hitGroup == BACK); /* Ok now how much damage will the second unit take from * charging */ if(mudconf.btech_newcharge && mudconf.btech_tl3_charge) target_damage = (((((float) MechChargeDistance(target)) * MP1) - MechSpeed(mech) * cos((MechFacing(target) - MechFacing(mech)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(mech) + 5) / 20; else target_damage = (MechRealTons(mech) + 5) / 10; /* REUSED! */ /* Record the damage for debugging then dish it out */ received_damage = target_damage; spread = target_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); MyDamageMech2(target, target, 0, -1, hitloc, isrear, iscritical, 5, 0); } if(mech_damage % 5) { hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); MyDamageMech2(target, target, 0, -1, hitloc, isrear, iscritical, (mech_damage % 5), 0); } /* Stop him */ MechSpeed(target) = 0; MechDesiredSpeed(target) = 0; /* Emit the damage for debugging purposes */ snprintf(emit_buff, LBUF_SIZE, "#%i charges #%i (%i/%i) Distance:" " %.2f DI: %i DR: %i", target->mynum, mech->mynum, targ_baseToHit, targ_roll, MechChargeDistance(target), inflicted_damage, received_damage); SendDebug(emit_buff); if(MechType(mech) == CLASS_MECH && !MadePilotSkillRoll(mech, 2)) { mech_notify(mech, MECHALL, "Your piloting skill fails and you fall over!!"); MechFalls(mech, 1, 1); } if(MechType(target) == CLASS_MECH && !MadePilotSkillRoll(target, 2)) { mech_notify(target, MECHALL, "Your piloting skill fails and you fall over!!"); MechFalls(target, 1, 1); } } /* Cycle the sections so they can't make another attack for a while */ if(MechType(mech) == CLASS_MECH) { for(i = 0; i < CHARGE_SECTIONS; i++) SetRecycleLimb(mech, resect[i], PHYSICAL_RECYCLE_TIME); } else { SetRecycleLimb(mech, FSIDE, PHYSICAL_RECYCLE_TIME); SetRecycleLimb(mech, TURRET, PHYSICAL_RECYCLE_TIME); } if(MechType(target) == CLASS_MECH) { for(i = 0; i < CHARGE_SECTIONS; i++) SetRecycleLimb(target, resect[i], PHYSICAL_RECYCLE_TIME); } else { SetRecycleLimb(target, FSIDE, PHYSICAL_RECYCLE_TIME); SetRecycleLimb(target, TURRET, PHYSICAL_RECYCLE_TIME); } /* MechChargeTarget(mech) and the others are set after the return */ MechChargeTarget(target) = -1; MechChargeTimer(target) = 0; MechChargeDistance(target) = 0; return; } /* Check to see if any weapons cycling in any of the sections */ for(i = 0; i < CHARGE_SECTIONS; i++) { if(SectHasBusyWeap(mech, i)) { ArmorStringFromIndex(i, location, MechType(mech), MechMove(mech)); mech_printf(mech, MECHALL, "You have weapons recycling on your %s.", location); return; } } /* Check if they going fast enough to charge */ DOCHECKMA(MechSpeed(mech) < MP1, "You aren't moving fast enough to charge."); /* Check to see if their sections cycling */ if(MechType(mech) == CLASS_MECH) { DOCHECKMA(MechSections(mech)[LLEG].recycle || MechSections(mech)[RLEG].recycle, "Your legs are still recovering from your last attack."); DOCHECKMA(MechSections(mech)[RARM].recycle || MechSections(mech)[LARM].recycle, "Your arms are still recovering from your last attack."); } else { DOCHECKMA(MechSections(mech)[FSIDE].recycle, "You are still recovering from your last attack!"); } /* See if either the target or the attacker are jumping */ DOCHECKMA(Jumping(target), "Your target is jumping, you charge underneath it."); DOCHECKMA(Jumping(mech), "You can't charge while jumping, try death from above."); /* If target is fallen make sure you in a tank */ DOCHECKMA(Fallen(target) && (MechType(mech) != CLASS_VEH_GROUND), "Your target's too low for you to charge it!"); /* Only mechs can charge mechs */ DOCHECKMA((MechType(mech) == CLASS_MECH) && (MechType(target) != CLASS_MECH), "You can only charge mechs!"); /* Only tanks can charge tanks and mechs */ DOCHECKMA((MechType(mech) == CLASS_VEH_GROUND) && ((MechType(target) != CLASS_MECH) && (MechType(target) != CLASS_VEH_GROUND)), "You can only charge mechs and tanks!"); /* Check the arc make sure target is in front arc */ ts = MechStatus(mech) & (TORSO_LEFT | TORSO_RIGHT); MechStatus(mech) &= ~ts; iwa = InWeaponArc(mech, MechFX(target), MechFY(target)); MechStatus(mech) |= ts; DOCHECKMA(!(iwa & FORWARDARC), "Your charge target is not in your forward arc and you are unable to charge it."); /* Damage inflicted by the charge */ if(mudconf.btech_newcharge) target_damage = (((((float) MechChargeDistance(mech)) * MP1) - MechSpeed(target) * cos((MechFacing(mech) - MechFacing(target)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(mech) + 5) / 10 + 1; else target_damage = ((MechSpeed(mech) - MechSpeed(target) * cos((MechFacing(mech) - MechFacing(target)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(mech) + 5) / 10 + 1; if(HasBoolAdvantage(MechPilot(mech), "melee_specialist")) target_damage++; /* Not enough damage done so no charge */ DOCHECKMP(target_damage <= 0, "Your target pulls away from you and you are unable to charge it."); /* BTH */ baseToHit += FindPilotPiloting(mech) - FindSPilotPiloting(target); baseToHit += (HasBoolAdvantage(MechPilot(mech), "melee_specialist") ? MIN(0, AttackMovementMods(mech) - 1) : AttackMovementMods(mech)); baseToHit += TargetMovementMods(mech, target, 0.0); #ifdef BT_MOVEMENT_MODES if(Dodging(target)) baseToHit += 2; #endif DOCHECKMA(baseToHit > 12, tprintf("Charge: BTH %d\tYou choose not to charge.", baseToHit)); /* Roll */ roll = Roll(); mech_printf(mech, MECHALL, "Charge: BTH %d\tRoll: %d", baseToHit, roll); /* Did the charge work ? */ if(roll >= baseToHit) { /* OUCH */ MechLOSBroadcasti(mech, target, tprintf("%ss %%s!", MechType(mech) == CLASS_MECH ? "charge" : "ram")); mech_printf(target, MECHSTARTED, "CRASH!!!\n%s %ss into you!", GetMechToMechID(target, mech), MechType(mech) == CLASS_MECH ? "charge" : "ram"); mech_notify(mech, MECHALL, "SMASH!!! You crash into your target!"); hitGroup = FindAreaHitGroup(mech, target); if(hitGroup == BACK) isrear = 1; else isrear = 0; /* Record the damage then dish it out */ inflicted_damage = target_damage; spread = target_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, isrear, iscritical, 5, 0); } if(target_damage % 5) { hitloc = FindHitLocation(target, hitGroup, &iscritical, &isrear); MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, isrear, iscritical, (target_damage % 5), 0); } hitGroup = FindAreaHitGroup(target, mech); isrear = (hitGroup == BACK); /* Damage done to the attacker for the charge */ if(mudconf.btech_newcharge && mudconf.btech_tl3_charge) mech_damage = (((((float) MechChargeDistance(mech)) * MP1) - MechSpeed(target) * cos((MechFacing(mech) - MechFacing(target)) * (M_PI / 180.))) * MP_PER_KPH) * (MechRealTons(target) + 5) / 20; else mech_damage = (MechRealTons(target) + 5) / 10; /* Record the damage then dish it out */ received_damage = mech_damage; spread = mech_damage / 5; for(i = 0; i < spread; i++) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); MyDamageMech2(mech, mech, 0, -1, hitloc, isrear, iscritical, 5, 0); } if(mech_damage % 5) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); MyDamageMech2(mech, mech, 0, -1, hitloc, isrear, iscritical, (mech_damage % 5), 0); } /* Force piloting roll for attacker if they are in a mech */ if(MechType(mech) == CLASS_MECH && !MadePilotSkillRoll(mech, 2)) { mech_notify(mech, MECHALL, "Your piloting skill fails and you fall over!!"); MechFalls(mech, 1, 1); } /* Force piloting roll for target if they are in a mech */ if(MechType(target) == CLASS_MECH && !MadePilotSkillRoll(target, 2)) { mech_notify(target, MECHSTARTED, "Your piloting skill fails and you fall over!!"); MechFalls(target, 1, 1); } /* Stop him */ MechSpeed(mech) = 0; MechDesiredSpeed(mech) = 0; /* Emit the damage for debugging purposes */ snprintf(emit_buff, LBUF_SIZE, "#%i charges #%i (%i/%i) Distance:" " %.2f DI: %i DR: %i", mech->mynum, target->mynum, baseToHit, roll, MechChargeDistance(mech), inflicted_damage, received_damage); SendDebug(emit_buff); } /* Cycle the sections so they can't make another attack for a while */ if(MechType(mech) == CLASS_MECH) { for(i = 0; i < CHARGE_SECTIONS; i++) SetRecycleLimb(mech, resect[i], PHYSICAL_RECYCLE_TIME); } else { SetRecycleLimb(mech, FSIDE, PHYSICAL_RECYCLE_TIME); SetRecycleLimb(mech, TURRET, PHYSICAL_RECYCLE_TIME); } return; } // end ChargeMech() /* * Checks to see if we can grab a club with our arms. */ int checkGrabClubLocation(MECH * mech, int section, int emit) { int tCanGrab = 1; char buf[100]; char location[20]; ArmorStringFromIndex(section, location, MechType(mech), MechMove(mech)); if(SectIsDestroyed(mech, section)) { sprintf(buf, "Your %s is destroyed.", location); tCanGrab = 0; } else if(!OkayCritSectS(section, 3, HAND_OR_FOOT_ACTUATOR)) { sprintf(buf, "Your %s's hand actuator is destroyed or missing.", location); tCanGrab = 0; } else if(!OkayCritSectS(section, 0, SHOULDER_OR_HIP)) { sprintf(buf, "Your %s's shoulder actuator is destroyed or missing.", location); tCanGrab = 0; } else if(SectHasBusyWeap(mech, section)) { sprintf(buf, "Your %s is still recovering from it's last attack.", location); tCanGrab = 0; } if(!tCanGrab && emit) mech_notify(mech, MECHALL, buf); return tCanGrab; } // end checkGrabClubLocation() /* * Handles the grabbing of a club. */ void mech_grabclub(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; int wcArgs = 0; int location = 0; char *args[1]; char locname[20]; cch(MECH_USUALO); wcArgs = mech_parseattributes(buffer, args, 1); // If we grabclub -, we're attempting to drop it. if(wcArgs >= 1 && toupper(args[0][0]) == '-') { if((MechSections(mech)[LARM].specials & CARRYING_CLUB) || (MechSections(mech)[RARM].specials & CARRYING_CLUB)) { DropClub(mech); } else { mech_notify(mech, MECHALL, "You aren't currently carrying a club."); } return; } // end if() - Check to drop club. DOCHECKMA(MechIsQuad(mech), "Quads can't carry a club."); DOCHECKMA(Fallen(mech), "You can't grab a club while lying flat on your face."); DOCHECKMA(Jumping(mech), "You can't grab a club while jumping!"); DOCHECKMA(OODing(mech), "Your rapid descent prevents that."); DOCHECKMA(UnJammingAmmo(mech), "You are too busy unjamming a weapon!"); DOCHECKMA(RemovingPods(mech), "You are too busy removing iNARC pods!"); // If they already have a physical weapon, disallow the grabbing of a club. DOCHECKMA(have_axe(mech, LARM) || have_axe(mech, RARM), "You can not grab a club if you carry an axe."); DOCHECKMA(have_sword(mech, LARM) || have_sword(mech, RARM), "You can not grab a club if you carry a sword."); DOCHECKMA(have_mace(mech, LARM) || have_mace(mech, RARM), "You can not grab a club if you carry an mace."); if(wcArgs == 0) { if(checkGrabClubLocation(mech, LARM, 0)) location = LARM; else if(checkGrabClubLocation(mech, RARM, 0)) location = RARM; else { mech_notify(mech, MECHALL, "You don't have a free arm with a working hand actuator!"); return; } } else { // Figure out which arm to use. switch (toupper(args[0][0])) { case 'R': location = RARM; break; case 'L': location = LARM; break; default: mech_notify(mech, MECHALL, "Invalid option for 'grabclub'"); return; } // end switch() - Determine location. // see if we have actuators and a working arm. if(!checkGrabClubLocation(mech, location, 1)) return; } DOCHECKMA(CarryingClub(mech), "You're already carrying a club."); DOCHECKMA(MechRTerrain(mech) != HEAVY_FOREST && MechRTerrain(mech) != LIGHT_FOREST, "There don't appear to be any trees within grabbing distance."); ArmorStringFromIndex(location, locname, MechType(mech), MechMove(mech)); MechLOSBroadcast(mech, "reaches down and yanks a tree out of the ground!"); mech_printf(mech, MECHALL, "You reach down and yank a tree out of the ground with your %s.", locname); // Grabbing a club sets a flag and recycles the arm used. MechSections(mech)[location].specials |= CARRYING_CLUB; SetRecycleLimb(mech, location, PHYSICAL_RECYCLE_TIME); } // end mech_grabclub()