/* * $Id: mech.physical.c,v 1.5 2005/07/25 15:14:55 av1-op Exp $ * * 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 * All rights reserved * * Last modified: Thu Sep 10 07:37:00 1998 fingon * */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <sys/file.h> #include "mech.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" #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)); #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?"); #define QUAD_CHECK(a) \ DOCHECK(MechType(mech) == CLASS_MECH && MechIsQuad(mech), \ tprintf("What are you going to %s with, your front right leg?", a)) /* * All 'mechs with arms can punch. */ static int have_punch(MECH * mech, int loc) { return 1; } /* * 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]); 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; } } switch (*using) { case P_LEFT: if (!have_fn(mech, LARM)) { mech_notify(mech, MECHALL, tprintf("You don't have %s in your left arm!", weapon)); return 1; } break; case P_RIGHT: if (!have_fn(mech, RARM)) { mech_notify(mech, MECHALL, tprintf("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; } return 0; } void mech_punch(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map; char *argl[5]; char **args = argl; int argc, ltohit = 4, rtohit = 4; int punching = P_LEFT | P_RIGHT; mech_map = getMap(mech->mapindex); cch(MECH_USUALO); ARM_PHYS_CHECK("punch"); QUAD_CHECK("punch"); #ifdef BT_MOVEMENT_MODES DOCHECK(Dodging(mech) || MoveModeLock(mech), "You cannot use physicals while using a special movement mode."); #endif argc = mech_parseattributes(buffer, args, 5); if (mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 1; if (get_arm_args(&punching, &argc, &args, mech, have_punch, "")) { return; } if (punching & P_LEFT) { if (SectIsDestroyed(mech, LARM)) mech_notify(mech, MECHALL, "Your left arm is destroyed, you can't punch with it."); else if (!OkayCritSectS(LARM, 0, SHOULDER_OR_HIP)) mech_notify(mech, MECHALL, "Your left shoulder is destroyed, you can't punch with that arm."); else { if (Fallen(mech)) { DOCHECK(SectIsDestroyed(mech, RARM), "You need both arms functional to punch while prone."); DOCHECK(SectHasBusyWeap(mech, RARM), "You have weapons recycling on your Right Arm."); DOCHECK(MechSections(mech)[RARM].recycle, "Your Right Arm is still recovering from your last attack."); } DOCHECK(MechSections(mech)[RARM].specials & CARRYING_CLUB, "You're carrying a club in that arm."); PhysicalAttack(mech, 10, ltohit, PA_PUNCH, argc, args, mech_map, LARM); } } if (punching & P_RIGHT) { if (SectIsDestroyed(mech, RARM)) mech_notify(mech, MECHALL, "Your right arm is destroyed, you can't punch with it."); else if (!OkayCritSectS(RARM, 0, SHOULDER_OR_HIP)) mech_notify(mech, MECHALL, "Your right shoulder is destroyed, you can't punch with that arm."); else { if (Fallen(mech)) { DOCHECK(SectIsDestroyed(mech, LARM), "You need both arms functional to punch while prone."); DOCHECK(SectHasBusyWeap(mech, LARM), "You have weapons recycling on your Left Arm."); DOCHECK(MechSections(mech)[LARM].recycle, "Your Left Arm is still recovering from your last attack."); } DOCHECK(MechSections(mech)[LARM].specials & CARRYING_CLUB, "You're carrying a club in that arm."); PhysicalAttack(mech, 10, rtohit, PA_PUNCH, argc, args, mech_map, RARM); } } } void mech_club(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map; char *args[5]; int argc; int clubLoc = -1; mech_map = getMap(mech->mapindex); cch(MECH_USUALO); ARM_PHYS_CHECK("club"); QUAD_CHECK("club"); #ifdef BT_MOVEMENT_MODES DOCHECK(Dodging(mech) || MoveModeLock(mech), "You cannot use physicals while using a special movement mode."); #endif 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."); 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."); 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); } int have_axe(MECH * mech, int loc) { return FindObj(mech, loc, I2Special(AXE)) >= (MechTons(mech) / 15); } int have_sword(MECH * mech, int loc) { return FindObj(mech, loc, I2Special(SWORD)) >= ((MechTons(mech) + 15) / 20); } int have_mace(MECH * mech, int loc) { return FindObj(mech, loc, I2Special(MACE)) >= (MechTons(mech) / 15); } void mech_axe(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map; char *argl[5]; char **args = argl; int argc, ltohit = 4, rtohit = 4; int using = P_LEFT | P_RIGHT; mech_map = getMap(mech->mapindex); cch(MECH_USUALO); ARM_PHYS_CHECK("axe"); QUAD_CHECK("axe"); #ifdef BT_MOVEMENT_MODES DOCHECK(Dodging(mech) || MoveModeLock(mech), "You cannot use physicals while using a special movement mode."); #endif argc = mech_parseattributes(buffer, args, 5); if (mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 1; ltohit += MechSections(mech)[LARM].basetohit; rtohit += MechSections(mech)[RARM].basetohit; if (get_arm_args(&using, &argc, &args, mech, have_axe, "an axe")) { return; } if (using & P_LEFT) { DOCHECK(SectIsDestroyed(mech, LARM), "Your left arm is destroyed, you can't axe with it."); DOCHECK(!OkayCritSectS(LARM, 0, SHOULDER_OR_HIP), "Your left shoulder is destroyed, you can't axe with that arm."); DOCHECK(!OkayCritSectS(LARM, 3, HAND_OR_FOOT_ACTUATOR), "Your left hand is destroyed, you can't axe with that arm."); PhysicalAttack(mech, 5, ltohit, PA_AXE, argc, args, mech_map, LARM); } if (using & P_RIGHT) { DOCHECK(SectIsDestroyed(mech, RARM), "Your right arm is destroyed, you can't axe with it."); DOCHECK(!OkayCritSectS(RARM, 0, SHOULDER_OR_HIP), "Your right shoulder is destroyed, you can't axe with that arm."); DOCHECK(!OkayCritSectS(RARM, 3, HAND_OR_FOOT_ACTUATOR), "Your right hand is destroyed, you can't axe with that arm."); PhysicalAttack(mech, 5, rtohit, PA_AXE, argc, args, mech_map, RARM); } DOCHECKMA(!using, "You may lack the axe, but not the will! Try punch/club until you find one."); } void mech_sword(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map; char *argl[5]; char **args = argl; int argc, ltohit = 3, rtohit = 3; int using = P_LEFT | P_RIGHT; mech_map = getMap(mech->mapindex); cch(MECH_USUALO); ARM_PHYS_CHECK("chop"); QUAD_CHECK("chop"); #ifdef BT_MOVEMENT_MODES DOCHECK(Dodging(mech) || MoveModeLock(mech), "You cannot use physicals while using a special movement mode."); #endif argc = mech_parseattributes(buffer, args, 5); if (mudconf.btech_phys_use_pskill) ltohit = rtohit = FindPilotPiloting(mech) - 2; ltohit += MechSections(mech)[LARM].basetohit; rtohit += MechSections(mech)[RARM].basetohit; if (get_arm_args(&using, &argc, &args, mech, have_sword, "a sword")) { return; } if (using & P_LEFT) { DOCHECK(SectIsDestroyed(mech, LARM), "Your left arm is destroyed, you can't use a sword with it."); DOCHECK(!OkayCritSectS(LARM, 0, SHOULDER_OR_HIP), "Your left shoulder is destroyed, you can't use a sword with that arm."); DOCHECK(!OkayCritSectS(LARM, 3, HAND_OR_FOOT_ACTUATOR), "Your left hand is destroyed, you can't use a sword with that arm."); PhysicalAttack(mech, 10, ltohit, PA_SWORD, argc, args, mech_map, LARM); } if (using & P_RIGHT) { DOCHECK(SectIsDestroyed(mech, RARM), "Your right arm is destroyed, you can't use a sword with it."); DOCHECK(!OkayCritSectS(RARM, 0, SHOULDER_OR_HIP), "Your right shoulder is destroyed, you can't use a sword with that arm."); DOCHECK(!OkayCritSectS(RARM, 3, HAND_OR_FOOT_ACTUATOR), "Your right hand is destroyed, you can't use a sword with that arm."); PhysicalAttack(mech, 10, rtohit, PA_SWORD, argc, args, mech_map, RARM); } DOCHECKMA(!using, "You have no sword to chop people with!"); } void mech_kick(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map; char *argl[5]; char **args = argl; int argc; int rl = RLEG, ll = LLEG; int leg; int using = P_RIGHT; mech_map = getMap(mech->mapindex); cch(MECH_USUALO); if (MechIsQuad(mech)) { rl = RARM; ll = LARM; } GENERIC_CHECK("kick", CountDestroyedLegs(mech)); #ifdef BT_MOVEMENT_MODES DOCHECK(Dodging(mech) || MoveModeLock(mech), "You cannot use physicals while using a special movement mode."); #endif argc = mech_parseattributes(buffer, args, 5); 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; } PhysicalAttack(mech, 5, (mudconf.btech_phys_use_pskill ? FindPilotPiloting(mech) - 2 : 3), PA_KICK, argc, args, mech_map, leg); } void mech_charge(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data, *target; MAP *mech_map; int targetnum; char targetID[5]; char *args[5]; int argc; int wcDeadLegs = 0; mech_map = getMap(mech->mapindex); cch(MECH_USUALO); #ifdef BT_MOVEMENT_MODES DOCHECK(Dodging(mech) || MoveModeLock(mech), "You cannot use physicals while using a special movement mode."); #endif DOCHECK(MechType(mech) == CLASS_MW || MechType(mech) == CLASS_BSUIT, "You cannot charge without a 'mech!"); DOCHECK(MechType(mech) != CLASS_MECH && (MechType(mech) != CLASS_VEH_GROUND || MechSpecials(mech) & SALVAGE_TECH), "You cannot charge with this vehicle!"); 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?"); } argc = mech_parseattributes(buffer, args, 2); switch (argc) { 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; } 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; 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; } 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_notify(mech, MECHALL, tprintf("%s target set to %s.", MechType(mech) == CLASS_MECH ? "Charge" : "Ram", GetMechToMechID(mech, target))); break; default: notify(player, "Invalid number of arguments!"); } } char *phys_form(int at, int flag) { switch (flag) { case 0: switch (at) { case PA_PUNCH: return "punches"; case PA_CLUB: case PA_MACE: return "clubs"; case PA_SWORD: return "chops"; case PA_AXE: return "axes"; case PA_KICK: return "kicks"; } break; default: switch (at) { case PA_PUNCH: return "punch"; case PA_SWORD: return "chop"; case PA_CLUB: case PA_MACE: return "club"; case PA_AXE: return "axe"; case PA_KICK: return "kick"; } break; } return "??bug??"; } #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, 0))); } void phys_fail(MECH * mech, MECH * target, int at) { phys_message(tprintf("attempts to %s %%s!", phys_form(at, 1))); } 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; char location[20]; int ts = 0, iwa; DOCHECKMA(Fallen(mech) && (AttackType != PA_PUNCH), "You can't attack from a prone position."); #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); if (AttackType == PA_KICK) DOCHECKMA((MechCritStatus(mech) & HIP_DAMAGED), "You can not kick if your have a destroyed hip."); if (SectHasBusyWeap(mech, sect)) { ArmorStringFromIndex(sect, location, MechType(mech), MechMove(mech)); mech_notify(mech, MECHALL, tprintf("You have weapons recycling on your %s.", location)); return; } switch (AttackType) { case PA_MACE: case PA_SWORD: case PA_AXE: DOCHECKMA(MechSections(mech)[LARM].recycle || MechSections(mech)[RARM].recycle, "You still have arms recovering from another attack."); DOCHECKMA(Fallen(mech), "You can't do this while fallen!"); case PA_PUNCH: DOCHECKMA(MechSections(mech)[LLEG].recycle || MechSections(mech)[RLEG].recycle, "You're still recovering from another attack."); if (MechSections(mech)[LARM].recycle) DOCHECKMA(MechSections(mech)[LARM].config & AXED, "You are recovering from another attack."); if (MechSections(mech)[RARM].recycle) DOCHECKMA(MechSections(mech)[RARM].config & AXED, "You are recovering from another attack."); break; case PA_CLUB: DOCHECKMA(MechSections(mech)[LLEG].recycle || MechSections(mech)[RLEG].recycle, "You're still recovering from your kick."); /* Check Weapons recycling on LARM because we only checked RARM above. */ DOCHECKMA(SectHasBusyWeap(mech, LARM), "You have weapons recycling on your Left Arm."); DOCHECKMA(Fallen(mech), "You can't do this while fallen!"); break; case PA_KICK: DOCHECKMA(Fallen(mech), "You can't kick while fallen!"); if (MechIsQuad(mech)) { DOCHECKMA(MechSections(mech)[LLEG].recycle || MechSections(mech)[RLEG].recycle, "Your rear legs are still recovering from your last attack."); DOCHECKMA(MechSections(mech)[RARM].recycle || MechSections(mech)[LARM].recycle, "Your front legs are not ready to attack again."); } else { DOCHECKMA(MechSections(mech)[LLEG].recycle || MechSections(mech)[RLEG].recycle, "Your legs are not ready to attack again."); DOCHECKMA(MechSections(mech)[RARM].recycle || MechSections(mech)[LARM].recycle, "Your arms are still recovering from your last attack."); } break; } /* major section recycle */ if (MechSections(mech)[sect].recycle != 0) { ArmorStringFromIndex(sect, location, MechType(mech), MechMove(mech)); mech_notify(mech, MECHALL, tprintf("Your %s is not ready to attack again.", location)); return; } switch (argc) { case -1: case 0: DOCHECKMA(MechTarget(mech) == -1, "You do not have a default target set!"); target = getMech(MechTarget(mech)); DOCHECKMA(!target, "Invalid default target!"); if ((MechType(target) == CLASS_BSUIT) || (MechType(target) == CLASS_MW)) maxRange = 0.5; DOCHECKMA((Fallen(mech) && (AttackType == PA_PUNCH)) && (MechType(target) != CLASS_VEH_GROUND), "You can only punch vehicles while you're fallen"); range = FaMechRange(mech, target); DOCHECKMA(!InLineOfSight_NB(mech, target, MechX(target), MechY(target), range), "You are unable to hit that target at the moment."); DOCHECKMA(range >= maxRange, "Target out of range!"); break; default: /* Any number of targets, take only the first -- mw 93 Oct */ 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(!target, "Invalid default target!"); if ((MechType(target) == CLASS_BSUIT) || (MechType(target) == CLASS_MW)) maxRange = 0.5; range = FaMechRange(mech, target); DOCHECKMA(!InLineOfSight_NB(mech, target, MechX(target), MechY(target), range), "Target is not in line of sight!"); DOCHECKMA(range >= maxRange, "Target out of range!"); } DOCHECKMA((Fallen(mech) && (AttackType == PA_PUNCH)) && (MechType(target) != CLASS_VEH_GROUND), "You can only punch vehicles while you're fallen"); DOCHECKMA(MechTeam(target) == MechTeam(mech) && MechNoFriendlyFire(mech), "You can't attack a teammate with FFSafeties on!"); 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."); if (MechMove(target) != MOVE_VTOL && MechMove(target) != MOVE_FLY) { DOCHECKMA((AttackType == PA_PUNCH || AttackType == PA_AXE || AttackType == PA_SWORD) && (MechZ(mech) - 1) > MechZ(target), tprintf("The target is too low in elevation for you to %s.", AttackType == PA_PUNCH ? "punch at." : "axe it.")); DOCHECKMA(AttackType == PA_KICK && 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) && (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 for you to kick at.") } else { DOCHECKMA((AttackType == PA_PUNCH || AttackType == PA_AXE || AttackType == PA_SWORD) && MechZ(target) - MechZ(mech) > 3, tprintf("The target is too far away for you to %s.", AttackType == PA_PUNCH ? "punch at" : "axe it")); DOCHECKMA(AttackType == PA_KICK && MechZ(mech) != MechZ(target), "The target is too far away for you to kick at."); DOCHECKMA(!(MechZ(target) - MechZ(mech) > -1 && MechZ(target) - MechZ(mech) < 4), "You can't attack, the elevation difference is too large."); } /* 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) { 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 { iwa = InWeaponArc(mech, MechFX(target), MechFY(target)); if (AttackType == PA_CLUB) { DOCHECKMA(!(iwa & FORWARDARC), "Target is not in your forward arc!"); } else { if (sect == RARM) { DOCHECKMA(!((iwa & FORWARDARC) || (iwa & RSIDEARC)), "Target is not in your forward or right side arc!"); } else { DOCHECKMA(!((iwa & FORWARDARC) || (iwa & LSIDEARC)), "Target is not in your forward or left side arc!"); } } } /* Add in the movement modifiers */ 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; baseToHit += ((MechType(target) == CLASS_BSUIT) && (AttackType == PA_KICK)) ? 3 : 0; #ifdef MOVEMENT_MODES if (Dodging(target)) baseToHit += 2; #endif if ((AttackType == PA_PUNCH || AttackType == PA_AXE || AttackType == PA_SWORD) && MechType(target) == CLASS_BSUIT && MechSwarmTarget(target) > 0) baseToHit += (AttackType == PA_AXE || AttackType == PA_SWORD) ? 3 : 5; DOCHECKMA((AttackType != PA_PUNCH && AttackType != PA_AXE && AttackType != PA_SWORD) && MechType(target) == CLASS_BSUIT && MechSwarmTarget(target) > 0, "You can hit swarming 'suits only with punches (or axe/sword)!"); roll = Roll(); switch (AttackType) { case PA_PUNCH: DOCHECKMA((Fallen(mech) && (AttackType == PA_PUNCH)) && (MechType(target) != CLASS_VEH_GROUND), "You can only punch vehicles while you're fallen"); if (MechZ(mech) >= MechZ(target)) DOCHECKMA(((Fallen(target) && MechType(target) == CLASS_MECH) || ((MechType(target) != CLASS_MECH && (MechType(target) != CLASS_BSUIT || MechSwarmTarget(target) != mech->mynum) && !IsDS(target)) && !Fallen(mech))), "The target is too low to punch!"); DOCHECKMA(Jumping(target) || (Jumping(mech) && MechType(target) == CLASS_MECH), "You cannot physically attack a jumping mech!"); DOCHECKMA(Standing(mech), "You are still trying to stand up!"); mech_notify(mech, MECHALL, tprintf("You try to punch the %s. BTH: %d,\tRoll: %d", GetMechToMechID(mech, target), baseToHit, roll)); mech_notify(target, MECHSTARTED, tprintf("%s tries to punch you!", GetMechToMechID(target, mech))); /* Switching to Exile method of tracking xp, where we split * Attacking and Piloting xp into two different channels * And since this is neither it goes to its own channel */ SendAttacks(tprintf("#%i attacks #%i (punch) (%i/%i)", mech->mynum, target->mynum, baseToHit, roll)); /* SendXP(tprintf("#%i attacks #%i (punch) (%i/%i)", mech->mynum, target->mynum, baseToHit, roll)); */ break; case PA_SWORD: case PA_AXE: case PA_CLUB: DOCHECKMA(Jumping(target) || (Jumping(mech) && MechType(target) == CLASS_MECH), "You cannot physically attack a jumping mech!"); DOCHECKMA(Standing(mech), "You are still trying to stand up!"); if (AttackType == PA_CLUB) { mech_notify(mech, MECHALL, tprintf("You try and club %s. BaseToHit: %d,\tRoll: %d", GetMechToMechID(mech, target), baseToHit, roll)); mech_notify(target, MECHSTARTED, tprintf("%s tries to club you!", GetMechToMechID(target, mech))); /* Switching to Exile method of tracking xp, where we split * Attacking and Piloting xp into two different channels * And since this is neither it goes to its own channel */ SendAttacks(tprintf("#%i attacks #%i (club) (%i/%i)", mech->mynum, target->mynum, baseToHit, roll)); /* SendXP(tprintf("#%i attacks #%i (club) (%i/%i)", mech->mynum, target->mynum, baseToHit, roll)); */ } else { mech_notify(mech, MECHALL, tprintf ("You try to swing your %s at %s. BTH: %d,\tRoll: %d", AttackType == PA_SWORD ? "sword" : "axe", GetMechToMechID(mech, target), baseToHit, roll)); mech_notify(target, MECHSTARTED, tprintf("%s tries to %s you!", GetMechToMechID(target, mech), AttackType == PA_SWORD ? "swing a sword at" : "axe")); /* Switching to Exile method of tracking xp, where we split * Attacking and Piloting xp into two different channels * And since this is neither it goes to its own channel */ SendAttacks(tprintf("#%i attacks #%i (%s) (%i/%i)", mech->mynum, target->mynum, AttackType == PA_SWORD ? "sword" : "axe", baseToHit, roll)); /* SendXP(tprintf("#%i attacks #%i (%s) (%i/%i)", mech->mynum, target->mynum, AttackType == PA_SWORD ? "sword" : "axe", baseToHit, roll)); */ } break; case PA_KICK: DOCHECKMA(Jumping(target) || (Jumping(mech) && MechType(target) == CLASS_MECH), "You cannot physically attack a jumping mech!"); DOCHECKMA(Standing(mech), "You are still trying to stand up!"); mech_notify(mech, MECHALL, tprintf("You try and kick %s. BaseToHit: %d,\tRoll: %d", GetMechToMechID(mech, target), baseToHit, roll)); mech_notify(target, MECHSTARTED, tprintf("%s tries to kick you!", GetMechToMechID(target, mech))); /* Switching to Exile method of tracking xp, where we split * Attacking and Piloting xp into two different channels * And since this is neither it goes to its own channel */ SendAttacks(tprintf("#%i attacks #%i (kick) (%i/%i)", mech->mynum, target->mynum, baseToHit, roll)); /* SendXP(tprintf("#%i attacks #%i (kick) (%i/%i)", mech->mynum, target->mynum, baseToHit, roll)); */ } /* set the sections to recycling */ SetRecycleLimb(mech, sect, PHYSICAL_RECYCLE_TIME); if (AttackType == PA_AXE || AttackType == PA_SWORD) MechSections(mech)[sect].config |= AXED; if (AttackType == PA_PUNCH) MechSections(mech)[sect].config &= ~AXED; if (AttackType == PA_CLUB) SetRecycleLimb(mech, LARM, PHYSICAL_RECYCLE_TIME); if (roll >= baseToHit) { /* hit the target */ phys_succeed(mech, target, AttackType); 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; } } PhysicalDamage(mech, target, damageweight, AttackType, sect); } else { 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); } } if (AttackType == PA_KICK || AttackType == PA_CLUB) { mech_notify(mech, MECHALL, "You miss and try to remain standing!"); if (!MadePilotSkillRoll(mech, 0)) { mech_notify(mech, MECHALL, "You lose your balance and fall down!"); MechFalls(mech, 1, 1); } } } } 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 void PhysicalDamage(MECH * mech, MECH * target, int weightdmg, int AttackType, int sect) { int hitloc = 0, damage, hitgroup = 0, isrear, iscritical, i; if (AttackType == PA_SWORD) damage = (MechTons(mech) + 5) / weightdmg + 1; else damage = (MechTons(mech) + weightdmg / 2) / weightdmg; if ((MechHeat(mech) >= 9.) && (MechSpecials(mech) & TRIPLE_MYOMER_TECH)) damage = damage * 2; if (HasBoolAdvantage(MechPilot(mech), "melee_specialist")) damage++; switch (AttackType) { case PA_PUNCH: if (sect == LARM) { if (!OkayCritSectS(LARM, 2, LOWER_ACTUATOR)) damage = damage / 2; if (!OkayCritSectS(LARM, 1, UPPER_ACTUATOR)) damage = damage / 2; } else if (sect == RARM) { if (!OkayCritSectS(RARM, 2, LOWER_ACTUATOR)) damage = damage / 2; if (!OkayCritSectS(RARM, 1, UPPER_ACTUATOR)) damage = damage / 2; } hitgroup = FindAreaHitGroup(mech, target); if (MechType(mech) == CLASS_MECH) { 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; case PA_SWORD: case PA_AXE: 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; case PA_KICK: if (sect == LLEG) { if (!OkayCritSectS(LLEG, 2, LOWER_ACTUATOR)) damage = damage / 2; if (!OkayCritSectS(LLEG, 1, UPPER_ACTUATOR)) damage = damage / 2; } else if (sect == RLEG) { if (!OkayCritSectS(RLEG, 2, LOWER_ACTUATOR)) damage = damage / 2; if (!OkayCritSectS(RLEG, 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; } MyDamageMech(target, mech, 1, MechPilot(mech), hitloc, (hitgroup == BACK) ? 1 : 0, 0, damage, 0); if (MechType(target) == CLASS_BSUIT && MechSwarmTarget(target) > 0 && (AttackType == PA_PUNCH || AttackType == PA_AXE || AttackType == PA_SWORD)) StopSwarming(target, 0); 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); } } #define CHARGE_SECTIONS 6 #define DFA_SECTIONS 4 /* 4 if pure FASA */ const int resect[CHARGE_SECTIONS] = { LARM, RARM, LLEG, RLEG, LTORSO, RTORSO }; 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_notify(mech, MECHALL, tprintf("You have weapons recycling on your %s.", location)); return 0; } DOCHECKMA0((mech->mapindex != target->mapindex), "Invalid Target."); DOCHECKMA0(((MechTeam(mech) == MechTeam(target)) && (Started(target)) && (!Destroyed(target))), "Friendly units ? I dont Think so.."); #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 jumping, 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_notify(mech, MECHALL, tprintf("DFA: BTH %d\tRoll: %d", baseToHit, roll)); MechStatus(mech) &= ~JUMPING; MechStatus(mech) &= ~DFA_ATTACK; if (roll >= baseToHit) { /* OUCH */ mech_notify(target, MECHSTARTED, tprintf("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; } 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_notify(mech, MECHALL, tprintf("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_notify(target, MECHALL, tprintf("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_notify(mech, MECHALL, tprintf("Charge: BTH %d\tYou choose not to charge.", mech_baseToHit)); mech_charge = 0; } if (target_charge) if (targ_baseToHit > 12) { mech_notify(target, MECHALL, tprintf("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_notify(mech, MECHALL, tprintf("Charge: BTH %d\tRoll: %d", mech_baseToHit, mech_roll)); if (target_charge) mech_notify(target, MECHALL, tprintf("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_notify(target, MECHALL, tprintf("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_notify(mech, MECHALL, tprintf("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_notify(mech, MECHALL, tprintf("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_notify(mech, MECHALL, tprintf("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_notify(target, MECHSTARTED, tprintf("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; } 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; } 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 (wcArgs >= 1) { if (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; } } DOCHECKMA(MechIsQuad(mech), "Quads can't carry around a club."); DOCHECKMA(Fallen(mech), "You can't grab a club while lying flat on your face."); DOCHECKMA(Jumping(mech), "Um, well, you're like jumping and stuff."); DOCHECKMA(OODing(mech), "You're too busy falling from the sky."); DOCHECKMA(UnJammingAmmo(mech), "You are too busy unjamming a weapon!"); DOCHECKMA(RemovingPods(mech), "You are too busy removing iNARC pods!"); 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 { 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; break; } 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_notify(mech, MECHALL, tprintf ("You reach down and yank a tree out of the ground with your %s.", locname)); MechSections(mech)[location].specials |= CARRYING_CLUB; SetRecycleLimb(mech, location, PHYSICAL_RECYCLE_TIME); }