Move app to apps/web-react in pnpm workspace layout
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Unit tests for connectAbilities: deaths are attributed to the next
|
||||
* scoreboard event and matched to a player row by name/weapon, with
|
||||
* ambiguous matches (two players on the same weapon, misread name)
|
||||
* left unattributed.
|
||||
*/
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import type {
|
||||
AbilityWithUnknown,
|
||||
MainWeaponId,
|
||||
} from "~/modules/in-game-lists/types";
|
||||
import { connectAbilities } from "../core/ability-harvest";
|
||||
import {
|
||||
DEATH_EVENT_TYPE,
|
||||
type DeathData,
|
||||
} from "../core/detectors/death/index";
|
||||
import { SCOREBOARD_EVENT_TYPE } from "../core/detectors/scoreboard/index";
|
||||
import type { DetectedEvent } from "../core/detectors/types";
|
||||
import test from "./node-test-compat";
|
||||
|
||||
const GRID_A: AbilityWithUnknown[][] = [
|
||||
["ISM", "ISM", "ISM", "ISM"],
|
||||
["RSU", "RSU", "RSU", "RSU"],
|
||||
["SSU", "SSU", "SSU", "SSU"],
|
||||
];
|
||||
const GRID_B: AbilityWithUnknown[][] = [
|
||||
["QR", "QR", "QR", "QR"],
|
||||
["QSJ", "QSJ", "QSJ", "QSJ"],
|
||||
["IRU", "IRU", "IRU", "IRU"],
|
||||
];
|
||||
|
||||
function player(name: string, weaponId: MainWeaponId) {
|
||||
return { name, weaponId, paint: null, ka: null, d: null, s: null };
|
||||
}
|
||||
|
||||
function death(
|
||||
t: number,
|
||||
name: string | null,
|
||||
weaponId: MainWeaponId | null,
|
||||
abilities: AbilityWithUnknown[][] = GRID_A,
|
||||
): DetectedEvent<DeathData> {
|
||||
return {
|
||||
type: DEATH_EVENT_TYPE,
|
||||
t,
|
||||
confidence: 0.9,
|
||||
data: {
|
||||
weaponId,
|
||||
weaponType: weaponId !== null ? "MAIN" : null,
|
||||
abilities,
|
||||
name,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function scoreboard(
|
||||
t: number,
|
||||
players: ReturnType<typeof player>[],
|
||||
): DetectedEvent {
|
||||
return {
|
||||
type: SCOREBOARD_EVENT_TYPE,
|
||||
t,
|
||||
confidence: 0.9,
|
||||
data: {
|
||||
lobby: null,
|
||||
mode: null,
|
||||
stage: null,
|
||||
scores: [null, null],
|
||||
players,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const PLAYERS = [
|
||||
player("Alpha", 40),
|
||||
player("Bravo", 50),
|
||||
player("Charlie", 60),
|
||||
player("Delta", 50),
|
||||
player("Echo", 70),
|
||||
player("Foxtrot", 80),
|
||||
player("Golf", 90),
|
||||
player("Hotel", 100),
|
||||
];
|
||||
|
||||
test("matches by name+weapon, unique name, and unique weapon", () => {
|
||||
const board = scoreboard(100, PLAYERS);
|
||||
const map = connectAbilities([
|
||||
death(10, "Bravo", 50), // name+weapon → index 1, not the other 50 at index 3
|
||||
death(20, "charlie ", null, GRID_B), // unique name alone (case/space-insensitive) → 2
|
||||
death(30, "garbled", 70), // unique weapon despite misread name → 4
|
||||
board,
|
||||
]);
|
||||
const abilities = map.get(board);
|
||||
assert.ok(abilities);
|
||||
assert.deepEqual(abilities.get(1), GRID_A);
|
||||
assert.deepEqual(abilities.get(2), GRID_B);
|
||||
assert.deepEqual(abilities.get(4), GRID_A);
|
||||
assert.equal(abilities.size, 3);
|
||||
});
|
||||
|
||||
test("ambiguous weapon with unknown name stays unattributed", () => {
|
||||
const board = scoreboard(100, PLAYERS);
|
||||
const map = connectAbilities([death(10, "garbled", 50), board]);
|
||||
assert.equal(map.get(board), undefined);
|
||||
});
|
||||
|
||||
test("deaths only attach to the next scoreboard", () => {
|
||||
const board1 = scoreboard(100, PLAYERS);
|
||||
const board2 = scoreboard(400, PLAYERS);
|
||||
const map = connectAbilities([
|
||||
death(10, "Alpha", 40),
|
||||
board1,
|
||||
death(200, "Echo", 70, GRID_B),
|
||||
board2,
|
||||
]);
|
||||
assert.deepEqual(map.get(board1)?.get(0), GRID_A);
|
||||
assert.equal(map.get(board1)?.get(4), undefined);
|
||||
assert.deepEqual(map.get(board2)?.get(4), GRID_B);
|
||||
assert.equal(map.get(board2)?.get(0), undefined);
|
||||
});
|
||||
|
||||
test("unsorted input is handled and trailing deaths are dropped", () => {
|
||||
const board = scoreboard(100, PLAYERS);
|
||||
const map = connectAbilities([
|
||||
death(150, "Alpha", 40), // after the scoreboard — belongs to a match never finished
|
||||
board,
|
||||
death(10, "Hotel", 100, GRID_B),
|
||||
]);
|
||||
const abilities = map.get(board);
|
||||
assert.ok(abilities);
|
||||
assert.deepEqual(abilities.get(7), GRID_B);
|
||||
assert.equal(abilities.size, 1);
|
||||
});
|
||||
73
apps/web-react/app/features/scanner/tests/banner.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* resolveMatchScores is pure — the parse side of banner.ts is covered by
|
||||
* the scoreboard fixture suite, this pins the winner/loser resolution.
|
||||
*/
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
type BannerScoreRead,
|
||||
resolveMatchScores,
|
||||
} from "../core/detectors/scoreboard/banner";
|
||||
import test from "./node-test-compat";
|
||||
|
||||
function read(value: number | null, confidence = 0.9): BannerScoreRead {
|
||||
return {
|
||||
value,
|
||||
confidence,
|
||||
digits: value === null ? 0 : String(value).length,
|
||||
reading: value === null ? "" : String(value),
|
||||
};
|
||||
}
|
||||
|
||||
test("both sides read: higher value is the winner's, either way around", () => {
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({ left: read(71), right: read(88), knockout: false }),
|
||||
[88, 71],
|
||||
);
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({ left: read(88), right: read(71), knockout: false }),
|
||||
[88, 71],
|
||||
);
|
||||
});
|
||||
|
||||
test("a knockout reports the full count over anything read off the burst", () => {
|
||||
// burst letters surviving the floor read as a junk low-confidence value
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({
|
||||
left: read(0, 0.88),
|
||||
right: read(7, 0.62),
|
||||
knockout: true,
|
||||
}),
|
||||
[100, 0],
|
||||
);
|
||||
});
|
||||
|
||||
test("a knockout's loser is the side that read", () => {
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({ left: read(null), right: read(0), knockout: true }),
|
||||
[100, 0],
|
||||
);
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({ left: read(0), right: read(null), knockout: true }),
|
||||
[100, 0],
|
||||
);
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({ left: read(null), right: read(null), knockout: true }),
|
||||
[100, null],
|
||||
);
|
||||
});
|
||||
|
||||
test("one unreadable side without a knockout reports nothing", () => {
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({ left: read(71), right: read(null), knockout: false }),
|
||||
[null, null],
|
||||
);
|
||||
assert.deepEqual(
|
||||
resolveMatchScores({
|
||||
left: read(null),
|
||||
right: read(null),
|
||||
knockout: false,
|
||||
}),
|
||||
[null, null],
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Shard 1/3 of the death suite — see tests/suites/death.ts. */
|
||||
import { runDeathSuite } from "./suites/death";
|
||||
|
||||
await runDeathSuite(0, 3);
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Shard 2/3 of the death suite — see tests/suites/death.ts. */
|
||||
import { runDeathSuite } from "./suites/death";
|
||||
|
||||
await runDeathSuite(1, 3);
|
||||
@@ -0,0 +1,4 @@
|
||||
/** Shard 3/3 of the death suite — see tests/suites/death.ts. */
|
||||
import { runDeathSuite } from "./suites/death";
|
||||
|
||||
await runDeathSuite(2, 3);
|
||||
32
apps/web-react/app/features/scanner/tests/fixtures/death/classic-squiffer-jp/expected.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 2000,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"SSU",
|
||||
"SSU",
|
||||
"SRU",
|
||||
"ISS"
|
||||
],
|
||||
[
|
||||
"BRU",
|
||||
"ISS",
|
||||
"UNKNOWN",
|
||||
"UNKNOWN"
|
||||
],
|
||||
[
|
||||
"SCU",
|
||||
"SS",
|
||||
"SCU",
|
||||
"SSU"
|
||||
]
|
||||
],
|
||||
"name": "ごあんぜんに",
|
||||
"weaponLabel": "Classic Squiffer"
|
||||
},
|
||||
"options": {
|
||||
"notes": "Japanese-language capture (message 'スクイックリンα で / やられた!', weapon on line 1, read via the death-weapon-ja atlas). Streamer webcam bottom-left pixelated for privacy (clear of every parsed ROI). The all-kana tag name needs the dakuten headroom in TAG_NAME_INNER (ご/ぜ) and the harvested に crop (its bar stroke reads as a near-perfect l otherwise)."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/classic-squiffer-jp/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
29
apps/web-react/app/features/scanner/tests/fixtures/death/colorful-splashtag/expected.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 40,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"LDE",
|
||||
"RSU",
|
||||
"ISS",
|
||||
"ISS"
|
||||
],
|
||||
[
|
||||
"SSU",
|
||||
"SSU",
|
||||
"QR",
|
||||
"SS"
|
||||
],
|
||||
[
|
||||
"SJ",
|
||||
"SSU",
|
||||
"QR",
|
||||
"QSJ"
|
||||
]
|
||||
],
|
||||
"name": "Aviators",
|
||||
"weaponLabel": "Splattershot"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/colorful-splashtag/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 4.1 MiB |
32
apps/web-react/app/features/scanner/tests/fixtures/death/e-liter-4k-jp-720p/expected.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 2030,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"QSJ",
|
||||
"SSU",
|
||||
"ISS",
|
||||
"UNKNOWN"
|
||||
],
|
||||
[
|
||||
"ISM",
|
||||
"SSU",
|
||||
"UNKNOWN",
|
||||
"UNKNOWN"
|
||||
],
|
||||
[
|
||||
"ISS",
|
||||
"SRU",
|
||||
"SPU",
|
||||
"SRU"
|
||||
]
|
||||
],
|
||||
"name": "JINGIのヒト",
|
||||
"weaponLabel": "E-liter 4K"
|
||||
},
|
||||
"options": {
|
||||
"notes": "720p VoD capture (upscaled to canonical at ingest), JP death message 'リッター4K で / やられた!'. The read garbles below both the plain-snap threshold and the lattice margin ('4K' is unreadable); the weapon resolves only via burst-icon corroboration — icon top-1 (E-liter 4K) agrees with the plain text ranking's top. Streamer webcam bottom-left pixelated for privacy (region sits left of every parsed ROI)."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/e-liter-4k-jp-720p/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
29
apps/web-react/app/features/scanner/tests/fixtures/death/fxg-supaatan-slosher/expected.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 3000,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"CB",
|
||||
"SS",
|
||||
"SSU",
|
||||
"ISS"
|
||||
],
|
||||
[
|
||||
"QR",
|
||||
"QR",
|
||||
"QR",
|
||||
"QSJ"
|
||||
],
|
||||
[
|
||||
"QR",
|
||||
"QR",
|
||||
"RES",
|
||||
"SRU"
|
||||
]
|
||||
],
|
||||
"name": "FxG スパータン",
|
||||
"weaponLabel": "Slosher"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/fxg-supaatan-slosher/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
29
apps/web-react/app/features/scanner/tests/fixtures/death/german-booyah-bomb/expected.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 6,
|
||||
"weaponType": "SPECIAL",
|
||||
"abilities": [
|
||||
[
|
||||
"CB",
|
||||
"ISM",
|
||||
"ISM",
|
||||
"SS"
|
||||
],
|
||||
[
|
||||
"SSU",
|
||||
"SCU",
|
||||
"SCU",
|
||||
"QSJ"
|
||||
],
|
||||
[
|
||||
"SJ",
|
||||
"SRU",
|
||||
"QSJ",
|
||||
"RES"
|
||||
]
|
||||
],
|
||||
"name": "aruka",
|
||||
"weaponLabel": "Booyah Bomb"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/german-booyah-bomb/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
29
apps/web-react/app/features/scanner/tests/fixtures/death/headphones/expected.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 10,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"CB",
|
||||
"BRU",
|
||||
"ISS",
|
||||
"ISS"
|
||||
],
|
||||
[
|
||||
"ISS",
|
||||
"BRU",
|
||||
"QSJ",
|
||||
"BRU"
|
||||
],
|
||||
[
|
||||
"SJ",
|
||||
"SSU",
|
||||
"QSJ",
|
||||
"SRU"
|
||||
]
|
||||
],
|
||||
"name": "Headphones",
|
||||
"weaponLabel": "Splattershot Jr."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/headphones/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
29
apps/web-react/app/features/scanner/tests/fixtures/death/pink-text-special/expected.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 18,
|
||||
"weaponType": "SPECIAL",
|
||||
"abilities": [
|
||||
[
|
||||
"RSU",
|
||||
"SPU",
|
||||
"SPU",
|
||||
"RSU"
|
||||
],
|
||||
[
|
||||
"RSU",
|
||||
"RSU",
|
||||
"SSU",
|
||||
"RES"
|
||||
],
|
||||
[
|
||||
"SJ",
|
||||
"SSU",
|
||||
"SSU",
|
||||
"QSJ"
|
||||
]
|
||||
],
|
||||
"name": "Jrod_14",
|
||||
"weaponLabel": "Triple Splashdown"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/pink-text-special/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 241,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"CB",
|
||||
"QR",
|
||||
"BRU",
|
||||
"QR"
|
||||
],
|
||||
[
|
||||
"QR",
|
||||
"QR",
|
||||
"QR",
|
||||
"QR"
|
||||
],
|
||||
[
|
||||
"SJ",
|
||||
"RES",
|
||||
"ISM",
|
||||
"ISM"
|
||||
]
|
||||
],
|
||||
"name": "heavy kez",
|
||||
"weaponLabel": "Rapid Blaster Deco"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/rapid-blaster-deco-heavy-kez/frame.jpg
vendored
Normal file
|
After Width: | Height: | Size: 311 KiB |
29
apps/web-react/app/features/scanner/tests/fixtures/death/splattershot-tezuka/expected.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 40,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"CB",
|
||||
"SCU",
|
||||
"SCU",
|
||||
"SS"
|
||||
],
|
||||
[
|
||||
"SSU",
|
||||
"IA",
|
||||
"IA",
|
||||
"RES"
|
||||
],
|
||||
[
|
||||
"SJ",
|
||||
"RES",
|
||||
"SRU",
|
||||
"QSJ"
|
||||
]
|
||||
],
|
||||
"name": "てづか",
|
||||
"weaponLabel": "Splattershot"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/splattershot-tezuka/frame.jpg
vendored
Normal file
|
After Width: | Height: | Size: 323 KiB |
30
apps/web-react/app/features/scanner/tests/fixtures/death/ultra-stamp-jp-720p/expected.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 11,
|
||||
"weaponType": "SPECIAL",
|
||||
"abilities": [
|
||||
[
|
||||
"IRU",
|
||||
"QR",
|
||||
"SPU"
|
||||
],
|
||||
[
|
||||
"AD",
|
||||
"SPU",
|
||||
"RES",
|
||||
"UNKNOWN"
|
||||
],
|
||||
[
|
||||
"SPU",
|
||||
"SS",
|
||||
"QSJ"
|
||||
]
|
||||
],
|
||||
"name": "gori_1",
|
||||
"weaponLabel": "Ultra Stamp"
|
||||
},
|
||||
"options": {
|
||||
"notes": "720p VoD capture (upscaled to canonical at ingest), JP death message 'ウルトラハンコ で / やられた!'. The garbled low-fidelity read only resolves through the candidate-lattice re-rank (weapon is a SPECIAL, so the burst-icon paths can't help). The name band is a two-hue split banner (yellow/green diagonal), read via the split-background candidate in the tag parse. Streamer webcam bottom-left pixelated for privacy (region sits left of every parsed ROI)."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/ultra-stamp-jp-720p/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 927 KiB |
32
apps/web-react/app/features/scanner/tests/fixtures/death/wipeout-52-gal/expected.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 50,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"OG",
|
||||
"SSU",
|
||||
"SSU",
|
||||
"QSJ"
|
||||
],
|
||||
[
|
||||
"SSU",
|
||||
"IA",
|
||||
"IA",
|
||||
"IA"
|
||||
],
|
||||
[
|
||||
"SJ",
|
||||
"SSU",
|
||||
"RES",
|
||||
"SRU"
|
||||
]
|
||||
],
|
||||
"name": "さんだいめドパかげ",
|
||||
"weaponLabel": ".52 Gal"
|
||||
},
|
||||
"options": {
|
||||
"notes": "WIPEOUT banner covers the weapon name line; the weapon resolves via the burst icon fallback. The kana name needs the dakuten headroom in TAG_NAME_INNER (げ), the widened merge gap cap (い's strokes sit 16px apart), and the harvested crops."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/wipeout-52-gal/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
32
apps/web-react/app/features/scanner/tests/fixtures/death/z-f-splatterscope-vod/expected.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"event": "Death",
|
||||
"data": {
|
||||
"weaponId": 2021,
|
||||
"weaponType": "MAIN",
|
||||
"abilities": [
|
||||
[
|
||||
"SSU",
|
||||
"RSU",
|
||||
"RSU",
|
||||
"SSU"
|
||||
],
|
||||
[
|
||||
"RP",
|
||||
"SS",
|
||||
"SS",
|
||||
"RSU"
|
||||
],
|
||||
[
|
||||
"OS",
|
||||
"ISM",
|
||||
"ISM",
|
||||
"ISM"
|
||||
]
|
||||
],
|
||||
"name": "おもちっち",
|
||||
"weaponLabel": "Z+F Splatterscope"
|
||||
},
|
||||
"options": {
|
||||
"notes": "Compressed VoD capture. The Respawn Punisher main (middle row) is gray-dark at the circle center, which the old grayscale gate icon probe failed on — the probe reads per-pixel max RGB channel now."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/death/z-f-splatterscope-vod/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"event": "MapStart",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"stage": 21,
|
||||
"stageLabel": "Robo ROM-en"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
8
apps/web-react/app/features/scanner/tests/fixtures/map-start/splat-zones-mahi/expected.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"event": "MapStart",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"stage": 7,
|
||||
"stageLabel": "Mahi-Mahi Resort"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/map-start/splat-zones-mahi/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"event": "MapStart",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"stage": 3,
|
||||
"stageLabel": "Undertow Spillway"
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/map-start/splat-zones-undertow-spillway/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"stage": 15,
|
||||
"stageLabel": "Um'ami Ruins",
|
||||
"spectator": false,
|
||||
"teammates": [
|
||||
{
|
||||
"slot": "up",
|
||||
"name": "Bread-Chan",
|
||||
"abilities": ["LDE", "SCU", "SPU"],
|
||||
"weaponLabel": "Clawz .96 Gal",
|
||||
"weaponId": 82,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "left",
|
||||
"name": "amo",
|
||||
"abilities": ["LDE", "RP", "OS"],
|
||||
"weaponLabel": "E-liter 4K",
|
||||
"weaponId": 2030,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "right",
|
||||
"name": "mr 2",
|
||||
"abilities": ["CB", "SSU", "SJ"],
|
||||
"weaponLabel": "Splatana Stamper",
|
||||
"weaponId": 8000,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "self",
|
||||
"name": "Sendou",
|
||||
"abilities": ["CB", "QR", "SJ"],
|
||||
"weaponLabel": "Custom Blaster",
|
||||
"weaponId": 211,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"abilities": ["CB", "QR", "SJ"],
|
||||
"weaponLabel": "Custom Blaster",
|
||||
"weaponId": 211,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"abilities": ["LDE", "RP", "OS"],
|
||||
"weaponLabel": "E-liter 4K",
|
||||
"weaponId": 2030,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"abilities": ["LDE", "SCU", "SJ"],
|
||||
"weaponLabel": "N-ZAP '85",
|
||||
"weaponId": 60,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"abilities": ["OG", "SSU", "SJ"],
|
||||
"weaponLabel": ".52 Gal",
|
||||
"weaponId": 50,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": ["teammates.3.weapon"],
|
||||
"notes": "Sendou POV, Um'ami Ruins SZ at ~4:55 (t=18 in the 2026-08-11 VoD) — fully rendered POV map screen, everyone alive on both teams. The scan's read off this moment claimed mr 2 (right card) and enemy rows 1–2 splatted, amo holding special, and Sendou on Octobrush — four phantoms in one read, seconds into the match. Teammate abilities transcribed from adjacent clean reads (t=12/t=40/t=42.5) that agree with the card icons. The phantom splats (bright ink bled through the translucent surfaces) are fenced by the cross probe's crispness gate, the phantom special by the camo probe's corner-saturation cap, and the bright-bleed surfaces get a dual template-set match with the camo floor. Sendou's own weapon stays skipped: the self-card render is soft and half-covered by the yellow freshness tag badge, and the blaster family trails Octobrush by 0.006 NCC — no robust margin to assert."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.3 MiB |
113
apps/web-react/app/features/scanner/tests/fixtures/minimap/snix-special-ready/expected.json
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"stage": 3,
|
||||
"teammates": [
|
||||
{
|
||||
"slot": "up",
|
||||
"name": "こむぎこをこねたユウ",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"ISS",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Splattershot",
|
||||
"weaponId": 40,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "left",
|
||||
"name": "Snix",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Splatana Stamper",
|
||||
"weaponId": 8000,
|
||||
"dead": false,
|
||||
"specialReady": true
|
||||
},
|
||||
{
|
||||
"slot": "right",
|
||||
"name": "スライムLv.13",
|
||||
"abilities": [
|
||||
"ISS",
|
||||
"ISS",
|
||||
"ISS"
|
||||
],
|
||||
"weaponLabel": "Splattershot Jr.",
|
||||
"weaponId": 10,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "self",
|
||||
"name": "Sendou",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"QR",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Custom Blaster",
|
||||
"weaponId": 211,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"abilities": [
|
||||
"CB",
|
||||
"QR",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Inkbrush Nouveau",
|
||||
"weaponId": 1101,
|
||||
"dead": false,
|
||||
"specialReady": true
|
||||
},
|
||||
{
|
||||
"abilities": [
|
||||
"OG",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": ".52 Gal",
|
||||
"weaponId": 50,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"abilities": [
|
||||
"ISM",
|
||||
"SSU",
|
||||
"SCU"
|
||||
],
|
||||
"weaponLabel": "Big Swig Roller",
|
||||
"weaponId": 1040,
|
||||
"dead": false,
|
||||
"specialReady": true
|
||||
},
|
||||
{
|
||||
"abilities": [
|
||||
"SSU",
|
||||
"NS",
|
||||
"SSU"
|
||||
],
|
||||
"weaponLabel": "Luna Blaster Neo",
|
||||
"weaponId": 201,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"stageLabel": "Undertow Spillway"
|
||||
},
|
||||
"options": {
|
||||
"skipFields": [
|
||||
"teammates.0.name"
|
||||
],
|
||||
"notes": "Tower Control frame with three special-ready camo surfaces: the left card (Splatana Stamper — dark art whose padded-square icon renders bigger than the 54px weapon box, unmatchable before the art-cropped templates) and enemy rows 0 (Inkbrush Nouveau) and 2 (Big Swig Roller). Self card is a Custom Blaster on a warm translucent background (kit tiles: Point Sensor + Triple Splashdown). teammates.0.name skipped: the up card reads こむきこをこねたユウ — the ぎ dakuten is below atlas resolution at 29px. Control values are the deterministic estimator output."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/minimap/snix-special-ready/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
118
apps/web-react/app/features/scanner/tests/fixtures/minimap/spectator-area-cup-round1/expected.json
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"stage": 14,
|
||||
"spectator": true,
|
||||
"teammates": [
|
||||
{
|
||||
"slot": "up",
|
||||
"name": "リビア",
|
||||
"abilities": [
|
||||
"SSU",
|
||||
"SCU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Splash-o-matic",
|
||||
"weaponId": 20,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "right",
|
||||
"name": "がらっこ",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"SPU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Dread Wringer",
|
||||
"weaponId": 3050,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "down",
|
||||
"name": "ーロたべる",
|
||||
"abilities": [
|
||||
"OG",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Custom Blaster",
|
||||
"weaponId": 211,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "left",
|
||||
"name": "ももドラグーン",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"SCU",
|
||||
"ISM"
|
||||
],
|
||||
"weaponLabel": "Snipewriter 5H",
|
||||
"weaponId": 2070,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"name": "ウルボー",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"SPU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Snipewriter 5H",
|
||||
"weaponId": 2070,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "ねこすき",
|
||||
"abilities": [
|
||||
"OG",
|
||||
"QR",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "S-BLAST '91",
|
||||
"weaponId": 261,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "そっぴ",
|
||||
"abilities": [
|
||||
"RSU",
|
||||
"RSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Zink Mini Splatling",
|
||||
"weaponId": 4001,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "しゅーた",
|
||||
"abilities": [
|
||||
"OG",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": ".52 Gal",
|
||||
"weaponId": 50,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"stageLabel": "Manta Maria"
|
||||
},
|
||||
"options": {
|
||||
"skipFields": [
|
||||
"enemies.0.name"
|
||||
],
|
||||
"notes": "8-player spectator map screen from the AREA CUP stream (Round 1), pre-match — teams at spawn, so the control estimate sits near zero (spawn markers only). Formerly negative/map-screen-2; relabeled when the spectator variant became gated off the X jump-button disc. enemies.0.name skipped: ウルボー reads ウルホー — the ボ dakuten is below atlas resolution at this size (same limit as snix-special-ready's ぎ). teammates.2.name ーロたべる is the pixel reading; if the real name uses kanji (一口たべる) it is unrecoverable — 一/ー and 口/ロ are pixel-identical and the atlas carries no kanji."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/minimap/spectator-area-cup-round1/frame.jpg
vendored
Normal file
|
After Width: | Height: | Size: 228 KiB |
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"stage": 4,
|
||||
"spectator": true,
|
||||
"teammates": [
|
||||
{
|
||||
"slot": "up",
|
||||
"name": "ももドラグーン",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"SCU",
|
||||
"ISM"
|
||||
],
|
||||
"weaponLabel": "Snipewriter 5H",
|
||||
"weaponId": 2070,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "right",
|
||||
"name": "れいまるがんばれや!",
|
||||
"abilities": [
|
||||
"SCU",
|
||||
"SSU",
|
||||
"SSU"
|
||||
],
|
||||
"weaponLabel": "Splash-o-matic",
|
||||
"weaponId": 20,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "down",
|
||||
"name": "ひまじん",
|
||||
"abilities": [
|
||||
"OG",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Custom Blaster",
|
||||
"weaponId": 211,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "left",
|
||||
"name": "!のりしお!",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Splatana Stamper",
|
||||
"weaponId": 8000,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"name": "とど",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"H",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Mint Decavitator",
|
||||
"weaponId": 8020,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "たなは",
|
||||
"abilities": [
|
||||
"RSU",
|
||||
"SSU",
|
||||
"SCU"
|
||||
],
|
||||
"weaponLabel": "Splat Brella",
|
||||
"weaponId": 6000,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "いなてんきんぐ",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"RSU",
|
||||
"RSU"
|
||||
],
|
||||
"weaponLabel": "Zink Mini Splatling",
|
||||
"weaponId": 4001,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "そふ",
|
||||
"abilities": [
|
||||
"RSU",
|
||||
"RSU",
|
||||
"RES"
|
||||
],
|
||||
"weaponLabel": "Heavy Splatling",
|
||||
"weaponId": 4010,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"stageLabel": "Mincemeat Metalworks"
|
||||
},
|
||||
"options": {
|
||||
"notes": "8-player spectator map screen (AREA CUP, Winners Quarterfinals), the frame that motivated the spectator gate: the stream chrome covers both overlay gate anchors (close disc behind the branding top-left, spawn-pill area behind the bottom name bar). ももドラグーン also appears in spectator-area-cup-round1 with the identical loadout — a cross-fixture consistency anchor. れいまるがんばれや! ties 29px vs 30px name reads at equal confidence; the 29px read (correct がんばれ) wins as first-tried."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/minimap/spectator-area-cup-winners-qf/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
118
apps/web-react/app/features/scanner/tests/fixtures/minimap/spectator-pxg/expected.json
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"stage": 18,
|
||||
"spectator": true,
|
||||
"teammates": [
|
||||
{
|
||||
"slot": "up",
|
||||
"name": "kyutpie<3",
|
||||
"abilities": [
|
||||
"SSU",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": ".96 Gal",
|
||||
"weaponId": 80,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "right",
|
||||
"name": "Grey",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"SCU",
|
||||
"ISM"
|
||||
],
|
||||
"weaponLabel": "Flingza Roller",
|
||||
"weaponId": 1030,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "down",
|
||||
"name": "JORDANYAGI",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"QR",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Order Slosher Replica",
|
||||
"weaponId": 3005,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "left",
|
||||
"name": "f(x)= x²-8",
|
||||
"abilities": [
|
||||
"OG",
|
||||
"SCU",
|
||||
"ISM"
|
||||
],
|
||||
"weaponLabel": "Rapid Blaster Pro WNT-R",
|
||||
"weaponId": 252,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"name": "Nala fan",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "S-BLAST '91",
|
||||
"weaponId": 261,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "バッラスト",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"SPU",
|
||||
"ISM"
|
||||
],
|
||||
"weaponLabel": "Snipewriter 5H",
|
||||
"weaponId": 2070,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "Jared",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"QR",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Twinklez Splat Dualies",
|
||||
"weaponId": 5012,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "Reiyu C:3",
|
||||
"abilities": [
|
||||
"SSU",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Splash-o-matic",
|
||||
"weaponId": 20,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"stageLabel": "Crableg Capital"
|
||||
},
|
||||
"options": {
|
||||
"skipFields": [
|
||||
"teammates.3.name"
|
||||
],
|
||||
"notes": "8-player spectator map screen from a different stream layout (PXG score bug top-left) — attests the card grid and the X jump-button disc land on the same ROIs across layouts. Latin names anchor the 29-30px glyph-height calibration. teammates.3.name skipped: the superscript two in f(x)= x²-8 is not a charset glyph (reads ´ or ·)."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/minimap/spectator-pxg/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
118
apps/web-react/app/features/scanner/tests/fixtures/minimap/spectator-sws26-swiss/expected.json
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"stage": 1,
|
||||
"spectator": true,
|
||||
"teammates": [
|
||||
{
|
||||
"slot": "up",
|
||||
"name": "King Inate",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"RSU",
|
||||
"RSU"
|
||||
],
|
||||
"weaponLabel": "Zink Mini Splatling",
|
||||
"weaponId": 4001,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "right",
|
||||
"name": "todo",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Mint Decavitator",
|
||||
"weaponId": 8020,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "down",
|
||||
"name": "tanaha",
|
||||
"abilities": [
|
||||
"RSU",
|
||||
"SSU",
|
||||
"SCU"
|
||||
],
|
||||
"weaponLabel": "Splat Brella",
|
||||
"weaponId": 6000,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "left",
|
||||
"name": "soph",
|
||||
"abilities": [
|
||||
"RSU",
|
||||
"RSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Heavy Splatling",
|
||||
"weaponId": 4010,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"name": "Burstie",
|
||||
"abilities": [
|
||||
"SSU",
|
||||
"SSU",
|
||||
"SSU"
|
||||
],
|
||||
"weaponLabel": ".96 Gal",
|
||||
"weaponId": 80,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "[K]yo!",
|
||||
"abilities": [
|
||||
"CB",
|
||||
"QR",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Slosher",
|
||||
"weaponId": 3000,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "leafi !!",
|
||||
"abilities": [
|
||||
"OG",
|
||||
"SSU",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Custom Blaster",
|
||||
"weaponId": 211,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"name": "biscuit",
|
||||
"abilities": [
|
||||
"LDE",
|
||||
"SCU",
|
||||
"ISM"
|
||||
],
|
||||
"weaponLabel": "Snipewriter 5H",
|
||||
"weaponId": 2070,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"stageLabel": "Eeltail Alley"
|
||||
},
|
||||
"options": {
|
||||
"skipFields": [
|
||||
"teammates.0.name"
|
||||
],
|
||||
"notes": "8-player spectator map screen from the SWS26 cast, same game as the objective/ splat-zones-cast-* fixtures: green team left column, purple right — the cross-detector anchor pair for objective team-color tracking. teammates.0.name skipped: King Inate reads King lnate — capital I and lowercase l are pixel-identical in BlitzMain at this size (the POV nameplate in the sibling frame confirms Inate)."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/minimap/spectator-sws26-swiss/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
98
apps/web-react/app/features/scanner/tests/fixtures/minimap/stonks-cross-both-teams/expected.json
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"event": "Minimap",
|
||||
"data": {
|
||||
"stage": 6,
|
||||
"teammates": [
|
||||
{
|
||||
"slot": "up",
|
||||
"name": "STONKS",
|
||||
"abilities": [
|
||||
"IRU",
|
||||
"RP",
|
||||
"SJ"
|
||||
],
|
||||
"weaponLabel": "Tri-Stringer",
|
||||
"weaponId": 7010,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "left",
|
||||
"name": "Player",
|
||||
"abilities": [
|
||||
"IRU",
|
||||
"QR",
|
||||
"SS"
|
||||
],
|
||||
"weaponLabel": "Splattershot Jr.",
|
||||
"weaponId": 10,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "right",
|
||||
"name": null,
|
||||
"abilities": [],
|
||||
"weaponLabel": null,
|
||||
"weaponId": null,
|
||||
"dead": true,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"slot": "self",
|
||||
"name": "Edd",
|
||||
"abilities": [
|
||||
"IRU",
|
||||
"QR",
|
||||
"SS"
|
||||
],
|
||||
"weaponLabel": "Ballpoint Splatling",
|
||||
"weaponId": 4030,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"enemies": [
|
||||
{
|
||||
"abilities": [
|
||||
"BRU",
|
||||
"SS",
|
||||
"IRU"
|
||||
],
|
||||
"weaponLabel": "Slosher",
|
||||
"weaponId": 3000,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"abilities": [],
|
||||
"weaponLabel": "Splat Roller",
|
||||
"weaponId": 1010,
|
||||
"dead": true,
|
||||
"specialReady": false
|
||||
},
|
||||
{
|
||||
"abilities": [],
|
||||
"weaponLabel": "Dualie Squelchers",
|
||||
"weaponId": 5030,
|
||||
"dead": true,
|
||||
"specialReady": true
|
||||
},
|
||||
{
|
||||
"abilities": [
|
||||
"IRU",
|
||||
"QR",
|
||||
"SS"
|
||||
],
|
||||
"weaponLabel": "Splattershot",
|
||||
"weaponId": 40,
|
||||
"dead": false,
|
||||
"specialReady": false
|
||||
}
|
||||
],
|
||||
"stageLabel": "Museum d'Alfonsino"
|
||||
},
|
||||
"options": {
|
||||
"notes": "One respawning card per team plus a struck-out special-ready (camo) enemy row. Enemy row 1's label was corrected from Flingza Roller to Splat Roller when the art-cropped templates landed (white roll, blue L-frame, yellow-striped grip — matches the 1010 icon exactly). Enemy row 2 is both struck out and special-ready: the red dualie art reads through the X, but the Custom-vs-plain Dualie Squelchers tag is hidden behind a stroke, so the icon matcher near-ties the twins (5031 0.600 vs 5030 0.597); the row's sub tile (Splat Bomb silhouette, readable despite the stroke clipping it) breaks the tie to plain Dualie Squelchers (subResolved). Control values are the deterministic estimator output (share of visible map surface, not in-game turf %)."
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/minimap/stonks-cross-both-teams/frame.jpg
vendored
Normal file
|
After Width: | Height: | Size: 581 KiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 277,
|
||||
"score": [97, 100],
|
||||
"penalty": [null, null],
|
||||
"control": [true, false]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Alpha (bright yellow-green ink) in control: their plate keeps the bright team-color fill (POV frame shows the We're in control! banner). Bravo still at the three-digit 100."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1002 KiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 157,
|
||||
"score": [2, 15],
|
||||
"penalty": [64, 65],
|
||||
"control": [true, false]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Penalty pills under both plates at once; alpha (bright green fill) in control."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 950 KiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 209,
|
||||
"score": [2, 84],
|
||||
"penalty": [75, null],
|
||||
"control": [false, true]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Single-digit count with a large penalty on alpha; bravo (purple) in control with the team-color fill."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 299,
|
||||
"score": [100, 100],
|
||||
"penalty": [null, null],
|
||||
"control": [false, false]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Match start: zone neutral, both plates near-black with three-digit 100s (green/pink digits)."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 262,
|
||||
"score": [50, 100],
|
||||
"penalty": [null, null],
|
||||
"control": [true, false]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Casted stream (Splat World Series 2026 playoffs) mid-fight: lime team in control, white '50' on the bright lime fill blurred by stream compression — the leading '5' scored under the main digit floor and the read truncated to 0 before the extension floor existed."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.8 MiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 249,
|
||||
"score": [100, 29],
|
||||
"penalty": [null, null],
|
||||
"control": [false, true]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same cast game seconds later after a camera swap put the lime team's plate on the right: white '29' on the bright lime fill, leading '2' blur-eroded under the main digit floor (was truncated to 9). Gray-band noise on this frame matches digit templates at up to 0.66 — the anchor-digit rule must keep rejecting it."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.7 MiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 243,
|
||||
"score": [49, 98],
|
||||
"penalty": [39, null],
|
||||
"control": [false, true]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Casted stream (SWS26) overhead camera of the same game as the specced-purple-left fixture: default arrangement puts green left (black plate, green digits, +39 penalty pill) and purple right (in control, team-color fill)."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 MiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 248,
|
||||
"score": [100, 49],
|
||||
"penalty": [null, null],
|
||||
"control": [false, false]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Casted stream (SWS26) from a purple-team player's POV: the specced team's plate sits left, so purple (magenta digits) is side 0 and green side 1 — opposite arrangement to the overhead frame from the same game. Zone neutral: both plates near-black."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 MiB |
10
apps/web-react/app/features/scanner/tests/fixtures/objective/splat-zones-mako-mart/expected.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"event": "Objective",
|
||||
"data": {
|
||||
"mode": "SZ",
|
||||
"time": 215,
|
||||
"score": [95, 53],
|
||||
"penalty": [4, null],
|
||||
"control": [false, true]
|
||||
}
|
||||
}
|
||||
BIN
apps/web-react/app/features/scanner/tests/fixtures/objective/splat-zones-mako-mart/frame.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 258,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "JP broadcast (AREA CUP) spectator HUD with the camera-button badges hidden — the strip keeps the cast icon geometry, so the layout must resolve to cast via the geometry score, not the badge probe (reading it as POV drifts the outer-slot boxes off their icons and flickers phantom deaths). All eight players alive."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.1 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 255,
|
||||
"special": [
|
||||
[true, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[true, false, true, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "AREA CUP FINAL (grey-win VoD, Flounder Heights) spectator HUD at 4:15. Right team's outermost slot is a plain alive purple body tilted mid-bounce — the live scan read it as splatted (✕·✕✕ @ 0.749), manufacturing a third death for the .52 Gal whose real second death only starts three seconds later; one second after this frame the same body washes into special ready. Right slots 1 and 3 are genuinely splatted. Left team's outermost slot carries the bright special-ready wash."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 250,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[true, false, false, true]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same footage one second on (4:10). Right slot 3 has respawned (gauge back at 15) while the outermost slot's X is still up over the dark hull; the live scan read only slot 1 dead (✕··· @ 0.814), missing the outer X for a second consecutive read."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 251,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[true, false, true, true]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same footage at 4:11, gauge-percentage spectator HUD. Right team slots 1, 3 and 4 are all splatted; the outermost X sits over a dark navy sky/structure backdrop and carries no gauge number, and the live scan missed it (✕·✕· @ 0.924), splitting the .52 Gal's single 7-second death into two runs. Left team all alive (window glare streaks the backdrop between the first icons)."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.6 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 253,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, true]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[true, false, true, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same footage at 4:13, spectator HUD with special-gauge percentages above the icons and camera-button/D-pad badges below (85/72/49 left, 11/17/26 right; a splatted icon keeps its number). Right team's outermost slot is fully washed with special ready and shows no gauge number — the live scan never captured this ★ (the .52 Gal's brief special between its two deaths, user-confirmed) and neighboring reads called the slot dead instead. Left outer slot's wash is gone by this frame (special spent), despite its missing gauge number."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.4 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast-mirror",
|
||||
"time": 124,
|
||||
"special": [
|
||||
[false, false, false, true],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[false, false, true, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same footage one second on (2:06:32, clock 2:04), player-cam view with gauge digits and camera badges — but in the MIRRORED cast arrangement (left '+' pads at [625,701,777,852], right A/B/Y/X at [1110,1207,1303,1401]; the standard badge probes miss them). The live scan read this frame as pov and emitted ·★·★: the ★ on left slot 1 is FALSE — the .52 Gal just respawned and shows gauge 29 — which is the user-reported dead/ready flicker. Left slot 3's wash IS real (pale cream body, no gauge digit; spent by the next second). Right slot 2 splatted (27 kept over the X). Labels authored by Claude from pixel evidence; the user-confirmed part is that left slot 1 held no special here."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast-mirror",
|
||||
"time": 123,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[false, false, true, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same footage at 2:06:33 (clock 2:03), mirrored player-cam view: left team all alive with gauges 47/31/3 — slot 3's wash from one second earlier is gone (special spent), slot 1 (.52 Gal) alive at 31. Right slot 2 still splatted. Guards the mirror arrangement against false ★/✕ on a clean frame. Labels authored by Claude from pixel evidence."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.3 MiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast-mirror",
|
||||
"time": 125,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, true, false, false],
|
||||
[false, false, true, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"skipFields": ["layout"],
|
||||
"notes": "AREA CUP FINAL (grey-win VoD, Flounder Heights) overhead map view at 2:06:31, same game as the gauge-overlay fixtures but after the specced POV switched teams: the badge-less strip is drawn in the MIRRORED cast arrangement (narrow ~76 pitch left, wide ~97 pitch right — the mirror's right column nearly coincides with POV's). The live scan classified this stretch as pov, which happened to read this frame right but flipped false ★s one second later. Left slot 1 (the yellow team's .52 Gal) is genuinely splatted here — its real death ends with a respawn by the next second — and right slot 2 is splatted too; everyone else alive. layout is skipped: a lone badge-less mirror frame is not distinguishable from POV by decisiveness (that ambiguity is exactly the misread this fixture guards), so a fresh parse reads it as pov with identical states; live scans resolve the arrangement from neighboring badge frames via stickiness. Labels authored by Claude from pixel evidence."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 174,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, true, false],
|
||||
[true, false, false, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "AREA CUP FINAL (grey-win VoD) spectator overhead map view: badge-less, digit-less strip whose left column sits a nudge right of the cast centers. The left team's yellow リード! lead banner clips the first slot's shoulder probe and the saturated yellow ink lights the glow class — previously a false special on an alive, ink-heavy body. One dead per side (left slot 3 crossed out, right slot 1 crossed out). Note the read side order: the overhead view keeps yellow on the left while the player-cam frames of the same footage put the specced purple team there."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.9 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 161,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, true],
|
||||
[false, false, false, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same AREA CUP overhead map view (grey-win VoD), 13s on. The first left slot's probes hang past the E-liter icon's edge onto pale sunlit buildings — bright AND unsaturated, so both the shoulder glow and the body pale class fired on an alive icon whose own body still reads ink-heavy; user-confirmed no special held here. Left slot 4 is splatted; everyone else alive."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 152,
|
||||
"special": [
|
||||
[true, false, false, false],
|
||||
[false, false, true, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, true, true],
|
||||
[true, false, false, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Actually dead[1][2] should be true but it is not in proper view so accepted."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.1 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 92,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, true, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, true, false],
|
||||
[true, false, false, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "AREA CUP FINAL (grey-win VoD) overhead map view at 1:32 — badge-less, digit-less strip with the リード! lead banner top-left and roster panels at the sides. The live scan classified this stretch as pov geometry (··✕· vs ✕··✕ (pov)), misaligning the slots: right slot 3's white special-ready wash went unread and the alive outermost slot got a false splat. Reads flip-flop pov/cast through this segment."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 256,
|
||||
"special": [
|
||||
[true, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[true, false, true, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Same badge-less AREA CUP spectator HUD two seconds on. Left team's outermost slot holds special caught at the dim trough of the ready wash's pulse — pale but below the glow floor, previously misread as splatted. Right team's first and third slots are splatted (grey X'd icons)."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 248,
|
||||
"special": [
|
||||
[false, false, true, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, true]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "AREA CUP spectator HUD with the camera-button badges visible. Right team's outermost slot is splatted with the arena behind it covered in that team's ink color — the ink bleeding around the translucent crossed-out icon previously read as an alive body. Left team's third slot holds special (pale wash behind the icon)."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.6 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 214,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, true, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, true],
|
||||
[true, false, false, true]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "Casted stream (SWS26) spectator HUD: bigger icons with camera-button badges below and special gauge percentages above (52/56/54/66 left, 72/27 right — exact counts not parsed yet; a dead icon can keep its number on screen). Left team's slot nearest the timer and the right team's outer two slots are splatted (grey X'd icons); the right team's third slot glows white with special ready."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.0 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 296,
|
||||
"special": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, false, false, false],
|
||||
[false, false, false, false]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "sendou-triton VoD (Hagglefish Market, match 1) spectator HUD at 4:56 — four seconds into the match, so no special can be charged, yet the live scan read left slot0 as special ready (★··· @ 0.971, cast layout). The slot's body is largely covered by the big white Splattershot Jr. silhouette, which fakes the ready wash's pale/glow classes; the bright sky backdrop pushes the layout scores into a near-tie (pov 0.278 / cast 0.273), so a history-less read flips to pov outright and stars five slots. All eight players are alive with saturated team-ink bodies."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"event": "PlayerStatus",
|
||||
"data": {
|
||||
"layout": "cast",
|
||||
"time": 241,
|
||||
"special": [
|
||||
[false, false, false, true],
|
||||
[false, false, false, false]
|
||||
],
|
||||
"dead": [
|
||||
[false, true, false, false],
|
||||
[false, false, false, true]
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"notes": "sendou-triton VoD (Hagglefish Market, match 12) spectator HUD at 4:01 (t=4822). The surrounding reads use cast geometry, but the live scan flipped this frame to pov (·✕·★ vs ··★★): the left column survives the flip (its pov centers nearly coincide), while the right column misreads — a false ★ on alive slot2 and slot3's grey crossed-out splat (still crossed a second later at 4:00) read as special ready, ending a real death span early. Left slot3 carries a genuine pale ready wash; right slot1 is a round octoling icon in saturated ink, alive."
|
||||
}
|
||||
}
|
||||