From 1849500d744879f08d5ece0fec7ca53a6db1be95 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 21 May 2025 13:52:16 +0200 Subject: [PATCH 1/3] feat(dice): enhance game UI and add sound effects --- .../app/feature/game/dice/dice.component.html | 388 +++++++++++++----- .../app/feature/game/dice/dice.component.ts | 44 +- .../shared/directives/drag-sound.directive.ts | 39 ++ .../src/app/shared/services/audio.service.ts | 11 + 4 files changed, 374 insertions(+), 108 deletions(-) create mode 100644 frontend/src/app/shared/directives/drag-sound.directive.ts diff --git a/frontend/src/app/feature/game/dice/dice.component.html b/frontend/src/app/feature/game/dice/dice.component.html index 5cc7a0e..8d1ae51 100644 --- a/frontend/src/app/feature/game/dice/dice.component.html +++ b/frontend/src/app/feature/game/dice/dice.component.html @@ -1,121 +1,305 @@ -
-

Dice Game

+
+

Dice

+
+
+
+
+
+
+

+ Zielwert: + {{ + diceForm.get('targetValue')?.value | number: '1.0-2' + }} +

+
-
-
- -
-
- +
+
+ 0 + 25 + 50 + 75 + 100 +
+ + + +
+
+ + + +
+
+ {{ potentialWin() | currency: 'EUR' : 'symbol' : '1.2-2' }} +
+ + + {{ diceForm.get('rollOver')?.value ? '>' : '<' }} + +
+ + + + @if (rolledValue() !== null) { +
+
+
+ } +
+ +
+ @for (i of [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; track i) { +
+ } +
+
+ + @if ( + hasError('targetValue', 'required') || + hasError('targetValue', 'min') || + hasError('targetValue', 'max') + ) { +
+ @if (hasError('targetValue', 'required')) { + Zielwert ist erforderlich + } + @if (hasError('targetValue', 'min')) { + Zielwert muss mindestens 1 sein + } + @if (hasError('targetValue', 'max')) { + Zielwert darf höchstens 99 sein + } +
+ } +
+ +
+ + +
+
+ + + @if (rolledValue() !== null) { +
+
+ @if (win()) { + + + +

+ Du hast gewonnen! Auszahlung: {{ payout() | currency: 'EUR' : 'symbol' : '1.2-2' }} +

+ } @else { + + + +

Du hast verloren.

+ } +
+
+ } +
+ +
+
+

Spielinformationen

+
+
+ Möglicher Gewinn: + {{ + potentialWin() | currency: 'EUR' : 'symbol' : '1.2-2' + }} +
+ +
+ Gewinnchance: + {{ winChance() | number: '1.0-2' }}% +
+ +
+ + + + +
+ +
+
+ +
@if (hasError('betAmount', 'required')) { - Bet Amount is required + Einsatz ist erforderlich } @if (hasError('betAmount', 'min')) { - Bet Amount must be at least 0.01Einsatz muss mindestens 0.01 sein }
-
-
Roll Mode:
-
- - -
-
+ +
+
-
- - - @if (hasError('targetValue', 'required')) { - Target Value is required - } - @if (hasError('targetValue', 'min')) { - Target Value must be at least 1 - } - @if (hasError('targetValue', 'max')) { - Target Value must be at most 100 - } -
-
- -
-

- Win Chance: {{ winChance() | number: '1.0-2' }}% -

-

- Potential Win: - {{ - potentialWin() | currency: 'EUR' : 'symbol' : '1.2-2' - }} -

-
- - - -
- -
- @if (rolledValue() !== null) { -
- {{ rolledValue() }} -
- } +
+

Spielanleitung

+
    +
  • • Setze deinen Einsatz und Zielwert
  • +
  • • Wähle "Über Zielwert" oder "Unter Zielwert"
  • +
  • • Gewinne, wenn der Würfel zu deinen Gunsten fällt
  • +
  • • Höheres Risiko = höhere Belohnung
  • +
+
- - @if (rolledValue() !== null) { -
- @if (win()) { -

- You Won! Payout: {{ payout() | currency: 'EUR' : 'symbol' : '1.2-2' }} -

- } @else { -

You Lost.

- } -
- }
diff --git a/frontend/src/app/feature/game/dice/dice.component.ts b/frontend/src/app/feature/game/dice/dice.component.ts index fe50cf6..4967b78 100644 --- a/frontend/src/app/feature/game/dice/dice.component.ts +++ b/frontend/src/app/feature/game/dice/dice.component.ts @@ -11,6 +11,9 @@ import { DiceService } from './dice.service'; import { DiceDto, DiceResult } from './dice.model'; import { tap } from 'rxjs/operators'; import { UserService } from '@service/user.service'; +import { PlaySoundDirective } from '@shared/directives/play-sound.directive'; +import { DragSoundDirective } from '@shared/directives/drag-sound.directive'; +import { AudioService } from '@shared/services/audio.service'; type DiceFormGroup = FormGroup<{ betAmount: FormControl; @@ -21,13 +24,14 @@ type DiceFormGroup = FormGroup<{ @Component({ selector: 'app-dice', standalone: true, - imports: [CommonModule, ReactiveFormsModule], + imports: [CommonModule, ReactiveFormsModule, PlaySoundDirective, DragSoundDirective], templateUrl: './dice.component.html', }) export class DiceComponent implements OnInit { private readonly formBuilder = inject(FormBuilder); private readonly diceService = inject(DiceService); private readonly userService = inject(UserService); + private readonly audioService = inject(AudioService); rolledValue = signal(null); win = signal(null); @@ -49,23 +53,23 @@ export class DiceComponent implements OnInit { createDiceForm(): DiceFormGroup { return this.formBuilder.group({ - betAmount: new FormControl(1.0, { - validators: [Validators.required, Validators.min(0.01)], + betAmount: new FormControl(1, { + validators: [Validators.required, Validators.min(1)], nonNullable: true, }), rollOver: new FormControl(true, { validators: [Validators.required], nonNullable: true, }), - targetValue: new FormControl(50.5, { - validators: [Validators.required, Validators.min(1), Validators.max(100)], + targetValue: new FormControl(50, { + validators: [Validators.required, Validators.min(1), Validators.max(99)], nonNullable: true, }), }); } toggleRollMode(): void { - const currentMode = this.diceForm.get('rollOver')?.value; + const currentMode = this.diceForm.get('rollOver')?.value ?? true; this.diceForm.get('rollOver')?.setValue(!currentMode); } @@ -104,6 +108,11 @@ export class DiceComponent implements OnInit { this.rolledValue.set(result.rolledValue); this.win.set(result.win); this.payout.set(result.payout); + + if (result.win) { + this.audioService.playWinSound(); + } + this.userService.refreshCurrentUser(); }, error: (error) => { @@ -112,6 +121,29 @@ export class DiceComponent implements OnInit { }); } + setBetAmount(percentage: number): void { + const user = this.userService['authService'].currentUserValue; + if (!user) return; + + const balance = user.balance || 0; + + const newBet = Math.max(1, Math.floor(balance * percentage * 100) / 100); + + this.diceForm.get('betAmount')?.setValue(newBet); + this.calculateWinChanceAndPotentialWin(); + } + + getTrackGradient(): string { + const targetValue = this.diceForm.get('targetValue')?.value ?? 50; + const isRollOver = this.diceForm.get('rollOver')?.value ?? true; + + if (isRollOver) { + return `linear-gradient(to right, var(--color-accent-red) ${targetValue}%, var(--color-emerald) ${targetValue}%)`; + } else { + return `linear-gradient(to right, var(--color-accent-red) ${targetValue}%, var(--color-emerald) ${targetValue}%)`; + } + } + hasError(controlName: string, errorName: string): boolean { const control = this.diceForm.get(controlName); return control !== null && control.touched && control.hasError(errorName); diff --git a/frontend/src/app/shared/directives/drag-sound.directive.ts b/frontend/src/app/shared/directives/drag-sound.directive.ts new file mode 100644 index 0000000..332cacc --- /dev/null +++ b/frontend/src/app/shared/directives/drag-sound.directive.ts @@ -0,0 +1,39 @@ +import { Directive, ElementRef, HostListener, inject, Input, OnInit } from '@angular/core'; +import { AudioService } from '../services/audio.service'; +import { AbstractControl } from '@angular/forms'; + +@Directive({ + selector: '[appDragSound]', + standalone: true, +}) +export class DragSoundDirective implements OnInit { + private audioService = inject(AudioService); + private elementRef = inject(ElementRef); + private lastValue: number | null = null; + + @Input('appDragSound') formControl: AbstractControl | null = null; + + ngOnInit() { + if (this.formControl) { + this.lastValue = this.formControl.value; + + this.formControl.valueChanges.subscribe((newValue) => { + if (this.lastValue !== newValue) { + this.playSound(); + this.lastValue = newValue; + } + }); + } + } + + private playSound() { + this.audioService.playDragStepSound(); + } + + @HostListener('input') + onInput() { + if (!this.formControl) { + this.playSound(); + } + } +} diff --git a/frontend/src/app/shared/services/audio.service.ts b/frontend/src/app/shared/services/audio.service.ts index 720658e..53850c0 100644 --- a/frontend/src/app/shared/services/audio.service.ts +++ b/frontend/src/app/shared/services/audio.service.ts @@ -27,4 +27,15 @@ export class AudioService { audio.currentTime = 0; audio.play().catch((error) => console.error('Error playing win sound:', error)); } + + getDragSound(): HTMLAudioElement { + return this.getAudio('drag.mp3'); + } + + playDragStepSound(): void { + const audio = this.getAudio('drag.mp3'); + audio.currentTime = 0; + audio.volume = 0.5; + audio.play().catch((error) => console.error('Error playing drag step sound:', error)); + } } -- 2.47.2 From a1997537eb268978caf3dada7dcab25171a97d09 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 21 May 2025 14:02:47 +0200 Subject: [PATCH 2/3] style(dice.component.html): Update layout of game instructions --- .../app/feature/game/dice/dice.component.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/feature/game/dice/dice.component.html b/frontend/src/app/feature/game/dice/dice.component.html index 8d1ae51..68a94ac 100644 --- a/frontend/src/app/feature/game/dice/dice.component.html +++ b/frontend/src/app/feature/game/dice/dice.component.html @@ -288,18 +288,18 @@ > Würfeln + +
+

Spielanleitung

+
    +
  • • Setze deinen Einsatz und Zielwert
  • +
  • • Wähle "Über Zielwert" oder "Unter Zielwert"
  • +
  • • Gewinne, wenn der Würfel zu deinen Gunsten fällt
  • +
  • • Höheres Risiko = höhere Belohnung
  • +
+
- -
-

Spielanleitung

-
    -
  • • Setze deinen Einsatz und Zielwert
  • -
  • • Wähle "Über Zielwert" oder "Unter Zielwert"
  • -
  • • Gewinne, wenn der Würfel zu deinen Gunsten fällt
  • -
  • • Höheres Risiko = höhere Belohnung
  • -
-
-- 2.47.2 From da90a332dc90727d6114d21e6090b5ae55433e81 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 21 May 2025 14:07:03 +0200 Subject: [PATCH 3/3] style(dice.component.html): fix whitespace in HTML file --- frontend/src/app/feature/game/dice/dice.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/feature/game/dice/dice.component.html b/frontend/src/app/feature/game/dice/dice.component.html index 68a94ac..6cf5d12 100644 --- a/frontend/src/app/feature/game/dice/dice.component.html +++ b/frontend/src/app/feature/game/dice/dice.component.html @@ -288,7 +288,7 @@ > Würfeln - +

Spielanleitung

    -- 2.47.2