This repository has been archived on 2025-06-18. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
casino/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.ts
Phan Huy Tran f42070cfae
All checks were successful
CI / Docker frontend validation (pull_request) Has been skipped
CI / Docker backend validation (pull_request) Has been skipped
CI / Get Changed Files (pull_request) Successful in 8s
CI / Checkstyle Main (pull_request) Has been skipped
CI / oxlint (pull_request) Successful in 25s
CI / eslint (pull_request) Successful in 29s
CI / prettier (pull_request) Successful in 28s
CI / test-build (pull_request) Successful in 37s
style: run quality tools
2025-05-14 09:34:41 +02:00

213 lines
5.8 KiB
TypeScript

import { ChangeDetectorRef, Component } 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;
constructor(
private route: ActivatedRoute,
private router: Router,
private lootboxService: LootboxService,
private userService: UserService,
private authService: AuthService,
private cdr: ChangeDetectorRef
) {
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.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;
}
}