BT410 Phase 5.3.15: the electrical charge model -- real charge, closed feedback loop
The emitter charge cycle is now the authentic electrical model end to end.
- WIRE-VERIFIED resource fix: the Emitter block is graphicLength /
dischargeTime / seekVoltage[5] (FRACTIONS of the generator's rated voltage,
-1 sentinel) / seekVoltageRecommendedIndex. The old two-field guess read
seekVoltage[3]=0.99 as "dischargeTime 0.99s" -- the true PPC discharge is
0.2s, and the calibration reads seekV={6000,7000,8000,9900} x rated 10000,
recommended gear 2: the documented curve exactly.
- Ctor calibration (@004bb120): energyTotal=(dmg+heat)x1e7; EC pins
E=0.5V^2EC to energyTotal at the recommended gear; voltageScale calibrates
the exponential charge to reach seekV[rec] in the authored RechargeRate
seconds cold; damageFraction = the damage share.
- TrackSeekVoltage (@004ba838) + ChargeTimeScale (@004b0d50): the charge
integrates from the generator through the heat-stretched time scale, and
the I2R loss heats the GENERATOR -- verified GeneratorA at 477.9K charging
its PPC (BT411 predicted ~+480K) while the avionics generator idles at 77.
Hot generators charge slower: the heat/firepower feedback loop is CLOSED.
- Emitter::ComputeOutputVoltage override (now virtual, as in the binary
vtable): dial = level/seekV[gear], 0.01 snap, over-1 clamp.
- Sub-stepped Loading (1/60s slices -- the BT411 weapon-brick fix) + the
documented-divergence overcharge rescue. VERIFIED: the PPC loads at level
~7920, inside the binary's exact [7920,8080] snap window.
- FireWeapon energy algebra (@004bace8): damage/heat = energy shares x
chargeRatio^2. VERIFIED dmg=11.78 / heat=1.079e8 at ratio 0.9906. The
heatCost x 1e7 partial is retired -- heat now comes from the energy.
Zero Fail; jams/shutdowns regressions stay live under spam.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,8 @@
|
||||
# include <mech.hpp>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
Derivation
|
||||
Emitter::ClassDerivations(
|
||||
MechWeapon::ClassDerivations,
|
||||
@@ -71,13 +73,105 @@ Emitter::Emitter(
|
||||
Check_Pointer(subsystem_resource);
|
||||
|
||||
chargeLevel = 0.0f;
|
||||
seekRate = 0.0f;
|
||||
damagePortion = 0.0f;
|
||||
heatPortion = 0.0f;
|
||||
firingActive = 0;
|
||||
|
||||
graphicLength = subsystem_resource->graphicLength;
|
||||
dischargeTime = subsystem_resource->dischargeTime;
|
||||
dischargeTimer = 0.0f;
|
||||
dischargeTimer = dischargeTime;
|
||||
|
||||
//
|
||||
// Install the beam-weapon fire state machine (a replicant copy is driven by
|
||||
// console updates / ServiceDischarge instead, still staged).
|
||||
// Bring-up-safe pre-init: the electrical block below overwrites these with
|
||||
// the calibrated values when the voltage source is live.
|
||||
//
|
||||
energyCoefficient = 1.0f;
|
||||
seekVoltageIndex = 0;
|
||||
seekVoltageRecommendedIndex = 0;
|
||||
minSeekVoltageIndex = 0;
|
||||
maxSeekVoltageIndex = 0;
|
||||
{
|
||||
for (int sv = 0; sv < 5; ++sv)
|
||||
{
|
||||
seekVoltage[sv] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// energyTotal = (damage + heat) x 1e7 -- the discharge energy in the
|
||||
// 1e7-native heat units.
|
||||
//
|
||||
energyTotal = (damageData.damageAmount + heatCostToFire) * 1.0e7f;
|
||||
|
||||
//
|
||||
// The SeekVoltage calibration (binary ctor @004bb120): the authored curve
|
||||
// values are FRACTIONS of the powering generator's rated voltage (-1
|
||||
// sentinel ends the list); the energy coefficient pins E = 0.5*V^2*EC to
|
||||
// energyTotal exactly at the recommended gear; and voltageScale calibrates
|
||||
// the exponential charge level(t) = Vrated*(1 - exp(-t/(EC*voltageScale)))
|
||||
// to reach the recommended seek voltage in the authored RechargeRate
|
||||
// seconds on a COLD generator (0.0001 == 1/Vrated). Generator heat then
|
||||
// stretches the scale through ChargeTimeScale -- the heat/firepower
|
||||
// feedback.
|
||||
//
|
||||
{
|
||||
Generator *src = (Generator *)ResolveVoltageSource();
|
||||
if (src != NULL)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 5; ++i)
|
||||
{
|
||||
seekVoltage[i] = 0.0f;
|
||||
}
|
||||
for (i = 0; i < 5; ++i)
|
||||
{
|
||||
if (subsystem_resource->seekVoltage[i] == -1.0f)
|
||||
{
|
||||
maxSeekVoltageIndex = i - 1;
|
||||
break;
|
||||
}
|
||||
seekVoltage[i] = subsystem_resource->seekVoltage[i]
|
||||
* src->RatedVoltageOf();
|
||||
}
|
||||
seekVoltageRecommendedIndex =
|
||||
subsystem_resource->seekVoltageRecommendedIndex;
|
||||
seekVoltageIndex = seekVoltageRecommendedIndex;
|
||||
minSeekVoltageIndex = 0;
|
||||
|
||||
Scalar v = seekVoltage[seekVoltageRecommendedIndex];
|
||||
energyCoefficient = energyTotal / (v * v * 0.5f);
|
||||
voltageScale = (rechargeRate
|
||||
/ -(Scalar)log((double)(1.0f - 0.0001f * v)))
|
||||
/ energyCoefficient;
|
||||
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[charge] '" << GetName()
|
||||
<< "' seekV={" << seekVoltage[0] << "," << seekVoltage[1]
|
||||
<< "," << seekVoltage[2] << "," << seekVoltage[3]
|
||||
<< "," << seekVoltage[4] << "} rec=" << seekVoltageRecommendedIndex
|
||||
<< " max=" << maxSeekVoltageIndex
|
||||
<< " EC=" << energyCoefficient
|
||||
<< " vScale=" << voltageScale
|
||||
<< " discharge=" << dischargeTime
|
||||
<< " E=" << energyTotal << endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// damageFraction = the damage share of the discharge energy.
|
||||
//
|
||||
damageFraction = damageData.damageAmount /
|
||||
(damageData.damageAmount + heatCostToFire);
|
||||
|
||||
//
|
||||
// Install the beam-weapon fire state machine; spawn LOADING (the charge
|
||||
// integrates up from zero). (A replicant copy is driven by console
|
||||
// updates / ServiceDischarge instead -- that path joins the network wave.)
|
||||
//
|
||||
weaponAlarm.SetLevel(LoadingState);
|
||||
if (owner->GetInstance() != Entity::ReplicantInstance)
|
||||
{
|
||||
SetPerformance(&Emitter::EmitterSimulation);
|
||||
@@ -86,6 +180,80 @@ Emitter::Emitter(
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// TrackSeekVoltage -- integrate the charge from the powering generator
|
||||
// (binary @004ba838): derive the seek rate from the voltage gap over the
|
||||
// (heat-stretched) charge time-scale, step the level, and feed the charging
|
||||
// I^2R loss back into the GENERATOR's heat -- recharging weapons is what
|
||||
// heats generators, which conduct to their condensers and slow further
|
||||
// charging through ChargeTimeScale. The heat/firepower feedback loop.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Emitter::TrackSeekVoltage(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Generator *src = (Generator *)ResolveVoltageSource();
|
||||
if (src == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Scalar dtScale = ChargeTimeScale();
|
||||
seekRate = (src->MeasuredVoltage() - chargeLevel) / dtScale;
|
||||
chargeLevel = (seekRate / energyCoefficient) * time_slice + chargeLevel;
|
||||
|
||||
if (HeatModelActive())
|
||||
{
|
||||
src->AddPendingHeat(seekRate * seekRate * dtScale * time_slice);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ComputeOutputVoltage -- the Emitter override (binary @004ba738): the
|
||||
// recharge dial = the charge level as a fraction of the selected seek
|
||||
// voltage, snapped to 1.0 within 0.01 and clamped to [0,1] (the byte-verified
|
||||
// over-1 clamp zeroes the dial).
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
Emitter::ComputeOutputVoltage()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Scalar magnitude = (chargeLevel < 0.0f) ? -chargeLevel : chargeLevel;
|
||||
if (magnitude > 1.0e-4f)
|
||||
{
|
||||
rechargeLevel = chargeLevel / seekVoltage[seekVoltageIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
rechargeLevel = 0.0f;
|
||||
}
|
||||
|
||||
Scalar snap = rechargeLevel - 1.0f;
|
||||
if (snap < 0.0f)
|
||||
{
|
||||
snap = -snap;
|
||||
}
|
||||
if (snap <= 0.01f)
|
||||
{
|
||||
rechargeLevel = 1.0f;
|
||||
}
|
||||
|
||||
if (rechargeLevel < 0.0f)
|
||||
{
|
||||
rechargeLevel = 0.0f;
|
||||
}
|
||||
else if (rechargeLevel > 1.0f)
|
||||
{
|
||||
rechargeLevel = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
Emitter::~Emitter()
|
||||
{
|
||||
}
|
||||
@@ -121,32 +289,49 @@ void
|
||||
{
|
||||
Check(this);
|
||||
|
||||
dischargeTimer = dischargeTime;
|
||||
chargeLevel = 0.0f;
|
||||
recoil = rechargeRate; // full recoil -> dial 0, decays in Loading
|
||||
ComputeOutputVoltage();
|
||||
//
|
||||
// Re-arm the beam-on countdown, then THE AUTHENTIC ENERGY ALGEBRA (binary
|
||||
// @004bace8): the shot's damage and heat are the two shares of the stored
|
||||
// charge energy, scaled by the SQUARE of the charge ratio (the level as a
|
||||
// fraction of the recommended seek voltage) -- an under-charged or
|
||||
// down-geared shot delivers proportionally less of both. At full charge on
|
||||
// the recommended gear: damage = the authored DamageAmount verbatim, heat =
|
||||
// heatCostToFire x 1e7 (the 1e7-native chain).
|
||||
//
|
||||
dischargeTimer = dischargeTime;
|
||||
|
||||
Scalar vRec = seekVoltage[seekVoltageRecommendedIndex];
|
||||
Scalar chargeRatio = (vRec > 0.0f) ? (chargeLevel / vRec) : 1.0f;
|
||||
|
||||
damagePortion = (damageFraction * energyTotal * 1.0e-7f)
|
||||
* chargeRatio * chargeRatio;
|
||||
heatPortion = (1.0f - damageFraction) * energyTotal
|
||||
* chargeRatio * chargeRatio;
|
||||
|
||||
//
|
||||
// Dump the firing heat into our own thermal accumulator, under the
|
||||
// heat-model experience gate (novice / standard fire generates no heat --
|
||||
// authentic); the HeatSink step absorbs it next frame and conducts it
|
||||
// toward the linked Condenser bank. The heat chain is 1e7-unit-native
|
||||
// (the BT411 calibration audit): the authored EMITTER heatCostToFire is
|
||||
// the closed form's full-charge value / 1e7 (PPC: 11 -> 1.1e8 units ->
|
||||
// +632 K on its own 174000-mass sink). (PARTIAL: the
|
||||
// (1-dF)*E*chargeRatio^2 charge-scaling joins the electrical-charge wave.)
|
||||
// The damage record carries this shot's portion (the submission at the
|
||||
// owner's target joins the targeting/damage wave).
|
||||
//
|
||||
damageData.damageAmount = damagePortion;
|
||||
|
||||
if (HeatModelActive())
|
||||
{
|
||||
AddPendingHeat(heatCostToFire * 10000000.0f);
|
||||
AddPendingHeat(heatPortion);
|
||||
}
|
||||
|
||||
//
|
||||
// Spend the charge.
|
||||
//
|
||||
ComputeOutputVoltage();
|
||||
chargeLevel = 0.0f;
|
||||
firingActive = 1;
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[fire] '" << GetName()
|
||||
<< "' FIRED (discharge=" << dischargeTime
|
||||
<< "s recharge=" << rechargeRate
|
||||
<< "s heat+=" << heatCostToFire
|
||||
<< "' FIRED (dmg=" << damagePortion
|
||||
<< " heat=" << heatPortion
|
||||
<< " ratio=" << chargeRatio
|
||||
<< " T=" << CurrentTemperatureOf() << ")" << endl << flush;
|
||||
}
|
||||
}
|
||||
@@ -162,6 +347,7 @@ void
|
||||
{
|
||||
Check(this);
|
||||
|
||||
firingActive = 0;
|
||||
weaponAlarm.SetLevel(LoadingState);
|
||||
}
|
||||
|
||||
@@ -267,27 +453,55 @@ void
|
||||
weaponAlarm.SetLevel(LoadingState);
|
||||
}
|
||||
//
|
||||
// Recharge only while the electrical supply is Ready (the authentic
|
||||
// charge integration gates on the voltage state): decay the recoil
|
||||
// over the authored RechargeRate seconds; the dial rises 0 -> 1.
|
||||
// THE CHARGE INTEGRATION, sub-stepped at the pod's locked 60 fps: the
|
||||
// binary's Loading tick assumes fixed frames -- the Loaded transition
|
||||
// fires while the dial crosses the +-0.01 snap window around the
|
||||
// selected seek voltage, a window a variable-dt spike can jump clean
|
||||
// over (the level then overshoots, the over-1 clamp zeroes the dial,
|
||||
// and the weapon bricks in Loading -- the BT411 weapon-brick fix).
|
||||
// Charging gates on the electrical Ready state.
|
||||
//
|
||||
if (GetVoltageState() == Ready)
|
||||
{
|
||||
recoil -= time_slice;
|
||||
if (recoil <= 0.0f)
|
||||
const Scalar kPodFrame = 1.0f / 60.0f;
|
||||
Scalar remaining = time_slice;
|
||||
int guard = 600;
|
||||
while (remaining > 0.0f && guard-- > 0
|
||||
&& GetWeaponState() == LoadingState)
|
||||
{
|
||||
recoil = 0.0f;
|
||||
}
|
||||
}
|
||||
ComputeOutputVoltage();
|
||||
if (recoil == 0.0f)
|
||||
{
|
||||
weaponAlarm.SetLevel(LoadedState);
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[fire] '" << GetName()
|
||||
<< "' LOADED (T=" << CurrentTemperatureOf() << ")"
|
||||
<< endl << flush;
|
||||
Scalar slice = (remaining < kPodFrame) ? remaining : kPodFrame;
|
||||
remaining -= slice;
|
||||
if (GetVoltageState() == Ready)
|
||||
{
|
||||
TrackSeekVoltage(slice);
|
||||
}
|
||||
ComputeOutputVoltage();
|
||||
if (rechargeLevel == 1.0f)
|
||||
{
|
||||
weaponAlarm.SetLevel(LoadedState);
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[fire] '" << GetName()
|
||||
<< "' LOADED (level=" << chargeLevel
|
||||
<< " T=" << CurrentTemperatureOf() << ")"
|
||||
<< endl << flush;
|
||||
}
|
||||
break;
|
||||
}
|
||||
//
|
||||
// Overcharge rescue (a DOCUMENTED DIVERGENCE, BT411 issue #21:
|
||||
// the binary deadlocks here -- a mid-charge gear change can
|
||||
// land the level above the new gear's snap window, the over-1
|
||||
// clamp zeroes the dial forever, and the weapon bricks. The
|
||||
// arcade's own math says full == the gear's seek voltage, so
|
||||
// an overcharged weapon IS loaded).
|
||||
//
|
||||
if (chargeLevel > seekVoltage[seekVoltageIndex]
|
||||
&& seekVoltage[seekVoltageIndex] > 0.0f)
|
||||
{
|
||||
rechargeLevel = 1.0f;
|
||||
weaponAlarm.SetLevel(LoadedState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -26,11 +26,19 @@
|
||||
//####################### Emitter Model Resource ########################
|
||||
//###########################################################################
|
||||
|
||||
//
|
||||
// WIRE-VERIFIED layout: graphicLength, dischargeTime, then the SeekVoltage
|
||||
// curve -- five FRACTIONS of the generator's rated voltage (-1 sentinel
|
||||
// ends the list) -- and the recommended (default) gear index. (The earlier
|
||||
// two-field guess read seekVoltage[3] = 0.99 as "dischargeTime 0.99 s".)
|
||||
//
|
||||
struct Emitter__SubsystemResource:
|
||||
public MechWeapon::SubsystemResource
|
||||
{
|
||||
Scalar seekVoltage[5];
|
||||
Scalar graphicLength;
|
||||
Scalar dischargeTime;
|
||||
Scalar seekVoltage[5];
|
||||
int seekVoltageRecommendedIndex;
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
@@ -104,7 +112,10 @@
|
||||
FireWeapon();
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame simulation (the beam-weapon fire state machine).
|
||||
// Per-frame simulation (the beam-weapon fire state machine) and the
|
||||
// electrical charge model: the Loading state integrates currentLevel
|
||||
// toward the powering generator's voltage (TrackSeekVoltage); Loaded snaps
|
||||
// when the recharge dial reaches 1.0 (level ~= the selected seek voltage).
|
||||
//
|
||||
public:
|
||||
typedef void
|
||||
@@ -120,14 +131,32 @@
|
||||
EmitterSimulation(Scalar time_slice);
|
||||
void
|
||||
ResetFiringState();
|
||||
void
|
||||
TrackSeekVoltage(Scalar time_slice);
|
||||
virtual void
|
||||
ComputeOutputVoltage(); // the charge-voltage form (level/seekV)
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Local Data
|
||||
// Local Data. chargeLevel is the ChargeLevel attribute alias of the raw
|
||||
// charge voltage (currentLevel in the binary's naming).
|
||||
//
|
||||
protected:
|
||||
Scalar chargeLevel;
|
||||
Scalar chargeLevel; // the raw charge voltage (binary currentLevel @0x414)
|
||||
Scalar dischargeTime;
|
||||
Scalar dischargeTimer;
|
||||
Scalar graphicLength;
|
||||
Scalar seekRate;
|
||||
Scalar energyCoefficient;
|
||||
Scalar energyTotal;
|
||||
Scalar damageFraction;
|
||||
Scalar damagePortion;
|
||||
Scalar heatPortion;
|
||||
int firingActive;
|
||||
int seekVoltageIndex;
|
||||
int seekVoltageRecommendedIndex;
|
||||
int minSeekVoltageIndex;
|
||||
int maxSeekVoltageIndex;
|
||||
Scalar seekVoltage[5];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -253,6 +253,8 @@
|
||||
GetHeatState() { Check(this); return heatAlarm.GetLevel(); }
|
||||
Scalar
|
||||
CurrentTemperatureOf() { Check(this); return currentTemperature; }
|
||||
Scalar
|
||||
StartingTemperatureOf() { Check(this); return startingTemperature; }
|
||||
void
|
||||
AddPendingHeat(Scalar heat)
|
||||
{ Check(this); pendingHeat += heat; }
|
||||
|
||||
@@ -223,3 +223,44 @@ past ambient -- actively radiating the mech's heat away.
|
||||
(0.05 x 6 x 20 = 6 -- the authentic tank), coolant refilled to it.
|
||||
|
||||
Zero Fail; the full expert economy regression stays green.
|
||||
|
||||
## The electrical charge model (Phase 5.3.15, 2026-07-24)
|
||||
|
||||
The emitter charge cycle is REAL (all bodies authentic from the BT411 RE):
|
||||
|
||||
- **Resource fix (wire-verified)**: the Emitter block is graphicLength,
|
||||
dischargeTime, seekVoltage[5] (FRACTIONS of the generator's rated voltage,
|
||||
-1 sentinel), seekVoltageRecommendedIndex. The earlier two-field guess had
|
||||
read seekVoltage[3] = 0.99 as "dischargeTime 0.99 s" -- the TRUE PPC
|
||||
discharge is 0.2 s, and the calibration reads seekV = {6000, 7000, 8000,
|
||||
9900} x rated 10000, rec gear 2, exactly the documented curve.
|
||||
- **Ctor calibration** (@004bb120): energyTotal = (damage + heat) x 1e7 (PPC
|
||||
2.3e8); EC pins E = 0.5 V^2 EC to energyTotal at the recommended gear;
|
||||
voltageScale calibrates the exponential charge to reach seekV[rec] in the
|
||||
authored RechargeRate seconds on a COLD generator; damageFraction = the
|
||||
damage share.
|
||||
- **TrackSeekVoltage** (@004ba838): seekRate = (genV - level)/ChargeTimeScale;
|
||||
level += rate/EC x dt; and the charging I^2R loss lands in the GENERATOR's
|
||||
pendingHeat -- recharging weapons is what heats generators. VERIFIED:
|
||||
GeneratorA at 477.9 K charging its PPC (BT411 predicted ~+480K), GeneratorD
|
||||
(avionics only) idle at 77.
|
||||
- **ChargeTimeScale** (@004b0d50, PoweredSubsystem): the base voltageScale
|
||||
stretched by (1 + thermalResistivity x generator temp rise) -- hot
|
||||
generators charge slower. The feedback loop is CLOSED.
|
||||
- **Emitter::ComputeOutputVoltage** override (@004ba738, now virtual as in the
|
||||
binary vtable): dial = level/seekV[gear], snap to 1.0 within 0.01, over-1
|
||||
clamp zeroes.
|
||||
- **Sub-stepped Loading** (the BT411 weapon-brick fix): the charge integrates
|
||||
in 1/60 s slices so the +-0.01 Loaded snap window can't be jumped by a dt
|
||||
spike; plus the DOCUMENTED-DIVERGENCE overcharge rescue (the binary
|
||||
deadlocks a weapon whose level lands above a re-geared snap window).
|
||||
VERIFIED: the PPC loads at level ~7920 -- inside the exact [7920, 8080]
|
||||
window from the binary's disasm.
|
||||
- **FireWeapon energy algebra** (@004bace8): damage/heat = the two
|
||||
energy shares x chargeRatio^2 (level/seekV[rec]). VERIFIED: dmg=11.78,
|
||||
heat=1.079e8 at ratio 0.9906 -- ~98% of authored at the snap edge. The
|
||||
heatCost x 1e7 partial is retired; the heat now comes from the ENERGY.
|
||||
|
||||
Deferred: the ToggleSeekVoltage gear button (needs the energy-weapon message
|
||||
table ids 4..0xb with pads), ServiceDischarge (replicant beam path), the beam
|
||||
build + damage submission (targeting/render waves).
|
||||
|
||||
@@ -189,8 +189,9 @@
|
||||
GetWeaponState() { Check(this); return weaponAlarm.GetLevel(); }
|
||||
Logical
|
||||
CheckFireEdge();
|
||||
void
|
||||
ComputeOutputVoltage();
|
||||
virtual void
|
||||
ComputeOutputVoltage(); // vtable slot 17; the Emitter overrides
|
||||
// with its charge-voltage form
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Local Data
|
||||
|
||||
@@ -179,6 +179,39 @@ int
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ChargeTimeScale -- the heat/firepower feedback (binary @004b0d50):
|
||||
// rise = max(0, sourceTemperature - sourceStartingTemperature)
|
||||
// scale = max(voltageScale,
|
||||
// (thermalResistivityCoefficient * rise + 1) * voltageScale)
|
||||
// A hot generator stretches the exponential charge constant, so recharging
|
||||
// slows exactly when the electrical plant is cooking.
|
||||
//#############################################################################
|
||||
//
|
||||
Scalar
|
||||
PoweredSubsystem::ChargeTimeScale()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
Generator *source = (Generator *)voltageSource.Resolve();
|
||||
if (source == NULL)
|
||||
{
|
||||
return voltageScale;
|
||||
}
|
||||
|
||||
Scalar rise = source->CurrentTemperatureOf()
|
||||
- source->StartingTemperatureOf();
|
||||
if (rise < 0.0f)
|
||||
{
|
||||
rise = 0.0f;
|
||||
}
|
||||
|
||||
Scalar stretched =
|
||||
(thermalResistivityCoefficient * rise + 1.0f) * voltageScale;
|
||||
return (stretched > voltageScale) ? stretched : voltageScale;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// PoweredSubsystemSimulation -- the per-frame electrical step (binary
|
||||
|
||||
@@ -93,6 +93,15 @@
|
||||
unsigned
|
||||
GetVoltageState() { Check(this); return electricalStateAlarm.GetLevel(); }
|
||||
|
||||
//
|
||||
// The charge time-scale (binary @004b0d50) -- the heat/firepower
|
||||
// feedback: the base voltageScale (the ctor-calibrated exponential
|
||||
// charge constant) stretched by the powering generator's temperature
|
||||
// rise above its start point.
|
||||
//
|
||||
Scalar
|
||||
ChargeTimeScale();
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame simulation (heat step + the electrical state machine).
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user