The last eight stubs plus the two chain layers they actually need (PoweredSubsystem @004b13ac and ProjectileWeapon @004bc7cc were DECLARED in our headers all along -- only their definitions were missing). These are the notation->stream converters the 1995 authoring pipeline ran; missions read prebuilt streams and never call them, so the verification standard is compile + link + the raw decomp's key fingerprints -- and those fingerprints convicted the donors three more times: - The heat chain: Condenser, Reservoir AND PoweredSubsystem all chain ONE shared parser (@004ae150, reconstructed in full from the decomp -- the BT411 donor's body was its own TODO stub with a miscited address). It reads the five thermal scalars and the conduction-graph link (the linkedSinkIndex our wire-format work dump-verified from the runtime side, with the +2 segment-table bias), and HeatSink adds no keys of its own. - The Emitter's SeekVoltage LADDER is a notation ENTRY LIST walked in file order (curve rungs in sequence, the RecommendedIndex entry by name); the donor flattened it to one keyed read, which would author one-rung ladders against the runtime's real five-rung ones. MakeEntryList/GetFirstEntry is the engine's own idiom. - PipColor and MuzzleVelocity parse through the engine's authentic Convert_From_Ascii overloads (RGBColor / Vector3D); the donors' sscanf substitutes were port drift. Explosion and alarm models resolve as model FAMILIES (ModelListResourceType == the literal 1 in the binary; the donor comment calling it GameModelResourceType was wrong-name-right-number). Entry reads target the SUBSYSTEM notation file -- the decomp's fifth argument at every site; several donors wrote model_file. Class IDs stamp through OUR reader's enum symbols (they parse the real BTL4.RES streams today, and the 3000-base enum lands exactly on the binary's 0xBBB.. ids; 0x0BCD is the weapon-base stamp every concrete leaf overwrites). Stubs: NONE. Every function in restoration/source410 now has a body. Mission soak clean. Remaining reconstruction is depth, not holes: the mech4/mech3 archaeology (IntegrateMotion, the master-perf disasm, the stream builders that seed the 0xC/0x100 flags), HUD blocks 4-6, and the live networked/rig verifications queued for when a pod is available. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
596 lines
17 KiB
C++
596 lines
17 KiB
C++
//===========================================================================//
|
|
// File: projweap.cpp //
|
|
// Project: BattleTech Brick: Mech weapons //
|
|
// Contents: ProjectileWeapon -- ballistic (ammo-fed) weapon base //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include <bt.hpp>
|
|
#pragma hdrstop
|
|
|
|
#if !defined(PROJWEAP_HPP)
|
|
# include <projweap.hpp>
|
|
#endif
|
|
|
|
#if !defined(MECH_HPP)
|
|
# include <mech.hpp>
|
|
#endif
|
|
|
|
#if !defined(AMMOBIN_HPP)
|
|
# include <ammobin.hpp>
|
|
#endif
|
|
|
|
#if !defined(RANDOM_HPP)
|
|
# include <random.hpp>
|
|
#endif
|
|
|
|
#if !defined(BTPLAYER_HPP)
|
|
# include <btplayer.hpp>
|
|
#endif
|
|
|
|
Derivation
|
|
ProjectileWeapon::ClassDerivations(
|
|
MechWeapon::ClassDerivations,
|
|
"ProjectileWeapon"
|
|
);
|
|
|
|
ProjectileWeapon::SharedData
|
|
ProjectileWeapon::DefaultData(
|
|
ProjectileWeapon::ClassDerivations,
|
|
MechWeapon::MessageHandlers,
|
|
MechWeapon::AttributeIndex,
|
|
Subsystem::StateCount
|
|
);
|
|
|
|
ProjectileWeapon::ProjectileWeapon(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data
|
|
):
|
|
MechWeapon(owner, subsystem_ID, subsystem_resource, shared_data),
|
|
ammoBinLink()
|
|
{
|
|
Check(owner);
|
|
Check_Pointer(subsystem_resource);
|
|
|
|
tracerInterval = subsystem_resource->tracerInterval;
|
|
minTimeOfFlight = subsystem_resource->minTimeOfFlight;
|
|
minJamChance = subsystem_resource->minJamChance;
|
|
minVoltagePercentToFire = subsystem_resource->minVoltagePercentToFire;
|
|
|
|
tracerCounter = 0;
|
|
ejectState = 0;
|
|
totalTimeToEject = 0.0f;
|
|
timeToEject = 0.0f;
|
|
percentOfEject = 0.0f;
|
|
tracerModelHandle = 0;
|
|
leadPosition = Point3D(0.0f, 0.0f, 0.0f);
|
|
launchVelocity = Vector3D(0.0f, 0.0f, 0.0f);
|
|
tracerOrigin = Vector3D(0.0f, 0.0f, 0.0f);
|
|
|
|
//
|
|
// Link the AmmoBin subsystem the resource references by roster index (the
|
|
// binary ctor resolves it from the owner's subsystem table and connects the
|
|
// embedded link @0x43C). Shipped content always links a bin; log when the
|
|
// data doesn't resolve one.
|
|
//
|
|
{
|
|
Subsystem *roster_bin = NULL;
|
|
if (subsystem_resource->ammoBinIndex >= 0
|
|
&& subsystem_resource->ammoBinIndex < owner->GetSubsystemCount())
|
|
{
|
|
roster_bin = owner->GetSubsystem(subsystem_resource->ammoBinIndex);
|
|
}
|
|
if (roster_bin != NULL)
|
|
{
|
|
ammoBinLink.Add(roster_bin);
|
|
}
|
|
if (getenv("BT_MECH_LOG"))
|
|
{
|
|
DEBUG_STREAM << "[proj] '" << GetName()
|
|
<< "' ammoBinIndex=" << subsystem_resource->ammoBinIndex
|
|
<< " -> ";
|
|
if (roster_bin != NULL)
|
|
{
|
|
DEBUG_STREAM << roster_bin->GetName()
|
|
<< " (rounds=" << ((AmmoBin *)roster_bin)->GetAmmoCount() << ")";
|
|
}
|
|
else
|
|
{
|
|
DEBUG_STREAM << "<no bin>";
|
|
}
|
|
DEBUG_STREAM << endl << flush;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Install the ballistic fire state machine (a replicant copy is driven by
|
|
// console updates instead).
|
|
//
|
|
if (owner->GetInstance() != Entity::ReplicantInstance)
|
|
{
|
|
SetPerformance(&ProjectileWeapon::ProjectileWeaponSimulation);
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
ProjectileWeapon::~ProjectileWeapon()
|
|
{
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// GetAmmoCount -- the linked bin's rounds (-1 with no bin). The cockpit's
|
|
// BallisticWeaponCluster ammo readout reads this each frame; the binary
|
|
// pointed its NumericDisplayInteger straight at the resolved bin's count.
|
|
//#############################################################################
|
|
//
|
|
int
|
|
ProjectileWeapon::GetAmmoCount()
|
|
{
|
|
Check(this);
|
|
|
|
AmmoBin
|
|
*bin = (AmmoBin *)ammoBinLink.Resolve();
|
|
return (bin != NULL) ? bin->GetAmmoCount() : -1;
|
|
}
|
|
|
|
Logical
|
|
ProjectileWeapon::TestClass(Mech &)
|
|
{
|
|
return True;
|
|
}
|
|
|
|
Logical
|
|
ProjectileWeapon::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(ClassDerivations);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// FireWeapon The ballistic discharge (PARTIAL -- binary @004bc104). The
|
|
// authentic body is heat + projectile/tracer spawn ONLY: the view/target gate,
|
|
// the ammo pull and the recoil set all live in the CALLER (the Loaded case of
|
|
// ProjectileWeaponSimulation) -- early-returning any gate from here while the
|
|
// caller cycles the alarm anyway is exactly the 1995 "denied shot fakes a full
|
|
// firing cycle" defect class. The projectile spawn (Missile entities /
|
|
// tracer) needs the entity-spawn + targeting waves; MissileLauncher overrides
|
|
// this for the salvo launch.
|
|
//#############################################################################
|
|
//
|
|
void
|
|
ProjectileWeapon::FireWeapon()
|
|
{
|
|
Check(this);
|
|
|
|
// Ballistic heatCostToFire is stored 1e7-unit-NATIVE in the stream (SRM6
|
|
// reads 5.06e7 = +641K on its own sink -- the same design magnitude as the
|
|
// PPC's +632K); dump it raw, under the heat-model experience gate (novice /
|
|
// standard fire generates no heat -- authentic). (The EMITTER's authored
|
|
// value is small and its 1e7 comes from the energy algebra -- see
|
|
// EMITTER.CPP.)
|
|
if (HeatModelActive())
|
|
{
|
|
AddPendingHeat(heatCostToFire);
|
|
}
|
|
|
|
if (getenv("BT_MECH_LOG"))
|
|
{
|
|
DEBUG_STREAM << "[fire] '" << GetName()
|
|
<< "' FIRED (ballistic, recharge=" << rechargeRate
|
|
<< "s heat+=" << heatCostToFire << ")"
|
|
<< endl << flush;
|
|
}
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// LiveFireEnabled -- the "sim live" novice lockout: owner mech ->
|
|
// Entity::playerLink -> BTPlayer::simLive, 0 only for NOVICE experience. A
|
|
// NULL player link reads LIVE (the unlinked dev mech / target dummy).
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
ProjectileWeapon::LiveFireEnabled()
|
|
{
|
|
Check(this);
|
|
|
|
if (owner == NULL)
|
|
{
|
|
return True;
|
|
}
|
|
BTPlayer *player = Cast_Object(BTPlayer *, owner->GetPlayerLink());
|
|
if (player == NULL)
|
|
{
|
|
return True;
|
|
}
|
|
return player->IsSimLive();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CheckForJam -- the heat-scaled jam roll (binary @4bbfcc), AUTHENTIC form:
|
|
// novice never jams (LiveFireEnabled); a cold heat model never jams (heatLoad
|
|
// stays 0 with the model off -- UpdateHeatLoad only runs under the experience
|
|
// gate); otherwise
|
|
// p = 0.41 * currentTemperature / failureTemperature,
|
|
// clamped to [minJamChance, 1.0], rolled against the uniform random. Note the
|
|
// floor: even a cool launcher at veteran+ carries the authored minimum jam
|
|
// chance per shot (SRM6: 5%) -- authentic pod behavior. A jammed launcher
|
|
// clears only on ResetToInitialState (mission reset).
|
|
//#############################################################################
|
|
//
|
|
Logical
|
|
ProjectileWeapon::CheckForJam()
|
|
{
|
|
Check(this);
|
|
|
|
if (!LiveFireEnabled())
|
|
{
|
|
return False;
|
|
}
|
|
if (heatLoad <= 0.0f)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
Scalar p = (0.41f * CurrentTemperatureOf()) / failureTemperature;
|
|
if (minJamChance <= p)
|
|
{
|
|
if (p > 1.0f)
|
|
{
|
|
p = 1.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p = minJamChance;
|
|
}
|
|
|
|
return (p > (Scalar)Random) ? True : False;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// ProjectileWeaponSimulation The ballistic per-frame fire state machine
|
|
// (binary @004bbd04, FULLY RECOVERED in the BT411 RE). The weapon state is
|
|
// carried in the weapon alarm: 0/1/4/6 transient audio blips, 2 Loaded,
|
|
// 3 Loading, 5 Jammed, 7 unavailable/NoAmmo (the roach-motel: nothing in the
|
|
// machine ever leaves it -- only a mission reset does).
|
|
//
|
|
// PARTIAL: the leading PoweredSubsystemSimulation electrical step, the
|
|
// destroyed / FailureHeat / mech-disabled hard gate (gate 1), the magazine
|
|
// eject, the electrical-Ready recoil gate, the heat-scaled jam roll and the
|
|
// HasActiveTarget half of the fire gate are deferred with the power / heat /
|
|
// damage / targeting waves. What runs is authentic in shape: the single
|
|
// trigger-edge sample, the dry-bin gate (gate 2), the Loaded ammo-pull ->
|
|
// Firing -> Loading cycle with the denial blip, the Loading recoil bleed +
|
|
// recharge dial, and the NoAmmo dry-fire blip.
|
|
//
|
|
// DEV hook BT_FORCE_FIRE=1: pulses the trigger whenever the weapon is Loaded
|
|
// (same as the emitter hook) -- every armed weapon auto-fires at its authored
|
|
// cadence until its bin runs dry.
|
|
//#############################################################################
|
|
//
|
|
void
|
|
ProjectileWeapon::ProjectileWeaponSimulation(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
// The PoweredSubsystem step first (binary @4bbd12): the HeatSink thermal
|
|
// absorb/conduct + the electrical state machine.
|
|
//
|
|
PoweredSubsystem::PoweredSubsystemSimulation(time_slice);
|
|
|
|
{
|
|
static int forceFire = -1;
|
|
if (forceFire < 0)
|
|
{
|
|
forceFire = (getenv("BT_FORCE_FIRE") != NULL) ? 1 : 0;
|
|
}
|
|
//
|
|
// BT_PLAYER_PASSIVE=1: the piloted mech holds fire (the lifecycle
|
|
// test -- let the enemy win).
|
|
//
|
|
static int playerPassive = -1;
|
|
if (playerPassive < 0)
|
|
{
|
|
playerPassive = (getenv("BT_PLAYER_PASSIVE") != NULL) ? 1 : 0;
|
|
}
|
|
if (forceFire
|
|
&& !(playerPassive && owner != NULL
|
|
&& owner->GetPlayerLink() != NULL))
|
|
{
|
|
fireImpulse = (GetWeaponState() == LoadedState) ? 1.0f : 0.0f;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The trigger edge is sampled ONCE, before the gates.
|
|
//
|
|
Logical trigger = CheckFireEdge();
|
|
|
|
targetWithinRange = UpdateTargeting();
|
|
|
|
//
|
|
// Gate 1 (@4bbd36): weapon DESTROYED or its own sink at FailureHeat -> pin
|
|
// full recoil + the unavailable alarm. No return -- the frame continues
|
|
// into the state machine (whose NoAmmo case re-asserts). (The owning-mech-
|
|
// disabled half joins with the damage wave.)
|
|
//
|
|
if (GetSimulationState() == 1 || GetHeatState() == FailureHeat
|
|
|| (owner != NULL && owner->IsMechDestroyed()))
|
|
{
|
|
if (getenv("BT_MECH_LOG") && GetWeaponState() != NoAmmoState)
|
|
{
|
|
DEBUG_STREAM << "[fire] '" << GetName()
|
|
<< "' -> UNAVAILABLE (gate1: heatState=" << GetHeatState()
|
|
<< " T=" << CurrentTemperatureOf() << ")" << endl << flush;
|
|
}
|
|
recoil = rechargeRate;
|
|
weaponAlarm.SetLevel(NoAmmoState);
|
|
}
|
|
|
|
//
|
|
// Gate 2: the linked AmmoBin. Empty (or missing) -> pin unavailable EVERY
|
|
// frame while dry.
|
|
//
|
|
AmmoBin *bin = (AmmoBin *)ammoBinLink.Resolve();
|
|
if (bin != NULL && bin->GetAmmoState() == AmmoBin::AmmoEmptyState)
|
|
{
|
|
if (getenv("BT_MECH_LOG") && GetWeaponState() != NoAmmoState)
|
|
{
|
|
DEBUG_STREAM << "[fire] '" << GetName()
|
|
<< "' -> NoAmmo (bin dry)" << endl << flush;
|
|
}
|
|
weaponAlarm.SetLevel(NoAmmoState);
|
|
}
|
|
|
|
switch (GetWeaponState())
|
|
{
|
|
case LoadedState:
|
|
if (trigger)
|
|
{
|
|
//
|
|
// The authentic fire gate (@4bbee6): armed in the current look
|
|
// view AND a target under the reticle.
|
|
//
|
|
if (viewFireEnable && HasActiveTarget())
|
|
{
|
|
//
|
|
// The ammo pull happens HERE, in the caller -- a failed pull
|
|
// just stays Loaded, silently.
|
|
//
|
|
if (bin != NULL && bin->FeedAmmo() != 0)
|
|
{
|
|
weaponAlarm.SetLevel(FiringState);
|
|
ForceUpdate();
|
|
FireWeapon();
|
|
if (bin->GetAmmoState() == AmmoBin::AmmoEmptyState)
|
|
{
|
|
weaponAlarm.SetLevel(NoAmmoState);
|
|
}
|
|
else if (CheckForJam())
|
|
{
|
|
weaponAlarm.SetLevel(JammedState);
|
|
if (getenv("BT_MECH_LOG"))
|
|
{
|
|
DEBUG_STREAM << "[fire] '" << GetName()
|
|
<< "' JAMMED (T=" << CurrentTemperatureOf()
|
|
<< ")" << endl << flush;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
weaponAlarm.SetLevel(LoadingState);
|
|
}
|
|
ForceUpdate();
|
|
recoil = rechargeRate;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// THE DENIAL BLIP: a shot denied by the look-view gate does
|
|
// SetLevel(4); SetLevel(2) -- a one-frame audio blip, the pip
|
|
// stays Loaded and NO ammo is pulled.
|
|
//
|
|
weaponAlarm.SetLevel(TriggerDuringLoadState);
|
|
weaponAlarm.SetLevel(LoadedState);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LoadingState:
|
|
if (trigger)
|
|
{
|
|
weaponAlarm.SetLevel(TriggerDuringLoadState);
|
|
weaponAlarm.SetLevel(LoadingState);
|
|
}
|
|
//
|
|
// Recoil bleeds ONLY while the electrical supply is Ready (binary
|
|
// @4bbdf5/@4bbe04); at zero (and a round chambered) -> Loaded.
|
|
//
|
|
if (GetVoltageState() == Ready)
|
|
{
|
|
recoil -= time_slice;
|
|
if (recoil < 0.0f)
|
|
{
|
|
recoil = 0.0f;
|
|
if (bin != NULL && bin->GetAmmoState() == AmmoBin::AmmoLoadedState)
|
|
{
|
|
weaponAlarm.SetLevel(LoadedState);
|
|
if (getenv("BT_MECH_LOG"))
|
|
{
|
|
DEBUG_STREAM << "[fire] '" << GetName()
|
|
<< "' LOADED (rounds=" << bin->GetAmmoCount()
|
|
<< " T=" << CurrentTemperatureOf() << ")"
|
|
<< endl << flush;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ComputeOutputVoltage();
|
|
break;
|
|
|
|
case JammedState:
|
|
if (trigger)
|
|
{
|
|
weaponAlarm.SetLevel(TriggerDuringJamState);
|
|
weaponAlarm.SetLevel(JammedState);
|
|
}
|
|
recoil = rechargeRate;
|
|
break;
|
|
|
|
case NoAmmoState:
|
|
if (trigger)
|
|
{
|
|
weaponAlarm.SetLevel(DryTriggerState); // the dry-fire blip
|
|
}
|
|
weaponAlarm.SetLevel(NoAmmoState); // re-assert (the roach-motel)
|
|
recoil = rechargeRate;
|
|
ComputeOutputVoltage(); // dial pinned at 0
|
|
break;
|
|
|
|
default: // 0/1/4/6: transient audio-blip states, no case body
|
|
break;
|
|
}
|
|
|
|
if (getenv("BT_VIS_LOG"))
|
|
{
|
|
static int projStateSamples = 0;
|
|
if (projStateSamples < 8)
|
|
{
|
|
++projStateSamples;
|
|
DEBUG_STREAM << "[proj-state] '" << GetName()
|
|
<< "' state=" << GetWeaponState()
|
|
<< " recoil=" << recoil
|
|
<< " rechargeRate=" << rechargeRate
|
|
<< " level=" << rechargeLevel
|
|
<< " bin=" << ((bin != NULL) ? bin->GetAmmoCount() : -1)
|
|
<< " simState=" << GetSimulationState()
|
|
<< " heatState=" << GetHeatState()
|
|
<< "\n" << flush;
|
|
}
|
|
}
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
// CreateStreamedSubsystem -- binary @004bc7cc (declared in PROJWEAP.HPP all
|
|
// along; the definition was the gap). Chains the weapon record, keeps the
|
|
// weapon-base classID (the concrete launcher re-stamps after), size 0x1D0,
|
|
// then the projectile block: TracerInterval, the REQUIRED "AmmoBin"
|
|
// subsystem link (+2 segment-table bias), MinTimeOfFlight, MinJamChance,
|
|
// MinVoltagePercentToFire.
|
|
//#############################################################################
|
|
//
|
|
int
|
|
ProjectileWeapon::CreateStreamedSubsystem(
|
|
ResourceFile *resource_file,
|
|
NotationFile *model_file,
|
|
const char *model_name,
|
|
const char *subsystem_name,
|
|
SubsystemResource *subsystem_resource,
|
|
NotationFile *subsystem_file,
|
|
const ResourceDirectories *directories,
|
|
int passes
|
|
)
|
|
{
|
|
if (
|
|
!MechWeapon::CreateStreamedSubsystem(
|
|
resource_file, model_file, model_name, subsystem_name,
|
|
subsystem_resource, subsystem_file, directories, passes
|
|
)
|
|
)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
subsystem_resource->subsystemModelSize = 0x1D0;
|
|
|
|
if (passes == 1)
|
|
{
|
|
subsystem_resource->tracerInterval = -1;
|
|
subsystem_resource->ammoBinIndex = -1;
|
|
subsystem_resource->minTimeOfFlight = -1.0f;
|
|
subsystem_resource->minVoltagePercentToFire = -1.0f;
|
|
subsystem_resource->minJamChance = -1.0f;
|
|
}
|
|
|
|
if (
|
|
!subsystem_file->GetEntry(subsystem_name, "TracerInterval",
|
|
&subsystem_resource->tracerInterval)
|
|
&& subsystem_resource->tracerInterval == -1
|
|
)
|
|
{
|
|
DEBUG_STREAM << subsystem_name << " missing TracerInterval!" << endl;
|
|
return False;
|
|
}
|
|
|
|
const char
|
|
*ammo_bin_name = "Unspecified";
|
|
if (
|
|
!subsystem_file->GetEntry(subsystem_name, "AmmoBin", &ammo_bin_name)
|
|
&& subsystem_resource->ammoBinIndex == -1
|
|
)
|
|
{
|
|
DEBUG_STREAM << subsystem_name << " missing AmmoBin!" << endl;
|
|
return False;
|
|
}
|
|
if (strcmp(ammo_bin_name, "Unspecified") != 0)
|
|
{
|
|
subsystem_resource->ammoBinIndex =
|
|
Find_Subsystem_Index(subsystem_file, ammo_bin_name);
|
|
}
|
|
if (subsystem_resource->ammoBinIndex < 0)
|
|
{
|
|
DEBUG_STREAM << subsystem_name << " has an invalid Ammo Bin!" << endl;
|
|
return False;
|
|
}
|
|
if (strcmp(ammo_bin_name, "Unspecified") != 0)
|
|
{
|
|
subsystem_resource->ammoBinIndex += 2;
|
|
}
|
|
|
|
if (
|
|
!subsystem_file->GetEntry(subsystem_name, "MinTimeOfFlight",
|
|
&subsystem_resource->minTimeOfFlight)
|
|
&& subsystem_resource->minTimeOfFlight == -1.0f
|
|
)
|
|
{
|
|
DEBUG_STREAM << subsystem_name << " missing MinTimeOfFlight !" << endl;
|
|
return False;
|
|
}
|
|
if (
|
|
!subsystem_file->GetEntry(subsystem_name, "MinJamChance",
|
|
&subsystem_resource->minJamChance)
|
|
&& subsystem_resource->minJamChance == -1.0f
|
|
)
|
|
{
|
|
DEBUG_STREAM << subsystem_name << " missing MinJamChance !" << endl;
|
|
return False;
|
|
}
|
|
if (
|
|
!subsystem_file->GetEntry(subsystem_name, "MinVoltagePercentToFire",
|
|
&subsystem_resource->minVoltagePercentToFire)
|
|
&& subsystem_resource->minVoltagePercentToFire == -1.0f
|
|
)
|
|
{
|
|
DEBUG_STREAM << subsystem_name << " missing MinVoltagePercentToFire !" << endl;
|
|
return False;
|
|
}
|
|
|
|
Check_Fpu();
|
|
return True;
|
|
}
|