//===========================================================================// // File: mechweap.cpp // // Project: BattleTech Brick: Mech weapons // // Contents: MechWeapon -- the base weapon subsystem // //---------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include #pragma hdrstop #if !defined(MECHWEAP_HPP) # include #endif #if !defined(MECH_HPP) # include #endif #if !defined(SEGMENT_HPP) # include #endif #include Derivation MechWeapon::ClassDerivations( PoweredSubsystem::ClassDerivations, "MechWeapon" ); // //############################################################################# // Message handlers. MechWeapon publishes no handlers of its own yet (the // weapon fire/damage handler wave), but the set MUST be a real defined object: // the surviving CODE PPC.CPP binds the inherited name (`PPC::MessageHandlers`) // into PPC::DefaultData, and a declared-but-undefined static links as a // zero-filled common block -- the same silent-NULL trap as the // Emitter::AttributeIndex crash (see MECHWEAP.NOTES.md). //############################################################################# // MechWeapon::MessageHandlerSet MechWeapon::MessageHandlers( 0, NULL, Subsystem::MessageHandlers ); // //############################################################################# // Attribute Support -- the 1995 binary table (@0x511890), IDs pinned so the // streamed per-mech control mappings resolve: TriggerState (0x13) is what the // authored fire buttons bind (the controls push then writes the trigger state // straight into fireImpulse each frame). The pads bridge our chain gap // 2..0x11 (parents publish nothing yet) -- REQUIRED, because Build leaves // uncovered gap slots uninitialized; they target rechargeLevel, harmless if a // stray mapping ever binds one (BT411 shipped the same). Pip*/WeaponRange // pads-to-rechargeLevel where the member isn't reconstructed yet are noted. //############################################################################# // #define MECHWEAP_PAD(n, name) \ { (int)MechWeapon::MechWeaponPadFirstAttributeID + n, name, \ (Simulation::AttributePointer)&MechWeapon::rechargeLevel } const MechWeapon::IndexEntry MechWeapon::AttributePointers[]= { MECHWEAP_PAD( 0, "MechWeaponPad02"), MECHWEAP_PAD( 1, "MechWeaponPad03"), MECHWEAP_PAD( 2, "MechWeaponPad04"), MECHWEAP_PAD( 3, "MechWeaponPad05"), MECHWEAP_PAD( 4, "MechWeaponPad06"), MECHWEAP_PAD( 5, "MechWeaponPad07"), MECHWEAP_PAD( 6, "MechWeaponPad08"), MECHWEAP_PAD( 7, "MechWeaponPad09"), MECHWEAP_PAD( 8, "MechWeaponPad0A"), MECHWEAP_PAD( 9, "MechWeaponPad0B"), MECHWEAP_PAD(10, "MechWeaponPad0C"), MECHWEAP_PAD(11, "MechWeaponPad0D"), MECHWEAP_PAD(12, "MechWeaponPad0E"), MECHWEAP_PAD(13, "MechWeaponPad0F"), MECHWEAP_PAD(14, "MechWeaponPad10"), MECHWEAP_PAD(15, "MechWeaponPad11"), ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel), // 0x12 ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // 0x13 ATTRIBUTE_ENTRY(MechWeapon, DistanceToTarget, rangeToTarget), // 0x14 ATTRIBUTE_ENTRY(MechWeapon, TargetWithinRange, targetWithinRange), // 0x15 ATTRIBUTE_ENTRY(MechWeapon, WeaponRange, effectiveRange), // 0x16 { (int)MechWeapon::PipPositionAttributeID, "PipPosition", // 0x17 (pip (Simulation::AttributePointer)&MechWeapon::rechargeLevel }, // members { (int)MechWeapon::PipColorAttributeID, "PipColor", // 0x18 not yet (Simulation::AttributePointer)&MechWeapon::rechargeLevel }, // recon- { (int)MechWeapon::PipExtendedRangeAttributeID, "PipExtendedRange", // 0x19 structed) (Simulation::AttributePointer)&MechWeapon::rechargeLevel }, ATTRIBUTE_ENTRY(MechWeapon, EstimatedReadyTime, estimatedReadyTime),// 0x1A ATTRIBUTE_ENTRY(MechWeapon, RearFiring, rearFiring), // 0x1B ATTRIBUTE_ENTRY(MechWeapon, WeaponState, weaponAlarm) // 0x1C }; MechWeapon::AttributeIndexSet MechWeapon::AttributeIndex( ELEMENTS(MechWeapon::AttributePointers), MechWeapon::AttributePointers, Subsystem::AttributeIndex ); MechWeapon::SharedData MechWeapon::DefaultData( MechWeapon::ClassDerivations, MechWeapon::MessageHandlers, MechWeapon::AttributeIndex, Subsystem::StateCount ); MechWeapon::MechWeapon( Mech *owner, int subsystem_ID, SubsystemResource *subsystem_resource, SharedData &shared_data ): PoweredSubsystem(owner, subsystem_ID, subsystem_resource, shared_data) { Check(owner); Check_Pointer(subsystem_resource); rechargeRate = subsystem_resource->rechargeRate; weaponRange = subsystem_resource->weaponRange; damageAmount = subsystem_resource->damageAmount; damageType = subsystem_resource->damageType; heatCostToFire = subsystem_resource->heatCostToFire; targetWithinRange = False; damageData.damageType = damageType; damageData.damageAmount = damageAmount; // // Fire-machine state: spawn fully charged and Loaded (ready to fire). // fireImpulse = 0.0f; previousFireImpulse = 0.0f; rechargeLevel = 1.0f; rangeToTarget = 0.0f; effectiveRange = weaponRange; estimatedReadyTime = 0; recoil = 0.0f; weaponAlarm.Initialize(WeaponStateCount); weaponAlarm.SetLevel(LoadedState); // // REAR-FIRING resolve (the binary ctor tail @004b99a8): a weapon is // rear-mounted when its MOUNT SEGMENT's site name carries the 'b' (back) // marker -- the back gun ports (sitelbgunport / siterbgunport) are the only // port names containing it. Rear-mounted weapons fire only into the // LOOK-BACK view; everything else fires forward. The look-state commit // (Mech::CommitLookState) re-arms viewFireEnable on every view change; a // weapon spawns armed for the forward view. // rearFiring = False; { EntitySegment *mount = owner->GetSegment(GetSegmentIndex()); if (mount != NULL) { const char *mount_name = mount->GetName(); if (mount_name != NULL && strstr(mount_name, "b") != NULL) { rearFiring = True; } } } viewFireEnable = (rearFiring == False); if (getenv("BT_MECH_LOG")) { EntitySegment *mount = owner->GetSegment(GetSegmentIndex()); DEBUG_STREAM << "[weap] '" << GetName() << "' seg=" << GetSegmentIndex() << " mount="; if (mount != NULL) { DEBUG_STREAM << mount->GetName(); } else { DEBUG_STREAM << ""; } DEBUG_STREAM << " rear=" << (int)rearFiring << endl << flush; } Check_Fpu(); } MechWeapon::~MechWeapon() { } Logical MechWeapon::TestClass(Mech &) { return True; } // //############################################################################# // CheckFireEdge -- the rising-edge detector on fireImpulse (binary @004b9608). // TriggerState (attr 0x13) carries the raw ControlsButton INT bit-copied by the // streamed direct mapping (+(button+1) press / -(button+1) release), so the // release value reads as a negative NaN in float terms; the binary's x87 // compare effectively treated it as "released". Comparing the BIT PATTERNS as // signed ints is sign-correct for the button ints AND for genuine float values // (0.0f = 0x0, positives > 0, negatives/NaNs < 0), reproducing the binary's // behavior exactly. //############################################################################# // Logical MechWeapon::CheckFireEdge() { Check(this); int current = *(int *)&fireImpulse; int previous = *(int *)&previousFireImpulse; Logical edge = (current > 0 && previous <= 0) ? True : False; previousFireImpulse = fireImpulse; return edge; } // //############################################################################# // ComputeOutputVoltage -- the recharge-dial writer (binary @004b9c9c, vtable // slot the Emitter overrides with its own voltage form): // rechargeLevel = (rechargeRate - recoil) / rechargeRate // The Loading tick decays recoil from rechargeRate toward 0, so the dial rises // 0 -> 1 over the authored RechargeRate seconds. //############################################################################# // void MechWeapon::ComputeOutputVoltage() { Check(this); rechargeLevel = (rechargeRate - recoil) / rechargeRate; } Logical MechWeapon::TestInstance() const { return IsDerivedFrom(ClassDerivations); } // //############################################################################# // FireWeapon Abstract -- each concrete weapon (Emitter/PPC, ProjectileWeapon/ // Gauss/MissileLauncher) overrides this. Reaching the base is a bug. //############################################################################# // void MechWeapon::FireWeapon() { Fail("MechWeapon::FireWeapon -- should not be here (abstract)"); } // //############################################################################# // SendDamage Deliver this weapon's damage to a struck entity. The damage/ // networking path is not yet reconstructed. //############################################################################# // void MechWeapon::SendDamage(Entity *, Damage &) { Fail("MechWeapon::SendDamage -- mechweap.cpp not yet reconstructed"); } // //############################################################################# // CreateStreamedSubsystem Model-load-time construction. Not yet reconstructed. //############################################################################# // int MechWeapon::CreateStreamedSubsystem( ResourceFile *, NotationFile *, const char *, const char *, SubsystemResource *, NotationFile *, const ResourceDirectories *, int ) { Fail("MechWeapon::CreateStreamedSubsystem -- mechweap.cpp not yet reconstructed"); return 0; }