scoring: restore the type-0 INTERCEPTOR -- per-hit inflicted credit was live all along
Players reported scoring and K/D going screwy on 4.11.817. Cause: build 787
(#45/#134) retired the port's per-hit inflicted crediting as an "invention",
on the strength of a KB claim that the type-0 score handler was dead code.
That claim was wrong.
BTPlayer overrides Dispatch -- vtable @00513300 slot 3 = FUN_004bffa0 -- and
splits type 0 off BEFORE base dispatch:
if (msg->id == 0x16 && msg->type == 0) FUN_004c0200(...); // ScoreInflicted
else base dispatch;
@004c0200 names itself in its own Verify string
("BTPlayer::ScoreInflictedMessageHandler") and computes
CalcInflicted(basis) -> negate if target==self -> x (targetTonnage/ownTonnage)
-> accumulate into +0x278. ScoreMessageHandler's type-0 arm Verify-rejects
precisely BECAUSE this interceptor guarantees type 0 never reaches it.
The port had the handler, faithfully reconstructed, and no interceptor -- so
Block B's inflicted reports all landed in the rejecting arm and banked 0.
Per-hit damage credit was silently deleted.
Independently corroborated by the ORIGINAL MANUAL'S SCORING CHART (filed as
reference/manual/scoring_chart.webp, from Lynx): "+1 each damage point scored
on opponent's armor" and "-1 each self-inflicted point of armor damage" -- the
negate-if-target-is-self arm exactly. Without that chart the dead-code note
would probably have stood.
Verified (scratchpad/night13/scoreverify.sh, cross-node kill, 2 nodes):
type-0 Verify rejections 0 (was firing on every non-lethal hit)
inflicted score rows 83 awards 0.98..25.00, all positive, tracking damage
kill path un-regressed type=2 award=4.88 kills=1, victim respawn x1
KB: combat-damage.md report B and the score-model paragraph rewritten, with
the full chart and THREE unreconciled rows flagged [T4] -- flat +500 kill vs
the benched 4.88, +1000 at game start, and -1000 eject / -500 ammo (which
would live in ScenarioRole::specialCaseDeathPenalty @role+0x20, read by the
port but authored nowhere in shipped content).
KNOWN, NOT FIXED HERE: in MP the running total does not persist -- currentScore
is flushed to the operator console and ZEROED (btplayer.cpp ~1219) because the
binary treats it as a console DELTA. Restoring the credit makes that very
visible (bench: totals climb to ~35 then reset). Needs its own decision; the
chart's "+1000 starting the game" implies a persistent total lives somewhere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCJQkvq6G2JNrpVbA75tVZ
This commit is contained in:
co-authored by
Claude Opus 5
parent
4642129e76
commit
27721754da
@@ -606,10 +606,21 @@ btplayer.hpp, static_assert-locked; `scoreAward`=APPLIED tally in all three):
|
|||||||
the VICTIM role's `killBonus` (role+0x1c). senderMechID = the victim. Suicide (eject charge,
|
the VICTIM role's `killBonus` (role+0x1c). senderMechID = the victim. Suicide (eject charge,
|
||||||
self-damage) IS dispatched — the handler negates the award (@0x4c03ab `fchs`) and skips
|
self-damage) IS dispatched — the handler negates the award (@0x4c03ab `fchs`) and skips
|
||||||
`killCount++`: **the #134 panic penalty, live** (bench: `type=2 award=-39.00 kills=0`).
|
`killCount++`: **the #134 panic penalty, live** (bench: `type=2 award=-39.00 kills=0`).
|
||||||
- **B @0x4a05d9, not newly killed && tally≠0** → type 0 to the shooter. The ONLY registered 0x16
|
- **B @0x4a05d9, not newly killed && tally≠0** → type 0 to the shooter: the **per-hit INFLICTED
|
||||||
receiver Verify-rejects type 0 → 1995 folded an UNINITIALIZED stack float into the shooter's
|
credit**, and it is LIVE. ⚠ **CORRECTED 2026-08-07** — this entry previously read "the ONLY
|
||||||
score on every non-lethal hit (real 1995 bug — @0x4c0200, the handler that accepts type 0, is
|
registered 0x16 receiver Verify-rejects type 0 → 1995 folded an UNINITIALIZED stack float into
|
||||||
in NO table entry: dead code). Port sends it for wire fidelity, banks award 0.
|
the shooter's score on every non-lethal hit (real 1995 bug — @0x4c0200 … is in NO table entry:
|
||||||
|
dead code)". **That was wrong**, and build 787 retired the port's per-hit crediting on the
|
||||||
|
strength of it (the scoring regression players reported on 4.11.817). `BTPlayer` overrides
|
||||||
|
**`Dispatch` — vtable `@00513300` slot 3 = `FUN_004bffa0`** — and splits type 0 off BEFORE base
|
||||||
|
dispatch: `if (id==0x16 && type==0) FUN_004c0200(...); else base;`. `@004c0200` names itself in
|
||||||
|
its own Verify string (`BTPlayer::ScoreInflictedMessageHandler`) and computes
|
||||||
|
`CalcInflicted(basis) → negate if target==self → × (targetTonnage/ownTonnage) → += +0x278`.
|
||||||
|
`ScoreMessageHandler`'s type-0 arm Verify-rejects precisely BECAUSE the interceptor guarantees
|
||||||
|
type 0 never reaches it. Independently corroborated by the original manual's SCORING CHART
|
||||||
|
(`reference/manual/scoring_chart.webp`, via Lynx): "+1 each damage point scored on opponent's armor" and "-1 each
|
||||||
|
self-inflicted point of armor damage" — the negate-if-self arm exactly. Interceptor restored;
|
||||||
|
benched 83 inflicted rows, awards 0.98–25.00, zero type-0 Verify rejections. [T1]
|
||||||
- **C @0x4a06c0, tally>0 (kills included)** → type 1 DamageReceivedScore to the VICTIM's player.
|
- **C @0x4a06c0, tally>0 (kills included)** → type 1 DamageReceivedScore to the VICTIM's player.
|
||||||
Basis = INTENDED damage (burstCount×amount). senderMechID = the INFLICTOR. Feeds the received
|
Basis = INTENDED damage (burstCount×amount). senderMechID = the INFLICTOR. Feeds the received
|
||||||
penalty (`CalcDamageReceivedScore` returns the NEGATIVE) + the operator-console VTVDamaged line
|
penalty (`CalcDamageReceivedScore` returns the NEGATIVE) + the operator-console VTVDamaged line
|
||||||
@@ -617,9 +628,31 @@ btplayer.hpp, static_assert-locked; `scoreAward`=APPLIED tally in all three):
|
|||||||
Reports carry the LOOP-ENTRY zone (msg+0x24, never rewritten mid-loop), the vital-wreck flag, and
|
Reports carry the LOOP-ENTRY zone (msg+0x24, never rewritten mid-loop), the vital-wreck flag, and
|
||||||
`inflictingSubsystemID` (msg+0x5c, engine T0 name).
|
`inflictingSubsystemID` (msg+0x5c, engine T0 name).
|
||||||
|
|
||||||
**Score model consequence** [T1]: 1995 pod scoring = **kill awards + received-damage penalties +
|
**Score model consequence** [T1, ⚠ REWRITTEN 2026-08-07]: 1995 pod scoring = **per-hit inflicted
|
||||||
death costs. No per-hit inflicted credit** (the port's old per-hit crediting — and the #95 salvo
|
credit + kill awards + received-damage penalties + death costs**. The previous text here claimed
|
||||||
fix on top of it — were inventions riding the dead @0x4c0200 channel; both retired).
|
"No per-hit inflicted credit … inventions riding the dead @0x4c0200 channel; both retired" — that
|
||||||
|
followed from the dead-code misreading corrected in report B above, and retiring the credit IS the
|
||||||
|
scoring regression reported on 4.11.817. The **original manual's SCORING CHART** (`reference/manual/scoring_chart.webp`)
|
||||||
|
is the cross-check for every row and should be consulted before touching this path again:
|
||||||
|
|
||||||
|
| Points | Event |
|
||||||
|
|---|---|
|
||||||
|
| +1000 | Starting the game |
|
||||||
|
| +1 | Each damage point scored on opponent's armor |
|
||||||
|
| +10..+30 | Destroying an opponent's internal system |
|
||||||
|
| +500 | Destroying an opponent's 'Mech |
|
||||||
|
| −1 | Each self-inflicted point of armor damage |
|
||||||
|
| −10..−30 | Knocking out one of your own internal systems |
|
||||||
|
| −500 | Destroying your own 'Mech by an ammo explosion |
|
||||||
|
| −1000 | Destroying your own 'Mech by ejecting |
|
||||||
|
|
||||||
|
⚠ **Three chart rows are NOT yet reconciled with the reconstruction** — treat as open [T4]:
|
||||||
|
(a) a kill benches at `award=4.88`, two orders off the chart's flat **+500**; (b) **+1000 at
|
||||||
|
game start** has no known implementation; (c) **−1000 eject / −500 ammo** would live in
|
||||||
|
`ScenarioRole::specialCaseDeathPenalty` (role+0x20), which the port reads
|
||||||
|
(`GetSpecialCaseDeathPenalty`, the death-cost block) but which **shipped content authors
|
||||||
|
nowhere**, so it is 0 in the field — the #134 symptom by another route. Do not "fix" these by
|
||||||
|
hard-coding chart numbers; find where the binary sources them.
|
||||||
Kill award = `(victimKillBonus + tally) × killerRole.damageInflictedModifier ×
|
Kill award = `(victimKillBonus + tally) × killerRole.damageInflictedModifier ×
|
||||||
(victimAvgZoneDamage@0x354 × damageBias + 1.0) × (victimTonnage/killerTonnage)`; same-team kill in
|
(victimAvgZoneDamage@0x354 × damageBias + 1.0) × (victimTonnage/killerTonnage)`; same-team kill in
|
||||||
a non-FFA game = `-friendlyFirePenalty` basis (inline strcmp of `teamName@0x20c`, gate
|
a non-FFA game = `-friendlyFirePenalty` basis (inline strcmp of `teamName@0x20c`, gate
|
||||||
|
|||||||
@@ -731,6 +731,47 @@ void
|
|||||||
suppressConsole = 0; // this+0x258
|
suppressConsole = 0; // this+0x258
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// Dispatch (@004bffa0, vtable @00513300 slot 3)
|
||||||
|
//
|
||||||
|
// THE TYPE-0 INTERCEPTOR. Restored 2026-08-07 -- it was missing, and its
|
||||||
|
// absence silently deleted per-hit inflicted scoring:
|
||||||
|
//
|
||||||
|
// * the binary carries all three score reports under ONE id (0x16) and
|
||||||
|
// splits type 0 off here, BEFORE base dispatch, straight into
|
||||||
|
// ScoreInflictedMessageHandler (@004c0200 -- which names itself in its own
|
||||||
|
// Verify string, "BTPlayer::ScoreInflictedMessageHandler");
|
||||||
|
// * ScoreMessageHandler's type-0 arm Verify-rejects ON PURPOSE, because this
|
||||||
|
// interceptor guarantees type 0 never gets that far;
|
||||||
|
// * the port had the handler, faithfully reconstructed, and NO interceptor.
|
||||||
|
// Block B sends its inflicted report under Player::ScoreMessageID, so every
|
||||||
|
// one of them landed in the rejecting arm and banked 0.
|
||||||
|
//
|
||||||
|
// The KB previously recorded @004c0200 as "in NO table entry: dead code" and
|
||||||
|
// concluded 1995 folded an uninitialised stack float into the shooter's score
|
||||||
|
// on every non-lethal hit -- and the port's per-hit crediting was retired as an
|
||||||
|
// "invention" on that basis (#45/#134, build 787, the build players report
|
||||||
|
// scoring regressed in). That reading was wrong: the handler is live through
|
||||||
|
// THIS vtable slot, and the original manual's SCORING CHART independently
|
||||||
|
// corroborates what it computes -- "+1 each damage point scored on opponent's
|
||||||
|
// armor" and "-1 each self-inflicted point of armor damage", which is exactly
|
||||||
|
// this handler's negate-if-target-is-self arm. combat-damage.md is corrected.
|
||||||
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
void
|
||||||
|
BTPlayer::Dispatch(Receiver::Message *what)
|
||||||
|
{
|
||||||
|
if (what != 0
|
||||||
|
&& what->messageID == Player::ScoreMessageID
|
||||||
|
&& ((BTPlayer::ScoreMessage *)what)->scoreType
|
||||||
|
== BTPlayer::ScoreMessage::DamageInflictedScore)
|
||||||
|
{
|
||||||
|
ScoreInflictedMessageHandler((BTPlayer::ScoreMessage *)what);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Player::Dispatch(what);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// ScoreInflictedMessageHandler
|
// ScoreInflictedMessageHandler
|
||||||
//
|
//
|
||||||
@@ -2351,9 +2392,14 @@ void BTMechPostCombatReports(
|
|||||||
else if (damage_tally != 0.0f && shooter_player != 0)
|
else if (damage_tally != 0.0f && shooter_player != 0)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Block B: the plain inflicted report. Wire fidelity only -- the 0x16
|
// Block B: the inflicted report -- "+1 each damage point scored on
|
||||||
// handler Verify-rejects type 0 and banks award 0 (1995 banked an
|
// opponent's armor" (original manual scoring chart). Goes out under
|
||||||
// uninitialized stack float; see the handler's type-0 arm note).
|
// Player::ScoreMessageID with type 0, exactly as the binary does; the
|
||||||
|
// Dispatch override (@004bffa0) intercepts it into
|
||||||
|
// ScoreInflictedMessageHandler. It is NOT wire-fidelity-only -- the
|
||||||
|
// old note here claimed the handler banks 0 because @004c0200 was
|
||||||
|
// "dead code", which was a misreading of the vtable; corrected
|
||||||
|
// 2026-08-07 and the interceptor restored.
|
||||||
//
|
//
|
||||||
BTPlayer::ScoreMessage inflicted(
|
BTPlayer::ScoreMessage inflicted(
|
||||||
Player::ScoreMessageID,
|
Player::ScoreMessageID,
|
||||||
|
|||||||
@@ -288,6 +288,23 @@ class DropZone__ReplyMessage;
|
|||||||
private:
|
private:
|
||||||
static const HandlerEntry MessageHandlerEntries[];
|
static const HandlerEntry MessageHandlerEntries[];
|
||||||
|
|
||||||
|
public:
|
||||||
|
//
|
||||||
|
// @004bffa0 -- the DISPATCH OVERRIDE (vtable @00513300 slot 3). The
|
||||||
|
// binary uses ONE message id (0x16) for all three score reports and
|
||||||
|
// splits type 0 off HERE, before base dispatch:
|
||||||
|
//
|
||||||
|
// if (msg->id == 0x16 && msg->type == 0) ScoreInflicted(msg);
|
||||||
|
// else base dispatch;
|
||||||
|
//
|
||||||
|
// which is why ScoreMessageHandler's own type-0 arm can Verify-reject:
|
||||||
|
// the interceptor guarantees type 0 never reaches it. Without this
|
||||||
|
// override every inflicted report lands in the rejecting arm and scores
|
||||||
|
// nothing -- see the note in btplayer.cpp.
|
||||||
|
//
|
||||||
|
virtual void
|
||||||
|
Dispatch(Receiver::Message *what); // @004bffa0
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static MessageHandlerSet& GetMessageHandlers();
|
static MessageHandlerSet& GetMessageHandlers();
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -0,0 +1,91 @@
|
|||||||
|
"""Night 13: post the ghost/eject findings to #108 and settle #144. ASCII only."""
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, r"C:\git\bt411\scratchpad\night7")
|
||||||
|
import gitea
|
||||||
|
|
||||||
|
gitea.comment(108, """Night 13 (2026-08-06, build **4.11.817**) -- eject-ghosts, and why the logs barely saw them.
|
||||||
|
|
||||||
|
## Field finding
|
||||||
|
|
||||||
|
Testers reported MANY ghost mechs, all from panic-button self-destructs, none respawning
|
||||||
|
properly -- and the operator has seen eject-respawn work every time before this night, so this is a
|
||||||
|
REGRESSION, not a gap (night 12 = build 774).
|
||||||
|
|
||||||
|
The logs show it happening, on three independent witnesses. Dave punch-ejected; on HIS machine the
|
||||||
|
whole death path ran (`DeathWithoutHonor`, `PUNCH-OUT`, death transition, wreck, explosion), he
|
||||||
|
respawned, took a drop-zone slot, and drove another 193 logged frames **in the same round**, which
|
||||||
|
then ended normally -- no crash, no disconnect. On BOTH peers his mech's history ends at the wreck
|
||||||
|
and never resumes; host 2 vanishes from santo's world for the remaining ~4300 lines of that round.
|
||||||
|
|
||||||
|
The discriminator is clean: the SAME mech's NORMAL deaths replicated their respawns 4-6 times in
|
||||||
|
that same session. Only the eject failed.
|
||||||
|
|
||||||
|
## Why the ghost detector said almost nothing
|
||||||
|
|
||||||
|
One `[ghost]` line all night, for an unrelated live mech. The gate is
|
||||||
|
|
||||||
|
&& !owner->WreckBuried() // "buried wrecks are expected-silent"
|
||||||
|
WreckBuried() { return collisionVolumeCount == 0; }
|
||||||
|
|
||||||
|
An eject-ghost IS a wreck that stopped receiving records, so the detector files it under
|
||||||
|
expected-silent. It can only ever catch a LIVE replicant going quiet -- structurally blind to this
|
||||||
|
failure.
|
||||||
|
|
||||||
|
## Why the census said "one" when testers saw many
|
||||||
|
|
||||||
|
Counting ghosts meant pairing the un-wreck receipt against
|
||||||
|
`[BTrender] wreck: 'thrdbr.bgf' missing -> gendbr.bgf fallback` -- a MISSING-ASSET warning, not a
|
||||||
|
death, which only prints for chassis whose wreck model is absent. Fixed in `4642129`: every
|
||||||
|
replicant entering the wreck state now emits an ungated
|
||||||
|
|
||||||
|
[wreck] replicant H:E entered wreck state (mode X->9) at (x,z)
|
||||||
|
|
||||||
|
symmetric with the existing un-wreck line, so the count is exactly (enters - un-wrecks) per entity.
|
||||||
|
Verified 2-node: 5 enters / 5 exits, exactly paired -- **while the old marker printed ZERO times in
|
||||||
|
the same run**. Five real deaths, invisible to what the census was reading. Treat the "one ghost on
|
||||||
|
night 13" number as a floor, not a count.
|
||||||
|
|
||||||
|
Census tooling: `scratchpad/night13/ghostcensus.py` (NB: filter the log owner's OWN mech -- an
|
||||||
|
owner's own wreck can never pair, since `un-wrecked` only logs for replicants; not doing so
|
||||||
|
manufactures false positives in every log).
|
||||||
|
|
||||||
|
## NOT reproduced by bench -- five rigs failed to trigger a punch-out at all
|
||||||
|
|
||||||
|
`BT_BTNTEST` never reached the mapper for the panic button (0x3D) or the searchlight (0x14): no
|
||||||
|
`[eject]`, no `PUNCH-OUT`, not even a lamp change -- so it is the SEAM, not the address.
|
||||||
|
`BT_EJECT_AT` (mech4.cpp:3310, the purpose-built hook) did not fire either. Benches are staged
|
||||||
|
(`ejectghost.sh`, `ejectlight.sh`, `ejectat.sh`) with their failure modes in the headers.
|
||||||
|
|
||||||
|
What the benches DID establish: **normal death replication is healthy on 817** -- 9 force-kill
|
||||||
|
deaths, 8 peer un-wrecks, 0 ghost lines. So an eject-only shortfall is the defect, not a broken rig.
|
||||||
|
|
||||||
|
## Open lead
|
||||||
|
|
||||||
|
The operator observed that the ghost mechs all had their SEARCHLIGHTS ON. Untested -- there is
|
||||||
|
currently no key or env that toggles the searchlight headlessly, so it needs a small bench hook.
|
||||||
|
Mechanically plausible: the searchlight is the only thing that attaches extra geometry into a peer's
|
||||||
|
mech render tree (btl4vid.cpp builds a `spot.bgf` cone as a `DPLStaticChildRenderable` parented to
|
||||||
|
the lamp's mount-segment renderable), and the death path does a wreck swap while respawn does
|
||||||
|
"rebuilt intact model (N segs restored, hulk dropped)" -- a child renderable held across those two
|
||||||
|
rebuilds is a lifetime hazard, and it would only bite mechs whose lamp was ON.
|
||||||
|
|
||||||
|
## Regression window
|
||||||
|
|
||||||
|
Commits 775-817. Two touch the death path: `91bd286` (787) rewrote the VehicleDead dispatch --
|
||||||
|
the message that drives the respawn cycle -- including the `killer == victim` eject case and the
|
||||||
|
filter that had blocked the panic-eject path; and `297127d` (784, death blast). Unverified.""")
|
||||||
|
|
||||||
|
gitea.close(144, """**NOT A BUG -- authentic. Closing.**
|
||||||
|
|
||||||
|
The eject sets `suppressConsole` (+0x258, `BTPlayerEjectBookkeeping`), and the #89 death-blast gate
|
||||||
|
reads it:
|
||||||
|
|
||||||
|
gates : owning player's advancedDamageOn (+0x264) AND
|
||||||
|
NOT suppressConsole (+0x258 -- eject sets it: punch-outs never blast)
|
||||||
|
[T1 @0x4a0aa8-0x4a0ad6]
|
||||||
|
|
||||||
|
So a mech that punches out does not splash its neighbourhood, by design, in the 1995 binary. SAURON's
|
||||||
|
observation ("splash worked on mechs that died close, but no splash on a panic / eject / suicide
|
||||||
|
death") is a correct reading of authentic behaviour.
|
||||||
|
|
||||||
|
Closing so nobody "fixes" it. Found while investigating the night-13 eject ghosts (#108).""")
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =========================================================================
|
||||||
|
# SCORING verify -- the type-0 interceptor (@004bffa0) restored.
|
||||||
|
#
|
||||||
|
# Composition lifted from night12/scorekill.sh, which is known to produce a
|
||||||
|
# clean cross-node kill: A (madcat, shooter) zone-walk-hammers B (loki,
|
||||||
|
# spinner) at 90u until B dies.
|
||||||
|
#
|
||||||
|
# THE A/B IS READABLE IN ONE RUN, because the old behaviour left a receipt:
|
||||||
|
# BEFORE every inflicted report hit ScoreMessageHandler's type-0 arm and
|
||||||
|
# tripped Verify "ScoreMessageHandler should not be given
|
||||||
|
# DamageInflictedScoreMessages!" -- night12's bench listed those
|
||||||
|
# Verify prints as an expected PASS signal.
|
||||||
|
# AFTER the interceptor routes type 0 to ScoreInflictedMessageHandler, so
|
||||||
|
# those Verify prints must be GONE and matchlog SCORE type=0 rows
|
||||||
|
# with non-zero awards must appear instead.
|
||||||
|
#
|
||||||
|
# Chart cross-check (original manual, via Lynx): "+1 each damage point scored
|
||||||
|
# on opponent's armor". The handler scales by tonnage ratio and the role's
|
||||||
|
# damageInflictedModifier, so award != damage exactly -- but it must TRACK
|
||||||
|
# damage, not sit at zero, and must be NEGATIVE for self-damage.
|
||||||
|
# =========================================================================
|
||||||
|
set -x
|
||||||
|
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||||
|
cd /c/git/bt411/content || exit 1
|
||||||
|
bt_assert_player_env
|
||||||
|
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||||
|
sleep 2
|
||||||
|
rm -f sv_a.log sv_b.log sv_relay.log matchlog_*.txt
|
||||||
|
bt_expert_egg MP.EGG SV.EGG
|
||||||
|
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/" SV.EGG
|
||||||
|
python - << 'EOF'
|
||||||
|
lines = open('SV.EGG').read().splitlines(True)
|
||||||
|
n = 0
|
||||||
|
for i, l in enumerate(lines):
|
||||||
|
if l.startswith('vehicle='):
|
||||||
|
n += 1
|
||||||
|
lines[i] = 'vehicle=madcat\n' if n == 1 else 'vehicle=loki\n'
|
||||||
|
open('SV.EGG', 'w').writelines(lines)
|
||||||
|
print('vehicles set:', n)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
( export BT_DMG_LOG=1 BT_DEATH_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1 BT_SCORE_LOG=1
|
||||||
|
bt_launch sv_b.log SV.EGG 0x0C -net 1601 )
|
||||||
|
sleep 2
|
||||||
|
( export BT_ZONE_WALK=8 BT_WALK_ZONES=dz_ldleg
|
||||||
|
export BT_GOTO=enemy BT_GOTO_STOP=90 BT_KEY_NOFOCUS=1
|
||||||
|
export BT_DMG_LOG=1 BT_DEATH_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1 BT_SCORE_LOG=1
|
||||||
|
bt_launch sv_a.log SV.EGG 0x03 -net 1501 )
|
||||||
|
sleep 5
|
||||||
|
python ../tools/btconsole.py SV.EGG 127.0.0.1:1501 127.0.0.1:1601 > sv_relay.log 2>&1 &
|
||||||
|
RELAY=$!
|
||||||
|
sleep 260
|
||||||
|
kill $RELAY 2>/dev/null
|
||||||
|
sleep 3
|
||||||
|
bt_kill_ours
|
||||||
|
sleep 2
|
||||||
|
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
echo "=================== SCORING VERIFY ==================="
|
||||||
|
echo "--- 0. did combat happen at all? (if 0 hits the run is VOID) ---"
|
||||||
|
echo -n "damage rows on victim B : "; grep -ac "dmghit\|DMG" sv_b.log
|
||||||
|
echo
|
||||||
|
echo "--- 1. THE OLD SYMPTOM: type-0 rejections (must be ZERO now) ---"
|
||||||
|
echo -n "'should not be given DamageInflictedScore' Verify prints: "
|
||||||
|
cat sv_a.log sv_b.log | grep -ac "should not be given DamageInflictedScore"
|
||||||
|
echo
|
||||||
|
echo "--- 2. THE FIX: inflicted score rows (matchlog SCORE type=0) ---"
|
||||||
|
echo -n "type=0 rows: "; cat matchlog_*.txt 2>/dev/null | grep -ac "type=0"
|
||||||
|
cat matchlog_*.txt 2>/dev/null | grep -a "type=0" | head -8
|
||||||
|
echo
|
||||||
|
echo "--- 3. award vs damage: does the credit TRACK damage? ---"
|
||||||
|
python - << 'EOF'
|
||||||
|
import glob, re
|
||||||
|
aw = []
|
||||||
|
for fn in glob.glob('matchlog_*.txt'):
|
||||||
|
for line in open(fn, errors='replace'):
|
||||||
|
m = re.search(r'type=0 award=(-?[\d.]+) total=(-?[\d.]+)', line)
|
||||||
|
if m:
|
||||||
|
aw.append((float(m.group(1)), float(m.group(2))))
|
||||||
|
if not aw:
|
||||||
|
print(' NO type=0 rows -- interceptor did not fire')
|
||||||
|
else:
|
||||||
|
pos = [a for a, t in aw if a > 0]
|
||||||
|
neg = [a for a, t in aw if a < 0]
|
||||||
|
print(' rows=%d positive=%d negative(self)=%d' % (len(aw), len(pos), len(neg)))
|
||||||
|
print(' award range: %.2f .. %.2f running total ends at %.2f'
|
||||||
|
% (min(a for a, t in aw), max(a for a, t in aw), aw[-1][1]))
|
||||||
|
EOF
|
||||||
|
echo
|
||||||
|
echo "--- 4. kill path un-regressed (type=2) + respawn ---"
|
||||||
|
echo -n "type=2 kill rows: "; cat matchlog_*.txt 2>/dev/null | grep -ac "type=2"
|
||||||
|
cat matchlog_*.txt 2>/dev/null | grep -a "type=2" | head -3
|
||||||
|
echo -n "victim death cycles: "; grep -ac "death cycle START" sv_b.log
|
||||||
Reference in New Issue
Block a user