import { ChangeDetectorRef, Component, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ActivatedRoute, Router } from '@angular/router'; import { LootboxService } from '../services/lootbox.service'; import { LootBox, Reward } from 'app/model/LootBox'; import { UserService } from '@service/user.service'; import { User } from 'app/model/User'; import { AuthService } from '@service/auth.service'; @Component({ selector: 'app-lootbox-opening', standalone: true, imports: [CommonModule], templateUrl: './lootbox-opening.component.html', styleUrls: ['./lootbox-opening.component.css'], }) export default class LootboxOpeningComponent { lootbox: LootBox | null = null; isLoading = true; error = ''; isOpening = false; isOpen = false; wonReward: Reward | null = null; prizeList: Reward[] = []; animationCompleted = false; currentUser: User | null = null; private winSound: HTMLAudioElement; private route = inject(ActivatedRoute); private router = inject(Router); private lootboxService = inject(LootboxService); private userService = inject(UserService); private authService = inject(AuthService); private cdr = inject(ChangeDetectorRef); constructor() { this.winSound = new Audio('/sounds/win.mp3'); this.loadLootbox(); this.authService.userSubject.subscribe((user) => { this.currentUser = user; this.cdr.detectChanges(); }); } private loadLootbox(): void { const idParam = this.route.snapshot.paramMap.get('id'); if (!idParam) { this.error = 'Invalid lootbox ID'; this.isLoading = false; return; } const lootboxId = parseInt(idParam, 10); this.lootboxService.getAllLootBoxes().subscribe({ next: (lootboxes) => { this.lootbox = lootboxes.find((box) => box.id === lootboxId) || null; this.isLoading = false; this.cdr.detectChanges(); }, error: () => { this.error = 'Failed to load lootbox data'; this.isLoading = false; this.cdr.detectChanges(); }, }); } openLootbox(): void { if (!this.lootbox || this.isOpening) return; // Check if user has enough balance if (!this.hasEnoughBalance()) { this.error = 'Nicht genug Guthaben, um diese Lootbox zu öffnen.'; window.scrollTo(0, 0); this.cdr.detectChanges(); setTimeout(() => { this.error = ''; this.cdr.detectChanges(); }, 5000); return; } this.resetState(true); if (this.lootbox.price) { this.userService.updateLocalBalance(-this.lootbox.price); } setTimeout(() => { this.lootboxService.purchaseLootBox(this.lootbox!.id).subscribe({ next: this.handleRewardSuccess.bind(this), error: this.handleRewardError.bind(this), }); }, 100); } private handleRewardSuccess(reward: Reward): void { this.wonReward = reward; this.generateCasePrizes(reward); this.isOpening = false; this.isOpen = true; this.cdr.detectChanges(); } private handleRewardError(): void { if (!this.lootbox) return; const rewards = this.lootbox.rewards; const fallback = rewards[Math.floor(Math.random() * rewards.length)]; this.wonReward = fallback; this.generateCasePrizes(fallback); this.isOpening = false; this.isOpen = true; this.cdr.detectChanges(); } private resetState(isOpening = false): void { this.isOpening = isOpening; this.isOpen = false; this.wonReward = null; this.prizeList = []; this.animationCompleted = false; this.cdr.detectChanges(); } generateCasePrizes(wonReward: Reward): void { if (!this.lootbox) return; const prizeCount = 120; const winningPosition = Math.floor(prizeCount / 2); const possibleRewards = this.lootbox.rewards; const items: Reward[] = []; for (let i = 0; i < prizeCount; i++) { if (i === winningPosition) { items.push({ ...wonReward }); } else { items.push(this.getWeightedRandomReward(possibleRewards)); } } this.prizeList = items; setTimeout(() => { this.animationCompleted = true; if (this.wonReward) { this.winSound.play(); this.userService.updateLocalBalance(this.wonReward.value); } this.userService.refreshCurrentUser(); this.cdr.detectChanges(); }, 10000); } getWeightedRandomReward(rewards: Reward[]): Reward { const totalProbability = rewards.reduce((sum, reward) => sum + reward.probability, 0); const randomValue = Math.random() * totalProbability; let cumulativeProbability = 0; for (const reward of rewards) { cumulativeProbability += reward.probability; if (randomValue <= cumulativeProbability) { return { ...reward }; } } return { ...rewards[0] }; } openAgain(): void { this.resetState(); this.openLootbox(); } getBoxImage(id: number): string { return `/images/${id}-box.png`; } goBack(): void { this.router.navigate(['/game/lootboxes']); } isWonReward(reward: Reward): boolean { if (!this.wonReward || !this.prizeList.length) return false; const middleIndex = Math.floor(this.prizeList.length / 2); return this.prizeList.indexOf(reward) === middleIndex; } getRewardRarityClass(reward: Reward): string { if (!reward) return 'text-common'; const probability = reward.probability; if (probability < 0.01) return 'text-mythic'; if (probability < 0.05) return 'text-legendary'; if (probability < 0.1) return 'text-epic'; if (probability < 0.2) return 'text-rare'; if (probability < 0.4) return 'text-uncommon'; return 'text-common'; } getRewardClass(): string { if (!this.wonReward || !this.lootbox) return ''; return this.wonReward.value > (this.lootbox.price || 0) ? 'text-emerald' : 'text-accent-red'; } hasEnoughBalance(): boolean { if (!this.currentUser || !this.lootbox) return false; return this.currentUser.balance >= this.lootbox.price; } }