style(blackjack): format code for better readability
Some checks failed
Some checks failed
This commit is contained in:
parent
adc5bbd345
commit
23888ceb27
9 changed files with 139 additions and 121 deletions
|
@ -5,17 +5,21 @@
|
||||||
<div class="lg:col-span-3 space-y-6 flex flex-col gap-4">
|
<div class="lg:col-span-3 space-y-6 flex flex-col gap-4">
|
||||||
<app-dealer-hand [cards]="dealerCards()"></app-dealer-hand>
|
<app-dealer-hand [cards]="dealerCards()"></app-dealer-hand>
|
||||||
<app-player-hand [cards]="playerCards()"></app-player-hand>
|
<app-player-hand [cards]="playerCards()"></app-player-hand>
|
||||||
|
|
||||||
<!-- Action message indicator -->
|
<!-- Action message indicator -->
|
||||||
@if (isActionInProgress()) {
|
@if (isActionInProgress()) {
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<div class="card p-4 flex items-center gap-3 animate-pulse bg-deep-blue-light border border-deep-blue-light/50">
|
<div
|
||||||
<div class="w-5 h-5 rounded-full border-2 border-white border-t-transparent animate-spin"></div>
|
class="card p-4 flex items-center gap-3 animate-pulse bg-deep-blue-light border border-deep-blue-light/50"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="w-5 h-5 rounded-full border-2 border-white border-t-transparent animate-spin"
|
||||||
|
></div>
|
||||||
<span>{{ currentAction() }}</span>
|
<span>{{ currentAction() }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (gameInProgress()) {
|
@if (gameInProgress()) {
|
||||||
<app-game-controls
|
<app-game-controls
|
||||||
[playerCards]="playerCards()"
|
[playerCards]="playerCards()"
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default class BlackjackComponent {
|
||||||
gameInProgress = signal(false);
|
gameInProgress = signal(false);
|
||||||
gameState = signal<string>('IN_PROGRESS');
|
gameState = signal<string>('IN_PROGRESS');
|
||||||
showGameResult = signal(false);
|
showGameResult = signal(false);
|
||||||
|
|
||||||
// Add loading state trackers
|
// Add loading state trackers
|
||||||
isActionInProgress = signal(false);
|
isActionInProgress = signal(false);
|
||||||
currentAction = signal<string>('');
|
currentAction = signal<string>('');
|
||||||
|
@ -66,7 +66,7 @@ export default class BlackjackComponent {
|
||||||
|
|
||||||
// When game ends, make sure all dealer cards are visible
|
// When game ends, make sure all dealer cards are visible
|
||||||
const isGameOver = game.state !== 'IN_PROGRESS';
|
const isGameOver = game.state !== 'IN_PROGRESS';
|
||||||
|
|
||||||
this.dealerCards.set(
|
this.dealerCards.set(
|
||||||
game.dealerCards.map((card, index) => ({
|
game.dealerCards.map((card, index) => ({
|
||||||
...card,
|
...card,
|
||||||
|
@ -85,7 +85,7 @@ export default class BlackjackComponent {
|
||||||
if (isGameOver) {
|
if (isGameOver) {
|
||||||
console.log('Game is over, state:', game.state);
|
console.log('Game is over, state:', game.state);
|
||||||
this.refreshUserBalance();
|
this.refreshUserBalance();
|
||||||
|
|
||||||
// Show result immediately without resetting first
|
// Show result immediately without resetting first
|
||||||
this.showGameResult.set(true);
|
this.showGameResult.set(true);
|
||||||
console.log('Game result dialog should be shown now');
|
console.log('Game result dialog should be shown now');
|
||||||
|
@ -95,7 +95,7 @@ export default class BlackjackComponent {
|
||||||
onNewGame(bet: number): void {
|
onNewGame(bet: number): void {
|
||||||
this.isActionInProgress.set(true);
|
this.isActionInProgress.set(true);
|
||||||
this.currentAction.set('Spiel wird gestartet...');
|
this.currentAction.set('Spiel wird gestartet...');
|
||||||
|
|
||||||
this.blackjackService.startGame(bet).subscribe({
|
this.blackjackService.startGame(bet).subscribe({
|
||||||
next: (game) => {
|
next: (game) => {
|
||||||
this.updateGameState(game);
|
this.updateGameState(game);
|
||||||
|
@ -114,7 +114,7 @@ export default class BlackjackComponent {
|
||||||
|
|
||||||
this.isActionInProgress.set(true);
|
this.isActionInProgress.set(true);
|
||||||
this.currentAction.set('Karte wird gezogen...');
|
this.currentAction.set('Karte wird gezogen...');
|
||||||
|
|
||||||
this.blackjackService.hit(this.currentGameId()!).subscribe({
|
this.blackjackService.hit(this.currentGameId()!).subscribe({
|
||||||
next: (game) => {
|
next: (game) => {
|
||||||
this.updateGameState(game);
|
this.updateGameState(game);
|
||||||
|
@ -130,7 +130,7 @@ export default class BlackjackComponent {
|
||||||
|
|
||||||
onStand(): void {
|
onStand(): void {
|
||||||
if (!this.currentGameId() || this.isActionInProgress()) return;
|
if (!this.currentGameId() || this.isActionInProgress()) return;
|
||||||
|
|
||||||
if (this.gameState() !== 'IN_PROGRESS') {
|
if (this.gameState() !== 'IN_PROGRESS') {
|
||||||
console.log('Cannot stand: game is not in progress');
|
console.log('Cannot stand: game is not in progress');
|
||||||
return;
|
return;
|
||||||
|
@ -138,7 +138,7 @@ export default class BlackjackComponent {
|
||||||
|
|
||||||
this.isActionInProgress.set(true);
|
this.isActionInProgress.set(true);
|
||||||
this.currentAction.set('Dealer zieht Karten...');
|
this.currentAction.set('Dealer zieht Karten...');
|
||||||
|
|
||||||
this.blackjackService.stand(this.currentGameId()!).subscribe({
|
this.blackjackService.stand(this.currentGameId()!).subscribe({
|
||||||
next: (game) => {
|
next: (game) => {
|
||||||
this.updateGameState(game);
|
this.updateGameState(game);
|
||||||
|
@ -154,7 +154,7 @@ export default class BlackjackComponent {
|
||||||
|
|
||||||
onDoubleDown(): void {
|
onDoubleDown(): void {
|
||||||
if (!this.currentGameId() || this.isActionInProgress()) return;
|
if (!this.currentGameId() || this.isActionInProgress()) return;
|
||||||
|
|
||||||
if (this.gameState() !== 'IN_PROGRESS' || this.playerCards().length !== 2) {
|
if (this.gameState() !== 'IN_PROGRESS' || this.playerCards().length !== 2) {
|
||||||
console.log('Cannot double down: game is not in progress or more than 2 cards');
|
console.log('Cannot double down: game is not in progress or more than 2 cards');
|
||||||
return;
|
return;
|
||||||
|
@ -162,7 +162,7 @@ export default class BlackjackComponent {
|
||||||
|
|
||||||
this.isActionInProgress.set(true);
|
this.isActionInProgress.set(true);
|
||||||
this.currentAction.set('Einsatz wird verdoppelt...');
|
this.currentAction.set('Einsatz wird verdoppelt...');
|
||||||
|
|
||||||
this.blackjackService.doubleDown(this.currentGameId()!).subscribe({
|
this.blackjackService.doubleDown(this.currentGameId()!).subscribe({
|
||||||
next: (game) => {
|
next: (game) => {
|
||||||
this.updateGameState(game);
|
this.updateGameState(game);
|
||||||
|
@ -181,20 +181,19 @@ export default class BlackjackComponent {
|
||||||
this.showGameResult.set(false);
|
this.showGameResult.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleGameError(error: any): void {
|
private handleGameError(error: HttpErrorResponse): void {
|
||||||
if (error instanceof HttpErrorResponse) {
|
if (error instanceof HttpErrorResponse) {
|
||||||
if (error.status === 400 && error.error?.error === 'Invalid state') {
|
if (error.status === 400 && error.error?.error === 'Invalid state') {
|
||||||
this.gameInProgress.set(false);
|
this.gameInProgress.set(false);
|
||||||
|
|
||||||
this.refreshUserBalance();
|
this.refreshUserBalance();
|
||||||
}
|
} else if (error.status === 500) {
|
||||||
else if (error.status === 500) {
|
|
||||||
console.log('Server error occurred. The game may have been updated in another session.');
|
console.log('Server error occurred. The game may have been updated in another session.');
|
||||||
|
|
||||||
this.gameInProgress.set(false);
|
this.gameInProgress.set(false);
|
||||||
|
|
||||||
this.refreshUserBalance();
|
this.refreshUserBalance();
|
||||||
|
|
||||||
if (this.currentGameId()) {
|
if (this.currentGameId()) {
|
||||||
this.refreshGameState(this.currentGameId()!);
|
this.refreshGameState(this.currentGameId()!);
|
||||||
}
|
}
|
||||||
|
@ -209,7 +208,7 @@ export default class BlackjackComponent {
|
||||||
},
|
},
|
||||||
error: (err) => {
|
error: (err) => {
|
||||||
console.error('Failed to refresh game state:', err);
|
console.error('Failed to refresh game state:', err);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,30 +35,30 @@ import { Card } from '../../models/blackjack.model';
|
||||||
export class DealerHandComponent implements OnChanges {
|
export class DealerHandComponent implements OnChanges {
|
||||||
@Input() cards: Card[] = [];
|
@Input() cards: Card[] = [];
|
||||||
cardsWithState: (Card & { isNew: boolean; id: string })[] = [];
|
cardsWithState: (Card & { isNew: boolean; id: string })[] = [];
|
||||||
|
|
||||||
private lastCardCount = 0;
|
private lastCardCount = 0;
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (changes['cards']) {
|
if (changes['cards']) {
|
||||||
this.updateCardsWithState();
|
this.updateCardsWithState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateCardsWithState(): void {
|
private updateCardsWithState(): void {
|
||||||
const newCards = this.cards.length > this.lastCardCount;
|
const newCards = this.cards.length > this.lastCardCount;
|
||||||
|
|
||||||
this.cardsWithState = this.cards.map((card, index) => {
|
this.cardsWithState = this.cards.map((card, index) => {
|
||||||
// Consider a card new if it's added after the initial state and is the latest card
|
// Consider a card new if it's added after the initial state and is the latest card
|
||||||
const isNew = newCards && index >= this.lastCardCount;
|
const isNew = newCards && index >= this.lastCardCount;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...card,
|
...card,
|
||||||
isNew,
|
isNew,
|
||||||
// Generate a unique ID to help Angular track the cards
|
// Generate a unique ID to help Angular track the cards
|
||||||
id: `${card.suit}-${card.rank}-${index}`
|
id: `${card.suit}-${card.rank}-${index}`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lastCardCount = this.cards.length;
|
this.lastCardCount = this.cards.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@ import { Card } from '../../models/blackjack.model';
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="flex justify-center text-lg mb-5">
|
<div class="flex justify-center text-lg mb-5">
|
||||||
<div class="card p-4">
|
<div class="card p-4">
|
||||||
<div class="text-emerald font-bold mb-1">Deine Punkte: {{ calculateHandValue(playerCards) }}</div>
|
<div class="text-emerald font-bold mb-1">
|
||||||
|
Deine Punkte: {{ calculateHandValue(playerCards) }}
|
||||||
|
</div>
|
||||||
<div class="text-text-secondary">
|
<div class="text-text-secondary">
|
||||||
Status: <span [class]="getStatusClass(gameState)">{{ getStatusText(gameState) }}</span>
|
Status: <span [class]="getStatusClass(gameState)">{{ getStatusText(gameState) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +28,9 @@ import { Card } from '../../models/blackjack.model';
|
||||||
<span [class.invisible]="isActionInProgress">Ziehen</span>
|
<span [class.invisible]="isActionInProgress">Ziehen</span>
|
||||||
@if (isActionInProgress) {
|
@if (isActionInProgress) {
|
||||||
<div class="absolute inset-0 flex items-center justify-center">
|
<div class="absolute inset-0 flex items-center justify-center">
|
||||||
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
|
<div
|
||||||
|
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
|
@ -39,7 +43,9 @@ import { Card } from '../../models/blackjack.model';
|
||||||
<span [class.invisible]="isActionInProgress">Halten</span>
|
<span [class.invisible]="isActionInProgress">Halten</span>
|
||||||
@if (isActionInProgress) {
|
@if (isActionInProgress) {
|
||||||
<div class="absolute inset-0 flex items-center justify-center">
|
<div class="absolute inset-0 flex items-center justify-center">
|
||||||
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
|
<div
|
||||||
|
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
|
@ -52,7 +58,9 @@ import { Card } from '../../models/blackjack.model';
|
||||||
<span [class.invisible]="isActionInProgress">Verdoppeln</span>
|
<span [class.invisible]="isActionInProgress">Verdoppeln</span>
|
||||||
@if (isActionInProgress) {
|
@if (isActionInProgress) {
|
||||||
<div class="absolute inset-0 flex items-center justify-center">
|
<div class="absolute inset-0 flex items-center justify-center">
|
||||||
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
|
<div
|
||||||
|
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
|
@ -71,9 +79,9 @@ import { Card } from '../../models/blackjack.model';
|
||||||
})
|
})
|
||||||
export class GameControlsComponent {
|
export class GameControlsComponent {
|
||||||
@Input() playerCards: Card[] = [];
|
@Input() playerCards: Card[] = [];
|
||||||
@Input() gameState: string = 'IN_PROGRESS';
|
@Input() gameState = 'IN_PROGRESS';
|
||||||
@Input() isActionInProgress: boolean = false;
|
@Input() isActionInProgress = false;
|
||||||
|
|
||||||
@Output() hit = new EventEmitter<void>();
|
@Output() hit = new EventEmitter<void>();
|
||||||
@Output() stand = new EventEmitter<void>();
|
@Output() stand = new EventEmitter<void>();
|
||||||
@Output() doubleDown = new EventEmitter<void>();
|
@Output() doubleDown = new EventEmitter<void>();
|
||||||
|
@ -82,7 +90,7 @@ export class GameControlsComponent {
|
||||||
calculateHandValue(cards: Card[]): number {
|
calculateHandValue(cards: Card[]): number {
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
let aceCount = 0;
|
let aceCount = 0;
|
||||||
|
|
||||||
const rankValues: Record<string, number> = {
|
const rankValues: Record<string, number> = {
|
||||||
TWO: 2,
|
TWO: 2,
|
||||||
THREE: 3,
|
THREE: 3,
|
||||||
|
@ -96,9 +104,9 @@ export class GameControlsComponent {
|
||||||
JACK: 10,
|
JACK: 10,
|
||||||
QUEEN: 10,
|
QUEEN: 10,
|
||||||
KING: 10,
|
KING: 10,
|
||||||
ACE: 11
|
ACE: 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const card of cards) {
|
for (const card of cards) {
|
||||||
if (!card.hidden) {
|
if (!card.hidden) {
|
||||||
const value = rankValues[card.rank] || 0;
|
const value = rankValues[card.rank] || 0;
|
||||||
|
@ -108,12 +116,12 @@ export class GameControlsComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sum > 21 && aceCount > 0) {
|
while (sum > 21 && aceCount > 0) {
|
||||||
sum -= 10;
|
sum -= 10;
|
||||||
aceCount--;
|
aceCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,9 @@ import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angula
|
||||||
<span [class.invisible]="isActionInProgress">Neues Spiel</span>
|
<span [class.invisible]="isActionInProgress">Neues Spiel</span>
|
||||||
@if (isActionInProgress) {
|
@if (isActionInProgress) {
|
||||||
<div class="absolute inset-0 flex items-center justify-center">
|
<div class="absolute inset-0 flex items-center justify-center">
|
||||||
<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
|
<div
|
||||||
|
class="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -8,9 +8,8 @@ describe('GameResultComponent', () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [GameResultComponent]
|
imports: [GameResultComponent],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(GameResultComponent);
|
fixture = TestBed.createComponent(GameResultComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
|
|
@ -7,55 +7,49 @@ import { animate, style, transition, trigger } from '@angular/animations';
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, CurrencyPipe],
|
imports: [CommonModule, CurrencyPipe],
|
||||||
template: `
|
template: `
|
||||||
<div
|
<div *ngIf="visible" [@fadeInOut] class="modal-bg" style="z-index: 1000; position: fixed;">
|
||||||
*ngIf="visible"
|
<div class="modal-card" [@cardAnimation]>
|
||||||
[@fadeInOut]
|
|
||||||
class="modal-bg"
|
|
||||||
style="z-index: 1000; position: fixed;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="modal-card"
|
|
||||||
[@cardAnimation]
|
|
||||||
>
|
|
||||||
<h2 class="modal-heading" [class]="getResultClass()">{{ getResultTitle() }}</h2>
|
<h2 class="modal-heading" [class]="getResultClass()">{{ getResultTitle() }}</h2>
|
||||||
<p class="py-2 text-text-secondary mb-4">{{ getResultMessage() }}</p>
|
<p class="py-2 text-text-secondary mb-4">{{ getResultMessage() }}</p>
|
||||||
|
|
||||||
<div class="bg-deep-blue-light/50 rounded-lg p-5 mb-6 shadow-inner border border-deep-blue-light/30">
|
<div
|
||||||
|
class="bg-deep-blue-light/50 rounded-lg p-5 mb-6 shadow-inner border border-deep-blue-light/30"
|
||||||
|
>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div class="text-text-secondary">Einsatz:</div>
|
<div class="text-text-secondary">Einsatz:</div>
|
||||||
<div class="font-medium text-right">{{ amount }} €</div>
|
<div class="font-medium text-right">{{ amount }} €</div>
|
||||||
|
|
||||||
<div class="text-text-secondary">{{ isDraw ? 'Zurückgegeben:' : (isWin ? 'Gewonnen:' : 'Verloren:') }}</div>
|
<div class="text-text-secondary">
|
||||||
<div
|
{{ isDraw ? 'Zurückgegeben:' : isWin ? 'Gewonnen:' : 'Verloren:' }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
class="font-medium text-right"
|
class="font-medium text-right"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'text-emerald': isWin,
|
'text-emerald': isWin,
|
||||||
'text-accent-red': isLoss,
|
'text-accent-red': isLoss,
|
||||||
'text-yellow-400': isDraw
|
'text-yellow-400': isDraw,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ isLoss ? '-' : '+' }}{{ amount }} €
|
{{ isLoss ? '-' : '+' }}{{ amount }} €
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-text-secondary border-t border-text-secondary/20 pt-3 font-medium">Gesamt:</div>
|
<div class="text-text-secondary border-t border-text-secondary/20 pt-3 font-medium">
|
||||||
<div
|
Gesamt:
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
class="font-medium text-right border-t border-text-secondary/20 pt-3"
|
class="font-medium text-right border-t border-text-secondary/20 pt-3"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'text-emerald': isWin,
|
'text-emerald': isWin,
|
||||||
'text-accent-red': isLoss,
|
'text-accent-red': isLoss,
|
||||||
'text-white': isDraw
|
'text-white': isDraw,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ isWin ? '+' : (isLoss ? '-' : '') }}{{ amount }} €
|
{{ isWin ? '+' : isLoss ? '-' : '' }}{{ amount }} €
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button type="button" (click)="closeDialog()" class="button-primary w-full py-2">
|
||||||
type="button"
|
|
||||||
(click)="closeDialog()"
|
|
||||||
class="button-primary w-full py-2"
|
|
||||||
>
|
|
||||||
Verstanden
|
Verstanden
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -67,68 +61,66 @@ import { animate, style, transition, trigger } from '@angular/animations';
|
||||||
trigger('fadeInOut', [
|
trigger('fadeInOut', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({ opacity: 0 }),
|
style({ opacity: 0 }),
|
||||||
animate('150ms ease-out', style({ opacity: 1 }))
|
animate('150ms ease-out', style({ opacity: 1 })),
|
||||||
]),
|
]),
|
||||||
transition(':leave', [
|
transition(':leave', [animate('150ms ease-in', style({ opacity: 0 }))]),
|
||||||
animate('150ms ease-in', style({ opacity: 0 }))
|
|
||||||
])
|
|
||||||
]),
|
]),
|
||||||
trigger('cardAnimation', [
|
trigger('cardAnimation', [
|
||||||
transition(':enter', [
|
transition(':enter', [
|
||||||
style({ opacity: 0, transform: 'scale(0.95)' }),
|
style({ opacity: 0, transform: 'scale(0.95)' }),
|
||||||
animate('200ms ease-out', style({ opacity: 1, transform: 'scale(1)' }))
|
animate('200ms ease-out', style({ opacity: 1, transform: 'scale(1)' })),
|
||||||
])
|
]),
|
||||||
])
|
]),
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
export class GameResultComponent {
|
export class GameResultComponent {
|
||||||
@Input() gameState: string = '';
|
@Input() gameState = '';
|
||||||
@Input() amount: number = 0;
|
@Input() amount = 0;
|
||||||
@Input() set show(value: boolean) {
|
@Input() set show(value: boolean) {
|
||||||
console.log('GameResultComponent show input changed:', value, 'gameState:', this.gameState);
|
console.log('GameResultComponent show input changed:', value, 'gameState:', this.gameState);
|
||||||
this.visible = value;
|
this.visible = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Output() close = new EventEmitter<void>();
|
@Output() gameResultClosed = new EventEmitter<void>();
|
||||||
|
|
||||||
visible = false;
|
visible = false;
|
||||||
|
|
||||||
get isWin(): boolean {
|
get isWin(): boolean {
|
||||||
return this.gameState === 'PLAYER_WON';
|
return this.gameState === 'PLAYER_WON';
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLoss(): boolean {
|
get isLoss(): boolean {
|
||||||
return this.gameState === 'PLAYER_LOST';
|
return this.gameState === 'PLAYER_LOST';
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDraw(): boolean {
|
get isDraw(): boolean {
|
||||||
return this.gameState === 'DRAW';
|
return this.gameState === 'DRAW';
|
||||||
}
|
}
|
||||||
|
|
||||||
getResultTitle(): string {
|
getResultTitle(): string {
|
||||||
if (this.isWin) return 'Gewonnen!';
|
if (this.isWin) return 'Gewonnen!';
|
||||||
if (this.isLoss) return 'Verloren!';
|
if (this.isLoss) return 'Verloren!';
|
||||||
if (this.isDraw) return 'Unentschieden!';
|
if (this.isDraw) return 'Unentschieden!';
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
getResultMessage(): string {
|
getResultMessage(): string {
|
||||||
if (this.isWin) return 'Glückwunsch! Du hast diese Runde gewonnen.';
|
if (this.isWin) return 'Glückwunsch! Du hast diese Runde gewonnen.';
|
||||||
if (this.isLoss) return 'Schade! Du hast diese Runde verloren.';
|
if (this.isLoss) return 'Schade! Du hast diese Runde verloren.';
|
||||||
if (this.isDraw) return 'Diese Runde endet unentschieden. Dein Einsatz wurde zurückgegeben.';
|
if (this.isDraw) return 'Diese Runde endet unentschieden. Dein Einsatz wurde zurückgegeben.';
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
getResultClass(): string {
|
getResultClass(): string {
|
||||||
if (this.isWin) return 'text-emerald';
|
if (this.isWin) return 'text-emerald';
|
||||||
if (this.isLoss) return 'text-accent-red';
|
if (this.isLoss) return 'text-accent-red';
|
||||||
if (this.isDraw) return 'text-yellow-400';
|
if (this.isDraw) return 'text-yellow-400';
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
closeDialog(): void {
|
closeDialog(): void {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.close.emit();
|
this.gameResultClosed.emit();
|
||||||
console.log('Dialog closed by user');
|
console.log('Dialog closed by user');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,30 +37,30 @@ import { Card } from '../../models/blackjack.model';
|
||||||
export class PlayerHandComponent implements OnChanges {
|
export class PlayerHandComponent implements OnChanges {
|
||||||
@Input() cards: Card[] = [];
|
@Input() cards: Card[] = [];
|
||||||
cardsWithState: (Card & { isNew: boolean; id: string })[] = [];
|
cardsWithState: (Card & { isNew: boolean; id: string })[] = [];
|
||||||
|
|
||||||
private lastCardCount = 0;
|
private lastCardCount = 0;
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (changes['cards']) {
|
if (changes['cards']) {
|
||||||
this.updateCardsWithState();
|
this.updateCardsWithState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateCardsWithState(): void {
|
private updateCardsWithState(): void {
|
||||||
const newCards = this.cards.length > this.lastCardCount;
|
const newCards = this.cards.length > this.lastCardCount;
|
||||||
|
|
||||||
this.cardsWithState = this.cards.map((card, index) => {
|
this.cardsWithState = this.cards.map((card, index) => {
|
||||||
// Consider a card new if it's added after the initial state and is the latest card
|
// Consider a card new if it's added after the initial state and is the latest card
|
||||||
const isNew = newCards && index >= this.lastCardCount;
|
const isNew = newCards && index >= this.lastCardCount;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...card,
|
...card,
|
||||||
isNew,
|
isNew,
|
||||||
// Generate a unique ID to help Angular track the cards
|
// Generate a unique ID to help Angular track the cards
|
||||||
id: `${card.suit}-${card.rank}-${index}`
|
id: `${card.suit}-${card.rank}-${index}`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
this.lastCardCount = this.cards.length;
|
this.lastCardCount = this.cards.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
import { ChangeDetectionStrategy, Component, Input, AfterViewInit, ElementRef, OnChanges, SimpleChanges } from '@angular/core';
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
AfterViewInit,
|
||||||
|
ElementRef,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges,
|
||||||
|
} from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { suitSymbols, Suit } from '../../models/blackjack.model';
|
import { suitSymbols, Suit } from '../../models/blackjack.model';
|
||||||
import { gsap } from 'gsap';
|
import { gsap } from 'gsap';
|
||||||
|
@ -14,7 +22,9 @@ import { gsap } from 'gsap';
|
||||||
[class]="hidden ? 'bg-red-800' : 'bg-white'"
|
[class]="hidden ? 'bg-red-800' : 'bg-white'"
|
||||||
>
|
>
|
||||||
@if (!hidden) {
|
@if (!hidden) {
|
||||||
<span class="text-xl font-bold" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{ getDisplayRank(rank) }}</span>
|
<span class="text-xl font-bold" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{
|
||||||
|
getDisplayRank(rank)
|
||||||
|
}}</span>
|
||||||
}
|
}
|
||||||
@if (!hidden) {
|
@if (!hidden) {
|
||||||
<span
|
<span
|
||||||
|
@ -24,25 +34,29 @@ import { gsap } from 'gsap';
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
@if (!hidden) {
|
@if (!hidden) {
|
||||||
<span class="text-xl font-bold self-end rotate-180" [class]="isRedSuit ? 'text-accent-red' : 'text-black'">{{
|
<span
|
||||||
getDisplayRank(rank)
|
class="text-xl font-bold self-end rotate-180"
|
||||||
}}</span>
|
[class]="isRedSuit ? 'text-accent-red' : 'text-black'"
|
||||||
|
>{{ getDisplayRank(rank) }}</span
|
||||||
|
>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
styles: [`
|
styles: [
|
||||||
.card-element {
|
`
|
||||||
transform-style: preserve-3d;
|
.card-element {
|
||||||
backface-visibility: hidden;
|
transform-style: preserve-3d;
|
||||||
}
|
backface-visibility: hidden;
|
||||||
`],
|
}
|
||||||
|
`,
|
||||||
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class PlayingCardComponent implements AfterViewInit, OnChanges {
|
export class PlayingCardComponent implements AfterViewInit, OnChanges {
|
||||||
@Input({ required: true }) rank!: string;
|
@Input({ required: true }) rank!: string;
|
||||||
@Input({ required: true }) suit!: Suit;
|
@Input({ required: true }) suit!: Suit;
|
||||||
@Input({ required: true }) hidden!: boolean;
|
@Input({ required: true }) hidden!: boolean;
|
||||||
@Input() isNew: boolean = false;
|
@Input() isNew = false;
|
||||||
|
|
||||||
constructor(private elementRef: ElementRef) {}
|
constructor(private elementRef: ElementRef) {}
|
||||||
|
|
||||||
|
@ -66,31 +80,31 @@ export class PlayingCardComponent implements AfterViewInit, OnChanges {
|
||||||
const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
|
const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
|
||||||
gsap.fromTo(
|
gsap.fromTo(
|
||||||
cardElement,
|
cardElement,
|
||||||
{
|
{
|
||||||
y: -100,
|
y: -100,
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
rotation: -10,
|
rotation: -10,
|
||||||
scale: 0.7
|
scale: 0.7,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y: 0,
|
y: 0,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
scale: 1,
|
scale: 1,
|
||||||
duration: 0.5,
|
duration: 0.5,
|
||||||
ease: 'power2.out'
|
ease: 'power2.out',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private animateCardFlip(): void {
|
private animateCardFlip(): void {
|
||||||
const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
|
const cardElement = this.elementRef.nativeElement.querySelector('.card-element');
|
||||||
gsap.to(cardElement, {
|
gsap.to(cardElement, {
|
||||||
rotationY: 180,
|
rotationY: 180,
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
gsap.set(cardElement, { rotationY: 0 });
|
gsap.set(cardElement, { rotationY: 0 });
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue