import { Component, Input, NgZone, OnInit } from '@angular/core'; import { ConfigService } from '../_services/config.service'; import { ShortcutService } from '../_services/shortcut.service'; import { CarouselComponent } from '../carousel/carousel.component'; import { PlanService } from '../_services/plan.service'; import { Plan, PlanElement } from '../_models/plan'; import { WorldAreaService } from '../_services/world-area.service'; import { WorldArea } from '../_models/world-area'; import { Subscription, from } from 'rxjs'; import { OverlayService, StateEvent } from '../_services/overlay.service'; import { appWindow } from '@tauri-apps/api/window'; import { EventsService } from '../_services/events.service'; import { Event } from '@tauri-apps/api/event'; import { MatDialog } from '@angular/material/dialog'; import { TimeTrackerService } from '../_services/time-tracker.service'; import { RunStatService } from '../_services/run-stat.service'; @Component({ selector: 'plan-display', templateUrl: './plan-display.component.html', styleUrls: ['./plan-display.component.scss'] }) export class PlanDisplayComponent implements OnInit { @Input() backgroundColor?: string; slideIndex: number = 0; zoneSlides?: CarouselComponent; currentSlides?: CarouselComponent; worldAreaMap?: Map; settingsOpen: boolean = false; hasAttachedOnce: boolean = false; overlayStateChangeHandle?: Subscription; bindsAreSetup: boolean = false; nextBind?: Subscription; prevBind?: Subscription; currentPlan?: Plan; constructor( public configService: ConfigService, public planService: PlanService, public worldAreaService: WorldAreaService, public overlayService: OverlayService, public dialog: MatDialog, public timeTrackerService: TimeTrackerService, private events: EventsService, private shortcut: ShortcutService, private zone: NgZone, private runStatService: RunStatService, ) { this.planService.getCurrentPlan().subscribe(plan => { this.currentPlan = plan; if (this.configService.config.enableStopwatch) { this.loadComparisonData(this.currentPlan); } this.timeTrackerService.onNewRun(plan); //Close settings anytime we get a new current plan. this.settingsOpen = false; setTimeout(() => this.setIndex(plan.current), 0); }) this.timeTrackerService.getNewRunSubject().subscribe(() => { this.currentSlides?.setIndex(0); this.zoneSlides?.setIndex(0); this.slideIndex = 0; this.currentPlan?.resetPlan(); }) this.registerOnZoneEnter(); } loadComparisonData(plan: Plan) { if (!this.configService.config.runCompareHistory) { return; } this.timeTrackerService.loadHistory(this.configService.config.runCompareHistory).subscribe(history => { if (history) { this.runStatService.insertTimesAtCheckpoints(history, plan); } }); } registerOnZoneEnter() { appWindow.listen("entered", (entered) => { if (this.currentPlan && typeof entered.payload == "string") { if (this.currentPlan.isNext(entered.payload)) { this.zone.run(() => this.next()); } } }); } ngOnInit() { this.worldAreaService.getFullWorldAreas().subscribe(a => this.worldAreaMap = a); this.overlayStateChangeHandle = this.events.listen("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this)); this.events.listen("overlay_or_target_focus").subscribe((event: Event) => { if (!event.payload) { this.destroyBinds(); } else { this.setupBinds(); } }); } onOverlayStateChange(event: Event) { if (event.payload.Hidden) { this.destroyBinds(); } else { this.setupBinds(); } } destroyBinds() { if (this.bindsAreSetup) { this.nextBind?.unsubscribe(); this.prevBind?.unsubscribe(); this.bindsAreSetup = false; } } abs(v: number) { return Math.abs(v); } hasWaypoint(key?: string): boolean { if (!key) { key = this.currentPlan!.plan[this.currentPlan!.current].area_key; } const world_area = this.worldAreaMap?.get(key); return world_area!.has_waypoint; } hasTrial(key?: string): boolean { if (!key) { key = this.currentPlan!.plan[this.currentPlan!.current].area_key; } return this.worldAreaService.hasTrial(key); } registerZoneSlides(carousel: CarouselComponent) { this.zoneSlides = carousel; this.zoneSlides.setIndex(this.slideIndex); } setupBinds() { if (this.currentSlides && !this.bindsAreSetup) { this.nextBind = this.shortcut.register(this.configService.config.prev).subscribe((_shortcut) => { this.prev(); if (this.configService.config.enableStopwatch) { this.timeTrackerService.onForcePrev(this.currentPlan!.plan[this.currentPlan!.current].area_key); this.checkCheckpoint(); } }); this.prevBind = this.shortcut.register(this.configService.config.next).subscribe((_shortcut) => { this.next(); if (this.configService.config.enableStopwatch) { this.timeTrackerService.onForceNext(this.currentPlan!.plan[this.currentPlan!.current].area_key); this.checkCheckpoint(); } }); this.bindsAreSetup = true; } } registerCurrentSlides(carousel: CarouselComponent) { this.currentSlides = carousel; this.currentSlides.setIndex(this.slideIndex); this.setupBinds(); } next() { if (this.overlayService.visible) { this.currentPlan!.next(); this.checkCheckpoint(); this.currentSlides?.next(); this.zoneSlides?.next(); } } checkCheckpoint() { if (!this.currentPlan || !this.timeTrackerService.isActive) return; const currentElem = this.currentPlan.plan[this.currentPlan.current]; if (currentElem.checkpoint && !currentElem.checkpoint_your_millis) { currentElem.checkpoint_your_millis = this.timeTrackerService.elapsedTimeMillis; this.timeTrackerService.reportCheckpoint(currentElem.uuid!); } } yourDiff(element: PlanElement) { if (!element.checkpoint || !element.checkpoint_your_millis || !element.checkpoint_millis) return ""; const diff = element.checkpoint_your_millis - element.checkpoint_millis; const neg = diff <= 0; const abs = Math.abs(diff); if (diff == 0) { return `${neg ? "-" : "+"}00:00:00`; } else { return `${neg ? "-" : "+"}${this.timeTrackerService.hmsTimestamp(abs)}`; } } yourDiffClass(element: PlanElement): string { if (!element.checkpoint || !element.checkpoint_your_millis || !element.checkpoint_millis) return ""; const diff = element.checkpoint_your_millis - element.checkpoint_millis; const neg = diff <= 0; return neg ? "negative-diff" : "positive-diff"; } showDiff(element: PlanElement) { return element.checkpoint && element.checkpoint_your_millis && element.checkpoint_millis; } cpMillis(element: PlanElement) { if (!element.checkpoint) return ""; if (!element.checkpoint_millis) return "N/A"; return this.timeTrackerService.hmsTimestamp(element.checkpoint_millis); } prev() { if (this.overlayService.visible) { this.currentPlan!.prev(); this.currentSlides?.prev(); this.zoneSlides?.prev(); } } setIndex(index: number) { this.slideIndex = index; if (this.currentSlides) { this.currentSlides.setIndex(index); } if (this.zoneSlides) { this.zoneSlides.setIndex(index); } } settingsClick(event: any) { this.settingsOpen = !this.settingsOpen; event.stopPropagation(); } onScroll(event: WheelEvent) { if (event.deltaY < 0) { this.prev(); this.timeTrackerService.onForcePrev(this.currentPlan!.plan[this.currentPlan!.current].area_key); this.checkCheckpoint(); } else { this.next(); this.timeTrackerService.onForceNext(this.currentPlan!.plan[this.currentPlan!.current].area_key); this.checkCheckpoint(); } } specialClasses() { const waypoint = this.hasWaypoint() ? 'active' : ''; const trial = this.hasTrial() ? 'trial-active' : ''; return `${waypoint} ${trial}`; } clampedOffset(): number { return Math.min(this.configService.config.numVisible - 1, this.configService.config.offset); } zonesStyle() { return { 'min-height': `${this.configService.config.numVisible * 18}px`, 'max-height': `${this.configService.config.numVisible * 40}px` } } shouldDisplayTimer(): boolean { if (!this.configService.config.enableStopwatch) return false; return this.timeTrackerService.isActive; } displayZoneName(zoneName: string) { if (this.configService.config.shortenZoneNames) { return this.trim(this.trimUnneccesaryWords(zoneName)); } else { return zoneName; } } trimUnneccesaryWords(zoneName: string) { if (zoneName.toLowerCase().startsWith("the ")) { return zoneName.substring(4); } else { return zoneName; } } trim(zoneName: string, letters: number = 12) { if (zoneName.length > letters + 3) { return zoneName.substring(0, letters) + "..."; } return zoneName; } }