diff --git a/frontend/src/app/feature/game/coinflip/coinflip.component.css b/frontend/src/app/feature/game/coinflip/coinflip.component.css index a1548ab..f896ade 100644 --- a/frontend/src/app/feature/game/coinflip/coinflip.component.css +++ b/frontend/src/app/feature/game/coinflip/coinflip.component.css @@ -53,21 +53,23 @@ background: linear-gradient(45deg, #ffd700, #ffb700); color: #333; z-index: 2; + transform: rotateY(0); } .back { - transform: rotateY(180deg); background: linear-gradient(45deg, #5a5a5a, #333333); color: white; z-index: 1; - /* Fix text display on the back of the coin */ - backface-visibility: hidden; + transform: rotateY(180deg); } -/* Fix reversed text on tails side by applying a counter-rotation to just the text */ -.back > span { - display: inline-block; - transform: rotateY(180deg); /* Counter-rotate the text so it appears correctly */ +/* We apply transform directly to the SVG element in HTML */ + +/* Text for both sides */ +.coin-text { + /* Ensure text is readable */ + user-select: none; + pointer-events: none; } /* Animation classes */ diff --git a/frontend/src/app/feature/game/coinflip/coinflip.component.html b/frontend/src/app/feature/game/coinflip/coinflip.component.html index 57736f5..4501de1 100644 --- a/frontend/src/app/feature/game/coinflip/coinflip.component.html +++ b/frontend/src/app/feature/game/coinflip/coinflip.component.html @@ -5,55 +5,62 @@ @if (gameResult()) {

- {{ (gameResult()?.isWin || gameResult()?.win) ? 'You Won!' : 'You Lost' }} + {{ gameResult()?.isWin ? 'You Won!' : 'You Lost' }}

- Coin landed on: {{ gameResult()?.coinSide === 'HEAD' ? 'HEAD' : 'TAILS' }} + Coin landed on: + {{ + gameResult()?.coinSide === 'HEAD' ? 'HEAD' : 'TAILS' + }}

- @if (gameResult()?.isWin || gameResult()?.win) { + @if (gameResult()?.isWin) {

+{{ gameResult()?.payout | currency: 'EUR' }}

}
} - + @if (errorMessage()) {

{{ errorMessage() }}

} - +
+
- HEAD +
HEAD
+ +
- TAILS + + TAILS
- +
- -
- +
@@ -74,7 +81,7 @@
- +
Your Balance: @@ -109,7 +116,7 @@ [placeholder]="balance() | currency: 'EUR'" />
- +

How to Play

diff --git a/frontend/src/app/feature/game/coinflip/coinflip.component.ts b/frontend/src/app/feature/game/coinflip/coinflip.component.ts index 498d770..27ff13f 100644 --- a/frontend/src/app/feature/game/coinflip/coinflip.component.ts +++ b/frontend/src/app/feature/game/coinflip/coinflip.component.ts @@ -1,7 +1,15 @@ import { CurrencyPipe } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { FormsModule } from '@angular/forms'; -import { ChangeDetectionStrategy, Component, ElementRef, inject, OnInit, signal, ViewChild } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + ElementRef, + inject, + OnInit, + signal, + ViewChild, +} from '@angular/core'; import { AnimatedNumberComponent } from '@blackjack/components/animated-number/animated-number.component'; import { catchError, finalize } from 'rxjs/operators'; import { of } from 'rxjs'; @@ -25,13 +33,13 @@ export default class CoinflipComponent implements OnInit { gameResult = signal(null); betInputValue = signal(10); errorMessage = signal(''); - + @ViewChild('coinElement') coinElement?: ElementRef; - + audioService = inject(AudioService); authService = inject(AuthService); private http = inject(HttpClient); - + private coinflipSound?: HTMLAudioElement; ngOnInit(): void { @@ -41,21 +49,21 @@ export default class CoinflipComponent implements OnInit { this.balance.set(user.balance); } }); - + // Initialize coinflip sound this.coinflipSound = new Audio('/sounds/coinflip.mp3'); } - + setBetAmount(percentage: number) { const newBet = Math.floor(this.balance() * percentage); this.betInputValue.set(newBet > 0 ? newBet : 1); this.currentBet.set(this.betInputValue()); } - + updateBet(event: Event) { const inputElement = event.target as HTMLInputElement; const value = Number(inputElement.value); - + if (value <= 0) { this.betInputValue.set(1); this.currentBet.set(1); @@ -71,43 +79,44 @@ export default class CoinflipComponent implements OnInit { betHeads() { this.placeBet('HEAD'); } - + betTails() { this.placeBet('TAILS'); } - + private placeBet(side: 'HEAD' | 'TAILS') { if (this.gameInProgress() || this.isActionInProgress()) return; - + // Reset previous result this.gameResult.set(null); this.errorMessage.set(''); - + // Set game state this.gameInProgress.set(true); this.isActionInProgress.set(true); - + // Play bet sound this.audioService.playBetSound(); - + // Create bet request const request: CoinflipRequest = { betAmount: this.currentBet(), - coinSide: side + coinSide: side, }; - + // Call API - this.http.post('/backend/coinflip', request) + this.http + .post('/backend/coinflip', request) .pipe( - catchError(error => { + catchError((error) => { console.error('Error playing coinflip:', error); - + if (error.status === 400 && error.error.message.includes('insufficient')) { this.errorMessage.set('Insufficient funds'); } else { this.errorMessage.set('An error occurred. Please try again.'); } - + this.gameInProgress.set(false); return of(null); }), @@ -115,35 +124,35 @@ export default class CoinflipComponent implements OnInit { this.isActionInProgress.set(false); }) ) - .subscribe(result => { + .subscribe((result) => { if (!result) return; - + console.log('API response:', result); - + // Fix potential property naming inconsistency from the backend const fixedResult: CoinflipGame = { isWin: result.isWin ?? result.win, payout: result.payout, - coinSide: result.coinSide + coinSide: result.coinSide, }; - + console.log('Fixed result:', fixedResult); - + // Play coin flip animation and sound this.playCoinFlipAnimation(fixedResult.coinSide); - + // Set result after animation completes setTimeout(() => { this.gameResult.set(fixedResult); - + // Update balance with new value from auth service this.authService.loadCurrentUser(); - + // Play win sound if player won if (fixedResult.isWin) { this.audioService.playWinSound(); } - + // Reset game state after showing result setTimeout(() => { this.gameInProgress.set(false); @@ -151,37 +160,37 @@ export default class CoinflipComponent implements OnInit { }, 1100); // Just after animation ends }); } - + private playCoinFlipAnimation(result: 'HEAD' | 'TAILS') { if (!this.coinElement) return; - + const coinEl = this.coinElement.nativeElement; - + // Reset any existing animations coinEl.classList.remove('animate-to-heads', 'animate-to-tails'); - + // Reset any inline styles from previous animations coinEl.style.transform = ''; - + // Force a reflow to restart animation void coinEl.offsetWidth; - + // Play flip sound if (this.coinflipSound) { this.coinflipSound.currentTime = 0; - this.coinflipSound.play().catch(err => console.error('Error playing sound:', err)); + this.coinflipSound.play().catch((err) => console.error('Error playing sound:', err)); } - + // Add appropriate animation class based on result if (result === 'HEAD') { coinEl.classList.add('animate-to-heads'); } else { coinEl.classList.add('animate-to-tails'); } - + console.log(`Animation applied for result: ${result}`); } - + getResultClass() { if (!this.gameResult()) return ''; const result = this.gameResult();