Skip to content

Scanner · Modules

3 modules · 29 exports · 0 repositories · 14 e2e tests · 1 route

Who imports who · hover a node
Depends onScanner modulesUsed byCoachEvents18 exportsCoachFilters6 exportsCoachPlayback5 exportsComponents4 filesOther1 file

CoachEvents

app/features/scanner/core/CoachEvents.ts

Coach events: the moments of a scanned match worth reviewing, always from the POV player's team's side (an enemy push is the POV team's PUSH_DEFENSE, an enemy death streak is nothing). A cast has no POV player: its events are told from the side of the team picked to coach, and the types that follow the POV player (followsPovPlayer) are left out, as the camera and its kill feed hop between players. Config driven: DEFINITIONS names each event type and the rule it is detected by; the interpreter below analyzes the match once and runs every definition whose modes apply. A new type is a new row, a new kind of moment a new rule kind. Definitions of one rule differing only in how much it takes are its tiers, later ones stricter (STAGGER_20 over STAGGER_15): a moment shows under every tier it reaches.

Every event is a window into the footage (start..end, seconds into the video/stream) cut to show the lead-up: an objective taken counts from the first splat on the losing side within KILL_LEAD_WINDOW_S before it, less LEAD_BUFFER_S.

Imports
no other modules
Used by
1 module2 components1 other

Types

type

CoachEventType

no JSDoc:224
export type CoachEventType = (typeof DEFINITIONS)[number]["type"];
type

CoachEventCategory

no JSDoc:226
export type CoachEventCategory = (typeof DEFINITIONS)[number]["category"];
interface

CoachEvent

no JSDoc:317
export interface CoachEvent {
	type: CoachEventType;
	/** seconds into the video/stream the window starts at, lead-up included */
	start: number;
	/** seconds into the video/stream the window ends at */
	end: number;
	/** the match timer at `start`; null when the clock was never read */
	time: number | null;
}
interface

CastPick

:396

A team picked to coach a cast from, in the game it was picked in.

export interface CastPick {
	match: ScannerMatch;
	team: Team;
}
interface

CoachLife

no JSDoc:479
export interface CoachLife {
	/** seconds into the video/stream the life starts at: the game's start, else the respawn */
	start: number;
	/** what happened in the life; null without a POV team (a cast) */
	summary: CoachLifeSummary | null;
}
interface

CoachLifeSummary

no JSDoc:486
export interface CoachLifeSummary {
	/** seconds alive, the first life counted from the game clock's start when read */
	duration: number;
	/** the POV player's splats in the life (a trade on the death's second included); null without a kill feed read */
	kills: ScannerMatchKill[] | null;
	/** null without the POV seat */
	specialsUsed: number | null;
	/** null in Turf War or when no objective read falls in the life */
	control: CoachLifeControl | null;
}
Only used inside the module
interface

CoachLifeControl

:498

SZ: whole seconds each side held the zone; TC/RM/CB: how far each side's count went down.

export interface CoachLifeControl {
	unit: "SECONDS" | "POINTS";
	ours: number;
	theirs: number;
}
type

Team

:505

a side of the match, teams index

export type Team = 0 | 1;

Functions & values

const

DEFINITIONS

no JSDoc:29
export const DEFINITIONS = [
	{
		type: "SPECIAL_USED",
		label: "Special used",
		category: "Special",
		variant: "Used",
		followsPovPlayer: true,
		rule: { kind: "specialUsed" },
	},
	{
		type: "OPENING_WON",
		label: "Opening won",
function

ofMatch

:334

The match's coach events, ordered by start. povDeaths are the POV player's death reads (seconds into the video/stream, e.g. povDeathTimes of the match's sources); reads of one death may repeat. A cast's are told from castTeam's side. POV footage whose POV team is unknown (a results screen without the POV seat) has none.

export function ofMatch(
	match: ScannerMatch,
	povDeaths: readonly number[],
	castTeam: Team = 0,
): CoachEvent[]
function

highestTiers

:366

The events less those a stricter tier of the same rule also covers, so each moment is left once, under the highest tier it reached (a 22s stagger as STAGGER_20, not also STAGGER_15). events of one match.

export function highestTiers(events: readonly CoachEvent[]): CoachEvent[]
function

label

:379

The definition's display label.

export function label(type: CoachEventType): string
function

category

:384

The category the type is grouped under.

export function category(type: CoachEventType): CoachEventCategory
function

followsPovPlayer

:389

Whether every type of the category needs the POV player, so casts have none of it.

export function followsPovPlayer(eventCategory: CoachEventCategory): boolean
function

coachedTeam

:408

The team the match is coached from. POV footage: its POV team (povTeamOf). A cast: the team picked in it, else the side sharing more player names with the latest pick's team (casts keep teams by ink color, so a team can change sides between games), else the latest pick's side, else teams[0]. castPicks are oldest first.

export function coachedTeam(
	match: ScannerMatch,
	castPicks: readonly CastPick[],
): Team | null
function

lives

:437

The POV player's lives in the match, chronological: the first starts at the game's start, then one at every respawn. A respawn is the first icon-strip read showing the player back after reading them splatted, else RESPAWN_FALLBACK_S after the death. The last life ends at the last HUD read (counter or icon strip, both re-confirmed every few seconds), else at match.endsAt — that is the results screen, often well after "Game!". povDeaths as in ofMatch; without a POV team only the game's start, with no summary.

export function lives(
	match: ScannerMatch,
	povDeaths: readonly number[],
): CoachLife[]
function

povTeamOf

:637

teams[pov.team] when a results screen named the seat; otherwise POV footage keeps the POV side as teams[0] (minimap own side, the left plate) until a results screen reorders teams winner-first — without the seat then, and on a cast, there is no POV team.

export function povTeamOf(match: ScannerMatch): Team | null
function

winnerByCount

:650

The winner of a game whose results screen was missed, off its objective samples (chronological, teams order): the side whose count went further down. Null on a tie, and when the last read has the clock running out with the trailing side holding the objective — overtime, whose end the counter reads don't show.

export function winnerByCount(
	objective: readonly ScannerMatchObjectiveSample[],
): Team | null

CoachFilters

app/features/scanner/core/CoachFilters.ts

Coach mode's game filters: which of a scanned file's games stay in the review, matched on what the scan read of them. Sides are told apart from the coached team's view ("friendly" and "enemy": the POV team, on a cast the team picked, see CoachEvents.coachedTeam), so a game without one (a results screen without the POV seat) fails every side filter, as a game whose value was never read fails that value's filter.

Imports
CoachEvents
Used by
2 components

Types

interface

Filters

no JSDoc:26
export interface Filters {
	/** match type, e.g. Private Battle or X Battle */
	lobby: ScannerLobby | null;
	mode: ModeShort | null;
	stage: StageId | null;
	/** true = knockouts only, false = games played to time only */
	knockout: boolean | null;
	friendlyWeapon: MainWeaponId | null;
	enemyWeapon: MainWeaponId | null;
	/** an in-game name, compared ignoring case, spaces and diacritics */
	friendlyName: string | null;
	enemyName: string | null;
}
interface

Options

no JSDoc:51
export interface Options {
	lobbies: ScannerLobby[];
	modes: ModeShort[];
	stages: StageId[];
	friendlyWeapons: MainWeaponId[];
	enemyWeapons: MainWeaponId[];
	friendlyNames: string[];
	enemyNames: string[];
}

Functions & values

const

DEFAULT_FILTERS

no JSDoc:40
export const DEFAULT_FILTERS: Filters = {
	lobby: null,
	mode: null,
	stage: null,
	knockout: null,
	friendlyWeapon: null,
	enemyWeapon: null,
	friendlyName: null,
	enemyName: null,
};
function

isActive

:62

Whether any filter is set.

export function isActive(filters: Filters): boolean
function

passes

:67

Whether match passes every set filter.

export function passes(
	match: ScannerMatch,
	filters: Filters,
	castPicks: readonly CoachEvents.CastPick[] = [],
): boolean
function

options

:100

The values the filters can pick from: those read in at least one of matches. Names are listed in their first read spelling, the POV player's own left out of the friendly ones.

export function options(
	matches: readonly ScannerMatch[],
	castPicks: readonly CoachEvents.CastPick[] = [],
): Options

Not shown, only used by tests: knockoutOf

CoachPlayback

app/features/scanner/core/CoachPlayback.ts

Coach mode's playback steps: jumping back and forth between the games, lives or events of a file from where the video is, or a few seconds, and the speeds it plays at. Stepping back restarts the one playing, as a music player's previous track does, unless it began moments ago.

Imports
no other modules
Used by
2 components

Types

type

Direction

no JSDoc:19
export type Direction = "previous" | "next";

Functions & values

const

SPEEDS

:9

Coach mode's playback steps: jumping back and forth between the games, lives or events of a file from where the video is, or a few seconds, and the speeds it plays at. Stepping back restarts the one playing, as a music player's previous track does, unless it began moments ago.

export const SPEEDS = [0.25, 0.5, 1, 1.5, 2] as const;
const

SECONDS_STEP_S

:12

how far the seconds step moves the video

export const SECONDS_STEP_S = 5;
function

step

:28

Where stepping direction from t (seconds into the video) lands among items, each starting at startOf(item), in any order: forward the first to start ahead, back the one playing — or the one before it, when that began under RESTART_GRACE_S ago. Undefined when there's nowhere to go; of items starting together the first listed.

export function step<T>(
	items: readonly T[],
	startOf: (item: T) => number,
	t: number,
	direction: Direction,
): T | undefined
function

speedStep

:58

The speed of SPEEDS one step slower or faster than current (which may be off the list, set from the player's own menu); current at either end.

export function speedStep(current: number, direction: Direction): number