/* * $Id: mech.move.c,v 1.6 2005/08/10 14:09:34 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: Mon Oct 26 20:15:47 1998 fingon * */ #include "config.h" #include <stdio.h> #include <stdlib.h> #include <math.h> #include <sys/file.h> #include "mech.h" #include "mech.events.h" #include "p.mech.events.h" #include "p.mech.ice.h" #include "p.mech.utils.h" #include "p.mine.h" #include "p.bsuit.h" #include "p.mech.los.h" #include "p.mech.update.h" #include "p.mech.physical.h" #include "p.mech.combat.h" #include "p.mech.combat.misc.h" #include "p.mech.damage.h" #include "p.btechstats.h" #include "p.mech.hitloc.h" #include "p.template.h" #include "p.map.conditions.h" #include "p.mech.fire.h" #include "mech.events.h" struct { char *name; char *full; int ofs; } lateral_modes[] = { { "nw", "Front/Left", 300}, { "fl", "Front/Left", 300}, { "ne", "Front/Right", 60}, { "fr", "Front/Right", 60}, { "sw", "Rear/Left", 240}, { "rl", "Rear/Left", 240}, { "se", "Rear/Right", 120}, { "rr", "Rear/Right", 120}, { "-", "None", 0}, { NULL, 0} }; const char *LateralDesc(MECH * mech) { int i; for (i = 0; MechLateral(mech) != lateral_modes[i].ofs; i++); return lateral_modes[i].full; } void mech_lateral(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; int i; cch(MECH_USUALO); #ifndef BT_MOVEMENT_MODES DOCHECK(MechType(mech) != CLASS_MECH || !MechIsQuad(mech), "Only quadrupeds can alter their lateral movement!"); #else DOCHECK(!((MechType(mech) == CLASS_MECH && MechIsQuad(mech)) || (MechType(mech) == CLASS_VTOL) || (MechMove(mech) == MOVE_HOVER)), "You cannot alter your lateral movement!"); #endif DOCHECK(CountDestroyedLegs(mech) > 0, "You need all four legs to use lateral movement!"); skipws(buffer); for (i = 0; lateral_modes[i].name; i++) if (!strcasecmp(lateral_modes[i].name, buffer)) break; DOCHECK(!lateral_modes[i].name, "Invalid mode!"); if (lateral_modes[i].ofs == MechLateral(mech)) { DOCHECK(!ChangingLateral(mech), "You are going that way already!"); mech_notify(mech, MECHALL, "Lateral mode change aborted."); StopLateral(mech); return; } mech_notify(mech, MECHALL, tprintf("Wanted lateral movement mode changed to %s.", lateral_modes[i].full)); StopLateral(mech); MECHEVENT(mech, EVENT_LATERAL, mech_lateral_event, LATERAL_TICK, i); } void mech_turnmode(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; if (!GotPilot(mech) || MechPilot(mech) != player) { notify(player, "You're not the pilot!"); return; } if (!HasBoolAdvantage(player, "maneuvering_ace")) { mech_notify(mech, MECHPILOT, "You're not skilled enough to do that."); return; } if (buffer && !strcasecmp(buffer, "tight")) { SetTurnMode(mech,1); mech_notify(mech, MECHALL, "You brace for tighter turns."); return; } if (buffer && !strcasecmp(buffer, "normal")) { SetTurnMode(mech,0); mech_notify(mech, MECHALL, "You assume a normal turn mode."); return; } mech_notify(mech, MECHALL, tprintf("Your turning type is : %s", GetTurnMode(mech) ? "TIGHT" : "NORMAL")); return; } void mech_bootlegger(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; float fMinSpeed = (4 * MP1); int wBTHMod = 0; int wFallLevels = 0; int i; int wHeadingChange = 0; int wNewHeading; float fMechSpeed = MechSpeed(mech); int wMechTons = MechTons(mech); char strLocation[50]; char *args[1]; cch(MECH_USUALO); DOCHECK(mech_parseattributes(buffer, args, 1) != 1, "Invalid number of arguments!"); DOCHECK(CountDestroyedLegs(mech) > 0, "You can't perform a bootlegger with destroyed legs!"); DOCHECK(fMechSpeed < fMinSpeed, tprintf ("You are going too slow to perform a bootlegger! The required minimum speed is %4.1f KPH.", fMinSpeed)); switch (toupper(args[0][0])) { case 'R': wHeadingChange = 90; break; case 'L': wHeadingChange = -90; break; } DOCHECK(wHeadingChange == 0, "Invalid turn direction!"); for (i = 0; i < NUM_SECTIONS; i++) { if ((i == LLEG) || (i == RLEG) || (MechIsQuad(mech) && ((i == LARM) || (i == RARM)))) { ArmorStringFromIndex(i, strLocation, MechType(mech), MechMove(mech)); if (SectHasBusyWeap(mech, i)) { mech_notify(mech, MECHALL, tprintf("You have weapons recycling in your %s.", strLocation)); return; } if (MechSections(mech)[i].recycle) { mech_notify(mech, MECHALL, tprintf ("Your %s is still recovering from its last action.", strLocation)); return; } wBTHMod += MechSections(mech)[i].basetohit; } } if (fMechSpeed <= (4 * MP1)) { wBTHMod += 0; } else if (fMechSpeed <= (8 * MP1)) { wBTHMod += 1; } else if (fMechSpeed <= (12 * MP1)) { wBTHMod += 2; } else { wBTHMod += 3; } if (wMechTons <= 35) { wBTHMod += 0; } else if (wMechTons <= 55) { wBTHMod += 1; } else if (wMechTons <= 75) { wBTHMod += 2; } else { wBTHMod += 3; } wBTHMod += (InWater(mech) ? 2 : 0); wBTHMod = MAX(wBTHMod, 1); skipws(buffer); SendDebug(tprintf ("#%d attempts to do a bootlegger (mech). Tonnage: %d, Speed: %4.1f, BTHMod: %d", mech->mynum, wMechTons, fMechSpeed, wBTHMod)); if (MadePilotSkillRoll(mech, wBTHMod)) { wNewHeading = AcceptableDegree(MechFacing(mech) + wHeadingChange); SetFacing(mech, wNewHeading); MechDesiredFacing(mech) = wNewHeading; MechSpeed(mech) = MechSpeed(mech) / 2; mech_notify(mech, MECHALL, tprintf ("You plant a foot and swivel, changing your heading to %d.", wNewHeading)); for (i = 0; i < NUM_SECTIONS; i++) { if ((i == LLEG) || (i == RLEG) || (MechIsQuad(mech) && ((i == LARM) || (i == RARM)))) SetRecycleLimb(mech, i, 30); } } else { wFallLevels = MAX(wBTHMod, 1); mech_notify(mech, MECHALL, "You plant a foot and try to swivel..."); mech_notify(mech, MECHALL, "... but realize a little late that this is harder than it looks!"); MechLOSBroadcast(mech, "attempts to fight the forces of inertia but looses the battle miserably!"); if (wFallLevels > 2) MechLOSBroadcast(mech, "tumbles over and over and over!"); MechFalls(mech, wFallLevels, 1); } } void mech_eta(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MAP *mech_map; int argc, eta_x, eta_y; float fx, fy, range; int etahr, etamin; char *args[3]; cch(MECH_USUAL); mech_map = getMap(mech->mapindex); argc = mech_parseattributes(buffer, args, 2); DOCHECK(argc == 1, "Invalid number of arguments!"); switch (argc) { case 0: DOCHECK(!(MechTargX(mech) >= 0 && MechTarget(mech) < 0), "You have invalid default target for ETA!"); eta_x = MechTargX(mech); eta_y = MechTargY(mech); break; case 2: eta_x = atoi(args[0]); eta_y = atoi(args[1]); break; default: notify(player, "Invalid arguments!"); return; } MapCoordToRealCoord(eta_x, eta_y, &fx, &fy); range = FindRange(MechFX(mech), MechFY(mech), 0, fx, fy, 0); if (fabs(MechSpeed(mech)) < 0.1) mech_notify(mech, MECHALL, tprintf ("Range to hex (%d,%d) is %.1f. ETA: Never, mech not moving.", eta_x, eta_y, range)); else { etamin = abs(range / (MechSpeed(mech) / KPH_PER_MP)); etahr = etamin / 60; etamin = etamin % 60; mech_notify(mech, MECHALL, tprintf("Range to hex (%d,%d) is %.1f. ETA: %.2d:%.2d.", eta_x, eta_y, range, etahr, etamin)); } } float MechCargoMaxSpeed(MECH * mech, float mspeed) { int lugged = 0, mod = 2; MECH *c; MAP *map; if (MechCarrying(mech) > 0) { /* Ug-lee! */ MECH *t; if ((t = getMech(MechCarrying(mech)))) if (!(MechCritStatus(t) & OWEIGHT_OK)) MechCritStatus(mech) &= ~LOAD_OK; } /*! \todo {Fix this calculation to include gravity and TSM for * when BT_MOVMENT_MODES is enabled} */ if ((MechCritStatus(mech) & LOAD_OK) && (MechCritStatus(mech) & OWEIGHT_OK) && (MechCritStatus(mech) & SPEED_OK)) { mspeed = MechRMaxSpeed(mech); #ifndef BT_MOVEMENT_MODES /* Is masc and/or scharge on */ if ((MechStatus(mech) & MASC_ENABLED) && (MechStatus(mech) & SCHARGE_ENABLED)) mspeed = ceil((rint(mspeed / 1.5) / MP1) * 2.5) * MP1; else if (MechStatus(mech) & MASC_ENABLED) mspeed *= 4. / 3.; else if (MechStatus(mech) & SCHARGE_ENABLED) mspeed *= 4. / 3.; if (InSpecial(mech) && InGravity(mech)) if ((map = FindObjectsData(mech->mapindex))) mspeed = mspeed * 100 / MAX(50, MapGravity(map)); #else /* Is masc and/or scharge and/or sprinting on */ if (MechStatus(mech) & (MASC_ENABLED|SCHARGE_ENABLED) || MechStatus2(mech) & SPRINTING) mspeed = WalkingSpeed(mspeed) * (1.5 + (MechStatus(mech) & MASC_ENABLED ? 0.5 : 0.0) + (MechStatus(mech) & SCHARGE_ENABLED ? 0.5 : 0.0) + (!MoveModeChange(mech) && MechStatus2(mech) & SPRINTING ? 0.5 : 0.0)); /* if the player has speed demon give him his boost in speed */ if (!MoveModeChange(mech) && MechStatus2(mech) & SPRINTING && HasBoolAdvantage(MechPilot(mech), "speed_demon")) mspeed += MP1; #endif return mspeed; } MechRTonsV(mech) = get_weight(mech); /*! \todo {Check some of this math better} */ if (!(MechCritStatus(mech) & LOAD_OK)) { if (MechCarrying(mech) > 0) if ((c = getMech(MechCarrying(mech)))) { lugged = get_weight(c) * 2; if (MechSpecials(mech) & SALVAGE_TECH) lugged = lugged / 2; if ((MechSpecials(mech) & TRIPLE_MYOMER_TECH) && (MechHeat(mech) >= 9.)) lugged = lugged / 2; if (MechSpecials2(mech) & CARRIER_TECH) lugged = lugged / 2; } if (MechSpecials(mech) & CARGO_TECH) mod = 1; if (MechType(mech) == CLASS_MECH) mod = mod * 2; lugged += MechCarriedCargo(mech) * mod / 2; MechRCTonsV(mech) = lugged; MechCritStatus(mech) |= LOAD_OK; } if (Destroyed(mech)) mspeed = 0.0; else { int mv = MechRTonsV(mech); int sv = MechTons(mech) * 1024; if (mv == 1 && !Destroyed(mech)) mv = sv; else { if (mv > sv) mv = mv + (mv - sv) / 2; else mv = mv + (sv - mv) / 3; } if (3 * sv < (MechRCTonsV(mech) + mv)) mspeed = 0.0; else #ifdef WEIGHT_OVERSPEEDING mspeed = MechMaxSpeed(mech) * MechTons(mech) * 1024.0 / MAX(1024 * MechRealTons(mech) + MechRCTonsV(mech) / 3, (MAX(1024, mv + MechRCTonsV(mech)))); #else mspeed = MechMaxSpeed(mech) * MechTons(mech) * 1024.0 / MAX(1024 * MechTons(mech) + MechRCTonsV(mech) / 3, (MAX(1024, mv + MechRCTonsV(mech)))); #endif /* WEIGHT_OVERSPEEDING */ } MechRMaxSpeed(mech) = mspeed; MechWalkXPFactor(mech) = MAX(1, (int) mspeed / MP1) * 2; MechCritStatus(mech) |= SPEED_OK; return MMaxSpeed(mech); } void mech_drop(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; float s1; int wDropLevels = 0; int wDropBTH = 0; int tHasSwarmers = 0; cch(MECH_USUAL); DOCHECK(MechType(mech) == CLASS_BSUIT, "No crawling (yet)!"); DOCHECK(MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW, "This vehicle cannot drop like a 'Mech."); DOCHECK(Fallen(mech), "You are already prone."); DOCHECK(Jumping(mech) || OODing(mech), "You can't drop while jumping!"); DOCHECK(Standing(mech), "You can't drop while trying to stand up!"); s1 = MMaxSpeed(mech) / 3.0; if ((MechType(mech) == CLASS_MECH) && CountSwarmers(mech)) tHasSwarmers = 1; if (MechType(mech) != CLASS_MW && fabs(MechSpeed(mech)) > s1 * 2) { mech_notify(mech, MECHALL, "You attempt a controlled drop while running."); wDropLevels = 2; wDropBTH = 2; } else if (fabs(MechSpeed(mech)) > s1) { mech_notify(mech, MECHALL, "You attempt a controlled drop from your fast walk."); wDropLevels = 1; } if (tHasSwarmers) mech_notify(mech, MECHALL, "The suits hanging off you make a controlled drop harder!"); if ((wDropLevels > 0) || tHasSwarmers) { if (MadePilotSkillRoll(mech, wDropBTH)) { mech_notify(mech, MECHALL, "You hit the ground with minimal damage"); MechLOSBroadcast(mech, "drops to the ground!"); if (tHasSwarmers) StopBSuitSwarmers(FindObjectsData(mech->mapindex), mech, 0); } else { mech_notify(mech, MECHALL, "You fall to the ground hard"); MechLOSBroadcast(mech, "falls hard to the ground!"); if (wDropLevels <= 0) wDropLevels = 1; if (tHasSwarmers) StopBSuitSwarmers(FindObjectsData(mech->mapindex), mech, 0); MechFalls(mech, wDropLevels, 1); } } else { mech_notify(mech, MECHALL, "You drop to the ground prone!"); MechLOSBroadcast(mech, "drops to the ground!"); } MakeMechFall(mech); MechDesiredSpeed(mech) = 0; MechSpeed(mech) = 0; MechFloods(mech); water_extinguish_inferno(mech); possible_mine_poof(mech, MINE_STEP); } void mech_stand(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; char *args[2]; int wcDeadLegs = 0; int tNeedsPSkill = 1; int tDoStand = 1; int bth, standanyway = 0; int i; cch(MECH_USUAL); DOCHECK(MechType(mech) == CLASS_BSUIT, "You're standing already!"); DOCHECK(MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW, "This vehicle cannot stand like a 'Mech."); DOCHECK(Jumping(mech), "You're standing while jumping!"); DOCHECK(OODing(mech), "You're standing while flying!"); /* set the number of dead legs we have */ wcDeadLegs = CountDestroyedLegs(mech); DOCHECK(((MechIsQuad(mech) && (wcDeadLegs > 3)) || (!MechIsQuad(mech) && (wcDeadLegs > 1))), "You have no legs to stand on!"); DOCHECK(wcDeadLegs > 2, "You'd be far too unstable!"); DOCHECK(MechCritStatus(mech) & GYRO_DESTROYED, "You cannot stand with a destroyed gyro!"); DOCHECK(!Fallen(mech), "You're already standing!"); DOCHECK(Standrecovering(mech), "You're still recovering from your last attempt!"); DOCHECK(IsHulldown(mech), "You can not stand while hulldown"); DOCHECK(ChangingHulldown(mech), "You are busy changing your hulldown mode"); bth = MechPilotSkillRoll_BTH(mech, 0); DOCHECK(!standanyway && bth > 12, "You would fail; use 'stand anyway' if you really want to stand."); /* Check to see if the user specified an argument for the command */ if (proper_explodearguments(buffer, args, 2)) { switch (tolower(args[0][0])) { case 'c': notify_printf(player, "Your BTH to stand would be: %d", bth); for(i = 0; i < 2; i++) { if(args[i]) free(args[i]); } return; case 'a': standanyway = 1; break; default: notify(player, "Unknown argument! use 'stand check' or " "'stand anyway'"); break; } } MakeMechStand(mech); /* quads with all 4 legs don't have to roll to stand */ if (((wcDeadLegs == 0) && MechIsQuad(mech)) || (MechType(mech) == CLASS_MW)) { tNeedsPSkill = 0; } MechLOSBroadcast(mech, "attempts to stand up."); if (MechRTerrain(mech) == ICE && MechZ(mech) == -1) break_thru_ice(mech); if (tNeedsPSkill) { if (!MadePilotSkillRoll(mech, 0)) { mech_notify(mech, MECHALL, "You fail your attempt to stand and fall back on the ground"); MechFalls(mech, 1, 1); MECHEVENT(mech, EVENT_STANDFAIL, mech_standfail_event, MechType(mech) == CLASS_MW ? DROP_TO_STAND_RECYCLE / 3 : StandMechTime(mech), 0); tDoStand = 0; } } if (tDoStand) { /* Now we set a counter in goingy to keep him from moving or jumping until he is finished standing */ mech_notify(mech, MECHALL, "You begin to stand up."); MECHEVENT(mech, EVENT_STAND, mech_stand_event, MechType(mech) == CLASS_MW ? DROP_TO_STAND_RECYCLE / 3 : StandMechTime(mech), 0); } /* Free args */ for(i = 0; i < 2; i++) { if(args[i]) free(args[i]); } } void mech_land(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; cch(MECH_USUAL); if (MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW && MechType(mech) != CLASS_BSUIT && MechType(mech) != CLASS_VEH_GROUND) { aero_land(player, data, buffer); return; } if (Jumping(mech)) { mech_notify(mech, MECHALL, "You abort your full jump and attempt to land early"); if (MadePilotSkillRoll(mech, 0)) { mech_notify(mech, MECHALL, "You are able to abort the jump."); /* MechLOSBroadcast (mech, "lands abruptly!"); */ LandMech(mech); } else { mech_notify(mech, MECHALL, "You don't quite make it."); MechLOSBroadcast(mech, "attempts a landing, but crashes to the ground!"); MechFalls(mech, 1, 0); MechDFATarget(mech) = -1; MechGoingX(mech) = MechGoingY(mech) = 0; MechSpeed(mech) = 0; MaybeMove(mech); } } else notify(player, "You're not jumping!"); } /* Facing related */ void mech_heading(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; char *args[1]; int newheading; cch(MECH_USUAL); if (mech_parseattributes(buffer, args, 1) == 1) { DOCHECK(MechMove(mech) == MOVE_NONE, "This piece of equipment is stationary!"); DOCHECK(WaterBeast(mech) && NotInWater(mech), "You are regrettably unable to move at this time. We apologize for the inconvenience."); DOCHECK(is_aero(mech) && Spinning(mech) && !Landed(mech), "You are unable to control your craft at the moment."); DOCHECK(PerformingAction(mech), "You are too busy at the moment to turn."); DOCHECK(MechDugIn(mech), "You are in a hole you dug, unable to move [use speed cmd to get out]."); DOCHECK(IsHulldown(mech), "You can not turn while hulldown"); DOCHECK(ChangingHulldown(mech), "You are busy changing your hulldown mode"); if (Digging(mech)) { mech_notify(mech, MECHALL, "You cease your attempts at digging in."); StopDigging(mech); } newheading = AcceptableDegree(atoi(args[0])); MechDesiredFacing(mech) = newheading; mech_notify(mech, MECHALL, tprintf("Heading changed to %d.", newheading)); MaybeMove(mech); } else { notify(player, tprintf("Your current heading is %i.", MechFacing(mech))); } } void mech_turret(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; char *args[1]; int newheading; cch(MECH_USUALO); DOCHECK(MechType(mech) == CLASS_MECH || MechType(mech) == CLASS_MW || MechType(mech) == CLASS_BSUIT || is_aero(mech) || !GetSectInt(mech, TURRET), "You don't have a turret."); DOCHECK(MechTankCritStatus(mech) & TURRET_JAMMED, "Your turret is jammed in position."); DOCHECK(MechTankCritStatus(mech) & TURRET_LOCKED, "Your turret is locked in position."); if (mech_parseattributes(buffer, args, 1) == 1) { newheading = AcceptableDegree(atoi(args[0]) - MechFacing(mech)); MechTurretFacing(mech) = newheading; mech_notify(mech, MECHALL, tprintf("Turret facing changed to %d.", AcceptableDegree(MechTurretFacing(mech) + MechFacing(mech)))); } else { notify(player, tprintf("Your turret is currently facing %d.", AcceptableDegree(MechTurretFacing(mech) + MechFacing(mech)))); } MarkForLOSUpdate(mech); } void mech_rotatetorso(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; char *args[2]; cch(MECH_USUALO); DOCHECK(MechType(mech) == CLASS_BSUIT, "Huh?"); DOCHECK(MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW, "You don't have a torso."); DOCHECK(Fallen(mech), "You're lying flat on your face, you can't rotate your torso."); DOCHECK((MechType(mech) == CLASS_MECH) && (MechIsQuad(mech)), "Quads can't rotate their torsos."); if (mech_parseattributes(buffer, args, 2) == 1) { switch (args[0][0]) { case 'L': case 'l': DOCHECK(MechStatus(mech) & TORSO_LEFT, "You cannot rotate torso beyond 60 degrees!"); if (MechStatus(mech) & TORSO_RIGHT) MechStatus(mech) &= ~TORSO_RIGHT; else MechStatus(mech) |= TORSO_LEFT; mech_notify(mech, MECHALL, "You rotate your torso left."); break; case 'R': case 'r': DOCHECK(MechStatus(mech) & TORSO_RIGHT, "You cannot rotate torso beyond 60 degrees!"); if (MechStatus(mech) & TORSO_LEFT) MechStatus(mech) &= ~TORSO_LEFT; else MechStatus(mech) |= TORSO_RIGHT; mech_notify(mech, MECHALL, "You rotate your torso right."); break; case 'C': case 'c': MechStatus(mech) &= ~(TORSO_RIGHT | TORSO_LEFT); mech_notify(mech, MECHALL, "You center your torso."); break; default: notify(player, "Rotate must have LEFT RIGHT or CENTER."); break; } } else notify(player, "Invalid number of arguments!"); MarkForLOSUpdate(mech); } struct { char *name; int flag; } speed_tables[] = { { "walk", 1}, { "run", 2}, { "stop", 0}, { "back", -1}, { "cruise", 1}, { "flank", 2}, { NULL, 0.0} }; void mech_speed(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; char *args[1]; float newspeed, walkspeed, maxspeed; int i; cch(MECH_USUAL); if (RollingT(mech)) { DOCHECK(!Landed(mech), "Use thrust command instead!"); } else if (FlyingT(mech)) { DOCHECK(MechType(mech) != CLASS_VTOL, "Use thrust command instead!"); } DOCHECK(MechMove(mech) == MOVE_NONE, "This piece of equipment is stationary!"); DOCHECK(PerformingAction(mech), "You are too busy at the moment to turn."); DOCHECK(Standing(mech), "You are currently standing up and cannot move."); DOCHECK((Fallen(mech)) && (MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW), "Your vehicle's movement system is destroyed."); DOCHECK(Fallen(mech), "You are currently prone and cannot move."); DOCHECK(WaterBeast(mech) && NotInWater(mech), "You are regrettably unable to move at this time. We apologize for the inconvenience."); if (MechType(mech) != CLASS_MECH) DOCHECK(RemovingPods(mech), "You are too busy removing iNARC pods!"); DOCHECK(IsHulldown(mech), "You can not move while hulldown"); DOCHECK(ChangingHulldown(mech), "You are busy changing your hulldown mode"); if (mech_parseattributes(buffer, args, 1) != 1) { notify(player, tprintf("Your current speed is %.2f.", MechSpeed(mech))); return; } DOCHECK(FlyingT(mech) && AeroFuel(mech) <= 0 && !AeroFreeFuel(mech), "You're out of fuel!"); maxspeed = MMaxSpeed(mech); if (MechMove(mech) == MOVE_VTOL) maxspeed = sqrt((float) maxspeed * maxspeed - MechVerticalSpeed(mech) * MechVerticalSpeed(mech)); maxspeed = maxspeed > 0.0 ? maxspeed : 0.0; if ((MechHeat(mech) >= 9.) && (MechSpecials(mech) & TRIPLE_MYOMER_TECH)) maxspeed = ceil((rint((maxspeed / 1.5) / MP1) + 1) * 1.5) * MP1; /* if (MechStatus(mech) & MASC_ENABLED) maxspeed = (4. / 3. ) * maxspeed; */ walkspeed = WalkingSpeed(maxspeed); newspeed = atof(args[0]); if (newspeed < 0.1) { /* Possibly a string speed instead? */ for (i = 0; speed_tables[i].name; i++) if (!strcasecmp(speed_tables[i].name, args[0])) { switch (speed_tables[i].flag) { case 0: newspeed = 0.0; break; case -1: newspeed = -walkspeed; break; case 1: newspeed = walkspeed; break; case 2: newspeed = maxspeed; break; } break; } } if (newspeed > maxspeed) newspeed = maxspeed; if (newspeed < -walkspeed) newspeed = -walkspeed; DOCHECK((newspeed < 0) && (MechCarrying(mech) > 0) && (!(MechSpecials(mech) & SALVAGE_TECH)), "You can not backup while towing!"); if (IsRunning(newspeed, maxspeed)) { DOCHECK(Dumping(mech), "You can not run while dumping ammo!"); DOCHECK(UnJammingAmmo(mech), "You can not run while unjamming your weapon!"); /* Exile Stun Code Effect */ if (MechCritStatus(mech) & MECH_STUNNED) { mech_notify(mech, MECHALL, "You cannot move faster than cruise" " speed while stunned!"); return; } DOCHECK(CrewStunned(mech), "Your cannot possibly control a vehicle going this fast in your " "current mental state!"); DOCHECK(MechTankCritStatus(mech) & TAIL_ROTOR_DESTROYED, "Your cannot possibly control a VTOL going this fast with a destroyed tail rotor!"); DOCHECK(MechType(mech) == CLASS_MECH && ((MechZ(mech) < 0 && (MechRTerrain(mech) == WATER || MechRTerrain(mech) == BRIDGE || MechRTerrain(mech) == ICE)) || MechRTerrain(mech) == HIGHWATER), "You can't run through water!"); } if (!Wizard(player) && In_Character(mech->mynum) && MechPilot(mech) != player) { if (newspeed < 0.0) { notify(player, "Not being the Pilot of this beast, you cannot move it backwards."); return; } else if (newspeed > walkspeed) { notify(player, "Not being the Pilot of this beast, you cannot go faster than walking speed."); return; } } MechDesiredSpeed(mech) = newspeed; MaybeMove(mech); if (fabs(newspeed) > 0.1) { if (MechSwarmTarget(mech) > 0) { StopSwarming(mech, 1); MechCritStatus(mech) &= ~HIDDEN; } if (Digging(mech)) { mech_notify(mech, MECHALL, "You cease your attempts at digging in."); StopDigging(mech); } MechTankCritStatus(mech) &= ~DUG_IN; } mech_notify(mech, MECHALL, tprintf("Desired speed changed to %d KPH", (int) newspeed)); } void mech_vertical(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; char *args[1], buff[50]; float newspeed, maxspeed; cch(MECH_USUAL); DOCHECK(MechType(mech) != CLASS_VTOL && MechMove(mech) != MOVE_SUB, "This command is for VTOLs only."); DOCHECK(MechType(mech) == CLASS_VTOL && AeroFuel(mech) <= 0 && !AeroFreeFuel(mech), "You're out of fuel!"); DOCHECK(WaterBeast(mech) && NotInWater(mech), "You are regrettably unable to move at this time. We apologize for the inconvenience."); DOCHECK(mech_parseattributes(buffer, args, 1) != 1, tprintf("Current vertical speed is %.2f KPH.", (float) MechVerticalSpeed(mech))); newspeed = atof(args[0]); maxspeed = MMaxSpeed(mech); maxspeed = sqrt((float) maxspeed * maxspeed - MechDesiredSpeed(mech) * MechDesiredSpeed(mech)); if ((newspeed > maxspeed) || (newspeed < -maxspeed)) { sprintf(buff, "Max vertical speed is + %d KPH and - %d KPH", (int) maxspeed, (int) maxspeed); notify(player, buff); } else { DOCHECK(Fallen(mech), "Your vehicle's movement system is destroyed."); DOCHECK(MechType(mech) == CLASS_VTOL && Landed(mech), "You need to take off first."); MechVerticalSpeed(mech) = newspeed; mech_notify(mech, MECHALL, tprintf("Vertical speed changed to %d KPH", (int) newspeed));; MaybeMove(mech); } } /* * - Only when fallen * - Tonnage / 3 (rounded up for .5) * - 5 Point groups to PA * - Clear or paved terrain only * - Automatically works * - Doesn't hit suits that are swarmed or jumping * - No weapons recycling in arms and legs * - Arms and legs recycle after attack * - Make pskill roll or take damage as if 1 level fall */ void mech_thrash(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MECH *target; MAP *map = getMap(mech->mapindex); int terrain; int limbs = 4; int aLimbs[] = { RARM, LARM, LLEG, RLEG }; int i; int tempLoc; char locName[50]; int damage, tempDamage; cch(MECH_USUALO); DOCHECK(!Fallen(mech), "You need to be prone to thrash!"); DOCHECK(!map, "Invalid map! Contact a wizard!"); terrain = GetRTerrain(map, MechX(mech), MechY(mech)); DOCHECK(((terrain == GRASSLAND) || (terrain == ROAD) || (terrain == BRIDGE)), "Thrashing only works in clear terrain or on roads or bridges."); /* Check locations */ for (i = 0; i < 4; i++) { tempLoc = aLimbs[i]; if (SectIsDestroyed(mech, tempLoc)) { limbs--; continue; } ArmorStringFromIndex(tempLoc, locName, MechType(mech), MechMove(mech)); DOCHECK(SectHasBusyWeap(mech, tempLoc), tprintf("You have weapons recycling on your %s.", locName)); DOCHECK(MechSections(mech)[tempLoc].recycle, tprintf("Your %s is still recovering from your last attack.", locName)); } /* Can't thrash if we have no limbs */ if (!limbs) { mech_notify(mech, MECHALL, "You can't thrash if you have no limbs!"); return; } #ifndef REALWEIGHT_DAMAGE damage = MechTons(mech) / 3; #else damage = MechRealTons(mech) / 3; #endif /* REALWEIGHT_DAMAGE */ damage = (damage * limbs) / 4; mech_notify(mech, MECHALL, "You start to flail your arms and legs like a wild man!"); MechLOSBroadcast(mech, "starts to flail its arms and legs like a wild beast!"); /* Let's see who we can smack around */ for (i = 0; i < map->first_free; i++) { if (map->mechsOnMap[i] >= 0) { target = (MECH *) FindObjectsData(map->mechsOnMap[i]); if (!target) continue; if (MechType(target) != CLASS_BSUIT) continue; if (MechTeam(target) == MechTeam(mech)) continue; if (Jumping(target) || OODing(target)) continue; if (FaMechRange(mech, target) > 1.0) continue; mech_notify(mech, MECHALL, tprintf("You manage to hit %s!", GetMechToMechID(mech, target))); mech_notify(target, MECHALL, tprintf("You get hit by %s's thrashing limbs!", GetMechToMechID(target, mech))); tempDamage = damage; while (tempDamage > 0) { if (tempDamage > 5) { DamageMech(target, mech, 1, MechPilot(mech), Number(0, NUM_BSUIT_MEMBERS - 1), 0, 0, 5, 0, -1, 0, -1, 0, 1); tempDamage -= 5; } else { DamageMech(target, mech, 1, MechPilot(mech), Number(0, NUM_BSUIT_MEMBERS - 1), 0, 0, tempDamage, 0, -1, 0, -1, 0, 1); tempDamage = 0; } } } } /* Make our roll and recycle our limbs */ if (!MadePilotSkillRoll_Advanced(mech, 0, 0)) { MechFalls(mech, 1, 1); } for (i = 0; i < 4; i++) { tempLoc = aLimbs[i]; if (SectIsDestroyed(mech, tempLoc)) continue; SetRecycleLimb(mech, tempLoc, PHYSICAL_RECYCLE_TIME); } } void mech_jump(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; MECH *tempMech = NULL; MAP *mech_map; char *args[3]; int argc; int target; char targetID[2]; short mapx, mapy; int bearing; float range = 0.0; float realx, realy; int sz, tz, jps; mech_map = getMap(mech->mapindex); cch(MECH_USUALO); #ifdef BT_MOVEMENT_MODES DOCHECK(MoveModeLock(mech), "Movement modes disallow jumping."); #endif DOCHECK(MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW && MechType(mech) != CLASS_BSUIT && MechType(mech) != CLASS_VEH_GROUND, "This unit cannot jump."); DOCHECK(MechCarrying(mech) > 0, "You can't jump while towing someone!"); DOCHECK((MechMaxSpeed(mech) - MMaxSpeed(mech)) > MP1, "No, with this cargo you won't!"); DOCHECK(Fallen(mech), "You can't Jump from a FALLEN position"); DOCHECK(IsHulldown(mech), "You can't Jump while hulldown"); DOCHECK(ChangingHulldown(mech), "You are busy changing your hulldown mode"); DOCHECK(Jumping(mech), "You're already jumping!"); DOCHECK(Stabilizing(mech), "You haven't stabilized from your last jump yet."); DOCHECK(Standing(mech), "You haven't finished standing up yet."); DOCHECK(fabs(MechJumpSpeed(mech)) <= 0.0, "This mech doesn't have jump jets!"); argc = mech_parseattributes(buffer, args, 3); DOCHECK(Dumping(mech), "You can not jump while dumping ammo!"); DOCHECK(UnJammingAmmo(mech), "You can not jump while unjamming your weapon!"); DOCHECK(RemovingPods(mech), "You are too busy removing iNARC pods!"); DOCHECK(MapIsUnderground(mech_map), "Realize the ceiling in this grotto is a bit to low for that!"); if (Staggering(mech)) { mech_notify(mech, MECHALL, "The damage inhibits your coordination..."); if (!MadePilotSkillRoll(mech, calcStaggerBTHMod(mech))) { mech_notify(mech, MECHALL, "... something you apparently can't handle!"); MechLOSBroadcast(mech, "engages jumpjets, rolls to the side and slams into the ground!"); MechFalls(mech, 1, 0); return; } } if (doJettisonChecks(mech)) return; DOCHECK(argc > 2, "Too many arguments to JUMP function!"); MechStatus(mech) &= ~DFA_ATTACK; /* By default no DFA */ switch (argc) { case 0: /* DFA current target... */ DOCHECK(MechType(mech) != CLASS_MECH, "Only mechs can do Death From Above attacks!"); target = MechTarget(mech); tempMech = getMech(target); DOCHECK(!tempMech, "Invalid Target!"); range = FaMechRange(mech, tempMech); DOCHECK(!InLineOfSight(mech, tempMech, MechX(tempMech), MechY(tempMech), range), "Target is not in line of sight!"); DOCHECK(MechType(tempMech) == CLASS_MW, "Even you can't aim your jump well enough to squish that!"); mapx = MechX(tempMech); mapy = MechY(tempMech); MechDFATarget(mech) = MechTarget(mech); break; case 1: /* Jump Target */ DOCHECK(MechType(mech) != CLASS_MECH, "Only mechs can do Death From Above attacks!"); targetID[0] = args[0][0]; targetID[1] = args[0][1]; target = FindTargetDBREFFromMapNumber(mech, targetID); tempMech = getMech(target); DOCHECK(!tempMech, "Target is not in line of sight!"); range = FaMechRange(mech, tempMech); DOCHECK(!InLineOfSight(mech, tempMech, MechX(tempMech), MechY(tempMech), range), "Target is not in line of sight!"); DOCHECK(MechType(tempMech) == CLASS_MW, "Even you can't aim your jump well enough to squish that!"); mapx = MechX(tempMech); mapy = MechY(tempMech); MechDFATarget(mech) = tempMech->mynum; break; case 2: bearing = atoi(args[0]); range = atof(args[1]); FindXY(MechFX(mech), MechFY(mech), bearing, range, &realx, &realy); /* This is so we are jumping to the center of a hex */ /* and the bearing jives with the target hex */ RealCoordToMapCoord(&mapx, &mapy, realx, realy); break; } DOCHECK(mapx >= mech_map->map_width || mapy >= mech_map->map_height || mapx < 0 || mapy < 0, "That would take you off the map!"); DOCHECK(MechX(mech) == mapx && MechY(mech) == mapy, "You're already in the target hex."); sz = MechZ(mech); tz = Elevation(mech_map, mapx, mapy); jps = JumpSpeedMP(mech, mech_map); DOCHECK(range > jps, "That target is out of range!"); if (MechType(mech) != CLASS_BSUIT && tempMech) MechStatus(mech) |= DFA_ATTACK; /* MechJumpTop(mech) = BOUNDED(3, (jps - range) + 2, jps - 1); */ /* New idea: JumpTop = (JP + 1 - range / 3) - in another words, SDR jumping for 1 hexes has 8 + 1 = 9 hex elevation in mid-flight, SDR jumping for 8 hexes has 8 + 1 - 2 = 7 hex elevation in mid-flight, TDR jumping for 4 hexes has 4 + 1 - 1 = 4 hex elevation in mid-flight Come to think of it, the last SDR figure was ridiculous. New value: 2 * 1 + 2 = 4 */ MechJumpTop(mech) = MIN(jps + 1 - range / 3, 2 * range + 2); DOCHECK((tz - sz) > jps, "That target's high for you to reach with a single jump!"); DOCHECK((sz - tz) > jps, "That target's low for you to reach with a single jump!"); DOCHECK(sz < -1, "Glub glub glub."); MapCoordToRealCoord(mapx, mapy, &realx, &realy); bearing = FindBearing(MechFX(mech), MechFY(mech), realx, realy); /* TAKE OFF! */ MechCocoon(mech) = 0; MechJumpHeading(mech) = bearing; MechStatus(mech) |= JUMPING; MechStartFX(mech) = MechFX(mech); MechStartFY(mech) = MechFY(mech); MechStartFZ(mech) = MechFZ(mech); MechJumpLength(mech) = length_hypotenuse((double) (realx - MechStartFX(mech)), (double) (realy - MechStartFY(mech))); MechGoingX(mech) = mapx; MechGoingY(mech) = mapy; MechEndFZ(mech) = ZSCALE * tz; MechSpeed(mech) = 0.0; if (MechStatus(mech) & DFA_ATTACK) mech_notify(mech, MECHALL, "You engage your jump jets for a Death From Above attack!"); else mech_notify(mech, MECHALL, "You engage your jump jets"); MechSwarmTarget(mech) = -1; MechLOSBroadcast(mech, "engages jumpjets!"); MECHEVENT(mech, EVENT_JUMP, mech_jump_event, JUMP_TICK, 0); } static void mech_hulldown_event(MUXEVENT * e) { MECH *mech = (MECH *) e->data; int type = (int) e->data2; if (!ChangingHulldown(mech)) return; if (!Started(mech)) return; if (type == 0) { MechStatus(mech) &= ~HULLDOWN; mech_notify(mech, MECHALL, "You finish lifting yourself up."); MechLOSBroadcast(mech, "finishes lifting itself up"); } else { MechStatus(mech) |= HULLDOWN; mech_notify(mech, MECHALL, "You finish lowering yourself to the ground."); MechLOSBroadcast(mech, "finishes lowering itself to the ground."); } } #ifdef BT_MOVEMENT_MODES void mech_sprint(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; int d = 0, i; cch(MECH_USUALO); DOCHECK(MechMove(mech) == MOVE_NONE, "This piece of equipment is stationary!"); DOCHECK(MechCarrying(mech) > 0, "You cannot sprint while towing!"); DOCHECK(Standing(mech), "You are currently standing up and cannot move."); DOCHECK(Jumping(mech), "You cannot do this while jumping."); DOCHECK((Fallen(mech)) && (MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW), "Your vehicle's movement system is destroyed."); DOCHECK(Fallen(mech), "You are currently prone and cannot move."); DOCHECK(WaterBeast(mech) && NotInWater(mech), "You are regrettably unable to move at this time. We apologize for the inconvenience."); DOCHECK(MoveModeChange(mech), "You are already changing movement modes!"); DOCHECK(MechStatus2(mech) & (EVADING|DODGING), "You cannot perform multiple movement modes!"); DOCHECK(MechSwarmTarget(mech) > 0, "You cannot sprint while mounted!"); if (MechType(mech) == CLASS_MECH) DOCHECK(SectIsDestroyed(mech, RLEG) || SectIsDestroyed(mech, LLEG) || (MechMove(mech) != MOVE_QUAD ? 0 : SectIsDestroyed(mech, RLEG) || SectIsDestroyed(mech, LLEG)), "That's kind of hard while limping."); d |= MODE_SPRINT|((MechStatus2(mech) & SPRINTING) ? MODE_OFF : MODE_ON); if (d & MODE_ON) { if ((i = MechFullNoRecycle(mech, CHECK_BOTH)) > 0) { mech_notify(mech, MECHALL, tprintf("You have %s recycling!", (i == 1 ? "weapons" : i == 2 ? "limbs" : "error"))); return; } mech_notify(mech, MECHALL, "You begin the process of sprinting....."); MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, (MechType(mech) == CLASS_BSUIT || MechType(mech) == CLASS_MW) ? TURN / 2 : TURN, d); } else { mech_notify(mech, MECHALL, "You begin the process of ceasing to sprint."); MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, (MechType(mech) == CLASS_BSUIT || MechType(mech) == CLASS_MW) ? TURN / 2 : TURN, d); } return; } void mech_evade(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; int d = 0, i; cch(MECH_USUALO); DOCHECK(MechMove(mech) == MOVE_NONE, "This piece of equipment is stationary!"); DOCHECK(Standing(mech), "You are currently standing up and cannot move."); DOCHECK(Jumping(mech), "You cannot do this while jumping."); DOCHECK((Fallen(mech)) && (MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW), "Your vehicle's movement system is destroyed."); DOCHECK(MechCarrying(mech) > 0, "You can't do that while towing"); DOCHECK(Fallen(mech), "You are currently prone and cannot move."); DOCHECK(!(MechStatus2(mech) & EVADING) && MechType(mech) == CLASS_MECH && (PartIsNonfunctional(mech, LLEG, 0) || PartIsNonfunctional(mech, RLEG, 0)), "You need both hip functional to evade."); DOCHECK(WaterBeast(mech) && NotInWater(mech), "You are regrettably unable to move at this time. We apologize for the inconvenience."); DOCHECK(MoveModeChange(mech), "You are already changing movement modes!"); DOCHECK(MechStatus2(mech) & (SPRINTING|DODGING), "You cannot perform multiple movement modes!"); DOCHECK(MechSwarmTarget(mech) > 0, "You cannot evade while mounted!"); if (MechType(mech) == CLASS_MECH) DOCHECK(SectIsDestroyed(mech, RLEG) || SectIsDestroyed(mech, LLEG) || (MechMove(mech) != MOVE_QUAD ? 0 : SectIsDestroyed(mech, RLEG) || SectIsDestroyed(mech, LLEG)), "That's kind of hard while limping."); d |= MODE_EVADE|((MechStatus2(mech) & EVADING) ? MODE_OFF : MODE_ON); if (d & MODE_ON) { if ((i = MechFullNoRecycle(mech, CHECK_BOTH)) > 0) { mech_notify(mech, MECHALL, tprintf("You have %s recycling!", (i == 1 ? "weapons" : i == 2 ? "limbs" : "error"))); return; } mech_notify(mech, MECHALL, "You begin the process of evading....."); MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, (MechType(mech) == CLASS_BSUIT || MechType(mech) == CLASS_MW) ? TURN / 2 : TURN, d); } else { mech_notify(mech, MECHALL, "You begin the process of ceasing to evade."); MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, (MechType(mech) == CLASS_BSUIT || MechType(mech) == CLASS_MW) ? TURN / 2 : TURN, d); } return; } void mech_dodge(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; int d = 0, i; cch(MECH_USUALO); DOCHECK(MechMove(mech) == MOVE_NONE, "This piece of equipment is stationary!"); DOCHECK(Standing(mech), "You are currently standing up and cannot move."); DOCHECK((Fallen(mech)) && (MechType(mech) != CLASS_MECH && MechType(mech) != CLASS_MW), "Your vehicle's movement system is destroyed."); DOCHECK(Fallen(mech), "You are currently prone and cannot move."); DOCHECK(WaterBeast(mech) && NotInWater(mech), "You are regrettably unable to move at this time. We apologize for the inconvenience."); DOCHECK(MoveModeChange(mech), "You are already changing movement modes!"); DOCHECK(MechStatus2(mech) & (SPRINTING|EVADING), "You cannot perform multiple movement modes!"); DOCHECK(!(HasBoolAdvantage(player, "dodge_maneuver")) || player != MechPilot(mech), "You either are not the pilot of this mech, have no Dodge Maneuver adavantage, or both."); d |= MODE_DODGE|((MechStatus2(mech) & DODGING) ? MODE_OFF : MODE_ON); if (d & MODE_ON) { if ((i = MechFullNoRecycle(mech, CHECK_PHYS)) > 0) { mech_notify(mech, MECHALL, tprintf("You have %s recycling!", (i == 1 ? "weapons" : i == 2 ? "limbs" : "error"))); return; } mech_notify(mech, MECHALL, "You begin the process of dodging....."); MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, 1, d); } else { mech_notify(mech, MECHALL, "You begin the process of ceasing to dodge."); MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, TURN, d); } return; } #endif void mech_hulldown(dbref player, void *data, char *buffer) { MECH *mech = (MECH *) data; char *args[1]; int argc; cch(MECH_USUALO); DOCHECK(!MechIsQuad(mech), "Only QUADs can hulldown."); DOCHECK(Fallen(mech), "You can't hulldown from a FALLEN position"); DOCHECK(Jumping(mech), "You can't hulldown while jumping!"); DOCHECK(MechSpeed(mech) > 0.5, "You can't hulldown while moving!"); DOCHECK(Stabilizing(mech), "You are still stabilizing from your last jump."); DOCHECK(Standing(mech), "You haven't finished standing up yet."); argc = mech_parseattributes(buffer, args, 1); if (argc > 0) { if (!strcmp(args[0], "-")) { if (!IsHulldown(mech)) mech_notify(mech, MECHALL, "You are not hulldown."); else if (ChangingHulldown(mech)) mech_notify(mech, MECHALL, "You are busy changing your hulldown mode."); else { mech_notify(mech, MECHALL, "You start to lift yourself up."); MechLOSBroadcast(mech, "begins to raise up on its legs."); MECHEVENT(mech, EVENT_CHANGING_HULLDOWN, mech_hulldown_event, StandMechTime(mech), 0); } } else if (!strcasecmp(args[0], "stop")) { if (!ChangingHulldown(mech)) mech_notify(mech, MECHALL, "You are not currently changing your hulldown mode."); else { StopHullDown(mech); mech_notify(mech, MECHALL, "You stop changing your hulldown mode."); } } else mech_notify(mech, MECHALL, "Invalid argument for 'hulldown'."); return; } DOCHECK(IsHulldown(mech), "You are already hulldown."); DOCHECK(ChangingHulldown(mech), "You are busy changing your hulldown mode."); mech_notify(mech, MECHALL, "You start to lower yourself to the ground."); MechLOSBroadcast(mech, "begins to lower itself to the ground."); MechDesiredSpeed(mech) = 0; MECHEVENT(mech, EVENT_CHANGING_HULLDOWN, mech_hulldown_event, StandMechTime(mech), 1); } int DropGetElevation(MECH * mech) { if (MechRTerrain(mech) == BRIDGE) { if (MechZ(mech) < (MechElev(mech))) { if (Overwater(mech)) return 0; return bridge_w_elevation(mech); } return MechElevation(mech); } if (Overwater(mech) || (MechRTerrain(mech) == ICE && MechZ(mech) >= 0)) return MAX(0, MechElevation(mech)); else return MechElevation(mech); } void DropSetElevation(MECH * mech, int wantdrop) { if (MechRTerrain(mech) == BRIDGE) { bridge_set_elevation(mech); return; } MechZ(mech) = DropGetElevation(mech); MechFZ(mech) = MechZ(mech) * ZSCALE; if (wantdrop) if (MechRTerrain(mech) == ICE && MechZ(mech) >= 0) possibly_drop_thru_ice(mech); } void LandMech(MECH * mech) { MECH *target; int dfa = 0; int done = 0; /* * Added check to see if we're actually awake when we try to land * - Kipsta * - 8/3/99 */ if (Uncon(mech)) { mech_notify(mech, MECHALL, "Your lack of conciousness makes you fall to the ground. Not like you can read this anyway."); MechFalls(mech, 1, 0); dfa = 1; done = 1; } else { /* Handle DFA attack */ if (MechStatus(mech) & DFA_ATTACK) { /* is the target here? */ target = getMech(MechDFATarget(mech)); if (target) { if (MechX(target) == MechX(mech) && MechY(target) == MechY(mech)) dfa = DeathFromAbove(mech, target); else mech_notify(mech, MECHPILOT, "Your DFA target has moved!"); } else mech_notify(mech, MECHPILOT, "Your target has become invalid."); } if (!dfa) mech_notify(mech, MECHALL, "You finish your jump."); if (Staggering(mech)) { mech_notify(mech, MECHALL, "The damage you've taken makes the landing a bit harder..."); if (!MadePilotSkillRoll(mech, calcStaggerBTHMod(mech))) { mech_notify(mech, MECHALL, "... something you apparently can't handle!"); MechLOSBroadcast(mech, "lands, staggers, and falls down!"); MechFalls(mech, 1, 0); return; } } /* Check piloting rolls, etc. */ if (MechType(mech) == CLASS_MECH) { if (CountDestroyedLegs(mech) > 0) { mech_notify(mech, MECHPILOT, "Your missing leg makes it harder to land"); if (!MadePilotSkillRoll(mech, 0)) { mech_notify(mech, MECHALL, "Your missing leg has caused you to fall upon landing!"); MechLOSBroadcast(mech, "lands, unbalanced, and falls down!"); dfa = 1; MechFalls(mech, 1, 0); done = 1; } } else if (MechSections(mech)[RLEG].basetohit || MechSections(mech)[LLEG].basetohit) { mech_notify(mech, MECHPILOT, "Your damaged leg actuators make it harder to land"); if (!MadePilotSkillRoll(mech, 0)) { mech_notify(mech, MECHALL, "Your damaged leg actuators have caused you to fall upon landing!"); MechLOSBroadcast(mech, "lands, stumbles, and falls down!"); dfa = 1; done = 1; MechFalls(mech, 1, 0); } } } } if ((MechType(mech) == CLASS_MECH) && CountSwarmers(mech)) { mech_notify(mech, MECHALL, "The suits hanging off you make landing harder!"); if (MadePilotSkillRoll(mech, 4)) { StopBSuitSwarmers(FindObjectsData(mech->mapindex), mech, 0); } else { mech_notify(mech, MECHALL, "You fail to properly control your unbalanced landing!"); MechLOSBroadcast(mech, "lands and crashes to the ground from the weight of the battlesuits!"); MechFalls(mech, 1, 0); } } if (!dfa && !Fallen(mech) && !domino_space(mech, 1)) { if (MechType(mech) != CLASS_VEH_GROUND) MechLOSBroadcast(mech, "lands gracefully."); else MechLOSBroadcast(mech, "returns to the ground where it belongs."); } /* If we aren't jumping anymore, we already took care of the event. (e.g. in MechFalls()) */ if (Jumping(mech)) MECHEVENT(mech, EVENT_JUMPSTABIL, mech_stabilizing_event, JUMP_TO_HIT_RECYCLE, 0); MechStatus(mech) &= ~JUMPING; MechStatus(mech) &= ~DFA_ATTACK; MechDFATarget(mech) = -1; MechGoingX(mech) = MechGoingY(mech) = 0; MechSpeed(mech) = 0; StopJump(mech); /* Kill the event for moving 'round */ MaybeMove(mech); /* Possibly start movin' on da ground */ DropSetElevation(mech, 1); if (!done) possible_mine_poof(mech, MINE_LAND); MechFloods(mech); water_extinguish_inferno(mech); StopStaggerCheck(mech); } /* Flooding code. Once we're in water, this is checked now and then (basically when DamageMech'ed and/or depth changes and/or we fall) */ void MechFloodsLoc(MECH * mech, int loc, int lev) { char locbuff[32];; if ((GetSectArmor(mech, loc) && (GetSectRArmor(mech, loc) || !GetSectORArmor(mech, loc))) || !GetSectInt(mech, loc)) return; if (!InWater(mech)) return; if (lev >= 0) return; /* No armor, and in water. */ if (lev == -1 && (!Fallen(mech) && loc != LLEG && loc != RLEG && (!MechIsQuad(mech) || (loc != LARM && loc != RARM)))) return; if (MechType(mech) != CLASS_MECH) return; if (SectIsFlooded(mech, loc)) return; /* Woo, valid target. */ ArmorStringFromIndex(loc, locbuff, MechType(mech), MechMove(mech)); mech_notify(mech, MECHALL, tprintf ("%%ch%%crWater floods into your %s disabling everything that was there!%%c", locbuff)); MechLOSBroadcast(mech, tprintf("has a gaping hole in %s, and water pours in!", locbuff)); SetSectFlooded(mech, loc); DestroyParts(mech, mech, loc, 1, 1); } void MechFloods(MECH * mech) { int i; int elev = MechElevation(mech); if (!InWater(mech)) return; /* Waterproof Tech - no flooding if we have this */ if (MechSpecials2(mech) & WATERPROOF_TECH) return; if (MechType(mech) == CLASS_BSUIT) { if (MechSwarmTarget(mech) > 0) return; mech_notify(mech, MECHALL, "You somehow find yourself in water and realize this may really really suck..."); mech_notify(mech, MECHALL, "Everything gets very dark as water starts to fill your suit " "and you sink towards the bottom!"); MechLOSBroadcast(mech, "shudders, splashes in the water for a second, then goes limp " "and sinks to the bottom."); KillMechContentsIfIC(mech->mynum); DestroyMech(mech, mech, 0); return; } if (MechType(mech) != CLASS_MECH) return; if (MechZ(mech) >= 0) return; for (i = 0; i < NUM_SECTIONS; i++) MechFloodsLoc(mech, i, elev); } void MechFalls(MECH * mech, int levels, int seemsg) { int roll, spread, i, hitloc, hitGroup = 0; int isrear = 0, damage, iscritical = 0; MAP *map; /* get rid of our swarmers */ if (CountSwarmers(mech)) StopBSuitSwarmers(FindObjectsData(mech->mapindex), mech, 0); /* damage pilot */ MechCocoon(mech) = 0; if (MechType(mech) == CLASS_MECH || MechType(mech) == CLASS_MW || seemsg) mech_notify(mech, MECHPILOT, "You try to avoid taking damage in the fall."); else mech_notify(mech, MECHPILOT, "You try to avoid taking damage."); if (!MadePilotSkillRoll(mech, levels)) { if (MechType(mech) == CLASS_MECH || MechType(mech) == CLASS_MW || seemsg) mech_notify(mech, MECHPILOT, "You take personal injury from the fall!"); else mech_notify(mech, MECHPILOT, "You take personal injury!"); headhitmwdamage(mech, 1); } MechSpeed(mech) = 0; MechDesiredSpeed(mech) = 0; if (Jumping(mech)) { MechStatus(mech) &= ~JUMPING; MechStatus(mech) &= ~DFA_ATTACK; StopJump(mech); MECHEVENT(mech, EVENT_JUMPSTABIL, mech_stabilizing_event, JUMP_TO_HIT_RECYCLE, 0); } #ifdef BT_MOVEMENT_MODES if (MoveModeChange(mech)) StopMoveMode(mech); if (MechStatus2(mech) & SPRINTING) MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, (MechType(mech) == CLASS_BSUIT || MechType(mech) == CLASS_MW) ? TURN / 2 : TURN, MODE_SPRINT|MODE_OFF); if (MechStatus2(mech) & EVADING) MECHEVENT(mech, EVENT_MOVEMODE, mech_movemode_event, (MechType(mech) == CLASS_BSUIT || MechType(mech) == CLASS_MW) ? TURN / 2 : TURN, MODE_EVADE|MODE_OFF); #endif if (MechMove(mech) == MOVE_VTOL || MechMove(mech) == MOVE_FLY) { MechVerticalSpeed(mech) = 0; MechGoingY(mech) = 0; MechStartFX(mech) = 0.0; MechStartFY(mech) = 0.0; MechStartFZ(mech) = 0.0; MechStatus(mech) |= LANDED; MechStatus(mech) |= FALLEN; StopMoving(mech); } else MaybeMove(mech); if (MechType(mech) == CLASS_MECH || MechType(mech) == CLASS_MW) MakeMechFall(mech); if (seemsg) MechLOSBroadcast(mech, "falls down!"); DropSetElevation(mech, 1); MechFZ(mech) = MechZ(mech) * ZSCALE; roll = Number(1, 6); switch (roll) { case 1: hitGroup = FRONT; break; case 2: AddFacing(mech, 60); hitGroup = RIGHTSIDE; break; case 3: AddFacing(mech, 120); hitGroup = RIGHTSIDE; break; case 4: AddFacing(mech, 180); hitGroup = BACK; break; case 5: AddFacing(mech, 240); hitGroup = LEFTSIDE; break; case 6: AddFacing(mech, 300); hitGroup = LEFTSIDE; break; } if (hitGroup == BACK) isrear = 1; SetFacing(mech, AcceptableDegree(MechFacing(mech))); MechDesiredFacing(mech) = MechFacing(mech); if (!InWater(mech) && MechRTerrain(mech) != HIGHWATER) #ifndef REALWEIGHT_DAMAGE damage = (levels * (MechTons(mech) + 5)) / 10; #else damage = (levels * (MechRealTons(mech) + 5)) / 10; #endif /* REALWEIGHT_DAMAGE */ else #ifndef REALWEIGHT_DAMAGE damage = (levels * (MechTons(mech) + 5)) / 20; #else damage = (levels * (MechRealTons(mech) + 5)) / 20; #endif /* REALWEIGHT_DAMAGE */ if (InSpecial(mech)) if ((map = FindObjectsData(mech->mapindex))) if (MapUnderSpecialRules(map)) damage = damage * MIN(100, MapGravity(map)) / 100; if (MechType(mech) == CLASS_MW) damage *= 40; spread = damage / 5; for (i = 0; i < spread; i++) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); DamageMech(mech, mech, 0, -1, hitloc, isrear, iscritical, 5, -1, -1, 0, -1, 0, 0); MechFloods(mech); water_extinguish_inferno(mech); } if (damage % 5) { hitloc = FindHitLocation(mech, hitGroup, &iscritical, &isrear); DamageMech(mech, mech, 0, -1, hitloc, isrear, iscritical, (damage % 5), -1, -1, 0, -1, 0, 0); MechFloods(mech); water_extinguish_inferno(mech); } possible_mine_poof(mech, MINE_FALL); MarkForLOSUpdate(mech); } int mechs_in_hex(MAP * map, int x, int y, int friendly, int team) { MECH *mech; int i, cnt = 0; for (i = 0; i < map->first_free; i++) if ((mech = FindObjectsData(map->mechsOnMap[i]))) { if (MechX(mech) != x || MechY(mech) != y) continue; if (Destroyed(mech)) continue; if (!(MechSpecials2(mech) & CARRIER_TECH) && IsDS(mech) && (Landed(mech) || !Started(mech))) { cnt += 2; continue; } if (MechType(mech) != CLASS_MECH) continue; if (Jumping(mech) || OODing(mech)) continue; if (friendly < 0 || ((MechTeam(mech) == team) == friendly)) cnt++; } return cnt; } enum { NORMAL, PUNCH, KICK }; void cause_damage(MECH * att, MECH * mech, int dam, int table) { int hitGroup, isrear, iscrit = 0, hitloc = 0; int i, sp = (dam - 1) / 5; if (!dam) return; if (att == mech) hitGroup = FRONT; else hitGroup = FindAreaHitGroup(att, mech); isrear = (hitGroup == BACK); if (Fallen(mech)) table = NORMAL; for (i = 0; i <= sp; i++) { switch (table) { case NORMAL: hitloc = FindHitLocation(mech, hitGroup, &iscrit, &isrear); break; case PUNCH: FindPunchLoc(mech, hitloc, hitGroup, iscrit, isrear); break; case KICK: FindKickLoc(mech, hitloc, hitGroup, iscrit, isrear); break; } if (dam <= 0) return; DamageMech(mech, att, (att == mech) ? 0 : 1, (att == mech) ? -1 : MechPilot(att), hitloc, isrear, iscrit, dam > 5 ? 5 : dam, 0, -1, 0, -1, 0, 0); dam -= 5; } } int domino_space_in_hex(MAP * map, MECH * me, int x, int y, int friendly, int mode, int cnt) { int tar = Number(0, cnt - 1), i, head, td; MECH *mech = NULL; int team = MechTeam(me); for (i = 0; i < map->first_free; i++) if ((mech = FindObjectsData(map->mechsOnMap[i]))) { if (MechX(mech) != x || MechY(mech) != y) continue; if (mech == me) continue; if (IsDS(mech) && (Landed(mech) || !Started(mech))) { tar -= 2; } else { if (!Started(mech)) continue; if (MechType(mech) != CLASS_MECH) continue; if (Jumping(mech) || OODing(mech)) continue; if (friendly < 0 || ((MechTeam(mech) == team) == friendly)) tar--; else continue; } if (tar <= 0) break; } if (i == map->first_free) return 0; /* Now we got a mech we hit, accidentally or otherwise */ /* Next, we figure out what'll happen */ /* 'wannabe-charge' is entirely based on the directional difference */ /* Multiplied by the speed - if both go in same direction at same speed, nothing untoward happens (unlikely, though) */ /* Jumping to a hex with multiple guys is BAD Thing(tm), though */ switch (mode) { case 1: case 2: head = MechJumpHeading(me); td = JumpSpeedMP(me, map) * (MechRTons(me) / 1024 + 5) / 10; break; default: head = MechFacing(me) + MechLateral(me); td = fabs(((MechSpeed(me) - MechSpeed(mech) * cos((head - (MechFacing(mech) + MechLateral(mech))) * (M_PI / 180.))) * MP_PER_KPH) * (MechRTons(me) / 1024 + 5) / 15); break; } if (td > 10) td = 10 + (td - 10) / 3; if (td <= 1) /* No point in 1pt hits */ return 0; switch (mode) { case 1: case 2: if (mudconf.btech_stacking == 2) { int factor = mudconf.btech_stackdamage; mech_notify(me, MECHALL, tprintf("You land on %s!", GetMechToMechID(me, mech))); mech_notify(mech, MECHALL, tprintf("%s lands on you!", GetMechToMechID(mech, me))); MechLOSBroadcasti(me, mech, "lands on %s!"); if (IsDS(mech)) { cause_damage(me, mech, MAX(1, td * factor / 500), PUNCH); cause_damage(me, me, MAX(1, td * factor / 100), KICK); } else { cause_damage(me, mech, MAX(1, td * factor / 100), PUNCH); cause_damage(me, me, MAX(1, td * factor / 500), KICK); } } else { mech_notify(me, MECHALL, tprintf("You nearly land on %s!", GetMechToMechID(me, mech))); mech_notify(mech, MECHALL, tprintf("%s nearly lands on you!", GetMechToMechID(mech, me))); MechLOSBroadcasti(me, mech, "nearly lands on %s!"); if (!MadePilotSkillRoll(me, cnt + JumpSpeedMP(me, map) / 2)) MechFalls(me, 1, JumpSpeedMP(me, map) / 2); } return 1; } if (mudconf.btech_stacking == 2) { int factor = mudconf.btech_stackdamage; mech_notify(me, MECHALL, tprintf("You bump into %s!", GetMechToMechID(me, mech))); mech_notify(mech, MECHALL, tprintf("%s bumps into you!", GetMechToMechID(mech, me))); MechLOSBroadcasti(me, mech, "bumps into %s!"); if (IsDS(mech)) { cause_damage(me, mech, MAX(1, td * factor / 500), NORMAL); cause_damage(me, me, MAX(1, td * factor / 100), NORMAL); } else { cause_damage(me, mech, MAX(1, td * factor / 100), NORMAL); cause_damage(me, me, MAX(1, td * factor / 500), NORMAL); } } else { mech_notify(me, MECHALL, tprintf("You nearly bump into %s!", GetMechToMechID(me, mech))); mech_notify(mech, MECHALL, tprintf("%s nearly bumps into you!", GetMechToMechID(mech, me))); MechLOSBroadcasti(me, mech, "nearly bumps into %s!"); if (!MadePilotSkillRoll(me, cnt)) MechFalls(me, 1, 0); MechDesiredSpeed(me) = 0; MechSpeed(me) = 0; } MechChargeTarget(me) = -1; MechChargeTimer(me) = 0; MechChargeDistance(me) = 0; return 1; } int domino_space(MECH * mech, int mode) { MAP *map = FindObjectsData(mech->mapindex); int cnt, fcnt; if (!map) return 0; if (MechType(mech) != CLASS_MECH) return 0; if (mudconf.btech_stacking == 0) return 0; cnt = mechs_in_hex(map, MechX(mech), MechY(mech), -1, 0); if (cnt <= 2) return 0; /* Possible nastiness */ if ((fcnt = mechs_in_hex(map, MechX(mech), MechY(mech), 1, MechTeam(mech))) > 2) return domino_space_in_hex(map, mech, MechX(mech), MechY(mech), 1, mode, fcnt); else if (cnt > 6) return domino_space_in_hex(map, mech, MechX(mech), MechY(mech), 0, mode, cnt - fcnt); return 0; }