From 692826e635ae8e4da26afc8e7f3d1ed140015eb3 Mon Sep 17 00:00:00 2001 From: Timotej Date: Mon, 2 Apr 2018 20:56:25 +0200 Subject: [PATCH] - Bumped version to 1.0.0 - Added semantic versioning - Added information dialog (which includes update checker) - Added sounds - Points are now calculated based on walls destroyed by the packet --- .angular-cli.json | 1 + README.md | 51 +++++++++---------- package.json | 2 +- src/app/app.component.html | 3 ++ src/app/app.component.ts | 77 +++++++++++++++++------------ src/app/app.constants.ts | 3 ++ src/app/app.module.ts | 8 ++- src/app/app.states.ts | 4 ++ src/app/classes/Packet.ts | 24 ++++++++- src/app/classes/Utils.ts | 17 +++++++ src/app/end/end.component.html | 2 +- src/app/end/end.component.ts | 9 ++-- src/app/hello/hello.component.html | 5 +- src/app/info/info.component.html | 26 ++++++++++ src/app/info/info.component.scss | 0 src/app/info/info.component.ts | 31 ++++++++++++ src/assets/sounds/paddle.wav | Bin 0 -> 22688 bytes src/assets/sounds/point.wav | Bin 0 -> 8580 bytes src/assets/sounds/wall.wav | Bin 0 -> 13170 bytes src/assets/styles/main.scss | 12 +++++ src/index.html | 2 +- 21 files changed, 205 insertions(+), 72 deletions(-) create mode 100644 src/app/info/info.component.html create mode 100644 src/app/info/info.component.scss create mode 100644 src/app/info/info.component.ts create mode 100644 src/assets/sounds/paddle.wav create mode 100644 src/assets/sounds/point.wav create mode 100644 src/assets/sounds/wall.wav diff --git a/.angular-cli.json b/.angular-cli.json index d62b300..e51a080 100644 --- a/.angular-cli.json +++ b/.angular-cli.json @@ -9,6 +9,7 @@ "outDir": "dist", "assets": [ { "glob": "**/*", "input": "./assets/images/", "output": "./assets/images/" }, + { "glob": "**/*", "input": "./assets/sounds/", "output": "./assets/sounds/" }, { "glob": "favicon.ico", "input": "./", "output": "./" } ], "index": "index.html", diff --git a/README.md b/README.md index 165157d..a52fbe0 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,24 @@ -# Breakout - -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.6.6. - -## 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). +# Hacker Breakout +Hacking themed Breakout game for school built using Angular framework. Written in Typescript, SCSS and HTML5. + +## Backstory +You are trapped in a world, where cyber technology is heavily advanced. Everything is controlled by an A.I. +It won't let you leave, so all you can do is compete with others. + +Everytime you break all walls, you open a door to a new level with higher security and bigger firewall, +therefore you will need higher network speeds. The packet will move faster based on the level you are in. +You only have 3 lives, but you gain a life after levelling up. + +Move the bouncer (paddle) with ⬅️ and ➡️ keys on your keyboard. You can check leaderboards by clicking +on the ⭐ button on the far left. Score is based on how many firewalls you destroy. + +## Download +You can download compiled project [here](https://github.com/Timic3/Breakout/releases/latest) or even better, view it in action [here](https://timic3.github.io/Breakout/) + +## Compiling +* Install Node.js (includes npm too) +* Install Angular CLI with ```npm install -g @angular/cli@latest``` +* Download the [latest source](https://github.com/Timic3/Breakout/archive/master.zip) of Galaxy Maze and unzip it +* Move into the same directory with command prompt and run ```npm install``` to install dependencies +* Run ```ng serve``` and open ```localhost:4200``` once it builds +* You can also build it with ```ng build``` for development build or ```ng build --prod``` for production build diff --git a/package.json b/package.json index 2c6be6d..29f0295 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "breakout", - "version": "0.0.1", + "version": "1.0.0", "license": "MIT", "scripts": { "ng": "ng", diff --git a/src/app/app.component.html b/src/app/app.component.html index 9a5fefc..adb0423 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -2,6 +2,9 @@ + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5048a76..6596854 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,16 +1,18 @@ import { Component, AfterContentInit, HostListener } from '@angular/core'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { MatDialog } from '@angular/material'; import { HelloComponent } from './hello/hello.component'; import { LeaderboardComponent } from './leaderboard/leaderboard.component'; import { EndComponent } from './end/end.component'; +import { InfoComponent } from './info/info.component'; import { AppConstants } from './app.constants'; import { AppStates } from './app.states'; import { Packet } from './classes/Packet'; import { Firewall } from './classes/Firewall'; import { Bouncer } from './classes/Bouncer'; -import { Easing } from './classes/Utils'; +import { Easing, Version } from './classes/Utils'; @Component({ selector: 'app-root', @@ -18,6 +20,9 @@ import { Easing } from './classes/Utils'; styleUrls: ['./app.component.scss'] }) export class AppComponent implements AfterContentInit { + public static LATEST_VERSION = 'Server error'; + public static OUTDATED = false; + public game: HTMLCanvasElement; public context: CanvasRenderingContext2D; @@ -48,7 +53,7 @@ export class AppComponent implements AfterContentInit { public heart; public helloScreen = true; - constructor(public dialog: MatDialog) { } + constructor(public dialog: MatDialog, private http: HttpClient) { } ngAfterContentInit() { this.game = document.getElementById('breakout'); @@ -66,6 +71,7 @@ export class AppComponent implements AfterContentInit { this.packet = new Packet(packetX, packetY, this.bouncer, this.context); this.then = Date.now(); + this.fetchVersion(); this.initiateHelloScreen(); this.gameLoop(); @@ -114,7 +120,8 @@ export class AppComponent implements AfterContentInit { // BAD AGAIN if (AppStates.ENDED) { AppStates.ENDED = false; - this.openEnd(); + this.openEnd(AppStates.SCORE); + AppStates.SCORE = 0; } if (this.levelUp) { @@ -161,7 +168,11 @@ export class AppComponent implements AfterContentInit { this.packet.draw(); // UI - // TODO + + this.context.font = '25px Ubuntu'; + this.context.textAlign = 'left'; + this.context.fillStyle = 'rgba(255, 255, 255, 1.0)'; + this.context.fillText('Score: ' + AppStates.SCORE, 5, AppConstants.GAME_HEIGHT + 32, AppConstants.GAME_WIDTH); for (let i = 0; i < Packet.health; i++) { this.context.drawImage(this.heart, (AppConstants.GAME_WIDTH - 40) - 40 * i, AppConstants.GAME_HEIGHT, 32, 32); @@ -194,6 +205,27 @@ export class AppComponent implements AfterContentInit { } } + fetchVersion() { + this.http.get('https://api.github.com/repos/Timic3/Breakout/releases/latest') + .subscribe( + (data: any) => { + AppStates.LATEST_VERSION = data.tag_name; + console.log('Current version: ' + AppConstants.APP_VERSION); + console.log('Latest GitHub version: ' + data.tag_name); + const compareVersions = Version.compare(AppConstants.APP_VERSION, data.tag_name); + console.log('Semantic version comparator: ' + compareVersions); + if (compareVersions < 0) { + console.log('You are using outdated version! Update here: https://github.com/Timic3/Breakout/releases/latest'); + AppStates.OUTDATED = true; + } + }, + (error: HttpErrorResponse) => { + console.log('An error occurred while fetching version.'); + console.log('Error: ' + error.status + ' (' + error.statusText + ')'); + console.log('Report to: https://github.com/Timic3/Maze/issues'); + }); + } + initiateHelloScreen() { setTimeout(() => { const dialog = this.dialog.open(HelloComponent, { @@ -215,37 +247,18 @@ export class AppComponent implements AfterContentInit { }); } - openEnd() { + openEnd(endScore) { const dialog = this.dialog.open(EndComponent, { - width: '500px' + width: '500px', + data: { + score: endScore + } }); } - cameraShake() { - if (this.shakeTickStart === -1) { - return; - } - - const tick = Date.now() - this.shakeTickStart; - if (tick > this.shakeTime) { - this.shakeTickStart = -1; - return; - } - - const easing = Math.pow(tick / this.shakeTime - 1, 3) + 1; - this.context.save(); - const shakeX = easing * (Math.cos(tick * 0.1) + Math.cos(tick * 0.3115)) * 2; - const shakeY = easing * (Math.sin(tick * 0.05) + Math.sin(tick * 0.057113)) * 2; - this.context.translate(shakeX, shakeY); - } - - // To be finished - virusPreShake() { - this.context.save(); - this.context.translate(Math.random() * 20, Math.random() * 20); - } - - restoreCamera() { - this.context.restore(); + openInfo() { + const dialog = this.dialog.open(InfoComponent, { + width: '500px' + }); } } diff --git a/src/app/app.constants.ts b/src/app/app.constants.ts index 4783307..6d0cc6f 100644 --- a/src/app/app.constants.ts +++ b/src/app/app.constants.ts @@ -1,4 +1,7 @@ export class AppConstants { + // Application version + static readonly APP_VERSION = '1.0.0'; + static readonly GAME_WIDTH = 1200; static readonly GAME_HEIGHT = 700; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 7722e82..d88be97 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; +import { HttpClientModule } from '@angular/common/http'; import { MatButtonModule, @@ -13,6 +14,7 @@ import { AppComponent } from './app.component'; import { HelloComponent } from './hello/hello.component'; import { LeaderboardComponent } from './leaderboard/leaderboard.component'; import { EndComponent } from './end/end.component'; +import { InfoComponent } from './info/info.component'; @NgModule({ @@ -20,11 +22,13 @@ import { EndComponent } from './end/end.component'; AppComponent, HelloComponent, LeaderboardComponent, - EndComponent + EndComponent, + InfoComponent ], imports: [ BrowserModule, BrowserAnimationsModule, + HttpClientModule, MatButtonModule, MatDialogModule, MatIconModule, @@ -32,6 +36,6 @@ import { EndComponent } from './end/end.component'; ], providers: [], bootstrap: [AppComponent], - entryComponents: [HelloComponent, LeaderboardComponent, EndComponent] + entryComponents: [HelloComponent, LeaderboardComponent, EndComponent, InfoComponent] }) export class AppModule { } diff --git a/src/app/app.states.ts b/src/app/app.states.ts index 708c853..1bed58b 100644 --- a/src/app/app.states.ts +++ b/src/app/app.states.ts @@ -16,4 +16,8 @@ export class AppStates { static readonly WALLS_HEIGHT = 40; public static NAME = 'guest'; + public static SCORE = 0; + + public static LATEST_VERSION = 'Server error'; + public static OUTDATED = false; } diff --git a/src/app/classes/Packet.ts b/src/app/classes/Packet.ts index fbc0db0..77f83a7 100644 --- a/src/app/classes/Packet.ts +++ b/src/app/classes/Packet.ts @@ -28,10 +28,26 @@ export class Packet extends Drawable { private bounces: PacketIP[] = []; private bouncer; // Collider + private wallAudio; + private pointAudio; + private paddleAudio; + constructor(x: number, y: number, bouncer: Bouncer, context: CanvasRenderingContext2D) { super(x, y, context); this.bouncer = bouncer; this.bounces.push(new PacketIP(bouncer.x + AppConstants.BOUNCER_WIDTH / 2, bouncer.y - 5)); + + this.wallAudio = new Audio(); + this.wallAudio.src = './assets/sounds/wall.wav'; + this.wallAudio.load(); + + this.paddleAudio = new Audio(); + this.paddleAudio.src = './assets/sounds/paddle.wav'; + this.paddleAudio.load(); + + this.pointAudio = new Audio(); + this.pointAudio.src = './assets/sounds/point.wav'; + this.pointAudio.load(); } draw() { @@ -62,6 +78,7 @@ export class Packet extends Drawable { this.y = this.bouncer.y - AppConstants.PACKET_RADIUS; this.velocityX = (this.x - this.bouncer.x - AppConstants.BOUNCER_WIDTH / 2) / 100; this.updateAngle(); + this.wallAudio.cloneNode(true).play(); if (this.bounces[0]) { this.bounces[0].returnX = this.x; this.bounces[0].returnY = this.y; @@ -160,6 +177,8 @@ export class Packet extends Drawable { manageWallHit(wall) { wall.active = false; + AppStates.SCORE += 10; + this.pointAudio.cloneNode(true).play(); this.managePacketIP(); if (this.checkForWalls()) { @@ -266,6 +285,7 @@ export class Packet extends Drawable { this.hitRight = true; // Don't bug out! this.x = AppConstants.GAME_WIDTH - AppConstants.PACKET_RADIUS; + this.wallAudio.cloneNode(true).play(); } if (this.y >= AppConstants.GAME_HEIGHT - AppConstants.PACKET_RADIUS) { @@ -284,7 +304,7 @@ export class Packet extends Drawable { if (--Packet.health <= 0) { // Reset game AppStates.STARTED = false; - LeaderboardComponent.saveScore(AppStates.STAGE); + LeaderboardComponent.saveScore(AppStates.SCORE); AppStates.ENDED = true; AppStates.STAGE = 1; AppStates.WALLS_X = 3; @@ -302,6 +322,7 @@ export class Packet extends Drawable { this.hitLeft = true; // Bug check this.x = AppConstants.PACKET_RADIUS; + this.wallAudio.cloneNode(true).play(); } if (this.y < AppConstants.PACKET_RADIUS) { @@ -310,6 +331,7 @@ export class Packet extends Drawable { this.managePacketIP(true); // Bug check this.y = AppConstants.PACKET_RADIUS; + this.wallAudio.cloneNode(true).play(); } if (this.hitRight) { diff --git a/src/app/classes/Utils.ts b/src/app/classes/Utils.ts index 3f5caf1..f5c52a6 100644 --- a/src/app/classes/Utils.ts +++ b/src/app/classes/Utils.ts @@ -22,3 +22,20 @@ export class Easing { return Math.sin(13 * (Math.PI / 2) * t) * Math.pow(2, 10 * (t - 1)); } } + +// Semantic versioning compare method +export class Version { + public static compare(a, b) { + const aVer = a.split('.'); + const bVer = b.split('.'); + for (let i = 0; i < 3; i++) { + if (Number(aVer[i]) > Number(bVer[i])) { + return 1; + } + if (Number(aVer[i]) < Number(bVer[i])) { + return -1; + } + } + return 0; + } +} diff --git a/src/app/end/end.component.html b/src/app/end/end.component.html index 5ea2950..b91927b 100644 --- a/src/app/end/end.component.html +++ b/src/app/end/end.component.html @@ -1,7 +1,7 @@

🖤 You've lost all your lives

-

Your score was saved.

+

You scored {{endScore}} points.

diff --git a/src/app/end/end.component.ts b/src/app/end/end.component.ts index b7d22b2..fd3ec31 100644 --- a/src/app/end/end.component.ts +++ b/src/app/end/end.component.ts @@ -1,5 +1,5 @@ -import { Component } from '@angular/core'; -import { MatDialogRef } from '@angular/material'; +import { Component, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; @Component({ selector: 'app-end', @@ -7,7 +7,10 @@ import { MatDialogRef } from '@angular/material'; styleUrls: ['./end.component.scss'] }) export class EndComponent { + public endScore = 0; - constructor(public dialogRef: MatDialogRef) { } + constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any) { + this.endScore = data.score; + } } diff --git a/src/app/hello/hello.component.html b/src/app/hello/hello.component.html index f1e1d3d..c360ddf 100644 --- a/src/app/hello/hello.component.html +++ b/src/app/hello/hello.component.html @@ -12,7 +12,7 @@

👨‍💻 Welcome to Hacker Breakout!

Move the bouncer (paddle) with ⬅️ and ➡️ keys on your keyboard. You can check leaderboards by clicking - on the ⭐ button on the far left. + on the ⭐ button on the far left. Score is based on how many firewalls you destroy.

