//===========================================================================// // File: mech.cpp // // Project: BattleTech Brick: Entity Manager // // Contents: Implementation details for the Mech entity // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // // //---------------------------------------------------------------------------// // Copyright (C) 1995, Virtual World Entertainment, Inc. // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include #pragma hdrstop #if !defined(MECH_HPP) # include #endif #if !defined(APP_HPP) # include #endif #if !defined(MEMSTRM_HPP) # include #endif #if !defined(RESOURCE_HPP) # include #endif // The subsystem roster the segment walk instantiates. #include // HeatableSubsystem, HeatSink, HeatWatcher, Condenser #include // PoweredSubsystem, PowerWatcher, Generator #include // Reservoir #include // Sensor #include // Gyroscope #include // Torso #include // Myomers #include // HUD #include // Searchlight #include // ThermalSight #include // MechTech #include // SubsystemMessageManager #include // MechWeapon #include // Emitter #include // PPC #include // GaussRifle #include // ProjectileWeapon #include // MissileLauncher #include // AmmoBin // //############################################################################# //############################################################################# // Derivation Mech::ClassDerivations( JointedMover::ClassDerivations, "Mech" ); Mech::SharedData Mech::DefaultData( Mech::ClassDerivations, JointedMover::MessageHandlers, JointedMover::AttributeIndex, 33, (Entity::MakeHandler)Mech::Make ); // //############################################################################# //############################################################################# // Mech* Mech::Make(MakeMessage *creation_message) { return new Mech(creation_message); } // //############################################################################# // Mech ctor -- the heart of the entity (walks the model segment table and // instantiates the full subsystem roster: power, heat, weapons, actuators, // controls, tech, damage zones). This is the largest single function in the // game and the current reconstruction frontier; see MECH.NOTES.md. Chains to // the JointedMover base so the skeleton/segments stream before it Fails. //############################################################################# // Mech::Mech( MakeMessage *creation_message, SharedData &shared_data ): JointedMover(creation_message, shared_data), controllableSubsystems(this), watchedSubsystems(this), heatableSubsystems(this), weaponRoster(this), damageableSubsystems(this) { Check_Pointer(creation_message); // // Cached subsystem back-pointers -- filled by the segment walk below. // sensorSubsystem = NULL; gyroSubsystem = NULL; sinkSourceSubsystem = NULL; hudSubsystem = NULL; messageManager = NULL; weaponCount = 0; // // Embedded status / animation / naming state. // mechNameFilter.Initialize(); masterAlarm.Initialize(0x21); heatAlarm.Initialize(3); stabilityAlarm.Initialize(2); statusAlarm.Initialize(0x21); targetReticle.reticleState = Reticle::ReticleOn; targetReticle.pickPointingOn = True; targetReticle.reticleElementMask = Reticle::AllEnabledGroup; animationState = StateIndicator(0x21); animationState.SetState(0); replicantAnimationState = StateIndicator(0x21); replicantAnimationState.SetState(0); collisionState = StateIndicator(4); collisionState.SetState(0); { for (int i = 0; i < 5; ++i) { telemetryFilter[i].SetSize(15, 0.0f); } } legAnimation.Init(this); bodyAnimation.Init(this); { for (int i = 0; i < 220; ++i) { reservedState[i] = 0; } } // //----------------------------------------------------------------------- // Segment-table walk: instantiate one Subsystem per streamed segment, // dispatching on its classID. The subsystem roster (subsystemArray / // subsystemCount) lives in the base Entity. //----------------------------------------------------------------------- // ResourceDescription::ResourceID modelResourceID = creation_message->resourceID; ResourceDescription *subsystemDesc = application->GetResourceFile()->SearchList( modelResourceID, ResourceDescription::SubsystemModelStreamResourceType ); Check(subsystemDesc); subsystemDesc->Lock(); // // Copy the raw stream into a padded buffer: reading a SubsystemResource // struct off the tail segment can over-read the raw resource, so pad it. // size_t rawSize = (size_t)subsystemDesc->resourceSize; size_t padSize = rawSize + 0x400; void *padBuffer = (void *)new char[padSize]; memcpy(padBuffer, subsystemDesc->resourceAddress, rawSize); MemoryStream subsystemStream(padBuffer, padSize); int streamedSubsystemCount = *(int *)subsystemStream.GetPointer(); subsystemStream.AdvancePointer(sizeof(int)); // // Slot 0 = the (later-installed) control mapper, slot 1 = the voltage bus // sentinel; the streamed subsystems fill from slot 2. // subsystemCount = streamedSubsystemCount + 2; subsystemArray = new Subsystem *[subsystemCount]; { for (int z = 0; z < subsystemCount; ++z) { subsystemArray[z] = NULL; } } for (int id = 2; id < subsystemCount; ++id) { Subsystem::SubsystemResource *seg = (Subsystem::SubsystemResource *)subsystemStream.GetPointer(); Subsystem *made = NULL; switch (seg->classID) { case CondenserClassID: made = new Condenser(this, id, (Condenser::SubsystemResource *)seg); break; case HeatSinkClassID: made = new HeatSink(this, id, (HeatSink::SubsystemResource *)seg); break; case HeatWatcherClassID: made = new HeatWatcher(this, id, (HeatWatcher::SubsystemResource *)seg); break; case ReservoirClassID: made = new Reservoir(this, id, (Reservoir::SubsystemResource *)seg); break; case GeneratorClassID: made = new Generator(this, id, (Generator::SubsystemResource *)seg); break; case PoweredSubsystemClassID: made = new PoweredSubsystem(this, id, (PoweredSubsystem::SubsystemResource *)seg); break; case SensorClassID: made = new Sensor(this, id, (Sensor::SubsystemResource *)seg); sensorSubsystem = made; break; case GyroscopeClassID: made = new Gyroscope(this, id, (Gyroscope::SubsystemResource *)seg); gyroSubsystem = made; break; case TorsoClassID: made = new Torso(this, id, (Torso::SubsystemResource *)seg); sinkSourceSubsystem = made; break; case MyomersClassID: made = new Myomers(this, id, (Myomers::SubsystemResource *)seg); break; case EmitterClassID: made = new Emitter(this, id, (Emitter::SubsystemResource *)seg); ++weaponCount; break; case PPCClassID: made = new PPC(this, id, (PPC::SubsystemResource *)seg, PPC::DefaultData); ++weaponCount; break; case AmmoBinClassID: made = new AmmoBin(this, id, (AmmoBin::SubsystemResource *)seg); break; case ProjectileWeaponClassID: made = new ProjectileWeapon(this, id, (ProjectileWeapon::SubsystemResource *)seg); ++weaponCount; break; case GaussRifleClassID: made = new GaussRifle(this, id, (GaussRifle::SubsystemResource *)seg); ++weaponCount; break; case MissileLauncherClassID: made = new MissileLauncher(this, id, (MissileLauncher::SubsystemResource *)seg); ++weaponCount; break; case SubsystemMessageManagerClassID: made = new SubsystemMessageManager(this, id, (SubsystemMessageManager::SubsystemResource *)seg); messageManager = (SubsystemMessageManager *)made; break; case HUDClassID: made = new HUD(this, id, (HUD::SubsystemResource *)seg); hudSubsystem = made; break; case SearchlightClassID: made = new Searchlight(this, id, (Searchlight::SubsystemResource *)seg); break; case ThermalSightClassID: made = new ThermalSight(this, id, (ThermalSight::SubsystemResource *)seg); break; case MechTechClassID: made = new MechTech(this, id, (MechTech::SubsystemResource *)seg); break; case EmitterClassID + 1: // LaserClassID -- an Emitter energy weapon case EmitterClassID + 2: // ParticleCannonClassID -- an Emitter energy weapon made = new Emitter(this, id, (Emitter::SubsystemResource *)seg); ++weaponCount; break; default: // // Unrecognised / not-yet-reconstructed subsystem class (Capacitor, // AmmoFeeder, Radar, Turret, ...): give the slot a base // MechSubsystem so control/damage bindings that resolve this // subsystemID find a real (if generic) subsystem rather than a NULL // plug. The roster stays aligned. // made = new MechSubsystem( this, id, (MechSubsystem::SubsystemResource *)seg, MechSubsystem::DefaultData ); break; } subsystemArray[id] = made; subsystemStream.AdvancePointer(seg->subsystemModelSize); } subsystemDesc->Unlock(); delete [] (char *)padBuffer; if (getenv("BT_MECH_LOG")) { DEBUG_STREAM << "[mech] segment walk done: subsystemCount=" << subsystemCount << " weaponCount=" << weaponCount << endl << flush; } Check_Fpu(); } Mech::~Mech() { } void Mech::SetMappingSubsystem(Subsystem *subsystem) { Check(this); Check_Pointer(subsystemArray); // // The control mapper lives in roster slot 0 (the streamed control-mapping // resource binds its DirectMappings to subsystemID 0, so it must resolve // there via Entity::GetSimulation(0)). On a re-spawn, drop the old one. // if (subsystemArray[0] != NULL) { Unregister_Object(subsystemArray[0]); delete subsystemArray[0]; } subsystemArray[0] = subsystem; }