endgame
This commit is contained in:
parent
6dd57b4025
commit
ab63f62d4e
25 changed files with 501 additions and 159 deletions
150
package-lock.json
generated
150
package-lock.json
generated
|
|
@ -24,7 +24,7 @@
|
|||
"i": "^0.3.7",
|
||||
"jquery": "^3.6.0",
|
||||
"npm": "^10.2.3",
|
||||
"rxjs": "~6.6.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"socket.io-client": "^4.2.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.13.3"
|
||||
|
|
@ -71,15 +71,6 @@
|
|||
"yarn": ">= 1.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/architect/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular": {
|
||||
"version": "16.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.9.tgz",
|
||||
|
|
@ -241,15 +232,6 @@
|
|||
"vite": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-angular/node_modules/vite": {
|
||||
"version": "4.4.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz",
|
||||
|
|
@ -324,15 +306,6 @@
|
|||
"webpack-dev-server": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/build-webpack/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/core": {
|
||||
"version": "16.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.9.tgz",
|
||||
|
|
@ -360,15 +333,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/core/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/schematics": {
|
||||
"version": "16.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.9.tgz",
|
||||
|
|
@ -387,15 +351,6 @@
|
|||
"yarn": ">= 1.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-devkit/schematics/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/animations": {
|
||||
"version": "16.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.12.tgz",
|
||||
|
|
@ -7331,15 +7286,6 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/inquirer/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inquirer/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
@ -13424,21 +13370,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/rxjs": {
|
||||
"version": "6.6.7",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
|
||||
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=2.0.0"
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rxjs/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
|
@ -15627,17 +15566,6 @@
|
|||
"requires": {
|
||||
"@angular-devkit/core": "16.2.9",
|
||||
"rxjs": "7.8.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular-devkit/build-angular": {
|
||||
|
|
@ -15738,15 +15666,6 @@
|
|||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"vite": {
|
||||
"version": "4.4.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz",
|
||||
|
|
@ -15769,17 +15688,6 @@
|
|||
"requires": {
|
||||
"@angular-devkit/architect": "0.1602.9",
|
||||
"rxjs": "7.8.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular-devkit/core": {
|
||||
|
|
@ -15794,17 +15702,6 @@
|
|||
"picomatch": "2.3.1",
|
||||
"rxjs": "7.8.1",
|
||||
"source-map": "0.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular-devkit/schematics": {
|
||||
|
|
@ -15818,17 +15715,6 @@
|
|||
"magic-string": "0.30.1",
|
||||
"ora": "5.4.1",
|
||||
"rxjs": "7.8.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular/animations": {
|
||||
|
|
@ -20871,15 +20757,6 @@
|
|||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
@ -25090,18 +24967,11 @@
|
|||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.6.7",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
|
||||
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
}
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
"i": "^0.3.7",
|
||||
"jquery": "^3.6.0",
|
||||
"npm": "^10.2.3",
|
||||
"rxjs": "~6.6.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"socket.io-client": "^4.2.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.13.3"
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
<button class="btn btn-danger" (click)="resetAllPlayersScore()">Reset score to 0</button>
|
||||
<h4>Game</h4>
|
||||
<button class="btn btn-danger" (click)="clearGameQueue()">Clear queue</button>
|
||||
<button class="btn btn-danger" (click)="simulateEndGamePoints()">Simulate endgame points</button>
|
||||
<h4>Versus</h4>
|
||||
<button class="btn btn-danger" (click)="simulateVersus()">Begin versus</button>
|
||||
<button class="btn btn-danger" (click)="resetAllVersusTasksAsIncompleted()">Mark all as uncompleted</button>
|
||||
<button class="btn btn-danger" disabled>Stop versus</button>
|
||||
<button class="btn btn-danger" disabled>Simulate endgame points</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="game-testing m-2" *ngIf="prodMode">
|
||||
|
|
|
|||
|
|
@ -49,4 +49,8 @@ export class AdminTestingComponent implements OnInit, OnDestroy {
|
|||
clearGameQueue() {
|
||||
this.testingApiService.clearGameQueue().pipe(takeUntil(this.destroyed$)).subscribe((r => console.log(r)));
|
||||
}
|
||||
|
||||
simulateEndGamePoints() {
|
||||
this.testingApiService.simulateEndGamePoints().pipe(takeUntil(this.destroyed$)).subscribe(r => console.log(r));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export class MainActionsComponent implements OnInit {
|
|||
{ title: 'Registration', name: 'register'},
|
||||
{ title: 'Onboarding', name: 'onboarding' },
|
||||
{ title: 'Start quiz', name: 'quiz' },
|
||||
{ title: 'Endgame Points', name: 'endgamepoints' },
|
||||
{ title: 'End', name: 'finish' },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { RegisterComponent } from "./views/register/register.component";
|
|||
import { OnboardingComponent } from "./views/onboarding/onboarding.component";
|
||||
import { InitialComponent } from './views/initial/initial.component';
|
||||
import { FinishComponent } from './views/finish/finish.component';
|
||||
import {EndgamepointsComponent} from "./views/endgamepoints/endgamepoints.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'quiz', component: QuizComponent },
|
||||
|
|
@ -14,6 +15,7 @@ const routes: Routes = [
|
|||
{ path: 'onboarding', component: OnboardingComponent },
|
||||
{ path: 'initial', component: InitialComponent },
|
||||
{ path: 'finish', component: FinishComponent },
|
||||
{ path: 'endgamepoints', component: EndgamepointsComponent },
|
||||
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { FinishComponent } from './views/finish/finish.component';
|
|||
import { InitialComponent } from './views/initial/initial.component';
|
||||
import { SkrepaComponent } from './components/skrepa/skrepa.component';
|
||||
import { VersusComponent } from './components/versus/versus.component';
|
||||
import { EndgamepointsComponent } from './views/endgamepoints/endgamepoints.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
|
@ -52,6 +53,7 @@ import { VersusComponent } from './components/versus/versus.component';
|
|||
InitialComponent,
|
||||
SkrepaComponent,
|
||||
VersusComponent,
|
||||
EndgamepointsComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
|
|
|||
|
|
@ -31,18 +31,24 @@
|
|||
<audio [src]="getAudio(screpaText,2)" autoplay></audio>
|
||||
<app-skrepa [text]="screpaText"></app-skrepa>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col" *ngIf="action.type === gameQueueTypes.showresults">
|
||||
<div *ngIf="results && (results.valid.length > 0 || results.invalid.length > 0)">
|
||||
<h1>Результаты</h1>
|
||||
<h2>Ответили правильно</h2>
|
||||
<div *ngFor="let item of results.valid" class="justify-content-center">
|
||||
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
|
||||
<div class="d-flex flex-row flex-wrap w-100 justify-content-center">
|
||||
<h2 *ngIf="results.valid.length > 0">Ответили правильно</h2>
|
||||
</div>
|
||||
<h2>Не смогли</h2>
|
||||
<div *ngFor="let item of results.invalid" class="justify-content-center">
|
||||
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
|
||||
<div class="d-flex flex-row w-100 justify-content-center">
|
||||
<div *ngFor="let item of results.valid">
|
||||
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-row flex-wrap w-100 justify-content-center">
|
||||
<h2 *ngIf="results.invalid.length > 0">Не смогли</h2>
|
||||
</div>
|
||||
<div class="d-flex flex-row w-100 justify-content-center">
|
||||
<div *ngFor="let item of results.invalid">
|
||||
<app-participant-item [participant]="participants[item.user]" [small]="true"></app-participant-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="!results || (results.valid.length === 0 && results.invalid.length === 0)">
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<div class="card rounded m-3 animate__animated" [ngClass]="{ 'small': small, 'shadow': shadow, 'transparent': transparent, 'banned': banned, 'animate__flipInY': small }">
|
||||
<figure class="p-1">
|
||||
<figure class="p-1" *ngIf="participant">
|
||||
<img [src]="getImageUrl()" class="participant-photo img-fluid">
|
||||
</figure>
|
||||
<div class="card-title">
|
||||
{{ participant?.name }}
|
||||
{{ participant.name }}
|
||||
</div>
|
||||
<div class="content" *ngIf="!small">
|
||||
<div class="card-subtitle">
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export class QuestionComponent implements OnInit, OnDestroy {
|
|||
private questionSubscription: Subscription;
|
||||
countdownInterval:ReturnType<typeof setInterval>|null= null;
|
||||
countdown = 0;
|
||||
readonly countDownTimer =20;
|
||||
readonly countDownTimer = 20;
|
||||
|
||||
|
||||
constructor(private apiService:ApiService, private eventService: EventService, private voiceService: VoiceService) { }
|
||||
|
|
|
|||
|
|
@ -21,4 +21,5 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<audio src="../../../assets/versus/cinematical-epic-loop-190906.mp3" autoplay loop [volume]="0.2"></audio>
|
||||
|
|
@ -13,7 +13,7 @@ import {QuestionresultsDto} from "../../types/questionresults.dto";
|
|||
import {map} from "rxjs/operators";
|
||||
import {VersusItem} from "../../types/versus-item";
|
||||
|
||||
export interface FeatureFlagStateDto {
|
||||
export class FeatureFlagStateDto {
|
||||
name: string;
|
||||
state: boolean;
|
||||
}
|
||||
|
|
@ -34,6 +34,24 @@ export interface ConfigRecordDto {
|
|||
value: string;
|
||||
}
|
||||
|
||||
export interface EndgameResultsDto {
|
||||
maxInvalidAnswers: {
|
||||
id: number;
|
||||
count: number;
|
||||
name: string;
|
||||
},
|
||||
maxRewards: {
|
||||
id: number;
|
||||
count: number;
|
||||
name: string;
|
||||
},
|
||||
maxPenalties: {
|
||||
id: number;
|
||||
count: number;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
|
|
@ -115,7 +133,14 @@ export class ApiService {
|
|||
}
|
||||
|
||||
getQuestionResults() {
|
||||
return this.httpClient.get<QuestionresultsDto[]>(`${API_URL}/quiz/question-results`)
|
||||
return this.httpClient.get<QuestionresultsDto[]>(`${API_URL}/quiz/question-results`).pipe(map((data) =>
|
||||
data.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
time: new Date(item.time)
|
||||
}
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
getFeatureFlagState(feature: string) {
|
||||
|
|
@ -140,4 +165,8 @@ export class ApiService {
|
|||
loser: loser
|
||||
});
|
||||
}
|
||||
|
||||
getEndgameResults() {
|
||||
return this.httpClient.get<EndgameResultsDto>(`${API_URL}/quiz/endgame-results`)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,8 @@ export class TestingApiService {
|
|||
clearGameQueue() {
|
||||
return this.httpClient.post(`${API_URL}/game/clear-queue`, {});
|
||||
}
|
||||
|
||||
simulateEndGamePoints() {
|
||||
return this.httpClient.post(`${API_URL}/quiz/calculate-endgame-extrapoints`, {})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";
|
||||
import { API_URL } from "../../app.constants";
|
||||
import { Subject } from "rxjs";
|
||||
import {Observable, of, Subject} from "rxjs";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
@ -17,6 +17,19 @@ export class VoiceService {
|
|||
this.voiceSubject.next(url);
|
||||
}
|
||||
|
||||
playAudio$(url: string) {
|
||||
this.voiceSubject.next(url);
|
||||
return new Observable((observer) => {
|
||||
const subscription = this.audioEndedSubject.subscribe({
|
||||
next: () => {
|
||||
observer.next(null);
|
||||
observer.complete();
|
||||
}
|
||||
});
|
||||
return () => subscription.unsubscribe();
|
||||
})
|
||||
}
|
||||
|
||||
getAudioUrl(text: string,voice: number = 1) {
|
||||
return `${API_URL}/voice/tts?voice=${voice}&text=${text}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ export class FeatureFlagList {
|
|||
"DontMarkQuestionsAsCompleted",
|
||||
"DisableVoice",
|
||||
"ProdMode",
|
||||
"EndgamePointsUseCssAnimation",
|
||||
];
|
||||
}
|
||||
5
src/app/shared/sharedmethods.ts
Normal file
5
src/app/shared/sharedmethods.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export class SharedMethods {
|
||||
static sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
83
src/app/views/endgamepoints/endgamepoints.component.html
Normal file
83
src/app/views/endgamepoints/endgamepoints.component.html
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<ng-container *ngIf="loaded">
|
||||
<ng-container *ngIf="useCssAnimation">
|
||||
<div class="dark-container">
|
||||
<div class="night">
|
||||
<div *ngFor="let i of [].constructor(25);" class="shooting_star">
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div *ngIf="showInitialText" class="text-announce" @slideOut>
|
||||
Время наградить особо отличившихся!
|
||||
</div>
|
||||
<div *ngIf="showMaxAmountOfInvalidAnswers" class="text-announce" @slideOut>
|
||||
За тупость +2
|
||||
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxInvalidAnswers?.id); let participant;">
|
||||
<div class="d-flex justify-content-center">
|
||||
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngIf="maxAmountOfPenalties" class="text-announce" @slideOut>
|
||||
За тяжелую судьбу +2
|
||||
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxPenalties?.id); let participant;">
|
||||
<div class="d-flex justify-content-center">
|
||||
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngIf="maxAmountOfRewards" class="text-announce" @slideOut>
|
||||
За полный успех -2
|
||||
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxRewards?.id); let participant;">
|
||||
<div class="d-flex justify-content-center">
|
||||
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<ng-container *ngIf="!useCssAnimation">
|
||||
<div class="background-video">
|
||||
<video autoplay muted loop>
|
||||
<source src="../../../assets/endgame/48569-454825064.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
<div class="content">
|
||||
<div *ngIf="showInitialText" class="text-announce" @slideOut>
|
||||
Время наградить особо отличившихся!
|
||||
</div>
|
||||
<div *ngIf="showMaxAmountOfInvalidAnswers" class="text-announce" @slideOut>
|
||||
За тупость <span class="score">+2</span>
|
||||
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxInvalidAnswers?.id); let participant;">
|
||||
<div class="d-flex justify-content-center">
|
||||
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngIf="maxAmountOfPenalties" class="text-announce" @slideOut>
|
||||
За тяжелую судьбу <span class="score">+2</span>
|
||||
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxPenalties?.id); let participant;">
|
||||
<div class="d-flex justify-content-center">
|
||||
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngIf="maxAmountOfRewards" class="text-announce" @slideOut>
|
||||
За полный успех <span class="score">−2</span>
|
||||
<ng-container *ngIf="endgameResults && getParticipant(endgameResults?.maxRewards?.id); let participant;">
|
||||
<div class="d-flex justify-content-center">
|
||||
<app-participant-item [participant]="participant" [small]="true" [transparent]="true" [shadow]="false"></app-participant-item>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<audio src="../../../assets/endgame/energetic-bgm-242515.mp3" autoplay [volume]="0.20">
|
||||
|
||||
</audio>
|
||||
202
src/app/views/endgamepoints/endgamepoints.component.scss
Normal file
202
src/app/views/endgamepoints/endgamepoints.component.scss
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
$shooting-time: 3000ms;
|
||||
|
||||
@keyframes slideInUp {
|
||||
0% {
|
||||
transform: translateY(3000%);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes opacityIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fontSizeTitle {
|
||||
0% {
|
||||
font-size: 1em;
|
||||
}
|
||||
100% {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.background-video {
|
||||
position: relative;
|
||||
height: 100vh; /* Full viewport height */
|
||||
width: 100%; /* Full width */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dark-container {
|
||||
position:relative;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #000220;
|
||||
}
|
||||
.background-video video {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
object-fit: cover; /* Ensures the video covers the container */
|
||||
z-index: -1; /* Places the video behind the content */
|
||||
}
|
||||
|
||||
.content {
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
z-index: 100;
|
||||
text-align: center;
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.text-announce {
|
||||
margin-top: 15%;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
animation: slideInUp 1s forwards, fontSizeTitle 1s forwards, opacityIn 1s forwards;
|
||||
}
|
||||
|
||||
.shooting_star {
|
||||
position: absolute;
|
||||
left: 10%;
|
||||
top: 10%;
|
||||
// width: 100px;
|
||||
height: 2px;
|
||||
background: linear-gradient(-45deg, rgb(243, 227, 6), rgba(0, 0, 255, 0));
|
||||
border-radius: 999px;
|
||||
filter: drop-shadow(0 0 6px rgb(250, 253, 148));
|
||||
animation:
|
||||
tail $shooting-time ease-in-out infinite,
|
||||
shooting $shooting-time ease-in-out infinite;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc(50% - 1px);
|
||||
right: 0;
|
||||
// width: 30px;
|
||||
height: 2px;
|
||||
background: linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0));
|
||||
transform: translateX(50%) rotateZ(45deg);
|
||||
border-radius: 100%;
|
||||
animation: shining $shooting-time ease-in-out infinite;
|
||||
}
|
||||
|
||||
&::after {
|
||||
// CodePen Error
|
||||
// @extend .shooting_star::before;
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc(50% - 1px);
|
||||
right: 0;
|
||||
// width: 30px;
|
||||
height: 2px;
|
||||
background: linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0));
|
||||
transform: translateX(50%) rotateZ(45deg);
|
||||
border-radius: 100%;
|
||||
animation: shining $shooting-time ease-in-out infinite;
|
||||
transform: translateX(50%) rotateZ(-45deg);
|
||||
}
|
||||
|
||||
@for $i from 1 through 20 {
|
||||
&:nth-child(#{$i}) {
|
||||
$delay: random(9999) + 0ms;
|
||||
top: calc(50% - #{random(2000) - 200px});
|
||||
left: calc(50% - #{random(300) + 0px});
|
||||
animation-delay: $delay;
|
||||
opacity: random(50) / 100 + 0.5;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
animation-delay: $delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tail {
|
||||
0% {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
30% {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shining {
|
||||
0% {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shooting {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(300px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sky {
|
||||
0% {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(45 + 360deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes glow {
|
||||
0% {
|
||||
text-shadow: 0 0 2px #f5f5f5, 0 0 10px #f5f5f5, 0 0 20px #ffc14d, 0 0 30px #fff1e0, 0 0 40px #ff4da6, 0 0 50px #ff4da6, 0 0 75px #ff4da6;
|
||||
}
|
||||
100% {
|
||||
text-shadow: 0 0 20px #ffffff, 0 0 20px #ff0080, 0 0 30px #ff0080, 0 0 40px #ff0080, 0 0 50px #ff0080, 0 0 75px #ff0080, 0 0 100px #ff0080;
|
||||
}
|
||||
}
|
||||
.night {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform: rotateZ(45deg);
|
||||
animation: sky 100000ms linear infinite;
|
||||
}
|
||||
|
||||
.score {
|
||||
background-color: #f8b12c;
|
||||
border-radius: 50%;
|
||||
padding: 10px;
|
||||
text-shadow: 0 0 5px #f5f5f5, 0 0 10px #f5f5f5, 0 0 20px #ff4da6, 0 0 30px #ff4da6, 0 0 40px #ff4da6, 0 0 50px #ff4da6, 0 0 75px #ff4da6;
|
||||
animation: glow 2s infinite alternate;
|
||||
}
|
||||
21
src/app/views/endgamepoints/endgamepoints.component.spec.ts
Normal file
21
src/app/views/endgamepoints/endgamepoints.component.spec.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EndgamepointsComponent } from './endgamepoints.component';
|
||||
|
||||
describe('EndgamepointsComponent', () => {
|
||||
let component: EndgamepointsComponent;
|
||||
let fixture: ComponentFixture<EndgamepointsComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [EndgamepointsComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(EndgamepointsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
99
src/app/views/endgamepoints/endgamepoints.component.ts
Normal file
99
src/app/views/endgamepoints/endgamepoints.component.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService, EndgameResultsDto, FeatureFlagStateDto} from "../../services/api.service";
|
||||
import {
|
||||
of,
|
||||
Subject,
|
||||
lastValueFrom,
|
||||
firstValueFrom,
|
||||
mergeWith,
|
||||
combineLatestAll,
|
||||
combineLatest,
|
||||
mergeMap,
|
||||
forkJoin, merge
|
||||
} from "rxjs";
|
||||
import { takeUntil} from "rxjs/operators";
|
||||
import {VoiceService} from "../../services/voice.service";
|
||||
import {animate, keyframes, style, transition, trigger} from "@angular/animations";
|
||||
import {Participant} from "../../../types/participant";
|
||||
import {SharedMethods} from "../../shared/sharedmethods";
|
||||
|
||||
@Component({
|
||||
selector: 'app-endgamepoints',
|
||||
templateUrl: './endgamepoints.component.html',
|
||||
styleUrls: ['./endgamepoints.component.scss'],
|
||||
animations: [
|
||||
trigger('slideOut', [
|
||||
transition(':leave', [
|
||||
animate(
|
||||
'1300ms ease-in',
|
||||
style({ transform: 'translateY(-100%)', opacity: 0 })
|
||||
),
|
||||
]),
|
||||
])]
|
||||
})
|
||||
export class EndgamepointsComponent implements OnInit{
|
||||
loaded = false;
|
||||
useCssAnimation = false;
|
||||
destroyed$ = new Subject();
|
||||
showInitialText = true;
|
||||
showMaxAmountOfInvalidAnswers = false;
|
||||
endgameResults: EndgameResultsDto | null;
|
||||
participants: Participant[] = [];
|
||||
maxAmountOfPenalties = false;
|
||||
maxAmountOfRewards = false;
|
||||
constructor(private apiService: ApiService, private voiceService: VoiceService) {
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
const ff$ = this.apiService.getFeatureFlagState("EndgamePointsUseCssAnimation");
|
||||
// @ts-ignore
|
||||
const results$ = this.apiService.getEndgameResults();
|
||||
combineLatest([results$, ff$])
|
||||
.pipe(takeUntil(this.destroyed$))
|
||||
.subscribe(([results,ff]) => {
|
||||
const userData$ = [];
|
||||
userData$.push(
|
||||
this.apiService.getParticipant(results.maxInvalidAnswers.id),
|
||||
this.apiService.getParticipant(results.maxPenalties.id),
|
||||
this.apiService.getParticipant(results.maxRewards.id),
|
||||
);
|
||||
merge(...userData$).pipe(takeUntil(this.destroyed$)).subscribe((r) => this.participants.push(r));
|
||||
this.useCssAnimation = ff.state;
|
||||
this.endgameResults = results;
|
||||
this.loaded = true;
|
||||
this.playScene().then(() => {});
|
||||
console.log(results);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async playScene() {
|
||||
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl("Время наградить особо отличившихся!")));
|
||||
this.showInitialText = false;
|
||||
await SharedMethods.sleep(1000);
|
||||
this.showMaxAmountOfInvalidAnswers = true;
|
||||
await SharedMethods.sleep(500);
|
||||
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(` За максимальное количество неверных ответов, плюс два очка получает ${this.endgameResults?.maxInvalidAnswers.name}`)));
|
||||
this.showMaxAmountOfInvalidAnswers = false;
|
||||
await SharedMethods.sleep(3000);
|
||||
this.maxAmountOfPenalties = true;
|
||||
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(`За самое большое количество полученных наказаний плюс два очка получил ${this.endgameResults?.maxPenalties.name}`)));
|
||||
await SharedMethods.sleep(3000)
|
||||
this.maxAmountOfPenalties = false;
|
||||
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(`И чтобы сделать игру более справедливой, есть последняя номинация`)));
|
||||
this.maxAmountOfRewards = true;
|
||||
await firstValueFrom(this.voiceService.playAudio$(this.voiceService.getAudioUrl(`${this.endgameResults?.maxRewards.name} лишается двух очков за свой невероятный ум`)));
|
||||
await SharedMethods.sleep(15000);
|
||||
|
||||
}
|
||||
|
||||
|
||||
getParticipant(id: number | undefined): Participant|null {
|
||||
if(id) {
|
||||
const p = this.participants.find(x => x.telegramId === id);
|
||||
return p !== undefined ? p : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -118,7 +118,6 @@ export class OnboardingComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
shakeCard(card: ElementRef) {
|
||||
console.log(`shake card`);
|
||||
this.renderer.addClass(card.nativeElement, 'shake');
|
||||
this.renderer.addClass(card.nativeElement, 'zoom-in');
|
||||
if(!this.allRulesAnnounced) {
|
||||
|
|
@ -142,7 +141,6 @@ export class OnboardingComponent implements OnInit, OnDestroy {
|
|||
|
||||
}
|
||||
stopShaking(card: ElementRef) {
|
||||
console.log(`stop shacking`);
|
||||
this.renderer.removeClass(card.nativeElement, 'shake');
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
src/assets/endgame/1469-147538044.mp4
Normal file
BIN
src/assets/endgame/1469-147538044.mp4
Normal file
Binary file not shown.
BIN
src/assets/endgame/48569-454825064.mp4
Normal file
BIN
src/assets/endgame/48569-454825064.mp4
Normal file
Binary file not shown.
BIN
src/assets/endgame/energetic-bgm-242515.mp3
Normal file
BIN
src/assets/endgame/energetic-bgm-242515.mp3
Normal file
Binary file not shown.
BIN
src/assets/versus/cinematical-epic-loop-190906.mp3
Normal file
BIN
src/assets/versus/cinematical-epic-loop-190906.mp3
Normal file
Binary file not shown.
Loading…
Reference in a new issue