One more thing: I will need your name. @@ -21,9 +21,6 @@

👨‍💻 Welcome to Hacker Breakout!

This field is required -

- - Timotej M. -

diff --git a/src/app/info/info.component.html b/src/app/info/info.component.html new file mode 100644 index 0000000..935150c --- /dev/null +++ b/src/app/info/info.component.html @@ -0,0 +1,26 @@ +

ℹ️ Information

+ + +

+ Hacker breakout is built using Angular, which is TypeScript-based open-source front-end web application led by Angular Team at Google. + It uses webpack which can preprocess and minify non-JavaScript files such as TypeScript, SASS and LESS files. +

+

+ In this project, I am using TypeScript, SCSS/SASS and HTML5. Angular was very easy to work with, because it's very easy to set up and is very well documented. + I used Visual Studio Code to write code with many plugins, such as Angular 5 Snippets and Auto Import. I also used TSLint for flagging stylistic errors and + bugs in the code. +

+

+ Source code is hosted on GitHub along with compiling guide. +

+

Current version: {{getCurrentVersion()}}
+ Latest version: {{getLatestVersion()}}

+

Version is outdated! Get new version here.

+

Made by Timotej M.

+
+ + + + diff --git a/src/app/info/info.component.scss b/src/app/info/info.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/info/info.component.ts b/src/app/info/info.component.ts new file mode 100644 index 0000000..44d7995 --- /dev/null +++ b/src/app/info/info.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; + +import { AppConstants } from '../app.constants'; +import { AppStates } from '../app.states'; + +@Component({ + selector: 'app-info', + templateUrl: './info.component.html', + styleUrls: ['./info.component.scss'] +}) +export class InfoComponent { + + constructor(public dialogRef: MatDialogRef) { } + + confirm() { + this.dialogRef.close(); + } + + getCurrentVersion() { + return AppConstants.APP_VERSION; + } + + getLatestVersion() { + return AppStates.LATEST_VERSION; + } + + isOutdated() { + return AppStates.OUTDATED; + } +} diff --git a/src/assets/sounds/paddle.wav b/src/assets/sounds/paddle.wav new file mode 100644 index 0000000000000000000000000000000000000000..af480580584901955884bd8bd54d8cf395479f8f GIT binary patch literal 22688 zcmW-p1$b0P7lsoYLb5Y+H@I7I3KVyDFGY(Lcc-|!ySuv;cQ01FxEI^KGqXvMKtl4r z{0~npBxUa0GvE2-%r>r7z50^oK|#$b{Z_4WuYp-aP*6}XzpCyC3YyY9I4D7o2k?&-bs^#2wIypn+XSrRiRYzqJInQ~lN~q3`$BA-!Ik)8_Syu(bO;0Y> zNQy)$B1ZW7>J+w$U10i}tL|v`xxS<`nrHf&eyfL=L^ei8>WSvQ$z)GkpOtpJJ!J

*KNzZVD^boyS?=jEKC%sU| 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 RvB499Ck8!=zY*Ut=zl^d4k-Wt literal 0 HcmV?d00001 diff --git a/src/assets/sounds/point.wav b/src/assets/sounds/point.wav new file mode 100644 index 0000000000000000000000000000000000000000..1eb543aae5480a94d5d9b7c87c24f3db0b1932e5 GIT binary patch literal 8580 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 - Breakout + Hacker Breakout