diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 896f43b..a207b91 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -28,7 +28,8 @@ export const routes: Routes = [ }, { path: 'game/lootboxes', - loadComponent: () => import('./feature/lootboxes/lootbox-selection/lootbox-selection.component'), + loadComponent: () => + import('./feature/lootboxes/lootbox-selection/lootbox-selection.component'), canActivate: [authGuard], }, { diff --git a/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.css b/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.css index c4f70f7..1272a49 100644 --- a/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.css +++ b/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.css @@ -308,7 +308,9 @@ body { .animation-fade { opacity: 0; transform: translateY(10px); - transition: opacity 0.5s ease-out, transform 0.5s ease-out; + transition: + opacity 0.5s ease-out, + transform 0.5s ease-out; transition-delay: 0.5s; } diff --git a/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.html b/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.html index 531d1a0..0da3ed8 100644 --- a/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.html +++ b/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.html @@ -2,53 +2,60 @@

Lootbox Öffnen

- +
- +
{{ error }}
- +
- +
- {{ lootbox.price | currency:'EUR' }} + {{ lootbox.price | currency: 'EUR' }}
- +

{{ lootbox.name }}

- +
-
- +
Öffne Lootbox...
- +
-
+

Dein Gewinn:

{{ wonReward?.value | currency: 'EUR' }}
- +
-
- +
+
-
+
{{ reward.value | currency: 'EUR' }}
@@ -71,13 +80,12 @@
- -
-
- +

Fairness garantiert - Alle Ergebnisse werden transparent berechnet.

@@ -97,18 +105,23 @@
- +

Mögliche Gewinne:

  • - {{ reward.value | currency:'EUR' }} + {{ + reward.value | currency: 'EUR' + }} {{ reward.probability * 100 | number: '1.0-0' }}%
- -
+ +

Gewinn-Details:

@@ -121,8 +134,12 @@
Profit: - - {{ (wonReward.value - (lootbox.price || 0)) | currency: 'EUR' }} + + {{ wonReward.value - (lootbox.price || 0) | currency: 'EUR' }}
@@ -130,4 +147,4 @@
-
\ No newline at end of file +
diff --git a/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.ts b/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.ts index ca7c3ca..cd3ed1f 100644 --- a/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.ts +++ b/frontend/src/app/feature/lootboxes/lootbox-opening/lootbox-opening.component.ts @@ -38,7 +38,7 @@ export default class LootboxOpeningComponent { this.isLoading = false; return; } - + const lootboxId = parseInt(idParam, 10); this.lootboxService.getAllLootBoxes().subscribe({ next: (lootboxes) => { @@ -56,9 +56,9 @@ export default class LootboxOpeningComponent { openLootbox(): void { if (!this.lootbox || this.isOpening) return; - + this.resetState(true); - + setTimeout(() => { this.lootboxService.purchaseLootBox(this.lootbox!.id).subscribe({ next: this.handleRewardSuccess.bind(this), @@ -77,7 +77,7 @@ export default class LootboxOpeningComponent { private handleRewardError(): void { if (!this.lootbox) return; - + const rewards = this.lootbox.rewards; const fallback = rewards[Math.floor(Math.random() * rewards.length)]; this.handleRewardSuccess(fallback); @@ -99,17 +99,17 @@ export default class LootboxOpeningComponent { 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}); + items.push({ ...wonReward }); } else { items.push(this.getWeightedRandomReward(possibleRewards)); } } this.prizeList = items; - + setTimeout(() => { this.animationCompleted = true; this.cdr.detectChanges(); @@ -120,14 +120,14 @@ export default class LootboxOpeningComponent { 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] }; } @@ -146,27 +146,26 @@ export default class LootboxOpeningComponent { 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.10) return 'text-epic'; - if (probability < 0.20) return 'text-rare'; - if (probability < 0.40) return 'text-uncommon'; + 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'; } } - diff --git a/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.css b/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.css index bbeb8b0..878220b 100644 --- a/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.css +++ b/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.css @@ -8,15 +8,21 @@ } @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } } .card { - transition: transform 0.3s ease, box-shadow 0.3s ease; + transition: + transform 0.3s ease, + box-shadow 0.3s ease; } .card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); -} \ No newline at end of file +} diff --git a/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.html b/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.html index a4293fd..bda9028 100644 --- a/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.html +++ b/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.html @@ -1,48 +1,60 @@

Lootboxen

- -
isLoading: {{ isLoading }} | error: {{ error }} | lootboxes: {{ lootboxes.length }}
- + +
+ isLoading: {{ isLoading }} | error: {{ error }} | lootboxes: {{ lootboxes.length }} +
+
- +
{{ error }}
- +
-
+
- +
- {{ lootbox.price | currency:'EUR' }} + {{ lootbox.price | currency: 'EUR' }}
- +

{{ lootbox.name }}

- +

Mögliche Gewinne:

  • - {{ reward.value | currency:'EUR' }} + {{ + reward.value | currency: 'EUR' + }} {{ formatProbability(reward.probability) }}
- +
-
- +

Fairness garantiert - Alle Ergebnisse werden transparent berechnet.

@@ -50,4 +62,4 @@
-
\ No newline at end of file +
diff --git a/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.ts b/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.ts index 1382b23..217af16 100644 --- a/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.ts +++ b/frontend/src/app/feature/lootboxes/lootbox-selection/lootbox-selection.component.ts @@ -11,7 +11,7 @@ import { timeout } from 'rxjs'; standalone: true, imports: [CommonModule, NavbarComponent], templateUrl: './lootbox-selection.component.html', - styleUrls: ['./lootbox-selection.component.css'] + styleUrls: ['./lootbox-selection.component.css'], }) export default class LootboxSelectionComponent implements OnInit { lootboxes: LootBox[] = []; @@ -22,68 +22,72 @@ export default class LootboxSelectionComponent implements OnInit { fallbackLootboxes: LootBox[] = [ { id: 1, - name: "Basic LootBox", - price: 2.00, + name: 'Basic LootBox', + price: 2.0, rewards: [ { id: 1, - value: 0.50, - probability: 0.70 + value: 0.5, + probability: 0.7, }, { id: 5, - value: 5.00, - probability: 0.30 - } - ] + value: 5.0, + probability: 0.3, + }, + ], }, { id: 2, - name: "Premium LootBox", - price: 5.00, + name: 'Premium LootBox', + price: 5.0, rewards: [ { id: 4, - value: 2.00, - probability: 0.60 + value: 2.0, + probability: 0.6, }, { id: 5, - value: 5.00, - probability: 0.30 + value: 5.0, + probability: 0.3, }, { id: 6, - value: 15.00, - probability: 0.10 - } - ] + value: 15.0, + probability: 0.1, + }, + ], }, { id: 3, - name: "Legendäre LootBox", - price: 15.00, + name: 'Legendäre LootBox', + price: 15.0, rewards: [ { id: 4, - value: 2.00, - probability: 0.60 + value: 2.0, + probability: 0.6, }, { id: 5, - value: 5.00, - probability: 0.30 + value: 5.0, + probability: 0.3, }, { id: 6, - value: 15.00, - probability: 0.10 - } - ] - } + value: 15.0, + probability: 0.1, + }, + ], + }, ]; - constructor(private lootboxService: LootboxService, private router: Router, private cdr: ChangeDetectorRef) {} + constructor( + private lootboxService: LootboxService, + private router: Router, + private cdr: ChangeDetectorRef + ) {} ngOnInit(): void { this.loadLootboxes(); @@ -91,23 +95,24 @@ export default class LootboxSelectionComponent implements OnInit { loadLootboxes(): void { this.isLoading = true; - this.lootboxService.getAllLootBoxes().pipe( - timeout(5000) - ).subscribe({ - next: (data) => { - console.log('Received lootboxes:', data); - this.lootboxes = data; - this.isLoading = false; - this.cdr.detectChanges(); - }, - error: (err) => { - this.error = 'Konnte keine Verbindung zum Backend herstellen. Zeige Demo-Daten.'; - this.lootboxes = this.fallbackLootboxes; - this.isLoading = false; - this.cdr.detectChanges(); - console.error('Failed to load lootboxes:', err); - } - }); + this.lootboxService + .getAllLootBoxes() + .pipe(timeout(5000)) + .subscribe({ + next: (data) => { + console.log('Received lootboxes:', data); + this.lootboxes = data; + this.isLoading = false; + this.cdr.detectChanges(); + }, + error: (err) => { + this.error = 'Konnte keine Verbindung zum Backend herstellen. Zeige Demo-Daten.'; + this.lootboxes = this.fallbackLootboxes; + this.isLoading = false; + this.cdr.detectChanges(); + console.error('Failed to load lootboxes:', err); + }, + }); } getBoxImage(id: number): string { @@ -131,4 +136,4 @@ export default class LootboxSelectionComponent implements OnInit { formatProbability(probability: number): string { return (probability * 100).toFixed(0) + '%'; } -} \ No newline at end of file +} diff --git a/frontend/src/app/feature/lootboxes/services/lootbox.service.ts b/frontend/src/app/feature/lootboxes/services/lootbox.service.ts index 040b55d..b0fb58d 100644 --- a/frontend/src/app/feature/lootboxes/services/lootbox.service.ts +++ b/frontend/src/app/feature/lootboxes/services/lootbox.service.ts @@ -10,14 +10,12 @@ export class LootboxService { private http = inject(HttpClient); getAllLootBoxes(): Observable { - return this.http - .get('/backend/lootboxes', { responseType: 'json' }) - .pipe( - catchError((error) => { - console.error('Get lootboxes error:', error); - throw error; - }) - ); + return this.http.get('/backend/lootboxes', { responseType: 'json' }).pipe( + catchError((error) => { + console.error('Get lootboxes error:', error); + throw error; + }) + ); } purchaseLootBox(lootBoxId: number): Observable { @@ -30,4 +28,4 @@ export class LootboxService { }) ); } -} \ No newline at end of file +} diff --git a/frontend/src/app/model/LootBox.ts b/frontend/src/app/model/LootBox.ts index f1547b1..ebcf6e8 100644 --- a/frontend/src/app/model/LootBox.ts +++ b/frontend/src/app/model/LootBox.ts @@ -9,4 +9,4 @@ export interface LootBox { name: string; price: number; rewards: Reward[]; -} \ No newline at end of file +}