diff --git a/scripts/common/timer.ts b/scripts/common/timer.ts index 4ce7d6b5..e92a1c72 100644 --- a/scripts/common/timer.ts +++ b/scripts/common/timer.ts @@ -1,3 +1,5 @@ +type Gamemode = import('common/web').Gamemode; + /* eslint-disable @typescript-eslint/naming-convention */ export enum TimerEvent_OLD { STARTED = 0, @@ -104,6 +106,18 @@ export interface RunSplits { segments: RunSegment[]; } +export interface RunMetadata { + filePath: string; + timestamp: number; + gameMode: Gamemode; + tickInterval: float; + playerSteamId: number; + playerName: string; + trackId: TrackID; + runTime: double; + runSplits: RunSplits | null; +} + /** Enum for why end of run is being shown */ export enum EndOfRunShowReason { PLAYER_FINISHED_RUN = 0, diff --git a/scripts/hud/comparisons.ts b/scripts/hud/comparisons.ts index cb41a1e6..75d9dcbc 100644 --- a/scripts/hud/comparisons.ts +++ b/scripts/hud/comparisons.ts @@ -1,12 +1,9 @@ import { PanelHandler } from 'util/module-helpers'; -import { TimerEvent_OLD, TimerState_OLD } from 'common/timer'; -import { Comparison_OLD, RunStats_OLD, Split_OLD } from 'common/timer'; +import { RunMetadata } from 'common/timer'; @PanelHandler() class HudComparisonsHandler { - runFinished = false; - currentZone = 0; - runStatsZoneIndex = 0; + comparison: RunMetadata = null; readonly maxActiveSplits = 12; readonly newSplitTransitionDuration = 2; @@ -17,129 +14,35 @@ class HudComparisonsHandler { }; constructor() { - $.RegisterEventHandler('HudCompare_Update', $.GetContextPanel(), () => this.updateComparisons()); - $.RegisterForUnhandledEvent('OnMomentumTimerStateChange', (arg, arg2) => this.onTimerEvent(arg, arg2)); + $.RegisterForUnhandledEvent('ComparisonRunUpdated', () => this.onComparisonRunUpdated()); + $.RegisterForUnhandledEvent('OnObservedTimerStateChange', () => this.updateComparisons()); + $.RegisterForUnhandledEvent('OnObservedTimerCheckpointProgressed', () => this.updateComparisons()); + $.RegisterForUnhandledEvent('OnObservedTimerReplaced', () => this.updateComparisons()); } - clearComparisons() { - // this.panels.compare.RemoveAndDeleteChildren(); - this.panels.splits.RemoveAndDeleteChildren(); - this.runFinished = false; - this.currentZone = 0; - this.runStatsZoneIndex = 0; - } + onComparisonRunUpdated() { + this.comparison = RunComparisonsAPI.GetComparisonRun(); + + $.Msg(`Got comparison run ${this.comparison?.filePath ?? 'NONE'}`); - onTimerEvent(_ent: any, eventType: any) { - if (eventType === TimerEvent_OLD.STARTED) { - this.clearComparisons(); + if (this.comparison) { + $.Msg(`jumps: ${this.comparison.runSplits.trackStats.jumps}`); } + + this.updateComparisons(); } updateComparisons() { - /* TODO - const currentData = $.GetContextPanel().currentRunData; - const currentStats = $.GetContextPanel().currentRunStats; - const comparisonRun = RunComparisonsAPI.GetLoadedComparison(); - - const hasCompare = !!comparisonRun.compareRun; + const timerStatus = MomentumTimerAPI.GetObservedTimerStatus(); + const runSplits = MomentumTimerAPI.GetObservedTimerRunSplits(); if ( - !currentData || - !currentData.isInZone || - !currentStats || - currentData.currentZone === 1 || - currentData.timerState === TimerState_OLD.PRACTICE + timerStatus.trackId.type === this.comparison.trackId.type && + timerStatus.trackId.number === this.comparison.trackId.number ) { - return; - } - - // Here, this.currentZone tracks how far into the run we are, so we can compare against currentData.currentZone, - // so we don't fire on stage we've already hit. - // this.runStatsZoneIndex tracks the correct index into the runStats array - - if (currentData.timerState === TimerState_OLD.NOT_RUNNING) { - // The only time we care about comparisons when timer is not running is if you just - // hit the end zone *for the first time* - if (!this.runFinished && currentData.currentZone === 0) { - // We're at the last zone, set index to last in the RunStats zone array - this.runStatsZoneIndex = currentStats.numZones - 1; - this.currentZone = 0; - - // Track that we've finished so this never runs again - this.runFinished = true; - } else { - return; - } } else { - // Return out if you went back a zone - if (currentData.currentZone <= this.currentZone) { - return; - } else { - // This is the first time you hit this zone - this.currentZone = currentData.currentZone; - this.runStatsZoneIndex = currentData.currentZone - 2; // -2 but currentZone is offset by the end and start zones - } + // Track differs -- could maybe do a best-effort comparison if one is a stage and one is the main track, + // otherwise make sure the comparison HUD is cleared } - - const splitPanels = this.panels.splits.Children().reverse(); - if (splitPanels.length > this.maxActiveSplits) { - for (const panel of splitPanels.filter((_, i) => splitPanels.length - i > this.maxActiveSplits)) - panel.RemoveAndDeleteChildren(); - } - - const data = hasCompare - ? Comparison_OLD.generateSplits( - new RunStats_OLD(currentStats, currentData.tickRate), - new RunStats_OLD(comparisonRun.compareRun.stats, currentData.tickRate) - // this.runStatsZoneIndex + 1 TODO: this was being passed to generateSplits, but that only takes two args. What was this for? - )[this.runStatsZoneIndex] - : new RunStats_OLD(currentStats, currentData.tickRate, this.runStatsZoneIndex + 1).zones[ - this.runStatsZoneIndex - ]; - - const wrapper = $.CreatePanel('Panel', this.panels.splits, `Split${data.name}`, { - class: 'hud-comparisons__split' - }); - - if (this.runStatsZoneIndex > 0) { - const lastSplit = this.panels.splits.GetFirstChild().GetFirstChild(); - lastSplit?.RemoveClass('split--latest'); - } - - this.panels.splits.MoveChildBefore(wrapper, this.panels.splits.Children()[0]); - - const panel = $.CreatePanel('Split', wrapper, '', { class: 'split--hud split--latest' }); - - // Animation code I might try in 0.9.2 - - // Avoids hardcoding a height value. Waits for one split to spawn and have correct height, then tracks it. - // Means first one won't animate height but that's fine as there's nothing for it to push upwards - // $.Schedule(0.05, () => (this.latestSplitHeight ??= panel.actuallayoutheight / panel.actualuiscale_y)); - - // if (!this.latestSplitHeight) return; - - // panel.style.height = `${this.latestSplitHeight}px`; - // wrapper.style.transitionDuration = '0s'; - // wrapper.style.height = '0px'; - // wrapper.style.transitionDuration = `${NEW_SPLIT_TRANSITION_DURATION}s`; - // wrapper.style.height = `${this.latestSplitHeight}px`; - - Object.assign( - panel, - hasCompare - ? { - name: data.name, - time: data.accumulateTime, - isFirst: false, - diff: (data as Split_OLD).diff, - delta: (data as Split_OLD).delta - } - : { - name: data.name, - time: data.accumulateTime, - isFirst: true - } - ); - */ } } diff --git a/scripts/pages/end-of-run/end-of-run.ts b/scripts/pages/end-of-run/end-of-run.ts index fd5d7335..3e762de2 100644 --- a/scripts/pages/end-of-run/end-of-run.ts +++ b/scripts/pages/end-of-run/end-of-run.ts @@ -34,9 +34,9 @@ class EndOfRunHandler { selectedSplit: Split_OLD; constructor() { - $.RegisterForUnhandledEvent('EndOfRun_CompareRuns', (baseRun, compareRun) => - this.setComparison(baseRun, compareRun) - ); + // $.RegisterForUnhandledEvent('EndOfRun_CompareRuns', (baseRun, compareRun) => + // this.setComparison(baseRun, compareRun) + // ); $.RegisterForUnhandledEvent('EndOfRun_Show', (reason) => this.showNewEndOfRun(reason)); $.RegisterForUnhandledEvent('EndOfRun_Result_RunUpload', (uploaded, cosXP, rankXP, lvlGain) => this.updateRunUploadStatus(uploaded, cosXP, rankXP, lvlGain) diff --git a/scripts/pages/leaderboards/entry.ts b/scripts/pages/leaderboards/entry.ts index 349c8be1..c1b5f07a 100644 --- a/scripts/pages/leaderboards/entry.ts +++ b/scripts/pages/leaderboards/entry.ts @@ -53,6 +53,15 @@ class LeaderboardEntryHandler { $.DispatchEvent('LeaderboardEntry_PlayReplay', index); } }); + + items.push({ + label: $.Localize('#Action_SetComparisonRun'), + icon: 'file://{images}/chart-timeline.svg', + style: 'icon-color-light-blue', + jsCallback: () => { + $.DispatchEvent('LeaderboardEntry_SetComparisonRun', index); + } + }); } if (timeData.type === LeaderboardEntryType.ONLINE || timeData.type === LeaderboardEntryType.ONLINE_CACHED) { diff --git a/scripts/types-mom/apis.d.ts b/scripts/types-mom/apis.d.ts index 3062384f..94ff7a32 100644 --- a/scripts/types-mom/apis.d.ts +++ b/scripts/types-mom/apis.d.ts @@ -1,3 +1,4 @@ +type RunMetadata = import('common/timer').RunMetadata; type Gamemode = import('common/web').Gamemode; declare namespace MomentumAPI { @@ -280,12 +281,8 @@ declare namespace SpectatorAPI { function GetSpecList(): steamID[]; } -/** Probably changing soon, doing weak types. */ declare namespace RunComparisonsAPI { - function GetLoadedComparison(): any; - function IsComparisonLoaded(): any; - function GetLoadedComparisonSpeed(...args: any[]): any; - function GetLoadedComparisonOverallDiff(arg: any): any; + function GetComparisonRun(): RunMetadata; } declare namespace ZonesAPI { diff --git a/scripts/types-mom/events.d.ts b/scripts/types-mom/events.d.ts index 5fc8f6ae..ce4c1205 100644 --- a/scripts/types-mom/events.d.ts +++ b/scripts/types-mom/events.d.ts @@ -61,8 +61,7 @@ interface GlobalEventNameMap { OnNewChatEntry: (panel: GenericPanel & { message: string; author_name: string; author_xuid: string }) => void; - /** Fired when the JS panel should update what it's showing */ - HudCompare_Update: () => void; + ComparisonRunUpdated: () => void; // TODO: Old, remove after rio stuff is in OnMomentumTimerStateChange: (arg1: any, arg2: any) => any; @@ -271,12 +270,6 @@ interface GlobalEventNameMap { /** Fired when the end of run panel should be hidden */ EndOfRun_Hide: () => void; - /** Fired when the end of run panel should set the two runs to compare. baseRun is compared to compareRun */ - EndOfRun_CompareRuns: ( - baseRun: import('common/timer').CPPRun_OLD, - compareRun: import('common/timer').CPPRun_OLD - ) => void; - /** Fired when the replay recording finishes and passes whether writing the file was successful */ EndOfRun_Result_RunSave: (saved: boolean) => void; @@ -322,6 +315,8 @@ interface GlobalEventNameMap { LeaderboardEntry_PlayReplay: (itemIndex: int32) => void; + LeaderboardEntry_SetComparisonRun: (itemIndex: int32) => void; + LeaderboardEntry_DeleteReplay: (itemIndex: int32) => void; /**