2024-02-29 02:02:14 +00:00
|
|
|
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
|
|
|
|
|
import {getEvents} from "./decode-replay";
|
|
|
|
|
import {DecimalPipe, JsonPipe} from "@angular/common";
|
|
|
|
|
import {beatmapData, replayData} from "./sample-replay";
|
|
|
|
|
import {ReplayService} from "./replay-service";
|
|
|
|
|
import {FormsModule} from "@angular/forms";
|
|
|
|
|
import {parseHitObjects} from "./decode-beatmap";
|
|
|
|
|
import {processReplay} from "./process-replay";
|
|
|
|
|
|
|
|
|
|
export enum KeyPress {
|
|
|
|
|
M1 = 1,
|
|
|
|
|
M2 = 2,
|
|
|
|
|
K1 = 5,
|
|
|
|
|
K2 = 10,
|
|
|
|
|
Smoke = 16,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ReplayEvent {
|
|
|
|
|
/**
|
|
|
|
|
* Time in milliseconds since the previous action
|
|
|
|
|
*/
|
|
|
|
|
timeDelta: number;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* x-coordinate of the cursor from 0 - 512
|
|
|
|
|
*/
|
|
|
|
|
x: number;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* y-coordinate of the cursor from 0 - 384
|
|
|
|
|
*/
|
|
|
|
|
y: number;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Key being pressed.
|
|
|
|
|
*/
|
|
|
|
|
key: KeyPress;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'app-replay-viewer',
|
|
|
|
|
standalone: true,
|
|
|
|
|
imports: [
|
|
|
|
|
JsonPipe,
|
|
|
|
|
FormsModule,
|
|
|
|
|
DecimalPipe
|
|
|
|
|
],
|
|
|
|
|
templateUrl: './replay-viewer.component.html',
|
|
|
|
|
styleUrl: './replay-viewer.component.css'
|
|
|
|
|
})
|
|
|
|
|
export class ReplayViewerComponent implements OnInit, AfterViewInit {
|
|
|
|
|
|
|
|
|
|
@ViewChild('replayCanvas') replayCanvas!: ElementRef<HTMLCanvasElement>;
|
|
|
|
|
private ctx!: CanvasRenderingContext2D;
|
|
|
|
|
|
2024-03-01 11:52:12 +00:00
|
|
|
// TODO: Calculate AudioLeadIn
|
|
|
|
|
// TODO: Calculate circle size (CS)
|
|
|
|
|
// TODO: Hard-Rock, DT, Easy
|
|
|
|
|
|
|
|
|
|
// TODO: Cursor trail and where keys are pressed
|
|
|
|
|
// TODO: Button for -100 ms, +100 ms, etc (precise seeking) (or keyboard shortcuts)
|
|
|
|
|
|
|
|
|
|
// TODO: Way to obtain replay+beatmap info from the backend
|
|
|
|
|
|
|
|
|
|
// TODO: UR bar
|
|
|
|
|
// Todo: Customizable speed
|
|
|
|
|
// TODO: Customizable zoom
|
|
|
|
|
// TODO: Fullscreen mode
|
|
|
|
|
|
|
|
|
|
// TODO: Hit/Miss, Combo, Accuracy
|
|
|
|
|
|
|
|
|
|
// TODO: Compare two replays for similarity (different cursor color)
|
|
|
|
|
|
2024-02-29 02:02:14 +00:00
|
|
|
constructor(public replayService: ReplayService) { }
|
|
|
|
|
|
|
|
|
|
ngOnInit() {
|
|
|
|
|
// Assume getEvents() method returns a promise of ReplayEvent[]
|
|
|
|
|
getEvents(replayData).then(events => {
|
|
|
|
|
this.replayService.setEvents(processReplay(events));
|
|
|
|
|
this.replayService.setHitObjects(parseHitObjects(beatmapData))
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngAfterViewInit() {
|
|
|
|
|
this.ctx = this.replayCanvas.nativeElement.getContext('2d')!;
|
|
|
|
|
|
|
|
|
|
this.replayService.setCanvasElement(this.replayCanvas);
|
|
|
|
|
this.replayService.setCanvasContext(this.ctx);
|
|
|
|
|
|
|
|
|
|
this.replayService.start(); // Start the animation loop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
togglePlayPause() {
|
|
|
|
|
if (this.replayService.getIsPlaying()) {
|
|
|
|
|
this.replayService.pause();
|
|
|
|
|
} else {
|
|
|
|
|
this.replayService.start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seek(time: number) {
|
|
|
|
|
this.replayService.seek(time);
|
|
|
|
|
if (!this.replayService.getIsPlaying()) {
|
|
|
|
|
// Redraw the canvas for the new current time without resuming playback
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|