diff --git a/src/app/home.component.html b/src/app/home.component.html index 53d75dc..56de60d 100644 --- a/src/app/home.component.html +++ b/src/app/home.component.html @@ -8,12 +8,12 @@ class="hide-split-gutter" > -
+
- Test your command line skills
+ Test your command line skills.
You have 60 seconds to enter commands from
@@ -52,16 +52,24 @@ }} for another try!
-
- -
-
+
+
+ +
Hold SPACE for stats.
+
+ Press ENTER to continue. +
diff --git a/src/app/home.component.ts b/src/app/home.component.ts index c94d04a..0e87b7a 100644 --- a/src/app/home.component.ts +++ b/src/app/home.component.ts @@ -1,7 +1,9 @@ import { Component, HostListener, OnInit, ViewChild } from '@angular/core'; import { BashbrawlterminalComponent, - Leaderboard, + GameFinished, + LeaderboardWithLocalPlacement, + Score, } from './terminals/bashbrawl/bashbrawlterminal.component'; import { LanguageCommandService } from './terminals/bashbrawl/languages/language-command.service'; import { @@ -31,6 +33,7 @@ export class Cooldown { width: '10vw', minWidth: '0', height: '10vh', + opacity: 0, }), ), state( @@ -65,6 +68,13 @@ export class Cooldown { maxHeight: '0vh', }), ), + state( + 'hidden', + style({ + opacity: 0, + maxHeight: '0vh', + }), + ), transition('normal <=> shrunk', animate('1000ms ease-in-out')), ]), ], @@ -72,6 +82,7 @@ export class Cooldown { export class HomeComponent implements OnInit { terminalState = 'hidden'; shrinkState = 'normal'; + leaderboardState = 'normal'; badgeScanningMode = false; gameStarted = false; @@ -82,9 +93,16 @@ export class HomeComponent implements OnInit { cooldown = false; cooldownTime = ''; + winningScreen: boolean; + leaderboardWithLocalPlacement: LeaderboardWithLocalPlacement; + score: Score; + @ViewChild('terminal', { static: false }) private terms: BashbrawlterminalComponent; + @ViewChild('instructions') + private instructions: HTMLElement; + constructor( private languageCommandService: LanguageCommandService, private scoreService: ScoreService, @@ -113,6 +131,7 @@ export class HomeComponent implements OnInit { setLargeTerminal() { this.shrinkState = 'shrunk'; + this.leaderboardState = 'hidden'; this.terminalState = 'large'; this.terms.resize(); } @@ -120,16 +139,19 @@ export class HomeComponent implements OnInit { setSmallTerminal() { this.shrinkState = 'normal'; this.terminalState = 'small'; + this.leaderboardState = 'hidden'; } setHiddenTerminal() { this.shrinkState = 'normal'; this.terminalState = 'hidden'; + this.leaderboardState = 'normal'; } resetToDefault() { this.code = ''; this.gameStarted = false; + this.winningScreen = false; if (this.terms) { this.terms.clearTerminal(); @@ -137,6 +159,23 @@ export class HomeComponent implements OnInit { this.setHiddenTerminal(); } + gameEnded(gameFinished: GameFinished) { + this.unfocusTerminal(); + + if (gameFinished && gameFinished.success) { + this.gameStarted = false; + this.winningScreen = true; + this.leaderboardState = 'normal'; + this.shrinkState = 'shrunk'; + this.terminalState = 'hidden'; + this.leaderboardWithLocalPlacement = + gameFinished.leaderboardWithLocalPlacement; + this.score = gameFinished.score; + } else { + this.resetToDefault(); + } + } + onScan(code: string) { this.code = code; this.cooldown = false; @@ -179,15 +218,28 @@ export class HomeComponent implements OnInit { } focusTerminal() { - setTimeout(() => this.terms.focusTerminal(), 0); + if (this.terminalState != 'hidden') { + setTimeout(() => this.terms?.focusTerminal(), 0); + } + } + + unfocusTerminal() { + this.terms?.blurTerminal(); } @HostListener('window:keypress', ['$event']) protected keyEvent(event: KeyboardEvent): void { + console.log(event.key); window.clearTimeout(this.scannerTimeoutId); if (event.code === 'Space') { this.advancedLeaderboard = true; + return; + } + + if (event.key === 'Enter' && this.winningScreen) { + this.resetToDefault(); + return; } if (event.key === 'Enter' && !this.badgeScanningMode) { diff --git a/src/app/leaderboard/embedded/leaderboard-embedded.component.html b/src/app/leaderboard/embedded/leaderboard-embedded.component.html index 3c85b22..a4c7bd1 100644 --- a/src/app/leaderboard/embedded/leaderboard-embedded.component.html +++ b/src/app/leaderboard/embedded/leaderboard-embedded.component.html @@ -13,7 +13,11 @@ - + {{ i + 1 }}. 🥇 🥈 @@ -45,5 +49,52 @@ + + + + ... + ... + ... + + ... + ... + ... + ... + + + + + + + {{ + leaderboardWithLocalPlacement.placement + + i - + getLocalScoreIndex() + + 1 + }}. + + + {{ score.name }} + + + {{ score.score }} + + + + {{ score.x.count }} + + + {{ score.x.maxStreak }} + + + + {{ score.x.avgLength | number:'1.2-2' }} + + + {{ score.x.speed | number:'1.2-2' }}/s + + + +
diff --git a/src/app/leaderboard/embedded/leaderboard-embedded.component.scss b/src/app/leaderboard/embedded/leaderboard-embedded.component.scss index f601c35..f94223a 100644 --- a/src/app/leaderboard/embedded/leaderboard-embedded.component.scss +++ b/src/app/leaderboard/embedded/leaderboard-embedded.component.scss @@ -5,6 +5,7 @@ .leaderboard { color: white; font-family: monospace; + padding-top: 20px; .score { text-align: right; @@ -27,7 +28,11 @@ } .entry-row { - font-size: 24px; + font-size: 2.5vh; + line-height: 2vh; + &.divider { + line-height: 1.5vh; + } td { padding: 10px; a { @@ -35,5 +40,10 @@ font-weight: bold; } } + &.self { + background-color: #74e2cd; + font-weight: bold; + color: #01162a; + } } } diff --git a/src/app/leaderboard/embedded/leaderboard-embedded.component.ts b/src/app/leaderboard/embedded/leaderboard-embedded.component.ts index 6b1f9ea..8df8a56 100644 --- a/src/app/leaderboard/embedded/leaderboard-embedded.component.ts +++ b/src/app/leaderboard/embedded/leaderboard-embedded.component.ts @@ -1,6 +1,10 @@ -import { AfterViewInit, Component, Input } from '@angular/core'; +import { AfterViewInit, Component, Input, OnChanges } from '@angular/core'; import { ScoreService } from '../../services/score.service'; -import { Leaderboard } from '../../terminals/bashbrawl/bashbrawlterminal.component'; +import { + Leaderboard, + LeaderboardWithLocalPlacement, + Score, +} from '../../terminals/bashbrawl/bashbrawlterminal.component'; export class Cooldown { cooldown: string; @@ -11,8 +15,9 @@ export class Cooldown { templateUrl: 'leaderboard-embedded.component.html', styleUrls: ['leaderboard-embedded.component.scss'], }) -export class EmbeddedLeaderboardComponent implements AfterViewInit { +export class EmbeddedLeaderboardComponent implements AfterViewInit, OnChanges { public leaderboard: Leaderboard; + public leaderboardWithLocalPlacement: LeaderboardWithLocalPlacement; @Input() onlyTop = 10; @@ -23,6 +28,15 @@ export class EmbeddedLeaderboardComponent implements AfterViewInit { @Input() advanced = false; + @Input() + showLocalScores: boolean; + + @Input() + score: Score; + + @Input() + leaderboardWithLocalPlacementInput: LeaderboardWithLocalPlacement; + constructor(private scoreService: ScoreService) {} ngAfterViewInit(): void { @@ -36,8 +50,73 @@ export class EmbeddedLeaderboardComponent implements AfterViewInit { }); } + ngOnChanges() { + if (this.leaderboardWithLocalPlacementInput && this.score) { + this.leaderboardWithLocalPlacement = structuredClone( + this.leaderboardWithLocalPlacementInput, + ); + if (this.leaderboardWithLocalPlacement.placement < 10) { + this.onlyTop = this.showLocalScores ? 10 : 5; + this.leaderboardWithLocalPlacement.scores.push(this.score); + this.leaderboardWithLocalPlacement.scores = + this.leaderboardWithLocalPlacement.scores.sort( + (a, b) => b.score - a.score, + ); + } else { + this.leaderboardWithLocalPlacement.localscores.push(this.score); + this.leaderboardWithLocalPlacement.localscores = + this.leaderboardWithLocalPlacement.localscores.sort( + (a, b) => b.score - a.score, + ); + } + + this.leaderboard = { + scores: this.leaderboardWithLocalPlacement.scores, + language: this.leaderboardWithLocalPlacement.language, + } as Leaderboard; + } + } + getScores() { - return this.leaderboard?.scores ?? []; + return this.leaderboard?.scores.slice(0, 10) ?? []; + } + + hasLocalScores() { + if (!this.showLocalScores) { + return false; + } + return this.leaderboardWithLocalPlacement?.localscores.length > 0; + } + + getLocalScores() { + return this.hasLocalScores() + ? this.leaderboardWithLocalPlacement.localscores + : []; + } + + getScoreIndex() { + if ( + !this.leaderboardWithLocalPlacement || + this.leaderboardWithLocalPlacement.placement >= 10 + ) { + return -1; + } + + return this.leaderboardWithLocalPlacement.placement; + } + + getLocalScoreIndex() { + if (this.leaderboardWithLocalPlacement.placement < 10) { + return -1; + } + + const scoreIndex = this.leaderboardWithLocalPlacement.localscores.findIndex( + (localScore) => + this.score.name === localScore.name && + this.score.score === localScore.score, + ); + + return scoreIndex; } getCodeBase64(code: string) { diff --git a/src/app/terminals/bashbrawl/bashbrawlterminal.component.ts b/src/app/terminals/bashbrawl/bashbrawlterminal.component.ts index 4a2bf6e..b9d7a00 100644 --- a/src/app/terminals/bashbrawl/bashbrawlterminal.component.ts +++ b/src/app/terminals/bashbrawl/bashbrawlterminal.component.ts @@ -20,6 +20,7 @@ import { sleep } from '@cds/core/internal'; import { LanguageCommandService } from './languages/language-command.service'; import { ScoreService } from '../../services/score.service'; import { firstValueFrom } from 'rxjs'; +import { scissorsIconName } from '@cds/core/icon'; export class Score { name: string; @@ -45,6 +46,12 @@ export class LeaderboardWithLocalPlacement { localscores: Score[]; } +export class GameFinished { + success: boolean; + leaderboardWithLocalPlacement: LeaderboardWithLocalPlacement; + score: Score; +} + // eslint-disable-next-line no-control-regex const stripAnsi = (str: string) => str.replace(/\x1b\[[0-9;]*m/g, ''); @@ -59,7 +66,7 @@ export class BashbrawlterminalComponent implements OnInit, AfterViewInit { code = ''; @Output() - gameEnded = new EventEmitter(); + gameEnded = new EventEmitter(); @Output() gameStarted = new EventEmitter(); @@ -309,7 +316,7 @@ export class BashbrawlterminalComponent implements OnInit, AfterViewInit { if (this.interrupted) { this.term.write('\r\n'); - this.gameEnded.emit(); + this.gameEnded.emit({ success: false } as GameFinished); return; } @@ -317,7 +324,7 @@ export class BashbrawlterminalComponent implements OnInit, AfterViewInit { this.input_blocked = false; if (this.interrupted) { - this.gameEnded.emit(); + this.gameEnded.emit({ success: false } as GameFinished); return; } @@ -424,14 +431,6 @@ export class BashbrawlterminalComponent implements OnInit, AfterViewInit { this.scoreService.setScoreForLanguage(this.gameLanguage, score), ); - await this.writeDelayed('You scored ' + score.score + '!'); - await this.writeDelayed( - 'Your entered ' + score.x.count + ' unique commands.', - ); - await this.writeDelayed( - 'Your highest Streak was ' + score.x.maxStreak + '.', - ); - if ( leaderboardWithLocalPlacement.placement < 10 && leaderboardWithLocalPlacement.placement > 0 @@ -442,10 +441,17 @@ export class BashbrawlterminalComponent implements OnInit, AfterViewInit { } // Add ANSI Escape Codes to format the name for the leaderboard so the current run shows in bold letters - score.name = '>>\x1b[1;31m ' + score.name + ' \x1b[0m<<'; // \x1b[1;31m makes the text bold (1) and red (31), \x1b[0m clears all effects + //score.name = '>>\x1b[1;31m ' + score.name + ' \x1b[0m<<'; // \x1b[1;31m makes the text bold (1) and red (31), \x1b[0m clears all effects - await this.writeDelayed(`Thank you for playing, ${fullName}!`); - await this.writeDelayed(`Let's view the Leaderboard.`); + this.resetToDefaultShell(); + this.input_blocked = true; + this.gameEnded.emit({ + success: true, + leaderboardWithLocalPlacement: leaderboardWithLocalPlacement, + score: score, + } as GameFinished); + + return; await this.displayLeaderboard( this.gameLanguage, @@ -523,8 +529,7 @@ export class BashbrawlterminalComponent implements OnInit, AfterViewInit { scores.push(['...', '...', '...']); const scoreIndex = leaderboardWithLocalPlacement.localscores.findIndex( (localScore) => - score.name === localScore.name && - localScore.score === localScore.score, + score.name === localScore.name && score.score === localScore.score, ); scores = scores.concat( @@ -987,4 +992,8 @@ export class BashbrawlterminalComponent implements OnInit, AfterViewInit { public focusTerminal() { this.term.focus(); } + + public blurTerminal() { + this.term.blur(); + } } diff --git a/src/app/terminals/bashbrawl/languages/php.ts b/src/app/terminals/bashbrawl/languages/php.ts index f8ac846..d5d5b11 100644 --- a/src/app/terminals/bashbrawl/languages/php.ts +++ b/src/app/terminals/bashbrawl/languages/php.ts @@ -3,7 +3,7 @@ */ import { LanguageConfig } from './language-config.interface'; export const phpConfig: LanguageConfig = { - name: 'php', + name: 'PHP 8', cmds: [ // keywords ['and'],