//===========================================================================// // File: seeker.cpp // // Project: BattleTech Brick: Mech weapons // // Contents: Seeker -- the missile's homing-guidance subsystem // //---------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// // // The class interface is the AUTHENTIC surviving CODE/BT/BT/SEEKER.HPP // (07/12/95 GDU). Bodies reconstructed from the binary via the BT411 // decomp: ctor @004bec34, LeadTarget @004beae4 (constants .rdata // @004bec18..28). The Subsystem base chain is the NAME ctor (binary // FUN_0041c52c) with the shipped string "Seeker" @00512e1e. // #include #pragma hdrstop #if !defined(SEEKER_HPP) # include #endif #if !defined(MISSILE_HPP) # include #endif // // Lead/guidance tuning (binary .rdata, BT411-decoded). // static const Scalar SeekerLeadMinRange = 200.0f; // _DAT_004bec18 static const Scalar SeekerLeadFloor = 50.0f; // _DAT_004bec20 static const Scalar SeekerLeadMaxClamp = 300.0f; // _DAT_004bec24 static const Scalar SeekerLeadCoef = 0.1f; // _DAT_004bec28 (loft gain) Derivation Seeker::ClassDerivations( Subsystem::ClassDerivations, "Seeker" ); const Seeker::IndexEntry Seeker::AttributePointers[] = { ATTRIBUTE_ENTRY(Seeker, TargetPosition, targetPosition), ATTRIBUTE_ENTRY(Seeker, RangeToTarget, rangeToTarget), ATTRIBUTE_ENTRY(Seeker, RangeFromCreation, rangeFromCreation) }; Seeker::AttributeIndexSet Seeker::AttributeIndex( ELEMENTS(Seeker::AttributePointers), Seeker::AttributePointers, Subsystem::AttributeIndex ); Seeker::SharedData Seeker::DefaultData( Seeker::ClassDerivations, Subsystem::MessageHandlers, Seeker::AttributeIndex, Seeker::StateCount ); // //############################################################################# // Construction (binary @004bec34): snapshot the firing geometry -- the // creation point, the homing target + aim offset, and the initial ranges. //############################################################################# // Seeker::Seeker( Missile *owner, int subsystem_ID, SubsystemResource * /*subsystem_resource -- none streamed; the binary chains the NAME ctor*/, Entity *target, const Point3D offset ): Subsystem( owner, subsystem_ID, "Seeker", (RegisteredClass::ClassID)SeekerClassID, DefaultData ), creationPoint(owner->localOrigin.linearPosition), targetOffset(offset) { Check(owner); targetEntity = target; if (targetEntity == NULL) { targetPosition = targetOffset; } else { targetPosition.x = targetOffset.x + targetEntity->localOrigin.linearPosition.x; targetPosition.y = targetOffset.y + targetEntity->localOrigin.linearPosition.y; targetPosition.z = targetOffset.z + targetEntity->localOrigin.linearPosition.z; } Vector3D delta; delta.Subtract(targetPosition, creationPoint); rangeToTarget = delta.Length(); startingRangeToTarget = rangeToTarget; rangeFromCreation = 0.0f; Check_Fpu(); } Seeker::~Seeker() { } Logical Seeker::TestInstance() const { return IsDerivedFrom(ClassDerivations); } // //############################################################################# // LeadTarget (binary @004beae4): re-aim at the (moving) target each slice -- // refresh the aim point from the target's live position + offset, then past // SeekerLeadMinRange loft the aim upward by SeekerLeadCoef x the clamped // excess (the authored missile arc). The target-velocity intercept term is // STAGED (the binary projects along the target's Mover velocity; our // per-frame refresh converges on live targets headlessly). //############################################################################# // void Seeker::LeadTarget() { Check(this); if (targetEntity == NULL) { return; } targetPosition.x = targetOffset.x + targetEntity->localOrigin.linearPosition.x; targetPosition.y = targetOffset.y + targetEntity->localOrigin.linearPosition.y; targetPosition.z = targetOffset.z + targetEntity->localOrigin.linearPosition.z; Vector3D toMissile; toMissile.Subtract( targetPosition, GetEntity()->localOrigin.linearPosition ); Scalar range = toMissile.Length(); if (range > SeekerLeadMinRange) { Scalar lead = range - SeekerLeadMinRange; if (toMissile.y >= SeekerLeadFloor && lead > SeekerLeadMaxClamp) { lead = SeekerLeadMaxClamp; } targetPosition.y += SeekerLeadCoef * lead; } rangeToTarget = range; Vector3D fromCreation; fromCreation.Subtract( GetEntity()->localOrigin.linearPosition, creationPoint ); rangeFromCreation = fromCreation.Length(); } void Seeker::FindTarget(Scalar /*time_slice*/) { Check(this); LeadTarget(); } void Seeker::ResetToInitialState() { Check(this); rangeToTarget = 0.0f; rangeFromCreation = 0.0f; startingRangeToTarget = 0.0f; }