feat(game): add blackjack game component and routing
Some checks failed
Some checks failed
This commit is contained in:
parent
32aa753452
commit
eb153f4459
12 changed files with 273 additions and 2 deletions
|
@ -16,4 +16,9 @@ export const routes: Routes = [
|
|||
loadComponent: () => import('./feature/home/home.component'),
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
{
|
||||
path: 'game/blackjack',
|
||||
loadComponent: () => import('./feature/game/blackjack/blackjack.component'),
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<app-navbar></app-navbar>
|
||||
|
||||
<div class="container mx-auto px-4 py-6 space-y-8">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
<div class="lg:col-span-3 space-y-6 flex flex-col gap-4">
|
||||
<app-dealer-hand [cards]="dealerCards"></app-dealer-hand>
|
||||
<app-player-hand [cards]="playerCards"></app-player-hand>
|
||||
<app-game-controls
|
||||
(onHit)="onHit()"
|
||||
(onStand)="onStand()"
|
||||
(onLeave)="leaveGame()"
|
||||
></app-game-controls>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-1 space-y-6">
|
||||
<app-game-info
|
||||
[balance]="balance()"
|
||||
[currentBet]="currentBet"
|
||||
(onNewGameClick)="onNewGame()"
|
||||
></app-game-info>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,63 @@
|
|||
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NavbarComponent } from '../../../shared/components/navbar/navbar.component';
|
||||
import { Router } from '@angular/router';
|
||||
import { UserService } from '../../../service/user.service';
|
||||
import { PlayingCardComponent } from './components/playing-card/playing-card.component';
|
||||
import { DealerHandComponent } from './components/dealer-hand/dealer-hand.component';
|
||||
import { PlayerHandComponent } from './components/player-hand/player-hand.component';
|
||||
import { GameControlsComponent } from './components/game-controls/game-controls.component';
|
||||
import { GameInfoComponent } from './components/game-info/game-info.component';
|
||||
import { Card } from './models/card.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-blackjack',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
NavbarComponent,
|
||||
PlayingCardComponent,
|
||||
DealerHandComponent,
|
||||
PlayerHandComponent,
|
||||
GameControlsComponent,
|
||||
GameInfoComponent,
|
||||
],
|
||||
templateUrl: './blackjack.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export default class BlackjackComponent {
|
||||
private router = inject(Router);
|
||||
private userService = inject(UserService);
|
||||
|
||||
dealerCards: Card[] = [
|
||||
{ value: '2', suit: '♥', hidden: false },
|
||||
{ value: '3', suit: '♦', hidden: false },
|
||||
{ value: 'B', suit: '□', hidden: true },
|
||||
];
|
||||
|
||||
playerCards: Card[] = [];
|
||||
currentBet = 0;
|
||||
balance = signal(0);
|
||||
|
||||
constructor() {
|
||||
this.userService.getCurrentUser().subscribe((user) => {
|
||||
this.balance.set(user?.balance ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
onHit(): void {
|
||||
// Implementation for hit action
|
||||
}
|
||||
|
||||
onStand(): void {
|
||||
// Implementation for stand action
|
||||
}
|
||||
|
||||
leaveGame(): void {
|
||||
this.router.navigate(['/home']);
|
||||
}
|
||||
|
||||
onNewGame(): void {
|
||||
// Implementation for new game
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { PlayingCardComponent } from '../playing-card/playing-card.component';
|
||||
import { Card } from '../../models/card.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dealer-hand',
|
||||
standalone: true,
|
||||
imports: [CommonModule, PlayingCardComponent],
|
||||
template: `
|
||||
<div class="space-y-4">
|
||||
<h3 class="section-heading text-2xl mb-4">Croupier's Karten</h3>
|
||||
<div class="card p-6 !bg-red-500">
|
||||
<div class="flex justify-center gap-4 min-h-[160px] p-4 border-2 border-red-400 rounded-lg">
|
||||
<app-playing-card
|
||||
*ngFor="let card of cards"
|
||||
[value]="card.value"
|
||||
[suit]="card.suit"
|
||||
[hidden]="card.hidden"
|
||||
></app-playing-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DealerHandComponent {
|
||||
@Input() cards: Card[] = [];
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-game-controls',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
template: `
|
||||
<div class="flex justify-center gap-4">
|
||||
<button
|
||||
(click)="onHit.emit()"
|
||||
class="button-primary px-8 py-4 text-lg font-medium min-w-[120px]"
|
||||
>
|
||||
Ziehen
|
||||
</button>
|
||||
<button
|
||||
(click)="onStand.emit()"
|
||||
class="button-primary px-8 py-4 text-lg font-medium min-w-[120px]"
|
||||
>
|
||||
Halten
|
||||
</button>
|
||||
<button
|
||||
(click)="onLeave.emit()"
|
||||
class="bg-accent-red hover:bg-accent-red/80 px-8 py-4 rounded text-lg font-medium min-w-[120px] transition-all duration-300"
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GameControlsComponent {
|
||||
@Output() onHit = new EventEmitter<void>();
|
||||
@Output() onStand = new EventEmitter<void>();
|
||||
@Output() onLeave = new EventEmitter<void>();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { CommonModule, CurrencyPipe } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-game-info',
|
||||
standalone: true,
|
||||
imports: [CommonModule, CurrencyPipe],
|
||||
template: `
|
||||
<div class="card p-4">
|
||||
<h3 class="section-heading text-xl mb-4">Spiel Informationen</h3>
|
||||
<div class="space-y-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-text-secondary">Guthaben:</span>
|
||||
<span class="text-emerald">{{ balance | currency: 'EUR' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-text-secondary">Aktuelle Wette:</span>
|
||||
<span [class]="currentBet > 0 ? 'text-accent-red' : 'text-text-secondary'">
|
||||
{{ currentBet | currency: 'EUR' }}
|
||||
</span>
|
||||
</div>
|
||||
<button class="button-primary w-full py-2" (click)="onNewGameClick.emit()">
|
||||
Neues Spiel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GameInfoComponent {
|
||||
@Input() balance: number = 0;
|
||||
@Input() currentBet: number = 0;
|
||||
@Output() onNewGameClick = new EventEmitter<void>();
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { PlayingCardComponent } from '../playing-card/playing-card.component';
|
||||
import { Card } from '../../models/card.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-player-hand',
|
||||
standalone: true,
|
||||
imports: [CommonModule, PlayingCardComponent],
|
||||
template: `
|
||||
<div class="space-y-4">
|
||||
<h3 class="section-heading text-2xl mb-4">Deine Karten</h3>
|
||||
<div class="card p-6 !bg-green-500">
|
||||
<div
|
||||
class="flex justify-center gap-4 min-h-[160px] p-4 border-2 border-green-400 rounded-lg"
|
||||
>
|
||||
<app-playing-card
|
||||
*ngFor="let card of cards"
|
||||
[value]="card.value"
|
||||
[suit]="card.suit"
|
||||
[hidden]="card.hidden"
|
||||
></app-playing-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PlayerHandComponent {
|
||||
@Input() cards: Card[] = [];
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-playing-card',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
template: `
|
||||
<div
|
||||
class="w-24 h-36 rounded-lg p-2 relative flex flex-col justify-between shadow-lg"
|
||||
[class]="hidden ? 'bg-red-800' : 'bg-white'"
|
||||
>
|
||||
<span *ngIf="!hidden" class="text-xl font-bold text-accent-red">{{ value }}</span>
|
||||
<span
|
||||
*ngIf="!hidden"
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-3xl text-accent-red"
|
||||
>{{ suit }}</span
|
||||
>
|
||||
<span *ngIf="!hidden" class="text-xl font-bold text-accent-red self-end rotate-180">{{
|
||||
value
|
||||
}}</span>
|
||||
</div>
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PlayingCardComponent {
|
||||
@Input({ required: true }) value!: string;
|
||||
@Input({ required: true }) suit!: string;
|
||||
@Input({ required: true }) hidden!: boolean;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface Card {
|
||||
value: string;
|
||||
suit: string;
|
||||
hidden: boolean;
|
||||
}
|
|
@ -34,7 +34,9 @@
|
|||
class="absolute bottom-4 left-4 right-4 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300"
|
||||
>
|
||||
<h4 class="game-heading">{{ game.name }}</h4>
|
||||
<button class="button-primary w-full py-2">Jetzt Spielen</button>
|
||||
<button class="button-primary w-full py-2" (click)="navigateToGame(game.route)">
|
||||
Jetzt Spielen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,7 +61,9 @@
|
|||
class="absolute bottom-4 left-4 right-4 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300"
|
||||
>
|
||||
<h4 class="game-heading">{{ game.name }}</h4>
|
||||
<button class="button-primary w-full py-2">Jetzt Spielen</button>
|
||||
<button class="button-primary w-full py-2" (click)="navigateToGame(game.route)">
|
||||
Jetzt Spielen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -38,31 +38,37 @@ export default class HomeComponent implements OnInit {
|
|||
id: '1',
|
||||
name: 'Poker',
|
||||
image: '/poker.webp',
|
||||
route: '/game/poker',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Blackjack',
|
||||
image: '/blackjack.webp',
|
||||
route: '/game/blackjack',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Slots',
|
||||
image: '/slots.webp',
|
||||
route: '/game/slots',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Plinko',
|
||||
image: '/plinko.webp',
|
||||
route: '/game/plinko',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
name: 'Liars Dice',
|
||||
image: '/liars-dice.webp',
|
||||
route: '/game/liars-dice',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
name: 'Lootboxen',
|
||||
image: '/lootbox.webp',
|
||||
route: '/game/lootbox',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -83,4 +89,8 @@ export default class HomeComponent implements OnInit {
|
|||
closeDepositConfirmationModal() {
|
||||
this.isDepositSuccessful = false;
|
||||
}
|
||||
|
||||
navigateToGame(route: string) {
|
||||
this.router.navigate([route]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,5 @@ export interface Game {
|
|||
id: string;
|
||||
name: string;
|
||||
image: string;
|
||||
route: string;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue