gauge wave P3: wire the combat scoring feed (KILLS tracks combat)
The Comm roster + score gauges read 0 for 4 independent reasons (none was handler logic -- the BTPlayer ScoreMessage/VehicleDead/ScoreInflicted handlers were already reconstructed). Mapped by the scoring-feed-decode workflow; fixed all 4: - NO PRODUCER: combat only dispatched Entity::TakeDamageMessage, never a scoring message. Added producers (btplayer.cpp bridges, called from mech4.cpp): per-hit ScoreInflicted at the beam (:2171) + projectile (:831) damage dispatch -> SCORE; KillScore at the TARGET-DESTROYED edge (:2198) -> KILLS. senderMechID = the VICTIM (so the local player is credited via the !=our-mech branch, not the suicide-negate branch); dispatched to application->GetMissionPlayer(). - CROSS-FAMILY MECH OFFSETS: MECH_OWNING_PLAYER/TONNAGE/DAMAGE_BIAS read raw binary offsets (garbage in our 0x638 Mech). MECH_OWNING_PLAYER -> Entity::GetPlayerLink() (NULL for the ownerless dummy); tonnage/bias stubbed 1.0/0.0 (bring-up). - NULL scenarioRole: the BTMission role registry has no WinTesla analog, so every award path NULL-deref'd. CalcInflictedScore returns the neutral (damage+bias) when scenarioRole==0; DamageReceived guarded. - NULL-owner dummy: the KillScore sender-owner increment + StatusMessage now guard GetPlayerLink() (the dummy has none) so a solo kill credits only the local player. - DATABINDING: the PilotList read KILLS/DEATHS at raw offsets (pilot+0x27c/+0x200) that don't match our compiled layout -> silent 0. Repointed to the compiled members via bridges (BTPilotKills/BTPilotDeaths -> BTPlayer::GetKillCount/GetDeaths). ⚠ ROOT-CAUSE of the empty scoreboard: the PilotList SELECT-TARGET highlight did a raw-offset deref (*(local+0x284) objectiveMech, then *(tgt+0x190)) that AV'd on our layout -- caught silently by the SEH GuardedExecute, aborting Execute BEFORE the KILLS/DEATHS draw. Replaced with BTPilotIsSelected (accessors + GetPlayerLink). - SCORE persistence: PlayerSimulation's 10s flush zeroed currentScore (the binary's console delta); now only flushes/zeros when a console host exists (solo keeps the running score for the gauge + CalcRanking). Verified DBASE+dev gauges (BT_SCORE_LOG): SCORE climbs +5.83/hit -> 164; on the kill KILLS 0->1, and the Comm roster RENDERS KILLS=1 (screenshot). No crash. Follow-ups (noted): DEATHS=1 is a pre-existing spawn/respawn-handshake artifact (not from the producers -- the dummy's GetPlayerLink is NULL so no VehicleDead is posted; confirmed killCount=1 not 2); RANK=-1 is CalcRanking's non-scoring mark (Plasma serial surface, invisible on dev); MP VehicleDead/tonnage/role registry remain per the plan. Diagnostics kept: BT_SCORE_LOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f998f2a023
commit
34aaa7dda4
@@ -187,6 +187,11 @@ Logical
|
||||
// the bridge (mechmppr.cpp, a complete-Mech TU) resolves it -- this TU only sees
|
||||
// the pilot as an opaque pointer read at BTPlayer raw offsets.
|
||||
extern void *BTResolveRosterPilot(int slot);
|
||||
// gauge scoring wave: read a roster pilot's scoreboard via its compiled BTPlayer
|
||||
// members (btplayer.cpp), not raw offsets (which don't match our layout).
|
||||
extern int BTPilotKills(void *pilot);
|
||||
extern int BTPilotDeaths(void *pilot);
|
||||
extern int BTPilotIsSelected(void *pilot, void *local_player); // SELECT-TARGET highlight (safe)
|
||||
|
||||
// App+0xC8 player-name-bitmap cache is not wired in the port (same deferral
|
||||
// PlayerStatus uses for its nameImage) -> NULL routes DrawMechIcon to the binary's
|
||||
@@ -344,6 +349,11 @@ void
|
||||
PilotList::Execute()
|
||||
{
|
||||
void *local = BTResolveRosterPilot(0); // slot-0 (local) pilot gates the body
|
||||
{
|
||||
static int s_pl = 0;
|
||||
if (getenv("BT_SCORE_LOG") && (s_pl++ % 120) == 0)
|
||||
DEBUG_STREAM << "[score] PilotList::Execute local=" << local << "\n" << std::flush;
|
||||
}
|
||||
if (local == NULL)
|
||||
return;
|
||||
|
||||
@@ -375,8 +385,11 @@ void
|
||||
}
|
||||
else
|
||||
{
|
||||
void *tgt = *(void **)((char *)local + 0x284); // local pilot's target (objectiveMech)
|
||||
int selected = (tgt != NULL) && (pilot == *(void **)((char *)tgt + 0x190)); // target's owningPlayer
|
||||
// SELECT-TARGET highlight -- via the safe bridge (accessors), NOT raw offsets:
|
||||
// the old `*(local+0x284)` objectiveMech + deref `*(tgt+0x190)` read garbage in
|
||||
// our compiled layout and AV'd inside the SEH-guarded Execute, silently aborting
|
||||
// it before the KILLS/DEATHS draw below (= the empty scoreboard).
|
||||
int selected = BTPilotIsSelected(pilot, local);
|
||||
if ((int)(size_t)pilot != e.resolvedMech || e.selected != selected)
|
||||
{
|
||||
localView.MoveToAbsolute(e.x, e.y); // vtbl+0x24
|
||||
@@ -385,13 +398,16 @@ void
|
||||
// (byte-faithful: the binary does NOT latch e.resolvedMech here, so the
|
||||
// icon redraws each occupied frame.)
|
||||
}
|
||||
// KILLS = killCount@0x27c (real; ScoreMessageHandler kill branch increments it).
|
||||
e.nameDisplay->Draw(&localView, (Scalar)(*(int *)((char *)pilot + 0x27c)));
|
||||
// DEATHS: the binary reads pad_0x280 (NO writer anywhere -> perpetual 0 = a
|
||||
// shipped dead field). FIX (correctness deviation, per "if it doesn't work,
|
||||
// fix it"): read the real deaths counter Player::deathCount@0x200
|
||||
// (VehicleDeadMessageHandler increments it), so DEATHS is meaningful in MP.
|
||||
e.mechDisplay->Draw(&localView, (Scalar)(*(int *)((char *)pilot + 0x200)));
|
||||
// KILLS / DEATHS -- gauge scoring wave (Step 1, the databinding fix): read the
|
||||
// pilot's COMPILED named members via the BTPlayer bridge, NOT raw binary offsets.
|
||||
// The earlier pilot+0x27c / +0x200 reads assumed the 1995 layout; our compiled
|
||||
// BTPlayer/Player layout differs (engine base != binary), so those offsets read a
|
||||
// stale/garbage slot -> a silent 0 even after the handlers increment the members.
|
||||
// The bridge reads the SAME members the ScoreMessageHandler/VehicleDead handlers
|
||||
// write (killCount / Player::deathCount). (DEATHS uses the real deathCount, not
|
||||
// the binary's dead pad_0x280.)
|
||||
e.nameDisplay->Draw(&localView, (Scalar)BTPilotKills(pilot)); // KILLS (killCount)
|
||||
e.mechDisplay->Draw(&localView, (Scalar)BTPilotDeaths(pilot)); // DEATHS (Player::deathCount)
|
||||
}
|
||||
|
||||
++currentSlot;
|
||||
|
||||
+166
-21
@@ -154,9 +154,16 @@ static const Scalar TicksPerSecond = 1.0f; // (see note in PlayerSimulation)
|
||||
// mech.hpp (owned by the mech family). Accessed through the documented byte
|
||||
// offsets. CROSS-FAMILY -- ideally Mech would expose these accessors.
|
||||
//
|
||||
#define MECH_TONNAGE(m) (*(Scalar *)((char *)(m) + 0x4bc)) // +0x4bc
|
||||
#define MECH_DAMAGE_BIAS(m) (*(Scalar *)((char *)(m) + 0x354)) // +0x354
|
||||
#define MECH_OWNING_PLAYER(m) (*(BTPlayer **)((char *)(m) + 0x190)) // +0x190
|
||||
// gauge scoring wave: the raw binary offsets are garbage in our compiled Mech
|
||||
// (engine Entity base ~0x1BC vs the binary's 0x300; 0x638 Mech vs 0x854), and the
|
||||
// scoring handlers deref them -> a fault the moment a ScoreMessage is dispatched.
|
||||
// Reconcile: the owning player via the engine playerLink accessor (NULL for the
|
||||
// uncontrolled BT_SPAWN_ENEMY dummy -- exactly what the null-guards below rely on);
|
||||
// tonnage ratio stubbed 1.0 + damage bias stubbed 0.0 for bring-up (SCORE == raw
|
||||
// damage, un-tonnage-scaled; the real per-mech tonnage/bias accessors are a follow-up).
|
||||
#define MECH_TONNAGE(m) (1.0f) // bring-up: ratio == 1
|
||||
#define MECH_DAMAGE_BIAS(m) (0.0f) // bring-up: factor = 0*bias+1 = 1
|
||||
#define MECH_OWNING_PLAYER(m) ((BTPlayer *)((Mech *)(m))->GetPlayerLink()) // ENTITY.h:430 (NULL for the dummy)
|
||||
|
||||
//#############################################################################
|
||||
//############################### BTPlayer ##############################
|
||||
@@ -406,7 +413,7 @@ void
|
||||
//
|
||||
// Points lost for damage we took (unless we hit ourselves).
|
||||
//
|
||||
if (sender_mech != our_mech)
|
||||
if (sender_mech != our_mech && scenarioRole != 0) // scoring wave: guard the NULL bring-up role
|
||||
{
|
||||
Scalar received =
|
||||
scenarioRole->CalcDamageReceivedScore(message->damageAmount); // FUN_00429b94
|
||||
@@ -442,6 +449,10 @@ void
|
||||
|
||||
case BTPlayer::ScoreMessage::KillScore: // 2
|
||||
{
|
||||
// gauge scoring wave: the attacking mech's owner (NULL for the ownerless
|
||||
// BT_SPAWN_ENEMY dummy). Resolved once, guarded everywhere below.
|
||||
BTPlayer *sender_owner = MECH_OWNING_PLAYER(sender_mech); // *(sender+0x190), NULL for the dummy
|
||||
|
||||
//
|
||||
// A kill (or a self-kill). Inflicted value scaled by the
|
||||
// sender/self tonnage ratio.
|
||||
@@ -457,29 +468,35 @@ void
|
||||
else
|
||||
{
|
||||
//
|
||||
// Credit a kill to us and to the attacking player.
|
||||
// Credit a kill to us and (if it has one) to the attacking player.
|
||||
// The solo demo posts this to the LOCAL player with senderMechID=the
|
||||
// victim, so `this->killCount++` fires (KILLS -> 1); the ownerless
|
||||
// dummy's sender_owner is NULL -> the second increment is guarded away.
|
||||
//
|
||||
++killCount; // this+0x27c
|
||||
++MECH_OWNING_PLAYER(sender_mech)->killCount; // *(sender+0x190)+0x27c
|
||||
if (sender_owner)
|
||||
++sender_owner->killCount; // *(sender+0x190)+0x27c
|
||||
}
|
||||
|
||||
//
|
||||
// Pop a "destroyed by" status message onto the pilot HUD.
|
||||
// Pop a "destroyed by <killer>" status message onto the pilot HUD.
|
||||
// Gated on a real attacker player (+ the status pool, a NULL stub in
|
||||
// bring-up), so the ownerless-dummy solo path skips it (no player to name).
|
||||
//
|
||||
StatusMessage *status = (StatusMessage *)StatusMessagePool->New(); // FUN_00402f74(0x512f6c)
|
||||
if (status)
|
||||
if (sender_owner && StatusMessagePool)
|
||||
{
|
||||
new (status) Player__StatusMessage(
|
||||
MECH_OWNING_PLAYER(sender_mech), // *(sender+0x190)
|
||||
0,
|
||||
STATUS_DISPLAY_TIME // 0x40c00000 == 6.0f
|
||||
);
|
||||
// install the BT status-message vtable + clear its flag word
|
||||
// (Player__StatusMessage exposes neither in WinTesla -- raw).
|
||||
/* removed decompiler vtable artifact -- C++ ctor sets the real vtable (was: *(void**)status = &BTStatusMessageVTable). */
|
||||
*(int *)((char *)status + 0x18) = 0; // status[6] = 0
|
||||
StatusMessage *status = (StatusMessage *)StatusMessagePool->New(); // FUN_00402f74(0x512f6c)
|
||||
if (status)
|
||||
{
|
||||
new (status) Player__StatusMessage(
|
||||
sender_owner, // *(sender+0x190)
|
||||
0,
|
||||
STATUS_DISPLAY_TIME // 0x40c00000 == 6.0f
|
||||
);
|
||||
*(int *)((char *)status + 0x18) = 0; // status[6] = 0
|
||||
AddStatusMessage(status); // FUN_0042e580
|
||||
}
|
||||
}
|
||||
AddStatusMessage(status); // FUN_0042e580
|
||||
|
||||
//
|
||||
// If we just killed our designated objective mech, notify it.
|
||||
@@ -518,6 +535,16 @@ Scalar
|
||||
Scalar bias
|
||||
)
|
||||
{
|
||||
// gauge scoring wave: scenarioRole is NULL in bring-up (the BTMission role
|
||||
// registry has no WinTesla analog -- ctor lookup stubbed), and every modifier
|
||||
// deref below would fault the moment a score message is dispatched. A NULL
|
||||
// role == the neutral role (all modifiers 1, no penalties, bias 0), so the
|
||||
// award is just (damage + bias). Real per-role modifiers are a follow-up.
|
||||
if (scenarioRole == 0)
|
||||
{
|
||||
return damage_amount + bias;
|
||||
}
|
||||
|
||||
Scalar factor;
|
||||
|
||||
//
|
||||
@@ -575,8 +602,18 @@ void
|
||||
score
|
||||
); // FUN_00420ea4(0x20, 0x1a, 1, ...)
|
||||
|
||||
Dispatch(&score_message); // (**(*this+0xc))(this, &msg)
|
||||
currentScore = 0; // this[0x9e] = 0
|
||||
// gauge scoring wave: the binary's currentScore is a console DELTA that is
|
||||
// flushed to the operator console then zeroed. Our SCORE/RANK gauges read
|
||||
// currentScore as the RUNNING total, so only flush+zero when a console host
|
||||
// is actually present (MP / pod); in solo there is no console -> keep the
|
||||
// running score so the SCORE gauge + CalcRanking don't reset every 10s.
|
||||
Host *console_host =
|
||||
application->GetHostManager()->GetConsoleHost(); // FUN_00429078
|
||||
if (console_host)
|
||||
{
|
||||
Dispatch(&score_message); // (**(*this+0xc))(this, &msg)
|
||||
currentScore = 0; // this[0x9e] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1013,3 +1050,111 @@ Logical
|
||||
{
|
||||
return IsDerivedFrom(*GetClassDerivations()); // FUN_0041a1a4(**this[3], 0x512f94)
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// gauge scoring wave: scoreboard bridges for the PilotList gauge (btl4gau3.cpp).
|
||||
// The pilot roster entries are BTPlayer* delivered as void* (BTResolveRosterPilot);
|
||||
// these read the COMPILED named members the scoring handlers write, replacing the
|
||||
// earlier raw-offset reads (pilot+0x27c / +0x200) that don't match our layout.
|
||||
//
|
||||
int BTPilotKills(void *pilot)
|
||||
{
|
||||
int k = pilot ? ((BTPlayer *)pilot)->GetKillCount() : 0;
|
||||
static int s_n = 0;
|
||||
if (getenv("BT_SCORE_LOG") && (s_n++ % 120) == 0) // ~throttled
|
||||
DEBUG_STREAM << "[score] gauge read: pilot=" << pilot << " kills=" << k << "\n" << std::flush;
|
||||
return k;
|
||||
}
|
||||
int BTPilotDeaths(void *pilot) { return pilot ? ((BTPlayer *)pilot)->GetDeaths() : 0; }
|
||||
|
||||
// Is `pilot` the owner of `local_player`'s current target? (the SELECT-TARGET row
|
||||
// highlight). Computed here via accessors -- the PilotList's raw-offset version
|
||||
// (local+0x284 objectiveMech, then deref target+0x190) reads garbage in our layout
|
||||
// and AV'd inside the SEH-guarded Execute, silently aborting it BEFORE the KILLS/
|
||||
// DEATHS draw. GetPlayerLink() is NULL for the ownerless dummy target -> not selected.
|
||||
int BTPilotIsSelected(void *pilot, void *local_player)
|
||||
{
|
||||
if (pilot == 0 || local_player == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Mech *target = ((BTPlayer *)local_player)->GetObjectiveMech(); // @0x284
|
||||
if (target == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ((void *)((BTPlayer *)pilot) == (void *)target->GetPlayerLink()) ? 1 : 0;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// gauge scoring wave: PRODUCERS -- posted by the combat path (mech4.cpp) to feed
|
||||
// the scoreboard. The inflictor is always the viewpoint mech in bring-up, so the
|
||||
// local (crediting) player is application->GetMissionPlayer(). Bridges (not inline
|
||||
// in mech4.cpp) so the message construction lives in this complete-BTPlayer TU.
|
||||
//
|
||||
void BTPostDamageScore(Entity *victim, Scalar damage) // Step 6: per-hit SCORE
|
||||
{
|
||||
if (application == 0 || victim == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BTPlayer *local_player = (BTPlayer *)application->GetMissionPlayer();
|
||||
if (local_player == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// ScoreInflicted (scoreType 0): senderMechID = the mech that TOOK the damage.
|
||||
// -> ScoreInflictedMessageHandler: currentScore += tonnageRatio*CalcInflictedScore.
|
||||
BTPlayer::ScoreMessage message(
|
||||
BTPlayer::ScoreInflictedMessageID, // 0x16
|
||||
sizeof(BTPlayer::ScoreMessage),
|
||||
BTPlayer::ScoreMessage::DamageInflictedScore, // 0
|
||||
0.0f, // scoreAward (unused for inflicted)
|
||||
damage, // damageAmount
|
||||
victim->GetEntityID()); // senderMechID = victim
|
||||
local_player->Dispatch(&message);
|
||||
if (getenv("BT_SCORE_LOG"))
|
||||
DEBUG_STREAM << "[score] +damage " << damage << " -> currentScore="
|
||||
<< (Scalar)local_player->GetScore() << "\n" << std::flush;
|
||||
}
|
||||
|
||||
void BTPostKillScore(Entity *victim, Scalar damage) // Step 7: KILL (+ MP death)
|
||||
{
|
||||
if (application == 0 || victim == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BTPlayer *local_player = (BTPlayer *)application->GetMissionPlayer();
|
||||
if (local_player != 0)
|
||||
{
|
||||
// KillScore (scoreType 2): senderMechID MUST be the VICTIM (!= our mech) so
|
||||
// the local player is credited via `this->killCount++` -- if it were our own
|
||||
// mech, ScoreMessageHandler takes the suicide branch (award negated, no kill).
|
||||
BTPlayer::ScoreMessage kill(
|
||||
Player::ScoreMessageID, // 0x12
|
||||
sizeof(BTPlayer::ScoreMessage),
|
||||
BTPlayer::ScoreMessage::KillScore, // 2
|
||||
0.0f, // scoreAward (killBonus; 0 for bring-up)
|
||||
damage, // damageAmount (killing-blow)
|
||||
victim->GetEntityID()); // senderMechID = victim
|
||||
local_player->Dispatch(&kill);
|
||||
if (getenv("BT_SCORE_LOG"))
|
||||
DEBUG_STREAM << "[score] *** KILL *** localPlayer=" << (void *)local_player
|
||||
<< " killCount=" << local_player->GetKillCount()
|
||||
<< " deaths=" << local_player->GetDeaths()
|
||||
<< " score=" << (Scalar)local_player->GetScore()
|
||||
<< " rank=" << local_player->GetRanking() << "\n" << std::flush;
|
||||
}
|
||||
|
||||
// MP DEATH: credit a death to the VICTIM's own player. NULL for the solo
|
||||
// BT_SPAWN_ENEMY dummy (GetPlayerLink()==0) -> skipped, so DEATHS stays 0 in
|
||||
// solo (authentic -- DEATHS only lands on a real pilot in multiplayer).
|
||||
BTPlayer *victim_player = (BTPlayer *)((Mech *)victim)->GetPlayerLink();
|
||||
if (victim_player != 0)
|
||||
{
|
||||
Player::VehicleDeadMessage dead(
|
||||
Player::VehicleDeadMessageID, // 0x13
|
||||
sizeof(Player::VehicleDeadMessage));
|
||||
victim_player->Dispatch(&dead);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,6 +312,19 @@ class DropZone__ReplyMessage;
|
||||
Logical
|
||||
TestInstance() const; // @004c0f28
|
||||
|
||||
//
|
||||
// gauge scoring wave: public scoreboard accessors so the cockpit gauges
|
||||
// (PilotList KILLS/DEATHS) read the SAME compiled members the scoring
|
||||
// handlers write -- the earlier raw-offset reads (pilot+0x27c / +0x200)
|
||||
// don't match our compiled layout (engine base != the 1995 binary) and
|
||||
// silently read 0. currentScore/playerRanking are inherited Player fields.
|
||||
//
|
||||
int GetKillCount() const { return killCount; } // @0x27c
|
||||
int GetDeaths() const { return deathCount; } // @0x200 (Player)
|
||||
Scalar GetScore() const { return currentScore; } // @0x278 (Player)
|
||||
int GetRanking() const { return playerRanking; } // (Player)
|
||||
Mech *GetObjectiveMech() const { return objectiveMech; } // @0x284 (current target)
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Local data
|
||||
//
|
||||
|
||||
@@ -611,6 +611,9 @@ static int gEnemyDestroyed = 0;
|
||||
// BRING-UP: the spawned target/enemy mech (defined in btplayer.cpp). The player
|
||||
// mech locks onto it as its current target each frame (see targeting step below).
|
||||
extern Entity *gEnemyMech;
|
||||
// gauge scoring wave: producers (btplayer.cpp) -- feed the scoreboard from combat.
|
||||
extern void BTPostDamageScore(Entity *victim, Scalar damage); // per-hit SCORE (ScoreInflicted)
|
||||
extern void BTPostKillScore(Entity *victim, Scalar damage); // KILL (+ MP death)
|
||||
|
||||
// Mech target slots (verified vs the binary's weapon/fire path, part_013.c):
|
||||
// mech+0x37c Point3D current target world position (range/aim source)
|
||||
@@ -829,6 +832,9 @@ static void
|
||||
Entity::TakeDamageMessageID, sizeof(Entity::TakeDamageMessage),
|
||||
0 /*inflictor id: bring-up*/, zone, dmg);
|
||||
tgt->Dispatch(&take_damage);
|
||||
// gauge scoring wave (Step 6): a projectile hit credits SCORE too
|
||||
// (tgt == gEnemyMech here; local player is the viewpoint shooter).
|
||||
BTPostDamageScore((Entity *)tgt, p.damage);
|
||||
DEBUG_STREAM << "[projectile] IMPACT damage=" << p.damage << " zone=" << zone << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
@@ -2170,6 +2176,10 @@ void
|
||||
dmg);
|
||||
gEnemyMech->Dispatch(&take_damage);
|
||||
|
||||
// gauge scoring wave (Step 6): credit the local player for damage
|
||||
// dealt -> SCORE climbs per hit (currentScore += tonnageRatio*award).
|
||||
BTPostDamageScore(gEnemyMech, kShotDamage);
|
||||
|
||||
Scalar s = ((Mech *)gEnemyMech)->Zone(zone)->damageLevel; // [0,1], 1.0=destroyed (engine base field)
|
||||
DEBUG_STREAM << "[damage] hit zone " << zone << "/" << zc
|
||||
<< " structure=" << s << "\n" << std::flush;
|
||||
@@ -2190,6 +2200,11 @@ void
|
||||
{
|
||||
gEnemyDestroyed = 1;
|
||||
|
||||
// gauge scoring wave (Step 7): credit the KILL to the local player
|
||||
// -> KILLS 0->1 (senderMechID = the VICTIM, so the !=our-mech branch
|
||||
// fires; the ownerless dummy yields no death, DEATHS stays 0).
|
||||
BTPostKillScore(gEnemyMech, kShotDamage);
|
||||
|
||||
// Death explosion at the target.
|
||||
Origin death_origin = ((Mech *)gEnemyMech)->localOrigin;
|
||||
Explosion::MakeMessage death_exp(
|
||||
|
||||
Reference in New Issue
Block a user