initial merge of both repos
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
UNO-Angular/node_modules
|
||||
UNO-Angular/package-lock.json
|
||||
UNO-Angular/dist
|
||||
UNO-Backend/node_modules
|
||||
UNO-Backend/package-lock.json
|
||||
UNO-Backend/dist
|
||||
|
||||
16
UNO-Angular/.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
46
UNO-Angular/.gitignore
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
27
UNO-Angular/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Uno
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.0.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
160
UNO-Angular/angular.json
Normal file
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"uno": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/uno",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets/felt.jpg",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||
"src/styles.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/solid.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/regular.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/brands.scss",
|
||||
"node_modules/angular-bootstrap-md/assets/scss/bootstrap/bootstrap.scss",
|
||||
"node_modules/angular-bootstrap-md/assets/scss/mdb.scss",
|
||||
"node_modules/animate.css/animate.css"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/chart.js/dist/Chart.js",
|
||||
"node_modules/hammerjs/hammer.min.js",
|
||||
"node_modules/jquery/dist/jquery.js",
|
||||
"node_modules/bootstrap/dist/js/bootstrap.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "uno:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "uno:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "uno:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets/felt.jpg",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
|
||||
"src/styles.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/solid.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/regular.scss",
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/brands.scss",
|
||||
"node_modules/angular-bootstrap-md/assets/scss/bootstrap/bootstrap.scss",
|
||||
"node_modules/angular-bootstrap-md/assets/scss/mdb.scss",
|
||||
"node_modules/animate.css/animate.css"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/chart.js/dist/Chart.js",
|
||||
"node_modules/hammerjs/hammer.min.js",
|
||||
"node_modules/jquery/dist/jquery.js",
|
||||
"node_modules/bootstrap/dist/js/bootstrap.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "uno:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "uno:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "uno",
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
12
UNO-Angular/browserslist
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
41
UNO-Angular/dockerfile
Normal file
@@ -0,0 +1,41 @@
|
||||
#build angular base build containeer
|
||||
FROM node:12-alpine AS build_base
|
||||
WORKDIR /usr/src/app
|
||||
RUN apk add --no-cache git
|
||||
|
||||
|
||||
#build project specific container containing modules
|
||||
FROM build_base as build_base_project
|
||||
COPY ./package.json /usr/src/app/package.json
|
||||
COPY ./package-lock.json /usr/src/app/package-lock.json
|
||||
|
||||
RUN npm install
|
||||
|
||||
# create build container and build ng project
|
||||
FROM build_base_project as build
|
||||
ARG CI_COMMIT_BRANCH
|
||||
ARG CI_COMMIT_TAG
|
||||
ARG CI_COMMIT_SHA
|
||||
ENV CI_COMMIT_BRANCH=$CI_COMMIT_BRANCH
|
||||
ENV CI_COMMIT_TAG=$CI_COMMIT_TAG
|
||||
ENV CI_COMMIT_SHA=$CI_COMMIT_SHA
|
||||
COPY ./ /usr/src/app
|
||||
RUN npm run build-prod
|
||||
|
||||
#build nginx container
|
||||
FROM nginx
|
||||
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
|
||||
RUN chgrp -R root /var/cache/nginx /var/run /var/log/nginx && \
|
||||
chmod -R 770 /var/cache/nginx /var/run /var/log/nginx
|
||||
|
||||
USER root
|
||||
|
||||
COPY --from=build /usr/src/app/dist/uno /usr/share/nginx/html
|
||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
|
||||
32
UNO-Angular/e2e/protractor.conf.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
23
UNO-Angular/e2e/src/app.e2e-spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { AppPage } from './app.po';
|
||||
import { browser, logging } from 'protractor';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getTitleText()).toEqual('uno app is running!');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
});
|
||||
});
|
||||
11
UNO-Angular/e2e/src/app.po.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo(): Promise<unknown> {
|
||||
return browser.get(browser.baseUrl) as Promise<unknown>;
|
||||
}
|
||||
|
||||
getTitleText(): Promise<string> {
|
||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
||||
}
|
||||
}
|
||||
13
UNO-Angular/e2e/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
32
UNO-Angular/karma.conf.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/uno'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
60
UNO-Angular/nginx.conf
Normal file
@@ -0,0 +1,60 @@
|
||||
user root;
|
||||
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
keepalive_timeout 65;
|
||||
sendfile on;
|
||||
|
||||
server {
|
||||
#security
|
||||
server_tokens off;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header Access-Control-Allow-Origin cdn.dennisgunia.de;
|
||||
add_header X-Robots-Tag none;
|
||||
add_header X-Download-Options noopen;
|
||||
add_header X-Permitted-Cross-Domain-Policies none;
|
||||
add_header Referrer-Policy no-referrer;
|
||||
add_header Cache-Control no-cache;
|
||||
default_type text/html;
|
||||
gzip on;
|
||||
gzip_static on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
expires -1;
|
||||
}
|
||||
|
||||
location /unogame {
|
||||
resolver 127.0.0.11 ipv6=off;
|
||||
#proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_connect_timeout 75s;
|
||||
proxy_pass http://backend:3000;
|
||||
|
||||
}
|
||||
location /stream {
|
||||
resolver 127.0.0.11 ipv6=off;
|
||||
proxy_pass http://backend:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
listen 80;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
67
UNO-Angular/package.json
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"name": "uno",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"build-prod": "ng build --prod --sourceMap"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.0",
|
||||
"@angular/cdk": "^9.2.0",
|
||||
"@angular/common": "~9.1.0",
|
||||
"@angular/compiler": "~9.1.0",
|
||||
"@angular/core": "~9.1.0",
|
||||
"@angular/forms": "~9.1.0",
|
||||
"@angular/localize": "^9.1.0",
|
||||
"@angular/material": "^9.2.0",
|
||||
"@angular/platform-browser": "~9.1.0",
|
||||
"@angular/platform-browser-dynamic": "~9.1.0",
|
||||
"@angular/router": "~9.1.0",
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.0.2",
|
||||
"@stomp/stompjs": "^5.4.4",
|
||||
"@types/chart.js": "^2.9.16",
|
||||
"@types/socket.io": "^2.1.4",
|
||||
"@types/sockjs-client": "^1.1.1",
|
||||
"@types/stompjs": "^2.3.4",
|
||||
"angular-bootstrap-md": "^9.0.0",
|
||||
"animate.css": "^3.7.2",
|
||||
"bootstrap": "^4.4.1",
|
||||
"chart.js": "^2.5.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"jquery": "^3.4.1",
|
||||
"net": "^1.0.2",
|
||||
"rxjs": "~6.5.4",
|
||||
"sockjs-client": "^1.4.0",
|
||||
"stompjs": "^2.3.3",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.0",
|
||||
"@angular/cli": "~9.1.0",
|
||||
"@angular/compiler-cli": "~9.1.0",
|
||||
"@angular/language-service": "~9.1.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~4.4.1",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~3.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.3",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.8.3"
|
||||
}
|
||||
}
|
||||
18
UNO-Angular/proxy.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"/unogame": {
|
||||
"target": {
|
||||
"host": "127.0.0.1",
|
||||
"protocol": "http:",
|
||||
"port": 3000
|
||||
},
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "info"
|
||||
},
|
||||
"/stream/*": {
|
||||
"target": "http://127.0.0.1:3000",
|
||||
"secure": false,
|
||||
"ws": true
|
||||
}
|
||||
|
||||
}
|
||||
23
UNO-Angular/src/app/app-routing.module.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { StartComponent } from './start/start.component';
|
||||
import { GameComponent } from './game/game.component';
|
||||
import { BrowserComponent } from './browser/browser.component';
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'start', component: StartComponent },
|
||||
{ path: 'game', component: GameComponent },
|
||||
{ path: 'browser', component: BrowserComponent },
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/start',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
7
UNO-Angular/src/app/app.component.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<nav class="navbar navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#"><b>UNO.DENNISGUNIA.DE</b> - Version 1.0</a>
|
||||
</nav>
|
||||
|
||||
<router-outlet #outlet="outlet"></router-outlet>
|
||||
|
||||
|
||||
3
UNO-Angular/src/app/app.component.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.navbar{
|
||||
opacity: 0.7;
|
||||
}
|
||||
75
UNO-Angular/src/app/app.component.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { trigger, transition, style, query, animateChild, animate, group } from '@angular/animations';
|
||||
|
||||
|
||||
|
||||
|
||||
const slideInAnimation =
|
||||
trigger('routeAnimations', [
|
||||
transition('HomePage <=> AboutPage', [
|
||||
style({ position: 'relative' }),
|
||||
query(':enter, :leave', [
|
||||
style({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%'
|
||||
})
|
||||
]),
|
||||
query(':enter', [
|
||||
style({ left: '-100%'})
|
||||
]),
|
||||
query(':leave', animateChild()),
|
||||
group([
|
||||
query(':leave', [
|
||||
animate('300ms ease-out', style({ left: '100%'}))
|
||||
]),
|
||||
query(':enter', [
|
||||
animate('300ms ease-out', style({ left: '0%'}))
|
||||
])
|
||||
]),
|
||||
query(':enter', animateChild()),
|
||||
]),
|
||||
transition('* <=> FilterPage', [
|
||||
style({ position: 'relative' }),
|
||||
query(':enter, :leave', [
|
||||
style({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%'
|
||||
})
|
||||
]),
|
||||
query(':enter', [
|
||||
style({ left: '-100%'})
|
||||
]),
|
||||
query(':leave', animateChild()),
|
||||
group([
|
||||
query(':leave', [
|
||||
animate('200ms ease-out', style({ left: '100%'}))
|
||||
]),
|
||||
query(':enter', [
|
||||
animate('300ms ease-out', style({ left: '0%'}))
|
||||
])
|
||||
]),
|
||||
query(':enter', animateChild()),
|
||||
])
|
||||
]);
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
animations: [
|
||||
slideInAnimation
|
||||
// animation triggers go here
|
||||
]
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'uno';
|
||||
prepareRoute(outlet: RouterOutlet) {
|
||||
return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
|
||||
}
|
||||
}
|
||||
38
UNO-Angular/src/app/app.module.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { StartComponent } from './start/start.component';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { MDBBootstrapModule } from 'angular-bootstrap-md';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { GameComponent } from './game/game.component';
|
||||
import { BrowserComponent } from './browser/browser.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
StartComponent,
|
||||
GameComponent,
|
||||
BrowserComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
AppRoutingModule,
|
||||
NgbModule,
|
||||
MDBBootstrapModule.forRoot(),
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
ReactiveFormsModule,
|
||||
|
||||
],
|
||||
providers: [{provide: Window, useValue: window},],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
107
UNO-Angular/src/app/browser/browser.component.html
Normal file
@@ -0,0 +1,107 @@
|
||||
<div>
|
||||
<div class="card lobby-card">
|
||||
<div class="card-header">
|
||||
<h1><b>UNO Sitzungen</b> - {{nick}}</h1>
|
||||
<hr />
|
||||
<button class="btn btn-success btn-sm" (click)="prepareModal()" data-toggle="modal" data-target="#basicExampleModal" style="margin-right: 1rem;"><i class="fas fa-magic mr-1"></i> Sitzung erstellen</button>
|
||||
<button class="btn btn-success btn-sm" style="margin-right: 1rem;"><i class="fas fa-random mr-1"></i> Zufäliger Sitzung beitreten</button>
|
||||
<button class="btn btn-primary btn-sm" (click)="loadLobbies()"><i class="fas fa-sync mr-1"></i> Übersicht Aktualisieren</button>
|
||||
<button class="btn btn-warning btn-sm" routerLink="/start" style="position:absolute; right: 1.25rem;"><i class="fas fa-edit mr-1"></i>Benutzernamen ändern</button>
|
||||
|
||||
</div>
|
||||
<ul class="list-group list-group-flush" *ngIf="lobbies.length > 0">
|
||||
<li class="list-group-item" *ngFor="let lobby of lobbies">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm btn-align" style="font-weight: 500;">
|
||||
{{lobby.properties.serverName}}
|
||||
<span *ngIf="lobby.status">(Im Spiel)</span>
|
||||
<span *ngIf="!lobby.status">(in Lobby)</span>
|
||||
</div>
|
||||
<div class="col-sm btn-align">
|
||||
{{lobby.players}} Spieler
|
||||
</div>
|
||||
<div class="col-sm btn-align">
|
||||
<ul>
|
||||
<li *ngIf="!(lobby.properties.startcards == 7)">{{lobby.properties.startcards}} Karten zu Spielbeginn</li>
|
||||
<li *ngIf="!(lobby.properties.sw_stacking == 7)">Kumulieren</li>
|
||||
<li *ngIf="!(lobby.properties.sw_jumpin == 7)">Jump-In</li>
|
||||
<li *ngIf="!(lobby.properties.sw_double == 7)">Doppeln</li>
|
||||
<li *ngIf="!(lobby.properties.sw_sleep == 7)">Pennen</li>
|
||||
<li *ngIf="!(lobby.properties.sw_seveno == 7)">Seven-O (Sieben-Null)</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<button class="btn btn-success btn-sm" [disabled]="lobby.status" (click)="joinGame(lobby)"><i class="fas fa-caret-square-right mr-1"></i>Spiel Beitreten</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<ul class="list-group list-group-flush" *ngIf="lobbies.length == 0">
|
||||
<li class="list-group-item text-muted" >
|
||||
Es gibt keine aktiven Sitzungen.
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="card-footer text-muted">
|
||||
Programmiert von Dennis Gunia (2020) - Open Source UNO - <a href="https://www.dennisgunia.de">dennisgunia.de</a> - Repo: <a href="https://git.dennis-gunia.de/dennisgunia/UNO-Backend">git.dennis-gunia.de</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="basicExampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Neue Sitzung erstellen</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form [formGroup]="createForm" (ngSubmit)="createLobby()">
|
||||
<div class="modal-body">
|
||||
<label for="sessionname">Sitzungsname</label>
|
||||
<input type="text" id="sessionname" [className]="createForm.controls.serverName.invalid ? ' form-control is-invalid' : 'form-control is-valid'" formControlName="serverName"> <br />
|
||||
<label for="startcards">Karten zu beginn</label>
|
||||
<input type="number" id="startcards" min="2" max="20" [className]="createForm.controls.startcards.invalid ? ' form-control is-invalid' : 'form-control is-valid'" formControlName="startcards"> <br />
|
||||
<label >Sonderregeln</label>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="sw_stacking" formControlName="sw_stacking">
|
||||
<label class="custom-control-label" for="sw_stacking">Kumulieren</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="sw_jumpin" formControlName="sw_jumpin">
|
||||
<label class="custom-control-label" for="sw_jumpin">Jump-In</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="sw_double" formControlName="sw_double">
|
||||
<label class="custom-control-label" for="sw_double">Doppeln</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="sw_sleep" formControlName="sw_sleep">
|
||||
<label class="custom-control-label" for="sw_sleep">Pennen</label>
|
||||
</div>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="sw_seveno" formControlName="sw_seveno">
|
||||
<label class="custom-control-label" for="sw_seveno">Seven-O (Sieben-Null)</label>
|
||||
</div>
|
||||
<a href="https://www.uno-kartenspiel.de/uno-spielvarianten/" target="_blank">Infos zu den Sonderregeln</a>
|
||||
<button id="closeModalButton" [hidden]="true" data-toggle="modal"
|
||||
data-target="#myModal" class="btn btn-default"
|
||||
data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-danger" data-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" type="submit" class="btn btn-success">Sitzung erstellen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
11
UNO-Angular/src/app/browser/browser.component.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
.lobby-card{
|
||||
margin: 0 auto;
|
||||
margin-top: 1rem;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.btn-align {
|
||||
padding: 6px 12px;
|
||||
line-height: 1.42857143;
|
||||
vertical-align: middle;
|
||||
}
|
||||
25
UNO-Angular/src/app/browser/browser.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BrowserComponent } from './browser.component';
|
||||
|
||||
describe('BrowserComponent', () => {
|
||||
let component: BrowserComponent;
|
||||
let fixture: ComponentFixture<BrowserComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ BrowserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BrowserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
105
UNO-Angular/src/app/browser/browser.component.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Router } from '@angular/router';
|
||||
import { FormBuilder,ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
|
||||
|
||||
export interface UnoLobbyProperties {
|
||||
serverName: string;
|
||||
startcards: number;
|
||||
|
||||
}
|
||||
|
||||
export interface UnoLobbyElement {
|
||||
id: string;
|
||||
properties?: UnoLobbyProperties;
|
||||
status?: boolean;
|
||||
players?: number;
|
||||
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-browser',
|
||||
templateUrl: './browser.component.html',
|
||||
styleUrls: ['./browser.component.scss']
|
||||
})
|
||||
export class BrowserComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private router: Router,
|
||||
private formBuilder: FormBuilder,
|
||||
) {
|
||||
this.lobbies = []
|
||||
}
|
||||
|
||||
createForm;
|
||||
nick: string;
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.createForm = this.formBuilder.group({
|
||||
serverName: ['Neue Sitzung', [<any>Validators.required, <any>Validators.minLength(3), <any>Validators.maxLength(20)]],
|
||||
startcards: [7, [<any>Validators.required, <any>Validators.min(2), <any>Validators.max(20)]],
|
||||
sw_stacking: [false, [<any>Validators.required]],
|
||||
sw_jumpin: [false, [<any>Validators.required]],
|
||||
sw_double: [false, [<any>Validators.required]],
|
||||
sw_sleep: [false, [<any>Validators.required]],
|
||||
sw_seveno: [false, [<any>Validators.required]],
|
||||
});
|
||||
|
||||
|
||||
this.nick = localStorage.getItem('username')
|
||||
if (!this.nick){
|
||||
this.router.navigate(['/start']);
|
||||
}
|
||||
this.loadLobbies();
|
||||
}
|
||||
|
||||
lobbies: UnoLobbyElement[];
|
||||
|
||||
loadLobbies(){
|
||||
this.httpClient.get("/unogame/lobbies").subscribe(result => {
|
||||
const cres: any = result;
|
||||
if (cres.success) {
|
||||
console.log(cres.data)
|
||||
this.lobbies = cres.data ? cres.data : [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
prepareModal(){
|
||||
|
||||
}
|
||||
|
||||
createLobby(){
|
||||
|
||||
console.log("create" , this.createForm.value);
|
||||
if(this.createForm.valid){
|
||||
document.getElementById("closeModalButton").click();
|
||||
this.httpClient.post("/unogame/create",this.createForm.value).subscribe(result => {
|
||||
const cres: any = result;
|
||||
if (cres.success) {
|
||||
console.log(cres.data.id)
|
||||
this.joinGame({id: cres.data.id})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
joinGame(lobby: UnoLobbyElement){
|
||||
this.httpClient.post("/unogame/join", {
|
||||
nick: localStorage.getItem('username'),
|
||||
lid: lobby.id
|
||||
}).subscribe(result => {
|
||||
const cres: any = result;
|
||||
if (cres.success) {
|
||||
console.log(cres.data)
|
||||
localStorage.setItem('uid', cres.data.userId);
|
||||
localStorage.setItem('session', lobby.id);
|
||||
this.router.navigate(['/game']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
10
UNO-Angular/src/app/game-state.service.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
|
||||
export class GameStateService {
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
202
UNO-Angular/src/app/game/game.component.html
Normal file
@@ -0,0 +1,202 @@
|
||||
<div class="wrapper fill">
|
||||
<!-- Sidebar -->
|
||||
<nav id="sidebar" class="playerlist">
|
||||
<h1 style="padding:0.5rem;">Hallo, {{thisPlayer.nickname}}</h1>
|
||||
<div class="player card" *ngFor="let player of winnerList; let i = index">
|
||||
<div class="card-body" style="padding: 0px;">
|
||||
<div id="textbox">
|
||||
<p class="alignleft lessspace playername">{{player.nickname}}</p>
|
||||
<p class="alignright lessspace">
|
||||
<span *ngIf="!(player.uno && player.cards == 1)" class="badge badge-warning">{{i + 1}}.</span>
|
||||
</p>
|
||||
</div><br />
|
||||
<div id="textbox" class="lessspace">
|
||||
<p> Spiel beendet </p>
|
||||
</div>
|
||||
<div id="textbox" class="lessspace" style="margin-top:-12px;">
|
||||
<p class="alignleft lessspace ">Runde {{player.rounds}}</p>
|
||||
<p class="alignright lessspace">{{player.score}} Punkte</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr *ngIf="winnerList?.length > 0" />
|
||||
<div class="player card" *ngFor="let player of playerList" [style.background-color]="player.userId == currentPlayer.userId?'#90EE90':(nextPlayerId == player.userId ?'#FFFFE0':'#FFF')">
|
||||
<div class="card-body" style="padding: 0px;">
|
||||
<div id="textbox">
|
||||
<p class="alignleft lessspace playername">{{player.nickname}}</p>
|
||||
<p class="alignright lessspace">
|
||||
<span *ngIf="player.uno && player.cards == 1" class="badge badge-danger">UNO</span>
|
||||
<span *ngIf="!(player.uno && player.cards == 1)" class="badge badge-secondary">{{player.cards}}</span>
|
||||
</p>
|
||||
</div><br />
|
||||
<div id="textbox" class="lessspace">
|
||||
<p>
|
||||
<span>{{player.state}}</span>
|
||||
-
|
||||
<span *ngIf="!gameStarted && !player.ready">In Lobby (Nicht bereit)</span>
|
||||
<span *ngIf="!gameStarted && player.ready">In Lobby (Bereit)</span>
|
||||
<span *ngIf="gameStarted && !(player.userId == currentPlayer.userId)">Wartet</span>
|
||||
<span *ngIf="gameStarted && player.userId == currentPlayer.userId">Am Zug</span>
|
||||
</p>
|
||||
</div>
|
||||
<div id="textbox" class="lessspace" style="margin-top:-12px;">
|
||||
<p class="alignleft lessspace ">Runde {{player.rounds}}</p>
|
||||
<p class="alignright lessspace">{{player.score}} Punkte</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<button mdbBtn type="button" *ngIf="!thisPlayer.ready" rounded="true" mdbWavesEffect class="btn btn_btm btn-success" (click)="ready()">Bereit</button>
|
||||
<button mdbBtn type="button" rounded="true" mdbWavesEffect class="btn btn_btm btn-warning" (click)="leave()">Verlassen</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Page Content -->
|
||||
<div id="content">
|
||||
<!-- We'll fill this with dummy content -->
|
||||
<div class="gamefield">
|
||||
<div class="footer2" [innerHTML]="chatText" #scrollMe [scrollTop]="scrollMe.scrollHeight"></div>
|
||||
<div class="card countdown" *ngIf="timer >= 0">
|
||||
<p><i class="fas fa-stopwatch"></i> {{timer}}</p>
|
||||
</div>
|
||||
|
||||
<div class="card votebox" [@simpleFadeAnimation]="'in'" *ngIf="currentVote">
|
||||
<div class="card-header">
|
||||
{{currentVote.questions[0].question}}
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
|
||||
<li class="list-group-item vote-option" (click)="vote(i)" *ngFor="let opt of currentVote.questions[0].options; let i = index">
|
||||
<div class="progress" [style.width]="((currentVote.questions[0].counter[i] / currentVote.all )*100) + '%'"></div>{{opt}}
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="text-header" *ngIf="gameStarted">
|
||||
<p class="text-header-current" *ngIf="!(currentPlayer.userId == thisPlayer.userId)">{{currentPlayer.nickname}} ist am Zug</p>
|
||||
<p class="text-header-current" *ngIf="currentPlayer.userId == thisPlayer.userId">Du bist am Zug</p>
|
||||
<p class="text-header-detail" *ngIf="topCard.color=='blue'">Aktuell wird <span style="color:#0095da">Blau</span> gespielt</p>
|
||||
<p class="text-header-detail" *ngIf="topCard.color=='green'">Aktuell wird <span style="color:#00ab67">Grün</span> gespielt</p>
|
||||
<p class="text-header-detail" *ngIf="topCard.color=='red'">Aktuell wird <span style="color:#ed1c24">Rot</span> gespielt</p>
|
||||
<p class="text-header-detail" *ngIf="topCard.color=='yellow'">Aktuell wird <span style="color:#ffde00">Gelb</span> gespielt</p>
|
||||
<p class="text-header-detail" *ngIf="topCard.color=='wild'">{{currentPlayer.nickname}} sucht sich eine Farbe aus</p>
|
||||
</div>
|
||||
<div class="text-header" *ngIf="!gameStarted">
|
||||
<p class="text-header-current">Warte bis alle Spieler bereit sind</p>
|
||||
</div>
|
||||
<div class="play-field" *ngIf="gameStarted">
|
||||
<div class="cards-center">
|
||||
<button type="button" class="btn uno-card" style="margin-right:8rem;" [style.background]="'url('+ topCard.tx +')'"></button>
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/000.png)'" placement="bottom" ngbTooltip="Karte ziehen" (click)="pull()"></button>
|
||||
<br /><br />
|
||||
<button mdbBtn type="button" rounded="true" mdbWavesEffect class="btn btn-skip btn-warning" (click)="skip()" *ngIf="(currentPlayer.userId == thisPlayer.userId) && pulled">Aussetzen</button>
|
||||
<button mdbBtn type="button" rounded="true" mdbWavesEffect class="btn btn-skip btn-danger" (click)="uno()" *ngIf="(currentPlayer.userId == thisPlayer.userId)">UNO!</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notify-banner" *ngIf="bannerShow && !bannerError" [@simpleFadeAnimation]="'in'">
|
||||
{{bannerText}}
|
||||
</div>
|
||||
<div class="notify-banner" *ngIf="bannerShow && bannerError" style="color:#ed1c24" [@simpleFadeAnimation]="'in'">
|
||||
{{bannerText}}
|
||||
</div>
|
||||
<div class="footer3">
|
||||
<div *ngFor="let card of handCards" [@flyInOut]="'in'">
|
||||
<button *ngIf="!((card.number == 'wild+4') || (card.number == 'wild'))" type="button" class="btn uno-card uno-overlap " [style.background]="'url('+ card.tx +')'" placement="bottom" ngbTooltip="Karte legen" (click)="selectedCard=card;play(card)"></button>
|
||||
<button *ngIf="card.number == 'wild+4'" type="button" class="btn uno-card uno-overlap " [style.background]="'url('+ card.tx +')'" placement="bottom" ngbTooltip="Karte legen" data-toggle="modal" data-target="#modal_plus4" (click)="selectedCard=card"></button>
|
||||
<button *ngIf="card.number == 'wild'" type="button" class="btn uno-card uno-overlap " [style.background]="'url('+ card.tx +')'" placement="bottom" ngbTooltip="Karte legen" data-toggle="modal" data-target="#modal_wild" (click)="selectedCard=card"></button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="modal_plus4" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="cards-center">
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/312.png)'" placement="bottom" ngbTooltip="Blau wünschen" (click)="playwild('wild+4','blue')" data-dismiss="modal"></button>
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/322.png)'" placement="bottom" ngbTooltip="Grün wünschen" (click)="playwild('wild+4','green')" data-dismiss="modal"></button>
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/332.png)'" placement="bottom" ngbTooltip="Rot wünschen" (click)="playwild('wild+4','red')" data-dismiss="modal"></button>
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/342.png)'" placement="bottom" ngbTooltip="Gelb wünschen" (click)="playwild('wild+4','yellow')" data-dismiss="modal"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="modal_wild" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="cards-center">
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/311.png)'" placement="bottom" ngbTooltip="Blau wünschen" (click)="playwild('wild','blue')" data-dismiss="modal"></button>
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/321.png)'" placement="bottom" ngbTooltip="Grün wünschen" (click)="playwild('wild','green')" data-dismiss="modal"></button>
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/331.png)'" placement="bottom" ngbTooltip="Rot wünschen" (click)="playwild('wild','red')" data-dismiss="modal"></button>
|
||||
<button type="button" class="btn uno-card" style="margin-right:0;" [style.background]="'url(/assets/cards/341.png)'" placement="bottom" ngbTooltip="Gelb wünschen" (click)="playwild('wild','yellow')" data-dismiss="modal"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--Rules Modal-->
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary" id="open_rules_modal" data-toggle="modal" data-target="#rulesModal" [hidden]="true">
|
||||
Launch modal
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="rulesModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Aktive Sonderregeln</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="text-align: left;">
|
||||
<span class="rule"><b>Mehr Karten</b> Jeder hat zu Beginn {{options.cards}} statt 7 Karten. </span><br />
|
||||
<span class="rule"><b>Doppeln</b> Hat ein Spieler zwei Karten von der selben Farbe und Zahl auf der Hand, so darf er diese zusammen ablegen. Dies gilt jedoch nicht für die +2- und +4-Karten. Diese müssen weiterhin nacheinander abgelegt werden. </span><br />
|
||||
<span class="rule"><b>Kumulieren</b> Kann ein Spieler, der von eine +2-Karte betroffen ist, selbst mit einer solchen Karte aufwarten, so i8st dies erlaubt und der nächste Spieler nach ihm muss nun 4 Karten ziehen. Hat dieser ebenfalls eine +2-Karte, so ist auch dies zulässig. Gleiches gilt entsprechend für die +4-Karten. Die Karten dürfen allerdings nicht kombiniert werden. </span><br />
|
||||
<span class="rule"><b>Jump-In</b> Wenn ein Spieler exakt die gleiche Karte auf der Hand hat, welche soeben ausgespielt wurde (gleiche Farbe und Zahl), so kann er diese sofort ablegen – auch wenn er nicht am Zug ist. Die Runde geht jedoch bei dem Spieler weiter, welcher regulär an der Reihe gewesen wäre. </span><br />
|
||||
<span class="rule"><b>Pennen</b> Wer seinen Einsatz verpasst, muss eine Strafkarte ziehen. </span><br />
|
||||
<span class="rule"><b>Seven-O (Sieben-Null)</b> Wird eine Null gespielt, so muss jeder Spieler sein komplettes Blatt an den Spielnachbarn in Uhrzeigerrichtugn weitergeben. Wird dagegen eine Sieben ausgespielt, so darf dieser Spieler sein komplettes Blatt mit einem anderen Spieler seiner Wahl tauschen. </span><br />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" data-dismiss="modal">Alles klar!</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--double modal-->
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary" id="open_rdouble_modal" data-toggle="modal" data-target="#modal_double" [hidden]="true">
|
||||
Launch modal
|
||||
</button>
|
||||
|
||||
<div class="modal fade" id="modal_double" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="cards-center">
|
||||
<button type="button" class="btn uno-card double-card" style="margin-right:0;" (click)="playSingle()" [style.background]="'url('+ selectedCard?.tx +')'" placement="bottom" ngbTooltip="Einzelne Karte legen" data-dismiss="modal">1x</button>
|
||||
<button type="button" class="btn uno-card double-card" style="margin-right:0;" (click)="playDouble()" [style.background]="'url('+ selectedCard?.tx +')'" placement="bottom" ngbTooltip="Doppelt legen" data-dismiss="modal">2x</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
264
UNO-Angular/src/app/game/game.component.scss
Normal file
@@ -0,0 +1,264 @@
|
||||
|
||||
|
||||
.playerlist {
|
||||
background-color: rgb(247, 247, 249);
|
||||
border-right: solid 1px rgb(128, 128, 128);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
min-width: 250px;
|
||||
max-width: 250px;
|
||||
background-color: rgba(247, 247, 249,0.3);
|
||||
}
|
||||
|
||||
.fill {
|
||||
top: 46px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.player {
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
|
||||
}
|
||||
|
||||
.alignleft {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.alignright {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.lessspace {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.playername {
|
||||
font-weight:500;
|
||||
}
|
||||
|
||||
.player:hover {
|
||||
background-color: #DCDCDC;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 249px;
|
||||
border-top: solid 1px rgb(128, 128, 128);
|
||||
text-align: center;
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
.footer2 {
|
||||
position: fixed;
|
||||
width: 35%;
|
||||
bottom: 15rem;
|
||||
right: 0;
|
||||
//top: 46px;
|
||||
height: 200px;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
text-shadow: 0px 0px 12px #000000;
|
||||
font-size: 1rem;
|
||||
text-align: right;
|
||||
color: white;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
//opacity: 0.75;
|
||||
}
|
||||
|
||||
|
||||
.footer3 {
|
||||
position: fixed;
|
||||
left: 250px;
|
||||
bottom: 0rem;
|
||||
right: 0;
|
||||
text-align: left;
|
||||
border-top: solid 5px #2e1a12;
|
||||
padding: 0.5rem;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
height: 15rem;
|
||||
background-image: url('/assets/felt.jpg');
|
||||
box-shadow: inset 0 0 10px #000000;
|
||||
}
|
||||
|
||||
.btn_btm {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 0.5rem;
|
||||
margin-top: 0;
|
||||
width: calc(100% - 1rem);
|
||||
}
|
||||
|
||||
.uno-card {
|
||||
height: 150px;
|
||||
width: 100px;
|
||||
float: left;
|
||||
margin-right: 0.5rem;
|
||||
background: white;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 0 10px #000000;
|
||||
}
|
||||
|
||||
.uno-card:hover {
|
||||
transform: scale(1.1); /* (150% zoom - Note: if the zoom is too large, it will go outside of the viewport) */
|
||||
}
|
||||
|
||||
.gamefield {
|
||||
position: fixed;
|
||||
left: 250px;
|
||||
bottom: 15rem;
|
||||
right: 0;
|
||||
text-align: left;
|
||||
padding: 0.5rem;
|
||||
top: 46px;
|
||||
background-image: url('/assets/felt.jpg');
|
||||
box-shadow: inset 0 0 10px #000000;
|
||||
}
|
||||
|
||||
.text-header {
|
||||
left: 250px;
|
||||
top: 46px;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
|
||||
color: snow;
|
||||
text-shadow: 0px 0px 7px #000000;
|
||||
}
|
||||
|
||||
.text-header-current {
|
||||
padding-top: 1.5rem;
|
||||
font-size: 4rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.text-header-detail {
|
||||
padding-top: 1.0rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.play-field {
|
||||
position: fixed;
|
||||
|
||||
left: 250px;
|
||||
top: 180px;
|
||||
right: 0;
|
||||
bottom: 20rem;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.cards-center {
|
||||
display: inline-block;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.btn-skip {
|
||||
margin-top:3rem;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: inline-block;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
|
||||
.notify-banner {
|
||||
position: fixed;
|
||||
left: 250px;
|
||||
bottom: 21rem;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
color: snow;
|
||||
text-shadow: 0px 0px 7px #000000;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.uno-overlap {
|
||||
margin-bottom: -100px;
|
||||
}
|
||||
:host span.chat-line-date {
|
||||
padding-right: 2rem;
|
||||
color:red;
|
||||
}
|
||||
|
||||
.chat-line-msg{
|
||||
|
||||
}
|
||||
|
||||
.votebox {
|
||||
//background-color: rgba($color: red, $alpha: 0.5);
|
||||
position: fixed;
|
||||
left: 260px;
|
||||
bottom: calc(15rem + 10px );
|
||||
//padding: 0.5rem;
|
||||
//color: snow;
|
||||
}
|
||||
|
||||
.vote-option{
|
||||
transition: 0.4s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vote-option:hover{
|
||||
background-color: lightgrey;
|
||||
|
||||
}
|
||||
|
||||
.progress{
|
||||
position:absolute; left:0px; top:0;
|
||||
background-color: rgba($color: red, $alpha: .2);
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
border-radius: 0px;
|
||||
transition: 1s;
|
||||
}
|
||||
.vote-opt-b{
|
||||
position:absolute; right: -27px; top:-18px;
|
||||
}
|
||||
|
||||
.rule {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
position:absolute; left: 10px; top:10px;
|
||||
padding: 0.5rem;
|
||||
padding-bottom: 0;
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
|
||||
.double-card{
|
||||
font-size: 3.5rem;
|
||||
padding: 0;
|
||||
text-shadow: 0px 0px 4px #FFF;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 42em) and (max-width: 64em) {
|
||||
/* min-width 672px / max-width 1024px */
|
||||
/* hier vermuten wir Tabletts */
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 42em) and (max-width: 42em) {
|
||||
/* min-width 672px / max-width 1024px */
|
||||
/* hier vermuten wir Handy */
|
||||
|
||||
}
|
||||
460
UNO-Angular/src/app/game/game.component.ts
Normal file
@@ -0,0 +1,460 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Router } from '@angular/router';
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { webSocket } from "rxjs/webSocket";
|
||||
|
||||
interface UnoCard {
|
||||
id: number;
|
||||
number: string;
|
||||
color: string;
|
||||
tx: string;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-game',
|
||||
templateUrl: './game.component.html',
|
||||
styleUrls: ['./game.component.scss'],
|
||||
animations: [
|
||||
// the fade-in/fade-out animation.
|
||||
trigger('simpleFadeAnimation', [
|
||||
|
||||
// the "in" style determines the "resting" state of the element when it is visible.
|
||||
state('in', style({ opacity: 1 })),
|
||||
|
||||
// fade in when created. this could also be written as transition('void => *')
|
||||
transition(':enter', [
|
||||
style({ opacity: 0 }),
|
||||
animate(150)
|
||||
]),
|
||||
|
||||
// fade out when destroyed. this could also be written as transition('void => *')
|
||||
transition(':leave',
|
||||
animate(300, style({ opacity: 0 })))
|
||||
]),
|
||||
trigger('flyInOut', [
|
||||
state('in', style({ transform: 'translateX(0)' })),
|
||||
transition('void => *', [
|
||||
style({ transform: 'translateX(100%)' }),
|
||||
animate(100)
|
||||
]),
|
||||
transition('* => void', [
|
||||
animate(100, style({ opacity: 0}))
|
||||
])
|
||||
])
|
||||
]
|
||||
})
|
||||
|
||||
export class GameComponent implements OnInit {
|
||||
|
||||
pulled: boolean = false;
|
||||
nextPlayerId: string = '';
|
||||
conn;
|
||||
myLastId: number = -1;
|
||||
|
||||
//new objects
|
||||
playerList: any = [];
|
||||
winnerList: any = [];
|
||||
topCard: any = {};
|
||||
currentPlayer: any = {};
|
||||
lastPlayer: any = {};
|
||||
direction: number;
|
||||
gameStarted: boolean = false;
|
||||
thisPlayer: any = {};
|
||||
handCards: UnoCard[] = [];
|
||||
|
||||
bannerText: string = "test";
|
||||
bannerShow: boolean = false;
|
||||
bannerError: boolean = false;
|
||||
bannerTimer;
|
||||
|
||||
chatText = "";
|
||||
selectedCard: UnoCard;
|
||||
doubleCard: UnoCard;
|
||||
|
||||
currentVote: any = undefined;
|
||||
|
||||
options:any;
|
||||
|
||||
timer: number = -1;
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private router: Router,
|
||||
private titleService: Title,
|
||||
@Inject(Window) private _window: Window
|
||||
) {
|
||||
this.conn = webSocket(`ws${location.origin.replace(/http/,'')}/stream/uno.io`);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
setInterval(() => {
|
||||
this.conn.next({action: 'heartbeat'});
|
||||
console.log("players", this.playerList)
|
||||
}, 4000);
|
||||
this.openSocket();
|
||||
}
|
||||
|
||||
private showBanner(message, type) {
|
||||
if (this.bannerTimer) {
|
||||
clearTimeout(this.bannerTimer);
|
||||
}
|
||||
this.bannerError = type;
|
||||
this.bannerText = message;
|
||||
this.bannerShow = true;
|
||||
|
||||
this.bannerTimer = setTimeout(() => {
|
||||
this.bannerShow = false;
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
private openSocket(){
|
||||
|
||||
|
||||
this.conn.subscribe(
|
||||
msg => this.processMessage(msg), // Called whenever there is a message from the server.
|
||||
err => {
|
||||
console.log(err)
|
||||
this.router.navigate(['/browser'])
|
||||
}, // Called if at any point WebSocket API signals some kind of error.
|
||||
() => this.router.navigate(['/browser']) // Called when connection is closed (for whatever reason).
|
||||
);
|
||||
|
||||
this.conn.next({
|
||||
action: 'register',
|
||||
id: localStorage.getItem('uid'),
|
||||
lobby: localStorage.getItem('session'),
|
||||
});
|
||||
this.conn.next({action: 'initData'});
|
||||
//this.conn.emit('new-message', "RAS@" + localStorage.getItem('uid'));
|
||||
}
|
||||
|
||||
private processMessage(msg){
|
||||
if (msg.notify){ this.showBanner(msg.notify,!msg.warning); return; }
|
||||
if (msg.player){ this.processPlayerList(msg); return; }
|
||||
if (msg.chat){ this.chatText += msg.chat; return; }
|
||||
if (msg.initClientData){this.processInitClient(msg); return; }
|
||||
if (msg.kick){
|
||||
this.conn.complete();
|
||||
this.router.navigate(['/browser']);
|
||||
}
|
||||
if (msg.gameUpdate){this.processGameUpdate(msg); return;}
|
||||
if (msg.hand){this.processHand(msg); return; }
|
||||
if (msg.vote){this.startVote(msg); return; }
|
||||
if (msg.voteres){this.updateVote(msg); return; }
|
||||
if (msg.endvote){this.stopVote(); return; }
|
||||
if (msg.timer || msg.timer == 0){this.updateTimer(msg); return; }
|
||||
if (msg.sound){this.playSound(msg); return; }
|
||||
if (msg.changprop){
|
||||
if (msg.changprop == 'canskip'){
|
||||
this.pulled = true; // kann jetzt uno sagen
|
||||
}
|
||||
}
|
||||
}
|
||||
//new update functions
|
||||
|
||||
private playSound(msg){
|
||||
let audio = new Audio();
|
||||
audio.src = msg.sound;
|
||||
audio.load();
|
||||
audio.play();
|
||||
}
|
||||
|
||||
private updateTimer(msg){
|
||||
if (msg.timer >= 0){
|
||||
let audio = new Audio();
|
||||
audio.src = "../assets/tick.wav";
|
||||
audio.load();
|
||||
audio.play();
|
||||
}
|
||||
this.timer = msg.timer;
|
||||
}
|
||||
|
||||
private startVote(msg){
|
||||
this.currentVote = {
|
||||
...msg,
|
||||
voted: false,
|
||||
all: 0,
|
||||
timer: setTimeout(() => this.stopVote(),msg.time)
|
||||
}
|
||||
|
||||
console.log("startvote",this.currentVote )
|
||||
}
|
||||
|
||||
private stopVote(){
|
||||
clearTimeout(this.currentVote.timer)
|
||||
this.currentVote = undefined;
|
||||
console.log("stopvote",this.currentVote )
|
||||
}
|
||||
|
||||
private updateVote(msg){
|
||||
this.currentVote.questions[0].counter = msg.voteres[0].counter;
|
||||
this.currentVote.all = msg.voteres[0].counter.reduce((a, b) => a + b, 0)
|
||||
console.log("updatevote",this.currentVote )
|
||||
|
||||
}
|
||||
|
||||
public vote(id){
|
||||
console.log("vote")
|
||||
this.conn.next({vote: id + 1});
|
||||
|
||||
}
|
||||
|
||||
private processPlayerList(msg){
|
||||
if(msg.player == 'modify'){
|
||||
let pfound: boolean= false;
|
||||
//search winner
|
||||
this.winnerList.forEach((player, ix) => {
|
||||
if(player.userId == msg.data.userId){
|
||||
pfound = true;
|
||||
this.winnerList[ix] = msg.data;
|
||||
console.log("update winner",msg.data)
|
||||
}
|
||||
});
|
||||
if (!pfound){
|
||||
this.playerList.forEach((player, ix) => {
|
||||
if(player.userId == msg.data.userId){
|
||||
pfound = true;
|
||||
this.playerList[ix] = msg.data;
|
||||
console.log("update player",msg.data)
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!pfound){
|
||||
console.log("add player to list",msg.data )
|
||||
this.playerList.push(msg.data);
|
||||
}
|
||||
}else if(msg.player == 'remove'){
|
||||
this.playerList = this.playerList.filter(el => el.userId != msg.data.userId);
|
||||
console.log("remove player from list",msg.data )
|
||||
}else if(msg.player == 'winners'){
|
||||
this.winnerList = msg.data
|
||||
}else if(msg.player == 'all'){
|
||||
this.playerList = msg.players
|
||||
this.winnerList = msg.winners
|
||||
|
||||
}
|
||||
this.processPlayerUpdate();
|
||||
this.playerChangeEvent();
|
||||
}
|
||||
|
||||
private processHand(msg){
|
||||
if(msg.hand == 'add'){
|
||||
this.handCards.push(msg.card)
|
||||
let audio = new Audio();
|
||||
audio.src = "../assets/cardSlide7.wav";
|
||||
audio.load();
|
||||
audio.play();
|
||||
}else if(msg.hand == 'remove'){
|
||||
this.handCards = this.handCards.filter(el => el.id != msg.card.id);
|
||||
}else if(msg.hand == 'all'){
|
||||
this.handCards = msg.cards;
|
||||
}
|
||||
}
|
||||
|
||||
private processInitClient(msg){
|
||||
this.playerList = msg.initClientData.players;
|
||||
this.winnerList = msg.initClientData.winners;
|
||||
//karten
|
||||
this.topCard = msg.initClientData.cardsPlayed[msg.initClientData.cardsPlayed.length -1 ];
|
||||
for (let pi = 0; pi < this.playerList.length; pi++) {
|
||||
if (this.playerList[pi].userId == msg.initClientData.currentPlayer) {
|
||||
this.currentPlayer = this.playerList[pi];
|
||||
}
|
||||
}
|
||||
this.handCards = msg.initClientData.hand;
|
||||
this.direction = msg.initClientData.direction;
|
||||
this.updateCardColor();
|
||||
this.processPlayerUpdate();
|
||||
this.playerChangeEvent();
|
||||
this.gameStarted = msg.initClientData.started;
|
||||
this.options = msg.initClientData.extraRules;
|
||||
document.getElementById("open_rules_modal").click();
|
||||
|
||||
}
|
||||
|
||||
private processGameUpdate(msg){
|
||||
switch(msg.gameUpdate){
|
||||
case 'playedStack':
|
||||
this.topCard = msg.card[msg.card.length -1 ];
|
||||
this.updateCardColor();
|
||||
let audio = new Audio();
|
||||
audio.src = "../assets/cardPlace1.wav";
|
||||
audio.load();
|
||||
audio.play();
|
||||
break;
|
||||
case 'currentPlayer':
|
||||
for (let pi = 0; pi < this.playerList.length; pi++) {
|
||||
if (this.playerList[pi].userId == msg.currentplayer) {
|
||||
this.currentPlayer = this.playerList[pi];
|
||||
}
|
||||
}
|
||||
this.direction = msg.direction;
|
||||
this.playerChangeEvent();
|
||||
break;
|
||||
case 'status':
|
||||
this.gameStarted = msg.started;
|
||||
this.processPlayerUpdate();
|
||||
this.playerChangeEvent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//helper functions
|
||||
private updateCardColor(){
|
||||
if (this.topCard){
|
||||
if (this.topCard.number == 'wild') {
|
||||
switch (this.topCard.color) {
|
||||
case 'blue': this.topCard.tx = "/assets/cards/311.png"; break;
|
||||
case 'green': this.topCard.tx = "/assets/cards/321.png"; break;
|
||||
case 'red': this.topCard.tx = "/assets/cards/331.png"; break;
|
||||
case 'yellow': this.topCard.tx = "/assets/cards/341.png"; break;
|
||||
}
|
||||
}
|
||||
if (this.topCard.number == 'wild+4') {
|
||||
switch (this.topCard.color) {
|
||||
case 'blue': this.topCard.tx = "/assets/cards/312.png"; break;
|
||||
case 'green': this.topCard.tx = "/assets/cards/322.png"; break;
|
||||
case 'red': this.topCard.tx = "/assets/cards/332.png"; break;
|
||||
case 'yellow': this.topCard.tx = "/assets/cards/342.png"; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private playerChangeEvent(){
|
||||
|
||||
|
||||
if (this.currentPlayer.userId != this.thisPlayer.userId) {
|
||||
this.pulled = false;
|
||||
this.titleService.setTitle("UNO - " + this.thisPlayer.nickname);
|
||||
} else {
|
||||
this.titleService.setTitle("DU BIST DRAN! - UNO - " + this.thisPlayer.nickname);
|
||||
|
||||
}
|
||||
//get next player
|
||||
let index = 0;
|
||||
this.playerList.forEach((obj, ix) => {
|
||||
if (obj.userId == this.currentPlayer.userId) {index = ix;}
|
||||
});
|
||||
let newIndex = index + this.direction;
|
||||
if (newIndex < 0) { newIndex += this.playerList.length }
|
||||
if (newIndex > ( this.playerList.length - 1)) { newIndex -= this.playerList.length }
|
||||
this.nextPlayerId = this.playerList[newIndex].userId;
|
||||
|
||||
if (this.currentPlayer.userId != this.lastPlayer){
|
||||
if (this.currentPlayer.userId == this.thisPlayer.userId){
|
||||
let audio = new Audio();
|
||||
audio.src = "../assets/when.mp3";
|
||||
audio.load();
|
||||
audio.play();
|
||||
}
|
||||
this.lastPlayer = this.currentPlayer.userId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private processPlayerUpdate(){
|
||||
const userId = localStorage.getItem('uid');
|
||||
for (let pi = 0; pi < this.playerList.length; pi++) {
|
||||
if (this.playerList[pi].userId == userId) {
|
||||
this.thisPlayer = this.playerList[pi];
|
||||
}
|
||||
}
|
||||
for (let pi = 0; pi < this.winnerList.length; pi++) {
|
||||
if (this.winnerList[pi].userId == userId) {
|
||||
this.thisPlayer = this.winnerList[pi];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//old
|
||||
|
||||
private comparer(otherArray) {
|
||||
return function (current) {
|
||||
return otherArray.filter(function (other) {
|
||||
return other.value == current.value && other.display == current.display
|
||||
}).length == 0;
|
||||
}
|
||||
}
|
||||
|
||||
private updateHand(newCards) {
|
||||
if (!newCards) { return; } //wenn fertig oder nicht begonnen tue nichts
|
||||
//prüfe differenz
|
||||
|
||||
newCards.forEach(el => {
|
||||
if ((this.handCards.filter(nel => el.id == nel.id)).length == 0) {
|
||||
this.handCards.push(el);
|
||||
}
|
||||
});
|
||||
this.handCards.forEach(el => {
|
||||
if ((newCards.filter(nel => el.id == nel.id)).length == 0) {
|
||||
this.handCards = this.handCards.filter(fel => fel.id != el.id);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public leave() {
|
||||
this.conn.next({action: 'uibutton',button:'leave'});
|
||||
this.conn.complete();
|
||||
}
|
||||
|
||||
public ready() {
|
||||
this.conn.next({action: 'uibutton',button:'ready'});
|
||||
}
|
||||
|
||||
public skip() {
|
||||
this.conn.next({action: 'uibutton',button:'skip'});
|
||||
}
|
||||
|
||||
public uno() {
|
||||
this.conn.next({action: 'uibutton',button:'uno'});
|
||||
}
|
||||
|
||||
public pull() {
|
||||
this.conn.next({action: 'uibutton',button:'pull'});
|
||||
}
|
||||
|
||||
public play(card) {
|
||||
//
|
||||
let doubleFound = -1;
|
||||
this.handCards.forEach(el => {
|
||||
if (el.id != card.id && el.color == card.color && el.number == card.number && card.number != '+2' && card.number != 'wild+4'){
|
||||
doubleFound = el.id;
|
||||
this.doubleCard = el;
|
||||
}
|
||||
})
|
||||
console.log(doubleFound)
|
||||
if (doubleFound > -1){
|
||||
document.getElementById("open_rdouble_modal").click();
|
||||
}else{
|
||||
this.playSingle();
|
||||
}
|
||||
}
|
||||
|
||||
public playSingle() {
|
||||
console.log("play single ", this.selectedCard)
|
||||
this.conn.next({action: 'uibutton',button:'play',card:this.selectedCard});
|
||||
}
|
||||
|
||||
public playDouble(){
|
||||
console.log("play double ", this.selectedCard)
|
||||
this.conn.next({action: 'uibutton',button:'double',card:[this.selectedCard,this.doubleCard]});
|
||||
}
|
||||
|
||||
public playwild(number, color) {
|
||||
this.conn.next({action: 'uibutton',button:'play',card:{
|
||||
id: this.selectedCard.id,
|
||||
number: number,
|
||||
color: color,
|
||||
tx: this.selectedCard.tx,
|
||||
}});
|
||||
}
|
||||
|
||||
}
|
||||
22
UNO-Angular/src/app/start/start.component.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<div class="row h-100">
|
||||
<div class="col-sm-12 h-100 d-table">
|
||||
<div class="card card-block d-table-cell align-middle">
|
||||
<div class="start-box">
|
||||
<h1>Wie heißt du ?</h1>
|
||||
<br />
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text" id="basic-addon1">@</span>
|
||||
</div>
|
||||
<input mdbInput type="text" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="basic-addon1" [(ngModel)]="nick">
|
||||
</div>
|
||||
<span class="error" *ngIf="nick.length > 15">Der Benutzername ist zu lange</span>
|
||||
<span class="error" *ngIf="nick.length < 3">Der Benutzername ist zu kurz</span>
|
||||
<div class="input-group mb-3 ">
|
||||
<button block="true" [disabled]="!buttonEnabled || (nick.length > 15) || (nick.length < 3)" mdbBtn type="button" gradient="peach" rounded="true" mdbWavesEffect class="btn" (click)="start()">Abfahrt!</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
18
UNO-Angular/src/app/start/start.component.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
.start-box {
|
||||
max-width: 500px;
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
font-weight: 500;
|
||||
padding-bottom: 2rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
49
UNO-Angular/src/app/start/start.component.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start',
|
||||
templateUrl: './start.component.html',
|
||||
styleUrls: ['./start.component.scss']
|
||||
})
|
||||
export class StartComponent implements OnInit {
|
||||
|
||||
nick: string = "";
|
||||
buttonEnabled: boolean = true;
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private router: Router
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
let uname = localStorage.getItem('username');
|
||||
if (uname) {
|
||||
this.nick = uname;
|
||||
}
|
||||
}
|
||||
|
||||
public start() {
|
||||
//this.buttonEnabled = false;
|
||||
localStorage.setItem('username', this.nick);
|
||||
this.router.navigate(['/browser']);
|
||||
/*
|
||||
setTimeout(() => {
|
||||
console.log("start " + this.nick);
|
||||
this.httpClient.post("/unogame/join", { nick: this.nick }).subscribe(result => {
|
||||
const cres: any = result;
|
||||
this.buttonEnabled = true;
|
||||
if (cres.success) {
|
||||
console.log(cres.data)
|
||||
localStorage.setItem('uid', cres.data.userId);
|
||||
localStorage.setItem('username', this.nick);
|
||||
this.router.navigate(['/game']);
|
||||
}
|
||||
});
|
||||
}, 200);
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
0
UNO-Angular/src/assets/.gitkeep
Normal file
BIN
UNO-Angular/src/assets/cardPlace1.wav
Normal file
BIN
UNO-Angular/src/assets/cardSlide7.wav
Normal file
BIN
UNO-Angular/src/assets/cards/000.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
UNO-Angular/src/assets/cards/110.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/111.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
UNO-Angular/src/assets/cards/112.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
UNO-Angular/src/assets/cards/113.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/114.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
UNO-Angular/src/assets/cards/115.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
UNO-Angular/src/assets/cards/116.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
UNO-Angular/src/assets/cards/117.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
UNO-Angular/src/assets/cards/118.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/119.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
UNO-Angular/src/assets/cards/120.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/121.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
UNO-Angular/src/assets/cards/122.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
UNO-Angular/src/assets/cards/123.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/124.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
UNO-Angular/src/assets/cards/125.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
UNO-Angular/src/assets/cards/126.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
UNO-Angular/src/assets/cards/127.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
UNO-Angular/src/assets/cards/128.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/129.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
UNO-Angular/src/assets/cards/130.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/131.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
UNO-Angular/src/assets/cards/132.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/133.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/134.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
UNO-Angular/src/assets/cards/135.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/136.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
UNO-Angular/src/assets/cards/137.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
UNO-Angular/src/assets/cards/138.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
UNO-Angular/src/assets/cards/139.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
UNO-Angular/src/assets/cards/140.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
UNO-Angular/src/assets/cards/141.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
UNO-Angular/src/assets/cards/142.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
UNO-Angular/src/assets/cards/143.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
UNO-Angular/src/assets/cards/144.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
UNO-Angular/src/assets/cards/145.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
UNO-Angular/src/assets/cards/146.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/147.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
UNO-Angular/src/assets/cards/148.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
UNO-Angular/src/assets/cards/149.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/211.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
UNO-Angular/src/assets/cards/212.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/213.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
UNO-Angular/src/assets/cards/221.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
UNO-Angular/src/assets/cards/222.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
UNO-Angular/src/assets/cards/223.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
UNO-Angular/src/assets/cards/231.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
UNO-Angular/src/assets/cards/232.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
UNO-Angular/src/assets/cards/233.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
UNO-Angular/src/assets/cards/241.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
UNO-Angular/src/assets/cards/242.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
UNO-Angular/src/assets/cards/243.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
UNO-Angular/src/assets/cards/301.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
UNO-Angular/src/assets/cards/302.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
UNO-Angular/src/assets/cards/311.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
UNO-Angular/src/assets/cards/312.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
UNO-Angular/src/assets/cards/321.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
UNO-Angular/src/assets/cards/322.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
UNO-Angular/src/assets/cards/331.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
UNO-Angular/src/assets/cards/332.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
UNO-Angular/src/assets/cards/341.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
UNO-Angular/src/assets/cards/342.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
UNO-Angular/src/assets/cards/felt.jpg
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
UNO-Angular/src/assets/felt.jpg
Normal file
|
After Width: | Height: | Size: 145 KiB |